CreateFileMapping,MapViewOfFile,处理泄漏的c ++

时间:2015-04-09 15:17:07

标签: c++ winapi multiprocess file-mapping handle-leak

后台:我正在尝试创建一个可由多个进程访问的内存映射文件。在下面的代码中,我只输入了与我目前的问题有关的代码,使事情更简单。根据msdn我应该能够创建一个文件映射,映射文件的视图并关闭我从CreateFileMapping收到的句柄,MapViewOfFile将保持我的FileMap活着。在我UnmapViewOfFile之前,FileMap仍然可以访问。

MSDN: CreateFileMapping function

  

文件映射对象的映射视图维护对该对象的内部引用,并且在释放对它的所有引用之前,文件映射对象不会关闭。因此,要完全关闭文件映射对象,应用程序必须通过调用UnmapViewOfFile取消映射文件映射对象的所有映射视图,并通过调用CloseHandle来关闭文件映射对象句柄。可以按任何顺序调用这些函数。

问题:成功映射文件视图然后关闭CreateFileMapping收到的句柄后,FileMap不再存在(它应该仍然存在),我的MemMapFileReader能够创建一个新的地图误差为0.(当它应该是接收错误183'已存在'时)

错误解决方案:不关闭句柄允许MemMapFileReader程序访问它,但会导致MemMapFileCreator中的句柄泄漏,因为在关闭进程之前句柄永远不会关闭。

问题:我错过了什么或做错了什么?

MemMapFileCreator

#include "stdafx.h"


#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <iostream>
#define BUF_SIZE 256
TCHAR szName[] = TEXT("MyFileMappingObject");
TCHAR szMsg[] = TEXT("Message from first process.");

int _tmain()
{
HANDLE hMapFile;
LPCTSTR pBuf;

hMapFile = CreateFileMapping(
    INVALID_HANDLE_VALUE,    // use paging file
    NULL,                    // default security
    PAGE_READWRITE,          // read/write access
    0,                       // maximum object size (high-order DWORD)
    BUF_SIZE,                // maximum object size (low-order DWORD)
    szName);                 // name of mapping object

DWORD lastError = GetLastError();
if (hMapFile == NULL)
{
    _tprintf(TEXT("Could not create file mapping object (%d).\n"),
        GetLastError());
    std::cin.get();
    return 1;
}
pBuf = (LPTSTR)MapViewOfFile(hMapFile,   // handle to map object
    FILE_MAP_ALL_ACCESS, // read/write permission
    0,
    0,
    BUF_SIZE);

if (pBuf == NULL)
{
    _tprintf(TEXT("Could not map view of file (%d).\n"),
        GetLastError());

    CloseHandle(hMapFile);

    std::cin.get();
    return 1;
}


CopyMemory((PVOID)pBuf, szMsg, (_tcslen(szMsg) * sizeof(TCHAR)));

CloseHandle(hMapFile);

_getch();


UnmapViewOfFile(pBuf);
return 0;
}

MemMapFileReader

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <iostream>
#pragma comment(lib, "user32.lib")

#define BUF_SIZE 256
TCHAR szName[] = TEXT("MyFileMappingObject");

int _tmain()
{
HANDLE hMapFile;
LPCTSTR pBuf;

hMapFile = CreateFileMapping(
    INVALID_HANDLE_VALUE,
    NULL,
    PAGE_READWRITE,   // read/write access
    0,
    BUF_SIZE,
    szName);               // name of mapping object
DWORD lastError = GetLastError();
if (hMapFile == NULL)
{
    _tprintf(TEXT("Could not open file mapping object (%d).\n"),
        GetLastError());
    std::cin.get();
    return 1;
}

pBuf = (LPTSTR)MapViewOfFile(hMapFile, // handle to map object
    FILE_MAP_ALL_ACCESS,  // read/write permission
    0,
    0,
    BUF_SIZE);

if (pBuf == NULL)
{
    _tprintf(TEXT("Could not map view of file (%d).\n"),
        GetLastError());

    CloseHandle(hMapFile);

    std::cin.get();
    return 1;
}

MessageBox(NULL, pBuf, TEXT("Process2"), MB_OK);

UnmapViewOfFile(pBuf);

CloseHandle(hMapFile);

std::cin.get();
return 0;
}

4 个答案:

答案 0 :(得分:4)

来自MSDN:

  

文件映射对象的映射视图维护对该对象的内部引用,并且在释放对它的所有引用之前,文件映射对象不会关闭。因此,要完全关闭文件映射对象,应用程序必须通过调用UnmapViewOfFile取消映射文件映射对象的所有映射视图,并通过调用CloseHandle来关闭文件映射对象句柄。可以按任何顺序调用这些函数。

CreateFileMapping文档说明要完全关闭文件,必须关闭所有句柄,顺序无关紧要。这个逻辑是不可逆的:你不能关闭一个句柄,并期望使用其他句柄,就像文件映射没有“关闭”一样。

换句话说,这意味着要清理文件映射,您需要以任何顺序关闭所有句柄。但是,您无法关闭基础文件映射对象,仍然使用依赖于它的视图。

答案 1 :(得分:2)

CreateFileMapping()文档说:

  

文件映射对象的映射视图将内部引用维护到对象,并且文件映射对象在释放所有对它的引用之前不会关闭。

CloseHandle()文档说:

  

通常, CloseHandle使指定的对象句柄无效,减少对象的句柄计数,并执行对象保留检查。关闭对象的最后一个句柄后,将从系统中删除该对象。

映射视图只是将映射对象的引用计数保持在零以上,直到它们被取消映射,但它们不会使底层文件/映射本身保持打开状态。

答案 2 :(得分:0)

关闭之后创建者进程中的节句柄视图不会消失,直到取消它的名称 - &#34; MyFileMappingObject&#34;在NT命名空间中被销毁。结果接下来调用CreateFileMapping - 找不到命名对象&#34; MyFileMappingObject&#34;并创建新的(当您的逻辑打开现有时)。再次 - 部分没有被摧毁,但它的名字被摧毁了。 你的名字 - 坏的解决方案 - 真的不错 - 这是绝对正常的 - 你没有关闭部分的句柄。这不是手柄泄漏 - 只需在您的过程中永久打开手柄。这种情况绝对正常

你使用的是NAMED部分。关闭后它处理 - 部分没有被破坏 - 因为存在部分的视图,它持有它。但部分的名称是DESTROYED。和新创建部分的调用 - 不打开现有但创建新部分。

答案 3 :(得分:0)

存在其他一个解决方案,这表明你们都错了。 它需要SE_CREATE_PERMANENT_PRIVILEGE,但对于演示这是正常的

STATIC_OBJECT_ATTRIBUTES_EX(g_oa, "\\BaseNamedObjects\\MyFileMappingObject", OBJ_CASE_INSENSITIVE|OBJ_PERMANENT, 0, 0);

NTSTATUS CreateAndWrite()
{
    NTSTATUS status;
    HANDLE hSection;
    LARGE_INTEGER Size = { PAGE_SIZE };
    if (0 <= (status = ZwCreateSection(&hSection, SECTION_MAP_READ|SECTION_MAP_WRITE, &g_oa, &Size, PAGE_READWRITE, SEC_COMMIT, 0)))
    {
        PVOID BaseAddress = 0;
        SIZE_T ViewSize = 0;
        if (0 <= (status = ZwMapViewOfSection(hSection, NtCurrentProcess(), &BaseAddress, 0, 0, 0, &ViewSize, ViewUnmap, 0, PAGE_READWRITE)))
        {
            STATIC_WSTRING(szMsg, "Message from first process.");
            memcpy(BaseAddress, szMsg, sizeof(szMsg));
            ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
        }
        ZwClose(hSection);
    }

    return status;
}

NTSTATUS OpenReadAndDestroy()
{
    NTSTATUS status;
    HANDLE hSection;
    if (0 <= (status = ZwOpenSection(&hSection, SECTION_MAP_READ|DELETE, &g_oa)))
    {
        PVOID BaseAddress = 0;
        SIZE_T ViewSize = 0;
        if (0 <= (status = ZwMapViewOfSection(hSection, NtCurrentProcess(), &BaseAddress, 0, 0, 0, &ViewSize, ViewUnmap, 0, PAGE_READONLY)))
        {
            MessageBox(0, (PCWSTR)BaseAddress, 0, 0);
            ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
        }

        ZwMakeTemporaryObject(hSection);

        ZwClose(hSection);
    }

    return status;
}

        if (0 <= GotPermanentPrivilege())
        {
      if (0 <= CreateAndWrite())
      {
        // at this point - no one handles for "MyFileMappingObject" exist
        // we close all and even unmap view
        // but 1 reference to section object exist (by OBJ_PERMANENT flag)
        // and NAME is still exist
        // as result we can open,map and read data :)
        OpenReadAndDestroy();
      }
        }
你怎么说的? 所有句柄关闭后,部分被销毁?不能使用意见? d)

P.S。 SE_CREATE_PERMANENT_PRIVILEGE - 通常只有LocalSystem,但我们可以得到它,如果有SE_DEBUG_PRIVILEGE + SE_IMPERSONATE_PRIVILEGE - 打开&#34; system&#34;线程并模仿它