Allow only one installer instance: Difference between revisions

From NSIS Wiki
Jump to navigationJump to search
m (Added category links.)
(Simplified mutex code and added file code)
 
(44 intermediate revisions by 24 users not shown)
Line 1: Line 1:
{|align=right
{{PageAuthor|Anders}}
|<small>Author: [[{{ns:2}}:Vytautas|Vytautas]] ([[{{ns:3}}:Vytautas|talk]], [[{{ns:-1}}:Contributions/Vytautas|contrib]])</small>
[[Category:System Plugin Examples]]
|}
 
<br style="clear:both;">
== Mutex ==
== Description ==
 
The best way is to use a kernel mutex. You can call the Win32 API using the System plug-in. Put this code in the .onInit function:
 
A [http://docs.microsoft.com/en-us/windows/win32/sync/mutex-objects mutex] is perhaps the best way to detect if another instance is running:


== The Script ==
<highlight-nsis>
<highlight-nsis>
System::Call 'kernel32::CreateMutexA(i 0, i 0, t "myMutex") i .r1 ?e'
!define INSTALLERMUTEXNAME "$(^Name)" ; TODO: Should really use a GUID here! (guidgenerator.com or guidgen.com)
 
!ifndef NSIS_PTR_SIZE & SYSTYPE_PTR
!define SYSTYPE_PTR i ; NSIS v2.x
!else
!define /ifndef SYSTYPE_PTR p ; NSIS v3.0+
!endif
 
!macro ActivateOtherInstance
StrCpy $3 "" ; Start FindWindow with NULL
loop:
FindWindow $3 "#32770" "" "" $3
StrCmp 0 $3 windownotfound
StrLen $0 "$(^UninstallCaption)"
IntOp $0 $0 + 1 ; GetWindowText count includes \0
System::Call 'USER32::GetWindowText(${SYSTYPE_PTR}r3, t.r0, ir0)'
StrCmp $0 "$(^UninstallCaption)" windowfound ""
StrLen $0 "$(^SetupCaption)"
IntOp $0 $0 + 1 ; GetWindowText count includes \0
System::Call 'USER32::GetWindowText(${SYSTYPE_PTR}r3, t.r0, ir0)'
StrCmp $0 "$(^SetupCaption)" windowfound loop
windowfound:
SendMessage $3 0x112 0xF120 0 /TIMEOUT=2000 ; WM_SYSCOMMAND:SC_RESTORE to restore the window if it is minimized
System::Call "USER32::SetForegroundWindow(${SYSTYPE_PTR}r3)"
windownotfound:
!macroend
 
!macro SingleInstanceMutex
!ifndef INSTALLERMUTEXNAME
!error "Must define INSTALLERMUTEXNAME"
!endif
System::Call 'KERNEL32::CreateMutex(${SYSTYPE_PTR}0, i1, t"${INSTALLERMUTEXNAME}")?e'
Pop $0
IntCmpU $0 183 "" launch launch ; ERROR_ALREADY_EXISTS?
!insertmacro ActivateOtherInstance
Abort
launch:
!macroend


  Pop $R0
Function .onInit
!insertmacro SingleInstanceMutex
FunctionEnd


  StrCmp $R0 0 +3
Function un.onInit
    MessageBox MB_OK "The installer is already running."
!insertmacro SingleInstanceMutex
    Abort
FunctionEnd
</highlight-nsis>
</highlight-nsis>
'myMutex' should be replaced with a unique value.


== The advanced way ==
== File ==
This code will bring the already running instance to front, instead of open a new one.
 
A open file handle that denies shared access can be used to detect a existing instance:
 
<highlight-nsis>
<highlight-nsis>
System::Call "kernel32::CreateMutexA(i 0, i 0, t '$(^Name)') i .r0 ?e"
#TODO !define INSTALLERLOCKFILEGUID "{....}" ; Use a GUID here! (guidgenerator.com or guidgen.com)
Pop $0
 
StrCmp $0 0 launch
!macro SingleInstanceFile
  StrLen $0 "$(^Name)"
!ifndef INSTALLERLOCKFILEGUID
loop:
!error "Must define INSTALLERLOCKFILEGUID"
  FindWindow $1 '#32770' '' 0 $1
!endif
  IntCmp $1 0 +4
!if "${NSIS_PTR_SIZE}" > 4
  System::Call "user32::GetWindowText(i r1, t .r2, i $0) i."
!include util.nsh
  StrCmp $2 "$(^Name)" 0 loop
!else ifndef IntPtrCmp
  System::Call "user32::SetForegroundWindow(i r1) i."
!define IntPtrCmp IntCmp
  Abort
!endif
launch:
!ifndef NSIS_PTR_SIZE & SYSTYPE_PTR
!define SYSTYPE_PTR i ; NSIS v2.x
!else
!define /ifndef SYSTYPE_PTR p ; NSIS v3.0+
!endif
!if "${NSIS_CHAR_SIZE}" < 2
Push "$TEMP\${INSTALLERLOCKFILEGUID}.lock"
!else
Push "$APPDATA\${INSTALLERLOCKFILEGUID}.lock"
!endif
System::Call 'KERNEL32::CreateFile(ts,i0x40000000,i0,${SYSTYPE_PTR}0,i4,i0x04000000,${SYSTYPE_PTR}0)${SYSTYPE_PTR}.r0'
${IntPtrCmp} $0 -1 "" launch launch
MessageBox MB_ICONSTOP "Already running!"
Abort
launch:
!macroend
 
Function .onInit
!insertmacro SingleInstanceFile
FunctionEnd
 
Function un.onInit
!insertmacro SingleInstanceFile
FunctionEnd
</highlight-nsis>
</highlight-nsis>


[[{{ns:14}}:System Plugin Examples]]
== See also ==
 
* [[CreateMutex plug-in]]
* [[Check whether your application is running]]

Latest revision as of 18:31, 4 September 2019

Author: Anders (talk, contrib)


Mutex

A mutex is perhaps the best way to detect if another instance is running:

!define INSTALLERMUTEXNAME "$(^Name)" ; TODO: Should really use a GUID here! (guidgenerator.com or guidgen.com)
 
!ifndef NSIS_PTR_SIZE & SYSTYPE_PTR
!define SYSTYPE_PTR i ; NSIS v2.x
!else
!define /ifndef SYSTYPE_PTR p ; NSIS v3.0+
!endif
 
!macro ActivateOtherInstance
StrCpy $3 "" ; Start FindWindow with NULL
loop:
	FindWindow $3 "#32770" "" "" $3
	StrCmp 0 $3 windownotfound
	StrLen $0 "$(^UninstallCaption)"
	IntOp $0 $0 + 1 ; GetWindowText count includes \0
	System::Call 'USER32::GetWindowText(${SYSTYPE_PTR}r3, t.r0, ir0)'
	StrCmp $0 "$(^UninstallCaption)" windowfound ""
	StrLen $0 "$(^SetupCaption)"
	IntOp $0 $0 + 1 ; GetWindowText count includes \0
	System::Call 'USER32::GetWindowText(${SYSTYPE_PTR}r3, t.r0, ir0)'
	StrCmp $0 "$(^SetupCaption)" windowfound loop
windowfound:
	SendMessage $3 0x112 0xF120 0 /TIMEOUT=2000 ; WM_SYSCOMMAND:SC_RESTORE to restore the window if it is minimized
	System::Call "USER32::SetForegroundWindow(${SYSTYPE_PTR}r3)"
windownotfound:
!macroend
 
!macro SingleInstanceMutex
!ifndef INSTALLERMUTEXNAME
!error "Must define INSTALLERMUTEXNAME"
!endif
System::Call 'KERNEL32::CreateMutex(${SYSTYPE_PTR}0, i1, t"${INSTALLERMUTEXNAME}")?e'
Pop $0
IntCmpU $0 183 "" launch launch ; ERROR_ALREADY_EXISTS?
	!insertmacro ActivateOtherInstance
	Abort
launch:
!macroend
 
Function .onInit
!insertmacro SingleInstanceMutex
FunctionEnd
 
Function un.onInit
!insertmacro SingleInstanceMutex
FunctionEnd

File

A open file handle that denies shared access can be used to detect a existing instance:

#TODO !define INSTALLERLOCKFILEGUID "{....}" ; Use a GUID here! (guidgenerator.com or guidgen.com)
 
!macro SingleInstanceFile
!ifndef INSTALLERLOCKFILEGUID
!error "Must define INSTALLERLOCKFILEGUID"
!endif
!if "${NSIS_PTR_SIZE}" > 4
!include util.nsh
!else ifndef IntPtrCmp
!define IntPtrCmp IntCmp
!endif
!ifndef NSIS_PTR_SIZE & SYSTYPE_PTR
!define SYSTYPE_PTR i ; NSIS v2.x
!else
!define /ifndef SYSTYPE_PTR p ; NSIS v3.0+
!endif
!if "${NSIS_CHAR_SIZE}" < 2
Push "$TEMP\${INSTALLERLOCKFILEGUID}.lock"
!else
Push "$APPDATA\${INSTALLERLOCKFILEGUID}.lock"
!endif
System::Call 'KERNEL32::CreateFile(ts,i0x40000000,i0,${SYSTYPE_PTR}0,i4,i0x04000000,${SYSTYPE_PTR}0)${SYSTYPE_PTR}.r0'
${IntPtrCmp} $0 -1 "" launch launch
	MessageBox MB_ICONSTOP "Already running!"
	Abort
launch:
!macroend
 
Function .onInit
!insertmacro SingleInstanceFile
FunctionEnd
 
Function un.onInit
!insertmacro SingleInstanceFile
FunctionEnd

See also