簡體   English   中英

如何在安裝項目 window 之上引入自定義 window 表單?

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

我有一個正在通過 Visual Studio 安裝項目部署的應用程序,但是,我必須創建一些自定義 Windows Forms 來從用戶那里收集一些特定數據。 這些 forms 顯示在應用程序安裝程序 class 的 Install() 方法中,就在安裝項目(即 MSI)部署應用程序文件之后。 問題是,當我的 forms 出現時,它們出現在安裝項目的 window 下面,而不是屏幕上最頂層的窗體。 如果用戶甚至注意到它,則必須通過單擊任務欄中的圖標來手動聚焦該表單以將其調出。

我所要做的就是從進程列表中選擇安裝項目的 window,並將其設置為我正在顯示的自定義 forms 的所有者。 以下是我使用的步驟的細分:

  1. Go 通過名為“msiexec”的進程列表,獲取顯示 Setup.msi 的進程的句柄 window,可通過 MainWindowTitle 識別——window 的標題。
  2. 您現在有了這個進程的句柄 (MainWindowHandle),但是如何使用它呢? 您可以在通過采用 IWin32Window 的參數調用 ShowDialog 時指定窗體的所有者; 問題是 IWin32Window 不允許您設置 window 的句柄。這可以通過使用擴展 IWin32Window 的包裝器 class 來解決。
  3. 所以最后,您所要做的就是在調用 ShowDialog() 時設置表單的所有者,例如 CustomForm.ShowDialog(new WindowWrapper(process.MainWindowHandle), message 等)。

我自己創建了一個方法,將安裝項目的 window 作為 WindowWrapper 返回,然后在每個 Installer 類的方法(安裝、提交、卸載和回滾)中使用它來設置我正在創建的每個表單和消息框的所有者。

另外,不要更改任何子 forms 的所有者或自定義 forms 的消息框(除了“this”),因為它們將由顯示它們的自定義 forms 擁有; 否則它們將顯示在安裝項目的 window 之上,但在自定義 forms 之下,這不是我們想要的。

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

選擇列表的第一個元素不是好的做法,下面的代碼已經過測試並且可以正常工作。

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

使用以下代碼。 此代碼專注於您的表單

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

    }

MSDN 解釋了如何將表單置於最前面並將其保持在最前面。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM