Friday, January 13, 2012

How to: Force windows forms to appear on top of Setup Project window

Visual Studio is shipped with deployment project that us to create Setup package of their project to be installed in the client’s computer. Most of the time (for simple project requirements), we are good with the options available at the disposal but at times when it comes to more complex user interaction requirement during setup, we have to build our custom windows form to collect required details and then make use of it during the installation.

 

We faced a similar situation and we managed to build our custom form for the setup. The problem we faced is that our custom window is not showing on top of the setup project window. We have tried the following options but none helped.

 

Option 1 //no success

 

myForm.TopMost = True

 

Option 2 //no success

 

myForm.BringToFront();

myForm.Focus();

 

Then we googled and came across the following link: http://social.msdn.microsoft.com/Forums/en-US/winformssetup/thread/bbe69f12-8908-4c65-aa89-1963720d4c11/

Here we got a clue to set the setup window to be the parent of our custom window and also the code to do so as shown below:

 

Step 1: Create the following class in our setup project

 

public class WindowWrapper : System.Windows.Forms.IWin32Window

    {

        public WindowWrapper(IntPtr handle)

        {

            _hwnd = handle;

        }

 

        public IntPtr Handle

        {

            get { return _hwnd; }

        }

 

        private IntPtr _hwnd;

    }

 

Step 2: Just before we showed our custom window, we added the following code:

 

       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)

            myForm.ShowDialog(wrapper);

        else

            //incase the operation failed, no need to fail the whole process, so you can open the form normally

            myForm.ShowDialog();

 

Now, we built our setup project and were happy to see our custom window showing on top. But wait, we were seeing intermittent behaviours that sometimes our custom window is not getting showed on top. Then after closely looking at the processes loaded, we saw 5 msiexec processes running. What??? We were wondering why 5 when we are running only one setup! Then we looked at the Step 2 code and we found that we were always picking up first process among the matching processes no matter how many are there in the procs collection. We then tweaked the Step 2 code to create the WindowWrapper instance as follows:

 

if (null != procs && procs.Length > 0)       

            for (int index = 0; index < procs.Length; index++)

            {

                hwnd = procs[index].MainWindowHandle;

       

                if(hwnd != IntPtr.Zero)

                {

                    wrapper = new WindowWrapper(hwnd);

                    break;

                }

            }

 

Now we built the project again and were ready to run our setup package having our fingers crossed. Bingo! We were getting our custom window on top. We tried a couple more times to make sure and it was now always showing on top. Hope this may help someone.