Check whether your application is running: Difference between revisions

From NSIS Wiki
Jump to navigationJump to search
 
(30 intermediate revisions by 15 users not shown)
Line 1: Line 1:
{{PageAuthor|saabz_500}}
{{PageAuthor|Wizou}}


There are many methods to detect whether your application is running or not.
There are many methods to detect whether your application is running or not.
Line 6: Line 6:
However many of these methods also work without specific cooperation from the application.
However many of these methods also work without specific cooperation from the application.


Choose your method below, and place the adequate code inside your installer
Choose your method below, and place the adequate code in your installer script.
<highlight-nsis>
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].
Function .onInit
    ...
FunctionEnd
</highlight-nsis>
or your uninstaller:
<highlight-nsis>
Function un.onInit
    ...
FunctionEnd
</highlight-nsis>


== 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 38: Line 28:
Pop $0
Pop $0
IntCmp $0 0 notRunning
IntCmp $0 0 notRunning
     MessageBox MB_OK|MB_ICONEXCLAMATION "MyApp is running (foreground window). Please close it first" /SD IDOK
     MessageBox MB_OK|MB_ICONEXCLAMATION "MyApp is running (see foreground window). Please close it first" /SD IDOK
     Abort
     Abort
notRunning:
notRunning:
</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.
If you can, make sure you choose a unique name for the application mutex, typically generated with the GuidGen tool. ex: <code>"MyApp-{5A60A807-D40C-4554-935A-E570B818DF75}"</code>


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.
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:
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
    Abort
notRunning:
</highlight-nsis>
 
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:
<highlight-nsis>
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
     MessageBox MB_OK|MB_ICONEXCLAMATION "MyApp is running. Please close it first" /SD IDOK
     Abort
     Abort
Line 59: Line 64:
</highlight-nsis>
</highlight-nsis>


Advanced applications may use other Win32 named synchronization objects like an Event or 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 [[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.


Make sure your application executable has an easily identifiable name, or you risk detecting another unrelated application having the same name as yours.
Make sure your application executable has an easily identifiable name, or you risk detecting another unrelated application having the same name as yours.
Line 75: Line 80:
</highlight-nsis>
</highlight-nsis>


== using a window class ==
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. Ergo, this will not work on Windows XP Home Edition.
 
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.
 
Example:
<highlight-nsis>
!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"
</highlight-nsis>
 
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%.<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.


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.
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 Visual Studio tool Spy++ or freeware tool SysTree++ to find the window class of your application.
You can use the Visual Studio tool [http://msdn.microsoft.com/en-us/library/dd460760.aspx Spy++] or a freeware tool like [http://www.nirsoft.net/utils/winlister.html Winlister] or [http://catch22.net/software/winspy WinSpy++] to find the window class of your application.


Examples:
Examples:
Line 88: Line 126:
     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
notRunning:
</highlight-nsis>
</highlight-nsis>
or
or
Line 95: Line 134:
     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
notRunning:
</highlight-nsis>
</highlight-nsis>


== using a registry entry ==
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 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 112: 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