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
//////////////////////////////////////////////////////////////////////////
//系统广播消息
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
#include
#include
#include
__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){
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
#include
#include
#include
//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
#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