在 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();
}