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


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

     

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

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

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

    22.2 使用注册表来注入DLL

    (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下

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

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

      B、Win8下

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

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

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

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

    (2)实例源码

    【动态链接库】

    /************************************************************************
    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 = 0; //安装钩子的线程(这里是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 != 0){
            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, 0, 0);
            }
    
        } 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, 0, 0);
        }
        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, 0, NULL, REG_OPTION_NON_VOLATILE,
                              KEY_SET_VALUE, NULL, &hKey, NULL);
    
        chASSERT(lRet == ERROR_SUCCESS);
    
        for (int nItem = 0; nItem < nMaxItems;nItem++){
    
            //获取ListItem项的名称及位置
            TCHAR szName[MAX_PATH];
            ListView_GetItemText(hWndLV, nItem, 0, szName, _countof(szName));
    
            POINT pt;
            ListView_GetItemPosition(hWndLV, nItem, &pt);
    
            //保存名称及位置
            lRet = RegSetValueEx(hKey, szName, 0, 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, 0, 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 = 0; 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, -1, &lvfi);
                if (nItem != -1){
                    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
    //
    
    1 TEXTINCLUDE 
    BEGIN
        "resource.h"
    END
    
    2 TEXTINCLUDE 
    BEGIN
        "#include ""winres.h""
    "
        ""
    END
    
    3 TEXTINCLUDE 
    BEGIN
        "
    "
        ""
    END
    
    #endif    // APSTUDIO_INVOKED
    
    
    /////////////////////////////////////////////////////////////////////////////
    //
    // Dialog
    //
    
    IDD_DIPS DIALOG DISCARDABLE  0, 0, 132, 13
    STYLE WS_CAPTION
    CAPTION "Wintellect DIPS"
    FONT 8, "MS Shell Dlg"
    BEGIN
    END
    
    /////////////////////////////////////////////////////////////////////////////
    //
    // DESIGNINFO
    //
    
    #ifdef APSTUDIO_INVOKED
    GUIDELINES DESIGNINFO
    BEGIN
        IDD_DIALOG1, DIALOG
        BEGIN
            LEFTMARGIN, 7
            RIGHTMARGIN, 302
            TOPMARGIN, 7
            BOTTOMMARGIN, 169
        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(0,0,TEXT("WorkerW"),NULL);
        while (hWndParent && (!hWnd)){
    
            hWndItem = FindWindowEx(hWndParent, 0, TEXT("SHELLDLL_DefView"), NULL);
            if (hWndItem){
                hWnd = FindWindowEx(hWndItem, 0, TEXT("SysListView32"), TEXT("FolderView"));
                break;
            }
            hWndParent = FindWindowEx(0, hWndParent, TEXT("WorkerW"), NULL);
        }
        return hWnd;
    }
    
    //////////////////////////////////////////////////////////////////////////
    int WINAPI _tWinMain(HINSTANCE hInstExe, HINSTANCE, PTSTR pszCmdLine, int){
        //将命令行转为大写字符串
        CharUpperBuff(pszCmdLine, 1);
        TCHAR cWhatToDo = pszCmdLine[0];
    
        if ((cWhatToDo !=TEXT('S')) && (cWhatToDo != TEXT('R'))){
            cWhatToDo = 0; //无效的命令行参数
        }
    
        //如果是从GUI界面启动本程序,而不是命令行启动
        if (cWhatToDo == 0){
            //无命令行参数时,显示对话框以让用户相应的选择
            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 == 0){
            return (0);
        }
    
        //获取桌面listView的窗口句柄,他是
        
        HWND hWndLV = GetDestopListViewHandle();
        chASSERT(IsWindow(hWndLV));
    
        //安装钩子到Explorer进程的地址空间。当安装完成后,Explorer里会创建一个“隐藏”
        //的窗口,我们可以向这个窗口发送消息,以便告诉他我们要想做的事情
        chASSERT(SetDIPSHook(GetWindowThreadProcessId(hWndLV, NULL)));
    
        //阻塞等待DIPS服务器窗口创建完毕,当创建完毕后,这里GetMessage会收到通知,线程被唤醒
        MSG msg;
        GetMessage(&msg, NULL, 0, 0);//阻塞等待
    
        //查找服务端这个被隐藏的窗口
        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, 0, 0);
    
        chASSERT(!IsWindow(hWndDIPS)); //断言窗口己被销毁
    
        //卸载钩子
        SetDIPSHook(0);
        return (0);
    }

    //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
    //
    
    1 TEXTINCLUDE 
    BEGIN
        "resource.h"
    END
    
    2 TEXTINCLUDE 
    BEGIN
        "#include ""winres.h""
    "
        ""
    END
    
    3 TEXTINCLUDE 
    BEGIN
        "
    "
        ""
    END
    
    #endif    // APSTUDIO_INVOKED
    
    
    /////////////////////////////////////////////////////////////////////////////
    //
    // Dialog
    //
    IDD_DIPS DIALOGEX 0, 0, 208, 95
    STYLE DS_SETFONT | DS_3DLOOK | DS_CENTER | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU
    CAPTION "桌面图标位置保存工具"
    FONT 8, "MS Shell Dlg", 0, 0, 0x1
    BEGIN
        CTEXT           "DIPS: 桌面图标位置保存工具
    版权所有 (c) 2016 by 浅墨浓香.", IDC_STATIC, 4, 4, 200, 20, SS_SUNKEN, WS_EX_DLGMODALFRAME | WS_EX_STATICEDGE
        LTEXT           "这个工具可以保存或恢复桌面图标的位置.
    
    保存位置: DIPS S
    恢复位置: DIPS R", IDC_STATIC, 4, 32, 200, 36
        DEFPUSHBUTTON   "&保存", IDC_SAVE, 4, 76, 50, 14
        PUSHBUTTON      "&恢复", IDC_RESTORE, 79, 76, 50, 14
        PUSHBUTTON      "取消", IDCANCEL, 154, 76, 50, 14
    END
    
    /////////////////////////////////////////////////////////////////////////////
    //
    // DESIGNINFO
    //
    
    #ifdef APSTUDIO_INVOKED
    GUIDELINES DESIGNINFO
    BEGIN
        IDD_DIALOG1, DIALOG
        BEGIN
            LEFTMARGIN, 7
            RIGHTMARGIN, 302
            TOPMARGIN, 7
            BOTTOMMARGIN, 169
        END
    END
    #endif    // APSTUDIO_INVOKED
    
    #endif    // 中文(简体,中国) resources
    /////////////////////////////////////////////////////////////////////////////
    
    
    
    #ifndef APSTUDIO_INVOKED
    /////////////////////////////////////////////////////////////////////////////
    //
    // Generated from the TEXTINCLUDE 3 resource.
    //
    
    
    /////////////////////////////////////////////////////////////////////////////
    #endif    // not APSTUDIO_INVOKED
  • 相关阅读:
    Updates were rejected because the tip of your current branch is behind 问题出现解决方案
    git初始化本地项目及关联github远程库
    git项目提交后执行添加忽略操作
    HTML5基础总结
    图表Echarts的使用
    百度API使用--javascript api进行多点定位
    HtmlAgilityPach基本使用方法
    Redis可以做哪些事儿?
    Asp.Net将Session保存在数据库中
    css基础学习
  • 原文地址:https://www.cnblogs.com/5iedu/p/5180828.html
Copyright © 2020-2023  润新知