阅读在Windows中使用Java访问内存映射文件

huangapple go评论78阅读模式
英文:

Reading memory mapped file in Windows using Java

问题

以下是翻译好的内容:

C++部分:

#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,    // 使用分页文件
        NULL,                    // 默认安全性
        PAGE_READWRITE,          // 读/写访问权限
        0,                       // 对象的最大大小(高位DWORD)
        BUF_SIZE,                // 对象的最大大小(低位DWORD)
        szName);                 // 映射对象的名称

    if (hMapFile == NULL)
    {
        _tprintf(TEXT("无法创建文件映射对象(%d)。\n"),
            GetLastError());
        return 1;
    }
    pBuf = (LPTSTR)MapViewOfFile(hMapFile,   // 映射对象的句柄
        FILE_MAP_ALL_ACCESS, // 读/写权限
        0,
        0,
        BUF_SIZE);

    if (pBuf == NULL)
    {
        _tprintf(TEXT("无法映射文件的视图(%d)。\n"),
            GetLastError());

        CloseHandle(hMapFile);

        return 1;
    }

    CopyMemory((PVOID)pBuf, szMsg, (_tcslen(szMsg) * sizeof(TCHAR)));
    _getch();

    UnmapViewOfFile(pBuf);

    CloseHandle(hMapFile);

    return 0;
}

Java部分:

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

编辑后的Java部分:

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));
    }
}
英文:

I used this example to build a program that is creating memory-mapped file containing string value "Message from first process."

#include &lt;windows.h&gt;
#include &lt;stdio.h&gt;
#include &lt;conio.h&gt;
#include &lt;tchar.h&gt;

#define BUF_SIZE 256
TCHAR szName[] = TEXT(&quot;Local\\MyFileMappingObject&quot;);
TCHAR szMsg[] = TEXT(&quot;Message from first process.&quot;);

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(&quot;Could not create file mapping object (%d).\n&quot;),
            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(&quot;Could not map view of file (%d).\n&quot;),
            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(&quot;kernel32&quot;, 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, &quot;Local\\MyFileMappingObject&quot;);
		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(&quot;kernel32&quot;, 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, &quot;Local\\MyFileMappingObject&quot;);
		view = Kernel32.INSTANCE.MapViewOfFile (h, 0x4, 0, 0, 256);
		System.out.println(view.getWideString(0));
	}
	
}

答案1

得分: 2

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

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

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

请注意,Windows 字符串是 UTF-16 编码,但使用 Pointer.getString() 将假定为 8 位 C 字符串。您应该使用 Pointer.getWideString()。为了避免读取超出内存边界,还应确保从中读取的 Pointer 处的缓冲区足够大,可以包含空终止符。

英文:

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.

huangapple
  • 本文由 发表于 2020年10月26日 07:11:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/64529747.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定