Reputation: 1914
I'm trying to understand how exactly i should extract MapViewOfFile's return buffer size. I used the following command to allocate a shared memory
hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, dwDataSize, strSharedMemoryName.c_str());
Filled it with stuff using the following code snippet:
pBuffer = DynamicAPI::MapViewOfFile(hFileMapping, FILE_MAP_WRITE, 0, 0, dwDataSize);
if (nullptr == pBuffer || GetLastError() != 0)
{
LOG_ERROR(L"Failed to MapViewOfFile: " << GetLastError());
break;
}
// Copy buffer to the shared memory
::CopyMemory(pBuffer, pData, dwDataSize);
And then, somewhere else, tried to reopen that shared memory and read the overall buffer:
HANDLE hSharedMemory = OpenFileMapping(FILE_MAP_READ, FALSE, m_strSharedName.c_str());
if (nullptr == hSharedMemory)
{
return false;
}
LPVOID pData = nullptr;
if (nullptr == (pData = MapViewOfFile(hSharedMemory, FILE_MAP_READ, 0, 0, 0)))
{
LOG_ERROR(L"Failed to MapViewOfFile");
return false;
}
my next line was going to be
std::string strData = pData; // use std::string::assign
However, i have no idea how big pData
is, one option is to send the overall size in the buffer, however MSDN states that VirtualQueryEx
is capable of doing such thing.
I tried to execute the following code snippet:
MEMORY_BASIC_INFORMATION info;
SIZE_T szBufferSize = ::VirtualQueryEx(::GetCurrentProcess(), pData, &info, sizeof(info));
But this, gives me the size of a single page if I'm not mistaken, how do i utilize this to give me the size of the overall buffer?
thanks!
Upvotes: 2
Views: 3684
Reputation: 22327
It is actually possible, but Microsoft for some reason did not document it. The NtQuerySection
API existed since early versions of Windows NT and still does in Windows 10.
So here's how you do it (obviously at your own risk of relying on an undocumented kernel API):
HANDLE hFileMapping = ::OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, strSharedMemName);
_ASSERT(hFileMapping);
ULONGLONG uicbSharedMemSize = 0;
//Get the handle returned by the CreateFileMapping function
//Assuming the same process here...
HANDLE hDupH;
if((::DuplicateHandle(
::GetCurrentProcess(), hFileMapping,
::GetCurrentProcess(), &hDupH,
DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS, FALSE, 0)))
{
hFileMapping = hDupH;
enum SECTION_INFORMATION_CLASS{
SectionBasicInformation,
SectionImageInformation
};
typedef struct _SECTION_BASIC_INFORMATION {
ULONG Unknown;
ULONG SectionAttributes;
LARGE_INTEGER SectionSize;
} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION;
static NTSTATUS
(NTAPI
*pfnNtQuerySection)(
IN HANDLE SectionHandle,
IN SECTION_INFORMATION_CLASS InformationClass,
OUT PVOID InformationBuffer,
IN ULONG InformationBufferSize,
OUT PULONG ResultLength OPTIONAL ) = NULL;
if(!pfnNtQuerySection)
(FARPROC&)pfnNtQuerySection = ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "NtQuerySection");
if(pfnNtQuerySection)
{
SECTION_BASIC_INFORMATION sbi = {0};
ULONG ucbRead = 0;
NTSTATUS stat = pfnNtQuerySection(hFileMapping, SectionBasicInformation, &sbi, sizeof(sbi), &ucbRead);
if(stat >= 0)
{
//The size returned will be rounded up to the page size (i.e. 4K in most cases)
uicbSharedMemSize = sbi.SectionSize.QuadPart;
}
}
}
first of all correct definition for SECTION_BASIC_INFORMATION
is :
typedef struct _SECTION_BASIC_INFORMATION
{
PVOID BaseAddress;
ULONG AllocationAttributes;
LARGE_INTEGER MaximumSize;
} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION;
so old definition (with DWORD
first member - was wrong and will be not work for 64bit code).
at second for call NtQuerySection
the section handle must have SECTION_QUERY
access, otherwise will be STATUS_ACCESS_DENIED
- so line:
OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, strSharedMemName);
is wrong. must be
OpenFileMapping(SECTION_QUERY|FILE_MAP_READ|FILE_MAP_WRITE, FALSE, strSharedMemName);
or any access, including SECTION_QUERY
note, that if use OpenFileMapping
and set dwDesiredAccess
exactly to SECTION_QUERY
the OpenFileMapping
by unknown reason change it to SECTION_MAP_READ
- was next line of code inside this api. if (dwDesiredAccess == SECTION_QUERY) dwDesiredAccess = SECTION_MAP_READ;
so need add some access to SECTION_QUERY
. of course we can use SECTION_QUERY
exactly with ZwOpenSection
next - for what call DuplicateHandle
?!? this is absolute senseless in task context call. we got handle to section via OpenFileMappingW
or ZwOpenSection
.
and finally for what GetModuleHandle
+ GetProcAddress
for ZwQuerySection
? how we interesting call OpenFileMappingW
without GetModuleHandle
+ GetProcAddress
for example ? how GetProcAddress
call ? in same way we can call ZwQuerySection
- simply link with ntdll.dll via ntdll.lib which exist in every wdk lib folder. so final code must be:
if (HANDLE hMap = OpenFileMappingW(SECTION_QUERY|SECTION_MAP_READ, FALSE, name))
{
SECTION_BASIC_INFORMATION sbi;
if (0 <= ZwQuerySection(hMap, SectionBasicInformation, &sbi, sizeof(sbi), 0))
{
DbgPrint("section size = %I64x\n", sbi.Size.QuadPart);
}
CloseHandle(hMap);
}
or
HANDLE hMap;
if (0 <= ZwOpenSection(&hMap, SECTION_QUERY, &oa))
{
SECTION_BASIC_INFORMATION sbi;
if (0 <= ZwQuerySection(hMap, SectionBasicInformation, &sbi, sizeof(sbi), 0))
{
DbgPrint("section size = %I64x\n", sbi.Size.QuadPart);
}
CloseHandle(hMap);
}
Upvotes: 1
Reputation: 36348
As far as I know, there is no way to retrieve the size of an existing file mapping or file mapping view. You are expected to track this information yourself.
MSDN states that VirtualQueryEx is capable of doing such thing.
No, all VirtualQueryEx can determine is the number of pages reserved for the view. That means that the result is always rounded up to a page size. Also, there is no explicit guarantee that MapViewOfFile will reserve only the minimum number of pages necessary to map the file. For example, it might choose to round it up to the allocation granularity.
Upvotes: 7