Allow only one installer instance: Difference between revisions
(Never mix t type and A/W suffix) |
No edit summary |
||
Line 6: | Line 6: | ||
You can list all mutex objects using [http://processhacker.sourceforge.net/ process hacker] - open process properties, handles tab: Mutant, \BaseNamedObjects\myMutex, 0xd0 | You can list all mutex objects using [http://processhacker.sourceforge.net/ process hacker] - open process properties, handles tab: Mutant, \BaseNamedObjects\myMutex, 0xd0 | ||
== Note == | |||
<span style="color:red">'''You should check the return value against ERROR_ALREADY_EXISTS (183) rather than 0. CreateMutex can set other errors such as ERROR_ACCESS_DENIED which results in a false positive.'''</span> | |||
== The Script == | == The Script == |
Revision as of 15:27, 30 May 2014
Author: Vytautas (talk, contrib) |
Description
The best way is to use a kernel mutex. You can call the Win32 API using the System plug-in. See also CreateMutex plug-in.
You can list all mutex objects using process hacker - open process properties, handles tab: Mutant, \BaseNamedObjects\myMutex, 0xd0
Note
You should check the return value against ERROR_ALREADY_EXISTS (183) rather than 0. CreateMutex can set other errors such as ERROR_ACCESS_DENIED which results in a false positive.
The Script
Put this code in the .onInit function:
System::Call 'kernel32::CreateMutex(i 0, i 0, t "myMutex") ?e' Pop $R0 StrCmp $R0 0 +3 MessageBox MB_OK "The installer is already running." Abort
'myMutex' should be replaced with a unique value (e.g. "MyCompany.MyApp" or a generate a unique GUID).
The advanced way
This code will bring the already running instance to front, instead of opening a new one. Side Note (Jamyn): Strangely, if the user has first minimized the installer, and then tries to launch another copy, the following code will correctly set the first installer as the active window, but it won't "pop" it back up from being minimized. I am not sure how to do that simply, but that may be useful. Note (Rescator): Try something like System::Call "user32::ShowWindow(i r1, i 9) i." The 9 is the flag #SW_RESTORE see PSDK for more info on ShowWindow()
System::Call "kernel32::CreateMutex(i 0, i 0, t '$(^Name)') i .r0 ?e" Pop $0 StrCmp $0 0 launch StrLen $0 "$(^Name)" IntOp $0 $0 + 1 loop: FindWindow $1 '#32770' '' 0 $1 IntCmp $1 0 +4 System::Call "user32::GetWindowText(i r1, t .r2, i r0) i." StrCmp $2 "$(^Name)" 0 loop System::Call "user32::SetForegroundWindow(i r1) i." Abort launch:
Even more advanced
This uses the "advanced way" method. Unlike the function above, it will restore the window if the installer is minimized. Note (Akshay): I tested this and it works on Windows XP 64-bit, but on Windows 7 the installer fails, saying "Installer integrity check has failed. Common causes include incomplete download and damaged media. Contact the installer's author to obtain a new copy. More information is at http://nsis.sf.net/NSIS_Error". Note (smartcom): Tested on Windows Vista and on Windows 7 64 bit and no problem found.
Function .onInit BringToFront ; Check if already running ; If so don't open another but bring to front System::Call "kernel32::CreateMutex(i 0, i 0, t '$(^Name)') i .r0 ?e" Pop $0 StrCmp $0 0 launch StrLen $0 "$(^Name)" IntOp $0 $0 + 1 loop: FindWindow $1 '#32770' '' 0 $1 IntCmp $1 0 +5 System::Call "user32::GetWindowText(i r1, t .r2, i r0) i." StrCmp $2 "$(^Name)" 0 loop System::Call "user32::ShowWindow(i r1,i 9) i." ; If minimized then restore System::Call "user32::SetForegroundWindow(i r1) i." ; Bring to front Abort launch: FunctionEnd