Current version

v1.10.4 (stable)

Navigation

Main page
Archived news
Downloads
Documentation
   Capture
   Compiling
   Processing
   Crashes
Features
Filters
Plugin SDK
Knowledge base
Contact info
Forum
 
Other projects
   Altirra

Search

Archives

01 Dec - 31 Dec 2013
01 Oct - 31 Oct 2013
01 Aug - 31 Aug 2013
01 May - 31 May 2013
01 Mar - 31 Mar 2013
01 Feb - 29 Feb 2013
01 Dec - 31 Dec 2012
01 Nov - 30 Nov 2012
01 Oct - 31 Oct 2012
01 Sep - 30 Sep 2012
01 Aug - 31 Aug 2012
01 June - 30 June 2012
01 May - 31 May 2012
01 Apr - 30 Apr 2012
01 Dec - 31 Dec 2011
01 Nov - 30 Nov 2011
01 Oct - 31 Oct 2011
01 Sep - 30 Sep 2011
01 Aug - 31 Aug 2011
01 Jul - 31 Jul 2011
01 June - 30 June 2011
01 May - 31 May 2011
01 Apr - 30 Apr 2011
01 Mar - 31 Mar 2011
01 Feb - 29 Feb 2011
01 Jan - 31 Jan 2011
01 Dec - 31 Dec 2010
01 Nov - 30 Nov 2010
01 Oct - 31 Oct 2010
01 Sep - 30 Sep 2010
01 Aug - 31 Aug 2010
01 Jul - 31 Jul 2010
01 June - 30 June 2010
01 May - 31 May 2010
01 Apr - 30 Apr 2010
01 Mar - 31 Mar 2010
01 Feb - 29 Feb 2010
01 Jan - 31 Jan 2010
01 Dec - 31 Dec 2009
01 Nov - 30 Nov 2009
01 Oct - 31 Oct 2009
01 Sep - 30 Sep 2009
01 Aug - 31 Aug 2009
01 Jul - 31 Jul 2009
01 June - 30 June 2009
01 May - 31 May 2009
01 Apr - 30 Apr 2009
01 Mar - 31 Mar 2009
01 Feb - 29 Feb 2009
01 Jan - 31 Jan 2009
01 Dec - 31 Dec 2008
01 Nov - 30 Nov 2008
01 Oct - 31 Oct 2008
01 Sep - 30 Sep 2008
01 Aug - 31 Aug 2008
01 Jul - 31 Jul 2008
01 June - 30 June 2008
01 May - 31 May 2008
01 Apr - 30 Apr 2008
01 Mar - 31 Mar 2008
01 Feb - 29 Feb 2008
01 Jan - 31 Jan 2008
01 Dec - 31 Dec 2007
01 Nov - 30 Nov 2007
01 Oct - 31 Oct 2007
01 Sep - 30 Sep 2007
01 Aug - 31 Aug 2007
01 Jul - 31 Jul 2007
01 June - 30 June 2007
01 May - 31 May 2007
01 Apr - 30 Apr 2007
01 Mar - 31 Mar 2007
01 Feb - 29 Feb 2007
01 Jan - 31 Jan 2007
01 Dec - 31 Dec 2006
01 Nov - 30 Nov 2006
01 Oct - 31 Oct 2006
01 Sep - 30 Sep 2006
01 Aug - 31 Aug 2006
01 Jul - 31 Jul 2006
01 June - 30 June 2006
01 May - 31 May 2006
01 Apr - 30 Apr 2006
01 Mar - 31 Mar 2006
01 Feb - 29 Feb 2006
01 Jan - 31 Jan 2006
01 Dec - 31 Dec 2005
01 Nov - 30 Nov 2005
01 Oct - 31 Oct 2005
01 Sep - 30 Sep 2005
01 Aug - 31 Aug 2005
01 Jul - 31 Jul 2005
01 June - 30 June 2005
01 May - 31 May 2005
01 Apr - 30 Apr 2005
01 Mar - 31 Mar 2005
01 Feb - 29 Feb 2005
01 Jan - 31 Jan 2005
01 Dec - 31 Dec 2004
01 Nov - 30 Nov 2004
01 Oct - 31 Oct 2004
01 Sep - 30 Sep 2004
01 Aug - 31 Aug 2004

Stuff

Powered by Pivot  
XML: RSS feed 
XML: Atom feed 

§ Gotchas when creating a single instance application

A single instance application is one that only permits one instance of itself to run at a time. One reason to do this is that the program requires exclusive access to a shared resource, such as a large persistent database within the user profile. Another reason is for better UI: perhaps for an image viewer you want the same window to be reused by default instead of popping up new windows each time that the user needs to close.

Depending on the specific requirements, there are a couple of ways to enforce a single instance on Windows, such as named mutexes and exclusive file locks. In addition to enforcing the restriction, though, you'll usually want to pass the command-line parameters over as well so that the existing instance can respond to the new request. The first inclination is to just marshal over the command-line parameters via some RPC mechanism. A cursory search for sample code shows this to be a common practice. This works great, until you get a command line like this:

awesomeness.exe file.awe

The problem here is that the passed-in path is relative. Since most Win32 GUI programs will change their current directory whenever the common file dialog is used, it's likely that the existing and new processes will have different current working directories and thus the open operation in the existing process will fail. Sending over the current directory solves this problem, but this is the case I find is often forgotten:

awesomeness.exe d:file.awe

This is a drive-relative path, which causes the same problem. These are useful if you're a command-line junkie like me. The tricky part is that there aren't explicit APIs in Win32 to access the current drive paths, even though Win32 itself does the resolution. It turns out they're hidden environment variables of the form =A:, =B:, etc. Send those over too, and now the paths work. Another way to solve this would be to expand all of the paths to full paths in the second instance, but that's a bit more command-line parsing in the cross-launch path than I like.

There is a third case to watch out for, which is when the existing instance is running under a different user account within the same window session. You can get into this situation via the "runas" command or the "run as a different user" functionality. This bites me in the butt occasionally with Firefox where some program I have running under one user account will launch a web link and it'll load into the existing Firefox instance under a different user profile than it would have otherwise. Detecting this situation involves a little bit of detective work with getting the process ID of the window, opening the process, and checking the user information on the process token.

The final odd situation is elevated vs. non-elevated status on Windows Vista, i.e. run as administrator. This is a pretty odd situation since the process is running under the same user and mostly the same but not quite the same environment. A particular issue is that elevated and non-elevated processes see different sets of SUBST'ed drives. It's pretty clear that you wouldn't want a non-elevated launcher to hand arbitrary arguments off to an existing elevated process, but silently failing or throwing an error isn't a good user experience either. Handling of this case likely needs to be tailored depending on the reason for the single-instance restriction.

Comments

Comments posted:


Another common gotcha (including in some major products like Office) is having a race condition in the single-instance code, so if you run two instances in parallel (or launch them async and very close to each other), they may both think they are the main instance and you end up with two instead of one.

It's usually caused by things checking if there is an existing instance, and then flagging themselves as the main instance if there isn't. If both instances check at the same time, nothing is flagged yet, and then they both flag themselves. Doing the check by acquiring a mutex is the obvious and easy solution, but it's surprising how many developers still screw it up.

Leo Davidson (link) - 18 08 12 - 18:19


That's a good point and one that I hadn't thought about. The mutex alone isn't enough to solve the case where the main instance is shutting down, though -- you'd need to also serialize the cross-call with the mutex as well, and it'd be tricky to do this safely. I'd be wary of trying a SendMessage(WM_COPYDATA) with a shared mutex held, for instance.

Then there's the case where the main instance is waiting in a modal dialog message loop....

This is one of those things that looks simple at first, but the more you dig into it, the bigger the can of worms turns out to be.

Phaeron - 18 08 12 - 18:41


That would make for a nice library. A simple interface with lots of sophistication under the hood.

tobi - 18 08 12 - 22:32


As Raymond Chen put it: writing a single-instance application is like setting up an easy way to DoS your application.

ender - 19 08 12 - 08:43


I respect Raymond Chen for a lot of reasons, but IMO this would be akin to calling database locking a DoS attack. There are valid reasons for doing so. I'd also call the cross-application interference issue mostly an "already on the other side of the airtight hatch" problem since if you have access to shared named mutexes there's a lot more damage you can do... such a locking up *all* GUI applications in the session on Windows XP.

Phaeron - 19 08 12 - 08:48


Making an application single-instance can mean any of the following:

- Avoiding multiple instances started in the same user session.
- Avoiding multiple instances started in the same desktop.
- Avoiding multiple instances started in any session of the same user account.
- Avoiding multiple instances started on the same machine.

Just using CreateMutex() is thus most certainly not enough -- you need to use some other stuff, and complexity varies depending on restrictions you want to impose -- pretty good overview of various cases including the ones that doesn't work can be found here:
http://www.codeproject.com/Articles/538/..

Igor Levicki (link) - 04 10 12 - 00:57

Comment form


Please keep comments on-topic for this entry. If you have unrelated comments about VirtualDub, the forum is a better place to post them.
Name:  
Remember personal info?

Email (Optional):
Your email address is only revealed to the blog owner and is not shown to the public.
URL (Optional):
Comment: /

An authentication dialog may appear when you click Post Comment. Simply type in "post" as the user and "now" as the password. I have had to do this to stop automated comment spam.



Small print: All html tags except <b> and <i> will be removed from your comment. You can make links by just typing the url or mail-address.