现象:
HANDLE handle = nullptr;
BOOL bCreate = FALSE;
handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, strFileID);
if (handle && NO_ERROR == GetLastError())
bCreate = TRUE;
今天遇到一个奇怪的现象,OpenFileMapping后handle有值但GetLastError() = 2,百思不得其解。
发现问题:
调试过程中发现$err,hr显示在OpenFileMapping之前就一直是2,猜测OpenFileMapping成功不会更新GetLastError()的值,于是:
SetLastError(ERROR_FILE_NOT_FOUND);
HANDLE handle1 = CreateFileMapping((HANDLE)0xFFFFFFFF, NULL, PAGE_READWRITE, 0, 0x1000, _T("123")); //$err,hr = S_OK
LPVOID pVoid1 = MapViewOfFile(handle1, FILE_MAP_ALL_ACCESS, 0, 0, 0);
SetLastError(ERROR_FILE_NOT_FOUND);
HANDLE handle2 = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, _T("123")); //$err,hr = ERROR_FILE_NOT_FOUND
LPVOID pVoid2 = MapViewOfFile(handle2, FILE_MAP_ALL_ACCESS, 0, 0, 0);
确定了刚才的猜测。
解决方法:
DWORD SafeGetLastError()
{
DWORD dwError = GetLastError();
SetLastError(NO_ERROR);
return dwError;
}
1.首先想到的是定义如上函数,获取之后马上将值重置,但随即否定,不可能在每个会更新GetLastError()值的地方都调用该函数。
2.去掉GetLastError的返回值判断,但可能发生其它意外情况,我不确定到底会不会有问题,有风险。
3.在调用OpenFileMapping之前SetLastError(NO_ERROR),不足之处在于每次调用之前都需要加,可以通过增加SafeOpenFileMapping函数解决,好在整个程序就三处,也就懒得加了。
题外:
但是又引出了另外的疑问:
handle1 = 0x0000010c;
handle2 = 0x00000108;
pVoid1 = 0x00320000;
pVoid2 = 0x00330000;
我们知道句柄是标识一个对象的ID,操作系统内核对象的句柄在不同进程间的值可以不相同,每个进程维护着自己的句柄映射表,但相同进程获得的值应该相同。虽然指针也是物理内存的映射地址,不同的指针可以指向同一块物理内存,但32位系统指针为8位十六进制的值,也就是一个进程可以访问的内存大小为16的8次方 = 4294967296B = 4096MB = 4G,如果两个指针指向了同一块内存就说明该程序在释放这个指针之前可以访问的内存大小就小了。
进行如下测试:
*(int*)pVoid1 = 4;
查看内存发现两个地址中的值都被改了,可以正常使用共享内存。
从代码级别上考虑,两次Create就要有两次Release,如果是相同的值,可以通过计数器实现,委实不知道为什么这么设计。