使用 Java 读取 Windows 中的内存映射文件

Reading memory mapped file in Windows using Java

我使用此示例构建了一个程序,该程序正在创建包含字符串值“来自第一个进程的消息”的内存映射文件。

#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;
}

我的 Java 应用程序在读取值时遇到问题。当上面的演示应用程序是 运行.

时,String data = view.getString(0); 给我“java.lang.Error:无效的内存访问”

我知道函数成功是因为失败(如果应用程序不是 运行)导致 NullPointerException,因为 this.hnull。所以指针是正确的,但是我在尝试获取值时做错了什么?

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);
    }
}

编辑。下面的完整工作代码。谢谢丹尼尔和德雷克。

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));
    }
    
}

问题是您将 returned HANDLE 视为指向数据的指针。但它不是一个普通的指针;它仅对其他文件映射 API 函数有意义。

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() 中的示例指定了起始偏移量和要映射的字节数,以及共享内存的权限。

该函数将 return 指向您随后可以操作的共享内存的实际指针(例如,检索字符串)。

请注意,Windows 字符串采用 UTF-16,但使用 Pointer.getString() 将假定为 8 位 C 字符串。你应该使用 Pointer.getWideString()。为避免读取超出内存范围,您还应确保正在读取的 Pointer 处的缓冲区足够大以包含空终止符。