如何在安装项目窗口顶部显示自定义窗口表单?

时间:2012-04-09 07:54:52

标签: c# .net visual-studio visual-studio-2010

我有一个通过Visual Studio安装项目部署的应用程序,但是,我必须创建一些自定义Windows窗体来从用户收集一些特定数据。在安装项目(I.E.MSI)部署应用程序文件之后,这些表单显示在应用程序的Installer类的Install()方法中。问题是当我的表单出现时,它们出现在Setup Project的窗口下,而不是屏幕上最顶层的表单。然后,如果用户甚至注意到,则必须通过单击任务栏中的图标来手动关注表单以将其启动。

4 个答案:

答案 0 :(得分:4)

我所要做的就是从进程列表中选择Setup项目的窗口,并在我显示它们时将其设置为自定义表单的所有者。以下是我使用的步骤的细分:

  1. 浏览名为“msiexec”的进程列表,并获取显示Setup .msi窗口的窗口的句柄,该窗口可由MainWindowTitle标识 - 窗口标题。
  2. 您现在拥有此过程的句柄(MainWindowHandle),但您如何使用它?通过带有IWin32Window的参数调用ShowDialog时,可以指定Form的所有者;问题是IWin32Window不允许你设置窗口的句柄。这可以通过使用扩展IWin32Window的包装类来解决。
  3. 最后,您需要做的就是在调用ShowDialog()时设置表单的所有者,例如CustomForm.ShowDialog(new WindowWrapper(process.MainWindowHandle),message等)。
  4. 我自己,我创建了一个方法,将Setup Project的窗口作为WindowWrapper返回,然后在每个Installer类的方法(安装,提交,卸载和回滚)中使用它来设置每个表单和消息框的所有者我是创建

    此外,不要更改自定义表单的任何子表单或消息框的所有者(除了“this”之外),因为它们将由显示它们的自定义表单所有;否则它们将显示在安装项目的窗口上但在自定义表单下,而不是我们想要的。

        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;
        }
    

答案 1 :(得分:1)

选择列表的第一个元素并不是一个好习惯,下面的代码经过测试和工作。

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;
}

答案 2 :(得分:0)

使用以下代码。此代码专注于您的表单

    [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();

    }

答案 3 :(得分:0)

MSDN解释了如何将表单放在前面并将其保留在前面。

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