標籤:getactivewindow setforegroundwindow
問題引出:
今天看了一下 深入解析MFC,第40頁到第50頁之間,有一句
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M02/2F/47/wKioL1OexYmisi79AAG7Pp4DiLM527.jpg" title="1.jpg" alt="wKioL1OexYmisi79AAG7Pp4DiLM527.jpg" />
這裡面說到了一個函數,,, GetActiveWindow,,根據直觀意思很容易理解 就是擷取 使用中視窗的控制代碼,但是什麼才是使用中視窗。
Retrieves a pointer to the active window.
//擷取一個指向使用中視窗的CWnd指標。
|
static CWnd* PASCAL GetActiveWindow( ); |
Return Value //傳回值
The active window or NULL if no window was active at the time of the call. The pointer may be temporary and should not be stored for later use.
//返回使用中視窗的控制代碼,如果在調用這個函數時沒有使用中視窗,則返回NULL。這個指標可能是指向臨時對象 因此不要 將它儲存起來以備後來之用。
Remarks//說明
The active window is either the window that has the current input focus or the window explicitly made active by the SetActiveWindow member function.
//使用中視窗是 擁有輸入焦點的視窗。顯示使用 SetActiveWindow 這個函數來指定的視窗也是使用中視窗。
上面是MSDN中給出的解釋 ,我做了簡單的翻譯。其實軟體技術本身並不複雜,複雜的是我們弄不清楚概念。但是上面 說明處 也給出了概念。那麼我們就來測試一下吧。
用VS2008 建立一個MFC 對話方塊程式。
然後再上面 對話方塊上放 2 個 編輯框(這樣就可以有輸入焦點了)。
然後 在 OnPaint 中添加如下代碼
void CTestDlg::OnPaint(){ if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // Center icon in client rectangle int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // Draw the icon dc.DrawIcon(x, y, m_hIcon); } else { //新添加 HWND hwndEdit1 = ((CEdit *)GetDlgItem(IDC_EDIT1))->m_hWnd; HWND hwndEdit2 = ((CEdit *)GetDlgItem(IDC_EDIT2))->m_hWnd; CString str; str.Format("edit1:%x edit2:%x dlg:%x",hwndEdit1,hwndEdit2,m_hWnd); CPaintDC dc(this); dc.TextOut(0,0,str); CDialog::OnPaint(); }}
其實功能就是在 對話方塊的客戶區上輸出 兩個編輯框和主對話方塊的控制代碼值。
運行效果如下:
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M02/2F/4A/wKiom1Oe0ByxAzZIAABfNXFrqU4895.jpg" title="1.jpg" alt="wKiom1Oe0ByxAzZIAABfNXFrqU4895.jpg" />
下面我們應該如何擷取一下 使用中視窗的控制代碼呢,就要用那個函數了,,我們來試試,問題是在哪裡使用這個函數呢?
因為要輸入焦點,而我們還想要切換焦點,所以,我想到了計時器,我們啟動一個計時器,比如10秒一次,在這裡面來調用函數。
在 BOOL CTestDlg::OnInitDialog() 裡面加上 SetTimer(1,10000,NULL);
為對話方塊添加 WM_TIMER 訊息響應函數
void CTestDlg::OnTimer(UINT_PTR nIDEvent){ // TODO: Add your message handler code here and/or call default switch (nIDEvent) { case 1: { CWnd * pWnd = CWnd::GetActiveWindow(); ASSERT(pWnd != NULL); CString str; str.Format("%x",pWnd->m_hWnd); AfxMessageBox(str); } break; default: break; } CDialog::OnTimer(nIDEvent);}
這樣子,我們的程式會每隔10秒鐘就彈出一個值,我們來測試一下,在測試過程中,我們可以對比彈出的值與對話方塊中輸出的三個值的關係,我們還可以點擊兩個編輯框,以讓它們擷取輸入焦點。
如果不出意外,那麼這個彈出的值會是 對話方塊的控制代碼,那麼什麼時候會出意外呢,就是你把其它視窗點開了,比如你切換到了瀏覽器視窗或者其它應用程式。這個時候 程式會出現 斷言錯誤,就是
ASSERT(pWnd != NULL);
這時候,pWnd擷取的值是 NULL。
這裡很奇怪。
1、為什麼總是 對話方塊的控制代碼呢
2、為什麼一切換到其它視窗就命中斷言了呢
我們去看一下這個函數的實現吧。
_AFXWIN_INLINE CWnd* PASCAL CWnd::GetActiveWindow()
{ return CWnd::FromHandle(::GetActiveWindow()); }
這是先調用API 函數 GetActiveWindow ,以擷取 控制代碼值,然後通過控制代碼值檢索對象或者構造臨時對象。
那麼我們就去看一下 這個API 的說明吧。
The GetActiveWindow function retrieves the window handle to the active window attached to the calling thread‘s message queue.
// 函數 GetActiveWindow 擷取 關聯到 調用線程的訊息佇列 的 使用中視窗的控制代碼值。
Syntax
HWND GetActiveWindow(VOID);
Return Value
The return value is the handle to the active window attached to the calling thread‘s message queue. Otherwise, the return value is NULL.
//意思跟上面差不多,但這裡我要說明一點知識,那就是一個線程只有一個訊息佇列,一個線程可以建立多個視窗,一個線程最多有一個使用中視窗,也可能沒有,明白這一點後你再看這點就可以理解了。
Remarks
To get the handle to the foreground window, you can use GetForegroundWindow.
Windows 98/Me and Windows NT 4.0 SP3 and later: To get the window handle to the active window in the message queue for another thread, use GetGUIThreadInfo.
//如果要擷取前台視窗的控制代碼,你應該使用 GetForegroundWindow 。
//如果要擷取其它線程中的使用中視窗,你應該使用 GetGUIThreadInfo。
好了,說了這麼多,我們終於有點眉目了。
雖然編輯框也是視窗,但這個函數中說明的視窗是指
Overlapped Windows 重疊的視窗
Pop-up Windows 快顯視窗
,而不是
其實視窗還有另外兩種類型
好了,這樣我們就明白了,編輯框是視窗不假,但是它是子視窗,而且在MFC對這個函數的說明不是太明確。
現在也就是 如果 我們再開一個背景工作執行緒,在這裡來 用 GetActiveWindow ,這是肯定不行的,因為我們的這個背景工作執行緒 自身 並沒有建立視窗。當我們啟用一個視窗時,就是在工作列點擊一個視窗程序,這個時候這個程式就擁有了輸入焦點,它就是使用中視窗。
還是之前的例子,我們在 程式的 標題列上右擊,選擇關於 ,此時會再彈出一個模態對話方塊,如
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M01/2F/4A/wKioL1Oe10rwMACyAADXu9lpi1M613.jpg" title="1.jpg" alt="wKioL1Oe10rwMACyAADXu9lpi1M613.jpg" />
可以看出不再是主對框了,因為在沒有彈出Test 那個對話方塊之前,About Test 才是 這個線程 中的 使用中視窗。如果再等 幾十秒(不要點確定),會發現 他還會彈出 提示框,而且每次的結果都不一樣,
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M00/2F/4A/wKioL1Oe2BKiMbNWAADtWYmvatI093.jpg" title="1.jpg" alt="wKioL1Oe2BKiMbNWAADtWYmvatI093.jpg" />
這是因為當第一個提示框彈出後,它就變成了使用中視窗了,第2個彈出的就是第一個的控制代碼了,第3個彈出的就是第二個的控制代碼了。
當我們一切到其它視窗的時候,程式會因為斷言宏 的問題 而中斷。
還有一個問題 就是,我們可以開一個線程,來擷取 其它線程中的 使用中視窗。
定義一個線程
UINT __cdecl Worker( LPVOID pParam )
{
CTestDlg * pDlg = (CTestDlg *)pParam;
ASSERT(pDlg != NULL);
DWORD tid = GetWindowThreadProcessId(pDlg->m_hWnd,NULL);
GUITHREADINFO guiTI = {0};
guiTI.cbSize = sizeof(GUITHREADINFO);
GetGUIThreadInfo(tid,&guiTI);
CString str;
str.Format("%x\n",guiTI.hwndActive);
AfxMessageBox(str);
return 0;
}
然後將 計時器 中 換成
switch (nIDEvent)
{
case 1:
{
AfxBeginThread(Worker,(LPVOID)this);
}
break;
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M02/2F/4A/wKioL1Oe2n2TFqduAAHVx9oPFX4044.jpg" title="1.jpg" alt="wKioL1Oe2n2TFqduAAHVx9oPFX4044.jpg" />
這樣子依舊可以擷取 其 使用中視窗,當然可以利用這種方式 來擷取 其它 軟體 的 使用中視窗哦。
如果還有不明白的地方,那就是前台視窗 與 使用中視窗 這兩個概念的 區別了。
不妨來做個測試。
我們隨便寫個小按鈕響應事件
//點擊開始菜單,點擊運行,,輸入 notepad 斷行符號即可
HWND hwndNotepad = ::FindWindow(NULL,"無標題 - 記事本");
ASSERT(hwndNotepad != NULL);
::SetForegroundWindow(hwndNotepad);
功能就是將記事本 置成前台,我們發現 記事本 果然就 跑前面來了,而且也被啟用了。
那麼它們到底有什麼區別嗎?
1、設定都沒有什麼好說的
2、擷取,一個是針對某一線程進行擷取,一個是針對整個系統中的所有視窗進行擷取
3、反上去,其它設定也是範圍不同,一個針對線程,另一個針對系統。
什麼時候應該哪個,要取決於你具體的目的是什麼,然後再做一個判斷。
其實說到這裡應該結束了,但是其實還有一點,就是如何 在一個線程中,啟用另一個線程中的 視窗,說白了就是 線程A 沒有視窗,線程B有3個視窗,我想用A 啟用B中的 2號視窗,怎麼辦。 擷取是 使用的GetGUIThreadInfo ,設定 可沒有 SetGUIThreadInfo 這個函數的,
這個時候 ,我們應該行看看MSDN中有沒有辦法
By using the AttachThreadInput function, a thread can attach its input processing to another thread. This allows a thread to call SetActiveWindow to activate a window attached to another thread‘s message queue.
可以看到 可以使用 AttachThreadInput 這個函數可以將 一個線程 的輸入處理 關聯到 另一個線程,進而 完成 設定 其它線程中的 使用中視窗的 目的。
可能有的人還不清楚 一個程式到底有多少個視窗,哈哈,這個是初學者最不容易理解的地方,一個程式可以有多個視窗的,舉一個例子吧,VS2008 等類似的軟體有吧,,知道怎麼設定行號嗎,知道怎麼改變字型大小嗎,知道怎麼建立工程嗎,知道怎麼添加檔案嗎,好吧,這些功能,每一個都是一個對話方塊哦,再比如,一個記事本程式,裡面有搜尋功能吧,這也是一個對話方塊,還有 最後一個例子,
直接
650) this.width=650;" src="http://s3.51cto.com/wyfs02/M00/2F/4A/wKiom1Oe4C3jiNAyAANCwasOPU0730.jpg" title="1.jpg" alt="wKiom1Oe4C3jiNAyAANCwasOPU0730.jpg" />
好了,我想大家對使用中視窗應該理解了吧。。。。
本文出自 “千千闕歌” 部落格,請務必保留此出處http://qianqianquege.blog.51cto.com/8004200/1427059