人民日報網(wǎng)站誰做的抖音seo排名系統(tǒng)哪個好用
目錄
前言
1 遍歷窗口句柄列表
2 使用 SendMessageTimeout 發(fā)送延時消息
3 并行發(fā)送消息實現(xiàn)模擬廣播消息
4 修改?UIPI 消息過濾器設置
5 托盤圖標刷新的處理
6 完整代碼和測試
本文屬于原創(chuàng)文章,轉(zhuǎn)載請注明出處:
https://blog.csdn.net/qq_59075481/article/details/136175227。
前言
在 Windows 上使得設置的更改立即生效的方法不唯一。本文分析全局發(fā)送?WM_SETTINGCHANGE 消息來通知系統(tǒng)設置發(fā)生更改這一方法。該方法適用范圍比較廣泛,但有時候還是需要結(jié)合 SHChangeNotify 等函數(shù)來刷新更改,甚至還有一部分設置更改就只能重啟計算機生效。
我們知道如果使用 HWND_BROADCAST 廣播消息的話,雖然會通知所有頂級窗口,只消息窗口等窗口,但是該消息的處理在任意一個窗口處理后就會立即終止并返回,消息接收方有權(quán)不處理消息,我們并不容易獲取消息處理的詳細情況,并且他不能針對子窗口等窗口。
所以我們肯定要自己去實現(xiàn)自己的廣播消息的方式。
1 遍歷窗口句柄列表
我們首先通過 EnumWindows 和 EnumChildWindows 以遞歸的方式遍歷,獲取所有窗口句柄列表。
// Callback function for window enumeration
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{// Cast the lParam to a vector of HWND pointersstd::vector<HWND>* windowList = reinterpret_cast<std::vector<HWND>*>(lParam);// Add the window handle to the vectorwindowList->push_back(hwnd);// Enumerate child windowsEnumChildWindows(hwnd, EnumWindowsProc, lParam);// Continue enumerationreturn TRUE;
}// Enumerate all windows
std::vector<HWND> windowList;
EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(&windowList));
2 使用 SendMessageTimeout 發(fā)送延時消息
SendMessageTimeout 的好處是它可以比 SendMessage 多出來等待時間這個限制,SendMessage 會阻滯調(diào)用線程直到接收消息的線程處理完消息,所以不能用 SendMessage 同時發(fā)送消息到多個窗口;它比 PostMessage 也有優(yōu)勢,PostMessage 立即返回,所以在不關心處理結(jié)果的情況下我們可能選擇 PostMessage。
SendMessageTimeout 函數(shù)的聲明如下:
LRESULT SendMessageTimeoutW(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam,UINT fuFlags,UINT uTimeout,PDWORD_PTR lpdwResult
);
我們使用該函數(shù)就可以實現(xiàn)發(fā)送消息并等待一定時間,MSDN 說明如下:
https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-sendmessagetimeouta。
3 并行發(fā)送消息實現(xiàn)模擬廣播消息
單線程似乎并不能滿足我們同時請求多個窗口的需求。所以,我們可以將處理放置在多線程任務中。并將消息處理的結(jié)果使用鏈表來管理。
數(shù)據(jù)結(jié)構(gòu):
typedef struct __STMO_MSGEVENT {SIZE_T nCount;SIZE_T nSize;HWND hWnd;BOOL isActive;LRESULT lResult;DWORD_PTR lpStatus;struct __STMO_MSGEVENT* pNext[1];
} STMO_MSGEVENT, * LPSTMO_MSGEVENT;
其中,isActive 表示接收消息的線程是否在規(guī)定時間處理了消息,nSize 表示結(jié)點總數(shù),頭結(jié)點的nCount 表示所有窗口個數(shù)。?
線程執(zhí)行函數(shù):
// Function to send message to a window using SendMessageTimeout
void SendMessageToWindow(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LPSTMO_MSGEVENT* lpstmoMsg)
{// Set the timeout value (in milliseconds)DWORD timeout = 5000;// Call ChangeWindowsMessageFilterEx to modify the message filterChangeWindowMessageFilterEx(hwnd, uMsg, MSGFLT_ALLOW, 0);// Send the message using SendMessageTimeoutDWORD_PTR lpStatus = 0;LRESULT lResult = 0;lResult = SendMessageTimeoutW(hwnd, uMsg, wParam, lParam, SMTO_ABORTIFHUNG, timeout, &lpStatus);bool oldCount = lResult > 0 ? true : false;AddNode(lpstmoMsg, hwnd, oldCount, lResult, lpStatus);}
4 修改?UIPI 消息過濾器設置
從 Vista 引入的消息安全機制將限制低完整級別程序向高完整級別程序發(fā)送消息的過程,此時可以使用 ChangeWindowMessageFilterEx 來允許特定的消息通過 UIPI。
// Call ChangeWindowsMessageFilterEx to modify the message filter
ChangeWindowMessageFilterEx(hwnd, uMsg, MSGFLT_ALLOW, 0);
指定 MSGFLT_ALLOW 以允許消息通過 UIPI。
5 托盤圖標刷新的處理
托盤圖標刷新需要單獨模擬發(fā)送 TaskbarCreated 消息。在任務欄重建時,會向系統(tǒng)中所有窗口廣播?TaskbarCreated 消息,在負責通知欄圖標的線程接收到消息時,接收消息的線程按照規(guī)范應該調(diào)用 Shell_NotifyIcon 重新創(chuàng)建通知欄圖標。
TaskbarCreated 字符串消息是通過?RegisterWindowMessage 在系統(tǒng)級別注冊的,因為該函數(shù)內(nèi)部封裝了?NtUserRegisterWindowMessage 的調(diào)用,這是一個用戶態(tài)/內(nèi)核態(tài)切換過程。通過逆向分析,我們了解到?RegisterWindowMessage 的返回值是 Global ATOM 數(shù)值的一部分,系統(tǒng)內(nèi)核在全局維護一個原子表 RTL_ATOM_TABLE 來通過哈希桶管理一些窗口進程交互需要的信息。
所以,在系統(tǒng)重啟前,TaskbarCreated 消息對應的 IntAtom 索引號保持不變,該值與 explorer 的重啟無關。
調(diào)用 RegisterWindowMessage 并指定?"TaskbarCreated" 字符串將首先檢索消息是否已經(jīng)在 ATOM 表中注冊。由于該消息在系統(tǒng)初始化時已經(jīng)注冊,所以,執(zhí)行過程只會增加該消息的引用計數(shù)而不會重建消息。
所以,我們可以利用 RegisterWindowMessage 作為一個官方支持的技巧,輕松獲取"TaskbarCreated"消息的窗口消息碼,調(diào)用在非消息線程/非窗口進程/非系統(tǒng)進程就可以調(diào)用成功。所以該消息不會失敗,除非交互系統(tǒng)未能夠完成初始化。
// 該字符串消息使用系統(tǒng)原子表,返回值是 IntAtom 編號,
// 一般情況下在計算機重啟時才刷新。
UINT QueryTaskbarCreateMsg()
{return RegisterWindowMessageW(L"TaskbarCreated");
}
6 完整代碼和測試
#include <Windows.h>
#include <iostream>
#include <vector>
#include <thread>#pragma comment(lib, "User32.lib")typedef struct __STMO_MSGEVENT {SIZE_T nCount;HWND hWnd;BOOL isActive;LRESULT lResult;DWORD_PTR lpStatus;struct __STMO_MSGEVENT* pNext[1];
} STMO_MSGEVENT, * LPSTMO_MSGEVENT;LPSTMO_MSGEVENT CreateNode(HWND hWnd) {LPSTMO_MSGEVENT newNode = (LPSTMO_MSGEVENT)malloc(sizeof(STMO_MSGEVENT));newNode->nCount = 0;newNode->hWnd = hWnd;newNode->isActive = FALSE;newNode->lResult = 0;newNode->lpStatus = 0;return newNode;
}void AddNode(LPSTMO_MSGEVENT* pList, HWND hWnd, LRESULT lResult, DWORD_PTR lpStatus) {LPSTMO_MSGEVENT newNode = CreateNode(hWnd);newNode->isActive = (lResult > 0);newNode->lResult = lResult;newNode->lpStatus = lpStatus;newNode->pNext[0] = *pList;*pList = newNode;(*pList)->nCount++;
}void SendMessageToWindow(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LPSTMO_MSGEVENT* lpstmoMsg)
{DWORD timeout = 5000;ChangeWindowMessageFilterEx(hwnd, uMsg, MSGFLT_ALLOW, 0);DWORD_PTR lpStatus = 0;LRESULT lResult = SendMessageTimeoutW(hwnd, uMsg, wParam, lParam, SMTO_ABORTIFHUNG, timeout, &lpStatus);//LPSTMO_MSGEVENT newNode = CreateNode(hwnd);//newNode->isActive = (lResult > 0);// newNode->lResult = lResult;//newNode->lpStatus = lpStatus;AddNode(lpstmoMsg, hwnd, lResult, lpStatus);
}BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{std::vector<HWND>* windowList = reinterpret_cast<std::vector<HWND>*>(lParam);windowList->push_back(hwnd);EnumChildWindows(hwnd, EnumWindowsProc, lParam);return TRUE;
}void TraverseList(LPSTMO_MSGEVENT pList) {LPSTMO_MSGEVENT pNode = pList;while (pNode != nullptr) {std::cout << "hWnd: " << pNode->hWnd << std::endl;std::cout << "isActive: " << (pNode->isActive ? "true" : "false") << std::endl;std::cout << "lResult: " << pNode->lResult << " lpStatus: " << pNode->lpStatus << std::endl;pNode = pNode->pNext[0];}
}void FreeList(LPSTMO_MSGEVENT* pList) {LPSTMO_MSGEVENT pNode = *pList;while (pNode != nullptr) {LPSTMO_MSGEVENT temp = pNode;pNode = pNode->pNext[0];free(temp);}*pList = nullptr;
}// 該字符串消息使用系統(tǒng)原子表,返回值是 IntAtom 編號,
// 一般情況下在計算機重啟時才刷新。
UINT QueryTaskbarCreateMsg()
{return RegisterWindowMessageW(L"TaskbarCreated");
}int main()
{std::vector<HWND> windowList;EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(&windowList));SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS, 0);std::vector<std::thread> threads;UINT uMsg = WM_SETTINGCHANGE;WPARAM wParam = SPI_SETNONCLIENTMETRICS;LPARAM lParam = (LPARAM)0;LPSTMO_MSGEVENT msgev = nullptr;for (HWND hwnd : windowList){threads.emplace_back(SendMessageToWindow, hwnd, uMsg, wParam, lParam, &msgev);}for (std::thread& thread : threads){thread.join();}std::cout << "List contents:\n";TraverseList(msgev);FreeList(&msgev);return 0;
}
?
#include <Windows.h>
#include <iostream>
#include <vector>
#include <thread>typedef struct __STMO_MSGEVENT {SIZE_T nCount;SIZE_T nSize;HWND hWnd;BOOL isActive;LRESULT lResult;DWORD_PTR lpStatus;struct __STMO_MSGEVENT* pNext[1];
} STMO_MSGEVENT, * LPSTMO_MSGEVENT;LPSTMO_MSGEVENT CreateNode(HWND hWnd, BOOL isActive, LRESULT lResult, DWORD_PTR lpStatus, SIZE_T nCount) {LPSTMO_MSGEVENT newNode = (LPSTMO_MSGEVENT)malloc(sizeof(STMO_MSGEVENT));newNode->nCount = nCount;newNode->nSize = 1;newNode->hWnd = hWnd;newNode->isActive = isActive;newNode->lResult = lResult;newNode->lpStatus = lpStatus;return newNode;
}LPSTMO_MSGEVENT InitializeList(SIZE_T initialSize) {LPSTMO_MSGEVENT newList = (LPSTMO_MSGEVENT)malloc(sizeof(STMO_MSGEVENT) + initialSize * sizeof(LPSTMO_MSGEVENT));if (newList != NULL) {memset(newList, 0, sizeof(LPSTMO_MSGEVENT) * initialSize + sizeof(STMO_MSGEVENT));newList->nCount = 1;newList->nSize = initialSize;newList->hWnd = NULL;newList->isActive = FALSE;newList->lResult = 0;newList->lpStatus = 0;return newList;}else {printf("Failed to allocate memory for the list.\n");return NULL;}
}void AddNode(LPSTMO_MSGEVENT* pList, HWND hWnd, BOOL isActive, LRESULT lResult, DWORD_PTR lpStatus) {LPSTMO_MSGEVENT newNode = CreateNode(hWnd, isActive, lResult, lpStatus, (*pList)->nCount); // 也可以固定為 nSize,這只是一個記錄if ((*pList)->nCount < (*pList)->nSize) {(*pList)->pNext[(*pList)->nCount] = newNode;(*pList)->nCount++;}else {SIZE_T newSize = (*pList)->nSize * 2;LPSTMO_MSGEVENT newList = (LPSTMO_MSGEVENT)realloc(*pList, sizeof(STMO_MSGEVENT) + newSize * sizeof(LPSTMO_MSGEVENT));if (newList != NULL) {memset(newList, 0, sizeof(LPSTMO_MSGEVENT) * newSize + sizeof(STMO_MSGEVENT));*pList = newList;(*pList)->pNext[(*pList)->nCount] = newNode;(*pList)->nCount++;(*pList)->nSize = newSize;}else {free(newNode);printf("Failed to allocate memory for the new node.\n");}}
}void TraverseList(LPSTMO_MSGEVENT pList) {for (SIZE_T i = 0; i < pList->nCount; i++) {LPSTMO_MSGEVENT pNode = pList->pNext[i];std::cout << "index: 0x" << i << std::endl;std::cout << "hWnd: " << pNode->hWnd << std::endl;std::cout << "isActive: " << (pNode->isActive ? "true" : "false") << std::endl;std::cout << "lResult: " << pNode->lResult << " lpStatus: " << pNode->lpStatus << std::endl;}
}void FreeList(LPSTMO_MSGEVENT* pList) {for (SIZE_T i = 0; i < (*pList)->nCount; i++) {free((*pList)->pNext[i]);}free(*pList);*pList = NULL;
}// Function to send message to a window using SendMessageTimeout
void SendMessageToWindow(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LPSTMO_MSGEVENT* lpstmoMsg)
{// Set the timeout value (in milliseconds)DWORD timeout = 5000;// Call ChangeWindowsMessageFilterEx to modify the message filterChangeWindowMessageFilterEx(hwnd, uMsg, MSGFLT_ALLOW, 0);// Send the message using SendMessageTimeoutDWORD_PTR lpStatus = 0;LRESULT lResult = 0;lResult = SendMessageTimeoutW(hwnd, uMsg, wParam, lParam, SMTO_ABORTIFHUNG, timeout, &lpStatus);bool oldCount = lResult > 0 ? true : false;AddNode(lpstmoMsg, hwnd, oldCount, lResult, lpStatus);}// 該字符串消息使用系統(tǒng)原子表,返回值是 IntAtom 編號,
// 一般情況下在計算機重啟時才刷新。
UINT QueryTaskbarCreateMsg()
{return RegisterWindowMessageW(L"TaskbarCreated");
}// Callback function for window enumeration
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{// Cast the lParam to a vector of HWND pointersstd::vector<HWND>* windowList = reinterpret_cast<std::vector<HWND>*>(lParam);// Add the window handle to the vectorwindowList->push_back(hwnd);// Enumerate child windowsEnumChildWindows(hwnd, EnumWindowsProc, lParam);// Continue enumerationreturn TRUE;
}int main()
{// Enumerate all windowsstd::vector<HWND> windowList;EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(&windowList));// 要刷新任務欄的話用這個消息即可UINT uTaskbarMsg = QueryTaskbarCreateMsg();std::cout << "TaskbarCreateMsgAtom: 0x" << std::hex << uTaskbarMsg << std::endl;HWND hWnd = FindWindowW(L"Shell_TrayWnd", nullptr);// Create a vector of threadsstd::vector<std::thread> threads;UINT uMsg = WM_SETTINGCHANGE; // uTaskbarMsg;WPARAM wParam = (WPARAM)0; // hWnd;LPARAM lParam = 0;LPSTMO_MSGEVENT msgev = InitializeList(1024);// Launch threads to send messages to windowsfor (HWND hwnd : windowList){// Create a thread and pass the window handle as an argumentthreads.emplace_back(SendMessageToWindow, hwnd, uMsg, wParam, lParam, &msgev);}// Wait for all threads to finishfor (std::thread& thread : threads){thread.join();}// Traverse and print the listprintf("List contents:\n");TraverseList(msgev);// Free the listFreeList(&msgev);return 0;
}
P.S. : 為了便于測試,我們選用了任務欄重建時的 TaskbarCreated 消息作為示例(這只會通知更新托盤圖標),如果要全局通知系統(tǒng)設置更改應該用?WM_SETTINGCHANGE 消息,但由于?WM_SETTINGCHANGE 是一瞬間的,不方便截圖,所以我只給出了 Taskbar Create 消息測試的結(jié)果。但是在上面的代碼中,我使用??WM_SETTINGCHANGE ,并注釋了?Taskbar Create 消息。
操作需要提升管理員權(quán)限,否則部分窗口可能因為 UIPI 過濾而無法接收到消息。
我們開起了一個托盤圖標窗口程序,用于在接收到 TASKBAR_CREATED 消息時彈出消息框。
使用上文代碼工具廣播消息時,程序成功受到廣播的信息:


本文屬于原創(chuàng)文章,轉(zhuǎn)載請注明出處:
https://blog.csdn.net/qq_59075481/article/details/136175227。
文章發(fā)布于:2024.02.19,更新于:2024.02.20。