热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

第17章内存映射文件(2)_内存映射文件的3个主要用途

17.4映射到内存的可执行文件和DLL(1)EXE文件格式节名作用.text.exe和.dll文件的代码

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

 


推荐阅读
  • [转]doc,ppt,xls文件格式转PDF格式http:blog.csdn.netlee353086articledetails7920355确实好用。需要注意的是#import ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 本文详细解析了使用C++实现的键盘输入记录程序的源代码,该程序在Windows应用程序开发中具有很高的实用价值。键盘记录功能不仅在远程控制软件中广泛应用,还为开发者提供了强大的调试和监控工具。通过具体实例,本文深入探讨了C++键盘记录程序的设计与实现,适合需要相关技术的开发者参考。 ... [详细]
  • 2018年9月21日,Destoon官方发布了安全更新,修复了一个由用户“索马里的海贼”报告的前端GETShell漏洞。该漏洞存在于20180827版本的某CMS中,攻击者可以通过构造特定的HTTP请求,利用该漏洞在服务器上执行任意代码,从而获得对系统的控制权。此次更新建议所有用户尽快升级至最新版本,以确保系统的安全性。 ... [详细]
  • 在 Windows 10 环境中,通过配置 Visual Studio Code (VSCode) 实现基于 Windows Subsystem for Linux (WSL) 的 C++ 开发,并启用智能代码提示功能。具体步骤包括安装 VSCode 及其相关插件,如 CCIntelliSense、TabNine 和 BracketPairColorizer,确保在 WSL 中顺利进行开发工作。此外,还详细介绍了如何在 Windows 10 中启用和配置 WSL,以实现无缝的跨平台开发体验。 ... [详细]
  • WinMain 函数详解及示例
    本文详细介绍了 WinMain 函数的参数及其用途,并提供了一个具体的示例代码来解析 WinMain 函数的实现。 ... [详细]
  • 本文介绍如何在 Android 中自定义加载对话框 CustomProgressDialog,包括自定义 View 类和 XML 布局文件的详细步骤。 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 在Windows系统中安装TensorFlow GPU版的详细指南与常见问题解决
    在Windows系统中安装TensorFlow GPU版是许多深度学习初学者面临的挑战。本文详细介绍了安装过程中的每一个步骤,并针对常见的问题提供了有效的解决方案。通过本文的指导,读者可以顺利地完成安装并避免常见的陷阱。 ... [详细]
  • 深入解析Android 4.4中的Fence机制及其应用
    在Android 4.4中,Fence机制是处理缓冲区交换和同步问题的关键技术。该机制广泛应用于生产者-消费者模式中,确保了不同组件之间高效、安全的数据传输。通过深入解析Fence机制的工作原理和应用场景,本文探讨了其在系统性能优化和资源管理中的重要作用。 ... [详细]
  • C++ 异步编程中获取线程执行结果的方法与技巧及其在前端开发中的应用探讨
    本文探讨了C++异步编程中获取线程执行结果的方法与技巧,并深入分析了这些技术在前端开发中的应用。通过对比不同的异步编程模型,本文详细介绍了如何高效地处理多线程任务,确保程序的稳定性和性能。同时,文章还结合实际案例,展示了这些方法在前端异步编程中的具体实现和优化策略。 ... [详细]
  • 本文深入探讨了WinRing0及其源代码实现,详细解析了如何通过获取Ring0权限在应用程序中直接执行需要Ring0权限的CPU指令。此外,文章还提供了实例截图和核心代码示例,帮助读者更好地理解和应用这一技术。 ... [详细]
  • 本文详细介绍了一种利用 ESP8266 01S 模块构建 Web 服务器的成功实践方案。通过具体的代码示例和详细的步骤说明,帮助读者快速掌握该模块的使用方法。在疫情期间,作者重新审视并研究了这一未被充分利用的模块,最终成功实现了 Web 服务器的功能。本文不仅提供了完整的代码实现,还涵盖了调试过程中遇到的常见问题及其解决方法,为初学者提供了宝贵的参考。 ... [详细]
  • 在Android开发中,实现多点触控功能需要使用`OnTouchListener`监听器来捕获触摸事件,并在`onTouch`方法中进行详细的事件处理。为了优化多点触控的交互体验,开发者可以通过识别不同的触摸手势(如缩放、旋转等)并进行相应的逻辑处理。此外,还可以结合`MotionEvent`类提供的方法,如`getPointerCount()`和`getPointerId()`,来精确控制每个触点的行为,从而提升用户操作的流畅性和响应性。 ... [详细]
  • 掌握Android UI设计:利用ZoomControls实现图片缩放功能
    本文介绍了如何在Android应用中通过使用ZoomControls组件来实现图片的缩放功能。ZoomControls提供了一种简单且直观的方式,让用户可以通过点击放大和缩小按钮来调整图片的显示大小。文章详细讲解了ZoomControls的基本用法、布局设置以及与ImageView的结合使用方法,适合初学者快速掌握Android UI设计中的这一重要功能。 ... [详细]
author-avatar
烟灰TT
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有