Фон: Я пытаюсь создать файл сопоставления памяти, к которому могут обращаться несколько процессов. В приведенном ниже коде я только вставляю код, относящийся к вопросу, который мне в настоящее время необходимо упростить. Согласно msdn, я должен иметь возможность создать карту файлов, отобразить вид файла и закрыть дескриптор, полученный от CreateFileMapping, а MapViewOfFile сохранит мою FileMap в действии. FileMap должен быть по-прежнему доступен, пока я не UnmapViewOfFile.
MSDN: Функция CreateFileMapping
Сопоставленные представления объекта сопоставления файлов поддерживают внутренние ссылки на объект, и объект сопоставления файлов не закрывается, пока не будут освобождены все ссылки на него. Поэтому, чтобы полностью закрыть объект сопоставления файлов, приложение должно отменить отображение всех отображенных представлений объекта сопоставления файлов, вызвав 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;
}
Из MSDN:
Сопоставленные представления объекта сопоставления файлов поддерживают внутренние ссылки на объект, и объект сопоставления файлов не закрывается, пока не будут освобождены все ссылки на него. Поэтому, чтобы полностью закрыть объект сопоставления файлов, приложение должно отменить отображение всех отображенных представлений объекта сопоставления файлов, вызвав UnmapViewOfFile, и закрыть дескриптор объекта сопоставления файлов, вызвав метод CloseHandle. Эти функции можно вызывать в любом порядке.
Документация CreateFileMapping гласит, что для полного закрытия файла все дескрипторы должны быть закрыты, и порядок не имеет значения. Эта логика необратима: вы не можете закрыть дескриптор и ожидать использования других дескрипторов, как если бы сопоставление файлов не было «закрытым».
Другими словами, это означает, что для очистки сопоставления файлов необходимо закрыть все дескрипторы в любом порядке. Однако вы не можете закрыть базовый объект сопоставления файлов и по-прежнему использовать представления, которые зависят от него.
CreateFileMapping()
Документация гласит:
Сопоставленные представления объекта сопоставления файлов внутренние ссылки к объекту, и объект сопоставления файлов не закрывается, пока все ссылки на него не будут освобождены.
CloseHandle()
Документация гласит:
В общем, CloseHandle делает недействительным указанный дескриптор объекта, уменьшает количество дескрипторов объекта и выполняет проверки сохранения объекта. После того, как последний дескриптор объекта закрыт, объект удаляется из системы.
Отображенные представления просто сохраняют счетчик ссылок объекта отображения выше нуля до тех пор, пока они не будут отображены, но они не сохраняют сам основной файл / отображение открытым.
после того, как вы закроете представление дескриптора раздела в процессе создания, оно не исчезнет, пока не отобразится имя файла — «MyFileMappingObject» в пространстве имен NT уничтожается. в результате следующий вызов CreateFileMapping — не найден именованный объект «MyFileMappingObject» и создайте новый (когда мачта по вашей логике откроет существующий). еще раз — раздел не уничтожен, но имя уничтожено.
То, что вы называете — Плохое решение — на самом деле не плохо — это абсолютно нормально — вы не должны близко обращаться к Разделу. и это не утечка ручки — просто будет постоянная открытая ручка в вашем процессе. эта ситуация абсолютно нормальная
Вы используете раздел NAMED. после закрытия этого дескриптора — раздел НЕ УНИЧТОЖЕН — потому что существует также вид раздела, который его держит. но НАИМЕНОВАНИЕ раздела УНИЧТОЖЕНО. и новый вызов для создания раздела — не открывать существующий, а создавать новый.
Существуют еще одно решение, которое показывает, что вы все не правы.
это требует 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)
постскриптум
SE_CREATE_PERMANENT_PRIVILEGE — обычно есть только LocalSystem, но мы можем его получить, если есть SE_DEBUG_PRIVILEGE + SE_IMPERSONATE_PRIVILEGE — открыть «системный» поток и выдать себя за него