在JNA中映射COM接口方法 [英] Mapping a COM interface method in JNA
问题描述
我试图了解Native.loadLibrary的工作原理,但是我在网上找不到很好的解释.
我需要访问位于VssApi.lib
中的IVssBackupComponents::AbortBackup
函数.这是函数文档的链接: https ://docs.microsoft.com/zh-CN/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原语或对象.)
- 代表返回值的Java对象.
在您的示例中,首先,您必须找到将实例化该对象的API函数.就您而言,这似乎是 VssApi
界面中将其映射:
public interface MyVssClass extends WinNT, StdCallLibrary
{
MyVssClass INSTANCE = Native.loadLibrary("VssApi", MyVssClass.class);
HRESULT CreateVssBackupComponents(PointerByReference ppBackup);
}
值得注意的是(这对于COM对象来说可能总是这样),API告诉您,当您使用完对象后,您有责任释放该对象,因此请确保执行此操作!
调用应用程序负责调用IUnknown :: Release 释放由返回的IVssBackupComponents持有的资源 当不再需要它时.
可以从JNA的COM映射(class VssBackupComponents extends Unknown { ... }
)中的Unknown
类继承来完成COM对象方法的映射,该类实现了IUnknown
接口.例如,继承为您提供了Release()
方法(您可以在 Wbemcli.java
类提供了一些直接和间接映射的示例.参数是一个对象数组,返回类型很简单,包含大量示例.
最困难的部分是找到VtblId
,这将使JNA可以找到实际功能的COM对象的地址.
此类的原始C头(vsbackup.h
)具有IVssBackupComponentsVtbl
结构,其中包含函数列表. VtblId
是这些功能的顺序. 0、1和2与IUnknown
中的3个功能匹配.
我无法在线找到vsbackup.h
的副本,但是我确实找到了此映射(不像原始API那样权威,但我怀疑是一致的)开始进行功能计数(在IUnknown
的0、1和2)在索引3处.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对象的ponter并实例化它,如下所示:
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();
}
I am trying to understand how Native.loadLibrary works, but I can not find a good explanation on the web.
I need to get access to the IVssBackupComponents::AbortBackup
function witch is located in VssApi.lib
. Here is a link for the function docs: https://docs.microsoft.com/en-us/windows/win32/api/vsbackup/nl-vsbackup-ivssbackupcomponents
My code is like this:
public interface MyVssClass extends WinNT, StdCallLibrary
{
MyVssClass INSTANCE = Native.loadLibrary("VssApi", MyVssClass.class);
public int AbortBackup();
}
But I get Error looking up function 'AbortBackup': The specified procedure could not be found. Which is obvious since I need somehow indicate that this function is in separate interface in this library.
How do I solve this issue?
Your code to load the VssApi DLL is correct. However, the AbortBackup()
function is a function on a COM object with the IVssBackupComponents
interface.
Mapping COM objects is a bit challenging but doable. JNA's COM support includes a way to invoke functions on COM objects, requiring 4 bits of information:
- A pointer to the object. This is obtained from another function, either in the conventional C API exposed by the DLL or from another COM function.
- A "pointer" to the function itself. This is something called the
VtblId
. - An array of arguments to the function. (Java primitives or objects.)
- A Java object representing the return value.
In your example, first, you have to locate the API function that will instantiate the object. In your case that appears to be the CreateVssBackupComponents()
function. So you'll map that in your VssApi
interface like this:
public interface MyVssClass extends WinNT, StdCallLibrary
{
MyVssClass INSTANCE = Native.loadLibrary("VssApi", MyVssClass.class);
HRESULT CreateVssBackupComponents(PointerByReference ppBackup);
}
Of note (and this is probably always the case for COM objects), that API tells you that you are responsible for releasing the object when you are done with it, so make sure to do this!
The calling application is responsible for calling IUnknown::Release to release the resources held by the returned IVssBackupComponents when it is no longer needed.
Mapping the COM object methods can be done inheriting from the Unknown
class in JNA's COM mappings (class VssBackupComponents extends Unknown { ... }
) , which implements the IUnknown
interface. Inheriting gives you the Release()
method, for example (you can peek inside the Unknown
class to see the implementation).
Unknown also exposes _invokeNativeObject()
, _invokeNativeInt()
, and _invokeNativeVoid()
methods that you can map either directly or with a "wrapper" class. Check out the Wbemcli.java
class in the JNA project for an example of a few direct and indirect mappings. The arguments are an array of objects, the return type is straightforward with ample examples.
The hard part is finding the VtblId
which will let JNA find the COM object's address of the actual function.
The original C header (vsbackup.h
) for this class has a IVssBackupComponentsVtbl
structure containing a list of functions. The VtblId
is the order of these functions. 0, 1, and 2 match the 3 functions in IUnknown
.
I am unable to find a copy of vsbackup.h
online, but I did locate this mapping for Rust, which isn't as authoritative as the original API, but I suspect is consistent, begins the function counting (after IUnknown
's 0, 1, and 2) at index 3. The AbortBackup()
function would then appear to be index 15. (Please check this with another source if you can.) So your final mapping should look something like this (completely untested):
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);
}
}
Then in your main code, you'll call the function to get a ponter to the COM object and instantiate it as follows:
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();
}
这篇关于在JNA中映射COM接口方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!