You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用JNA在Java中读取DLL回调函数的hBitMap指针数据?

如何读取JNA回调中hBitmap指针的图像数据?

我尝试从扫描硬件中获取图像数据,提供的DLL包含以下回调函数:

  • 回调函数注册接口
  • 回调函数结构定义

我的Java(基于JNA)实现如下:

1. 导入库

public interface DllReader extends Library {
    Boolean voRegisterImageCallback(ImageCallBack callback, PointerByReference parameter);
    DllReader INSTANCE = Native.load("VideoOCR.dll", DllReader.class);
}

2. 回调接口定义

public interface ImageCallBack extends StdCallLibrary.StdCallCallback {
    void imageCallBack(PointerByReference reference, int imageType, Pointer hBitMap);
}

3. 回调实现类

public class ImageCallBackImpl implements ImageCallBack{
    @Override
    public void imageCallBack(PointerByReference reference, int imageType, Pointer hBitMap) {
        System.out.println("image data");
        // 如何在事件触发时读取这里的数据?
    }
}

请问如何读取hBitMap指针中的图像数据?


读取hBitmap数据的具体方案

hBitMap是Windows GDI位图句柄,需通过JNA调用Windows系统API解析,步骤如下:

1. 映射Windows GDI32/USER32 API

先定义JNA接口对接系统API:

public interface GDI32 extends StdCallLibrary {
    GDI32 INSTANCE = Native.load("gdi32", GDI32.class);
    int GetObjectW(Pointer hBitmap, int cbBuffer, Pointer lpObject);
    Pointer CreateCompatibleDC(Pointer hdc);
    Pointer SelectObject(Pointer hdc, Pointer hBitmap);
    int GetDIBits(Pointer hdc, Pointer hBitmap, int uStartScan, int cScanLines, Pointer lpvBits, Pointer lpbi, int uUsage);
    boolean DeleteDC(Pointer hdc);
}

public interface USER32 extends StdCallLibrary {
    USER32 INSTANCE = Native.load("user32", USER32.class);
    Pointer GetDC(Pointer hwnd);
    int ReleaseDC(Pointer hwnd, Pointer hdc);
}

2. 在回调中解析位图数据

修改回调实现,加入解析逻辑:

@Override
public void imageCallBack(PointerByReference reference, int imageType, Pointer hBitMap) {
    System.out.println("image data");

    // 1. 获取位图基础信息
    BITMAP bmp = new BITMAP();
    GDI32.INSTANCE.GetObjectW(hBitMap, bmp.size(), bmp.getPointer());
    bmp.read();

    int width = bmp.bmWidth;
    int height = bmp.bmHeight;
    int bitsPerPixel = bmp.bmBitsPixel;
    int byteCount = width * height * (bitsPerPixel / 8);

    // 2. 创建兼容DC并绑定位图
    Pointer desktopDC = USER32.INSTANCE.GetDC(null);
    Pointer compatibleDC = GDI32.INSTANCE.CreateCompatibleDC(desktopDC);
    GDI32.INSTANCE.SelectObject(compatibleDC, hBitMap);

    // 3. 准备像素缓冲区和位图信息结构
    Pointer pixelBuffer = new Memory(byteCount);
    BITMAPINFO bmi = new BITMAPINFO();
    bmi.bmiHeader.biSize = BITMAPINFOHEADER.SIZE;
    bmi.bmiHeader.biWidth = width;
    bmi.bmiHeader.biHeight = -height; // 负数值表示像素从上到下排列
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = (short) bitsPerPixel;
    bmi.bmiHeader.biCompression = 0; // BI_RGB无压缩格式

    // 4. 读取像素数据
    int result = GDI32.INSTANCE.GetDIBits(compatibleDC, hBitMap, 0, height, pixelBuffer, bmi.getPointer(), 0);
    if (result > 0) {
        // 转换为Java BufferedImage方便后续处理
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        int[] pixels = new int[width * height];
        pixelBuffer.read(0, pixels, 0, pixels.length);
        image.setRGB(0, 0, width, height, pixels, 0, width);
        
        // 这里可以添加保存图像、处理图像的逻辑
    }

    // 5. 释放GDI资源,避免内存泄漏
    USER32.INSTANCE.ReleaseDC(null, desktopDC);
    GDI32.INSTANCE.DeleteDC(compatibleDC);
}

3. 定义对应Windows的结构体

需实现JNA结构体映射Windows的BITMAP、BITMAPINFO等结构:

public static class BITMAP extends Structure {
    public int bmType;
    public int bmWidth;
    public int bmHeight;
    public int bmWidthBytes;
    public short bmPlanes;
    public short bmBitsPixel;
    public Pointer bmBits;

    @Override
    protected List<String> getFieldOrder() {
        return Arrays.asList("bmType", "bmWidth", "bmHeight", "bmWidthBytes", "bmPlanes", "bmBitsPixel", "bmBits");
    }
}

public static class BITMAPINFO extends Structure {
    public BITMAPINFOHEADER bmiHeader;

    @Override
    protected List<String> getFieldOrder() {
        return Arrays.asList("bmiHeader");
    }
}

public static class BITMAPINFOHEADER extends Structure {
    public static final int SIZE = 40;
    public int biSize;
    public int biWidth;
    public int biHeight;
    public short biPlanes;
    public short biBitCount;
    public int biCompression;
    public int biSizeImage;
    public int biXPelsPerMeter;
    public int biYPelsPerMeter;
    public int biClrUsed;
    public int biClrImportant;

    @Override
    protected List<String> getFieldOrder() {
        return Arrays.asList("biSize", "biWidth", "biHeight", "biPlanes", "biBitCount", "biCompression", "biSizeImage", "biXPelsPerMeter", "biYPelsPerMeter", "biClrUsed", "biClrImportant");
    }
}

注意事项

  • 必须释放所有GDI资源(DC、句柄等),防止内存泄漏
  • 根据实际返回的bitsPerPixel调整BufferedImage的类型(比如24位对应TYPE_3BYTE_BGR,32位对应TYPE_INT_ARGB)
  • 若DLL返回的是设备无关位图(DIB),可跳过CreateCompatibleDC步骤,直接调用GetDIBBits

内容的提问来源于stack exchange,提问作者Dipendra Nath

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 09:25:40