2

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

4

4 回答 4

4

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

  1. 浏览名为“msiexec”的进程列表,并获取显示 Setup .msi 窗口的进程的句柄,可通过 MainWindowTitle(窗口标题)识别。
  2. 你现在有了这个进程的句柄(MainWindowHandle),但是你如何使用它呢?您可以在调用 ShowDialog 时通过采用 IWin32Window 的参数指定 Form 的所有者;问题是 IWin32Window 不允许您设置窗口的句柄。这可以通过使用扩展 IWin32Window 的包装类来解决。
  3. 最后,您所要做的就是在调用 ShowDialog() 时设置表单的所有者,例如 CustomForm.ShowDialog(new WindowWrapper(process.MainWindowHandle), message 等)。

我自己创建了一个方法,它将安装项目的窗口作为 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;
    }
于 2012-04-09T13:00:14.763 回答
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;
}
于 2014-02-06T17:42:41.087 回答
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();

    }
于 2012-04-09T08:04:20.537 回答
0

MSDN 解释了如何将表单置于最前面并将其保持在最前面。

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

于 2012-04-09T08:06:09.393 回答