Packet.dll 获取 mac 地址 (JNR-FFI)
Packet.dll get mac address (JNR-FFI)
如何使用 jnr-ffi 将下面的函数映射到 java?
BOOLEAN PacketRequest(LPADAPTER AdapterObject,BOOLEAN Set,PPACKET_OID_DATA OidData);
public interface NativeMappings {
public static class PPACKET_OID_DATA extends Struct {
public final UnsignedLong Oid = new UnsignedLong();
public final UnsignedLong Length = new UnsignedLong();
public final byte[] Data = new byte[6];
public PPACKET_OID_DATA(Runtime runtime) {
super(runtime);
}
}
Pointer PacketOpenAdapter(String AdapterName);
int PacketRequest(Pointer AdapterObject, int set, @Out PPACKET_OID_DATA OidData);
void PacketCloseAdapter(Pointer lpAdapter);
public static class Main {
public static void main(String[] args) {
NativeMappings mappings = LibraryLoader.create(NativeMappings.class).load("Packet");
Runtime runtime = Runtime.getRuntime(mappings);
Pointer adapterObject = mappings.PacketOpenAdapter("\Device\NPF_{53152A2F-39F7-458E-BD58-24D17099256A}");
PPACKET_OID_DATA oid_data = new PPACKET_OID_DATA(runtime);
oid_data.Oid.set(0x01010102L);
oid_data.Length.set(6L);
int status = mappings.PacketRequest(adapterObject, 0, oid_data);
if (status == 0) {
System.out.println("Fail.");
} else {
System.out.println("Success.");
}
mappings.PacketCloseAdapter(adapterObject);
}
}
}
首先要进行正确的映射,您应该查看要映射的类型的定义。 PacketRequest
函数 returns BOOLEAN
变量。根据 windows data type description,BOOLEAN
声明为 typedef BYTE BOOLEAN;
。这意味着,您可以使用 byte
类型作为 java 中的函数类型。但是JNR也支持映射boolean
类型to/from原生byte
。所以两个定义都是正确的:
byte PacketRequest (...)
boolean PacketRequest (...)
接下来,您需要映射参数。同样在这里,看看定义,你就会知道要使用什么类型。结果将是:
boolean PacketRequest(Pointer AdapterObject, boolean set, PPACKET_OID_DATA OidData);
set
参数被错误地声明为 int
。此外,最后一个字段的 @Out
注释告诉 JNR 传递一个空结构而不将值复制到本机内存。因此不会传递任何预设值。
最后一个参数有 PPACKET_OID_DATA
类型 - a structure.
struct _PACKET_OID_DATA {
ULONG Oid; ///< OID code. See the Microsoft DDK documentation or the file ntddndis.h
///< for a complete list of valid codes.
ULONG Length; ///< Length of the data field
UCHAR Data[1]; ///< variable-lenght field that contains the information passed to or received
///< from the adapter.
};
结构映射比本地类型复杂一些。您不能在此处使用 java 类型。相反,您应该使用 jnr.ffi.Struct
inner classes 来定义结构字段。此规则包括数组定义。您的结构的正确定义如下所示:
class PPACKET_OID_DATA extends Struct {
public final UnsignedLong Oid = new UnsignedLong();
public final UnsignedLong Length = new UnsignedLong();
public final Unsigned8[] Data = array(new Unsigned8[6]);
public PPACKET_OID_DATA(Runtime runtime) {
super(runtime);
}
}
注意这个 UCHAR
数组定义。这种类型在本地定义为 unsigned char
,因此对于 JNR 结构,它将映射到 jnr.ffi.Strunc.Unsigned8
class 或 jnr.ffi.Struct.BYTE
(几乎相同)。
要声明一个数组字段,您应该在构造时初始化数组。您需要使用 jnr.ffi.Struct#array(...)
函数才能正确执行此操作。这意味着你应该知道数组的大小。示例如上所示。
为什么要这样定义?
在初始化期间,Struct
是某种可变长度的指针。每个 inner-class 字段,在其中初始化,保留其自己的 space 增加此指针的最大大小。所以每个字段都是一个 "view" 到一些内存片段,有自己的方式与这个内存交互(public 方法)。但是要制作这样的视图数组,您需要用视图实例填充空数组。这正是 array
函数的作用。
如何使用 jnr-ffi 将下面的函数映射到 java?
BOOLEAN PacketRequest(LPADAPTER AdapterObject,BOOLEAN Set,PPACKET_OID_DATA OidData);
public interface NativeMappings {
public static class PPACKET_OID_DATA extends Struct {
public final UnsignedLong Oid = new UnsignedLong();
public final UnsignedLong Length = new UnsignedLong();
public final byte[] Data = new byte[6];
public PPACKET_OID_DATA(Runtime runtime) {
super(runtime);
}
}
Pointer PacketOpenAdapter(String AdapterName);
int PacketRequest(Pointer AdapterObject, int set, @Out PPACKET_OID_DATA OidData);
void PacketCloseAdapter(Pointer lpAdapter);
public static class Main {
public static void main(String[] args) {
NativeMappings mappings = LibraryLoader.create(NativeMappings.class).load("Packet");
Runtime runtime = Runtime.getRuntime(mappings);
Pointer adapterObject = mappings.PacketOpenAdapter("\Device\NPF_{53152A2F-39F7-458E-BD58-24D17099256A}");
PPACKET_OID_DATA oid_data = new PPACKET_OID_DATA(runtime);
oid_data.Oid.set(0x01010102L);
oid_data.Length.set(6L);
int status = mappings.PacketRequest(adapterObject, 0, oid_data);
if (status == 0) {
System.out.println("Fail.");
} else {
System.out.println("Success.");
}
mappings.PacketCloseAdapter(adapterObject);
}
}
}
首先要进行正确的映射,您应该查看要映射的类型的定义。 PacketRequest
函数 returns BOOLEAN
变量。根据 windows data type description,BOOLEAN
声明为 typedef BYTE BOOLEAN;
。这意味着,您可以使用 byte
类型作为 java 中的函数类型。但是JNR也支持映射boolean
类型to/from原生byte
。所以两个定义都是正确的:
byte PacketRequest (...)
boolean PacketRequest (...)
接下来,您需要映射参数。同样在这里,看看定义,你就会知道要使用什么类型。结果将是:
boolean PacketRequest(Pointer AdapterObject, boolean set, PPACKET_OID_DATA OidData);
set
参数被错误地声明为 int
。此外,最后一个字段的 @Out
注释告诉 JNR 传递一个空结构而不将值复制到本机内存。因此不会传递任何预设值。
最后一个参数有 PPACKET_OID_DATA
类型 - a structure.
struct _PACKET_OID_DATA {
ULONG Oid; ///< OID code. See the Microsoft DDK documentation or the file ntddndis.h
///< for a complete list of valid codes.
ULONG Length; ///< Length of the data field
UCHAR Data[1]; ///< variable-lenght field that contains the information passed to or received
///< from the adapter.
};
结构映射比本地类型复杂一些。您不能在此处使用 java 类型。相反,您应该使用 jnr.ffi.Struct
inner classes 来定义结构字段。此规则包括数组定义。您的结构的正确定义如下所示:
class PPACKET_OID_DATA extends Struct {
public final UnsignedLong Oid = new UnsignedLong();
public final UnsignedLong Length = new UnsignedLong();
public final Unsigned8[] Data = array(new Unsigned8[6]);
public PPACKET_OID_DATA(Runtime runtime) {
super(runtime);
}
}
注意这个 UCHAR
数组定义。这种类型在本地定义为 unsigned char
,因此对于 JNR 结构,它将映射到 jnr.ffi.Strunc.Unsigned8
class 或 jnr.ffi.Struct.BYTE
(几乎相同)。
要声明一个数组字段,您应该在构造时初始化数组。您需要使用 jnr.ffi.Struct#array(...)
函数才能正确执行此操作。这意味着你应该知道数组的大小。示例如上所示。
为什么要这样定义?
在初始化期间,Struct
是某种可变长度的指针。每个 inner-class 字段,在其中初始化,保留其自己的 space 增加此指针的最大大小。所以每个字段都是一个 "view" 到一些内存片段,有自己的方式与这个内存交互(public 方法)。但是要制作这样的视图数组,您需要用视图实例填充空数组。这正是 array
函数的作用。