Check whether your application is running: Difference between revisions
Thoblerone (talk | contribs) |
m (→Using a Win32 Synchronization Object: Fixed typo) |
||
(17 intermediate revisions by 9 users not shown) | |||
Line 9: | Line 9: | ||
If you want to check at the very start, use the <code>.onInit</code> (installer) or <code>un.onInit</code> (uninstaller) [http://nsis.sourceforge.net/Docs/Chapter4.html#4.7 function]. Or you may want to do it later, in a [http://nsis.sourceforge.net/Docs/Chapter4.html#4.6 section] or a [http://nsis.sourceforge.net/Docs/Chapter4.html#4.5.3 page callback function]. | If you want to check at the very start, use the <code>.onInit</code> (installer) or <code>un.onInit</code> (uninstaller) [http://nsis.sourceforge.net/Docs/Chapter4.html#4.7 function]. Or you may want to do it later, in a [http://nsis.sourceforge.net/Docs/Chapter4.html#4.6 section] or a [http://nsis.sourceforge.net/Docs/Chapter4.html#4.5.3 page callback function]. | ||
== | == Using a DDE server == | ||
If the application implements a DDE server, your (un)installer can use the [[nsisDDE plug-in]] to contact the DDE server to query its state, bring the application to the foreground or ask the application to exit. | If the application implements a DDE server, your (un)installer can use the [[nsisDDE plug-in]] to contact the DDE server to query its state, bring the application to the foreground or ask the application to exit. | ||
If your application is written in MFC, it is very easy to add a DDE server in it | If your application is written in MFC, it is very easy to add a DDE server in it. (don't forget to use GuidGen tool to generate a unique service identifier specific to your application) | ||
Many applications already implement such a DDE server (typically used by Explorer to open/print documents). You can search for "ddeexec" keys under HKEY_CLASSES_ROOT to find the name of applications' DDE server. | Many applications already implement such a DDE server (typically used by Explorer to open/print documents). You can search for "ddeexec" keys under HKEY_CLASSES_ROOT to find the name of applications' DDE server. | ||
Line 33: | Line 33: | ||
</highlight-nsis> | </highlight-nsis> | ||
== | == Using a Win32 Synchronization Object == | ||
Many applications typically create a named mutex to prevent multiple instance of the application. | Many applications typically create a named mutex to prevent multiple instance of the application. | ||
Your (un)installer could call the Win32 API "OpenMutex" to detect if this named mutex is active. | Your (un)installer could call the Win32 API "OpenMutex" to detect if this named mutex is active. | ||
Line 44: | Line 44: | ||
Example: | Example: | ||
<highlight-nsis> | <highlight-nsis> | ||
System::Call 'kernel32::OpenMutex(i 0x100000, | System::Call 'kernel32::OpenMutex(i 0x100000, i 0, t "MyAppMutex")p.R0' | ||
IntPtrCmp $R0 0 notRunning | |||
System::Call 'kernel32::CloseHandle( | System::Call 'kernel32::CloseHandle(p $R0)' | ||
MessageBox MB_OK|MB_ICONEXCLAMATION "MyApp is running. Please close it first" /SD IDOK | MessageBox MB_OK|MB_ICONEXCLAMATION "MyApp is running. Please close it first" /SD IDOK | ||
Abort | Abort | ||
Line 56: | Line 56: | ||
Example: | Example: | ||
<highlight-nsis> | <highlight-nsis> | ||
System::Call 'kernel32::OpenFileMapping(i 0x000F001F, | System::Call 'kernel32::OpenFileMapping(i 0x000F001F, i 0, t "MyAppFileMapping")p.R0' | ||
IntPtrCmp $R0 0 notRunning | |||
System::Call 'kernel32::CloseHandle( | System::Call 'kernel32::CloseHandle(p $R0)' | ||
MessageBox MB_OK|MB_ICONEXCLAMATION "MyApp is running. Please close it first" /SD IDOK | MessageBox MB_OK|MB_ICONEXCLAMATION "MyApp is running. Please close it first" /SD IDOK | ||
Abort | Abort | ||
Line 66: | Line 66: | ||
Advanced applications may use other Win32 named synchronization objects like an Event or a Pipe to send a signal or a command to the application. | Advanced applications may use other Win32 named synchronization objects like an Event or a Pipe to send a signal or a command to the application. | ||
== | == Using the name of the process == | ||
You can detect if your application is running by using the [[FindProcDLL plug-in]] to check if a process having the name of your application executable is running. | You can detect if your application is running by using the [[FindProcDLL plug-in]] to check if a process having the name of your application executable is running. | ||
Line 82: | Line 82: | ||
Additionally you may decide (or offer the user the possibility) to kill the running process forcibly, by using the [[KillProc plug-in]]. Note: This can be dangerous as the state of the application and edited files might not be saved correctly. | Additionally you may decide (or offer the user the possibility) to kill the running process forcibly, by using the [[KillProc plug-in]]. Note: This can be dangerous as the state of the application and edited files might not be saved correctly. | ||
'' Note: the [[FindProcDLL plug-in|FindProcDLL documentation]] states that as of NSIS 2.46 this plugin no longer works... | |||
'' Note: there seems to be an alternative working for NSIS 2.46 at [[NsProcess_plugin]] | |||
== | == Using the name of the process (with Windows' tasklist command) == | ||
''Note: tasklist tool is only present in Windows XP professional or more recent Windows | ''Note: tasklist tool is only present in Windows XP professional or more recent Windows. Ergo, this will not work on Windows XP Home Edition. | ||
An alternative to the FindProc plugin above is to check the output of Windows' | An alternative to the FindProc plugin above is to check the output of Windows' <code>tasklist</code> command to see if it contains the process name. (FindProc didn't seem to work for the author of this subsection). This ''could'' be done with the ExecWait command, but that would flash a command prompt window. Instead, the [[ExecCmd_plug-in]] can be used to run the command in a hidden window. | ||
Again, make sure your application executable has an easily identifiable name, or you risk detecting another unrelated application having the same name as yours. | Again, make sure your application executable has an easily identifiable name, or you risk detecting another unrelated application having the same name as yours. | ||
Line 95: | Line 98: | ||
!define FindProc_FOUND 0 | !define FindProc_FOUND 0 | ||
!macro FindProc result processName | !macro FindProc result processName | ||
ExecCmd::exec "tasklist /NH /FI $\"IMAGENAME eq ${processName}$\" | find /I $\"${processName}$\"" | ExecCmd::exec "%SystemRoot%\System32\tasklist /NH /FI $\"IMAGENAME eq ${processName}$\" | %SystemRoot%\System32\find /I $\"${processName}$\"" | ||
Pop $0 ; The handle for the process | Pop $0 ; The handle for the process | ||
ExecCmd::wait $0 | ExecCmd::wait $0 | ||
Line 107: | Line 110: | ||
Additionally you may decide (or offer the user the possibility) to kill the running process forcibly, by using the [[KillProc plug-in]]. Note: This can be dangerous as the state of the application and edited files might not be saved correctly. | Additionally you may decide (or offer the user the possibility) to kill the running process forcibly, by using the [[KillProc plug-in]]. Note: This can be dangerous as the state of the application and edited files might not be saved correctly. | ||
''Note: this version might give unexpected results when running the setup from an UNC Path as cmd.exe itself doesn't support it. The result might include a complaining message. | ''Note: The %SystemRoot%\System prefix is not always necessary, but makes the process robust against users with a modified %PATH%.<br/> | ||
''Note: this version might give unexpected results when running the setup from an UNC Path as cmd.exe itself doesn't support it. The result might include a complaining message. To ovverrride this limitation, see this link: http://support.microsoft.com/kb/156276 . DO IT ON YOUR OWN RISK!!! | |||
== | == Using a window class or title == | ||
If your application creates a (main) window with a specific classname or title, you can use it to detect whether the application is running. | If your application creates a (main) window with a specific classname or title, you can use it to detect whether the application is running. | ||
Line 135: | Line 139: | ||
Once you obtained the window handle with <code>FindWindow</code>, you can also use <code>SendMessage</code> to send [http://support.microsoft.com/kb/178893 WM_CLOSE to close the application]. Note that this may not work gracefully with all applications, for example applications with multiple top-level windows. | Once you obtained the window handle with <code>FindWindow</code>, you can also use <code>SendMessage</code> to send [http://support.microsoft.com/kb/178893 WM_CLOSE to close the application]. Note that this may not work gracefully with all applications, for example applications with multiple top-level windows. | ||
As FindWindow does not allow wildcards you may try this function to search for partial name of a window - http://nsis.sourceforge.net/Enhanced_FindWindow_for_variable_window_class_names | As FindWindow does not allow wildcards you may try this function to search for partial class name of a window - http://nsis.sourceforge.net/Enhanced_FindWindow_for_variable_window_class_names | ||
and this function to match a partial title http://nsis.sourceforge.net/Enhanced_FindWindow_for_variable_window_title_names | |||
== | == Using a registry entry == | ||
Your application could, for example, write 1 on startup in a registry entry, and write 0 when the application is closed. | Your application could, for example, write 1 on startup in a registry entry, and write 0 when the application is closed. | ||
You can then test this registry in your (un)installer. | You can then test this registry in your (un)installer. | ||
Line 152: | Line 157: | ||
</highlight-nsis> | </highlight-nsis> | ||
== Comparison == | |||
{|- Supported OS| Supported NSIS| | | | | |||
|- DDE| < Windows Vista| ?| | | | |||
|- Mutex / FileMapping| Windows 95 >| ?| | | | |||
|- Process name (using NSIS plugin)| ?| < 2.46| | | | |||
|- Process name (using Windows Tasklist)| Windows XP Pro >| Any| | | | |||
|- Window Class Name| ?| Any| | | | |||
|- Windows Registry| ?| ?| | |} | |||
== See also == | |||
[[Allow only one installer instance]] | |||
[[Category:Code Examples]] | [[Category:Code Examples]] |
Latest revision as of 17:32, 3 June 2021
Author: Wizou (talk, contrib) |
There are many methods to detect whether your application is running or not.
It is much better to implement a method that involve some kind of cooperation from your application so that you are sure that you will detect your application specifically. However many of these methods also work without specific cooperation from the application.
Choose your method below, and place the adequate code in your installer script.
If you want to check at the very start, use the .onInit
(installer) or un.onInit
(uninstaller) function. Or you may want to do it later, in a section or a page callback function.
Using a DDE server
If the application implements a DDE server, your (un)installer can use the nsisDDE plug-in to contact the DDE server to query its state, bring the application to the foreground or ask the application to exit.
If your application is written in MFC, it is very easy to add a DDE server in it. (don't forget to use GuidGen tool to generate a unique service identifier specific to your application)
Many applications already implement such a DDE server (typically used by Explorer to open/print documents). You can search for "ddeexec" keys under HKEY_CLASSES_ROOT to find the name of applications' DDE server.
Examples:
nsisDDE::Execute "MyApp-{5A60A807-D40C-4554-935A-E570B818DF75}" "[Quit]" Pop $0 IntCmp $0 0 notRunning Sleep 2000 ; give 2 seconds for the application to finish exiting notRunning:
nsisDDE::Execute "MyApp-{5A60A807-D40C-4554-935A-E570B818DF75}" "[SetForeground]" Pop $0 IntCmp $0 0 notRunning MessageBox MB_OK|MB_ICONEXCLAMATION "MyApp is running (see foreground window). Please close it first" /SD IDOK Abort notRunning:
Using a Win32 Synchronization Object
Many applications typically create a named mutex to prevent multiple instance of the application. Your (un)installer could call the Win32 API "OpenMutex" to detect if this named mutex is active.
If you can, make sure you choose a unique name for the application mutex, typically generated with the GuidGen tool. ex: "MyApp-{5A60A807-D40C-4554-935A-E570B818DF75}"
If you don't have access to the application source code, you can use tools like ProcessExplorer to find the synchronization objects owned by an application.
Example:
System::Call 'kernel32::OpenMutex(i 0x100000, i 0, t "MyAppMutex")p.R0' IntPtrCmp $R0 0 notRunning System::Call 'kernel32::CloseHandle(p $R0)' MessageBox MB_OK|MB_ICONEXCLAMATION "MyApp is running. Please close it first" /SD IDOK Abort notRunning:
An alternative to this approach would be using Named Shared Memory as this allows more flexibility in your main application. In this case the above example would look like this:
Example:
System::Call 'kernel32::OpenFileMapping(i 0x000F001F, i 0, t "MyAppFileMapping")p.R0' IntPtrCmp $R0 0 notRunning System::Call 'kernel32::CloseHandle(p $R0)' MessageBox MB_OK|MB_ICONEXCLAMATION "MyApp is running. Please close it first" /SD IDOK Abort notRunning:
Advanced applications may use other Win32 named synchronization objects like an Event or a Pipe to send a signal or a command to the application.
Using the name of the process
You can detect if your application is running by using the FindProcDLL plug-in to check if a process having the name of your application executable is running.
Make sure your application executable has an easily identifiable name, or you risk detecting another unrelated application having the same name as yours.
Example:
FindProcDLL::FindProc "MyApp.exe" IntCmp $R0 1 0 notRunning MessageBox MB_OK|MB_ICONEXCLAMATION "MyApp is running. Please close it first" /SD IDOK Abort notRunning:
Additionally you may decide (or offer the user the possibility) to kill the running process forcibly, by using the KillProc plug-in. Note: This can be dangerous as the state of the application and edited files might not be saved correctly.
Note: the FindProcDLL documentation states that as of NSIS 2.46 this plugin no longer works...
Note: there seems to be an alternative working for NSIS 2.46 at NsProcess_plugin
Using the name of the process (with Windows' tasklist command)
Note: tasklist tool is only present in Windows XP professional or more recent Windows. Ergo, this will not work on Windows XP Home Edition.
An alternative to the FindProc plugin above is to check the output of Windows' tasklist
command to see if it contains the process name. (FindProc didn't seem to work for the author of this subsection). This could be done with the ExecWait command, but that would flash a command prompt window. Instead, the ExecCmd_plug-in can be used to run the command in a hidden window.
Again, make sure your application executable has an easily identifiable name, or you risk detecting another unrelated application having the same name as yours.
Example:
!define FindProc_NOT_FOUND 1 !define FindProc_FOUND 0 !macro FindProc result processName ExecCmd::exec "%SystemRoot%\System32\tasklist /NH /FI $\"IMAGENAME eq ${processName}$\" | %SystemRoot%\System32\find /I $\"${processName}$\"" Pop $0 ; The handle for the process ExecCmd::wait $0 Pop ${result} ; The exit code !macroend Var /GLOBAL processFound !insertmacro FindProc $processFound "MyApp.exe"
Additionally you may decide (or offer the user the possibility) to kill the running process forcibly, by using the KillProc plug-in. Note: This can be dangerous as the state of the application and edited files might not be saved correctly.
Note: The %SystemRoot%\System prefix is not always necessary, but makes the process robust against users with a modified %PATH%.
Note: this version might give unexpected results when running the setup from an UNC Path as cmd.exe itself doesn't support it. The result might include a complaining message. To ovverrride this limitation, see this link: http://support.microsoft.com/kb/156276 . DO IT ON YOUR OWN RISK!!!
Using a window class or title
If your application creates a (main) window with a specific classname or title, you can use it to detect whether the application is running.
Unless you're sure of the application behaviour, be aware that many applications have dynamically generated window classname (different each time), and localized window title depending on the user language.
You can use the Visual Studio tool Spy++ or a freeware tool like Winlister or WinSpy++ to find the window class of your application.
Examples:
FindWindow $0 "MyAppWindowClass" StrCmp $0 0 notRunning MessageBox MB_OK|MB_ICONEXCLAMATION "MyApp is running. Please close it first" /SD IDOK Abort notRunning:
or
FindWindow $0 "" "MyApp Window Title" StrCmp $0 0 notRunning MessageBox MB_OK|MB_ICONEXCLAMATION "MyApp is running. Please close it first" /SD IDOK Abort notRunning:
Once you obtained the window handle with FindWindow
, you can also use SendMessage
to send WM_CLOSE to close the application. Note that this may not work gracefully with all applications, for example applications with multiple top-level windows.
As FindWindow does not allow wildcards you may try this function to search for partial class name of a window - http://nsis.sourceforge.net/Enhanced_FindWindow_for_variable_window_class_names and this function to match a partial title http://nsis.sourceforge.net/Enhanced_FindWindow_for_variable_window_title_names
Using a registry entry
Your application could, for example, write 1 on startup in a registry entry, and write 0 when the application is closed. You can then test this registry in your (un)installer.
However this solution is not clean because the registry would not be reset to 0 if your application crashes or is forcibly killed.
Example:
ReadRegDWORD $R0 HKLM "SOFTWARE\MyCompany\MyApp" "IsRunning" IntCmp $R0 0 notRunning MessageBox MB_OK|MB_ICONEXCLAMATION "MyApp is running. Please close it first" /SD IDOK Abort notRunning:
Comparison
See also
Allow only one installer instance