有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java映射JNA中的COM接口方法

我在努力理解自己是多么的土生土长。loadLibrary可以工作,但我在web上找不到很好的解释

我需要访问位于VssApi.libIVssBackupComponents::AbortBackup函数。下面是函数docs的链接: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”时出错:找不到指定的过程这很明显,因为我需要以某种方式指示此函数位于该库中的单独接口中

我如何解决这个问题


共 (1) 个答案

  1. # 1 楼答案

    加载VssApi DLL的代码是正确的。但是,AbortBackup()函数是COM对象上带有IVssBackupComponents接口的函数

    映射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告诉您,当您使用完对象后,您有责任释放它,所以一定要这样做

    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类继承来完成,该类实现了IUnknown接口。例如,继承为您提供了Release()方法(您可以查看^{}类内部的实现)

    Unknown还公开了_invokeNativeObject() _invokeNativeInt()_invokeNativeVoid()方法,这些方法可以直接映射,也可以使用“包装器”类映射。查看JNA项目中的^{}类,以获取一些直接和间接映射的示例。参数是一个对象数组,返回类型简单明了,示例丰富

    困难的部分是找到VtblId,这将让JNA找到实际函数的COM对象地址

    这个类的原始C头(vsbackup.h)有一个包含函数列表的IVssBackupComponentsVtbl结构。VtblId是这些函数的顺序。0、1和2与^{中的3个函数匹配

    我无法在网上找到vsbackup.h的副本,但我确实找到了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();
    }