本文介紹幾種使應用一直置於頂層的方法。
問題描述
一般情況下,想要將應用置於頂層,設置其TopMost屬性為true即可。對於多個設置了TopMost屬性的應用,后激活的在上面。
但有的應用,比如全局的快捷操作工具條,它需要在所有應用之上,即使是設置了TopMost的應用。
解決思路
注意:使某個應用永遠不會被其它應用覆蓋,這本身是個偽命題。因為假如有兩個程序(A和B)這樣做,拖動兩個窗口使它們重疊,這兩個窗口中的一個必須在另一個之上,這在邏輯上是互相矛盾的。
所以應該盡量避免這種情況,如果非要這樣做,本文提供如下幾種辦法實現(不要將兩個這樣的應用重疊,否則會不停將置頂)。
首先,該應用程序需要設置其TopMost屬性為true,這樣普通窗口本身就會在它下面。本文主要討論該窗口如何置於設置了TopMost屬性的窗口之上。
方案一:捕獲WM_WINDOWPOSCHANGING消息
我們知道,使用Win32的SetWindowPos接口可以改變窗口的Z Order,可以猜測,當另外一個應用置頂時,我們的應用會改變其Z Order,因此,我們可以嘗試捕獲WM_WINDOWPOSCHANGING消息。
當窗口的大小、位置、Z序改變時,窗口會接收到WM_WINDOWPOSCHANGING消息,我們可以使用WndProc處理窗口消息。當捕獲到該消息時,我們可以嘗試將應用再次置頂。關鍵代碼如下,測試可行,但不確定是否有副作用:
/// <summary>
/// 方案一:捕獲WM_WINDOWPOSCHANGING消息,若無SWP_NOZORDER標志,則置頂
/// </summary>
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case Win32Api.WM_WINDOWPOSCHANGING:
Win32Api.WINDOWPOS wp = (Win32Api.WINDOWPOS)Marshal.PtrToStructure(
lParam, typeof(Win32Api.WINDOWPOS));
if ((wp.flags & Win32Api.SWP_NOZORDER) == 0)
_ = SetTopMostLater(); // 不使用棄元編譯器會發出警告
break;
}
return IntPtr.Zero;
}
private async Task SetTopMostLater()
{
await Task.Delay(300);
var interopHelper = new WindowInteropHelper(this);
Win32Api.SetWindowPos(interopHelper.Handle, Win32Api.HWND_TOPMOST, 0, 0, 0, 0, Win32Api.TOPMOST_FLAGS);
}
方案二:循環置頂
這個是比較容易想到的一個方案,每隔一定的時間給應用設置下TopMost,該方案也是可行的:
/// <summary>
/// 方案二:循環置頂
/// </summary>
/// <returns></returns>
private async Task SetTopMostLoop()
{
while (true)
{
await Task.Delay(2000);
var interopHelper = new WindowInteropHelper(this);
Win32Api.SetWindowPos(interopHelper.Handle, Win32Api.HWND_TOPMOST, 0, 0, 0, 0, Win32Api.TOPMOST_FLAGS);
}
}
方案三:使用鈎子
思考一下,其實大部分情況下,使用鼠標或鍵盤等其它輸入設備才會導致窗口的置頂被搶,因此可以使用全局鈎子捕獲輸入事件,然后進行處理。
該方案是存在瑕疵的,因為存在不使用輸入設備打開某個應用的情況,這種情況下置頂效果就會被新打開的置頂應用搶占。
// 方案三:當鼠標按下時置頂(僅考慮了鼠標)
private void MouseHook_OnMouseActivity(object sender, System.Windows.Forms.MouseEventArgs e)
{
Console.WriteLine("mouse down......");
_ = SetTopMostLater();
}
private MouseHook _mouseHook;
最后,本文是我對該問題想到的一些解決方案,Windows系統的任務管理器可以運行在所有應用的最上層,也許微軟正是考慮到上文提到的偽命題,因此沒有開放該接口吧,了解原理的小伙伴歡迎討論。