Click here to Skip to main content
11,928,352 members (46,438 online)
Click here to Skip to main content
Add your own
alternative version


27 bookmarked

Prompt user to close applications on install/uninstall with WIX custom action

, 16 Aug 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
When you need to prompt user to close certain aplications on install/uninstall.


One of my projects needed to check if particular processes and applications are not running during installation and uninstallation. Also I wanted to ask a user to close them, not terminating them automatically. I looked through WIX documentation and found CloseApplication element in Util Extensions, which should do exactly what I wanted. But it looks like it is not trivial to get functionality I wanted using this element. Searching the web I found a number of unresolved issues with this element. Eventually I decided to make my own CustomAction ClosePrompt, which is generalized and can be reused in any WIX project without changes. 


This solution allows to: 

  • parameterize what processes to look for in WXS script
  • if defined processes are running during install/uninstall dialog message will ask user to close application
  • if blocking application is closed dialog message is closed automatically and process continues

Custom action ClosePrompt forces user to close predefined applications on Install

and Uninstall 

Using the code 

You can add project to your solution or use built library ClosePromptCA.CA.dll in WXS script.

WXS script

To prompt user to close application you need to set two properties under the Product node - PromptToCloseProcesses and PromptToCloseDisplayNames. First one holds list of names of the processes to close and second one - list of corresponding display names. Make sure you provide right process names, e.g. for notepad it will be "notepad", not "notpad.exe".
<Property Id="PromptToCloseProcesses" Value="Process1,Process2,Process3" />
<Property Id="PromptToCloseDisplayNames" Value="Application name1,Application name2,Application name3" /> 

Then you should set up CustomAction. Instead of $(var.BuildOutputDir)ClosePromptCA.CA.dll you should provide right path to ClosePromptCA.CA.dll. Launching custom action after CostFinalize makes checking right after Install button is pressed.

 <Binary Id="ClosePromptBinary" SourceFile="$(var.BuildOutputDir)ClosePromptCA.CA.dll" />
<CustomAction Id="CloseAppsPrompt" BinaryKey="ClosePromptBinary" 
         DllEntry="ClosePrompt" Return="check" />
  <Custom Action="CloseAppsPrompt" After="CostFinalize"></Custom>

Custom Action 

This implementation of close prompt shows dialogs for each of the processes that are blocking execution. You may want to show only one dialog window holding a list of blocking applications. It will require small redesign but conceptually is the same. 


ClosePrompt is an entry point for custom action. After making two arrays code iterates through processes and calls prompt of corresponding PromptCloseApplication object. If the process is not running or user closed and application true is returned, otherwise if user rejected closing false is returned and custom action returns failure. 

public class CustomActions
    public static ActionResult ClosePrompt(Session session)
        session.Log("Begin PromptToCloseApplications");
            var productName = session["ProductName"];
            var processes = session["PromptToCloseProcesses"].Split(',');
            var displayNames = session["PromptToCloseDisplayNames"].Split(',');

            if (processes.Length != displayNames.Length)
                session.Log(@"Please check that 'PromptToCloseProcesses' and" + 
                  @" 'PromptToCloseDisplayNames' exist and have same number of items.");
                return ActionResult.Failure;

            for (var i = 0; i < processes.Length; i++)
                session.Log("Prompting process {0} with name {1} to close.", processes[i], displayNames[i]);
                using (var prompt = new PromptCloseApplication(productName, processes[i], displayNames[i]))
                    if (!prompt.Prompt())
                        return ActionResult.Failure;
        catch(Exception ex)
            session.Log("Missing properties or wrong values. Please check that" + 
              " 'PromptToCloseProcesses' and 'PromptToCloseDisplayNames' exist and have " + 
              "same number of items. \nException:" + ex.Message);
            return ActionResult.Failure;

        session.Log("End PromptToCloseApplications");
        return ActionResult.Success;


If process is running dialog should be shown. As you can see ClosePromptForm windows form is created and after it code searches for appropriate owner of this dialog form. If we do not provide any owner when dialog is shown, installer window and dialog form will be independent and user may not even notice the dialog window. So code searches for right handle and then it is used when dialog is shown _form.ShowDialog(new WindowWrapper(_mainWindowHanle)).

When you install the product MSI installer window will be called like product name with " Setup" after it, when product is being uninstalled uninstaller window is called exactly like product name, but in this case window class is dialog and its class name will always be "#32770". Using FindWindow API function code finds appropriate window handle for both install/uninstall cases. 

Also code uses a timer so if user closes the blocking application, dialog window will be closed and installation/uninstallation will continue. User can close dialog window in this case process will exit with failure.

public class PromptCloseApplication : IDisposable
    private readonly string _productName;
    private readonly string _processName;
    private readonly string _displayName;
    private System.Threading.Timer _timer;
    private Form _form;
    private IntPtr _mainWindowHanle;

    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    public PromptCloseApplication(string productName, string processName, string displayName)
        _productName = productName;
        _processName = processName;
        _displayName = displayName;

    public bool Prompt()
        if (IsRunning(_processName))
            _form = new ClosePromptForm(String.Format("Please close running " + 
                "instances of {0} before running {1} setup.", _displayName, _productName));
            _mainWindowHanle = FindWindow(null, _productName + " Setup");
            if (_mainWindowHanle == IntPtr.Zero)
                _mainWindowHanle = FindWindow("#32770", _productName);

            _timer = new System.Threading.Timer(TimerElapsed, _form, 200, 200);

            return ShowDialog();
        return true;

    bool ShowDialog()
        if (_form.ShowDialog(new WindowWrapper(_mainWindowHanle)) == DialogResult.OK)
            return !IsRunning(_processName) || ShowDialog();
        return false;

    private void TimerElapsed(object sender)
        if (_form == null || IsRunning(_processName) || !_form.Visible)
        _form.DialogResult = DialogResult.OK;

    static bool IsRunning(string processName)
        return Process.GetProcessesByName(processName).Length > 0;

    public void Dispose()
        if (_timer != null)
        if (_form != null && _form.Visible)


Form has some properties set like size, minbox, maxbox etc. to look like normal dialog window. Also it has simple initialization with text message and handler for button click, which sets dialog result:  

public partial class ClosePromptForm : Form
    public ClosePromptForm(string text)
        messageText.Text = text;

    private void OkButtonClick(object sender, EventArgs e)
        DialogResult = DialogResult.OK;

Points of Interest

As I wrote above you can use it differently, providing the whole list of processes to be closed in one dialog window or allow to kill all blocking processes from this dialog. So you can customize it further for your needs. 


  • 08/16/2013: Processes check improved. Sources are on github, see link at the top.  
  • 05/16/2013: Comment about processes names added.  
  • 04/26/2013: Initial version. 


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Ivan Leonenko
Software Developer (Senior) Enkata
Russian Federation Russian Federation
More than 7 years of software design and development.

Specialties: C#, .NET 2 3.5 4, WPF (MVVM), Silverlight, ASP.NET, WCF, Javascript, Flash, ActionScript, SQL, WinAPI, Powershell

My blog

You may also be interested in...

Comments and Discussions

QuestionNote on Value for "PromptToCloseProcess" <Property> Pin
Member 1170341319-May-15 11:04
memberMember 1170341319-May-15 11:04 
GeneralVery useful. Excellent documentation. Pin
Monte Christo30-Sep-14 23:16
memberMonte Christo30-Sep-14 23:16 
QuestionLocalization Pin
mrp1417-Dec-13 4:52
membermrp1417-Dec-13 4:52 
AnswerRe: Localization Pin
Ivan Leonenko23-Feb-14 21:59
memberIvan Leonenko23-Feb-14 21:59 
QuestionWorked for me Pin
davton15-Aug-13 4:51
memberdavton15-Aug-13 4:51 
AnswerRe: Worked for me Pin
Ivan Leonenko16-Aug-13 5:16
memberIvan Leonenko16-Aug-13 5:16 
QuestionOne suggestion Pin
jaseRR16-May-13 3:30
memberjaseRR16-May-13 3:30 
AnswerRe: One suggestion Pin
Ivan Leonenko16-May-13 3:34
memberIvan Leonenko16-May-13 3:34 
QuestionCan't get it to work - Fixed. Thanks and 5 stars Pin
jaseRR15-May-13 22:57
memberjaseRR15-May-13 22:57 
AnswerRe: Can't get it to work Pin
Ivan Leonenko15-May-13 23:31
memberIvan Leonenko15-May-13 23:31 
GeneralRe: Can't get it to work Pin
jaseRR16-May-13 0:54
memberjaseRR16-May-13 0:54 
GeneralRe: Can't get it to work Pin
Ivan Leonenko16-May-13 1:19
memberIvan Leonenko16-May-13 1:19 
GeneralRe: Can't get it to work Pin
jaseRR16-May-13 3:27
memberjaseRR16-May-13 3:27 
GeneralRe: Can't get it to work Pin
Ivan Leonenko16-May-13 3:37
memberIvan Leonenko16-May-13 3:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.151126.1 | Last Updated 16 Aug 2013
Article Copyright 2013 by Ivan Leonenko
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid