Open file in the currently running instance of my program?

3.4k views Asked by At

An Example:

If you have Visual Studio ( 2010 ) open and running, and then double click a misc *.cs file on your PC desktop, the file will open in the current running instance of Visual Studio, instead of opening another instance of VS.

How can I get my own C# program to mimic this behavior ?

In other words, if I have a file type such as *.myfile associated with my program, and the user double-clicks the *.myfile in Windows Explorer, and.... my program is already running..... it should open the file without Windows starting another instance of my program. If the program was not running, then Windows can start an instance normally.

Note that multiple instances of my program are allowed - same as Visual Studio.

Any suggestions would be appreciated !!

3

There are 3 answers

1
Fratyx On BEST ANSWER

If you look at what is registered for for .cs file in registry you will see that it is not the Visual Studio. For Express edition e.g. the registered application is 'VCSExpress.exe' and the studio is running in in 'WDExpress.exe'. In advanced versions I think the studio runs as 'devenv.exe'. The interesting point is that there are two applications: your UI application and a kind of launcher application. I don't know how VS does it, but I could imagine this way: launcher communicates with UI by any kind of interprocess communication e.g. named pipes. (See here) Maybe try this:

  • Launcher application (your file extension is registered with it) tries to open a pipe to UI application as client.
  • If it fails, it starts a new instance of UI application and passes file name as parameter. UI application start server side of named pipe
  • If pipe is opened successful i.e. there is already running a UI instance, launcher sends file name to existing UI process via pipe.
  • Launcher exits after passing the job to UI
1
plinth On

It's been almost 20 years since I had to do something like this, but IIRC, you do something like this:

  1. Before anything else, create a Mailslot (or any other convenient IPC tool)
  2. If you are asked to open a document of the type that should go to an existing instance and if there are no other Mailslots, you continue on
  3. If there IS a Mailslot, you send the Mailslot an open message with the file info and then you exit.
  4. Write code to respond to Mailslot open messages.

If you do the steps before you create windows, it should act like what you want.

0
user3582780 On

I made some project-template which implements this stuff, using windows-messaging. The template is huge and contains some other stuff (like localization, updates, formclosing, clipboard, and an interface for documents, this way the actions in the MDI can be easily forwarded to the MDI-children). If you want to view the template, try this link (or this link)

Some of the code:

Win32.cs:

public partial class Win32
{
    //public const int WM_CLOSE = 16;
    //public const int BN_CLICKED = 245;
    public const int WM_COPYDATA = 0x004A;

    public struct CopyDataStruct : IDisposable
    {
        public IntPtr dwData;
        public int cbData;
        public IntPtr lpData;

        public void Dispose()
        {
            if (this.lpData != IntPtr.Zero)
            {
                LocalFree(this.lpData);
                this.lpData = IntPtr.Zero;
            }
        }
    }

    [DllImport("user32.dll")]
    public static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, ref CopyDataStruct lParam);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr LocalAlloc(int flag, int size);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr LocalFree(IntPtr p);

}

Program.cs:

static class Program
{
    static Mutex mutex = new Mutex(true, guid());
    static string guid()
    {
        // http://stackoverflow.com/questions/502303/how-do-i-programmatically-get-the-guid-of-an-application-in-net2-0
        Assembly assembly = Assembly.GetExecutingAssembly();
        var attribute = (GuidAttribute)assembly.GetCustomAttributes(typeof(GuidAttribute), true)[0];
        return attribute.Value;
    }

    static int MainWindowHandle
    {
        get
        {
            return Settings.Default.hwnd;
        }
        set
        {
            Settings sett = Settings.Default;
            sett.hwnd = value;
            sett.Save();
        }
    }
    public static string GetFileName()
    {
        ActivationArguments a = AppDomain.CurrentDomain.SetupInformation.ActivationArguments;
        // aangeklikt bestand achterhalen
        string[] args = a == null ? null : a.ActivationData;
        return args == null ? "" : args[0];
    }

    [STAThread]
    static void Main()
    {
        if (mutex.WaitOne(TimeSpan.Zero, true))
        {
            #region standaard
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            #endregion
            #region Culture instellen
            string cult = CultureInfo.CurrentCulture.Name;
            Thread.CurrentThread.CurrentUICulture = new CultureInfo(cult);
            Thread.CurrentThread.CurrentCulture = new CultureInfo(cult);
            #endregion
            MainForm frm = new MainForm();
            MainWindowHandle = (int)frm.Handle;
            Application.Run(frm);
            MainWindowHandle = 0;
            mutex.ReleaseMutex();
        }
        else
        {
            int hwnd = 0;
            while (hwnd == 0)
            {
                Thread.Sleep(600);
                hwnd = MainWindowHandle;
            }
            if (hwnd != 0)
            {
                Win32.CopyDataStruct cds = new Win32.CopyDataStruct();
                try
                {
                    string data = GetFileName();
                    cds.cbData = (data.Length + 1) * 2; // number of bytes
                    cds.lpData = Win32.LocalAlloc(0x40, cds.cbData); // known local-pointer in RAM
                    Marshal.Copy(data.ToCharArray(), 0, cds.lpData, data.Length); // Copy data to preserved local-pointer
                    cds.dwData = (IntPtr)1;
                    Win32.SendMessage((IntPtr)hwnd, Win32.WM_COPYDATA, IntPtr.Zero, ref cds);
                }
                finally
                {
                    cds.Dispose();
                }
            }
        }
    }
}

MainFrom.cs:

[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
        case Win32.WM_COPYDATA:
            Win32.CopyDataStruct st = (Win32.CopyDataStruct)Marshal.PtrToStructure(m.LParam, typeof(Win32.CopyDataStruct));
            string strData = Marshal.PtrToStringUni(st.lpData);
            OpenFile(strData);
            Activate();
            break;
        default:
            // let the base class deal with it
            base.WndProc(ref m);
            break;
    }
}

It even works when launching up to 15 files at once.