The underlying problem I wish to solve is to have two regions of virtual memory that is backed by physical memory (ie, VirtualAlloc), and a third region of virtual memory that "points" to one of the other two regions of virtual memory.
While I believe you can map one of the two backed regions of memory with MapViewOfFileEx, I can't find any way of ensuring that the lpBaseAddress doesn't get stolen when changing the mapping from one region to another region.
My initial idea was to VirtualAlloc with MEM_RESERVE, but MapViewOfFileEx can't use reserved memory.
I believe I can accomplish the same thing on posix with shm_open and family, as mmap can override mapped regions.
You can't directly override the address if it is in use.
To use the same address (mappedAddress) you need call UnmapViewOfFile before remap. Something like this:
HANDLE targetFile = CreateFile(L"target.txt", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
HANDLE fileMap = CreateFileMapping(targetFile, 0, PAGE_READWRITE, 0, 10, L"myTestMap");
LPVOID mappedAddress = MapViewOfFileEx(fileMap, FILE_MAP_ALL_ACCESS, 0, 0, 0, 0);
BOOL result = UnmapViewOfFile(mappedAddress); // Get the address.
HANDLE targetFile2 = CreateFile(L"target2.txt", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
HANDLE fileMap2 = CreateFileMapping(targetFile2, 0, PAGE_READWRITE, 0, 10, L"myTestMap2");
LPVOID mappedAddress2 = MapViewOfFileEx(fileMap2, FILE_MAP_ALL_ACCESS, 0, 0, 0, mappedAddress); // Use the same address.
Related
I wrote a font parser and renderer based on some functionality in the freetype library, but it currently only works for font files that I specify. I'd like to be able to retrieve fonts from the Windows Font Mapper and then parse and render them, but I can't figure out how to get the actual font data from a logical font.
I thought that the easy way to accomplish this would be to get the font file name from the logical font, but based on other stack overflow posts, there isn't any good solution to this because there isn't a 1 to 1 mapping of physical fonts to the logical font you specify.
This code shows how I'm currently retrieving a handle to a logical font:
HFONT windowsFont = CreateFontA(72, 0, 0, 0, 0,
0, 0, 0, ANSI_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH, "Arial");
HDC deviceContext = GetDC(windowHandle);
SelectObject(deviceContext, windowsFont);
DWORD fontSize = GetFontData(deviceContext, 0, 0, NULL, 0);
void *fontData = malloc(fontSize);
fontSize = GetFontData(deviceContext, 0, 0, fontData, 0);
char *fontFileData;
size_t fontFileSize;
// This is a function that I wrote that does what you'd expect. It opens
// a file, reads all the bytes to a buffer and closes the file
readFileToBuff(&fontFileData, &fontFileSize, "c:/windows/fonts/arial.ttf");
assert(fontFileSize == fontSize); // This passes
assert(((char)fontFileData) == ((char)fontData)); // This fails
Based on this stack overflow post which is for Java, I'm thinking that what I want to do may not be possible. It seems that the only solution may be to look at all the system font files and try to figure out what they are.
How to get ttf font data from system fonts in java
This surprises me though, because it seems that it would be relatively common for a program to want to render fonts themselves, without relying on the Windows renderer. Does anyone know of a way to get the font data, or how other people have solved this problem?
I don't understand why it's getting the exact same amount of bytes as
the file, but the data isn't the same.
From the official sample, I found the correct way to use GetFontData.
This is the modified code, and the returned data is the same as fontFileData.
HRESULT hr = S_OK;
LPVOID ptr = NULL;
HGLOBAL hGlobal = NULL;
...
HFONT windowsFont = CreateFontA(72, 0, 0, 0, 0,
0, 0, 0, ANSI_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH, "Arial");
HDC deviceContext = GetDC(hWnd);
SelectObject(deviceContext, windowsFont);
DWORD fontSize = GetFontData(deviceContext, 0, 0, NULL, 0);
hGlobal = GlobalAlloc(GMEM_MOVEABLE, fontSize);
ptr = GlobalLock(hGlobal);
if (!ptr)
{
hr = HRESULT_FROM_WIN32(GetLastError());
fwprintf(stderr, L"ERROR: Could not lock global memory object: %08X\n", hr);
}
else
{
if (GetFontData(deviceContext, 0, 0, ptr, fontSize) == GDI_ERROR)
{
fwprintf(stderr, L"ERROR: Could not get font data\n");
hr = E_UNEXPECTED;
}
GlobalUnlock(hGlobal);
}
char *fontFileData;
size_t fontFileSize;
// This is a function that I wrote that does what you'd expect. It opens
// a file, reads all the bytes to a buffer and closes the file
readFileToBuff(&fontFileData, &fontFileSize, "c:/windows/fonts/arial.ttf");
assert(fontFileSize == fontSize); // This passes
assert(((char)fontFileData) == ((char)fontData)); // This fails
I am using the following code to memory-map a file for reading in windows (Windows 10):
HANDLE windowsFileHdl = CreateFileA(filePath, GENERIC_READ, FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (windowsFileHdl == INVALID_HANDLE_VALUE)
{
return false;
}
HANDLE fileMappedHdl = CreateFileMappingA(windowsFileHdl, 0, PAGE_READONLY, 0, 0, nullptr);
char* baseFileData = (char*)MapViewOfFile(fileMapedHdl, FILE_MAP_READ, 0, 0, 0);
I would still like others to write to this file while I am reading it. Is this possible?
Currently, I get an error like this:
The process cannot access the file because it is being used by another process.
The above error was received by trying to open the file in a separate program (Notepad) while I had it memory mapped.
Is this a limitation with how the OS maps the file to memory or am I missing some flag?
EDIT:
#zett42 pointed out that I am missing FILE_SHARE_READ adding this makes the code:
HANDLE windowsFileHdl = CreateFileA(filePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (windowsFileHdl == INVALID_HANDLE_VALUE)
{
return false;
}
HANDLE fileMappedHdl = CreateFileMappingA(windowsFileHdl, 0, PAGE_READONLY, 0, 0, nullptr);
char* baseFileData = (char*)MapViewOfFile(fileMapedHdl, FILE_MAP_READ, 0, 0, 0);
Now, notepad is able to open and write to the file!
However other programs like Visual Studio, Notepad++, or Emacs still have errors when writing to the file.
Visual Studio error:
The process cannot access the file because it is being used by another process.
Notepad++ error:
The file cannot be saved and it may be protected. Do you want to launch Notepad++ in Administrator mode?
How can I read a file from 100th byte, using ReadFile function and C++
I used this code but it reads the first 100 bytes of the file
I want to read the second 100 bytes
hndl = CreateFileW(L"1.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
SetFilePointer(hndl, 100, NULL, FILE_BEGIN);
ReadFile(hndl, pbytReadBuffer, 100, NULL, &ol);
The ReadFile API offers two distinct ways to set the starting offset when doing synchronous I/O:
By using the implicitly stored file pointer (which can be manipulated through the SetFilePointer API call).
Or by passing an explicit offset through the OVERLAPPED structure.
Your code fails because you are setting the implicitly stored file pointer, but then (presumably) pass a zero-initialized OVERLAPPED structure, which ignores the file pointer (see Synchronization and File Position for more information).
Either of the following solutions will work. First, by using the implicitly stored file pointer. This is useful, when you want to read chunks of a file in consecutive calls:
hndl = CreateFileW(L"1.txt", GENERIC_READ, 0, nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
// Move the file pointer to offset 100
SetFilePointer(hndl, 100, NULL, FILE_BEGIN);
// Read contents from the current offset
DWORD dwBytesRead{0};
ReadFile(hndl, pbytReadBuffer, 100, &dwBytesRead, nullptr);
Alternatively, you can pass an OVERLAPPED structure to pass the offset. This ignores the implicitly stored file pointer. It is slightly more efficient, because it doesn't need two calls to the file I/O API.
hndl = CreateFileW(L"1.txt", GENERIC_READ, 0, nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
OVERLAPPED ol{};
// Set the offset from the start of the file
ol.Offset = 100;
ReadFile(hndl, pbytReadBuffer, 100, nullptr, &ol);
Note that error handling is elided in these samples for brevity. In real code, you always must check for errors.
you need code like this
HANDLE hFile = CreateFile(L"c:\\windows\\notepad.exe", FILE_GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
OVERLAPPED ov = {};
ov.Offset = 100;
UCHAR buf[100];
ULONG cb;
if (ReadFile(hFile, buf, sizeof(buf), &cb, &ov))
{
}
else
{
GetLastError();
}
CloseHandle(hFile);
}
you not need call SetFilePointer - much better set offset direct in OVERLAPPED structure
EDIT
for every read/write operation we need to specify the starting byte offset. we can do this directed by setting values in Offset and OffsetHigh from OVERLAPPED.
or indirect only if file opened if synchronous mode - I/O Manager can use the current file position in FILE_OBJECT - so we can not direct set offset - it will be get from FILE_OBJECT.CurrentByteOffset. FILE_OBJECT.CurrentByteOffset we can set with SetFilePointer also every read/write operation update this offset - move forward to count of bytes which readed/ written. of course this is correct only if file used in synchronous mode when all operations with file is sequential
if we use direct offset in OVERLAPPED he and used - so FILE_OBJECT.CurrentByteOffset is ignored - this mean that previous call to SetFilePointer - also lost all effect - will be used offset from OVERLAPPED and after read operation FILE_OBJECT.CurrentByteOffset will be updated based on offset + bytes readed
I'm writing a driver that needs to allocate a Non Paged pool of memory and this memory, for performance sake, must be directly accessible from a usermode program.
In the driver entry I've allocated some memory with these two type of methods:
pMdl = IoAllocateMdl(NULL,
4096,
FALSE,
FALSE,
NULL);
if(!pMdl) {
DbgPrintEx(DPFLTR_IHVVIDEO_ID, DPFLTR_INFO_LEVEL, "Error on IoAllocateMdl. Returning from driver early.\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
MmBuildMdlForNonPagedPool(pMdl);
userMemory = (void *)MmMapLockedPagesSpecifyCache(pMdl, UserMode, MmWriteCombined, NULL, FALSE, LowPagePriority);
and
userMemory = ExAllocatePoolWithTag(
NonPagedPool,
4096,
POOL_TAG);
Now I don't want to issue a DeviceIoControl every time I need to write/read from this memory, but instead I want to do something like this:
char* sharedMem;
.....
transactionResult = DeviceIoControl ( hDevice,
(DWORD) IOCTL_MMAP,
NULL,
0,
sharedMem,
sizeof(int),
&bRetur,
NULL
);
.....
sharedMem[0]='c';
Using a DeviceIoControl to get the address in kernel memory and then using it directly, like it were an mmap under Linux.
Is there some kind of way to do this in Windows?
I've done this:
hMapFile = OpenFileMapping(
FILE_MAP_ALL_ACCESS, // Read/write access
TRUE,
"Global\\SharedMemory"); // Name of mapping object
lastError = GetLastError();
if (hMapFile == NULL)
{
printf("Could not create file mapping object (%d).\n" ,GetLastError());
return 1;
}
pBuf = (char*)MapViewOfFile(hMapFile, // Handle to map object
FILE_MAP_ALL_ACCESS, // Read/write permission
0,
0,
4096);
if (pBuf == NULL)
{
printf("Could not map view of file (%d).\n", GetLastError());
CloseHandle(hMapFile);
return 1;
}
pBuf[0] = 'c';
pBuf[1] = '\n';
CloseHandle(hMapFile);
And I've created the view in Kernel like this:
RtlInitUnicodeString(&name, L"\\BaseNamedObjects\\SharedMemory");
InitializeObjectAttributes(&oa, &name, 0, 0, NULL);
ZwCreateSection(&hsection, SECTION_ALL_ACCESS, &oa, &Li, PAGE_READWRITE, SEC_COMMIT, NULL);
ZwMapViewOfSection(hsection, NtCurrentProcess(),
&userMem, 0, MEM_WIDTH, NULL,
&j, ViewShare, 0, PAGE_READWRITE);
But in the kernel when I read the memory it's empty: how can it be?
I finally understood how this needs to work.
First I've created a structure like the following.
typedef struct _MEMORY_ENTRY
{
PVOID pBuffer;
} MEMORY_ENTRY, *PMEMORY_ENTRY;
This will be used to return the virtual address from the kernel space to the user space.
In the DriverEntry I used
userMem = ExAllocatePoolWithTag(NonPagedPool,
MEM_WIDTH,
POOL_TAG );
to set up the NonPaged memory.
Then I've created an IOCTL working in DIRECT_OUT mode that does the following snippet:
...
PMDL mdl = NULL;
PVOID buffer = NULL;
MEMORY_ENTRY returnedValue;
void* UserVirtualAddress = NULL;
...
buffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); // Gets safely the pointer for the output in the IRP
mdl = IoAllocateMdl(userMem, MEM_WIDTH, FALSE, FALSE, NULL); // Allocate the memory descriptor list
MmBuildMdlForNonPagedPool(mdl); // This is needed when we're managing NonPaged memory
UserVirtualAddress = MmMapLockedPagesSpecifyCache(
mdl,
UserMode,
MmNonCached,
NULL,
FALSE,
NormalPagePriority); // Return the virtual address in the context of
// the user space program who called the IOCTL
returnedValue.pBuffer = UserVirtualAddress;
RtlCopyMemory(buffer,
&returnedValue,
sizeof(PVOID)); // I copy the virtual address in the structure that will
// be returned to the user mode program by the IRP
In the user mode program I just needed to to this
transactionResult = DeviceIoControl(
hDevice,
(DWORD) IOCTL_MMAP,
NULL,
0,
sharedMem,
sizeof(void*),
&bRetur,
NULL
);
In (MEMORY_ENTRY*)sharedMem->pBuffer we will find the memory area created and shared by the kernel space directly accessible by the kernel and by the user program.
I haven't wrote it but we need to remember to wrap the entire MmGetSystemAddressForMdlSafe(...)----->RtlCopyMemory(...) in a Try...Except block because we can encounter various problems here that may eventually cause a BugCheck so better be safe than sorry.
Anyway, if you're compiling this kind of code in a checked environment the Microsoft AutocodeReview will be pointing this out.
If someone needs more clarifications, or if I wrote something wrong just let me know and I will be happy to modify this post.
I'm trying to save some HBITMAPs into an array, and display them at a later time. Creating a HBITMAP from a DC works, but when I try to display a saved HBITMAP I seem to get the wrong one. This leads me to think that I haven't really understood how HTBIMAPs are stored in memory. I was assuming that the data type HBITMAP is essentially a pointer to the object in memory and that that's all I need, but maybe there is more to it?
Here's a sample code:
HBITMAP aBitMaps[NUM_BITMAPS];
int iNumBitMaps;
void SaveScreen(CDC *dc)
{
if (iNumBitMaps>0)
{
if (iNumBitMaps>=NUM_BITMAPS)
{
iNumBitMaps=NUM_BITMAPS-1;
DeleteObject(aBitMaps[iNumBitMaps]);
}
for (int i=iNumBitMaps;i>0;i--)
{
aBitMaps[i] = aBitMaps[i-1];
}
}
iNumBitMaps++;
aBitMaps[0] = CreateCompatibleBitmap(dc->m_hDC, 800, 800);
HDC hdcMem = CreateCompatibleDC(dc->m_hDC);
SelectObject(hdcMem, aBitMaps[0]);
BitBlt(hdcMem, 0, 0, 800, 800, dc->m_hDC, 0, 0, SRCCOPY);
ReleaseDC(hdcMem);
}
void RestoreScreen(CDC *dc, int i)
{
if (i>=NUM_BITMAPS) i = NUM_BITMAPS-1;
if (i<0) i = 0;
HDC hdcMem = CreateCompatibleDC(dc->m_hDC);
SelectObject(hdcMem, aBitMaps[i]);
BitBlt(dc->m_hDC, 0, 0, 800, 800, hdcMem, 0, 0, SRCCOPY);
ReleaseDC(hdcMem);
}
So, the idea is essentially to push the HBITMAP pointers to an array, and at a later time display any of the stored images by selecting it into a memory DC and then copying it to the actual DC. What's wrong with this?
I'm using VC++, Visual Studio 2010, no MFC.
EDIT:
I did some more tests. For debugging, I tried to display all stored bitmaps (shifted in position) by adding the following for loop to SaveScreen
...
BitBlt(hdcMem, 0, 0, 800, 800, dc->m_hDC, 0, 0, SRCCOPY);
for (int j=0; j<iNumBitMaps;j++)
{
SelectObject(hdcMem, aBitMaps[j]);
BitBlt(dc->m_hDC, 20*(j+1), 100*(j+1), 800+20*(j+1), 800+100*(j+1), hdcMem, 0, 0, SRCCOPY);
}
ReleaseDC(hdcMem);
This then does display iNumBitMaps times the image, but it's always the same image (namely the one I stored in aBitMaps[0]). I did verify in the debugger, that aBitMaps contains all different pointers. So, somehow I think I don't correctly load the bitmaps into the memory DC.
I figured it out. In my code above there were two mistakes (of which I'm aware of). First, the memory DC must be deleted after use, so ReleaseDC must be replaced by DeleteDC.
Second, one must know that an object that is selected into a DC cannot be selected into another DC. Hence it is important that after operating on an object in the memory DC, the object must be unloaded by selecting the DC's previous object. The correct contruction is thus essentially
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, aBitMaps[0]);
BitBlt(...);
HBITMAP hbmNew = (HBITMAP)SelectObject(hdcMem, hbmOld);
where now hbmNew points to the same object as aBitMaps[0].