none
Windows Forms won't appear on top of Setup Project window

    Question

  • I have an application that is being deployed via a Visual Studio Setup Project, however, I had to create some custom Windows Forms to collect some specific data from the user. These forms are shown in the Commit() method of the application's Installer class, right after application files have been deployed by the Setup Project (I.E. the MSI). The problem is that when my forms appear, they appear under the Setup Project's window rather than being the topmost form on the screen. The form then has to be focused on manually by clicking on its icon in the taskbar to bring it up, if the user even notices it.

    I'm using the ShowDialog() method to show the forms. I've tried setting TopMost to true, using BringToFront(), using Focus(), but none of these brought the form over the Setup Project's window. I've also experimented with a suggestion I've found online that uses user32.dll and a method like the following to try to force the form to the top; unfortunately, this didn't work either.

    SetWindowPos(form.Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);

    I'd be grateful for any advice you can give. Thank you.
    • Moved by Jing0Moderator Thursday, March 18, 2010 5:52 AM (From:Windows Forms General)
    Thursday, March 11, 2010 7:18 AM

Answers

  • The problem's been resolved, according to Ciaran's suggestion. All I had to do was pick the Setup project's window from the list of processes and set it as the owner of the custom forms as I was displaying them. Here's a breakdown of the steps I used:

    1) Go through the list of processes named "msiexec", and get the handle of the one displaying the Setup .msi's window, identifiable by the MainWindowTitle - the title of the window. This step actually gets at the root of the problem, since the Setup project and Installer class (running when InstallUtil.exe gets called by Setup) are separate processes, and thus, any forms displayed in the latter won't be children of the former and consequently won't pop over it.

    2) You now have the handle of this process (MainWindowHandle), but how do you use it? You can specify the owner of a Form when you're calling ShowDialog through the parameter that takes an IWin32Window; the problem is that IWin32Window doesn't let you set the handle of the window. This is worked around by using a wrapper class which extends IWin32Window, as done here .

    3) So finally, all you have to do is set the owner of the form as you're calling ShowDialog(), with something like CustomForm.ShowDialog(new WindowWrapper(process.MainWindowHandle), message, etc.). Myself, I made a method which returns the Setup Project's window as a WindowWrapper, and then used that in each of the Installer class's methods (Install, Commit, Uninstall and Rollback) to set the owner of every form and messagebox I am creating.

    Of note is that this has to be done for messageboxes as well, not just forms, as they will also pop under the .msi's window. Also, don't change the owners of any child forms or messageboxes of your custom forms (except maybe to "this"), as they'll be owned by the custom forms that show them; otherwise they'll show up over the Setup project's window but under the custom forms, not that we desired.

    I've also found that setting the Owner property of the Form doesn't seem to solve this problem, since you need to cast to a Form, so I'm not sure how to use the window handle to set the Owner property, but the ShowDialog way works well enough.

    Thanks again for all your input, especially to Ciaran who put me on the right trail! It looks like this isn't a Windows 7 problem per se but a very logical one. There are multiple instances of the msiexec.exe process, one of which is the Setup project's window, another which runs the actual Installer class via InstallUtil.exe and its Install and Commit methods, and there's no way for these processes to know of each other. Since InstallUtil doesn't have a window, the owner of any forms created in it will be null. I suppose the window handle of the setup project could be passed as a parameter to InstallUtil.exe just like any other custom action, but I don't think an Installer class can have an Owner that is an IWin32Window.

    • Marked as answer by Palatinus Tuesday, March 23, 2010 3:32 AM
    Tuesday, March 23, 2010 3:32 AM
  • This explains windows being on top of others. Some of the answers on this question are quite helpful.
    http://stackoverflow.com/questions/51150/why-does-clicking-a-child-window-not-always-bring-the-application-to-the-foregrou
    after reading that, you might be able to get the HWND of the install wizard and set the owner of your form.

    HTH Ciaran http://wannabedeveloper.spaces.live.com
    • Edited by CiaranODonnell Thursday, March 11, 2010 10:09 AM Set the link properly
    • Marked as answer by Palatinus Tuesday, March 23, 2010 2:54 AM
    Thursday, March 11, 2010 10:08 AM

All replies

  • This explains windows being on top of others. Some of the answers on this question are quite helpful.
    http://stackoverflow.com/questions/51150/why-does-clicking-a-child-window-not-always-bring-the-application-to-the-foregrou
    after reading that, you might be able to get the HWND of the install wizard and set the owner of your form.

    HTH Ciaran http://wannabedeveloper.spaces.live.com
    • Edited by CiaranODonnell Thursday, March 11, 2010 10:09 AM Set the link properly
    • Marked as answer by Palatinus Tuesday, March 23, 2010 2:54 AM
    Thursday, March 11, 2010 10:08 AM
  • Hi,

     

    Based on my understanding, it’s not able to show a form just like dialog form in custom action to setup project. You’d better run another form to custom program config after install.

     

    There is another similar thread about this. PhilWilson gave a detailed explanation.

    http://social.msdn.microsoft.com/Forums/en-US/winformssetup/thread/8b46a62d-5748-4a7f-a1b7-3e822803e2d7

     

    Hope this helps.

     

    Best regards,

    Ling Wang


    Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This can be beneficial to other community members reading the thread.
    Thursday, March 18, 2010 3:49 AM
    Moderator
  • Hello,

    I'm guessing you're either using Vista or Windows7 to test with? Seems to be a common problem. The OS just ignores bringtofront, topmost, etc...

    The only solution is to show/hide forms until MS fixes it.

    Adam
    Dibble and dabble but please don't babble.
    Thursday, March 18, 2010 5:17 AM

  • IMO Microsoft is not going to fix this for a number of reasons. One is that installer classes aren't supported by the Windows Installer team.  Read this:

    http://robmensching.com/blog/posts/2007/4/19/Managed-Code-CustomActions-no-support-on-the-way-and-heres 

    In addition, the architecture of Windows Installer is that you collect all relevant data in the UI sequence up front, not from a Windows Form custom action at the end.  If Visuial Studio setups don't supply enough functionality for you UI needs then use one of the many other tools that generate MSI-based setups.

    The DTF people do some managed code custom action support, maybe their solutions address this area.
    Phil Wilson
    • Proposed as answer by Jing0Moderator Friday, March 19, 2010 5:56 AM
    • Marked as answer by Jing0Moderator Tuesday, March 23, 2010 2:25 AM
    • Unmarked as answer by Palatinus Tuesday, March 23, 2010 2:54 AM
    • Unproposed as answer by Palatinus Tuesday, March 23, 2010 3:33 AM
    Thursday, March 18, 2010 5:49 PM
    Moderator
  • The problem's been resolved, according to Ciaran's suggestion. All I had to do was pick the Setup project's window from the list of processes and set it as the owner of the custom forms as I was displaying them. Here's a breakdown of the steps I used:

    1) Go through the list of processes named "msiexec", and get the handle of the one displaying the Setup .msi's window, identifiable by the MainWindowTitle - the title of the window. This step actually gets at the root of the problem, since the Setup project and Installer class (running when InstallUtil.exe gets called by Setup) are separate processes, and thus, any forms displayed in the latter won't be children of the former and consequently won't pop over it.

    2) You now have the handle of this process (MainWindowHandle), but how do you use it? You can specify the owner of a Form when you're calling ShowDialog through the parameter that takes an IWin32Window; the problem is that IWin32Window doesn't let you set the handle of the window. This is worked around by using a wrapper class which extends IWin32Window, as done here .

    3) So finally, all you have to do is set the owner of the form as you're calling ShowDialog(), with something like CustomForm.ShowDialog(new WindowWrapper(process.MainWindowHandle), message, etc.). Myself, I made a method which returns the Setup Project's window as a WindowWrapper, and then used that in each of the Installer class's methods (Install, Commit, Uninstall and Rollback) to set the owner of every form and messagebox I am creating.

    Of note is that this has to be done for messageboxes as well, not just forms, as they will also pop under the .msi's window. Also, don't change the owners of any child forms or messageboxes of your custom forms (except maybe to "this"), as they'll be owned by the custom forms that show them; otherwise they'll show up over the Setup project's window but under the custom forms, not that we desired.

    I've also found that setting the Owner property of the Form doesn't seem to solve this problem, since you need to cast to a Form, so I'm not sure how to use the window handle to set the Owner property, but the ShowDialog way works well enough.

    Thanks again for all your input, especially to Ciaran who put me on the right trail! It looks like this isn't a Windows 7 problem per se but a very logical one. There are multiple instances of the msiexec.exe process, one of which is the Setup project's window, another which runs the actual Installer class via InstallUtil.exe and its Install and Commit methods, and there's no way for these processes to know of each other. Since InstallUtil doesn't have a window, the owner of any forms created in it will be null. I suppose the window handle of the setup project could be passed as a parameter to InstallUtil.exe just like any other custom action, but I don't think an Installer class can have an Owner that is an IWin32Window.

    • Marked as answer by Palatinus Tuesday, March 23, 2010 3:32 AM
    Tuesday, March 23, 2010 3:32 AM
  • You could possibly paste the code instead of that long description, anyway, if anybody is interested here is the code: Create A IWin32Window Wrapper:

     public class WindowWrapper : System.Windows.Forms.IWin32Window
      {
        public WindowWrapper(IntPtr handle)
        {
          _hwnd = handle;
        }
    
        public IntPtr Handle
        {
          get { return _hwnd; }
        }
    
        private IntPtr _hwnd;
      }

    Then add the following code to your installer class:

    //Get the process Hwnd
            IntPtr hwnd = IntPtr.Zero;
            WindowWrapper wrapper = null;
            Process[] procs = Process.GetProcessesByName("msiexec");
            
            if (null != procs && procs.Length > 0)        
               hwnd = procs[0].MainWindowHandle;
            
            if(hwnd != IntPtr.Zero)
              wrapper = new WindowWrapper(hwnd);
    
            //Set the windows forms owner to setup project so it can be focused and 
            //set infront
            if (null != wrapper)
              [YOUR_FORM].ShowDialog(wrapper);
            else
    //incase the operation failed, no need to fail the whole process, so you can open the form normally
              [YOUR_FORM].ShowDialog();
    
    

    If you would like to debug the code while the setup is in progress add the following line of code where you want the code to break:

    System.Diagnostics.Debugger.Launch();
    • Proposed as answer by chennas Friday, September 28, 2012 10:42 AM
    • Unproposed as answer by chennas Friday, September 28, 2012 10:42 AM
    • Proposed as answer by chennas Friday, September 28, 2012 10:43 AM
    Saturday, April 24, 2010 8:44 PM
  • I've tried finding the msiexec process with my setup package title, and this works for install/commit custom actions, but doesn't work for uninstall action :( . There are 2 msiexec processes, but they both have  MainWindowTitle == "" and MainWindowHandle == 0, although I do see an uninstall window. What is the solution for the uninstall custom action? I am running my setup package under windows 7 .
    Monday, March 07, 2011 4:39 PM
  • You're using the wrong way of gathering user input. Yes I know people hack around until they think it's working but there'll always be a risk that it won't work on some OS version. If you want to show custom forms in an install then use a too that lets you show them properly.  Note that Visual Studio setups of this type are being retired, and that InstallShield LE doesn't offer them!

    There are two issues that will bite you:

    1. Windows is trying hard to keep service code and code running with the system account isolated from the desktop. If you could find the Window of a process running with the system account and send messages to it you'd be doing exactly what Windows is trying to prevent.

    2. A Windows Install uninstall uses no UI apart from progress, so that's another reason why there be no window you can find.

    What is it that you're trying to accomplish?

     


    Phil Wilson
    Monday, March 07, 2011 8:26 PM
    Moderator
  • Yes, I can see that both InstallShield LE and setup projects don't support uninstall dialogs. I am thinking about using WiX, or maybe using Orca to edit the install packages and instert my custom uninstall dialogs there.

    I need to display few yes/no dialogs for the user so he can choose to delete the database, some files, IIS web site and pool, etc. . Also, I might need to ask the user for the database login and password because it may change since the application was installed.

    Tuesday, March 08, 2011 8:52 AM
  • 2. A Windows Install uninstall uses no UI apart from progress, so that's another reason why there be no window you can find.

    But that's also a window. And I'd love to get it's handle.
    Tuesday, March 08, 2011 8:55 AM
  • I found out that passing MessageBoxOptions.DefaultDesktopOnly to the MessageBox.Show method displays the message box above the wizard window.
    Wednesday, March 09, 2011 11:36 AM
  • Hi Yazeed Hamdan

    Your code perfectly worked for me. Thank you.

    Friday, September 28, 2012 10:43 AM
  • To get the setup window during uninstall custom action, see here.
    Wednesday, December 05, 2012 5:19 AM