I used this example to build a program that is creating memory-mapped file containing string value "Message from first process."
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#define BUF_SIZE 256
TCHAR szName[] = TEXT("Local\\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
if (hMapFile == NULL)
{
_tprintf(TEXT("Could not create file mapping object (%d).\n"),
GetLastError());
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);
return 1;
}
CopyMemory((PVOID)pBuf, szMsg, (_tcslen(szMsg) * sizeof(TCHAR)));
_getch();
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
return 0;
}
I have problem in my Java application reading the value. String data = view.getString(0);
gives me "java.lang.Error: Invalid memory access" when above demo application is running.
I know the function succeeds because failure (if the application is not running) results in a NullPointerException
because this.h
is null
. So the Pointer is correct, but what am I doing wrong when trying to get the value?
package ACC;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.win32.W32APIOptions;
public class ACCSharedMemory{
private final MyKernel32 myKernel32;
private HANDLE h;
private Pointer view;
public interface MyKernel32 extends Kernel32 {
MyKernel32 INSTANCE = (MyKernel32)Native.load("kernel32", MyKernel32.class, W32APIOptions.DEFAULT_OPTIONS);
HANDLE OpenFileMapping(int dwDesiredAccess, boolean bInheritHandle, String lpName);
}
public ACCSharedMemory() {
myKernel32 = MyKernel32.INSTANCE;
}
public void test() {
h = myKernel32.OpenFileMapping(983071, true, "Local\\MyFileMappingObject");
view = h.getPointer();
String data = view.getString(0);
}
}
EDIT. Full working code below. Thanks Daniel & Drake.
public class ACCSharedMemory{
private final MyKernel32 myKernel32;
private HANDLE h;
private Pointer view;
public interface MyKernel32 extends Kernel32 {
MyKernel32 INSTANCE = (MyKernel32)Native.load("kernel32", MyKernel32.class, W32APIOptions.DEFAULT_OPTIONS);
HANDLE OpenFileMapping(int dwDesiredAccess, boolean bInheritHandle, String lpName);
}
public ACCSharedMemory() {
myKernel32 = MyKernel32.INSTANCE;
}
public void test() {
h = myKernel32.OpenFileMapping(0x4, true, "Local\\MyFileMappingObject");
view = Kernel32.INSTANCE.MapViewOfFile (h, 0x4, 0, 0, 256);
System.out.println(view.getWideString(0));
}
}
The problem is that you are treating the returned HANDLE
as a pointer to the data. But it is not a plain pointer; it only has meaning to other File Mapping API functions.
The example at Creating Named Shared Memory shows how to use this returned handle in the second process. Note the native code there to deal with the retrieved handle value is to pass it to MapViewOfFile()
specifying the starting offset and number of bytes to map, as well as permissions for the shared memory.
That function will return the actual pointer to the shared memory that you can then manipulate (e.g., retrieve the string).
Note that Windows strings are in UTF-16, but using Pointer.getString()
will assume an 8-bit C string. You should use Pointer.getWideString()
. To avoid reading beyond memory bounds, you should also ensure that the buffer at the Pointer
you are reading from is large enough to include the null terminator.