使用WS_EX_TOPMOST創建或SetWindowPos(hWnd, HWND_TOPMOST, ...)設定的置頂視窗,仍然可以被其他置頂視窗所覆寫
如何能夠優雅地實作視窗置頂后不能被其他頂層視窗覆寫?
WIN10系統自帶的任務管理器中,設定“置于頂層”后無法被其他置頂視窗覆寫(僅WIN10,WIN7會被覆寫),這和我期望的完美吻合,但不知如何實作
之前嘗試過失去焦點時AttachThreadInput其他頂層視窗后,再SetForegroundWindow自己,具體代碼如下:
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TOPWNDTEST));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"TopWndTest";
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
RegisterClassExW(&wcex);
HWND hWnd = CreateWindowExW(WS_EX_TOPMOST, L"TopWndTest", L"這是一個置頂視窗", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 600, 400, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
return FALSE;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KILLFOCUS:
{
HWND hForegdWnd = GetForegroundWindow();
DWORD dwForegdWndThreadID = GetWindowThreadProcessId(hForegdWnd, NULL);
DWORD dwThreadID = GetCurrentThreadId();
AttachThreadInput(dwThreadID, dwForegdWndThreadID, TRUE);
SetForegroundWindow(hWnd);
SetFocus(hWnd);
AttachThreadInput(dwThreadID, dwForegdWndThreadID, FALSE);
break;
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
但上述方法有較多局限且不夠優雅:1、影響其他視窗訊息;2、效率低拖動時閃爍;3、仍然會被WIN10任務管理器覆寫
有沒大牛對此有研究過,指點一二
uj5u.com熱心網友回復:
定時器 不斷的置頂uj5u.com熱心網友回復:
這方法和失去焦點時不斷置頂本質一樣,而且沒必把代碼放定時器
不使用AttachThreadInput,SetForegroundWindow經常失敗
uj5u.com熱心網友回復:
void CDlg9Dlg::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
::SetWindowPos(m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
CDialog::OnTimer(nIDEvent);
}
uj5u.com熱心網友回復:
會不會是系統對那一個表單“特殊照顧”而實作的結果呢?畢竟用真正“TOPMOST”的只能有一個,通過創建時標識或“后期”API置頂,
那些視窗都是“平級”的,這時自然就是哪個表單擁有焦點、哪個表單就會到“最頂層”。
uj5u.com熱心網友回復:
不行,只這樣設定沒什么效果,視窗雖然被其他置頂視窗覆寫,但他本身是置頂的狀態仍然沒變,所以再設定也是沒用的
uj5u.com熱心網友回復:
就目前公開的API來看好像確實是這樣
uj5u.com熱心網友回復:
只想問問強悍的程式開2個,肯定有一個不強悍了吧。還是定時器大法好uj5u.com熱心網友回復:
只需要比普通置頂的視窗強悍就行,比上不足比下有余就行,畢竟現在的普通置頂視窗很多
uj5u.com熱心網友回復:
我覺得你真想達到那種“預期效果”,可能只有用“耍流氓”的手法了,
簡單易實作,只是“手感”(就是“用戶體驗”)可能有時候稍差點。
其實就“瘟到死系統”來說,它的“置頂”也并不是完全可靠的:
在“視窗模式”(就是沒有“全屏”的程式)下,WinXP、Win7,我都遇到過很多回了,
象“記事本”之流的普通視窗,有時候莫明其妙的能蓋住“任務欄”,
具體怎么出現的我也記不太清楚了,反正遇到的次數不少……
都是感覺“莫明其妙”的出現、又莫明其妙的恢復正常。
Win10系統我極少使用,目前還沒什么印象,估計也會有同樣問題。
uj5u.com熱心網友回復:
只想問問強悍的程式開2個,肯定有一個不強悍了吧。還是定時器大法好
只需要比普通置頂的視窗強悍就行,比上不足比下有余就行,畢竟現在的普通置頂視窗很多
真正的強制應該是類似windows 8的超級工具列那樣(win+c打開的時鐘/右邊4個按鈕),不屬于視窗管理系統,那才叫置頂。
uj5u.com熱心網友回復:
只想問問強悍的程式開2個,肯定有一個不強悍了吧。還是定時器大法好
只需要比普通置頂的視窗強悍就行,比上不足比下有余就行,畢竟現在的普通置頂視窗很多
真正的強制應該是類似windows 8的超級工具列那樣(win+c打開的時鐘/右邊4個按鈕),不屬于視窗管理系統,那才叫置頂。
沒用過那個,那它用的什么?還是創建視窗時指定了某種STYLE
uj5u.com熱心網友回復:
只想問問強悍的程式開2個,肯定有一個不強悍了吧。還是定時器大法好
只需要比普通置頂的視窗強悍就行,比上不足比下有余就行,畢竟現在的普通置頂視窗很多
我覺得你真想達到那種“預期效果”,可能只有用“耍流氓”的手法了,
簡單易實作,只是“手感”(就是“用戶體驗”)可能有時候稍差點。
其實就“瘟到死系統”來說,它的“置頂”也并不是完全可靠的:
在“視窗模式”(就是沒有“全屏”的程式)下,WinXP、Win7,我都遇到過很多回了,
象“記事本”之流的普通視窗,有時候莫明其妙的能蓋住“任務欄”,
具體怎么出現的我也記不太清楚了,反正遇到的次數不少……
都是感覺“莫明其妙”的出現、又莫明其妙的恢復正常。
Win10系統我極少使用,目前還沒什么印象,估計也會有同樣問題。
WIN10那個任務管理器就真很完美,不知是不是“特殊照顧”,遲點跟一下
uj5u.com熱心網友回復:
試試視窗穿透置頂會咋樣uj5u.com熱心網友回復:
這個我之前研究過,不過后來太忙,沒有繼續弄,應該是系統有特殊處理。我補充一下,Win10任務管理器的置頂實在太牛,別說其他TOP_MOST表單了,就連彈出的開始選單,桌面下方的任務欄,桌面的右鍵彈出選單,通通擋不住他。
而樓主的AttachThreadInput其他頂層視窗方案即使能勉強擋住其他TOP_MOST,也擋不住開始選單。
uj5u.com熱心網友回復:
其實你的SetForegroundWindow是失敗的uj5u.com熱心網友回復:
void CDlg9Dlg::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
::SetWindowPos(m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
CDialog::OnTimer(nIDEvent);
}
不行,只這樣設定沒什么效果,視窗雖然被其他置頂視窗覆寫,但他本身是置頂的狀態仍然沒變,所以再設定也是沒用的
他這個只是一個勁的置頂,你在置頂之前加一句取消置頂,兩句配合用,好像好使
uj5u.com熱心網友回復:
試試視窗穿透置頂會咋樣
你指的是WS_EX_TRANSPARENT嗎?
uj5u.com熱心網友回復:
這個我之前研究過,不過后來太忙,沒有繼續弄,應該是系統有特殊處理。
我補充一下,Win10任務管理器的置頂實在太牛,別說其他TOP_MOST表單了,就連彈出的開始選單,桌面下方的任務欄,桌面的右鍵彈出選單,通通擋不住他。
而樓主的AttachThreadInput其他頂層視窗方案即使能勉強擋住其他TOP_MOST,也擋不住開始選單。
你說的沒錯,普通的置頂視窗是會被開始選單覆寫的,但WIN10任務管理器不會,看來自己要實作不容易
uj5u.com熱心網友回復:
其實你的SetForegroundWindow是失敗的
沒錯,所以加上了AttachThreadInput讓它成功,但也不是百分百成功
uj5u.com熱心網友回復:
case WM_KILLFOCUS:
SetWindowPos(hWnd,HWND_TOPMOST,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE);
uj5u.com熱心網友回復:
除了api還有gui 介面啊。。你呼叫gui介面置頂即可了。。。達到和任務管理器一個級別。。。ahk腳本uj5u.com熱心網友回復:
除了api還有gui 介面啊。。你呼叫gui介面置頂即可了。。。達到和任務管理器一個級別。。。ahk腳本
請問什么GUI介面,沒聽說過,可否說明白一點,試了用ahk腳本置頂視窗,仍然是普通版的置頂呢
uj5u.com熱心網友回復:
uj5u.com熱心網友回復:
能否把滑鼠的焦點范圍控制在你的對話框區域之內
uj5u.com熱心網友回復:
能否把滑鼠的焦點范圍控制在你的對話框區域之內
ClipCursor就可以,但又不是說只能通過滑鼠激活其他視窗
uj5u.com熱心網友回復:
試試視窗穿透置頂會咋樣
你指的是WS_EX_TRANSPARENT嗎?
嗯,
uj5u.com熱心網友回復:
試試看。就是滑鼠,鍵盤,訊息不能獲取了,需要弄個HOOK。再處理uj5u.com熱心網友回復:
試試視窗穿透置頂會咋樣
你指的是WS_EX_TRANSPARENT嗎?
嗯,
這個只是把訊息傳到后面的視窗,對置頂沒用
uj5u.com熱心網友回復:
樓主有什么新發現沒?
uj5u.com熱心網友回復:
樓主有什么新發現沒?
還沒新發現,這問題不一定能完美解決,主要來聽下大家意見
uj5u.com熱心網友回復:
百度搜相關關鍵字。
https://www.baidu.com/s?wd=win10%20任務管理器%20視窗置頂
uj5u.com熱心網友回復:
試試視窗穿透置頂會咋樣
你指的是WS_EX_TRANSPARENT嗎?
嗯,
這個只是把訊息傳到后面的視窗,對置頂沒用
配合使用。置頂的肯定是要用的
uj5u.com熱心網友回復:
矛和盾的問題么?uj5u.com熱心網友回復:
在水區問過樓主了,樓主說還沒解決,好可憐啊。uj5u.com熱心網友回復:
在水區問過樓主了,樓主說還沒解決,好可憐啊。
沒什么好可憐,本來就知道這問題不容易,主要是聽下大家看法
uj5u.com熱心網友回復:
理論上可以(未測驗):建立一個隱藏視窗,設定為自己可見視窗的Parent
周期性(也可以在WM_ACTIVATEAPP中處理)OpenDesktop、EnumDesktopWindows
如果列舉到的視窗可見,(其Parent不是自己的隱藏視窗)將其Parent設定為自己的隱藏視窗
SetWindowPos自己的可見視窗
uj5u.com熱心網友回復:
為什么手機客戶端不能寫博客?uj5u.com熱心網友回復:
理論上可以(未測驗):
建立一個隱藏視窗,設定為自己可見視窗的Parent
周期性(也可以在WM_ACTIVATEAPP中處理)OpenDesktop、EnumDesktopWindows
如果列舉到的視窗可見,(其Parent不是自己的隱藏視窗)將其Parent設定為自己的隱藏視窗
SetWindowPos自己的可見視窗
@DelphiGuy,感謝你提供的思路,此思路基本是我想要的了,雖然沒WIN10任務管理器那么強,但可以完美覆寫普通的置頂視窗,是我希望的效果,但按你的方法目前還有個問題沒解決:
在WIN10測驗,如果進行列舉,對所有列舉到非隱藏視窗設定父視窗后,則會導致直接顯示一個空白桌面(其他什么都看不到,必須重啟explorer行程才能恢復);
我同時測驗了不列舉,只對我已知的另一個置頂視窗進行設定,可完美覆寫該置頂視窗。
是否列舉到的部分視窗不應改變其父視窗,可否再指導一下。
附上我按你思路寫的測驗代碼:
HWND hWndParent;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"TopWndTest";
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
RegisterClassExW(&wcex);
hWndParent = CreateWindowExW(WS_EX_TOPMOST, L"TopWndTest", L"這是一個置頂視窗的父視窗(隱藏)", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 600, 400, nullptr, nullptr, hInstance, nullptr);
if (!hWndParent)
return FALSE;
HWND hWnd = CreateWindowExW(WS_EX_TOPMOST, L"TopWndTest", L"這是一個置頂視窗", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 600, 400, hWndParent, nullptr, hInstance, nullptr);
if (!hWnd)
return FALSE;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
BOOL CALLBACK EnumWindowsProc(_In_ HWND hWnd, _In_ LPARAM lParam)
{
if (IsWindowVisible(hWnd) && GetParent(hWnd) != hWndParent)
{
SetParent(hWnd, hWndParent);
}
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KILLFOCUS:
{
//HWND hForegdWnd = GetForegroundWindow();
//DWORD dwForegdWndThreadID = GetWindowThreadProcessId(hForegdWnd, NULL);
//DWORD dwThreadID = GetCurrentThreadId();
//AttachThreadInput(dwThreadID, dwForegdWndThreadID, TRUE);
//SetForegroundWindow(hWnd);
//SetFocus(hWnd);
//AttachThreadInput(dwThreadID, dwForegdWndThreadID, FALSE);
break;
}
case WM_ACTIVATEAPP:
{
if (!wParam) //being deactivated
{
HDESK hDesk = OpenDesktop(TEXT("Default"), 0, FALSE, DESKTOP_ENUMERATE);
if (!hDesk)
{
MessageBox(0, TEXT("OpenDesktop Failed!"), 0, 0);
return 0;
}
if (!EnumDesktopWindows(hDesk, (WNDENUMPROC)EnumWindowsProc, 0))
{
MessageBox(0, TEXT("EnumDesktopWindows Failed!"), 0, 0);
return 0;
}
CloseDesktop(hDesk);
//SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}
break;
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
uj5u.com熱心網友回復:
理論上可以(未測驗):
建立一個隱藏視窗,設定為自己可見視窗的Parent
周期性(也可以在WM_ACTIVATEAPP中處理)OpenDesktop、EnumDesktopWindows
如果列舉到的視窗可見,(其Parent不是自己的隱藏視窗)將其Parent設定為自己的隱藏視窗
SetWindowPos自己的可見視窗
不好意思,再經測驗,發現上面說的也不完全準確,離我想要的效果還是差了一點點
按上述代碼,對于普通置頂視窗,如果點擊非標題欄部分,無法覆寫我程式視窗,但如果點擊其標題欄,則可覆寫
uj5u.com熱心網友回復:
我反匯編了一下win10的taskmgr.exe,發現它也用了OpenDesktop和EnumDesktopWindows,估計思路差不多吧,但是taskmgr有很多C++寫的方法,不是純API呼叫,還沒有研究徹底。另外,它用了兩個user32的匯出函式:
(hint = 01E4) GhostWindowFromHungWindow
(hint = 01E9) HungWindowFromGhostWindow
不清楚有什么用
uj5u.com熱心網友回復:
我反匯編了一下win10的taskmgr.exe,發現它也用了OpenDesktop和EnumDesktopWindows,估計思路差不多吧,但是taskmgr有很多C++寫的方法,不是純API呼叫,還沒有研究徹底。
另外,它用了兩個user32的匯出函式:
(hint = 01E4) GhostWindowFromHungWindow
(hint = 01E9) HungWindowFromGhostWindow
不清楚有什么用
這兩個未公開API我查了一下,大概說的是:一個視窗如果沒有回應,會創建一個影子視窗GhostWindow,GetForegroundWindow等函式獲取到的實際是這個影子視窗句柄,而不是實際懸掛起來的視窗句柄,這兩個API就是在兩者之間進行一個轉換。
我再研究一下吧,你有時間也可幫看一下
uj5u.com熱心網友回復:
DedkPins 這個軟體的寫法可能對你有幫助,它是讓其他視窗置頂的,與任務管理器可以相互遮蓋,
在xp下,不能遮蓋開始選單視窗,如果針對開始選單專門處理一下,估計也可以
有原始碼下載
uj5u.com熱心網友回復:
DedkPins 這個軟體的寫法可能對你有幫助,它是讓其他視窗置頂的,
與任務管理器可以相互遮蓋,
在xp下,不能遮蓋開始選單視窗,如果針對開始選單專門處理一下,估計也可以
有原始碼下載
感謝回復,但deskpins實作的只是普通置頂,是覆寫不了WIN10置頂后的任務管理器的
uj5u.com熱心網友回復:
https://bbs.csdn.net/topics/391880391
uj5u.com熱心網友回復:
https://bbs.csdn.net/topics/391880391
感謝回復,切換桌面顯示仍然會被這個桌面其他置頂視窗覆寫了
uj5u.com熱心網友回復:
有沒有用?When a software is not responding,windows automatecaly transforms the window of that program into a ghost window.This means that the user can no longer interact with the that window.The function is FrostCrashedWindow and it is located in user32.dll.
uj5u.com熱心網友回復:
有沒有用?When a software is not responding,windows automatecaly transforms the window of that program into a ghost window.This means that the user can no longer interact with the that window.The function is FrostCrashedWindow and it is located in user32.dll.
It has the following signature:
HWND FrostCrashedWindow(HWND,HWND);
uj5u.com熱心網友回復:
有沒有用?
When a software is not responding,windows automatecaly transforms the window of that program into a ghost window.This means that the user can no longer interact with the that window.The function is FrostCrashedWindow and it is located in user32.dll.
It has the following signature:
HWND FrostCrashedWindow(HWND,HWND);
這個和置頂關系不大,這個只是對沒有回應的視窗變成“霧化”顯示
uj5u.com熱心網友回復:
“”This means that the user can no longer interact with the that window.“”視窗 被 凍結 !
uj5u.com熱心網友回復:
“”This means that the user can no longer interact with the that window.“”
視窗 被 凍結 !
但我想要的是用戶仍然能夠和視窗進行互動
uj5u.com熱心網友回復:
截圖,列印在一張透明貼紙上,再貼在螢屏上!
uj5u.com熱心網友回復:
還沒新發現,這問題不一定能完美解決,主要來聽下大家意見uj5u.com熱心網友回復:
想到的是把欲置頂視窗區域的重繪訊息處理掉uj5u.com熱心網友回復:
想到的是把欲置頂視窗區域的重繪訊息處理掉
uj5u.com熱心網友回復:
只想問問強悍的程式開2個,肯定有一個不強悍了吧。還是定時器大法好uj5u.com熱心網友回復:
會不會是系統對那一個表單“特殊照顧”而實作的結果呢???畢竟用真正“TOPMOST”的只能有一個,通過創建時標識或“后期”API置頂,? 那些視窗都是“平級”的,這時自然就是哪個表單擁有焦點、哪個表單就會到“最頂層”。uj5u.com熱心網友回復:
是不是與行程 優先度有關 ,提高試試。uj5u.com熱心網友回復:
試一下這個bat檔案:檔案名:dead.bat
while 1:
start
dead
pause
uj5u.com熱心網友回復:
以前想要程式全屏就碰到這個問題,,任務管理器真是強悍。uj5u.com熱心網友回復:
據說任務管理器用的是CreateWindowInBanduj5u.com熱心網友回復:
每個程式都想頂置。·~~都成流氓程式了。其實我感覺 SetWindowPos 夠用。至于被其它程式遮住,一般沒什么辦法。
uj5u.com熱心網友回復:
我試過,放大鏡可以遮住任務管理器uj5u.com熱心網友回復:
A ghost window replaces a window that hasn't called Get or PeekMessage for 5 seconds, in other words a hung window. It displays a bitmap of the hung window's client areauj5u.com熱心網友回復:
我試過,放大鏡可以遮住任務管理器
厲害,這都被你發現了,而且這個置頂感覺也沒什么必要啊,任務管理器置頂后雖然顯示是置頂了,但是當你打開其他新視窗后,焦點已經在新打開的視窗了。
uj5u.com熱心網友回復:
onpaint的時候置頂,只要被遮蓋就會引起重繪轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/31958.html
標籤:基礎類
