在 Windows 程式設計中,嘗試讀取其他進程視窗(如編輯框、按鈕、下拉方塊)的文字時,直接呼叫 GetWindowText 常會失敗返回空字串。本文深入解釋原因,並示範正確的跨進程取文字方案:SendMessageTimeout(WM_GETTEXT)。
GetWindowText 的「設計限制」MSDN 文件明確指出:
若目標視窗屬於不同進程,GetWindowText**不會**發送WM_GETTEXT訊息。它僅能取得頂層視窗的標題列文字;若視窗無標題(如子控制項),直接返回空字串。這是「設計使然」。
原因在於 GetWindowText 為了防止當前執行緒因跨進程同步呼叫而無限期阻塞(目標進程可能掛起、死鎖),犧牲了功能完整性換取「不會掛住調用者」的安全性。
WM_GETTEXT 訊息繞過 GetWindowText 的封裝,直接向目標視窗發送訊息:
// C++ / MFC 風格
CWnd* pWnd = GetOtherAppWindow(); // 取得目標 HWND
TCHAR buf[512];
pWnd->SendMessage(WM_GETTEXT, sizeof(buf)/sizeof(TCHAR), (LPARAM)buf);
或使用更安全的超時版本(推薦):
DWORD dwResult;
SendMessageTimeout(hwnd, WM_GETTEXT, STRINGLEN, (LPARAM)s.GetBuffer(STRINGLEN),
SMTO_ABORTIFHUNG, 1000, &dwResult); // 1 秒超時,目標掛起即放棄
SendMessageTimeout 是最佳實踐?WM_GETTEXT 並返回文字。SMTO_ABORTIFHUNG 旗標確保目標進程無回應時,調用端在指定毫秒內返回,避免 UI 凍結。WM_GETTEXT 的控制項(Edit、ComboBox、Button、Static、ListBox 項目等)。文章附帶的 GWTTest 程式(CListView 架構)同時顯示 GetWindowText 與 SendMessageTimeout(WM_GETTEXT) 的結果:
GetWindowText 返回空;WM_GETTEXT 正確返回文字。核心取文字函數 CMyView::AddWindowInfo 程式碼片段:
void CMyView::AddWindowInfo(int iItem, HWND hwnd) {
CListCtrl& lc = GetListCtrl();
CWnd* pWnd = CWnd::FromHandle(hwnd);
int iSub = 1;
CString s;
// 類別名
::GetClassName(hwnd, s.GetBuffer(STRINGLEN), STRINGLEN);
lc.SetItemText(iItem, iSub++, s);
// GetWindowText 結果
pWnd->GetWindowText(s);
lc.SetItemText(iItem, iSub++, s);
// WM_GETTEXT 結果
DWORD result;
SendMessageTimeout(hwnd, WM_GETTEXT, STRINGLEN, (LPARAM)s.GetBuffer(STRINGLEN),
0, 1000, &result);
lc.SetItemText(iItem, iSub++, s);
s.ReleaseBuffer();
}
| API | 跨進程子控制項 | 阻塞風險 | 適用場景 |
|---|---|---|---|
GetWindowText | ❌ 僅標題 | 無 (內部特殊處理) | 同進程控制項、取頂層視窗標題 |
SendMessage(WM_GETTEXT) | ✅ 完整支援 | 高 (目標掛起則永久阻塞) | 同進程、或確信目標極度響應 |
SendMessageTimeout(WM_GETTEXT) | ✅ 完整支援 | 可控 (毫秒級超時) | 跨進程取文字的標準最佳實踐 |