在 JNA 中映射 COM 接口方法
Mapping a COM interface method in JNA
我想了解 Native.loadLibrary 的工作原理,但我在网上找不到很好的解释。
我需要访问 IVssBackupComponents::AbortBackup
函数 位于 VssApi.lib
。这是函数文档的 link:https://docs.microsoft.com/en-us/windows/win32/api/vsbackup/nl-vsbackup-ivssbackupcomponents
我的代码是这样的:
public interface MyVssClass extends WinNT, StdCallLibrary
{
MyVssClass INSTANCE = Native.loadLibrary("VssApi", MyVssClass.class);
public int AbortBackup();
}
但是我得到查找函数时出错'AbortBackup':找不到指定的过程。这很明显,因为我需要以某种方式表明这个函数是单独的此库中的界面。
如何解决这个问题?
您加载 VssApi DLL 的代码是正确的。但是,AbortBackup()
函数是具有 IVssBackupComponents
接口的 COM 对象上的函数。
映射 COM 对象有点困难,但可行。 JNA 的 COM 支持包括一种在 COM 对象上调用函数的方法,需要 4 位信息:
- 指向对象的指针。这是从另一个函数获得的,或者是在 DLL 公开的常规 C API 中,或者是从另一个 COM 函数。
- 指向函数本身的“指针”。这就是所谓的
VtblId
.
- 函数的参数数组。 (Java 基元或对象。)
- 代表 return 值的 Java 对象。
在您的示例中,首先,您必须找到将实例化对象的 API 函数。在您的情况下,这似乎是 CreateVssBackupComponents()
函数。因此,您将像这样在 VssApi
界面中映射它:
public interface MyVssClass extends WinNT, StdCallLibrary
{
MyVssClass INSTANCE = Native.loadLibrary("VssApi", MyVssClass.class);
HRESULT CreateVssBackupComponents(PointerByReference ppBackup);
}
请注意(对于 COM 对象可能总是如此),API 告诉您您有责任在使用完对象后释放该对象,因此请务必执行此操作!
The calling application is responsible for calling IUnknown::Release
to release the resources held by the returned IVssBackupComponents
when it is no longer needed.
COM 对象方法的映射可以通过继承 JNA 的 COM 映射 (class VssBackupComponents extends Unknown { ... }
) 中的 Unknown
class 来完成,它实现了 IUnknown
接口。例如,继承为您提供了 Release()
方法(您可以查看 Unknown
class 中的实现)。
Unknown 还公开了 _invokeNativeObject()
、_invokeNativeInt()
和 _invokeNativeVoid()
方法,您可以直接映射或使用“包装器”映射 class。查看 JNA 项目中的 Wbemcli.java
class 以获取一些直接和间接映射的示例。参数是一个对象数组,return 类型很简单,有大量示例。
困难的部分是找到 VtblId
,它将让 JNA 找到实际函数的 COM 对象地址。
这个 class 的原始 C 头文件 (vsbackup.h
) 有一个包含函数列表的 IVssBackupComponentsVtbl
结构。 VtblId
是这些函数的顺序。 0、1、2匹配IUnknown
.
中的3个函数
我无法在网上找到 vsbackup.h
的副本,但我确实找到了 Rust 的 this mapping,它不如原版 API 权威,但我怀疑是一致的,在索引 3 处开始函数计数(在 IUnknown
的 0、1 和 2 之后)。然后 AbortBackup()
函数将显示为索引 15。(请与另一个来源核实是否你可以。)所以你的最终映射应该看起来像这样(完全未经测试):
class VssBackupComponents extends Unknown {
public VssBackupComponents() {
}
public VssBackupComponents(Pointer p) {
super(p);
}
public HRESULT AbortBackup() {
// 16th method (MAYBE?) in IVssBackupComponentsVtbl
return (HRESULT) this._invokeNativeObject(15,
new Object[] { this.getPointer() }, HRESULT.class);
}
}
然后在您的主代码中,您将调用该函数以获取 COM 对象的指针并将其实例化,如下所示:
PointerByReference ppBackup = new PointerByReference();
MyVssClass.INSTANCE.CreateVssBackupCompontents(ppBackup);
// you should really test the HRESULT of the above line...
VssBackupComponents backup = new VssBackupComponents(ppBackup.getValue());
// You have an object now! Do stuff with it
try {
// ... stuff ...
backup.AbortBackup();
// you probably want to test HRESULT
// and do whatever else ...
} finally {
backup.Dispose();
}
我想了解 Native.loadLibrary 的工作原理,但我在网上找不到很好的解释。
我需要访问 IVssBackupComponents::AbortBackup
函数 位于 VssApi.lib
。这是函数文档的 link:https://docs.microsoft.com/en-us/windows/win32/api/vsbackup/nl-vsbackup-ivssbackupcomponents
我的代码是这样的:
public interface MyVssClass extends WinNT, StdCallLibrary
{
MyVssClass INSTANCE = Native.loadLibrary("VssApi", MyVssClass.class);
public int AbortBackup();
}
但是我得到查找函数时出错'AbortBackup':找不到指定的过程。这很明显,因为我需要以某种方式表明这个函数是单独的此库中的界面。
如何解决这个问题?
您加载 VssApi DLL 的代码是正确的。但是,AbortBackup()
函数是具有 IVssBackupComponents
接口的 COM 对象上的函数。
映射 COM 对象有点困难,但可行。 JNA 的 COM 支持包括一种在 COM 对象上调用函数的方法,需要 4 位信息:
- 指向对象的指针。这是从另一个函数获得的,或者是在 DLL 公开的常规 C API 中,或者是从另一个 COM 函数。
- 指向函数本身的“指针”。这就是所谓的
VtblId
. - 函数的参数数组。 (Java 基元或对象。)
- 代表 return 值的 Java 对象。
在您的示例中,首先,您必须找到将实例化对象的 API 函数。在您的情况下,这似乎是 CreateVssBackupComponents()
函数。因此,您将像这样在 VssApi
界面中映射它:
public interface MyVssClass extends WinNT, StdCallLibrary
{
MyVssClass INSTANCE = Native.loadLibrary("VssApi", MyVssClass.class);
HRESULT CreateVssBackupComponents(PointerByReference ppBackup);
}
请注意(对于 COM 对象可能总是如此),API 告诉您您有责任在使用完对象后释放该对象,因此请务必执行此操作!
The calling application is responsible for calling IUnknown::Release to release the resources held by the returned IVssBackupComponents when it is no longer needed.
COM 对象方法的映射可以通过继承 JNA 的 COM 映射 (class VssBackupComponents extends Unknown { ... }
) 中的 Unknown
class 来完成,它实现了 IUnknown
接口。例如,继承为您提供了 Release()
方法(您可以查看 Unknown
class 中的实现)。
Unknown 还公开了 _invokeNativeObject()
、_invokeNativeInt()
和 _invokeNativeVoid()
方法,您可以直接映射或使用“包装器”映射 class。查看 JNA 项目中的 Wbemcli.java
class 以获取一些直接和间接映射的示例。参数是一个对象数组,return 类型很简单,有大量示例。
困难的部分是找到 VtblId
,它将让 JNA 找到实际函数的 COM 对象地址。
这个 class 的原始 C 头文件 (vsbackup.h
) 有一个包含函数列表的 IVssBackupComponentsVtbl
结构。 VtblId
是这些函数的顺序。 0、1、2匹配IUnknown
.
我无法在网上找到 vsbackup.h
的副本,但我确实找到了 Rust 的 this mapping,它不如原版 API 权威,但我怀疑是一致的,在索引 3 处开始函数计数(在 IUnknown
的 0、1 和 2 之后)。然后 AbortBackup()
函数将显示为索引 15。(请与另一个来源核实是否你可以。)所以你的最终映射应该看起来像这样(完全未经测试):
class VssBackupComponents extends Unknown {
public VssBackupComponents() {
}
public VssBackupComponents(Pointer p) {
super(p);
}
public HRESULT AbortBackup() {
// 16th method (MAYBE?) in IVssBackupComponentsVtbl
return (HRESULT) this._invokeNativeObject(15,
new Object[] { this.getPointer() }, HRESULT.class);
}
}
然后在您的主代码中,您将调用该函数以获取 COM 对象的指针并将其实例化,如下所示:
PointerByReference ppBackup = new PointerByReference();
MyVssClass.INSTANCE.CreateVssBackupCompontents(ppBackup);
// you should really test the HRESULT of the above line...
VssBackupComponents backup = new VssBackupComponents(ppBackup.getValue());
// You have an object now! Do stuff with it
try {
// ... stuff ...
backup.AbortBackup();
// you probably want to test HRESULT
// and do whatever else ...
} finally {
backup.Dispose();
}