17.4 映射到内存的可执行文件和DLL
(1)EXE文件格式
节名 |
作用 |
.text |
.exe和.dll文件的代码 |
.data |
己经初始化的数据 |
.bss |
未初始化的数据 |
.reloc |
重定位表(装载进程的进程地址空间) |
.rdata |
运行期只读数据 |
.CRT |
C运行期只读数据 |
.debug |
调用试信 |
.xdata |
异常处理表 |
.tls |
线程本地化存储 |
.idata |
输入文件名表 |
.edata |
输出文件名表 |
.rsrc |
资源表 |
.didata |
延迟输入文件名表 |
(2)加载exe的过程
①先CreateProcess创建进程内核对象,为进程创建一个私有地址空间(页目录和页表)
②系统根据exe大小,在默认的基地址0x0040 0000上预订适当大小的区域(可以在链接程序时用/BASE 选项更改基地址,方法是在VC工程属性\链接器\高级上设置)。
③系统会对地址空间区域进行标注,表明该区域的后备物理存储器来自于磁盘上的exe文件,而并非来自系统的页交换文件。
④读取exe文件的.idata节,此节列出exe所用到的所有dll文件。然后和exe文件一样,将dll文件映射到进程空间中。如果无法映射到基地址,系统会重新定位。(详见后面的《加载Dll过程》
⑤ 把所有的exe文件和DLL文件都映射到进程的地址空间之后,系统开始执行exe文件的启动代码,将第一页代码加载到内存,然后更新页目和页表。将第一条指令的地址交给线程指令指针。当系统执行时,会发现代码没有在内存中,就会通过页面错误机制,将exe文件中的代码加载到内存中。
(2)加载DLL的过程
系统通过LoadLibrary载入每个DLL,如果哪个DLL需要调用其他DLL,系统会同样地调用LoadLibrary来载入相应的DLL,其载入过程如下:
①预订一块足够大的地址空间来容纳DLL,并在默认的基地址(如0x10000000)预订该区域。(可以使用/BASE链接器开关来指定这个基地址)。与Windows系统的DLL都有不同的基地址,这样即使把它们载入到同一个地址空间,也不会发生重叠。
②如果系统无法在DLL文件指定的基地址处预订区域(可能是该区域被另一个DLL或EXE占用,或区域不够大),这时系统尝试在另一个地址来为DLL预订区域。但这时需要重定位,但重定位需要占用页交换文件中额外的存储空间,而且会增加加载DLL所需的时间。而如果DLL不包含重定位信息(使用链接器的/FIXED开关构建的DLL是不包含重定位信息的,这开关的好处是可以使DLL文件变得更小),那么将无法被载入。
③系统会对地址空间区域进行标注,表明该区域的后备物理存储器来自磁盘上的DLL文件,而不是系统的页交换文件。如果由于Windows不能将DLL载入到指定的基地址而必须执行重定位的话,那么系统还会另外进行标注,表明DLL中有一部分物理存储器被映射到了页交换文件。
(3)第2次加载exe的过程
①建立进程、映射进程空间与第1次加载EXE是一样的,只是当系统发现这个EXE己经建立了内存映射文件对象时,它就直接映射到进程空间了。只是当系统分配物理页面时,根据节的保护属性赋予页面的保护属性,对于代码节赋予READ属性,全局变量节赋予COPY_ON_WRITE属性。
②不同的实例共享代码节和其他的节,当实例需要改变页面内容时,会拷贝页面内容到新的页面,更新页目和页表。
③对于不同进程实例需要共享的变量,EXE文件有一个默认的节,给这个节赋予SHARED属性,我们也可以创建自己的SHARED节(请后面相关的内容)
17.4.1 同一个可执行文件和DLL的多个实例不会共享静态数据
(1)多实例的启动
①如果一个应用程序己经运行,当创建该应用程序的新实例时,会根据己经创建的同一个映射文件,打开另一个内存映射视图。通过内存映射文件,同一个实例可以共享内存中的代码和数据。
②当第2个实例启动时,系统把包含应用程序代码和数据的虚拟内存页面映射到第2个实例的地址空间。(注意虚拟内存即为内存的一部分,被载入到虚拟内存中的数据或代码可理解为就是内存中的数据了,注意与进程的地址空间的区别)
(2)“写时复制”机制——保证了多实例不会共享静态数据
①任何时候,当应用程序试图写入内存映射文件的时候,系统会截获这种尝试,接着发生“写时复制”,即为应用程序(如实例2)分配一块新的内存,然后复制“数据页面2”的内容到新的页面。并重新将“实例2”地址空间中的“数据页面2”重新映射到“新页面”
②这样,“实例1”和“实例2”地址空间中的“数据页面2”就分别被映射到虚拟内存中的不同页面,从而保护数据,使得数据不会被另一个实例修改。
17.4.2 在同一个可执行文件或DLL的多个实例间共享静态数据
(1)可执行文件的常用段以及段的属性
①常用的段:如.bss、.data、.text等,详细见前面的“EXE文件格式”表格
②段的属性
属性 |
含义 |
READ |
可以从该段读取数据 |
WRITE |
可以向该段写入数据 |
EXECUTE |
可以执行该段的内容 |
SHARED |
该段的内容为多个实例所共享(本质上是关闭了写时复制机制) |
(2)多实例共享数据的方法
①创建自己的“数据段”
#pragma data_seg(“MyShareName”) // MyShareName为段的名字,可自定义
LONG g_lInstanceCount = 0; //要共享的变量必须是经过初始化的
#pragma data_seg(); //告诉编译器停止把己初始化的变量放到MyShareName段中。
② 将自定义的“数据段”属性设为“共享”
#pragma comment(linker, “/SECTION:MyShareName,RWS”) //读、写、共享
(3)allocate声明符——可将初始化或未初始化的数据放到指定的段中
//创建一个“共享段”,并将己初始化的数据放入其中
#pragma data_seg("Shared")
int a = 0; //己初始化数据,在“Shared”中
int b = 0; //未初始化的数据,不在“Shared”段中
#pragma data_seg() //让编译器停止将己初始化变量放入“Shared”段
//初始化,并将变量放入“Shared”段中
__declspec(allocate("Shared")) int c = 0;
//将未始化变量放入“Shared”段中
__declspec(allocate("Shared")) int d; //己初始化,但不在“Shared”段中 int e = 0; //未初始化,也不在“Shared”段中 int f;
【AppInst程序】统计共有多少个应用程序的实例正在运行
//AppInst.cpp
/************************************************************************
Module: AppInst.cpp
Notices:Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre
************************************************************************/
#include "../../CommonFiles/CmnHdr.h" #include "resource.h" #include <tchar.h> ////////////////////////////////////////////////////////////////////////// //系统广播消息 UINT g_uMsgAppInstCountUpdate = WM_APP + 123; ////////////////////////////////////////////////////////////////////////// //创建“共享段” #pragma data_seg("Shared") volatile LONG g_lApplicationInstances = 0; //正在运行的实例的个数 #pragma data_seg() //告诉编译器,让Shared段只有可读、可读及共享属性 #pragma comment(linker,"/Section:Shared,RWS") BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam){ chSETDLGICONS(hwnd, IDI_APPINST); //强制刷新,以显示正确的实例数。 PostMessage(HWND_BROADCAST, g_uMsgAppInstCountUpdate, 0, 0); return TRUE; } ////////////////////////////////////////////////////////////////////////// void Dlg_OnCommand(HWND hWnd, int id, HWND hWndCtrl, UINT codeNotify){ switch (id) { case IDCANCEL: EndDialog(hWnd, id); break; } } ////////////////////////////////////////////////////////////////////////// INT_PTR WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ if (uMsg == g_uMsgAppInstCountUpdate){ SetDlgItemInt(hwnd, IDC_COUNT, g_lApplicationInstances, FALSE); } switch (uMsg) { chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog); chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand); } return FALSE; } ////////////////////////////////////////////////////////////////////////// int WINAPI _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nShowCmd) { //注删用于广播的消息,并何存ID,该消息用来通知实例个数发生变化 g_uMsgAppInstCountUpdate = RegisterWindowMessage(TEXT("MsgAppInstCountUpdate")); //新的应用程序实例正在运行 InterlockedExchangeAdd(&g_lApplicationInstances, 1); DialogBox(hInstance, MAKEINTRESOURCE(IDD_APPINST), NULL, Dlg_Proc); //应用程序的结束运行 InterlockedExchangeAdd(&g_lApplicationInstances, -1); //通知其他实例更新显示 PostMessage(HWND_BROADCAST, g_uMsgAppInstCountUpdate, 0, 0); return 0; }
//resource.h
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 17_AppInst.rc 使用
//
#define IDD_APPINST 1
#define IDC_COUNT 100
#define IDI_APPINST 101
#define IDI_ICON1 102
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
//AppInst.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_APPINST DIALOGEX 0, 0, 161, 21 STYLE DS_SETFONT | DS_CENTER | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "多个实例的应用程序" FONT 10, "宋体", 400, 0, 0x0 BEGIN LTEXT "正在运行的实例数:",IDC_STATIC,25,6,93,8,SS_NOPREFIX RTEXT "#",IDC_COUNT,113,6,16,12,SS_NOPREFIX END ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDI_APPINST ICON "AppInst.Ico" ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO BEGIN IDD_APPINST, DIALOG BEGIN END END #endif // APSTUDIO_INVOKED #endif // 中文(简体,中国) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED
17.5 映射到内存的数据文件
17.5.1 用内存映射文件来处理大文件
(1)处理大文件的思路
①把文件头的部分映射到视图中,完成对文件的第1个视图的访问后
②撤消对前一部分的映射,然后把文件的另一部分映射到视图,重复此过程,直到完成对整个文件的访问。
【Count0程序】演示一个32位地址空间中使用8GB文件的例子,用来统计一个二进制文件中所有值为0的字节数。
#include <stdio.h> #include <windows.h> #include <tchar.h> #include <locale.h> __int64 Count0s(PCTSTR pFileName) { //视图的地址必须是分配粒度的整数倍 SYSTEM_INFO sinf; GetSystemInfo(&sinf); //打开数据文件 HANDLE hFile = CreateFile(pFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); //创建文件映像 HANDLE hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); DWORD dwFileSizeHigh; __int64 qwFileSize = GetFileSize(hFile, &dwFileSizeHigh); qwFileSize += ((__int64)dwFileSizeHigh<<32); CloseHandle(hFile);//不必再使用hFile句柄 __int64 qwFileOffset = 0, qwNumOf0s = 0; while (qwFileSize > 0){ //计算块的大小 DWORD dwBytesInBlock = sinf.dwAllocationGranularity; if (qwFileSize < sinf.dwAllocationGranularity) dwBytesInBlock = (DWORD)qwFileSize; PBYTE pbFile = (PBYTE)MapViewOfFile(hFileMapping, FILE_MAP_READ, //access mode (DWORD)(qwFileOffset>>32), //high-order DWORD of Offset开始地址 (DWORD)(qwFileOffset & 0xFFFFFFFF),//low-order DWORD of offset dwBytesInBlock);//number of bytes to map //计算块中0的数量 for (DWORD dwByte = 0; dwByte < dwBytesInBlock;dwByte++){ if (pbFile[dwByte]==0) qwNumOf0s++; } //撤消视图,在地址空间中并不需要多视图 UnmapViewOfFile(pbFile); //跳到下一个块 qwFileOffset += dwBytesInBlock; qwFileSize -= dwBytesInBlock; } CloseHandle(hFileMapping); return (qwNumOf0s); } int _tmain(){ _tsetlocale(LC_ALL, _T("chs")); TCHAR szFileName[] = _T("aaa.mp4"); __int64 qwNumOf0s = 0; qwNumOf0s = Count0s(szFileName); _tprintf(_T("%s文件的字节流中,“0”的个数共有%I64d个"),szFileName,qwNumOf0s); return 0; }
17.5.2 内存映射文件和一致性
(1)同一个文件映射对象被映射成多个视图,则系统会确保各视图中的数据是一致的。即使是多个进程把同一个数据文件映射到多个视图中,数据也仍然会保持一致。(因为数据在每个页面在内存中只有一份,只是这些内存页被映射到多个进程的地址空间。
(2)同一数据文件创建多个文件映射对象,这些不同的文件映射对象的各个视图,系统并不保证数据是一致的。系统只保证同一文件映射对象的多个视图间保持一致。
(3)只读文件不存在一致性问题,经常用于内存映射文件。
(4)跨网络共享可写文件时,系统无法保证数据视图的一致性。
17.5.3 给内存映射文件指定基地址
(1)MapViewOfFileEx函数:可以把文件映射到指定的地址(类似VirtualAlloc可在指定基地址预订地址空间)
参数 |
含义 |
HANDLE hFileMappingObject |
这些参数的含义与MapViewOfFile函数一样 |
DWORD dwDesiredAccess |
|
DWORD dwFileOffsetHigh |
|
DWORD dwFileOffsetLow |
|
SIZE_T dwNumerOfBytesToMap |
|
PVOID pvBaseAddress |
①为要映射的文件指定一个目标地址。该地址必须是分配粒度(64K)的整数倍。否则函数会返回NULL,表示有错误发生,GetLastError得到ERROR_MAPPED_ALIGNMENT。 ②如果指定的NULL,则函数的行为与MapViewOfFile完全相同。 |
备注:①如果系统无法将文件映射至指定地址(如文件太大,导致与其他己预订的地址空间发生重叠,函数不会尝试去找另一个能够容纳文件的地址空间,而是返回NULL。 ②指定的地址必须是在进程的用户模式分区中,否则函数返回NULL |
(2)MapViewOfFileEx函数在跨进程共享数据的时候很有用。如共享链表时,每个元素的保存是下一个元素的内存地址。这时为防止这个内存地址在两个进程中指向的内容不同,可以通过内存映射文件,将映射到两个进程中的同一个基地址去。
17.5.4 内存映射文件的实现细节
(1)Win98下内存映射文件的细节
①Win98下视图总是被映射到0x80000000至0xBFFFFFFF范围内。注意,这范围的地址是被所有进程共享的。如果另一个进程也对同一个文件映射对象调用MapViewOfFile函数。则Windows会将第1个内存地址返回给第2个进程,即两个进程返回的内存地址是相同的。所以他们访问相同的数据,并且它们的视图具有相关性。
②当文件映射对象的视图被映射时,系统会为整个文件映射对象保留足够的地址空间,哪怕我们在MapViewOfFile函数中指定的只是其中的一小部分。因此,如果两个调用MapViewOfFile时,如果第1次映射的是整个文件,第2次映射的是文件从64KB处开始的位置,则两次调用时函数返回的地址会相差64KB。
(2)Win2000以上系统映射的细节
①不同进程对同一个文件映射对象调用MapViewOfFile返回的内存地址可能不同。文件映射对象是个内核对象,每次调用MapViewOfFile都会增加使用计数。
②如果某个进程两次调用MapViewOfFile,第1次映射整个文件,第2次从文件64KB偏移处开始映射,则首先两个文件映射大小是不同的,第一个区域的大小为整个文件映射对象的大小,第二个区域为文件映射对象大小减去64KB。其次,函数返回的地址相差也未必是64KB,因为每次调用MapViewOfFile时,文件被映射到不同的地址上去。
③尽管区域不同(包括大小和地址),但这两个视图是来自同一个文件映射对象,所以他们的数据仍然是相关的。
【TwoViews程序】演示两个MapViewOfFile得到的内存地址的不同(可以Win98与WinVista下做实验比较)
#include <stdio.h> #include <windows.h> #include <tchar.h> #include <locale.h> //TwoView int TwoView(PCTSTR pszFileName){ //打开一个己经存在的文件,但必须大于64KB HANDLE hFile = CreateFile(pszFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); //创建一个文件映射对象 HANDLE hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL); //创建整个文件的视图 PBYTE pbFile = (PBYTE)MapViewOfFile(hFileMapping, FILE_MAP_WRITE, 0, 0, 0); //创建视图2(从64KB偏移处开始) PBYTE pbFile2 = (PBYTE)MapViewOfFile(hFileMapping, FILE_MAP_WRITE, 0, 64 * 1024, 0); //文件大小 DWORD dwFileSizeHigh; __int64 qwFileSize = GetFileSize(hFile, &dwFileSizeHigh); //在Win98下视图被映射到0x80000000至0xBFFFFFFF的范围内,这个视图被所有进程共享 //两次MapViewOfFile时,指向同一个视图。所以,pbFile2-pbFile相差正好是64KB。 //但Win2000以上,即使是两次相同的MapViewOfFile映射时,其返回内存地址也是不同的。 //所以pbFile2-pbFile未必等于64KB。 int iDifference = int(pbFile2 - pbFile); _tprintf(_T("指针pbFile=0x%08X,pbFile2=0x%08X,\n两者相差=%d KB(%s64KB)\n"), pbFile2, pbFile, iDifference / 1024, iDifference == 64 * 1024 ? TEXT("等于") : TEXT("不等于")); UnmapViewOfFile(pbFile2); UnmapViewOfFile(pbFile); CloseHandle(hFileMapping); CloseHandle(hFile); return 0; } int _tmain(){ _tsetlocale(LC_ALL, _T("chs")); TCHAR szFileName[] = _T("aaa.mp4"); TwoView(szFileName); return 0; }
17.7 以页交换文件为后备存储器的内存映射文件
(1)以页交换文件而不是磁盘文件来作为后备存储器。(因不必创建或打开磁盘文件,所以不需要调用CreateFile)。
(2)调用CreateFileMapping时将hFile设为INVALID_HANDLE_VALUE(这等于告诉系统我们希望从页交换文件,而不是磁盘上的文件来作为文件映射对象的物理存储器)。所需的大小由dwMaximumSizeHigh和dwMaximumSizeLow参数决定。(注意,如果是以磁盘文件为后备存储器时,当CreateFile的返回值是INVLID_HANDLE_VALUE,系统则会创建一个以页交换文件为后备存储器的文件映射对象,这点一定要特别注意!因此,我们建议要经常对CreateFile返回值进行检查!)
(3)将视图映射到进程的地址空间:MapViewOfFile。
(4)CloseHandle(hFileMapping)关闭文件映射对象,并从页交换文件中回收所有己调拨的存储器。
【MMFShare程序】演示如何用内存映射文件在多个进程中传输数据
/************************************************************************* Module: MMFShare.cpp Notices:Copyright(c) 2008 Jeffrey Richter & Cristophe Nasarre *************************************************************************/ #include "../../CommonFiles/CmnHdr.h" #include "resource.h" #include <tchar.h> #define MMF_NAME TEXT("MMFSharedData") ////////////////////////////////////////////////////////////////////////// BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam){ chSETDLGICONS(hwnd, IDI_MMFSHARE); //初始化编辑框 Edit_SetText(GetDlgItem(hwnd, IDC_DATA), TEXT("Some test data")); //禁用关闭按钮 Button_Enable(GetDlgItem(hwnd, IDC_CLOSEFILE), FALSE); return TRUE; } ////////////////////////////////////////////////////////////////////////// void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify){ static HANDLE s_hFileMap = NULL;//文件映射对象句柄 switch (id) { case IDCANCEL: EndDialog(hwnd, id); break; case IDC_CREATEFILE: if (codeNotify != BN_CLICKED) break; //创建以页交换文件为后备存储器的文件映射对象 s_hFileMap = CreateFileMapping( INVALID_HANDLE_VALUE,//以页交换文件为后备存储器 NULL,//默认安全属性 PAGE_READWRITE, //可读可写 0, 4*1024, //所需的空间为4KB MMF_NAME); //名称为:MMFSharedData if (s_hFileMap != NULL){ if (GetLastError() == ERROR_ALREADY_EXISTS){ chMB("文件映射对象己经存在!"); CloseHandle(s_hFileMap); } else{ //成功创建内存映射文件 //视图映射到进程地址空间 PVOID pView = MapViewOfFile(s_hFileMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); if (pView != NULL){ //将编辑框的数据写入MMF Edit_GetText(GetDlgItem(hwnd, IDC_DATA), (PTSTR)pView, 4 * 1024); //撤销映射 UnmapViewOfFile(pView); //为防止用户多次单击该按钮,禁用这个按钮 Button_Enable(hwndCtl, FALSE); //启用关闭文件按钮 Button_Enable(GetDlgItem(hwnd, IDC_CLOSEFILE), TRUE); } } } else { chMB("创建文件映射对象失败!"); } break; case IDC_CLOSEFILE: if (codeNotify != BN_CLICKED) break; if (CloseHandle(s_hFileMap)){ //将按钮还原成默认的状态 Button_Enable(hwndCtl, FALSE); //关闭按钮 Button_Enable(GetDlgItem(hwnd, IDC_CREATEFILE), TRUE); //创建按钮 } break; case IDC_OPENFILE: if (codeNotify != BN_CLICKED) break; //判断是否存在一个名为“MMFSharedData”的文件映射对象 HANDLE hFileMapT = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, MMF_NAME); if (hFileMapT != NULL){ //映射到进程的地址空间 PVOID pView = MapViewOfFile(hFileMapT, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); if (pView != NULL){ //将MMF里的内容显示在编辑框中 Edit_SetText(GetDlgItem(hwnd, IDC_DATA), (PTSTR)pView); UnmapViewOfFile(pView); } else{ chMB("无法映射视图"); } } else{ chMB("无法打开文件映射对象"); } 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; } ////////////////////////////////////////////////////////////////////////// int WINAPI _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nShowCmd) { DialogBox(hInstance, MAKEINTRESOURCE(IDD_MMFSHARE), NULL, Dlg_Proc); return 0; }
//resource.h
//{{NO_DEPENDENCIES}} // Microsoft Visual C++ 生成的包含文件。 // 供 17_MMFShare.rc 使用 // #define IDD_MMFSHARE 1 #define IDC_DATA 100 #define IDC_CREATEFILE 101 #define IDC_OPENFILE 102 #define IDI_MMFSHARE 102 #define IDC_CLOSEFILE 103 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 103 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
//MMFShare.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 ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDI_MMFSHARE ICON "MMFShare.ico" ///////////////////////////////////////////////////////////////////////////// // // Dialog // IDD_MMFSHARE DIALOGEX 38, 36, 186, 61 STYLE DS_SETFONT | DS_CENTER |WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "共享数据(内存映射文件)" FONT 10, "宋体", 400, 0, 0x0 BEGIN PUSHBUTTON "&创建映射数据",IDC_CREATEFILE,4,4,84,14,WS_GROUP PUSHBUTTON "关闭映射数据",IDC_CLOSEFILE,96,4,84,14 LTEXT "数据:",IDC_STATIC,4,26,24,8 EDITTEXT IDC_DATA,28,24,153,12 PUSHBUTTON "打开映射并获取数据",IDC_OPENFILE,40,44,104,14,WS_GROUP END ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO // #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO BEGIN IDD_MMFSHARE, DIALOG BEGIN END END #endif // APSTUDIO_INVOKED #endif // 中文(简体,中国) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED