[RESOLVED] Single instance app works - still feels a bit like a hack though...

If this is your first visit, be sure to
check out the FAQ by clicking the
link above. You may have to register or Login
before you can post: click the register link above to proceed. To start viewing messages,
select the forum that you want to visit from the selection below.

Everything works (under XP Pro SP3) so I should be really satisfied, yet I don't feel completely comfortable with the solution. All possible ways I found in the articles and several forum posts to bring the primary instance window to the front didn't really work. The best I got was actually bringing the window to the front, but not directly activating it, instead flashing its task bar button. I noticed though, that it behaved as desired when it was restored from the minimized state (either on the task bar or the tray) in response to the instance activation message. So I desperately resorted to minizing the app if it isn't already minimized and then (re-)restoring it. Now this does work and it doesn't even cause any annoying optical effects. It doesn't even require the previously used call to Activate() anymore so I could comment it out. But isn't that a bit hackish?

BTW, I changed the way of handling the mutex to what the codeproject article describes before actually having read it. It simply looked pretty natural to me to handle it that way.

Also note that I do not use the GC::KeepAlive() trick that is mentioned all around the articles and forum posts. It seems to work perfectly fine without it. I tested letting the app silently lie in the backgound for slightly more than 45 minutes and the instance protection still worked. However, Form1 contains a MonthCalendar that triggers its DateChanged event at least every two minutes, regardless of whether anything has happened (except time passing and probably timer ticks of course) or not. But I don't believe this has any influence on the mutex which is a local variable of main() and thus almost as unrelated as can be.

Any comments? You're welcome.

P.S.: Please forgive me all those C-style casts. They simply saved a significant amount of typing effort during lots of back-and-forth changes of the code. Also, the two-stage casts via void * would probably look really ugly without them. I wonder why they're necessary at all but void * was the only type I found to be available as the intermediate step that has machine word size.

Last edited by Eri523; April 24th, 2011 at 11:37 PM.
Reason: Merely fixed a comment in the code

I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

Re: Single instance app works - still feels a bit like a hack though...

Thanks for the link.

I haven't yet read the article really thoroughly but the code in there basically appears to use something I used sucessfully all the way while implementing the single instance behaviour (a mutex) and something I tried without success (::SetForegroundWindow()).

However, at least the discussion of potential problems, in particular race conditions, in the article is something I haven't yet read in that context and the article definitely looks worth thorough reading once I have more time.

I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

Re: Single instance app works - still feels a bit like a hack though...

Yes, of course what you suggest is the natural variant and I was perfectly sure I had already tested that and it did not work. But maybe I've missed something, so I tried to revert my code to (an equivalent of) what you posted. The following is my BringMeUp() function at the moment that gets called from WndProc() in case it detects a WM_BRINGUP message before calling its base class implementation. (WndProc() is the unchanged original from the OP. - Of course it calls its base class implementation; anything else would be quite fatal... ) Maybe despite careful checking I'm still overlooking something and four (or more) eyes possibly see more than two. So please have a look:

The first extra line in the if branch is to handle the case when the app is minimized to the tray, in which case I set it to invisible in order to remove it from the task bar. The other extra line controls the visibility of the tray icon, which is to be set to false upon restore from tray unless the "ghost" is activated.

The behaviour of this variant is exactly what I described it the OP: Anything works except activating the primary instance from a secondary one by sending a WM_BRINGUP message in case the primary instance form is behind any other application window (regardless of whether it's covered entirely, partially or not at all). In this case the primary instance task bar button is flashed instead of bringing its form to the front. It does work, however, if the primary instance form is minimized intead of covered, what originally brought me to the minimize-restore trick.

Although this renders some other functionality of the app nonfunctional, I even temporarily commented out the two extra lines to make my code exactly resemble your suggestion (except that it's located in an extra function instead of directly in WndProc() and a redundant pair of curly braces). That didn't change anything about the specific behaviour we're discussing here.

Last edited by Eri523; April 27th, 2011 at 09:49 PM.

I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

Re: Single instance app works - still feels a bit like a hack though...

Ok, looks like this one could become tricky... To simplify testing for those who want to assist I have set up a test project and attached it to this post. It includes the original WndMessageHolder class from the original app project, plus the minimum required to reproduce the problem, with two alternative versions of Form1::WndProc(): one based on TheGreatCthulhu's suggestion from post #4 and one based on my original code as posted in the OP between which can be chosen using a macro definition.

Re: Single instance app works - still feels a bit like a hack though...

In the end, call the base class' implementation to do the rest of the processing.

Originally Posted by Eri523

Of course it calls its base class implementation; anything else would be quite fatal...

Sorry, it sounded (... it read) as if I was addressing you - I obviously know you don't need to be reminded of such things; but as there may be other people reading, I wanted to mention it for the sake of completeness.

BTW: When I originally posted, I only gave your code a quick look, and now that I've taken some more time, I see that (aside from the fact that it behaves differently under WinXP - at least in your case) there is nothing remarkably different in "my approach", so scrap that.

Re: Single instance app works - still feels a bit like a hack though...

Yes I did. That's where this remnant function in WndMessageHolder is left over from:

Code:

// Taken strictly, the following doesn't really belong here...
// Actually, it isn't even used as of now. It's a remnant from earlier experiments.
bool SetForegroundWindow_(IWin32Window ^);

I just didn't remove it yet in case it could be useful for testing something I hadn't thought of yet in the future.

[...] there is nothing remarkably different in "my approach", so scrap that.

Oh, I wouldn't say there's no remarkable difference. Your approach is the natural, classic one and in fact that was (most likely - the code in BringMeUp() is essentially inherited from versions of the app that didn't have the single instance feature at all, so I'm not really 100% sure) what I tried first. The "alternative approach", OTOH, first "superfluously" minimizes the window if in wasn't already minimized, just to restore it immediatly afterwards. I'd say that's pretty weird and a big difference.

I already thought of making a new version of the test app which supports logging. I already have a logging class in the original app (designed for reusability in the first place) that I more or less would just need to copy over, but then again, that tiny test app has so few things that are candidate for logging... Would that actually gain anything?

At the moment I primarily hope someone will test that on another XP to rule out this actually is a peculiarity of my own XP (in which case I'd be really stumped about what to look for...). Unfortunately, none of my test users around here uses that anymore.

Last edited by Eri523; April 27th, 2011 at 09:57 PM.

I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

Re: Single instance app works - still feels a bit like a hack though...

Well, I didn't really get ahead with this since the last post. I'd really love if someone could test that for me on another Windows XP. To simplify testing I have attached a new version of the test project to this post that allows switching between the two instance switching methods at runtime, i.e. in particular without recompilation, so that the compiled version can be passed to people who don't have VC++/VS.

I'm still not sure wether this is a peculiarity of my own XP, but it looks like it's not a peculiarity of my specific app: Some other apps not written by me appear to have this problem as well, some of which are definitely unmanaged.

Re: Single instance app works - still feels a bit like a hack though...

Update:
OMG! Turn's out it's NOT a Windows XP issue - it behaves the same on Windows 7! Except when run from Visual Studio...

The way I originally tested it is: I use a somewhat customized UI, where right next to the "Start Debugging" button I have a "Start Without Debugging" button. So I just ran the app from there, and it worked in both cases. However, when started in other ways, the "classic" solution behaves as you described.

Is there something I'm missing about how VS launches the app? I thought that it might have something to do with vshost.exe hosting process, but it seems that it's not used for C++/CLI projects. It behaves the same in both Debug and Release.

Furthermore, if you hit F5, and then try to run it from somewhere other than VS, it works.

...
In the meantime, I've done some more testing, and now I'm really confused...
I tried to run it from console, and contrary to my expectations, it worked. Now I can't get it not to work, using the same methods as before. I don't think I did anything special. How weird is that?

Re: Single instance app works - still feels a bit like a hack though...

Update: OK, now I deleted the entire folder and extracted your rar file again, recompiled, and it doesn't work again.
This is sooooo The X Files.
EDIT: I don't know what I did, I cannot reproduce the described behavior.
EDIT 1: This is getting weirder and weirder. F5- start from console... Sometimes it works, sometimes it doesn't...
EDIT 2: - now it works all the time?! Microsoft, what did you do?!

Re: Single instance app works - still feels a bit like a hack though...

Eri523, I'm going attach the exe file that works (at least on my machine, for now), so that you can test it on yours. Please post the results here. Note that I haven't altered a single line of code, so if it turns out to work, what to heck that means? That windows has somehow altered the PE file?

Re: Single instance app works - still feels a bit like a hack though...

UPDATE: OK, I was right to write "for now", as the "working" exe occasionally doesn't work, but most of the time it does. Again, for now... I don't get it. If the conditions are somehow altered, it's not obvious at all.

Try compiling your app and testing it in various scenarios, like 20 times per run, and see if it behaves the same. This is really, really... unsettling.

Re: Single instance app works - still feels a bit like a hack though...

Really, really weird... I already suspected the issue on my pesonal side because I got my copy of VC++ from Warehouse 13, but now that you can reproduce the problem, even on Win 7, my abberation gradually increases...

The file you posted obviously is a debug build and I had to make a new one. My Debug directory of that project was empty because I apparently didn't do a debug build since I cleaned the project for the attachment to post #10.

The first thing I checked was the MSIL output of the compiler for Form1::BringMeUp(). They're literally identical in your version of the file and mine, namely:

The two .exe files have identical lengths, however, a binary compare reveals 13791 bytes comparing unequal. I'm not familiar enough with the PE format to really interpret them all, but it looks like a considerable bunch of them is just the result of shifting some stuff around inside the file.

Dumping the structture of the two files to a text file using ILDASM and comparing them shows some differences as well (I estimate significantly less than 10&#37; of the file from looking at Notepad++'s file comaprison display) and they seem all to either come from different hex numbers used to name "UnnamedClass"es and shifting around otherwise unchanged lines (over short distances).

I have no idea why Windows should modify a PE file on its own at all (maybe to accomodate for some weird permission issues?), and if it actually did, it probably wouln't touch the actual code, rather just maybe the PE header or some manifest stuff, and I don't think that would modify the program's behaviour in the way we're observing. Also, wouldn't it need to modify the file and then somewhen revert it to the original state to cause what we're observing? Even harder to believe I think...

I didn't check as many variants to start the app as you did (thank's for the effort ). I merely started it either from the IDE or by double-clicking it in Explorer (and I don't think I ever started two instances attempting to run siultaneously both from the IDE). I also did tests involving starting the app via a Start Menu link, but only with the original app the code we discuss here is from. At any rate, the classic method always misbehaved consistently.

EDIT: Ah, and... In the tests I did with your version of the test app (not many yet, though) it behaved exactly like my version.

Although I'm somehow disappointed by this thing becomming weirder and weirder instead of getting cleared up, I could pull at least one small immediate benefit from your post #11:

Originally Posted by TheGreatCthulhu

I use a somewhat customized UI, where right next to the "Start Debugging" button I have a "Start Without Debugging" button.

I always wished I had such a button because more often than not I run my apps without debugging in the IDE and the button often would be more convenient than hitting Ctrl+F5. I failed to consider the fact that the IDE has buttons for virtually everything that just need to be placed on the tool bars. Now I have such a button as well. Thanks!

So, and now I'm gonna watch today's three Twin Peaks episodes. (Actually, the first one already is 2/3 over, but I have the TV in the room with me here.) And the next thing I'll do is change the real-life app so that it always defaults to the alternative method which can only be overridden by a setting in the app config file or an undocumented keyboard shortcut.

Last edited by Eri523; May 17th, 2011 at 05:03 PM.

I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.