Allow only one installer instance: Difference between revisions

From NSIS Wiki
Jump to navigationJump to search
m (→‎Even more advanced: Copy edited. Expansion.)
(Never mix t type and A/W suffix)
Line 4: Line 4:
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]].
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]].


Put this code in the .onInit function:
You can list all mutex objects using [http://processhacker.sourceforge.net/ process hacker] - open process properties, handles tab: Mutant, \BaseNamedObjects\myMutex, 0xd0


== Unicode warning ==
Warning! You should use "CreateMutexW" instead of "CreateMutexA" for unicode NSIS! Using a wrong one like
<highlight-nsis>
System::Call 'kernel32::CreateMutexA(i 0, i 0, t "abcdef") ?e'
</highlight-nsis>
will silently create a mutex named "a", not "abcdef"!
You can list all mutex objects using [http://processhacker.sourceforge.net/ process hacker] - open process properties, handles tab: Mutant, \BaseNamedObjects\myMutex, 0xd0


== The Script ==
== The Script ==
Put this code in the .onInit function:
<highlight-nsis>
<highlight-nsis>
   System::Call 'kernel32::CreateMutexA(i 0, i 0, t "myMutex") ?e'
   System::Call 'kernel32::CreateMutex(i 0, i 0, t "myMutex") ?e'
   Pop $R0
   Pop $R0
   StrCmp $R0 0 +3
   StrCmp $R0 0 +3
Line 23: Line 16:
     Abort
     Abort
</highlight-nsis>
</highlight-nsis>
'myMutex' should be replaced with a unique value (e.g. ProgramSetup.exe).
'myMutex' should be replaced with a unique value (e.g. "MyCompany.MyApp" or a [http://www.guidgenerator.com/ generate] [http://www.guidgen.com/ a unique] [https://developer.mozilla.org/en/docs/Generating_GUIDs GUID]).


== The advanced way ==
== The advanced way ==
Line 31: Line 24:


<highlight-nsis>
<highlight-nsis>
  System::Call "kernel32::CreateMutexA(i 0, i 0, t '$(^Name)') i .r0 ?e"
  System::Call "kernel32::CreateMutex(i 0, i 0, t '$(^Name)') i .r0 ?e"
  Pop $0
  Pop $0
  StrCmp $0 0 launch
  StrCmp $0 0 launch
Line 54: Line 47:
; Check if already running
; Check if already running
; If so don't open another but bring to front
; If so don't open another but bring to front
   System::Call "kernel32::CreateMutexA(i 0, i 0, t '$(^Name)') i .r0 ?e"
   System::Call "kernel32::CreateMutex(i 0, i 0, t '$(^Name)') i .r0 ?e"
   Pop $0
   Pop $0
   StrCmp $0 0 launch
   StrCmp $0 0 launch

Revision as of 15:01, 25 May 2013

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


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

See also

Check whether your application is running