Author Topic: C#, forms, single instance app with command line params from additional instance  (Read 4107 times)

0 Members and 1 Guest are viewing this topic.

Offline warz

  • Hero Member
  • *****
  • Posts: 1134
    • View Profile
    • chyea.org
I'm writing a C# tiff viewer at work, and one requirement is that it re-uses the an application instance if one is already running, instead of opening up multiple instances. The application is a windows forms app, and it opens image files specified via command line params.

Restricting the application to a single instance was the easy part - I just used a named mutex. I'm just trying to find a way to make my main Form reload the newly specified file path, though. I know of 3 ways but none really satisfy me, unless they're the only way.

  • The first one is to simply kill the running process and start a new one, but that looks really bad and it would be much nicer if the app just seamlessly loaded the new image file.
  • The second is to use some visual basic form class, or something, that supposedly supports this, but I'd rather not start importing visual basic namespaces if I don't have to. I'd rather kill the previous instance instead of using the visual basic method.
  • The third is to use RemotingServices and ChannelServices to kind of tunnel the params in, this just seems overkill and I don't know that this will be an options since hundreds of users could be using this app on the same windows 2008 server at a time.

Anyone know of a better way to do this?
http://www.chyea.org/ - web based markup debugger

Offline Newby

  • Moderator
  • Hero Member
  • *****
  • Posts: 10877
  • Thrash!
    • View Profile
Firefox and such are open source. Look how they handle loading .html files -- it'll seamlessly open a new tab and everything.
- Newby
http://www.x86labs.org

Quote
[17:32:45] * xar sets mode: -oooooooooo algorithm ban chris cipher newby stdio TehUser tnarongi|away vursed warz
[17:32:54] * xar sets mode: +o newby
[17:32:58] <xar> new rule
[17:33:02] <xar> me and newby rule all

I'd bet that you're currently bloated like a water ballon on a hot summer's day.

That analogy doesn't even make sense.  Why would a water balloon be especially bloated on a hot summer's day? For your sake, I hope there wasn't too much logic testing on your LSAT. 

Offline warz

  • Hero Member
  • *****
  • Posts: 1134
    • View Profile
    • chyea.org
Firefox and such are open source. Look how they handle loading .html files -- it'll seamlessly open a new tab and everything.

Thanks captain obvious, but I'd rather not. I'm not being paid enough for that. :P

I doubt the way they do it would be a way that I'd do it, anyways. I'm sure theirs is threaded, or at least much different than mine. I know of various methods but none seem to be that "simple, clean" approach that I'm looking for. I'm going to just kill the process for now, and if I get something working better I'll deploy a modified version, I guess.

edit: I did just download firefox's source, though. :(
« Last Edit: October 27, 2009, 04:17:23 pm by warz »
http://www.chyea.org/ - web based markup debugger

Offline MyndFyre

  • Boticulator Extraordinaire
  • x86
  • Hero Member
  • *****
  • Posts: 4540
  • The wait is over.
    • View Profile
    • JinxBot :: the evolution in boticulation
I have something (you have to wait until I get back from my workout though).  Presently it uses WCF though, so it's a .NET 3.0 or newer dependency.  If not it could be reworked to use remoting, but WCF makes it really easy. 

That OK?
I have a programming folder, and I have nothing of value there

Running with Code has a new home!

Our species really annoys me.

Offline warz

  • Hero Member
  • *****
  • Posts: 1134
    • View Profile
    • chyea.org
I have something (you have to wait until I get back from my workout though).  Presently it uses WCF though, so it's a .NET 3.0 or newer dependency.  If not it could be reworked to use remoting, but WCF makes it really easy. 

That OK?

Yea, that's fine. Anything will work, but I just don't feel like those other options were exactly what I was looking for.

edit: email it to rcole@ilstech.com if you want, or zip it up and paste a link here. either work for me.
« Last Edit: October 27, 2009, 11:35:38 pm by warz »
http://www.chyea.org/ - web based markup debugger

Offline MyndFyre

  • Boticulator Extraordinaire
  • x86
  • Hero Member
  • *****
  • Posts: 4540
  • The wait is over.
    • View Profile
    • JinxBot :: the evolution in boticulation
I apologize that there's a bit of interdependency here because this code was authored to support Windows 7 jump lists in JinxBot, so it might not make the most sense....

The service contract is IJinxBotSingleInstanceService.  It is realized as a service in InstanceManagementService (which is the hosted WCF service, created when I create my main window which marshals the call through IJumpListWindowTarget and then actually handles the logic in MainWindow lines 336-358) and InstanceManagementClient (which is the client used to invoke the service when the second process realizes that it a second process).

The actual interaction occurs in the Program class.  The interesting lines are 51-64:
Code: [Select]
Process[] currentProcesses = (from p in Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName)
                              where p.Id != Process.GetCurrentProcess().Id
                              select p).ToArray();
if (currentProcesses.Length > 0)
{
    IntPtr mainWindowHandle = currentProcesses[0].MainWindowHandle;
    InstanceManagementClient client = new InstanceManagementClient(currentProcesses[0].Id);
    if (mainWindowHandle != IntPtr.Zero)
    {
        UnsafeNativeMethods.ShowWindow(mainWindowHandle, 9); //SW_RESTORE=9
        UnsafeNativeMethods.UpdateWindow(mainWindowHandle);
    }
    client.InvokeParameter(args);
}

Note the dependency on UnsafeNativeMethods as well.

The things that you need to do then are:
1.) Create your service contract.
2.) Create your service realization.
3.) Create your client realization, including the channel interface and proxy.
4.) Modify your Main() method to create your global mutex, marshal your service endpoint, and handle the mutex-already-exists case.

This code should run in XP, Vista, and Win7 (I've tested it and it works on Win7).  There might be catches for XP and Vista though:
1.) The way I've implemented service marshaling depends on process ID.  On XP using fast user switching, there could be instances where Process.GetCurrentProcesses() returns processes from other users.  That isn't the default for Win7/Vista because of UAC, but I need to fix that at some point, probably by getting the current session ID.
2.) The way I've implemented service marshaling also depends on local named pipes.  In Vista, there was a problem doing this with Remoting, but I didn't experience a security issue with Windows 7.

LMK how that works or if you have errors.
I have a programming folder, and I have nothing of value there

Running with Code has a new home!

Our species really annoys me.