Check whether your application is running: Difference between revisions

From NSIS Wiki
Jump to navigationJump to search
No edit summary
 
(13 intermediate revisions by 7 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 ==
== 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.. (don't forget to use GuidGen tool to generate a unique service identifier specific to your application)
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 ==
== 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, b 0, t "MyAppMutex") i .R0'
System::Call 'kernel32::OpenMutex(i 0x100000, i 0, t "MyAppMutex")p.R0'
IntCmp $R0 0 notRunning
IntPtrCmp $R0 0 notRunning
     System::Call 'kernel32::CloseHandle(i $R0)'
     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, b 0, t "MyAppFileMapping") i .R0'
System::Call 'kernel32::OpenFileMapping(i 0x000F001F, i 0, t "MyAppFileMapping")p.R0'
IntCmp $R0 0 notRunning
IntPtrCmp $R0 0 notRunning
     System::Call 'kernel32::CloseHandle(i $R0)'
     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 ==
== 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 84: Line 84:
'' Note: the [[FindProcDLL plug-in|FindProcDLL documentation]] states that as of NSIS 2.46 this plugin no longer works...
'' Note: the [[FindProcDLL plug-in|FindProcDLL documentation]] states that as of NSIS 2.46 this plugin no longer works...


== using the name of the process (with Windows's tasklist command) ==
'' Note: there seems to be an alternative working for NSIS 2.46 at [[NsProcess_plugin]]
''Note: tasklist tool is only present in Windows XP professional or more recent Windows
 
== 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's '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.
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 96: 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 108: 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 ==
== 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 136: 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 ==
== 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 153: Line 157:
</highlight-nsis>
</highlight-nsis>


== Comparsion ==
== Comparison ==


{|- Supported OS| Supported NSIS| | | |
{|- Supported OS| Supported NSIS| | | |

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