第22章 DLL注入和API拦截(1)

时间:2024-11-05 08:34:32

22.1 注入的一个例子(跨进程子类化窗口)

第22章 DLL注入和API拦截(1)

  ①子类化窗口可以改变窗口的行为,让发往该窗口的消息重新发到我们指定的过程来处理。但这种行为只能在本进程中(如A),对于从一个进程(如B)去子类化另一个进程(如A)时,会遇到无法跨越进程地址空间的边界问题。

  ②上图,进程B试图调用SetWindowLongPtr将进程A中的hWnd窗口过程,重新指定为MySubClassProc来处理(注意,MySubClassProc在进程B的地址空间中),而不是hWnd窗口的标准窗口处理过程。但因该行为是跨进程的,如果被允许的话,当系统要执行进程A的hWnd窗口过程时,就会试图调用进程A地址空间中另一个地址(该地址的值与进程B中的地址相同),原因是不同进程的地址空间是相互隔离的,所以会出现内存访问违规的错误。因此,为了避免这种错误,微软不允许SetWindowLongPrt修改别的进程的窗口过程

  ③要实现跨进程子类化窗口,须用到“DLL注入”技术,将DLL注入到进程的地址空间中。

22.2 使用注册表来注入DLL

(1)注册表项的使用

第22章 DLL注入和API拦截(1)

  ①AppInit_Dlls键可能会包含一个或一组Dll的文件名(用空格或逗号分隔,第一个Dll的文件名可包含路径,但其他Dll包含的路径会被忽略)。为了让系统使用这个注册表项,还须将LoadAppInit_Dlls的值设为1。

  ②因为路径可能会被忽略,所以最好将自己的Dll放到Windows的系统目录中,这样就不必指定路径了。

(2)User32.dll的映射

  ①当User32.dll被映射到一个新的进程时,该进程会收到DLL_PROCESS_ATTACH通知。User32.dll对它进行处理的时候,会取得上述注册表键的值,并调用LoadLibrary来载入这个字符串中指定的每个DLL。

  ②当系统载入每个DLL时,会调用它们的DllMain函数,并将参数fdwReason设为DLL_PROCESS_ATTACH,这样每个DLL就能够对自己进行初始化。

  ③由于被注入的DLL是在进程生命期的早期被载入,所以调用Kernel32.dll中的函数是没问题的,但其他DLL中的函数可能会导致问题。

(3)注册表注入DLL的缺点

  ①基于GUI的应用程序都使用了User32.dll,但大多数基于CUI的应用程序不会使用它。因此想要将DLL注入到编译器或链接器,这种方法是行不通的。

  ②DLL会被映射进每个基于GUI的应用程序中,导致这些进程的线程都在运行我们的代码,如果我们的代码进入无限循环或错误地访问了内存,那么会影响到“容器”进程(即其他基于GUI的所有进程)的行为和健壮性

  ③我们的DLL被映射到每个基于GUI应用程序中,其生命期会直到应用程序终止(因为进程终止时,才会发送DLL_PROCESS_DETACH通知给User32.dll,在这里会卸载到我们的DLL)。而理想的情况下,DLL应注入到我们需要的进程中,并且映射的时间越短越好。

22.3 使用Windows钩子来注入DLL

22.3.1 钩子的类型

(1)按作用范围

  ①局部钩子:仅钩挂属于自身进程的事件

  ②远程钩子:可钩挂自身进程,还可以钩挂其他进程中发生的事件。又分为基于线程和系统范围的。

(2)按监视消息的类型分类

钩子名称

监视消息的类型和时机

WH_CALLWNDPROC

每当调用SendMessage函数时,函数将消息发送给目标窗口前首先调用钩子函数

WH_CALLWNDPROCRET

每当调用SendMessage函数时,函数将消息发送给目标窗口过程后再调用钩子函数

WH_GETMESSAGE

每当调用GetMessage或PeekMessage函数时,函数从程序的消息队列中获取一个消息后调用钩子函数

WH_KEYBOARD

每当调用GetMessage或PeekMessage函数时,如果从消息队列中得到的是WM_KEYUP或WM_KEYDOWN消息时,则调用钩子函数

WH_MOUSE

每当调用GetMessage或PeekMessage函数时,如果从消息队列中得到的是鼠标消息,则调用钩子函数

WH_HARDWARE

每当调用GetMessage或PeekMessage函数时,如果从消息队列中得到的是非鼠标和键盘消息时,则调用钩子函数

WH_MSGFILTER

当用户对对话框、菜单和滚动条有所操作时,系统在发送对应的消息之前调用钩子函数,这种钩子只能是局部的

WH_SYSMSGFILTER

同WH_MSGFILTER,不过是系统范围的

WH_SHELL

当Windows shell程序准备接收一些通知事件前调用钩子函数,如shell被激活或重绘等。外壳应用程序是不接受WH_SHELL消息的,要使应用程序能够接收WH_SHELL,必须调用SystemParametersInfo函数注册该消息。WM_SHELL共有5种情况:

①TaskBar需要重绘某个按钮时

②当系统地要显示关于Taskbar的一个程序的最小化形式

③当目前的键盘布局状态改变。

④当按Ctrl+Esc去执行TaskManager(或相等级别的程序)时。

⑤只要有个top-level、unowned窗口被创建、起作用或被摧毁

WH_DEBUG

用来给其他钩子函数除错

WH_CBT

当基于计算机的训练(CBT)事件发生时调用钩子函数

WH_JOURNALRECORD

日志记录钩子,用来记录发送给系统消息队列的所有消息

WH_JOURNALPLAYBACK

日志回放钩子,用来回放日志记录钩子记录的系统事件

WH_FOREGROUNDIDLE

系统空闲钩子,当系统空闲的时候调用钩子函数,这样就可以在这里安排一些低优先级的任务

22.3.2 钩子程序的结构:

(1)主要的3个功能模块

模块

作用

①主程序

用来实现界面或者其他功能

②钩子回调函数

用来接收系统发过来的消息

①对于局部钩子,这些模块可在同一个EXE文件中

②对于远程钩子这个回调函数必须在一个DLL中

③钩子的安装和卸载模块

调用SetWindowsHookEx和UnhookWindowsHookEx函数。

①这部分虽然没有要求,但一般也放在DLL中,因为钩子创建以后会得到一个钩子句柄,这个句柄要在钩子回调函数中,以及卸载钩子的时候用到。

②如果把这部分代码放在主程序中的话,还需要创建一个函数将它传回给动态链接库,所以不如直接放在库中

(2)钩子的安装:SetWindowsHookEx

参数

说明

int idHook

要安装的钩子的类型

HOOKPROC lpfn

钩子回调函数(在我们的地址空间或某个DLL)

HINSTANCE hInstDll

标识一个DLL,这个DLL包含了钩子回调函数。

如果是局部钩子这里须为NULL。

DWORD dwThreadId

给哪个线程安装钩子。如果为0,表示给系统中所有的GUI线程安装钩子。

(3)举例说明钩子的工作过程——以WM_GETMESSAGE为例(进程A监视进程B的窗口消息,设钩子回调函数为GetMsgProc)

  ①当进程B的一个线程准备GetMessage一条窗口消息时。系统会检查该线程是否安装了WH_GETMESSAGE钩子。

  ②系统检查GetMsgProc所在的DLL是否己被映射进程B的地址空中。

  ③如果DLL尚未被映射,那么系统会强制将该DLL映射到进程B的地址空间中,并将进程B中该DLL的锁计数器递增

  ④由于安装钩子时参数hInstDll和回调函数的地址都是进程A地址空间中的值。当被映射到进程B时,系统会检查该DLL被映射的基地址是否与进程A中的位置相同。

    Ⅰ如果相同,那么在两个进程的地址空间中,GetMsgProc函数位于相同的位置,在这情况下,系统可以直接在进程B(课本这里写A,是否是错误?)的地址空间中调用GetMsgProc。

    Ⅱ如果不同,则得通过GetMsgProcB = hInstDllB + (GetMsgProcA - hInstDllA)公式计算GetMsgProc在进程B中的虚拟地址。

  ⑤系统在进程B中递增该DLL的锁计数器,这是因为调用GetMsgProc函数时须先锁定,以防止该DLL被别的线程卸载掉。当GetMsgProc返回后,系统递减该DLL在进程B中的锁计数器。

(4)钩子的卸载:BOOL UnhookWindowsHookEx(HHOOK hHook)

  ①当一个线程调用UnhookWindowsHookEx时,系统会遍历其内部维护的一个进程列表,该列表记录着那些己经注入过该DLL的进程。

  ②并将这些进程中该DLL的锁计数递减(注意每个进程都有各自对该DLL的锁计数器)。当某个进程的锁计数器减到0时,系统会自动从该进程的地址空间中撤消对该DLL的映射。

22.3.3 钩子链

(1)钩子链

  ①每一个钩子都有一个与之相关联的指针列表,称之为钩子链表,由系统来维护。这个列表的指针指向由应用程序定义的、后面由钩子调用的回调函数,也就是该钩子的各个处理子程。

  ②多个程序可以同时安装同一种钩子(如WH_GETMESSAGE),这些钩子组成一个钩子链,最近加入的钩子放在链表的头部,Windows负责为每种钩子维护一个钩子链。

  ③当一个事件发生的时候,Windows调用最后安装的钩子,然后由这个钩子的回调函数发起调用下一个钩子的动作,Windows收到这个动作后,再从链表中取出下一个钩子的地址并将调用传递下去。

(2)CallNextHookEx函数:调用下一个钩子回调函数的函数。

【Desktop Item Position Saver(DIPS)工具】——用来保存/恢复桌面图标的位置

(1)可能遇到的几个问题

  ①获得桌面的ListView控件窗口句柄的方法

  A、XP下

第22章 DLL注入和API拦截(1)

  //桌面的ListView控件是ProgMan窗口的孙子窗口

  hWndLV = GetFirstChild(GetFirstChild(FindWindow(TEXT("ProgMan"),NULL)));

  B、Win8下

第22章 DLL注入和API拦截(1)

HWND GetDestopListViewHandle(){
HWND hWnd = NULL,hWndItem;
HWND hWndParent = FindWindowEx(,,TEXT("WorkerW"),NULL);
while (hWndParent && (!hWnd)){ hWndItem = FindWindowEx(hWndParent, , TEXT("SHELLDLL_DefView"), NULL);
if (hWndItem){
hWnd = FindWindowEx(hWndItem, , TEXT("SysListView32"), TEXT("FolderView"));
break;
}
hWndParent = FindWindowEx(, hWndParent, TEXT("WorkerW"), NULL);
}
return hWnd;
}

  ②获取创建窗口的线程ID:dwThreadID = GetWindowThreadProcessID(hwnd,NULL)

  ③为Windows资源管理器创建一个“隐藏窗口”用于与DIPS.exe通信(利用窗口消息)

  ④卸载钩子时,须先销毁“隐藏窗口”这个对话框,再清除钩子。否则当对话框收到一下条消息时会导致Windows资源管理器的线程引发访问违规。

(2)实例源码

第22章 DLL注入和API拦截(1)

第22章 DLL注入和API拦截(1)

【动态链接库】

/************************************************************************
Module: DIPSLib.h
Notices: Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre
************************************************************************/
#pragma once
#include <windows.h> #if !defined(DIPSLIBAPI)
#define DIPSLIBAPI __declspec(dllimport)
#endif ////////////////////////////////////////////////////////////////////////// //导出的外部函数原型
DIPSLIBAPI BOOL WINAPI SetDIPSHook(DWORD dwThreadId); //////////////////////////////////////////////////////////////////////////

//DIPSLib.cpp

/************************************************************************
Module: DIPSLib.cpp
Notices: Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre
************************************************************************/ #include "../../CommonFiles/CmnHdr.h" #define DIPSLIBAPI __declspec(dllexport)
#include "DIPSLib.h"
#include "resource.h" //////////////////////////////////////////////////////////////////////////
#ifdef _DEBUG
//这个函数会强制调试器被调用
void ForceDebugBreak(){
__try{ DebugBreak();}
__except (UnhandledExceptionFilter(GetExceptionInformation())){}
}
#else
#define ForceDebugBreak()
#endif //////////////////////////////////////////////////////////////////////////
//前置声明
LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam); INT_PTR WINAPI Dlg_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); //////////////////////////////////////////////////////////////////////////
//通知编译器将g_hHook变量放在共享数据段中,以便让不同进程共享
#pragma data_seg("Shared")
HHOOK g_hHook = NULL;
DWORD g_dwThreadIdDIPS = ; //安装钩子的线程(这里是DIPS.exe中一个线程)
#pragma data_seg() #pragma comment(linker,"/section:Shared,rws") //可读可读、可共享 ////////////////////////////////////////////////////////////////////////// HINSTANCE g_hInstDll = NULL; //////////////////////////////////////////////////////////////////////////
BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, PVOID fImpLoad){
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
g_hInstDll = hInstDll;
break; case DLL_THREAD_ATTACH:
break; case DLL_THREAD_DETACH:
break; case DLL_PROCESS_DETACH:
break;
}
return (TRUE);
} //////////////////////////////////////////////////////////////////////////
BOOL WINAPI SetDIPSHook(DWORD dwThreadId){
BOOL bOk = FALSE; if (dwThreadId != ){
chASSERT(g_hHook == NULL);//断言钩子还未被安装 //保存我们的线程ID,以便在GetMsgProc函数里可以通过客户程序(DIPS.exe)与
//服务程序(Windows资源管理器)间进行通信
g_dwThreadIdDIPS = GetCurrentThreadId(); //为指定的线程安装WH_GETMESSAGE钩子
g_hHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hInstDll, dwThreadId); bOk = (g_hHook != NULL);
if (bOk){
//钩子安装成功,发送一条消息到Windows资源管理器的消息队列里,这时资源管理器
//会检测到一条消息,并调用GetMessage,于是钩子函数就开始工作。
bOk = PostThreadMessage(dwThreadId, WM_NULL, , );
} } else{
//当传入dwThreadId为0时,表示要卸载钩子
chASSERT(g_hHook != NULL);//断言钩子己被安装,
bOk = UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
} return (bOk);
} //////////////////////////////////////////////////////////////////////////
LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam){
static BOOL bFirstTime = TRUE; if (bFirstTime){
//Dll刚被注入
bFirstTime = FALSE; //当调试被注入DLL的进程时,要取消注释
//ForceDebugBreak(); //创建DIPS服务窗口,以处理客户请求
CreateDialog(g_hInstDll, MAKEINTRESOURCE(IDD_DIPS), NULL, Dlg_Proc); //通知DIPS应用程序服务窗口己创建完毕,可以接收请求了
PostThreadMessage(g_dwThreadIdDIPS, WM_NULL, , );
}
return (CallNextHookEx(g_hHook, nCode, wParam, lParam));
} //////////////////////////////////////////////////////////////////////////
void Dlg_OnClose(HWND hWnd){
DestroyWindow(hWnd);
} //////////////////////////////////////////////////////////////////////////
static const TCHAR g_szRegSubKey[] =
TEXT("Software\\Wintellect\\Desktop Item Position Saver"); //////////////////////////////////////////////////////////////////////////
void SaveListViewItemPositions(HWND hWndLV){
int nMaxItems = ListView_GetItemCount(hWndLV); //删除先前的旧信息
LONG lRet = RegDeleteKey(HKEY_CURRENT_USER, g_szRegSubKey); //创建新的注册表项
HKEY hKey;
lRet = RegCreateKeyEx(HKEY_CURRENT_USER, g_szRegSubKey, , NULL, REG_OPTION_NON_VOLATILE,
KEY_SET_VALUE, NULL, &hKey, NULL); chASSERT(lRet == ERROR_SUCCESS); for (int nItem = ; nItem < nMaxItems;nItem++){ //获取ListItem项的名称及位置
TCHAR szName[MAX_PATH];
ListView_GetItemText(hWndLV, nItem, , szName, _countof(szName)); POINT pt;
ListView_GetItemPosition(hWndLV, nItem, &pt); //保存名称及位置
lRet = RegSetValueEx(hKey, szName, , REG_BINARY, (PBYTE)&pt, sizeof(pt)); chASSERT(lRet == ERROR_SUCCESS);
} RegCloseKey(hKey);
} //////////////////////////////////////////////////////////////////////////
void RestoreListViewItemPosition(HWND hWndLV){
HKEY hKey;
LONG lRet = RegOpenKeyEx(HKEY_CURRENT_USER, g_szRegSubKey, , KEY_QUERY_VALUE, &hKey); if (lRet != ERROR_SUCCESS)
return; //如果ListView打开了自动排列属性,则暂时关闭
DWORD dwStyle = GetWindowStyle(hWndLV);
if (dwStyle & LVS_AUTOARRANGE)
SetWindowLong(hWndLV, GWL_STYLE, dwStyle & ~LVS_AUTOARRANGE); lRet = NO_ERROR;
for (int nIndex = ; lRet != ERROR_NO_MORE_ITEMS; nIndex++){
TCHAR szName[MAX_PATH];
DWORD cbValueName = _countof(szName);//必须要在循环体内重新设置该值,因为每次会被RegEnumValue改变,用来接受下一个szName POINT pt;
DWORD cbData = sizeof(pt), nItem;//必须要在循环体内重新设置cbData,因为每次会被RegEnumValue改变 //从注册表中读取名称及位置信息
DWORD dwType; lRet = RegEnumValue(hKey, nIndex, szName, &cbValueName, NULL, &dwType, (PBYTE)&pt, &cbData); if (lRet == ERROR_NO_MORE_ITEMS)
continue; if ((dwType == REG_BINARY) && (cbData = sizeof(pt))){
//查找ListView控件相应的项目
LV_FINDINFO lvfi;
lvfi.flags = LVFI_STRING;
lvfi.psz = szName;
nItem = ListView_FindItem(hWndLV, -, &lvfi);
if (nItem != -){
ListView_SetItemPosition(hWndLV, nItem, pt.x, pt.y);
}
}
}
//将自动排列属性恢复为原来的值
SetWindowLong(hWndLV, GWL_STYLE, dwStyle);
RegCloseKey(hKey);
} //////////////////////////////////////////////////////////////////////////
INT_PTR WINAPI Dlg_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
switch (uMsg)
{
chHANDLE_DLGMSG(hWnd, WM_CLOSE, Dlg_OnClose); //隐藏服务窗口
case WM_APP:
//当调试被注入DLL的进程时,要取消注释
//ForceDebugBreak(); //lParam:表示保存或恢复操作。wParam:桌面ListView的句柄
if (lParam)
SaveListViewItemPositions((HWND)wParam);
else
RestoreListViewItemPosition((HWND)wParam);
break;
}
return (FALSE);
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 22_DipsLib.rc 使用
//
#define IDD_DIPS 101 // Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

//rc文件

// Microsoft Visual C++ generated resource script.
//
#include "resource.h" #define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h" /////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS /////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
// TEXTINCLUDE
BEGIN
"resource.h\0"
END TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END #endif // APSTUDIO_INVOKED /////////////////////////////////////////////////////////////////////////////
//
// Dialog
// IDD_DIPS DIALOG DISCARDABLE , , ,
STYLE WS_CAPTION
CAPTION "Wintellect DIPS"
FONT , "MS Shell Dlg"
BEGIN
END /////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
// #ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_DIALOG1, DIALOG
BEGIN
LEFTMARGIN,
RIGHTMARGIN,
TOPMARGIN,
BOTTOMMARGIN,
END
END
#endif // APSTUDIO_INVOKED #endif // 中文(简体,中国) resources
///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
// /////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

【DIPS工具源码】

/************************************************************************
Module: DIPS.cpp
Notices: Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre
************************************************************************/ #include "../../CommonFiles/CmnHdr.h"
#include <tchar.h>
#include "resource.h"
#include "../22_DipsLib/DIPSLib.h" #pragma comment(lib,"../../Debug/22_DIPSLib.lib")
//////////////////////////////////////////////////////////////////////////
BOOL Dlg_OnInitDialog(HWND hWnd, HWND hWndFocus, LPARAM lParam){
chSETDLGICONS(hWnd, IDI_DIPS);
return (TRUE);
} //////////////////////////////////////////////////////////////////////////
void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtrl, UINT codeNotity){
switch (id)
{
case IDC_SAVE:
case IDC_RESTORE:
case IDCANCEL:
EndDialog(hwnd, id);
break;
}
} //////////////////////////////////////////////////////////////////////////
INT_PTR WINAPI Dlg_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
switch (uMsg){
chHANDLE_DLGMSG(hWnd, WM_INITDIALOG, Dlg_OnInitDialog);
chHANDLE_DLGMSG(hWnd, WM_COMMAND, Dlg_OnCommand);
} return (FALSE);
} HWND GetDestopListViewHandle(){
HWND hWnd = NULL,hWndItem;
HWND hWndParent = FindWindowEx(,,TEXT("WorkerW"),NULL);
while (hWndParent && (!hWnd)){ hWndItem = FindWindowEx(hWndParent, , TEXT("SHELLDLL_DefView"), NULL);
if (hWndItem){
hWnd = FindWindowEx(hWndItem, , TEXT("SysListView32"), TEXT("FolderView"));
break;
}
hWndParent = FindWindowEx(, hWndParent, TEXT("WorkerW"), NULL);
}
return hWnd;
} //////////////////////////////////////////////////////////////////////////
int WINAPI _tWinMain(HINSTANCE hInstExe, HINSTANCE, PTSTR pszCmdLine, int){
//将命令行转为大写字符串
CharUpperBuff(pszCmdLine, );
TCHAR cWhatToDo = pszCmdLine[]; if ((cWhatToDo !=TEXT('S')) && (cWhatToDo != TEXT('R'))){
cWhatToDo = ; //无效的命令行参数
} //如果是从GUI界面启动本程序,而不是命令行启动
if (cWhatToDo == ){
//无命令行参数时,显示对话框以让用户相应的选择
switch (DialogBox(hInstExe, MAKEINTRESOURCE(IDD_DIPS), NULL, Dlg_Proc)){
case IDC_SAVE:
cWhatToDo = TEXT('S');
break;
case IDC_RESTORE:
cWhatToDo = TEXT('R');
break;
}
} //表示用户什么也不做
if (cWhatToDo == ){
return ();
} //获取桌面listView的窗口句柄,他是 HWND hWndLV = GetDestopListViewHandle();
chASSERT(IsWindow(hWndLV)); //安装钩子到Explorer进程的地址空间。当安装完成后,Explorer里会创建一个“隐藏”
//的窗口,我们可以向这个窗口发送消息,以便告诉他我们要想做的事情
chASSERT(SetDIPSHook(GetWindowThreadProcessId(hWndLV, NULL))); //阻塞等待DIPS服务器窗口创建完毕,当创建完毕后,这里GetMessage会收到通知,线程被唤醒
MSG msg;
GetMessage(&msg, NULL, , );//阻塞等待 //查找服务端这个被隐藏的窗口
HWND hWndDIPS = FindWindow(NULL, TEXT("Wintellect DIPS")); chASSERT(IsWindow(hWndDIPS)); //断言窗口是存在的 //告诉服务端的窗口进行保存或恢复桌面图标操作
BOOL bSave = (cWhatToDo == TEXT('S'));
SendMessage(hWndDIPS, WM_APP, (WPARAM)hWndLV, bSave); //销毁隐形窗口,使用SendMessage可以保证在函数返回后,窗口己这被销毁
SendMessage(hWndDIPS, WM_CLOSE, , ); chASSERT(!IsWindow(hWndDIPS)); //断言窗口己被销毁 //卸载钩子
SetDIPSHook();
return ();
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 22_DIPS.rc 使用
//
#define IDC_RESTORE 3
#define IDI_DIPS 101
#define IDD_DIPS 102
#define IDC_SAVE 1000 // Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

//rc文件

// Microsoft Visual C++ generated resource script.
//
#include "resource.h" #define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h" /////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS /////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED /////////////////////////////////////////////////////////////////////////////
//
// Icon
// // Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_DIPS ICON "DIPS.ico" #ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
// TEXTINCLUDE
BEGIN
"resource.h\0"
END TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END #endif // APSTUDIO_INVOKED /////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_DIPS DIALOGEX , , ,
STYLE DS_SETFONT | DS_3DLOOK | DS_CENTER | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU
CAPTION "桌面图标位置保存工具"
FONT , "MS Shell Dlg", , , 0x1
BEGIN
CTEXT "DIPS: 桌面图标位置保存工具\n版权所有 (c) 2016 by 浅墨浓香.", IDC_STATIC, , , , , SS_SUNKEN, WS_EX_DLGMODALFRAME | WS_EX_STATICEDGE
LTEXT "这个工具可以保存或恢复桌面图标的位置.\n\n保存位置: DIPS S\n恢复位置: DIPS R", IDC_STATIC, , , ,
DEFPUSHBUTTON "&保存", IDC_SAVE, , , ,
PUSHBUTTON "&恢复", IDC_RESTORE, , , ,
PUSHBUTTON "取消", IDCANCEL, , , ,
END /////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
// #ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_DIALOG1, DIALOG
BEGIN
LEFTMARGIN,
RIGHTMARGIN,
TOPMARGIN,
BOTTOMMARGIN,
END
END
#endif // APSTUDIO_INVOKED #endif // 中文(简体,中国) resources
///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
// /////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED