【问题标题】:how to bring custom window form on top of setup project window?如何将自定义窗口窗体置于设置项目窗口之上?
【发布时间】:2012-04-09 07:54:52
【问题描述】:

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

【问题讨论】:

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


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    选择列表的第一个元素不是好的做法,下面的代码已经过测试并且可以工作。

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-08-25
          • 2012-05-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多