首页 > 解决方案 > 使用Java在Windows中读取内存映射文件

问题描述

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

#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:无效的内存访问”。

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

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

标签: javawinapishared-memoryjna

解决方案


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

创建命名共享内存中的示例显示了如何在第二个进程中使用此返回的句柄。请注意,处理检索到的句柄值的本机代码是将其传递给MapViewOfFile()指定起始偏移量和要映射的字节数,以及共享内存的权限。

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

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


推荐阅读