Reputation:
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://learn.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?
Upvotes: 2
Views: 1163
Reputation: 9131
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:
VtblId
.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();
}
Upvotes: 2