用JNA检测HICON的大小
Detect size of HICON with JNA
我的最终目标是借助 JNA 库在 Java 中获得一个 HWND 的图标。除了一件重要的事情外,一切都很好:我需要图标的大小才能在 Java.
中进行进一步的处理步骤
我好像不能要求尺寸。我总是获得大小 0x0。我究竟做错了什么?基本代码示例如下所示。大多数 API 函数模板不是 JNA 的一部分。所以,我不得不自己定义它们。
final long hicon = ExtUser32.INSTANCE.SendMessageA(hwnd, ExtUser32.WM_GETICON, ExtUser32.ICON_BIG, 0);
final Pointer hIcon = new Pointer(hicon);
final ICONINFO info = new ICONINFO();
final BITMAP bmp = new BITMAP();
final SIZE size = new SIZE();
System.out.println(ExtUser32.INSTANCE.GetIconInfo(hIcon, info));
System.out.println(info);
System.out.println(ExtGdi32.INSTANCE.GetBitmapDimensionEx(info.hbmColor, size));
System.out.println(size);
if (info.hbmColor != null)
{
final int nWrittenBytes = ExtGdi32.INSTANCE.GetObjectA(info.hbmColor, bmp.size(), bmp.getPointer());
System.out.println(nWrittenBytes);
System.out.println(bmp);
}
系统输出打印如下:
true
ICONINFO(auto-allocated@0x5b72b4f0 (32 bytes)) {
WinDef$BOOL fIcon@0=1
WinDef$DWORD xHotspot@4=16
WinDef$DWORD yHotspot@8=16
WinDef$HBITMAP hbmMask@10=native@0xffffffffb00515e8 (com.sun.jna.platform.win32.WinDef$HBITMAP@b00515e7)
WinDef$HBITMAP hbmColor@18=native@0xffffffffa50515c8 (com.sun.jna.platform.win32.WinDef$HBITMAP@a50515c7)
}
true
WinUser$SIZE(auto-allocated@0x652a3000 (8 bytes)) {
int cx@0=0
int cy@4=0
}
32
BITMAP(auto-allocated@0x5b72b5b0 (32 bytes)) {
WinDef$LONG bmType@0=0
WinDef$LONG bmWidth@4=0
WinDef$LONG bmHeight@8=0
WinDef$LONG bmWidthBytes@c=0
WinDef$WORD bmPlanes@10=0
WinDef$WORD bmBitsPixel@12=0
WinDef$LPVOID bmBits@18=0
}
ICONINFO 结构的请求似乎是正确的。但是,如果我尝试通过 Gdi32.GetBitmapDimensionEx()
请求集合 hbmColor
结构组件的维度,则该结构会保持用零初始化。这种通过 hbmColor
或 hbmMask
的方法是由以下人员建议的:
How to determine the size of an icon from a HICON?
更新 1
添加了错误跟踪!
如系统输出所示 (true
),相关函数调用没有失败。
更新 2
进一步观察:在Java中,这些重新创建的结构类型在实例化后用零初始化。我将 SIZE 和 BITMAP 中的结构组件的初始值设置为偏离零的值。 GetBitmapDimensionEx
将其设置回零。但是 GetObjectA
不修改结构!函数的 return 结果表明已写入字节,但事实并非如此!
...
size.cx = 1;
size.cy = 2;
bmp.bmType.setValue(1);
bmp.bmWidth.setValue(2);
bmp.bmHeight.setValue(3);
bmp.bmWidthBytes.setValue(4);
bmp.bmPlanes.setValue(5);
bmp.bmBitsPixel.setValue(6);
bmp.bmBits.setValue(7);
System.out.println(ExtGdi32.INSTANCE.GetBitmapDimensionEx(info.hbmColor, size));
System.out.println(size);
if (info.hbmColor != null)
{
final int nWrittenBytes = ExtGdi32.INSTANCE.GetObjectA(info.hbmColor, bmp.size(), bmp.getPointer());
System.out.println(nWrittenBytes);
System.out.println(bmp);
}
结果:
true
WinUser$SIZE(auto-allocated@0x64fbcb20 (8 bytes)) {
int cx@0=0
int cy@4=0
}
32
BITMAP(auto-allocated@0x64fb91f0 (32 bytes)) {
WinDef$LONG bmType@0=1
WinDef$LONG bmWidth@4=2
WinDef$LONG bmHeight@8=3
WinDef$LONG bmWidthBytes@c=4
WinDef$WORD bmPlanes@10=5
WinDef$WORD bmBitsPixel@12=6
WinDef$LPVOID bmBits@18=7
}
如果这只是一个 32 位应用程序:
您的 BITMAP
结构不正确。这是一个简单的 C 程序,它打印 BITMAP
的所有字段的预期偏移量及其总大小:
// 24 january 2015
#include <windows.h>
#include <stdio.h>
#include <stddef.h>
int main(void)
{
printf("%d\n", sizeof (BITMAP));
#define O(f) printf("%s %x\n", #f, offsetof(BITMAP, f))
O(bmType);
O(bmWidth);
O(bmHeight);
O(bmWidthBytes);
O(bmPlanes);
O(bmBitsPixel);
O(bmBits);
return 0;
}
这是我得到的(在 wine 中,使用 MinGW-w64 编译为 32 位程序):
24
bmType 0
bmWidth 4
bmHeight 8
bmWidthBytes c
bmPlanes 10
bmBitsPixel 12
bmBits 14
请注意,上面的 Java 输出对于 BITMAP
具有不同的大小,对于 bmBits
具有不同的偏移量。
(BITMAP
结构对于 64 位应用程序是正确的。)
一般:
为什么 GetObject()
表示成功超出了我的范围。
我完全不了解 JNA,所以我不知道该怎么办。 JNA不提供WinGDI$BITMAP
吗?
我本来可以将此添加为评论,但我的声誉太低了:
您没有显示您的 BITMAP 或 GetObjectA 定义,所以我猜是
在你的行中:
final int nWrittenBytes = ExtGdi32.INSTANCE.GetObjectA(info.hbmColor, bmp.size(), bmp.getPointer());
您之后未能调用 'bmp.read()'。
如果您查看 javadoc for Struture.getPointer()
https://jna.java.net/javadoc/com/sun/jna/Structure.html
您看到您负责在调用本机方法之前和之后调用 Structure.write() 和 Structure.read(),该方法使用通过 getPointer() 获得的指针。在你的情况下,写入是多余的,但这是一个很好的做法。
要理解为什么这是必要的,请考虑您的 BITMAP/bmp 对象是一个生活在 Java 堆中的 Java 对象,它可以在垃圾收集期间移动。因此 getPointer() 不能 return 'real' 对象的真实地址。相反,它 return 是指向本机堆中一个单独的固定(不可移动)内存块的指针(JNA 分配该内存块并与您的 Java 对象关联。现在您的 getObjectA() 例程将写入它的内容that 内存但是 JNA 或 java 方的任何人都不知道发生了什么。所以你需要调用 read() 告诉 JNA 复制Java 对象的原生内容。
我的最终目标是借助 JNA 库在 Java 中获得一个 HWND 的图标。除了一件重要的事情外,一切都很好:我需要图标的大小才能在 Java.
中进行进一步的处理步骤我好像不能要求尺寸。我总是获得大小 0x0。我究竟做错了什么?基本代码示例如下所示。大多数 API 函数模板不是 JNA 的一部分。所以,我不得不自己定义它们。
final long hicon = ExtUser32.INSTANCE.SendMessageA(hwnd, ExtUser32.WM_GETICON, ExtUser32.ICON_BIG, 0);
final Pointer hIcon = new Pointer(hicon);
final ICONINFO info = new ICONINFO();
final BITMAP bmp = new BITMAP();
final SIZE size = new SIZE();
System.out.println(ExtUser32.INSTANCE.GetIconInfo(hIcon, info));
System.out.println(info);
System.out.println(ExtGdi32.INSTANCE.GetBitmapDimensionEx(info.hbmColor, size));
System.out.println(size);
if (info.hbmColor != null)
{
final int nWrittenBytes = ExtGdi32.INSTANCE.GetObjectA(info.hbmColor, bmp.size(), bmp.getPointer());
System.out.println(nWrittenBytes);
System.out.println(bmp);
}
系统输出打印如下:
true
ICONINFO(auto-allocated@0x5b72b4f0 (32 bytes)) {
WinDef$BOOL fIcon@0=1
WinDef$DWORD xHotspot@4=16
WinDef$DWORD yHotspot@8=16
WinDef$HBITMAP hbmMask@10=native@0xffffffffb00515e8 (com.sun.jna.platform.win32.WinDef$HBITMAP@b00515e7)
WinDef$HBITMAP hbmColor@18=native@0xffffffffa50515c8 (com.sun.jna.platform.win32.WinDef$HBITMAP@a50515c7)
}
true
WinUser$SIZE(auto-allocated@0x652a3000 (8 bytes)) {
int cx@0=0
int cy@4=0
}
32
BITMAP(auto-allocated@0x5b72b5b0 (32 bytes)) {
WinDef$LONG bmType@0=0
WinDef$LONG bmWidth@4=0
WinDef$LONG bmHeight@8=0
WinDef$LONG bmWidthBytes@c=0
WinDef$WORD bmPlanes@10=0
WinDef$WORD bmBitsPixel@12=0
WinDef$LPVOID bmBits@18=0
}
ICONINFO 结构的请求似乎是正确的。但是,如果我尝试通过 Gdi32.GetBitmapDimensionEx()
请求集合 hbmColor
结构组件的维度,则该结构会保持用零初始化。这种通过 hbmColor
或 hbmMask
的方法是由以下人员建议的:
How to determine the size of an icon from a HICON?
更新 1
添加了错误跟踪!
如系统输出所示 (true
),相关函数调用没有失败。
更新 2
进一步观察:在Java中,这些重新创建的结构类型在实例化后用零初始化。我将 SIZE 和 BITMAP 中的结构组件的初始值设置为偏离零的值。 GetBitmapDimensionEx
将其设置回零。但是 GetObjectA
不修改结构!函数的 return 结果表明已写入字节,但事实并非如此!
...
size.cx = 1;
size.cy = 2;
bmp.bmType.setValue(1);
bmp.bmWidth.setValue(2);
bmp.bmHeight.setValue(3);
bmp.bmWidthBytes.setValue(4);
bmp.bmPlanes.setValue(5);
bmp.bmBitsPixel.setValue(6);
bmp.bmBits.setValue(7);
System.out.println(ExtGdi32.INSTANCE.GetBitmapDimensionEx(info.hbmColor, size));
System.out.println(size);
if (info.hbmColor != null)
{
final int nWrittenBytes = ExtGdi32.INSTANCE.GetObjectA(info.hbmColor, bmp.size(), bmp.getPointer());
System.out.println(nWrittenBytes);
System.out.println(bmp);
}
结果:
true
WinUser$SIZE(auto-allocated@0x64fbcb20 (8 bytes)) {
int cx@0=0
int cy@4=0
}
32
BITMAP(auto-allocated@0x64fb91f0 (32 bytes)) {
WinDef$LONG bmType@0=1
WinDef$LONG bmWidth@4=2
WinDef$LONG bmHeight@8=3
WinDef$LONG bmWidthBytes@c=4
WinDef$WORD bmPlanes@10=5
WinDef$WORD bmBitsPixel@12=6
WinDef$LPVOID bmBits@18=7
}
如果这只是一个 32 位应用程序:
您的 BITMAP
结构不正确。这是一个简单的 C 程序,它打印 BITMAP
的所有字段的预期偏移量及其总大小:
// 24 january 2015
#include <windows.h>
#include <stdio.h>
#include <stddef.h>
int main(void)
{
printf("%d\n", sizeof (BITMAP));
#define O(f) printf("%s %x\n", #f, offsetof(BITMAP, f))
O(bmType);
O(bmWidth);
O(bmHeight);
O(bmWidthBytes);
O(bmPlanes);
O(bmBitsPixel);
O(bmBits);
return 0;
}
这是我得到的(在 wine 中,使用 MinGW-w64 编译为 32 位程序):
24
bmType 0
bmWidth 4
bmHeight 8
bmWidthBytes c
bmPlanes 10
bmBitsPixel 12
bmBits 14
请注意,上面的 Java 输出对于 BITMAP
具有不同的大小,对于 bmBits
具有不同的偏移量。
(BITMAP
结构对于 64 位应用程序是正确的。)
一般:
为什么 GetObject()
表示成功超出了我的范围。
我完全不了解 JNA,所以我不知道该怎么办。 JNA不提供WinGDI$BITMAP
吗?
我本来可以将此添加为评论,但我的声誉太低了:
您没有显示您的 BITMAP 或 GetObjectA 定义,所以我猜是 在你的行中:
final int nWrittenBytes = ExtGdi32.INSTANCE.GetObjectA(info.hbmColor, bmp.size(), bmp.getPointer());
您之后未能调用 'bmp.read()'。
如果您查看 javadoc for Struture.getPointer()
https://jna.java.net/javadoc/com/sun/jna/Structure.html
您看到您负责在调用本机方法之前和之后调用 Structure.write() 和 Structure.read(),该方法使用通过 getPointer() 获得的指针。在你的情况下,写入是多余的,但这是一个很好的做法。
要理解为什么这是必要的,请考虑您的 BITMAP/bmp 对象是一个生活在 Java 堆中的 Java 对象,它可以在垃圾收集期间移动。因此 getPointer() 不能 return 'real' 对象的真实地址。相反,它 return 是指向本机堆中一个单独的固定(不可移动)内存块的指针(JNA 分配该内存块并与您的 Java 对象关联。现在您的 getObjectA() 例程将写入它的内容that 内存但是 JNA 或 java 方的任何人都不知道发生了什么。所以你需要调用 read() 告诉 JNA 复制Java 对象的原生内容。