如何HOOK桌面窗口消息
2011-07-17 01:32
429 查看
本文通过一个DEMO来演示如何HOOK桌面消息。
代码详见:http://download.csdn.net/source/3448133
需求:截获桌面窗口鼠标单击事件,解析所选中的桌面 Item,并将解析后的 item
信息发送给主调程序,并将信息显示在一个窗口上面。如下图:
思路:
1. 确定HOOK的类型。很明显,这一个进程外的HOOK,我们的应用程序DesktopCaptor2.exe
需要捕获 Explorer.exe
这个进程的桌面窗口所在的线程的消息。因此,需要将HOOK过程放在一个独立的DLL
中去,然后使用 SetWindowsHookEx
将HOOK过程安装到HOOK链中去。
2. 如何解析点击的桌面 Item
信息?这个其实也比较好做,由于桌面窗口本身是一个
listview
控件(不解释),因此,我们可以通过
listview
拿到桌面窗口的简单信息。
3. 如何将解析后的桌面 Item
信息发送给主调程序,让它弹出一个窗口,并显示桌面 Item
信息?我们这里采用WM_COPYDATA
将从桌面进程获取到信息发送到我们的应用程序。
工程目录如下:
在主调程序(DesktopCaptor2.exe)是一个简单Win32 Dialog的程序。在这个程序中,我们干了两件事情:
1. 调用 DekstopHook
工程中的
DesktopHook.h
中的两个导出函数,通过这两个函数,对 HOOK
过程安装和卸载。
2. 接收来自于Explorer.exe
进程发送的 WM_COPYDATA
消息,还原桌面 Item
数据,并将在点击桌面 Item
的位置弹出一个窗口出来,显示 Item
信息。至于这个窗口如何***,我就不再描述了。
以下为部分代码:
我们重点说明一下DekstopHookDLL中的内容。
DesktopHook.h
中定义的是一些导出接口,代码如下:
其中:
1. DesktopItemData
结构体是用来存放解析桌面 Item
的数据。
2. CreateDesktopEventCaptor
函数是用来安装 HOOK;
3. CloseDesktopEventCaptor
函数是用来卸载 HOOK;
以下是
DesktopHook.cpp
中的实现代码:
说明:在该实现中,CreateDesktopEventCaptor
函数与
CloseDesktopEventCaptor
函数被
DesktopCaptor2.exe
进程调用,此时,DesktopHook.dll
会被加载到
DesktopCaptor2.exe
所在进程,当使用
CreateDesktopEventCaptor
函数后,HOOK
过程函数
GetMsgProc
会被安装到 Explorer.exe
进程中去,此时,DesktopHook.dll
会被加载到 Explorer.exe
中去。当用鼠标单击桌面 Item
时,消息将被传递到
GetMsgProc
中去,然后,在这个函数中发送一个 WM_COPYDATA
消息给DesktopCaptor2
的窗口,实际上,这相当于是 Explorer.exe
进程发送的 WM_COPYDATA
消息到 DesktopCaptor2.exe
进程
。需要注意一点的是,在
GetMsgProc
中发送消息的时候,g_hNotifierWnd
必须要设置成为共享数据,因为它是DesktopCaptor2.exe
进程在调用
DesktopHook.dll 的
CreateDesktopEventCaptor
函数的时候被设置的,要想在Explorer.exe进程中继续有效,需要将之设置为 DLL共享数据。
另外,这里还有另外两个问题:
如何找到 Desktop
窗口句柄?
通过Spy++,我们得到:
其中,蓝色部分就是我们的桌面窗口句柄。我们的
FindShellWindow
函数就是为了干这个事情,但是有时候,这个函数会失败,因此,我们最多循环10次去查找桌面窗口句柄。
WH_GETMESSAGE
类型的消息 HOOK
只能钩住使用 PostMessage
方式发送的消息,这点很重要,否则,如果是以SendMessage发送的方式,则需要使用
WH_CALLWNDPROC 或者
WH_CALLWNDPROCRET
的 HOOK
方式。
如何解析点击桌面的
Item 信息?
实际上,由于桌面窗口本身是一个 ListView
控件,通过ListView
的控件的API,我们便能够拿到相关的信息(当然这里只是一个简单的信息,深层次的信息还需要深掘)。
首先,通过下面的函数,能够拿到选中的 Item
的
ID,其中 hwnd
就是桌面窗口句柄。
UINT ListView_GetSelectedCount(
HWND hwnd
);
其次,通过下面的函数,传入选中的 Item
的
ID,我们就能拿到 Item
对应的文本。
void ListView_GetItemText(
HWND hwnd,
int iItem,
int iSubItem,
LPTSTR pszText,
int cchTextMax
);
在这里,我使用了一个
CDesktopItem
类来专门干这个事情。
具体请详见附录代码。
至此,简单的代码讲解就结束了。
下面说一下如何调试。
因为本例子是进程外的HOOK,因此调试起来有很多不方便的地方。调试的难处在于如何调试HOOK过程函数。先说一个简单的例子,可能大家经常会遇到这种情况:假如一个Solution下有ProjectA和
Project B,它们都是exe,但涉及到相互发消息,有人就会打开2个Visual
Studio,同时进行调试,这样的确可以做到,但总感觉不太方便。实际上,在同一个Visual Studio中,是可以同时调试多个程序的。对于刚刚这种情况,只需要在每个Project
A 和 ProjectB
上面分别右击->Debug->Start new instance
即可。
然而,对于本例,这样做是不行的,因为
DesktopHook
是一个DLL工程,本身是无法进行独立调试的。因此,要想同时调试
DesktopCaptor2
工程和
DesktopHook
工程,需要按如下操作:
将DesktopHook工程设置为默认启动的工程。如图:
将
DesktopCaptor2
工程通过
右击->Debug->Start
new instance
启动起来,点击 Start
Desktop Captor 按钮启动HOOK,将DesktopHook
.dll 注入到进程Explorer.exe中去。
将
DesktopHook
工程到附加(Attach)到Explorer.exe进程中去。如图:
点击
Tools->Attachto Process...
然后找到Explorer.exe,点击Attach按钮即可。
通过上面的这种方式,我们就能够很简单的在一个工程中,调试两个不同的进程的程序。这时,我们将断点打在DesktopHook
工程的函数 GetMsgProc中,并在DesktopCaptor2
工程
WM_COPYDATA
内部打上断点,发现点击桌面图标时,断点会走到GetMsgProc内部,当发送完消息后,就能走到DesktopCaptor2
工程
WM_COPYDATA
内部。。
另外,你调试的时候,要注意一下当前用户的权限以及Visual Studio的权限。如果当前用户是管理员组用户,而你的VisualStudio是以管理员启动进来的,那么DesktopCaptor2.exe将也是管理员权限,但此时,Explorer.exe却是普通权限,此时,在GetMsgProc内部发送WM_COPYDATA是会出现问题的,因为不能向高权限进程发送消息。
The End...
代码详见:http://download.csdn.net/source/3448133
需求:截获桌面窗口鼠标单击事件,解析所选中的桌面 Item,并将解析后的 item
信息发送给主调程序,并将信息显示在一个窗口上面。如下图:
思路:
1. 确定HOOK的类型。很明显,这一个进程外的HOOK,我们的应用程序DesktopCaptor2.exe
需要捕获 Explorer.exe
这个进程的桌面窗口所在的线程的消息。因此,需要将HOOK过程放在一个独立的DLL
中去,然后使用 SetWindowsHookEx
将HOOK过程安装到HOOK链中去。
2. 如何解析点击的桌面 Item
信息?这个其实也比较好做,由于桌面窗口本身是一个
listview
控件(不解释),因此,我们可以通过
listview
拿到桌面窗口的简单信息。
3. 如何将解析后的桌面 Item
信息发送给主调程序,让它弹出一个窗口,并显示桌面 Item
信息?我们这里采用WM_COPYDATA
将从桌面进程获取到信息发送到我们的应用程序。
工程目录如下:
在主调程序(DesktopCaptor2.exe)是一个简单Win32 Dialog的程序。在这个程序中,我们干了两件事情:
1. 调用 DekstopHook
工程中的
DesktopHook.h
中的两个导出函数,通过这两个函数,对 HOOK
过程安装和卸载。
2. 接收来自于Explorer.exe
进程发送的 WM_COPYDATA
消息,还原桌面 Item
数据,并将在点击桌面 Item
的位置弹出一个窗口出来,显示 Item
信息。至于这个窗口如何***,我就不再描述了。
以下为部分代码:
#include "CommonDef.h" #include "DesktopHook.h" #include "FloatWin.h" const UINT WM_DESKTOP_CLICKED_ITEM = RegisterWindowMessage(L"WM_DESKTOP_CLICKED_ITEM"); BOOL g_isCaptured = FALSE; CFloatWin* g_floatWin = NULL; INT_PTR WINAPI DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) ; int WINAPI _tWinMain(HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int) { HWND hwnd = FindWindow(TEXT("#32770"), TEXT("DesktopCaptor2")); if (IsWindow(hwnd)) { // An instance is already running, show a messagebox MessageBox(GetForegroundWindow(), L"An instance is already running", L"Error", MB_ICONERROR); } else { DialogBox(hinstExe, MAKEINTRESOURCE(IDD_DESKTOP_CAPTOR), NULL, DlgProc); } return(0); } INT_PTR CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: { // Set icon for the application SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM) LoadIcon((HINSTANCE) GetWindowLongPtr(hDlg, GWLP_HINSTANCE), MAKEINTRESOURCE(IDI_DESKTOPCAPTOR2))); // Set dialog's position int nScreenWidth = ::GetSystemMetrics(SM_CXSCREEN); int nScreenHeight = ::GetSystemMetrics(SM_CYSCREEN); RECT rect = { 0 }; GetWindowRect(hDlg, &rect); SetWindowPos( hDlg, HWND_TOP, nScreenWidth - (rect.right - rect.left), 0, 0, 0, SWP_NOSIZE); g_floatWin = CFloatWin::getInstance(); } return (INT_PTR)TRUE; case WM_COMMAND: { UINT wmId = LOWORD(wParam); UINT wmEvent = HIWORD(wParam); switch (wmId) { case IDOK: case IDCANCEL: EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; case IDC_START_CAPTOR: if (FALSE == g_isCaptured) { g_isCaptured = CreateDesktopEventCaptor(hDlg); } break; case IDC_STOP_CAPTOR: if (TRUE == g_isCaptured) { CloseDesktopEventCaptor(); g_isCaptured = FALSE; } break; default: return DefWindowProc(hDlg, message, wParam, lParam); } } break; case WM_COPYDATA: { COPYDATASTRUCT* pCopyData = (COPYDATASTRUCT*)lParam; if (pCopyData->dwData == WM_DESKTOP_CLICKED_ITEM) { DesktopItemData itemData(*(DesktopItemData*)pCopyData->lpData); g_floatWin->ShowWindow(TRUE, &itemData); } } break; } return (INT_PTR)FALSE; }
我们重点说明一下DekstopHookDLL中的内容。
DesktopHook.h
中定义的是一些导出接口,代码如下:
#ifndef _DESKTOPHOOK_H_ #define _DESKTOPHOOK_H_ //#ifdef __cplusplus //extern "C" { //#endif #ifdef DESKTOPHOOK_EXPORTS #define DESKTOPHOOK_API __declspec(dllexport) #else #define DESKTOPHOOK_API __declspec(dllimport) #endif typedef struct DESKTOPHOOK_API _DesktopItemData { POINT point; WCHAR szName[MAX_PATH]; // The desktop item's name. int nIndex; // The desktop item's index on desktop. _DesktopItemData() { ZeroMemory(szName, MAX_PATH); point.x = point.y = 0; nIndex = -1; } _DesktopItemData(POINT pt, WCHAR* pszName, int nIx) { if (NULL != pszName) { point.x = pt.x; point.y = pt.y; ZeroMemory(szName, MAX_PATH); _tcscpy_s(szName, MAX_PATH, pszName); nIndex = nIx; } } _DesktopItemData(const _DesktopItemData& itemDataRef) { point.x = itemDataRef.point.x; point.y = itemDataRef.point.y; ZeroMemory(szName, MAX_PATH); _tcscpy_s(szName, MAX_PATH, itemDataRef.szName); nIndex = itemDataRef.nIndex; } _DesktopItemData& operator = (const _DesktopItemData& itemDataRef) { point.x = itemDataRef.point.x; point.y = itemDataRef.point.y; ZeroMemory(szName, MAX_PATH); _tcscpy_s(szName, MAX_PATH, itemDataRef.szName); nIndex = itemDataRef.nIndex; return *this; } } DesktopItemData, *LPDesktopItemData; EXTERN_C DESKTOPHOOK_API BOOL CreateDesktopEventCaptor(HWND hNotifierhWnd); EXTERN_C DESKTOPHOOK_API void CloseDesktopEventCaptor(); //#ifdef __cplusplus //} //#endif #endif // _DESKTOPHOOK_H_
其中:
1. DesktopItemData
结构体是用来存放解析桌面 Item
的数据。
2. CreateDesktopEventCaptor
函数是用来安装 HOOK;
3. CloseDesktopEventCaptor
函数是用来卸载 HOOK;
以下是
DesktopHook.cpp
中的实现代码:
// DesktopHook.cpp : Defines the exported functions for the DLL application. // #include "stdafx.h" #include "DesktopHook.h" #include "DesktopItem.h" #pragma data_seg("SHARED_DATA") HWND g_hNotifierWnd = NULL; HHOOK g_hPostMsgHook = NULL; WCHAR g_szBuf[MAX_PATH] = {0}; #pragma data_seg() #pragma comment(linker, "/SECTION:SHARED_DATA,RWS") // Global data const UINT WM_DESKTOP_CLICKED_ITEM = RegisterWindowMessage(L"WM_DESKTOP_CLICKED_ITEM"); HMODULE g_hModule; HWND g_hDesktopWnd = NULL; // The low-order word specifies the x-coordinate of the cursor. // The high-order word specifies the y-coordinate of the cursor. BOOL g_bDoubleClick = FALSE; UINT_PTR g_timerID = 0; POINT g_clickPt; CDesktopItem g_singleClickDesktopItem = CDesktopItem::Empty; DesktopItemData g_desktopItemData; // Declaration of methods. static HWND FindShellWindow(); static LRESULT CALLBACK GetMsgProc(int code,WPARAM wParam,LPARAM lParam); static VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime); EXTERN_C DESKTOPHOOK_API BOOL CreateDesktopEventCaptor(HWND hNotifierhWnd) { g_hDesktopWnd = FindShellWindow(); if (NULL == g_hDesktopWnd) { OutputDebugString(L"Can not find desktop window handle"); return FALSE; } g_hNotifierWnd = hNotifierhWnd; // Get desktop handle's thread id. DWORD targetThreadid = ::GetWindowThreadProcessId(g_hDesktopWnd, NULL); // Hook post message. g_hPostMsgHook = ::SetWindowsHookEx(WH_GETMESSAGE, &GetMsgProc, g_hModule, targetThreadid); if (g_hPostMsgHook != NULL) { return TRUE; } return FALSE; } EXTERN_C DESKTOPHOOK_API void CloseDesktopEventCaptor() { if (g_hPostMsgHook != NULL) { ::UnhookWindowsHookEx(g_hPostMsgHook); } } HWND FindShellWindow() { // Sometimes, we can't find the desktop window when we use this function, but we must // find it's handle, so we do a loop to find it, but at most we find for 10 times. UINT uFindCount = 0; HWND hSysListView32Wnd = NULL; while (NULL == hSysListView32Wnd && uFindCount < 10) { HWND hParentWnd = ::GetShellWindow(); HWND hSHELLDLL_DefViewWnd = ::FindWindowEx(hParentWnd, NULL, L"SHELLDLL_DefView", NULL); hSysListView32Wnd = ::FindWindowEx(hSHELLDLL_DefViewWnd, NULL, L"SysListView32", L"FolderView"); if (NULL == hSysListView32Wnd) { hParentWnd = ::FindWindowEx(NULL, NULL, L"WorkerW", L""); while((!hSHELLDLL_DefViewWnd) && hParentWnd) { hSHELLDLL_DefViewWnd = ::FindWindowEx(hParentWnd, NULL, L"SHELLDLL_DefView", NULL); hParentWnd = FindWindowEx(NULL, hParentWnd, L"WorkerW", L""); } hSysListView32Wnd = ::FindWindowEx(hSHELLDLL_DefViewWnd, 0, L"SysListView32", L"FolderView"); } if (NULL == hSysListView32Wnd) { Sleep(1000); uFindCount++; } else { break; } } return hSysListView32Wnd; } // The message which is "Post" type can be hook in this hook procedure. LRESULT CALLBACK GetMsgProc(int code, WPARAM wParam, LPARAM lParam) { MSG* pMsg = (MSG*)lParam; if( NULL != pMsg && pMsg->hwnd != NULL ) { switch(pMsg->message) { case WM_MOUSEMOVE: { //OutputDebugStringW(L"Mouse Move"); } break; case WM_LBUTTONUP: { //OutputDebugStringW(L"WM_LBUTTONUP"); if (g_bDoubleClick) { OutputDebugString(L"g_bDoubleClick == TRUE"); g_singleClickDesktopItem = CDesktopItem::Empty; g_bDoubleClick = FALSE; ::KillTimer(NULL, g_timerID); } else { OutputDebugString(L"g_bDoubleClick == FALSE"); ::KillTimer(NULL, g_timerID); g_clickPt.x = pMsg->pt.x; g_clickPt.y = pMsg->pt.y; g_singleClickDesktopItem = pMsg; g_timerID = ::SetTimer(NULL, 1, ::GetDoubleClickTime(), TimerProc); } } break; case WM_LBUTTONDBLCLK: g_bDoubleClick = TRUE; break; } } return ::CallNextHookEx(g_hPostMsgHook, code, wParam, lParam); } VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) { UNREFERENCED_PARAMETER(hwnd); UNREFERENCED_PARAMETER(uMsg); UNREFERENCED_PARAMETER(idEvent); UNREFERENCED_PARAMETER(dwTime); if (g_timerID == idEvent && g_bDoubleClick == FALSE && g_singleClickDesktopItem != CDesktopItem::Empty) { OutputDebugString(L"SendMessageTimeout Process begin"); wstring strName = g_singleClickDesktopItem.GetItemName(); //_tcscpy_s(g_szBuf, MAX_PATH, strName.c_str()); //::PostMessage(g_hNotifierWnd, WM_DESKTOP_CLICKED_ITEM, NULL, (LPARAM)g_szBuf); ZeroMemory(&g_desktopItemData, sizeof(DesktopItemData)); g_desktopItemData.point.x = g_clickPt.x; g_desktopItemData.point.y = g_clickPt.y; _tcscpy_s(g_desktopItemData.szName, MAX_PATH, strName.c_str()); g_desktopItemData.nIndex = g_singleClickDesktopItem.GetItemIndex(); COPYDATASTRUCT copyData; copyData.dwData = (UINT_PTR)WM_DESKTOP_CLICKED_ITEM; copyData.cbData = sizeof(DesktopItemData); copyData.lpData = &g_desktopItemData; DWORD_PTR rt = -1; SetActiveWindow(g_hNotifierWnd); SetForegroundWindow(g_hNotifierWnd); //SendMessageTimeout(g_hNotifierWnd, WM_COPYDATA, (WPARAM)g_hDesktopWnd, (LPARAM)©Data, SMTO_NORMAL, 1000, &rt); SendMessage(g_hNotifierWnd, WM_COPYDATA, (WPARAM)g_hDesktopWnd, (LPARAM)©Data); OutputDebugString(L"SendMessageTimeout Process end"); } ::KillTimer(NULL, g_timerID); }
说明:在该实现中,CreateDesktopEventCaptor
函数与
CloseDesktopEventCaptor
函数被
DesktopCaptor2.exe
进程调用,此时,DesktopHook.dll
会被加载到
DesktopCaptor2.exe
所在进程,当使用
CreateDesktopEventCaptor
函数后,HOOK
过程函数
GetMsgProc
会被安装到 Explorer.exe
进程中去,此时,DesktopHook.dll
会被加载到 Explorer.exe
中去。当用鼠标单击桌面 Item
时,消息将被传递到
GetMsgProc
中去,然后,在这个函数中发送一个 WM_COPYDATA
消息给DesktopCaptor2
的窗口,实际上,这相当于是 Explorer.exe
进程发送的 WM_COPYDATA
消息到 DesktopCaptor2.exe
进程
。需要注意一点的是,在
GetMsgProc
中发送消息的时候,g_hNotifierWnd
必须要设置成为共享数据,因为它是DesktopCaptor2.exe
进程在调用
DesktopHook.dll 的
CreateDesktopEventCaptor
函数的时候被设置的,要想在Explorer.exe进程中继续有效,需要将之设置为 DLL共享数据。
另外,这里还有另外两个问题:
如何找到 Desktop
窗口句柄?
通过Spy++,我们得到:
其中,蓝色部分就是我们的桌面窗口句柄。我们的
FindShellWindow
函数就是为了干这个事情,但是有时候,这个函数会失败,因此,我们最多循环10次去查找桌面窗口句柄。
WH_GETMESSAGE
类型的消息 HOOK
只能钩住使用 PostMessage
方式发送的消息,这点很重要,否则,如果是以SendMessage发送的方式,则需要使用
WH_CALLWNDPROC 或者
WH_CALLWNDPROCRET
的 HOOK
方式。
如何解析点击桌面的
Item 信息?
实际上,由于桌面窗口本身是一个 ListView
控件,通过ListView
的控件的API,我们便能够拿到相关的信息(当然这里只是一个简单的信息,深层次的信息还需要深掘)。
首先,通过下面的函数,能够拿到选中的 Item
的
ID,其中 hwnd
就是桌面窗口句柄。
UINT ListView_GetSelectedCount(
HWND hwnd
);
其次,通过下面的函数,传入选中的 Item
的
ID,我们就能拿到 Item
对应的文本。
void ListView_GetItemText(
HWND hwnd,
int iItem,
int iSubItem,
LPTSTR pszText,
int cchTextMax
);
在这里,我使用了一个
CDesktopItem
类来专门干这个事情。
具体请详见附录代码。
至此,简单的代码讲解就结束了。
下面说一下如何调试。
因为本例子是进程外的HOOK,因此调试起来有很多不方便的地方。调试的难处在于如何调试HOOK过程函数。先说一个简单的例子,可能大家经常会遇到这种情况:假如一个Solution下有ProjectA和
Project B,它们都是exe,但涉及到相互发消息,有人就会打开2个Visual
Studio,同时进行调试,这样的确可以做到,但总感觉不太方便。实际上,在同一个Visual Studio中,是可以同时调试多个程序的。对于刚刚这种情况,只需要在每个Project
A 和 ProjectB
上面分别右击->Debug->Start new instance
即可。
然而,对于本例,这样做是不行的,因为
DesktopHook
是一个DLL工程,本身是无法进行独立调试的。因此,要想同时调试
DesktopCaptor2
工程和
DesktopHook
工程,需要按如下操作:
将DesktopHook工程设置为默认启动的工程。如图:
将
DesktopCaptor2
工程通过
右击->Debug->Start
new instance
启动起来,点击 Start
Desktop Captor 按钮启动HOOK,将DesktopHook
.dll 注入到进程Explorer.exe中去。
将
DesktopHook
工程到附加(Attach)到Explorer.exe进程中去。如图:
点击
Tools->Attachto Process...
然后找到Explorer.exe,点击Attach按钮即可。
通过上面的这种方式,我们就能够很简单的在一个工程中,调试两个不同的进程的程序。这时,我们将断点打在DesktopHook
工程的函数 GetMsgProc中,并在DesktopCaptor2
工程
WM_COPYDATA
内部打上断点,发现点击桌面图标时,断点会走到GetMsgProc内部,当发送完消息后,就能走到DesktopCaptor2
工程
WM_COPYDATA
内部。。
另外,你调试的时候,要注意一下当前用户的权限以及Visual Studio的权限。如果当前用户是管理员组用户,而你的VisualStudio是以管理员启动进来的,那么DesktopCaptor2.exe将也是管理员权限,但此时,Explorer.exe却是普通权限,此时,在GetMsgProc内部发送WM_COPYDATA是会出现问题的,因为不能向高权限进程发送消息。
The End...
相关文章推荐
- 如何HOOK桌面窗口消息
- 如何HOOK桌面窗口消息
- 关于HOOK,如何通过钩子截获指定窗口的所有消息
- 关于HOOK,如何通过钩子截获指定窗口的所有消息 SetWindowsHookEx demo
- 如何获取有关窗口正在处理的当前消息的信息
- 如何找到当前桌面某一窗口上的类名
- MFC中窗口收到WM_PAINT消息是如何处理的,什么情况下产生WM_PAINT消息
- hook 窗口消息队列
- Qt 中如何捕获窗口停用和激活的消息 Activate&Deactivate
- 如何限制窗口只在桌面工作区(不包括任务栏)活动?
- 如何让窗口停放在桌面的右下角
- 如何在MDI客户区窗口响应鼠标双击消息?
- 钩子中向窗口发送消息、操作窗口无反应的问题解决方法(Hook dll ShowWindow HWND)
- Qt 中如何捕获窗口停用和激活的消息
- VC6系统托盘类|崩溃自动重建图标|HOOK窗口消息|气泡效果更新SDK
- 如何处理窗口的鼠标离开消息
- 无窗口的CWnd类如何向框架发送消息
- 如何处理鼠标离开窗口的消息
- 如何让你的WinForm嵌入桌面窗口最底层
- Qt 中如何捕获窗口停用和激活的消息