浅墨浓香

想要天亮进城,就得天黑赶路。

导航

第4章 进程(4)

Posted on 2015-07-28 10:03  浅墨浓香  阅读(1545)  评论(0编辑  收藏  举报

4.5.8 ProcessInfo示例程序

(1)自定义函数

函数名称

功能

GetProcessIntegrityLevel

1、获取进程完整性级别和代码策略:分别在GetTokenInformation中

   TokenIntegrityLevel及TokenMandatoryPolicy

2、获取资源完整性级别及资源策略:调用API—GetSecurityInfo传入LABEL_SECURITY_INFORMATION,然后从SACL中获取

GetProcessElevation

1、获取令牌提升的类型:

GetTokenInformation(…,TokenElevationType,…),分别对应

TokenElevationTypeDefault、TokenEvationTypeLimited、Token、

TokenElevationTypeFull

2、判断当前进程是否以管理员身份运行

StartElevatedProcess

手动提升权限,利用ShellExecuteEx函数,结构体SHELLEXECUTEINFO参数中lpVert=TEXT("runas"),lpFile = szExecutable(应用程序文件名)

GetProcessCmdLine

1、获取远程进程的命令行(注意,与GetCommandLine不同,该函数不仅仅获得当前进程的命令行,还可以获得远程进程的命令行)

2、该函数是通过NT内核的函数NtQueryInformationProcess获得远程进程的环境块来实现的

GetProcessOwner

获取进程属主:通过GetTokenInformation传入TokenUser

GetModulePreferredBaseAddr

获取某进程中指定模块首选的装载基址。通过调用Toolhelp32ReadProcessMemory来获取进程DOS及NT头结构。

(2)CToolhelp类

函数名称

功能

EnablePrivilege

提升进程权限

CreateSnapshot

创建进程快照,可以用来枚举系统正在运行的进程,该函数是通过调用CreateToolhelp32Snapshot来实现的。

ProcessFirst、ProcessNext

进程快照中的第1个和下一个进程,通过调用Process32First及Process32Next实现

ModuleFirst、ModuleNext

进程快照中含所有的模块,这个函数分别获得第1个及下一个。

通过Module32First及Module32Next来实现

ThreadFirst、ThreadNext

进程快照中含所有的正在运行的线程,分别获取第1个和下一个线程。通过Thread32First及Thread32Next实现。

HeapListFirst、HeapListNext

进程快照中所有的堆,分别获取堆链表中的第1个和下一个堆。通过Heap32ListFirst及Heap32ListNext实现

HowManyHeaps

进程快照中共有多少个堆

HeapFirst、HeapNext

指定进程中的第1个堆和下一个堆,通过Heap32First和Heap32Next实现

IsAHeap

判断给定的地址是否在指定进程的堆中,通过VirtualQueryEx实现

HowManyBlockInHeap

获得指定进程共有多少个堆

RetriveTokenInformationClass

获得令牌中指定的信息,要用自定义的FreeTokenInformation来释放内存。

【ProcessInfo程序】——枚举所有进程、线程及模块信息

Process截图

 

Module截图

/***********************************************************************
Module:  ProcessInfo.cpp
Notices: Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre
************************************************************************/
#include "..\\..\\CommonFiles\CmnHdr.h"
#include "..\\..\\CommonFiles\ToolHelp.h"
#include <windows.h>
#include "resource.h"
#include <tchar.h>

#include <Shlwapi.h>    //for StrFormatKBSize
#include <winternl.h>   //for Windows internal declarations
#include <aclapi.h>     //for ACL management
#include <ShlObj.h>     //for IsUserAnAdmin

#include <strsafe.h>

//////////////////////////////////////////////////////////////////////////

#pragma  comment(lib,"shlwapi.lib")

/************************************************************************/
//静态变量

//Token类型:默认——即禁用UAC,完全访问权限—未筛选,受限——筛选的
TOKEN_ELEVATION_TYPE s_elevationType = TokenElevationTypeDefault; 
BOOL                 s_bIsAdmin         = FALSE;
const     int        s_cchAddress     = sizeof(PVOID) * 2;

/************************************************************************/

//////////////////////////////////////////////////////////////////////////
//在编辑框控件中增加文本
void AddText(HWND hwnd, PCTSTR pszFormat, ...)
{
    va_list argList;
    va_start(argList, pszFormat);
    TCHAR sz[100 * 1024];
    Edit_GetText(hwnd, sz, _countof(sz));

    //_tcschr从一个字符串中查找字符,返回str中的第一个字符c出现的位置
    //开始的字符串的指针
    _vstprintf_s(_tcschr(sz, TEXT('\0')),   //wchar_t *buffer,
                  _countof(sz)-_tcslen(sz), //size_t numberOfElements,
                  pszFormat,                //const wchar_t *format,
                  argList);                    //va_list argptr
    Edit_SetText(hwnd, sz);
    va_end(argList);
}

//////////////////////////////////////////////////////////////////////////
//获取进程完整性级别及代码策略、资源的完整性级别及资源策略
BOOL GetProcessIntegrityLevel(HANDLE hProcess, PDWORD pIntegrityLevel,
    PDWORD pPolicy, PDWORD pResourceIntegrityLevel, PDWORD pResourcePolicy)
{
    BOOL bReturn = FALSE;
    HANDLE hToken = NULL;

    if (!OpenProcessToken(hProcess, TOKEN_READ, &hToken))
        return bReturn;

    //首先,计算接收进程完整性级别信息的缓冲区大小
    DWORD dwNeededSize = 0;
    if (!GetTokenInformation(hToken,TokenIntegrityLevel,NULL,0,&dwNeededSize)){
        PTOKEN_MANDATORY_LABEL pTokenInfo = NULL;
        if (ERROR_INSUFFICIENT_BUFFER == GetLastError()){
            //接着,根据dwNeededSize分配内存
            pTokenInfo = (PTOKEN_MANDATORY_LABEL)LocalAlloc(0, dwNeededSize);{
                //最后,获得完整性级别
                if (GetTokenInformation(hToken,TokenIntegrityLevel,
                             pTokenInfo,dwNeededSize,&dwNeededSize)){
                    //SID:S-1-5-21-917267712-1342860078-1792151419-500
                    //第1项:S表示字符串SID,第2项1表示SID的版本号,第3项5表示授权机构ID
                    //而后面的21-917267712-1342860078-1792151419就是以下函数要获取的子授
                    //限机构ID(一般为用来标志域的),最后一项为500为帐号和组,这里表示administrator用户
                    *pIntegrityLevel = *GetSidSubAuthority(pTokenInfo->Label.Sid,
                                          (*GetSidSubAuthorityCount(pTokenInfo->Label.Sid) - 1));
                    
                    bReturn = TRUE;
                }
                //释放内存
                LocalFree(pTokenInfo);
            }
        }
    }

    //如果进程完整性级别用可,就尝试获取代码策略
    if (bReturn){
        *pPolicy = TOKEN_MANDATORY_POLICY_OFF;
        dwNeededSize = sizeof(DWORD);
        GetTokenInformation(hToken, TokenMandatoryPolicy, pPolicy, dwNeededSize, &dwNeededSize);
    }

    //查看资源策略
    *pResourceIntegrityLevel = 0; //0表示没有显式设置
    *pResourcePolicy = 0;

    PACL pSACL = NULL;
    PSECURITY_DESCRIPTOR pSD = NULL;
    DWORD dwResult = ERROR_SUCCESS;

    //从SACL中查找资源策略:no-read-up/no-write-up
    if (NULL != hToken){
        //获取指定对象SD的副本,要用 LocalFree(pSD)
        dwResult = GetSecurityInfo(hProcess, SE_KERNEL_OBJECT,
                        LABEL_SECURITY_INFORMATION,
                        NULL, NULL, NULL, 
                        &pSACL, &pSD);
        if (ERROR_SUCCESS == dwResult){
            if (NULL != pSACL){
                SYSTEM_MANDATORY_LABEL_ACE* pACE = NULL;
                //SD中SACL列表中的第1项表示资源完整性及策略
                if ((pSACL->AceCount > 0) && (GetAce(pSACL, 0, (PVOID*)&pACE))){
                    if (pACE != NULL){
                        SID* pSID = (SID*)(&pACE->SidStart);
                        *pResourceIntegrityLevel = pSID->SubAuthority[0]; //第1个子授权项表示完整性级别
                        *pResourcePolicy = pACE->Mask; //资源策略NO-WRITE-UP/NO-READ-UP
                    }
                }
            }
        }
        if (pSD != NULL)LocalFree(pSD); //清理内存
    }

    CloseHandle(hToken);//关闭hToken句柄
    return bReturn;
}

BOOL GetProcessIntegrityLevel(DWORD PID, PDWORD pIntegrityLevel, PDWORD pPolicy,
    PDWORD pResourceIntegrityLevel, PDWORD pResourcePolicy)
{
    BOOL bReturn = FALSE;
    //合法性检查
    if ((PID <= 0) || (pIntegrityLevel == NULL))
        return FALSE;

    //检查是否可以获取进程信息
    HANDLE hProcess = OpenProcess(READ_CONTROL | PROCESS_QUERY_INFORMATION,
                                    FALSE, PID);
    if (hProcess == NULL)
        return FALSE;

    bReturn = GetProcessIntegrityLevel(hProcess, pIntegrityLevel, pPolicy, 
                                         pResourceIntegrityLevel, pResourcePolicy);
    CloseHandle(hProcess); //关闭进程句柄
    return bReturn;
}

//////////////////////////////////////////////////////////////////////////
void Dlg_PopulateProcessList(HWND hwnd)
{
    HWND hwndList = GetDlgItem(hwnd, IDC_PROCESSMODULELIST);
    SetWindowRedraw(hwndList, FALSE);
    ComboBox_ResetContent(hwndList); //重置下拉列表框

    //the Processes:所有运行中的进程,参数函数的第2个参数省略,默认为0
    CToolhelp  thProcesses(TH32CS_SNAPPROCESS);
    //存放快照进程信息的一个结构体
    /*
    dwSize:结构体大小,cntUsage:此进程的引用计数。th32ProcessID:进程ID
    th32DefaultHeapID:进程默认堆ID(总是为0) th32ModuleID:总是为0
    cnTreads进程开启的线程性 th32ParentProcessID:父进程ID
    pcPriClass:线程基础优先级 dwFlags总是0 szExeFile进程的可执行文件名称
    */
    PROCESSENTRY32 pe = { sizeof(pe) };

    TCHAR sz[1024];
    BOOL fOk = thProcesses.ProcessFirst(&pe);
    for (; fOk; fOk = thProcesses.ProcessNext(&pe))
    {    
        //进程名称(不含路径)和ID
        //_tcsrchr从一个字符串中查找字符'\'最后出现位置(指针)
        PCTSTR pszExeFile = _tcsrchr(pe.szExeFile, TEXT('\\'));
        if (pszExeFile == NULL)
            pszExeFile = pe.szExeFile;
        else
            pszExeFile++;//跳过'\',指定进程名称

        //追加代码/资源完整性级别和策略
        DWORD dwCodeIntergrityLevel = 0;
        DWORD dwCodePolicy = TOKEN_MANDATORY_POLICY_OFF;
        DWORD dwResourcePolicy = 0;
        DWORD dwResourceIntegrityLevel = 0;
        TCHAR szCodeDetails[256];
        TCHAR szResourceDetails[256];

        szCodeDetails[0] = TEXT('\0');
        szResourceDetails[0] = TEXT('\0');

        if (GetProcessIntegrityLevel(pe.th32ProcessID,&dwCodeIntergrityLevel,&dwCodePolicy,
                 &dwResourceIntegrityLevel,&dwResourcePolicy))
        {
            //进程完整性级别及代码策略
            switch (dwCodeIntergrityLevel) 
            {
            case SECURITY_MANDATORY_LOW_RID:
                _tcscpy_s(szCodeDetails, _countof(szCodeDetails), TEXT("- Low "));
                break;

            case SECURITY_MANDATORY_MEDIUM_RID:
                _tcscpy_s(szCodeDetails, _countof(szCodeDetails), TEXT("- Medium "));
                break;

            case SECURITY_MANDATORY_HIGH_RID:
                _tcscpy_s(szCodeDetails, _countof(szCodeDetails), TEXT("- High "));
                break;

            case SECURITY_MANDATORY_SYSTEM_RID:
                _tcscpy_s(szCodeDetails, _countof(szCodeDetails), TEXT("- System "));
                break;

            default:
                _tcscpy_s(szCodeDetails, _countof(szCodeDetails), TEXT("- ??? "));

            }

            if (dwCodePolicy == TOKEN_MANDATORY_POLICY_OFF)  //0
                _tcscat_s(szCodeDetails, _countof(szCodeDetails), TEXT(" + no policy"));
            else {
                if ((dwCodePolicy & TOKEN_MANDATORY_POLICY_VALID_MASK)==0)
                {
                    _tcscat_s(szCodeDetails, _countof(szCodeDetails), TEXT(" + ???"));
                }
                else
                {
                    if (dwCodePolicy & TOKEN_MANDATORY_POLICY_NO_WRITE_UP)
                    {
                        _tcscat_s(szCodeDetails, _countof(szCodeDetails), TEXT(" + no write-up"));
                    }

                    if (dwCodePolicy & TOKEN_MANDATORY_POLICY_NEW_PROCESS_MIN)
                    {
                        _tcscat_s(szCodeDetails, _countof(szCodeDetails), TEXT(" + new process min"));
                    }
                }
            }

            //资源完整性级别及代码策略
            switch (dwResourceIntegrityLevel)
            {
            case SECURITY_MANDATORY_LOW_RID:
                _tcscpy_s(szResourceDetails, _countof(szResourceDetails), TEXT("Low"));
                break;

            case SECURITY_MANDATORY_MEDIUM_RID:
                _tcscpy_s(szResourceDetails, _countof(szResourceDetails), TEXT("Medium"));
                break;

            case SECURITY_MANDATORY_HIGH_RID:
                _tcscpy_s(szResourceDetails, _countof(szResourceDetails), TEXT("High"));
                break;

            case SECURITY_MANDATORY_SYSTEM_RID:
                _tcscpy_s(szResourceDetails, _countof(szResourceDetails), TEXT("System"));
                break;

            case 0:
                _tcscpy_s(szResourceDetails, _countof(szResourceDetails), TEXT("Not Set"));
                break;

            default:
                _tcscpy_s(szResourceDetails, _countof(szResourceDetails), TEXT("???"));
            }

            if (dwResourcePolicy == 0){  //没有策略
                _tcscat_s(szResourceDetails, _countof(szResourceDetails), TEXT(" + 0 policy"));
            }else{
                if ((dwResourcePolicy & TOKEN_MANDATORY_POLICY_VALID_MASK)==0){
                    _tcscat_s(szResourceDetails, _countof(szResourceDetails), TEXT(" + ???"));
                }else
                {
                    if ((dwResourcePolicy & SYSTEM_MANDATORY_LABEL_NO_WRITE_UP) ==
                        SYSTEM_MANDATORY_LABEL_NO_WRITE_UP){
                        _tcscat_s(szResourceDetails, _countof(szResourceDetails), TEXT(" + no write-up"));
                    }

                    if ((dwResourcePolicy & SYSTEM_MANDATORY_LABEL_NO_READ_UP) ==
                        SYSTEM_MANDATORY_LABEL_NO_READ_UP){
                        _tcscat_s(szResourceDetails, _countof(szResourceDetails), TEXT(" + no read-up"));
                    }

                    if ((dwResourcePolicy & SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP) ==
                        SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP){
                        _tcscat_s(szResourceDetails, _countof(szResourceDetails), TEXT(" + no excute-up"));
                    }
                }
            }
        }

        //
        StringCchPrintf(sz, _countof(sz), TEXT("%s (0x%08X) %s    [%s]"),
                            pszExeFile,pe.th32ProcessID,szCodeDetails,szResourceDetails);
        int n = ComboBox_AddString(hwndList, sz);
        ComboBox_SetItemData(hwndList, n, pe.th32ProcessID);

    } //End for

    ComboBox_SetCurSel(hwndList, 0);
    SetWindowRedraw(hwndList, TRUE);
    InvalidateRect(hwndList, NULL, FALSE);

}

//////////////////////////////////////////////////////////////////////////
void Dlg_PopulateModuleList(HWND hwnd)
{
    HWND hwndModuleHelp = GetDlgItem(hwnd, IDC_MODULEHELP);
    ListBox_ResetContent(hwndModuleHelp);

    CToolhelp thProcesses(TH32CS_SNAPPROCESS);
    PROCESSENTRY32 pe = { sizeof(pe) };
    //增加全部进程的所有模块到列表框中
    BOOL fOk = thProcesses.ProcessFirst(&pe);
    for (; fOk;fOk=thProcesses.ProcessNext(&pe)){
        CToolhelp thModules(TH32CS_SNAPMODULE, pe.th32ProcessID);
        MODULEENTRY32 me = { sizeof(me) };
        BOOL fOk = thModules.ModuleFirst(&me);
        for (; fOk;fOk=thModules.ModuleNext(&me)){
            /*
            LB_FINDSTRING:从指定开始表项序号查找某表项的文本字符串的前面包括指定的字符串则结束,
                          找不到则转到列表框第一项继续查找,直到查完所有表项,如果wParam为-1则从
                          列表框第一项开始查找,如果找到则返回表项序号,否则返回LB_ERR。
                          如:表项字符串为"abc123"和指定字串"ABC"就算匹配。
            LB_FINDSTRINGEXACT:与LB_FINDSTRING不同,本操作必须整个字符串相同。如果找到则返回表项序号,
                          否则返回LB_ERR。
            */
            int n = ListBox_FindStringExact(hwndModuleHelp, -1, me.szExePath);
            if (LB_ERR==n){
                ListBox_AddString(hwndModuleHelp, me.szExePath);
            }
        }
    }

    //
    HWND hwndList = GetDlgItem(hwnd, IDC_PROCESSMODULELIST);
    SetWindowRedraw(hwndList, FALSE);
    ComboBox_ResetContent(hwndList);
    int nNumModules = ListBox_GetCount(hwndModuleHelp);
    for (int i = 0; i < nNumModules;i++){
        TCHAR sz[1024];
        ListBox_GetText(hwndModuleHelp, i, sz);
        //将模块名称(不含路径)增加到组合列表框中
        int nIndex = ComboBox_AddString(hwndList, _tcsrchr(sz, TEXT('\\')) + 1);
        //将列表框索引号关联到组合框中新加入的这项。
        ComboBox_SetItemData(hwndList, nIndex, i);
    }
    ComboBox_SetCurSel(hwndList, 0);//选择组合列表框中的第1项

    //模拟用户选中第1项,并把结果显示在编辑框中
    //发送CBN_SELCHANGE消息
    FORWARD_WM_COMMAND(hwnd, IDC_PROCESSMODULELIST, hwndList, CBN_SELCHANGE, SendMessage);

    SetWindowRedraw(hwndList, TRUE); //重绘组合列表框
    InvalidateRect(hwndList, NULL, FALSE);
}

//////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////
//获取令牌提升的类型及判断当前进程是否以管理员身份运行
BOOL GetProcessElevation(TOKEN_ELEVATION_TYPE* pElevationType, BOOL* pIsAdmin)
{
    HANDLE  hToken = NULL;
    DWORD   dwSize;

    //获得当前进程的Token
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
        return FALSE;

    BOOL bResult = FALSE;

    //获取提升类型
    if (GetTokenInformation(hToken,TokenElevationType,pElevationType,
                                 sizeof(TOKEN_ELEVATION_TYPE),&dwSize))
    {
        //创建管理员组的SID
        byte adminSID[SECURITY_MAX_SID_SIZE];
        dwSize = sizeof(adminSID);
        CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, &adminSID, &dwSize);

        //筛选令牌
        if (*pElevationType == TokenElevationTypeLimited)
        {
            //取得与筛选令牌关联的未筛选令牌
            HANDLE hUnfilteredToken = NULL;
            GetTokenInformation(hToken, TokenLinkedToken,
                (LPVOID)&hUnfilteredToken, sizeof(HANDLE), &dwSize);

            //通过初始未被筛选的令牌判断是否包含管理员SID
            if (CheckTokenMembership(hUnfilteredToken, &adminSID, pIsAdmin))
                bResult = TRUE;
            
            //关闭未筛选的令牌
            CloseHandle(hUnfilteredToken);
        }
        else   //非筛选令牌
        {
            *pIsAdmin = IsUserAnAdmin();
            bResult = TRUE;
        }
    }
    //关闭当前进程的令牌
    CloseHandle(hToken);
    return bResult;
}

//////////////////////////////////////////////////////////////////////////
//手动提升权限
DWORD StartElevatedProcess(LPCTSTR szExecutable, LPCTSTR szCmdLine){
    //初始化结构体
    SHELLEXECUTEINFO sei = { sizeof(SHELLEXECUTEINFO) };

    //请求管理员身份
    sei.lpVerb = TEXT("runas"); //run administrator?

    //传入要提高权限的应用程序名
    sei.lpFile = szExecutable;

    //传入命令行参数
    sei.lpParameters = szCmdLine;

    //窗口正常显示,否则新启动的进程,其窗口将被隐藏
    sei.nShow = SW_SHOWNORMAL;

    ShellExecuteEx(&sei);

    return (GetLastError());
}

//////////////////////////////////////////////////////////////////////////
/*
PEB(Process Environment Block)是一个未公开的结构体
https://msdn.microsoft.com/en-us/library/windows/desktop/aa813741(v=vs.85).aspx
typedef struct _PEB {
    BYTE                          Reserved1[2];   //2字节
    BYTE                          BeingDebugged;  //1
    BYTE                          Reserved2[1];   //1
    PVOID                         Reserved3[2];   //2*4=8
    PPEB_LDR_DATA                 Ldr;            //4
    PRTL_USER_PROCESS_PARAMETERS  ProcessParameters; //进程参数,如command line
    BYTE                          Reserved4[104];
    PVOID                         Reserved5[52];
    PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
    BYTE                          Reserved6[128];
    PVOID                         Reserved7[1];
    ULONG                         SessionId;
} PEB, *PPEB;

typedef struct _RTL_USER_PROCESS_PARAMETERS {
    BYTE           Reserved1[16]; //16字节
    PVOID          Reserved2[10]; //10*4=40字节
    UNICODE_STRING ImagePathName; //2+2+4字节,进程映射文件的路径
    UNICODE_STRING CommandLine;   //2+2+4字节,其中最后4字节为命令行参数指针
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;

*/
//以上两个结构体的简化版本分别如下,
typedef struct  _tag_PEB
{
    DWORD Filler[4];  //用来填充的,4*4=16字节
    DWORD InfoBlockAddress; //进程命令行
}__PEB;

typedef struct _tag_INFOBLOCK {
    DWORD Filler[17]; //17*4=68字节填充
    DWORD wszCmdLineAddress;
}__INFOBLOCK;

//NtQueryInformationProcess在winternl.h中定义
typedef NTSTATUS(CALLBACK* PFN_NTQUERYINFORMATIONPROCESS)(
                HANDLE ProcessHandle,
                PROCESSINFOCLASS ProcessInformationClass,
                PVOID ProcessInformation,
                ULONG ProcessInformationLength,
                PULONG ReturnLength OPTIONAL);

NTSTATUS _NtQueryInformationProcess(
    HANDLE hProcess,
    PROCESSINFOCLASS pic,
    PVOID pPI,
    ULONG cbSize,
    PULONG pLength)
{
    HMODULE hNtDll = LoadLibrary(TEXT("ntdll.dll"));
    if (hNtDll == NULL)
        return -1;
    NTSTATUS lStatus = -1;  //默认的错误代码

    //注意,以下函数名是非Unicode的
    PFN_NTQUERYINFORMATIONPROCESS pfnNtQIP =
        (PFN_NTQUERYINFORMATIONPROCESS)GetProcAddress(hNtDll, "NtQueryInformationProcess");
    if (pfnNtQIP!=NULL)
        lStatus = pfnNtQIP(hProcess, pic, pPI, cbSize, pLength);
    
    FreeLibrary(hNtDll);
    return lStatus;
}

//////////////////////////////////////////////////////////////////////////
/*
PROCESS_BASIC_INFORMATION:
typedef struct
{
    DWORD ExitStatus; // 接收进程终止状态
    DWORD PebBaseAddress; // 接收进程环境块地址
    DWORD AffinityMask; // 接收进程关联掩码
    DWORD BasePriority; // 接收进程的优先级类
    ULONG UniqueProcessId; // 接收进程ID
    ULONG InheritedFromUniqueProcessId; //接收父进程ID
} PROCESS_BASIC_INFORMATION;
*/
BOOL GetProcessCmdLine(HANDLE hProcess, LPTSTR szCmdLine, DWORD Size)
{
    if ((hProcess == NULL) || (szCmdLine == NULL) || (Size == 0))
        return FALSE;
    
    //0:获取环境块地址
    int iReturn = 1;
    DWORD dwSize;
    SIZE_T size;

    PROCESS_BASIC_INFORMATION pbi;
    //可以将指定类型的进程信息拷贝到pbi所指向的缓冲区
    //XP中PEB的地址总是在0x7FFDF000,但Vista以后就不一定了
    iReturn = _NtQueryInformationProcess(     //见上面的定义部分
        hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), &dwSize);

    //NtQueryInformationProcess失败时将返回负数
    if (iReturn>=0){
        //1.查找进程环境块PEB
        __PEB PEB;

        //size = dwSize;
        /*ReadProcessMemory各参数:
         hProcess [in]要读取的另一个进程句柄
         pvAddressRemote [in]要读取另一个进程的内存地址。 从具体何处读取
         pvBufferLocal [out]本地进程中内存地址. 函数将读取的内容写入此处
        dwSize [in]要传送的字节数。要写入多少
        pdwNumBytesRead [out]实际传送的字节数. 函数返回时报告实际写入多少
        */
        if (!ReadProcessMemory(hProcess, pbi.PebBaseAddress, &PEB,
            sizeof(PEB), &size))
            return FALSE; //如果有需要也可以GetLastError
        //2.从PEB中获取指向一个包含CmdLine指针的信息块
        __INFOBLOCK Block;
        if (!ReadProcessMemory(hProcess, (LPVOID)PEB.InfoBlockAddress,
            &Block, sizeof(Block), &size))
            return FALSE;  //如果有需要也可以GetLastError

        //3.获取指定进程的命令行参数
        wchar_t wszCmdLine[MAX_PATH + 1];
        if (!ReadProcessMemory(hProcess, (LPVOID)Block.wszCmdLineAddress,
            wszCmdLine,MAX_PATH*sizeof(wchar_t), &size))
            return FALSE;  //如果有需要也可以GetLastError

        //4.跳过应用程序名
        //    可能是空的,"C:\...\app.exe" 
        wchar_t*  pPos = wszCmdLine;
        if (*pPos!=L'0')
        {
            if (*pPos=='"')
            {
                //找到下一个"字符
                pPos = wcschr(&pPos[1], L'"');
            } else{
                //查找下一个空格
                pPos = wcschr(&pPos[1], L' ');
            }

            //跳过"或空格字符,找到命令行真正的参数(除应用程序自身路径及文件名外)
            if (pPos != NULL)
                pPos++;
        }

        //拷贝到szCmdLine参数所指定的缓冲区中
        if (pPos!=NULL)
        {
            if (*pPos != L'\0')
            {
#ifdef UNICODE
                //两者都是Unicode,(环境块的命令行永远是UNICODE)
                _tcscpy_s(szCmdLine, Size, pPos);
#else
                //from Unicode to Ansi
                MultiByteToWideChar(CP_ACP, 0, szCmdLine, Size, pPos, wcslen(pPos));
#endif
            } else szCmdLine[0] = '\0';
        } else szCmdLine[0] = '\0';

    } else return FALSE;

    return TRUE;

}

BOOL GetProcessCmdLine(DWORD PID, LPTSTR szCmdLine, DWORD Size)
{
    if ((PID <= 0) || (szCmdLine == NULL))
        return FALSE;

    //检查是否可以获取到进程信息
    HANDLE hProcess =
        OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, PID);
    if (hProcess == NULL)
        return FALSE;

    BOOL bReturn = GetProcessCmdLine(hProcess, szCmdLine, Size);

    CloseHandle(hProcess); //关闭句柄
    return bReturn;
}

//////////////////////////////////////////////////////////////////////////
BOOL GetProcessOwner(HANDLE hProcess, LPTSTR szOwner, size_t cchSize)
{
    //合法性检查
    if ((NULL == szOwner) || (cchSize == 0))
        return FALSE;

    //默认值
    szOwner[0] = TEXT('\0');

    //获取进程的令牌
    HANDLE hToken = NULL;
    CToolhelp::EnablePrivilege(SE_TCB_NAME, TRUE);
    if (!OpenProcessToken(hProcess,TOKEN_QUERY,&hToken))
    {
        CToolhelp::EnablePrivilege(SE_TCB_NAME, FALSE);
        return FALSE;
    }

    DWORD cbti = 0;
    PTOKEN_USER ptiUser = (PTOKEN_USER)CToolhelp::RetrieveTokenInformationClass(hToken, 
                                                TokenUser,&cbti);
    if (ptiUser!=NULL)
    {
        SID_NAME_USE snu;
        TCHAR szUser[MAX_PATH];
        DWORD chUser = MAX_PATH;
        PDWORD  pcchUser = &chUser;
        TCHAR  szDomain[MAX_PATH];
        DWORD  chDomain = MAX_PATH;
        PDWORD pcchDomain = &chDomain;

        //从用户SID去用户名和域名
        if (LookupAccountSid(NULL,
                    ptiUser->User.Sid,
                    szUser,
                    pcchUser,
                    szDomain,
                    pcchDomain,
                    &snu
                    ))
        {
               //创建属主字符串,比如\\DomainName\UserName
            _tcscpy_s(szOwner, cchSize, TEXT("\\\\"));
            _tcscat_s(szOwner, cchSize, szDomain);
            _tcscat_s(szOwner, cchSize, TEXT("\\"));
            _tcscat_s(szOwner, cchSize, szUser);
        }

        CToolhelp::FreeTokenInformation((PVOID*)&ptiUser);
    }

    CloseHandle(hToken);
    CToolhelp::EnablePrivilege(SE_TCB_NAME, FALSE);
    return TRUE;
}

BOOL GetProcessOwner(DWORD PID, LPTSTR szOwner, DWORD cchSize)
{
    //合法性检查
    if ((PID <= 0) || (szOwner == NULL))
        return FALSE;

    //检查是否可以获得进程的信息
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, PID);
    if (hProcess == NULL)
        return FALSE;

    BOOL bReturn = GetProcessOwner(hProcess, szOwner, cchSize);

    //关闭进程句柄
    CloseHandle(hProcess);

    return bReturn;
}

//////////////////////////////////////////////////////////////////////////
//dwProcessID:进程ID;pvModuleRemote进程中指定模块的地址
//返回模块首选的基地址
PVOID GetModulePreferredBaseAddr(DWORD dwProcessID, PVOID pvModuleRemote){
    PVOID pvModulePreferredBaseAddr = NULL;
    IMAGE_DOS_HEADER  idh;
    IMAGE_NT_HEADERS  inth;

    //读取远程模块的DOS头
    Toolhelp32ReadProcessMemory(dwProcessID, pvModuleRemote, &idh, sizeof(idh), NULL);

    //验证是否是DOS头部
    if (idh.e_magic == IMAGE_DOS_SIGNATURE){
        //读取远程模块的NT头
        Toolhelp32ReadProcessMemory(dwProcessID, (PBYTE)pvModuleRemote + idh.e_lfanew, &inth,
                                    sizeof(inth), NULL);
        //验证是否是NT image header
        if (inth.Signature == IMAGE_NT_SIGNATURE){
            //有效的NT头,获取首先的基地址
            pvModulePreferredBaseAddr = (PVOID)inth.OptionalHeader.ImageBase;
        }
    }

    return (pvModulePreferredBaseAddr);
}

//////////////////////////////////////////////////////////////////////////
//hwnd:进程信息要显示的窗口,dwProcessID进程ID
VOID ShowProcessInfo(HWND hwnd, DWORD dwProcessID)
{
    SetWindowText(hwnd, TEXT("")); //清除编辑框的内容

    //SNAP-ALL,在快照中包含系统中所有的进程和线程,再加上指定的进程中所有堆和所有模块
    CToolhelp th(TH32CS_SNAPALL, dwProcessID); //SNAP-ALL

    //显示进程详细的信息
    PROCESSENTRY32 pe = { sizeof(pe) };
    BOOL fOk = th.ProcessFirst(&pe);
    for (; fOk; fOk = th.ProcessNext(&pe))
    {
        if (pe.th32ProcessID == dwProcessID)
        {
            TCHAR szCmdLine[1024];
            if (GetProcessCmdLine(dwProcessID, szCmdLine, _countof(szCmdLine)))
            {
                AddText(hwnd,
                        TEXT("CommandLine:%s %s\r\n"), pe.szExeFile, szCmdLine);
            } else{
                AddText(hwnd,
                        TEXT("FileName:%s\r\n"), pe.szExeFile);
            }

            AddText(hwnd, TEXT("  PID=%08X, ParentPID=%08X, ")
                    TEXT("PriorityClass=%d, Threads=%d, Heaps=%d\r\n"),
                    pe.th32ProcessID, pe.th32ParentProcessID,
                    pe.pcPriClassBase, pe.cntThreads, th.HowManyHeaps());
            TCHAR szOwner[MAX_PATH + 1];
            if (GetProcessOwner(dwProcessID, szOwner, MAX_PATH))
                AddText(hwnd, TEXT("Owner:%s\r\n"), szOwner);

            break;
        }
    }

    //显示指定进程中所有的模块    
    AddText(hwnd, TEXT("\r\nModules Information:\r\n")
            TEXT("  Usage  %-*s(%-*s)  %10s   Module\r\n"),
            s_cchAddress, TEXT("BaseAddr"),
            s_cchAddress, TEXT("ImagAddr"), TEXT("Size"));

    MODULEENTRY32 me = { sizeof(me) };
    fOk = th.ModuleFirst(&me);
    for (; fOk; fOk = th.ModuleNext(&me)){
        if (me.ProccntUsage == 65535){  //全局模块的使用计数。通常这一项也是没有意义的,被设置为0xFFFF。
            //表示该模块是显式加载的而且不能被卸载
            AddText(hwnd, TEXT("  Fixed"));
        } else{
            AddText(hwnd, TEXT("  %5d"), me.ProccntUsage); //使用计数
        }

        //格式化size字段(单位kb);
        TCHAR szFormattedSize[64];
        //将数字转化为字符串,单位为Kb 
        //第1个参数:要转换的数字;第2个参数:接收缓冲区;第3个参数:缓冲区大小(字符为单位)
        if (StrFormatKBSize(me.modBaseSize, szFormattedSize, _countof(szFormattedSize)) == NULL){
            StringCchPrintf(szFormattedSize, _countof(szFormattedSize),
                            TEXT("%10u"), me.modBaseSize);
        }

        PVOID pvPreferredBaseAddr =
            GetModulePreferredBaseAddr(pe.th32ProcessID, me.modBaseAddr);

        if (me.modBaseAddr == pvPreferredBaseAddr){
            AddText(hwnd, TEXT("  %p %*s   %10s  %s\r\n"),
                    me.modBaseAddr, s_cchAddress, TEXT(""), szFormattedSize, me.szExePath);
        } else{
            AddText(hwnd, TEXT("  %p(%p)   %10s  %s\r\n"),
                    me.modBaseAddr, pvPreferredBaseAddr, szFormattedSize, me.szExePath);
        }
    }

    //显示进程中的线程信息
    AddText(hwnd, TEXT("\r\nThread Information:\r\n")
                  TEXT("     TID       Priority\r\n"));
    THREADENTRY32 te = { sizeof(te) };
    fOk = th.ThreadFirst(&te);
    for (; fOk;fOk=th.ThreadNext(&te))
    {
        if (te.th32OwnerProcessID == dwProcessID)
        {
            int nPriority = te.tpBasePri + te.tpDeltaPri;
            if ((te.tpBasePri<16) && (nPriority>15)) nPriority = 15;
            if ((te.tpBasePri>15) && (nPriority>31)) nPriority = 31;
            if ((te.tpBasePri<16) && (nPriority<1 )) nPriority =  1;
            if ((te.tpBasePri>15) && (nPriority<16)) nPriority = 16;

            AddText(hwnd, TEXT("  %08X       %2d\r\n"),
                    te.th32ThreadID,nPriority);
        }
    }
}


//////////////////////////////////////////////////////////////////////////
//显示模块被哪些进程加载
VOID ShowModuleInfo(HWND hwnd, PCTSTR pszModulePath)
{
    SetWindowText(hwnd, TEXT("")); //清除编辑框内容

    CToolhelp thProcesses(TH32CS_SNAPPROCESS);
    PROCESSENTRY32 pe = { sizeof(pe) };
    BOOL fOk = thProcesses.ProcessFirst(&pe);
    AddText(hwnd, TEXT("PathName:%s\r\n\r\n"), pszModulePath);
    AddText(hwnd, TEXT("Process Information:\r\n"));
    AddText(hwnd, TEXT("     PID    %-*s  Process\r\n"), s_cchAddress, TEXT("BaseAddr"));

    for (; fOk;fOk=thProcesses.ProcessNext(&pe)){
        CToolhelp thModules(TH32CS_SNAPMODULE, pe.th32ProcessID);
        MODULEENTRY32 me = { sizeof(me) };
        BOOL fOk = thModules.ModuleFirst(&me);
        for (; fOk;fOk=thModules.ModuleNext(&me)){
            //查找指定的模块被哪些进程加载
            if (_tcscmp(pszModulePath,me.szExePath) == 0)
            {
                //显示被加载到的进程ID,模块的基地址及进程名称
                AddText(hwnd, TEXT("  %08X  %p  %s\r\n"),
                        pe.th32ProcessID,me.modBaseAddr,pe.szExeFile);
            }
        }
    }

}

//////////////////////////////////////////////////////////////////////////
void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtrl, UINT codeNotify)
{
    static BOOL s_fProcesses = TRUE;

    switch (id)
    {
    case IDCANCEL:
        EndDialog(hwnd, id);
        break;

        //以管理员身份重启应用程序
    case IDC_BTN_SYSTEM_PROCESSES:{  //case分支里定义变量,要加大括号!
            //在提升权限之前先隐藏自己
            ShowWindow(hwnd, SW_HIDE);

            TCHAR szApplication[MAX_PATH];
            DWORD  cchLength = _countof(szApplication);
            //获取进程的完整映射名称
            //第2个参为0表示使用Win32路径格式,PROCESS_NAME_NATIVE,表示使用本地系统路径格式
            //第4个参数:返回字符串中字符的个数(不含\0)
            QueryFullProcessImageName(GetCurrentProcess(), 0, szApplication, &cchLength);

            DWORD dwStatus = StartElevatedProcess(szApplication, NULL);

            if (dwStatus == S_OK)
                ExitProcess(0);  //退出本进程

            //否则,新进程如果启动失败时,重新显示本进程的主窗口
            ShowWindow(hwnd, SW_SHOWNORMAL);
        }
        break;

    case IDM_PROCESSES: //Processes菜单项
        s_fProcesses = TRUE;              
        EnableMenuItem(GetMenu(hwnd), IDM_VMMAP, MF_BYCOMMAND | MF_ENABLED);//MF_BYCOMMAND:通过ID定位
        DrawMenuBar(hwnd);
        Dlg_PopulateProcessList(hwnd);

        break;

    case IDM_MODULES:   //Modules菜单项
        EnableMenuItem(GetMenu(hwnd), IDM_VMMAP, MF_BYCOMMAND | MF_GRAYED);
        DrawMenuBar(hwnd);
        s_fProcesses = FALSE;

        Dlg_PopulateModuleList(hwnd);        
        break;

        //会用到第14章虚拟内存映射的小程序,用来遍历进程的地址空间,并列出区域及区域内的块
    case IDM_VMMAP:     //VMMAP菜单项
        {
            TCHAR szCmdLine[32];
            HWND hwndCB = GetDlgItem(hwnd, IDC_PROCESSMODULELIST); //下拉列表框
            DWORD dwProcessID = (DWORD)
                ComboBox_GetItemData(hwndCB, ComboBox_GetCurSel(hwndCB));

            //命令行参数中传入所选的进程ID
            StringCchPrintf(szCmdLine, _countof(szCmdLine), TEXT("%d"), dwProcessID);

            DWORD dwStatus =             //"14-VMMap.exe"
                StartElevatedProcess(TEXT("\"14-VMMap.exe\""), szCmdLine);

            //以管理员身份运行14-VMMap.exe程序时,会进行提升权限的询问,如果是用户拒绝
            //则显示下列消息框。
            if (dwStatus == ERROR_CANCELLED)
                chMB("Failed to run 14-VMMAP.exe: you refused access.");
        }
        break;

    case IDC_PROCESSMODULELIST:
        if (codeNotify == CBN_SELCHANGE)
        {
            DWORD dw = ComboBox_GetCurSel(hwndCtrl);
            dw = (DWORD)ComboBox_GetItemData(hwndCtrl, dw); //ProcessID

            if (s_fProcesses){
                ShowProcessInfo(GetDlgItem(hwnd, IDC_RESULTS), dw);
            }else{
                TCHAR szMoudlePath[1024];
                ListBox_GetText(GetDlgItem(hwnd, IDC_MODULEHELP), dw, szMoudlePath);
                ShowModuleInfo(GetDlgItem(hwnd, IDC_RESULTS), szMoudlePath);
            }
        }
        break;

    }
}

//////////////////////////////////////////////////////////////////////////
BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
{
    chSETDLGICONS(hwnd, IDI_PROCESSINFO);
    
    //当提升权限或禁用UAC时,管理员特权被设置为TRUE
    BOOL bCanReadSystemProcesses = FALSE;

    //显示是否正在运行“Filter Token”
    if (GetProcessElevation(&s_elevationType, &s_bIsAdmin))
    {
        //提升标题的前缀
        TCHAR szTitle[64];
        switch (s_elevationType)
        {
            //默认用户或禁用UAC
        case TokenElevationTypeDefault:
            if (IsUserAnAdmin()){
                _tcscpy_s(szTitle, _countof(szTitle), TEXT("Default Administrator:"));
                bCanReadSystemProcesses = TRUE;
            }
            else{
                _tcscpy_s(szTitle, _countof(szTitle), TEXT("Default:"));
            }
            break;

            //进程权限被提升成功
        case TokenElevationTypeFull:
            if (IsUserAnAdmin()){
                _tcscpy_s(szTitle, _countof(szTitle), TEXT("Elevated Administrator:"));
                bCanReadSystemProcesses = TRUE;
            }
            else{
                _tcscpy_s(szTitle, _countof(szTitle), TEXT("Elevated:"));
            }
            break;

            //进程运行于受限(筛选令牌)下
        case TokenElevationTypeLimited:
            if (IsUserAnAdmin()){
                _tcscpy_s(szTitle, _countof(szTitle), TEXT("Filtered Administrator:"));
                bCanReadSystemProcesses = TRUE;
            }
            else{
                _tcscpy_s(szTitle, _countof(szTitle), TEXT("Filtered:"));
            }
            break;
        }

        //根据提升类型更新对话框标题
        GetWindowText(hwnd, _tcschr(szTitle, TEXT('\0')), 
                       _countof(szTitle)-_tcslen(szTitle));
        SetWindowText(hwnd, szTitle);

        //增加“循牌”图标,以允许用户以提升的权限来运行程序
        if (!bCanReadSystemProcesses){
            //SendMessage(hwndCtrl, BCM_SETSHIELD, 0, (LPARAM)fRequired)
            Button_SetElevationRequiredState(
                GetDlgItem(hwnd, IDC_BTN_SYSTEM_PROCESSES),
                !bCanReadSystemProcesses);
        }else{
            //己经提升权类,则隐茂按钮
            ShowWindow(GetDlgItem(hwnd, IDC_BTN_SYSTEM_PROCESSES), SW_HIDE);

            //将组合列表框扩展到整个对话框的宽度
            MoveWindow(GetDlgItem(hwnd, IDC_BTN_SYSTEM_PROCESSES),
                        0, 0, 0, 0, FALSE);
        }
    }

    //隐藏“模块帮助”列表框
    ShowWindow(GetDlgItem(hwnd, IDC_MODULEHELP), SW_HIDE);

    //使输出窗口使用等宽字体
    SetWindowFont(GetDlgItem(hwnd, IDC_RESULTS), 
             GetStockFont(ANSI_FIXED_FONT),FALSE);

    //默认下,显示运行中的进程
    Dlg_PopulateProcessList(hwnd);
    return TRUE;
}

//////////////////////////////////////////////////////////////////////////
BOOL Dlg_OnSize(HWND hwnd, UINT state, int cx, int cy)
{
    RECT  btnRect;
    HWND hwndCtrl = GetDlgItem(hwnd, IDC_BTN_SYSTEM_PROCESSES);
    GetClientRect(hwndCtrl, &btnRect);

    RECT rc;
    int n = LOWORD(GetDialogBaseUnits()); //字符的宽度

    hwndCtrl = GetDlgItem(hwnd, IDC_PROCESSMODULELIST);
    GetClientRect(hwndCtrl, &rc);

    SetWindowPos(hwndCtrl, NULL,
                 n+n + btnRect.right, //x=按钮宽度+一个字符宽
                 n,                 //y=一个字符的高度
                 cx - n - n -n - btnRect.right,//Combox宽度,右边距留一字符宽度
                 rc.bottom, 
                 SWP_NOZORDER);

    hwndCtrl = GetDlgItem(hwnd, IDC_RESULTS);
    SetWindowPos(hwndCtrl, NULL,
                 n,
                 n + rc.bottom + n,
                 cx - n - n,
                 cy - (n + rc.bottom + n) - n,
                SWP_NOZORDER);

    return 0;
}

//////////////////////////////////////////////////////////////////////////
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_SIZE, Dlg_OnSize);
        chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand);
    }
    return FALSE;
}

//////////////////////////////////////////////////////////////////////////
int WINAPI _tWinMain(HINSTANCE hInstanceExe, HINSTANCE, PTSTR pszCmdLine, int)
{
    //打开调试权限以允许应用程序查看服务应用
    CToolhelp::EnablePrivilege(SE_DEBUG_NAME, TRUE);

    //打开访问SACL权限
    CToolhelp::EnablePrivilege(SE_SECURITY_NAME, TRUE);

    //显示主窗口
    DialogBox(hInstanceExe, MAKEINTRESOURCE(IDD_PROCESSINFO), NULL, Dlg_Proc);

    CToolhelp::EnablePrivilege(SE_DEBUG_NAME, FALSE);
    CToolhelp::EnablePrivilege(SE_SECURITY_NAME, FALSE);
    return 0;
}

 //resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 04_ProcessInfo.rc 使用
//
#define IDD_PROCESSINFO                 101
#define IDR_PROCESSINFO                 102
#define IDI_PROCESSINFO                 103
#define IDC_BTN_SYSTEM_PROCESSES        1001
#define IDC_RESULTS                     1002
#define IDC_PROCESSMODULELIST           1003
#define IDC_MODULEHELP                  1004
#define IDM_PROCESSES                   40001
#define IDM_MODULES                     40002
#define IDM_VMMAP                       40003

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        104
#define _APS_NEXT_COMMAND_VALUE         40007
#define _APS_NEXT_CONTROL_VALUE         1008
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//ProcessInfo.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\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_PROCESSINFO DIALOGEX 0, 0, 399, 234
STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Process Information"
MENU IDR_PROCESSINFO
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    PUSHBUTTON      "System Processes",IDC_BTN_SYSTEM_PROCESSES,6,5,88,14
    COMBOBOX        IDC_PROCESSMODULELIST,102,39,283,156,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP
    LISTBOX         IDC_MODULEHELP,115,2,48,19,NOT LBS_NOTIFY | LBS_SORT | LBS_NOINTEGRALHEIGHT | NOT WS_VISIBLE | NOT WS_BORDER | WS_TABSTOP
    EDITTEXT        IDC_RESULTS,6,22,384,207,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL
END


/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
    IDD_PROCESSINFO, DIALOG
    BEGIN
        RIGHTMARGIN, 397
    END
END
#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

IDR_PROCESSINFO MENU
BEGIN
    MENUITEM "&Processes!",                 IDM_PROCESSES
    MENUITEM "&Modules!",                   IDM_MODULES
    MENUITEM "&VMMap!",                     IDM_VMMAP
END


/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_PROCESSINFO         ICON                    "ProcessInfo.ico"
#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

//ToolHelp.h文件

#pragma once

//////////////////////////////////////////////////////////////////////////
#include "CmnHdr.h"
#include <TlHelp32.h>
#include <tchar.h>

//////////////////////////////////////////////////////////////////////////
class CToolhelp{
private:
    HANDLE m_hSnapshot;

public:
    CToolhelp(DWORD dwFlags = 0, DWORD dwProcessID = 0);
    ~CToolhelp();

    BOOL CreateSnapshot(DWORD dwFlags, DWORD dwProcessID=0);

    BOOL ProcessFirst(PPROCESSENTRY32 ppe) const;
    BOOL ProcessNext(PPROCESSENTRY32 ppe)  const;
    BOOL ProcessFind(DWORD dwProcessID, PPROCESSENTRY32 ppe) const;

    BOOL ModuleFirst(PMODULEENTRY32 pme) const;
    BOOL ModuleNext(PMODULEENTRY32 pme) const;
    BOOL ModuleFind(PVOID pvBaseAddr, PMODULEENTRY32 pme) const;
    BOOL ModuleFind(PTSTR pszModName, PMODULEENTRY32 pme) const;

    BOOL ThreadFirst(PTHREADENTRY32 pte) const;
    BOOL ThreadNext(PTHREADENTRY32 pte) const;

    BOOL HeapListFirst(PHEAPLIST32 phl) const;
    BOOL HeapListNext(PHEAPLIST32 phl) const;
    int  HowManyHeaps() const;

    //注意:堆块函数并不利用快照,每次都是从进程堆的首部开始
    //以下函数在枚举目标进程堆块的期间,可能因进程移动堆块,从而
    //造成死循环

    BOOL HeapFirst(PHEAPENTRY32 phe,DWORD dwProcessID,UINT_PTR dwHeapID) const;
    BOOL HeapNext(PHEAPENTRY32 phe) const;
    BOOL IsAHeap(HANDLE hProcess, PVOID pvBlock, PDWORD pdwFlags);
    int  HowManyBlockInHeap(DWORD dwProcessID,DWORD dwHeapID) const;

public:
    static BOOL EnablePrivilege(PCTSTR szPrivilege, BOOL fEnable = TRUE);

    //获得指定类型指定类型的信息
    static LPVOID RetrieveTokenInformationClass(HANDLE hToken, TOKEN_INFORMATION_CLASS InfoClass, LPDWORD pdwSize);
    static void FreeTokenInformation(PVOID* pTokenInfo);
};

//////////////////////////////////////////////////////////////////////////
inline CToolhelp::CToolhelp(DWORD dwFlags, DWORD dwProcessID)
{
    m_hSnapshot = INVALID_HANDLE_VALUE;
    CreateSnapshot(dwFlags, dwProcessID);
}

//////////////////////////////////////////////////////////////////////////
inline CToolhelp::~CToolhelp(){
    if (m_hSnapshot != INVALID_HANDLE_VALUE){
        CloseHandle(m_hSnapshot);
    }
}

//////////////////////////////////////////////////////////////////////////
/*
参数1
TH32CS_INHERIT - 声明快照句柄是可继承的。
TH32CS_SNAPALL - 在快照中包含系统中所有的进程和线程
TH32CS_SNAPHEAPLIST - 在快照中包含在dwProcessID中指定的进程的所有的堆
TH32CS_SNAPMODULE - 在快照中包含在dwProcessID中指定的进程的所有的模块
TH32CS_SNAPPROCESS - 在快照中包含系统中所有的进程
TH32CS_SNAPTHREAD - 在快照中包含系统中所有的线程
参数2:指定将要快照的进程ID。
     当TH32CS_SNAPHEAPLIST或者TH32CS_SNAPMODULE才有效:0表示当前进程,否则为指定ID
     在其他情况下该参数被忽略,所有的进程都会被快照
*/
inline BOOL CToolhelp::CreateSnapshot(DWORD dwFlags, DWORD dwProcessID){
    if (m_hSnapshot !=INVALID_HANDLE_VALUE)
        CloseHandle(m_hSnapshot);

    //进程快照(堆、模块、线程等)
    if (dwFlags == 0)
        m_hSnapshot = INVALID_HANDLE_VALUE;
    else
        m_hSnapshot = CreateToolhelp32Snapshot(dwFlags, dwProcessID);
    
    return (m_hSnapshot != INVALID_HANDLE_VALUE);    
}

//////////////////////////////////////////////////////////////////////////
inline BOOL CToolhelp::ProcessFirst(PPROCESSENTRY32 ppe) const{
    BOOL fOk = Process32First(m_hSnapshot, ppe);

    if (fOk && (ppe->th32ProcessID == 0))
        fOk = ProcessNext(ppe); //自定义函数,会递归删除"[系统进程]" (PID=0)

    return fOk;
}

BOOL  CToolhelp::ProcessNext(PPROCESSENTRY32 ppe)  const{
    BOOL fOk = Process32Next(m_hSnapshot, ppe);
    if (fOk && (ppe->th32ProcessID == 0))
        fOk = ProcessNext(ppe);//自定义函数,会递归删除"[系统进程]" (PID=0)
    
    return fOk;
}

BOOL  CToolhelp::ProcessFind(DWORD dwProcessID, PPROCESSENTRY32 ppe) const{
    BOOL fFound = FALSE;

    for (BOOL fOk = ProcessFirst(ppe); fOk;fOk=ProcessNext(ppe))
    {
        fFound = (ppe->th32ProcessID == dwProcessID);
        if (fFound) break;
    }
    return fFound;
}

//////////////////////////////////////////////////////////////////////////
inline BOOL CToolhelp::EnablePrivilege(PCTSTR szPrivilege, BOOL fEnable){
    //
    BOOL fOk = FALSE;  //假设函数调用失败
    HANDLE hToken;

    //尝试打开进程的访问令牌
    if (OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken))
    {
        //试图修改指定的特权
        TOKEN_PRIVILEGES tp;
        tp.PrivilegeCount = 1;
        LookupPrivilegeValue(NULL, szPrivilege, &tp.Privileges[0].Luid);
        tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;
        AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
        fOk = (GetLastError() == ERROR_SUCCESS);

        //关闭Token句柄
        CloseHandle(hToken);
    }
    return fOk;
}

//////////////////////////////////////////////////////////////////////////
inline BOOL CToolhelp::ModuleFirst(PMODULEENTRY32 pme) const{
    return Module32First(m_hSnapshot, pme);
}

inline BOOL CToolhelp::ModuleNext(PMODULEENTRY32 pme) const
{
    return Module32Next(m_hSnapshot, pme);
}
//根据模块的基址返回模块信息
inline BOOL CToolhelp::ModuleFind(PVOID pvBaseAddr, PMODULEENTRY32 pme) const{
    BOOL fFound = FALSE;
    for (BOOL fOk = ModuleFirst(pme); fOk;fOk=ModuleNext(pme)){
        fFound = (pme->modBaseAddr == pvBaseAddr);
        if (fFound) break;
    }
    return fFound;
}

//根据模块的名称返回模块信息
inline BOOL CToolhelp::ModuleFind(PTSTR pszModName, PMODULEENTRY32 pme) const{
    BOOL fFound = FALSE;

    for (BOOL fOk = ModuleFirst(pme); fOk;fOk=ModuleNext(pme))
    {
        //lstrcmpi比较字符串(忽略大小写)
        fFound = (lstrcmpi(pme->szModule, pszModName) == 0) ||
                   (lstrcmpi(pme->szExePath,pszModName)==0);

        if (fFound) break;
    }
    return TRUE;
}

//////////////////////////////////////////////////////////////////////////
inline BOOL CToolhelp::ThreadFirst(PTHREADENTRY32 pte) const{
    return Thread32First(m_hSnapshot, pte);
}

inline BOOL CToolhelp::ThreadNext(PTHREADENTRY32 pte) const{
    return Thread32Next(m_hSnapshot, pte);
}

//////////////////////////////////////////////////////////////////////////
inline BOOL CToolhelp::HeapListFirst(PHEAPLIST32 phl) const{
    return (Heap32ListFirst(m_hSnapshot, phl));
}

inline BOOL CToolhelp::HeapListNext(PHEAPLIST32 phl) const{
    return Heap32ListNext(m_hSnapshot, phl);
}

inline int CToolhelp::HowManyHeaps() const{
    int nHowManyHeaps = 0;
    HEAPLIST32 h1 = { sizeof(h1) };
    for (BOOL fOk = HeapListFirst(&h1);fOk;fOk=HeapListNext(&h1))
    {
        nHowManyHeaps++;
    }
    return nHowManyHeaps;
}

inline BOOL CToolhelp::HeapFirst(PHEAPENTRY32 phe, DWORD dwProcessID, UINT_PTR dwHeapID) const{
    return Heap32First(phe, dwProcessID, dwHeapID);
}

inline BOOL CToolhelp::HeapNext(PHEAPENTRY32 phe) const{
    return Heap32Next(phe);
}
inline BOOL CToolhelp::IsAHeap(HANDLE hProcess, PVOID pvBlock, PDWORD pdwFlags){
    HEAPLIST32 hl = { sizeof(hl) };

    for (BOOL fOkHL = HeapListFirst(&hl);fOkHL;fOkHL = HeapListNext(&hl)){
        HEAPENTRY32 he = { sizeof(he) };
        BOOL fOkHE = HeapFirst(&he, hl.th32ProcessID, hl.th32HeapID);
        for (;fOkHE;fOkHE = HeapNext(&he))
        {
            MEMORY_BASIC_INFORMATION mbi;
            //查询地址空间中内存地址的信息
            //参数2:表示查询内存的地址,参数3:用于接收内存信息
            VirtualQueryEx(hProcess, (PVOID)he.dwAddress, &mbi, sizeof(mbi));
            if (chINRANGE(mbi.AllocationBase, pvBlock,
                (PBYTE)mbi.AllocationBase + mbi.RegionSize)){
                *pdwFlags = hl.dwFlags;
                return TRUE;
            }
        }
    }
    return FALSE;
}

inline int CToolhelp::HowManyBlockInHeap(DWORD dwProcessID, DWORD dwHeapID) const {
    int nHowManyBlocksInHeap = 0;
    HEAPENTRY32 he = { sizeof(he) };
    BOOL fOk = HeapFirst(&he, dwProcessID, dwHeapID);
    for (;fOk;fOk=HeapNext(&he))
        nHowManyBlocksInHeap++;
    
    return nHowManyBlocksInHeap;
}

//////////////////////////////////////////////////////////////////////////
inline LPVOID CToolhelp::RetrieveTokenInformationClass(HANDLE hToken, TOKEN_INFORMATION_CLASS InfoClass, LPDWORD pdwSize)
{
    static LPVOID pInfo = NULL;
    static BOOL fSucess = FALSE;
    *pdwSize = 0;
    __try
    {
        GetTokenInformation(hToken, InfoClass, NULL, 0, pdwSize);
        if (ERROR_INSUFFICIENT_BUFFER != GetLastError())
            __leave;

        pInfo = LocalAlloc(LPTR, *pdwSize);
        if (NULL == pInfo)
            __leave;

        if (!GetTokenInformation(hToken, InfoClass, pInfo, *pdwSize, pdwSize))
            __leave;
        fSucess = TRUE;
    }
    __finally
    {
        if (fSucess == FALSE)
        {
            FreeTokenInformation(&pInfo);
        }
    }
    return pInfo;
}
inline void CToolhelp::FreeTokenInformation(PVOID* pTokenInfo)
{
    if (NULL !=*pTokenInfo)
    {
        LocalFree(*pTokenInfo);
        *pTokenInfo = NULL;
    }
}

///////////////////////////////// End  of  File /////////////////////////////////////

 //cmmHdr.h

/******************************************************************************
Module:  CmnHdr.h
Notices: Copyright (c) 2008 Jeffrey Richter & Christophe Nasarre
Purpose: Common header file containing handy macros and definitions
         used throughout all the applications in the book.
         See Appendix A.
******************************************************************************/


#pragma once   // Include this header file once per compilation unit
//Windows版本构建选项
//////////////////////// Windows Version Build Option /////////////////////////

//因本书程序调用了一些Windows Vista中的新函数,在Vista以上必须定义_WIN32_WINNT
//才能调用这些函数,否则会引起编译错误。
// = 0x0600 for VISTA level from sdkddkver.h
#define _WIN32_WINNT _WIN32_WINNT_LONGHORN  //_WIN32_WINNT_LONGHORN =0x0600
#define WINVER       _WIN32_WINNT_LONGHORN 


//Unicode构建选项,本书所有程序即可编译成ANSI版本,也可编译成Unicode版本
//////////////////////////// Unicode Build Option /////////////////////////////


// Always compiler using Unicode.
#ifndef UNICODE
    #define UNICODE
#endif

// When using Unicode Windows functions, use Unicode C-Runtime functions too.
#ifdef UNICODE
   #ifndef _UNICODE
      #define _UNICODE
   #endif
#endif

//Windows定义和编译警告级别,警告级别越高,表示检查越严格
///////////////////////// Include Windows Definitions /////////////////////////

#pragma warning(push, 3) //存储当前警告级别,并设置警级别为3,来编译Windows.h文件
#include <windows.h>
#include <windowsx.h>    //要用到SetDlgMsgResult、Edit_LimitText等宏
#pragma warning(pop)     //恢复原新的警告级别
#pragma warning(push, 4) //存储当前警告级别,并设置为4级,来编译后面的内容
#include <CommCtrl.h>
#include <process.h>       // For _beginthreadex


///////////// Verify that the proper header files are being used //////////////


#ifndef FILE_SKIP_COMPLETION_PORT_ON_SUCCESS
#pragma message("You are not using the latest Platform SDK header/library ")
#pragma message("files. This may prevent the project from building correctly.")
#endif

//在警告级别下,允许禁用以下的警告
////////////// Allow code to compile cleanly at warning level 4 ///////////////

//允许使用非标准扩展的单行注释
/* nonstandard extension 'single line comment' was used */
#pragma warning(disable:4001)

// unreferenced formal parameter
#pragma warning(disable:4100)

// Note: Creating precompiled header 
#pragma warning(disable:4699)

// function not inlined
#pragma warning(disable:4710)

// unreferenced inline function has been removed
#pragma warning(disable:4514)

// assignment operator could not be generated
#pragma warning(disable:4512)

// conversion from 'LONGLONG' to 'ULONGLONG', signed/unsigned mismatch
#pragma warning(disable:4245)

// 'type cast' : conversion from 'LONG' to 'HINSTANCE' of greater size
#pragma warning(disable:4312)

// 'argument' : conversion from 'LPARAM' to 'LONG', possible loss of data
#pragma warning(disable:4244)

// 'wsprintf': name was marked as #pragma deprecated
#pragma warning(disable:4995)

// unary minus operator applied to unsigned type, result still unsigned
#pragma warning(disable:4146)

// 'argument' : conversion from 'size_t' to 'int', possible loss of data
#pragma warning(disable:4267)

// nonstandard extension used : nameless struct/union
#pragma warning(disable:4201)

///////////////////////// Pragma message helper macro /////////////////////////

//以下的前缀ch表示Common Header的缩写
/* 
//为了让程序能够运行起来,可以先忽略一些代码,但在输出窗口中给予提示
When the compiler sees a line like this: //当编译器发现以下代码时,会在输出窗口中
   #pragma chMSG(Fix this later)         //提示“某某文件,第几个以后要修复”,
                                         //注意为了使用方便,字符串不需要加引号

it outputs a line like this:

  c:\CD\CmnHdr.h(82):Fix this later     //提示的内容

You can easily jump directly to this line and examine the surrounding code.
*/

#define chSTR2(x) #x  //#x,即表示把x当成字符(串)来用
#define chSTR(x)  chSTR2(x)
//#desc表示将desc变量当成字符串。这样输入时,可以不用加引用了。(如#pragma  chMsg(这里需要修复!))
#define chMSG(desc) message(__FILE__ "(" chSTR(__LINE__) "):" #desc) //见上面的例子


////////////////////////////// chINRANGE Macro ////////////////////////////////


// This macro returns TRUE if a number is between two others
//该宏用来检查一个值是否介于两个值之间
#define chINRANGE(low, Num, High) (((low) <= (Num)) && ((Num) <= (High)))



///////////////////////////// chSIZEOFSTRING Macro ////////////////////////////


// This macro evaluates to the number of bytes needed by a string.
//该宏用来计算字符串占用的字节总数(含\0)
#define chSIZEOFSTRING(psz)   ((lstrlen(psz) + 1) * sizeof(TCHAR))



/////////////////// chROUNDDOWN & chROUNDUP inline functions //////////////////


// This inline function rounds a value down to the nearest multiple
//计算一个数,让其最接近Value(小于等于Value,因为是向下),但这个数是Multiple的整数倍
//eg.chROUNDDOWN(10,3) =9,表示显然9最接近10,但这个数是3的倍数。
template <class TV, class TM>
inline TV chROUNDDOWN(TV Value, TM Multiple) {
   return((Value / Multiple) * Multiple);
}

// This inline function rounds a value down to the nearest multiple
//计算一个数,让其最接近Value(大于等于Value,因为是向上),但这个数是Multiple的整数倍
//eg.chROUNDUP(10,3) =12,表示显然12最接近10,但这个数是3的倍数。
template <class TV, class TM>
inline TV chROUNDUP(TV Value, TM Multiple) {
   return(chROUNDDOWN(Value, Multiple) + 
      (((Value % Multiple) > 0) ? Multiple : 0));
}



///////////////////////////// chBEGINTHREADEX Macro ///////////////////////////


// This macro function calls the C runtime's _beginthreadex function. 
// The C runtime library doesn't want to have any reliance on Windows' data 
// types such as HANDLE. This means that a Windows programmer needs to cast
// values when using _beginthreadex. Since this is terribly inconvenient, 
// I created this macro to perform the casting.
typedef unsigned (__stdcall *PTHREAD_START) (void *);

#define chBEGINTHREADEX(psa, cbStackSize, pfnStartAddr, \
   pvParam, dwCreateFlags, pdwThreadId)                 \
      ((HANDLE)_beginthreadex(                          \
         (void *)        (psa),                         \
         (unsigned)      (cbStackSize),                 \
         (PTHREAD_START) (pfnStartAddr),                \
         (void *)        (pvParam),                     \
         (unsigned)      (dwCreateFlags),               \
         (unsigned *)    (pdwThreadId)))


////////////////// DebugBreak Improvement for x86 platforms ///////////////////
//允许进程不是通过调试器启动,仍然可以停在某一个断点上。
//在Windows平台上,线程是调用DebugBreak来实现断点的,这个Kernel32.dll中的函数
//让我们在附加一个调试器到进程时,指令指针会停在那条触发断点的CPU指令上。
//因为这条指令是Kernel32.dll中的函数指令,为了调试我们的代码,可以在代码中
//嵌入汇编int 3重新定义DebugBreak函数,从而产生一个效果等价的断点功能

#ifdef _X86_
   #define DebugBreak()    _asm { int 3 }
#endif


/////////////////////////// Software Exception Macro //////////////////////////


// Useful macro for creating your own software exception codes
//创建软件异常码,也就是SetLasCode中要用的异常码
#define MAKESOFTWAREEXCEPTION(Severity, Facility, Exception) \
   ((DWORD) ( \
   /* Severity code    */  (Severity       ) |     \
   /* MS(0) or Cust(1) */  (1         << 29) |     \
   /* Reserved(0)      */  (0         << 28) |     \
   /* Facility code    */  (Facility  << 16) |     \
   /* Exception code   */  (Exception <<  0)))


/////////////////////////// Quick MessageBox Macro ////////////////////////////

//弹出一个消息对话框,对话框的标题是调用进程对应的可执行文件的完整路径名称。
inline void chMB(PCSTR szMsg) {
   char szTitle[MAX_PATH];
   GetModuleFileNameA(NULL, szTitle, _countof(szTitle)); //模拟完整的路径名称
   MessageBoxA(GetActiveWindow(), szMsg, szTitle, MB_OK);
}


//////////////////////////// Assert/Verify Macros /////////////////////////////

//失败时弹出对话框,并设置断点
inline void chFAIL(PSTR szMsg) {
   chMB(szMsg);
   DebugBreak();
}

// Put up an assertion failure message box. 
//ASSERT(断言)为FALSE时,弹出消息对话框,并设置断点
inline void chASSERTFAIL(LPCSTR file, int line, PCSTR expr) { 
   char sz[2*MAX_PATH];
   wsprintfA(sz, "File %s, line %d : %s", file, line, expr);
   chFAIL(sz);
}


// Put up a message box if an assertion fails in a debug build.
#ifdef _DEBUG
   #define chASSERT(x) if (!(x)) chASSERTFAIL(__FILE__, __LINE__, #x) //调试版,弹对话框
#else
   #define chASSERT(x)   //发行版就不弹
#endif


// Assert in debug builds, but don't remove the code in retail builds.
#ifdef _DEBUG
   #define chVERIFY(x) chASSERT(x)
#else
   #define chVERIFY(x) (x)  //发行版时,代码不删除
#endif


/////////////////////////// chHANDLE_DLGMSG Macro /////////////////////////////
//消息分流器(Messasge Craker)
//一般的对话框过程只返回TRUE或FALSE来说明消息有没有被处理,而以下的宏会像
//WndProc中消息处理的LRESULT结果,返回值或句柄。该宏一般在对话框过程中使用
// The normal HANDLE_MSG macro in WindowsX.h does not work properly for dialog
// boxes because DlgProc returns a BOOL instead of an LRESULT (like
// WndProcs). This chHANDLE_DLGMSG macro corrects the problem:
#define chHANDLE_DLGMSG(hWnd, message, fn)                 \
   case (message): return (SetDlgMsgResult(hWnd, uMsg,     \
        HANDLE_##message((hWnd), (wParam), (lParam), (fn))))

//////////////////////// Dialog Box Icon Setting Macro ////////////////////////


// Sets the dialog box icons
//设置对话框的图标(可以以WM_INITDIALOG中调用以下函数)
inline void chSETDLGICONS(HWND hWnd, int idi) {
   SendMessage(hWnd, WM_SETICON, ICON_BIG,  (LPARAM) 
      LoadIcon((HINSTANCE) GetWindowLongPtr(hWnd, GWLP_HINSTANCE), 
         MAKEINTRESOURCE(idi)));
   SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) 
      LoadIcon((HINSTANCE) GetWindowLongPtr(hWnd, GWLP_HINSTANCE), 
      MAKEINTRESOURCE(idi)));
}
    

/////////////////////////// Common Linker Settings ////////////////////////////


#pragma comment(linker, "/nodefaultlib:oldnames.lib") //强制编译器寻找(w)WinMain入口点函数

// Needed for supporting XP/Vista styles.
//支持XP风格的用户界面主题,默认下应用程序是不支持的
#if defined(_M_IA64)
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='IA64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif
#if defined(_M_X64)
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.6000.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif
#if defined(M_IX86)
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif


///////////////////////////////// End of File /////////////////////////////////