14.4 确定地址空间的状态
14.4.1 查询内存状态API
(1)VirtualQuery(Ex)函数
参数 |
描述 |
HANDLE hProcess |
要查询的另一个进程的句柄。(注意,这个参数只有VirtualQueryEx才有,VirtualQuery只能查本进程的信息) |
LPCVOID pvAddress |
需要查询的虚拟内存地址 |
PMEMORY_BASIC_INFORMATION pmbi |
返回的信息被放入该指针指向的MEMORY_BASIC_INFORMATION结构体。 |
DWORD dwLength |
上述结构体的大小,表示返回时复制到上述结构体中的字节数。 |
(2)MEMORY_BASIC_INFORMATION结构体
字段 |
描述 |
BaseAddress |
这个值是VirtualQuery(Ex)第一个参数向下取整到页面的边界的值。如区域中的块,可以从AllocationBase开始以4KB倍数的偏移处作为其BaseAddress。 |
AllocationBase |
标识出区域的基地址,该区域包含参数pvAddress所指向的地址。. VirtualAlloc分配的内存称为区域(Region),是一片连续的页.。Windows会以64KB为边界计算区域的起始地址,这里的64KB就是所谓的分配粒度,因此 AllocationBase正是一个区域(Region)的起始地址。 |
AllocationProtect |
标识出在最开始预订区域时为该区域指定的保护属性 |
RegionSize |
标识出区域的大小,以字节为单位。区域的起始地址为BaseAddress,区域中所有页面拥有相同的保护属性、状态及类型 |
State |
标识出区域中页面的状态(MEM_FREE、MEM_RESERVE或MEM_COMMIT) 如果为MEM_FREE,则AllocationBase、AllocationProtect、Protect及Type成员都没有意义。 如果为MEM_RESERVE,那么Protect成员没有意义。 |
Protect |
针对所有相邻页面(前提是其保护属性、状态和类型与其中包含 pvAddress参数中所指定地址的页面相同),标识出它们的保护属性(PAGE_*) |
Type |
标识出区域中页面的类型(MEM_IMAGE、MEM_MAPPED或MEM_PRIVATE) |
14.4.2 自定义的VMQuery(Ex)函数——得到更完整的内存信息
(1)BOOL VMQuery(HANDLE hProcess,LPCVOID pvAddress,PVMQUERY pVMQ);
(2)VMQUERY结构体
字段 |
描述 |
备注 |
PVOID pvRgnBaseAddress |
表示虚拟地址空间区域的起始地址,该区域包含了参数pvAddress所指定的地址 |
区 域 信 息 |
DWORD dwRgnProtection |
表示在最开始预订地址空间区域时为该区域指定的保护属性(PAGE_*) |
|
SIZE_T RgnSize |
表示所预订区域的大小,以字节为单位 |
|
DWORD dwRgnStorage |
表示用于区域中各块的物理存储器的类型。它可以是以下任一值:MEM_FREE、MEM_IMAGE、MEM_MAPPED或MEM_PRIVATE |
|
DWORD dwRgnBlocks |
表示区域中块的数量 |
|
DWORD dwRgnGuardBlks |
表示区域中具有PAGE_GUARD保护属性标志的块的数量。通常这个值为0或1。如果为1,说明该区域是为了线程栈而预订的。 |
|
BOOL bRgnIsAStack |
表示该区域是否包含线程栈。该值是通过近似猜测得到的,因为没有任何方法能百分百地肯定一个区域是否包含线程栈 |
|
PVOID pvBlkBaseAddress |
表示块的起始地址,该块包含了参数pvAddress所指示的地址 |
块 信 息 |
DWORD dwBlkProtection |
表示块的保护属性 |
|
SIZE_T BlkSize |
表示块的大小,以字节为单位 |
|
DWORD dwBlkStorage |
表示块的存储器类型。它可以是以下任一值:MEM_FREE、MEM_IMAGE、MEM_RESERVE、MEM_MAPPED或MEM_PRIVATE |
【VMMap程序】显示虚拟内存映射情况
/************************************************************************* Module: VMMap.cpp Notices:Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre *************************************************************************/ #include "../../CommonFiles/CmnHdr.h" #include "../../CommonFiles/ToolHelp.h" #include "resource.h" #include "VMQuery.h" #include <Psapi.h> //For GetMappedFileName函数 #include <tchar.h> #include <strsafe.h> #pragma comment(lib,"psapi") ////////////////////////////////////////////////////////////////////////// DWORD g_dwProcessID = 0; //要遍历的进程 BOOL g_bExpandRegions = FALSE; CToolhelp g_toolhelp; ////////////////////////////////////////////////////////////////////////// void CopyControlToClipboard(HWND hWnd){ TCHAR szClipData[128 * 1024] = { 0 }; int nCount = ListBox_GetCount(hWnd); for (int nNum = 0; nNum < nCount;nNum++){ TCHAR szLine[1000]; ListBox_GetText(hWnd, nNum, szLine); _tcscat_s(szClipData, _countof(szClipData), szLine); _tcscat_s(szClipData, _countof(szClipData), TEXT(" ")); } OpenClipboard(NULL); EmptyClipboard(); //锁定内存块 HGLOBAL hClipData = GlobalAlloc(GMEM_MOVEABLE |GMEM_ZEROINIT, sizeof(TCHAR)*(_tcslen(szClipData)+1)); PTSTR pClipData = (PTSTR)GlobalLock(hClipData); _tcscpy_s(pClipData, _tcslen(szClipData) + 1, szClipData); #ifdef UNICODE BOOL bOk = (hClipData == SetClipboardData(CF_UNICODETEXT,hClipData)); #else BOOL bOk = (hClipData == SetClipboardData(CF_TEXT,hClipData)); #endif CloseClipboard(); if (!bOk){ GlobalFree(hClipData); chMB("数据复制到剪贴板时出错!"); } } ////////////////////////////////////////////////////////////////////////// PCTSTR GetMemStorageText(DWORD dwStorage){ PCTSTR p = TEXT("未知"); switch (dwStorage){ case MEM_FREE: p = TEXT("空闲 "); break; case MEM_RESERVE: p = TEXT("保留 "); break; case MEM_IMAGE: p = TEXT("映像 "); break; case MEM_MAPPED: p = TEXT("己映射"); break; case MEM_PRIVATE: p = TEXT("私有 "); break; } return p; } ////////////////////////////////////////////////////////////////////////// PTSTR GetProtectText(DWORD dwProtect, PTSTR szBuf, size_t chSize, BOOL bShowFlags){ PCTSTR p = TEXT("未知"); switch (dwProtect & ~(PAGE_GUARD | PAGE_NOCACHE | PAGE_WRITECOMBINE)) { case PAGE_READONLY: p = TEXT("-R--"); break; case PAGE_READWRITE: p = TEXT("-RW-"); break; case PAGE_WRITECOPY: p = TEXT("-RWC"); break; case PAGE_EXECUTE: p = TEXT("E---"); break; case PAGE_EXECUTE_READ: p = TEXT("ER--"); break; case PAGE_EXECUTE_READWRITE:p = TEXT("ERW-"); break; case PAGE_EXECUTE_WRITECOPY:p = TEXT("ERWC"); break; case PAGE_NOACCESS: p = TEXT("----"); break; } _tcscpy_s(szBuf, chSize, p); if (bShowFlags){ _tcscat_s(szBuf, chSize, TEXT(" ")); _tcscat_s(szBuf, chSize, (dwProtect & PAGE_GUARD) ? TEXT("G") : TEXT("-")); _tcscat_s(szBuf, chSize, (dwProtect & PAGE_NOCACHE) ? TEXT("N") : TEXT("-")); _tcscat_s(szBuf, chSize, (dwProtect & PAGE_WRITECOMBINE) ? TEXT("W") : TEXT("-")); } return (szBuf); } ////////////////////////////////////////////////////////////////////////// void ConstructRgnInfoLine(HANDLE hProcess, PVMQUERY pVMQ, PTSTR szLine, int cchMaxLen){ StringCchPrintf(szLine, cchMaxLen, TEXT("%p %s %12u "), pVMQ->pvRgnBaseAddress, GetMemStorageText(pVMQ->dwRgnStorage), pVMQ->RgnSize); if (pVMQ->dwRgnStorage !=MEM_FREE){ StringCchPrintf(_tcschr(szLine,0),cchMaxLen -_tcslen(szLine), TEXT("%u "),pVMQ->dwRgnBlocks); GetProtectText(pVMQ->dwRgnProtection, _tcschr(szLine, 0), cchMaxLen - _tcslen(szLine), FALSE); } else{ _tcscat_s(szLine, cchMaxLen, TEXT(" ")); } _tcscat_s(szLine, cchMaxLen, TEXT(" ")); //尝试获取区块的模块路径名称 if ((pVMQ->dwRgnStorage != MEM_FREE)&&(pVMQ->pvRgnBaseAddress != NULL)){ MODULEENTRY32 me = { sizeof(me) }; if (g_toolhelp.ModuleFind(pVMQ->pvRgnBaseAddress,&me)){ _tcscat_s(szLine, cchMaxLen, me.szExePath); } else{ int cchLen = _tcslen(szLine); //如果不是一个模块,查看一下是否是内存映射文件 DWORD dwLen = GetMappedFileName(hProcess, pVMQ->pvRgnBaseAddress, szLine + cchLen, cchMaxLen - cchLen); //注意:当GetMappedFileName调用失败时,会修改缓冲区中的内容 if (dwLen == 0){ szLine[cchLen] = 0; } } } if (pVMQ->bRgnIsAStack){ _tcscat_s(szLine, cchMaxLen, TEXT("线程栈")); } } ////////////////////////////////////////////////////////////////////////// //将区域中一个块的信息加入添加到列表框中 void ConstructBlkInfoLine(PVMQUERY pVMQ, PTSTR szLine, int cchMaxLen){ _stprintf_s(szLine, cchMaxLen, TEXT(" %p %s %12u "), pVMQ->pvBlkBaseAddress, GetMemStorageText(pVMQ->dwBlkStorage), pVMQ->BlkSize); if (pVMQ->dwBlkStorage != MEM_FREE){ //增加一个空的单元格(因为对于块来说,该列没有个数这列) _tcscat_s(szLine, cchMaxLen, TEXT(" ")); GetProtectText(pVMQ->dwBlkProtection, _tcschr(szLine, 0), cchMaxLen - _tcslen(szLine), TRUE); } else{ _tcscat_s(szLine, cchMaxLen, TEXT(" ")); } _tcscat_s(szLine, cchMaxLen, TEXT(" ")); } ////////////////////////////////////////////////////////////////////////// void Refresh(HWND hWndLB, DWORD dwProcessID, BOOL bExpandRegions){ //删除列表框内容,并增加水平滚动条 ListBox_ResetContent(hWndLB); ListBox_SetHorizontalExtent(hWndLB, 300 * LOWORD(GetDialogBaseUnits())); //判断进程是否仍在运行 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessID); if (hProcess == NULL){ ListBox_AddString(hWndLB, TEXT("")); //增加空行(为了更好看一点) ListBox_AddString(hWndLB, TEXT(" 指定的进程己经退出!")); return; } //抓取指定进程的快照 g_toolhelp.CreateSnapshot(TH32CS_SNAPALL, dwProcessID); //遍历进程地址空间 BOOL bOk = TRUE; PVOID pvAddress = NULL; SetWindowRedraw(hWndLB, FALSE); //遍历期间,禁止刷新列表框界面 while (bOk){ VMQUERY vmq; bOk = VMQuery(hProcess, pvAddress, &vmq); if (bOk){ //为区域内的每个块创建一行来显示 TCHAR szLine[1024]; ConstructRgnInfoLine(hProcess, &vmq, szLine, _countof(szLine)); ListBox_AddString(hWndLB, szLine); //如果需要展开块,更增加显示各块的信息 if (bExpandRegions){ for (DWORD dwBlock = 0; bOk && (dwBlock < vmq.dwRgnBlocks);dwBlock++){ ConstructBlkInfoLine(&vmq, szLine, _countof(szLine)); ListBox_AddString(hWndLB, szLine); //获取下一个区域的地址(注意,不是块) pvAddress = ((PBYTE)pvAddress + vmq.BlkSize); if (dwBlock<vmq.dwRgnBlocks-1){ //区域的最后一个块不需要查询 bOk = VMQuery(hProcess, pvAddress, &vmq); } } } //获取下一个区域(注意是区域,不是区域内的块) pvAddress = ((PBYTE)vmq.pvRgnBaseAddress + vmq.RgnSize); } } SetWindowRedraw(hWndLB, TRUE); //遍历完成,刷新列表框界面 CloseHandle(hProcess); } ////////////////////////////////////////////////////////////////////////// void Dlg_OnSize(HWND hWnd, UINT state, int cx, int cy){ //将列表框铺满客户区 SetWindowPos(GetDlgItem(hWnd, IDC_LISTBOX), NULL, 0, 0, cx, cy,SWP_NOZORDER); } ////////////////////////////////////////////////////////////////////////// void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtrl, UINT codeNotity) { switch (id) { case IDCANCEL: EndDialog(hwnd, id); break; case ID_REFRESH: Refresh(GetDlgItem(hwnd, IDC_LISTBOX), g_dwProcessID, g_bExpandRegions); break; case ID_EXPANDREGIONS: g_bExpandRegions = !g_bExpandRegions; Refresh(GetDlgItem(hwnd, IDC_LISTBOX), g_dwProcessID, g_bExpandRegions); break; case ID_COPYTOCLIPBOARD: CopyControlToClipboard(GetDlgItem(hwnd, IDC_LISTBOX)); break; } } ////////////////////////////////////////////////////////////////////////// BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { chSETDLGICONS(hwnd, IDI_VMMAP); //在标题栏中正在分析的是哪个进程 TCHAR szCaption[MAX_PATH * 2]; GetWindowText(hwnd, szCaption, _countof(szCaption)); g_toolhelp.CreateSnapshot(TH32CS_SNAPALL, g_dwProcessID); PROCESSENTRY32 pe = { sizeof(pe) }; StringCchPrintf(&szCaption[_tcslen(szCaption)], _countof(szCaption) - _tcslen(szCaption), TEXT("(PID=%u "%s")"), g_dwProcessID, g_toolhelp.ProcessFind(g_dwProcessID, &pe) ? pe.szExeFile : TEXT("未知")); SetWindowText(hwnd, szCaption); //默认显示方式为最大化 ShowWindow(hwnd, SW_MAXIMIZE); //设置列表框各列的宽度 int aTabs[5]; aTabs[0] = 48; aTabs[1] = aTabs[0] + 40; aTabs[2] = aTabs[1] + 52;//Count aTabs[3] = aTabs[2] + 12;//flags aTabs[4] = aTabs[3] + 20;//描述 ListBox_SetTabStops(GetDlgItem(hwnd, IDC_LISTBOX), _countof(aTabs), aTabs); //强制刷新列表框 Refresh(GetDlgItem(hwnd,IDC_LISTBOX),g_dwProcessID,g_bExpandRegions); return (TRUE); } ////////////////////////////////////////////////////////////////////////// INT_PTR WINAPI Dlg_Proc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam){ switch (uMsg) { chHANDLE_DLGMSG(hDlg, WM_INITDIALOG, Dlg_OnInitDialog); chHANDLE_DLGMSG(hDlg, WM_COMMAND, Dlg_OnCommand); chHANDLE_DLGMSG(hDlg, WM_SIZE, Dlg_OnSize); } return (FALSE); } ////////////////////////////////////////////////////////////////////////// int WINAPI _tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd) { CToolhelp::EnablePrivilege(SE_DEBUG_NAME, TRUE); g_dwProcessID = _ttoi(lpCmdLine); if (g_dwProcessID == 0){ g_dwProcessID = GetCurrentProcessId(); } DialogBox(hInstance, MAKEINTRESOURCE(IDD_VMMAP), NULL, Dlg_Proc); CToolhelp::EnablePrivilege(SE_DEBUG_NAME, FALSE); }
//VMQuery.h
/************************************************************************* Module: VMQuery.h Notices:Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre *************************************************************************/ #pragma once #include <windows.h> ////////////////////////////////////////////////////////////////////////// typedef struct{ //区域信息 PVOID pvRgnBaseAddress; DWORD dwRgnProtection; //PAGE_* SIZE_T RgnSize; DWORD dwRgnStorage; //MEM_*:Free,Image,Mapped,Private DWORD dwRgnBlocks; DWORD dwRgnGuardBlks; //If>0,表示区域包含线程栈 BOOL bRgnIsAStack; //TRUE:区域包含线程栈 //块信息 PVOID pvBlkBaseAddress; DWORD dwBlkProtection; //PAGE_* SIZE_T BlkSize; DWORD dwBlkStorage; //MEM_*:Free,Reserve,Image,Mapped,Private }VMQUERY, *PVMQUERY; ////////////////////////////////////////////////////////////////////////// BOOL VMQuery(HANDLE hProcess, LPCVOID pvAddress, PVMQUERY pVMQ); ///////////////////////////////文件结束///////////////////////////////////
//VMQuery.cpp
/************************************************************************* Module: VMQuery.cpp Notices:Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre *************************************************************************/ #include "../../CommonFiles/CmnHdr.h" #include "VMQuery.h" ////////////////////////////////////////////////////////////////////////// //Helper 结构体 typedef struct{ SIZE_T RgnSize; DWORD dwRgnStorage; //MEM_*:Free,Image,Mapped,Private DWORD dwRgnBlocks; DWORD dwRgnGuardBlks; //If>0,表示区域包含线程栈 BOOL bRgnIsAStack; //TRUE:区域包含线程栈 }VMQUERY_HELP,*PVMQUERY_HELP; ////////////////////////////////////////////////////////////////////////// //全局变量用来保存CPU分配粒度 static DWORD gs_dwAllocGran = 0; ////////////////////////////////////////////////////////////////////////// //遍历一个区域中所有的块,并将汇总信息通过VMQUERY_HELP返回 static BOOL VMQueryHelp(HANDLE hProcess, LPCVOID pvAddress, VMQUERY_HELP* pVMQHelp){ ZeroMemory(pVMQHelp, sizeof(*pVMQHelp)); //获得区域信息 MEMORY_BASIC_INFORMATION mbi; BOOL bOk = (sizeof(mbi) == VirtualQueryEx(hProcess, pvAddress, &mbi, sizeof(mbi))); if (!bOk){ return bOk; //错误的内存地址,返回FALSE } pVMQHelp->dwRgnStorage = mbi.Type; //从区域起始地址开始遍历 PVOID pvRgnBaseAddress = mbi.AllocationBase;//(这里是不会变的) //块的地址指针,先指向第1个块 PVOID pvAddressBlk = pvRgnBaseAddress; for (;;){ //获得当前块的信息 bOk = (VirtualQueryEx(hProcess, pvAddressBlk, &mbi, sizeof(mbi))==sizeof(mbi)); if (!bOk) break; //无法获得信息,则退出循环 //块是否在同一个区域? if (mbi.AllocationBase != pvRgnBaseAddress) break; //如果是下一个区域中的块,则退出循环 //己经获得了区域中的一个块 pVMQHelp->dwRgnBlocks++; //找到一个块,块数加1 pVMQHelp->RgnSize += mbi.RegionSize; //将块大小累加到区域大小 //如果块具有PAGE_GUARD属性,则计数加1 if ((mbi.Protect & PAGE_GUARD) == PAGE_GUARD) pVMQHelp->dwRgnGuardBlks++; //判断区域的物理存储器类型,因为块可能从MEM_IMAGE转为MEM_PRIVATE //或从MEM_MAPPED转为MEM_PRIVATE;而MEM_PRIVATE总是可以被MEM_IMAGE //或MEM_MAPPED重写。 if (pVMQHelp->dwRgnStorage == MEM_PRIVATE) pVMQHelp->dwRgnStorage = mbi.Type; //获取一下个块的地址 pvAddressBlk = (PVOID)((PBYTE)pvAddressBlk + mbi.RegionSize); } //最后检查区域是否包含有线程栈 pVMQHelp->bRgnIsAStack = (pVMQHelp->dwRgnGuardBlks > 0); return (TRUE); } ////////////////////////////////////////////////////////////////////////// BOOL VMQuery(HANDLE hProcess, LPCVOID pvAddress, PVMQUERY pVMQ){ if (gs_dwAllocGran ==0){ //第1次调用时,先获取CPU分配粒度 SYSTEM_INFO sinf; GetSystemInfo(&sinf); gs_dwAllocGran = sinf.dwAllocationGranularity; } ZeroMemory(pVMQ, sizeof(*pVMQ));//初始化VMQUERY结构体 //通过传入的pvAddress获得MEMORY_BASIC_INFORMATION信息 MEMORY_BASIC_INFORMATION mbi; BOOL bOk = (VirtualQueryEx(hProcess, pvAddress, &mbi, sizeof(mbi)) == sizeof(mbi)); if (!bOk){ return (bOk); //错误的内存地址,返回失败 } //首先,填充块字段 switch (mbi.State) //3种状态:FREE、RESERVE和COMMIT { case MEM_FREE: //空闲块 pVMQ->pvBlkBaseAddress = NULL; pVMQ->BlkSize = 0; pVMQ->dwBlkProtection = 0; pVMQ->dwBlkStorage = MEM_FREE; break; case MEM_RESERVE: //保留,(未提交到该块存储器里) pVMQ->pvBlkBaseAddress = mbi.BaseAddress; pVMQ->BlkSize = mbi.RegionSize; //对于一个未提交的块,mbi.Protect是无效的。所以可以让 //块的属性继承自区域的保护属性 pVMQ->dwBlkProtection = mbi.AllocationProtect; pVMQ->dwBlkStorage = MEM_RESERVE; break; case MEM_COMMIT: //己提交块 pVMQ->pvBlkBaseAddress = mbi.BaseAddress; pVMQ->BlkSize = mbi.RegionSize; pVMQ->dwBlkProtection = mbi.Protect; pVMQ->dwBlkStorage = mbi.Type; break; default: DebugBreak(); break; } //然后,填充区域字段 VMQUERY_HELP VMQHelp; switch (mbi.State) { case MEM_FREE: //空闲 pVMQ->pvRgnBaseAddress = mbi.BaseAddress; pVMQ->dwRgnProtection = mbi.AllocationProtect; pVMQ->RgnSize = mbi.RegionSize; pVMQ->dwRgnStorage = MEM_FREE; pVMQ->dwRgnBlocks = 0; pVMQ->dwRgnGuardBlks = 0; //1为线程栈所预订 pVMQ->bRgnIsAStack = FALSE; break; case MEM_RESERVE: pVMQ->pvRgnBaseAddress = mbi.AllocationBase; //这里与上面的块是不同的。 pVMQ->dwRgnProtection = mbi.AllocationProtect; //迭代,通过所有块属性来推荐整个区域的属性 VMQueryHelp(hProcess, pvAddress, &VMQHelp); pVMQ->RgnSize = VMQHelp.RgnSize; pVMQ->dwRgnStorage = VMQHelp.dwRgnStorage; pVMQ->dwRgnBlocks = VMQHelp.dwRgnBlocks; pVMQ->dwRgnGuardBlks = VMQHelp.dwRgnGuardBlks; pVMQ->bRgnIsAStack = VMQHelp.bRgnIsAStack; break; case MEM_COMMIT: //己提交 pVMQ->pvRgnBaseAddress = mbi.AllocationBase; //这里与上面的块是不同的。 pVMQ->dwRgnProtection = mbi.AllocationProtect; //迭代,通过所有块属性来推荐整个区域的属性 VMQueryHelp(hProcess, pvAddress, &VMQHelp); pVMQ->RgnSize = VMQHelp.RgnSize; pVMQ->dwRgnStorage = VMQHelp.dwRgnStorage; pVMQ->dwRgnBlocks = VMQHelp.dwRgnBlocks; pVMQ->dwRgnGuardBlks = VMQHelp.dwRgnGuardBlks; pVMQ->bRgnIsAStack = VMQHelp.bRgnIsAStack; break; default: DebugBreak(); break; } return bOk; } ////////////////////////////////////文件结束////////////////////////////////
//resource.h
//{{NO_DEPENDENCIES}} // Microsoft Visual C++ 生成的包含文件。 // 供 14_VMMap.rc 使用 // #define IDD_VMMAP 100 #define IDC_LISTBOX 101 #define IDI_VMMAP 102 #define IDR_MENU1 102 #define IDR_VMMAP 103 #define ID_REFRESH 40001 #define ID_EXPANDREGIONS 40002 #define ID_COPYTOCLIPBOARD 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 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
//VMMap.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"" " "