Talha
Talha

Reputation: 19242

how to bring custom window form on top of setup project window?

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 Install() 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.

Upvotes: 2

Views: 4397

Answers (4)

Talha
Talha

Reputation: 19242

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.
  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.
  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.

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.

    public override void Install(System.Collections.IDictionary stateSaver)
    {
        base.Install(stateSaver);
        try
        {
            ExecuteSqlScript();
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    private void ExecuteSqlScript()
    {
        IntPtr hwnd = IntPtr.Zero;
        WindowWrapper wrapper = null;
        Process[] procs = Process.GetProcessesByName("msiexec");
        if (null != procs && procs.Length > 0)
            hwnd = procs[0].MainWindowHandle;
        wrapper = new WindowWrapper(hwnd);
        //Set the windows forms owner to setup project so it can be focused and
        //set infront
        frmInstance objInstance = new frmInstance();
        if (null != wrapper)
            objInstance.ShowDialog(wrapper);
        else
            objInstance.ShowDialog();
    }

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

        public IntPtr Handle
        {
            get { return _hwnd; }
        }

        private IntPtr _hwnd;
    }

Upvotes: 4

que dal
que dal

Reputation: 149

picking the first element of your list is not good practice, code below is tested and working.

internal static IntPtr InstallerWindow()
{
  IntPtr hwnd = IntPtr.Zero;

  foreach (var proc in Process.GetProcessesByName("msiexec"))
  {
    if (proc.MainWindowHandle == IntPtr.Zero)
      continue;

    if (string.IsNullOrEmpty(proc.MainWindowTitle))
      continue;

    hwnd = proc.MainWindowHandle;
    break;

  }

  return hwnd;
}

Upvotes: 1

Esteval Del Toro
Esteval Del Toro

Reputation: 138

MSDN explains how to bring a form to the front and keep it at the front.

http://msdn.microsoft.com/en-us/library/3saxwsad.aspx

Upvotes: 0

abdul
abdul

Reputation: 157

Use the following code . This code make focus on your form

    [DllImport("user32.dll", SetLastError = true)]
    static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

    // When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
    [DllImport("user32.dll")]
    static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);

    [DllImport("kernel32.dll")]
    static extern uint GetCurrentThreadId();

    /// <summary>The GetForegroundWindow function returns a handle to the foreground window.</summary>
    [DllImport("user32.dll")]
    private static extern IntPtr GetForegroundWindow();

    [DllImport("user32.dll")]
    static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);

    [DllImport("user32.dll", SetLastError = true)]
    static extern bool BringWindowToTop(IntPtr hWnd);

    [DllImport("user32.dll", SetLastError = true)]
    static extern bool BringWindowToTop(HandleRef hWnd);

    [DllImport("user32.dll")]
    static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);  

       myform.Show();         
        uint foreThread = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
        uint appThread = GetCurrentThreadId();
        const uint SW_SHOW = 5;
        if (foreThread != appThread)
        {
            AttachThreadInput(foreThread, appThread, true);
            BringWindowToTop(myform.Handle);
            ShowWindow(objFrmViewer.Handle, SW_SHOW);
            AttachThreadInput(foreThread, appThread, false);
        }
        else
        {
            BringWindowToTop(myform.Handle);
            ShowWindow(myform.Handle, SW_SHOW);
        }
        myform.Activate();

    }

Upvotes: 0

Related Questions