在JNA中映射COM接口方法 [英] Mapping a COM interface method in JNA

查看:157
本文介绍了在JNA中映射COM接口方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图了解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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆