javapointerswinapijna

Invoking IMMDeviceEnumerator method with JNA returns S_FALSE and empty(?) pointer


I don't really know how to phrase this question (sorry). But basically (using JNA and Windows API) i'm trying to get an instance(?) of the mmDevice interface by calling GetDefaultAudioEndpoint on my MMDeviceEnumerator instance(?). The problem is that the call to GetDefaultAudioEndpoint returns what I think is an empty pointer null@0x2bd70eebeb0. Which I think causes later calls to it to produce this error during runtime:

Calling MMDeviceEnumerator.create()
response: 0
Got MMDeviceEnumerator
Pointer: native@0x2bd70f68590
Pointer.getPointer(): native@0x2bd70f68590

Calling mmEnumerator.GetDefaultAudioEndpoint(args)
response: 1
Got mmDevice
Pointer: null@0x2bd70eebeb0
Pointer.getValue(): null
Pointer.getPointer(): allocated@0x2bd70eebeb0 (8 bytes)
null

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.sun.jna.Pointer.getPointer(long)" because "vptr" is null
        at com.sun.jna.platform.win32.COM.COMInvoker._invokeNativeObject(COMInvoker.java:46)
        at spicey.IAudioEndpointVolume.Activate(App.java:76)
        at spicey.App.main(App.java:58)

The call to GetDefaultAudioEndpoint also returns an HRESULT of S_FALSE or 1, if that is any help. Anyways, here is the program in its entirety. I'll also be happy to provide any extra info. Thanks in advance!

public final class App extends Unknown {

    public static final CLSID CLSID_IMMDeviceEnumerator = new CLSID("bcde0395-e52f-467c-8e3d-c4579291692e");
    public static final GUID IID_IMMDeviceEnumerator = new GUID("a95664d2-9614-4f35-a746-de8db63617e6");
    public static final GUID IID_IAudioEndpointVolume = new GUID("5CDF2C82-841E-4546-9722-0CF74078229A");
    public static final GUID IID_IAudioStreamVolume = new GUID("93014887-242D-4068-8A15-CF5E93B90FE3");

    public static void main(String[] args) {
        Ole32.INSTANCE.CoInitializeEx(null, 0); // 0 is multi threaded
        System.out.println("Calling MMDeviceEnumerator.create()");
        MMDeviceEnumerator mmEnumerator = MMDeviceEnumerator.create();
        System.out.println("Got MMDeviceEnumerator");
        System.out.println("Pointer: " + mmEnumerator.getPointer());
        System.out.println("Pointer.getPointer(): " + mmEnumerator.getPointer());
        System.out.println();

        PointerByReference ppEndpoint = new PointerByReference();
        System.out.println("Calling mmEnumerator.GetDefaultAudioEndpoint(args)");
        mmEnumerator.GetDefaultAudioEndpoint(0, 0, ppEndpoint);

        System.out.println("Got mmDevice");
        System.out.println("Pointer: " + ppEndpoint);
        System.out.println("Pointer.getValue(): " + ppEndpoint.getValue());
        System.out.println("Pointer.getPointer(): " + ppEndpoint.getPointer());
        System.out.println();

        IAudioEndpointVolume iEndpoint = IAudioEndpointVolume.create(ppEndpoint.getPointer());
        PointerByReference ppInterface = new PointerByReference();
        iEndpoint.Activate(IID_IAudioEndpointVolume, WTypes.CLSCTX_ALL, ppInterface.getPointer());

    }

}

class IAudioEndpointVolume extends Unknown {

    public IAudioEndpointVolume(Pointer p) {
        super(p);

    }

    public static IAudioEndpointVolume create(Pointer p) {
        return new IAudioEndpointVolume(p);
    }

    public void Activate(GUID iid, int dwClsCtx, Pointer ppInterface) {
        HRESULT res = (HRESULT) _invokeNativeObject(1, new Object[] { getPointer(), iid, dwClsCtx, null, ppInterface },
                HRESULT.class);
        COMUtils.checkRC(res);

    }

}

class MMDeviceEnumerator extends Unknown {
    public static final CLSID CLSID_MMDeviceEnumerator = new CLSID("bcde0395-e52f-467c-8e3d-c4579291692e");
    public static final GUID IID_IMMDeviceEnumerator = new GUID("a95664d2-9614-4f35-a746-de8db63617e6");

    public static interface Ole32 extends Library {
        Ole32 INSTANCE = (Ole32) Native.load("Ole32",
                Ole32.class);

        HRESULT CoCreateInstance(GUID rclsid, Pointer pUnkOuter, int dwClsContext, GUID riid, PointerByReference ppv);

    }

    public MMDeviceEnumerator(Pointer p) {
        super(p);
    }

    public static MMDeviceEnumerator create() {
        PointerByReference pEnumerator = new PointerByReference();

        HRESULT hres = Ole32.INSTANCE.CoCreateInstance(
                CLSID_MMDeviceEnumerator, null,
                WTypes.CLSCTX_ALL, IID_IMMDeviceEnumerator,
                pEnumerator);
        System.out.println("response: " + hres);
        if (COMUtils.FAILED(hres)) {
            return null;
        }

        return new MMDeviceEnumerator(pEnumerator.getValue());
    }

    // functionz
    public void GetDefaultAudioEndpoint(int dataFlow, int role, PointerByReference ppEndpoint) {
        HRESULT res = (HRESULT) _invokeNativeObject(2,
                new Object[] { getPointer(), dataFlow, role, ppEndpoint }, HRESULT.class);

        COMUtils.checkRC(res);
        System.out.println("response: " + res);

    }
}

I also tried looking directly at the COMInvoker class with no luck

public abstract class COMInvoker extends PointerType {

    protected int _invokeNativeInt(int vtableId, Object[] args) {
        Pointer vptr = this.getPointer().getPointer(0);
        // we take the vtable id and multiply with the pointer size (4 bytes on
        // 32bit OS)
        Function func = Function.getFunction(vptr.getPointer(vtableId
                * Native.POINTER_SIZE));
        return func.invokeInt(args);
    }

    protected Object _invokeNativeObject(int vtableId, Object[] args, Class<?> returnType) {
        Pointer vptr = this.getPointer().getPointer(0);
        // we take the vtable id and multiply with the pointer size (4 bytes on
        // 32bit OS)
        Function func = Function.getFunction(vptr.getPointer(vtableId
                * Native.POINTER_SIZE));
        return func.invoke(returnType, args);
    }

    protected void _invokeNativeVoid(int vtableId, Object[] args) {
        Pointer vptr = this.getPointer().getPointer(0);
        // we take the vtable id and multiply with the pointer size (4 bytes on
        // 32bit OS)
        Function func = Function.getFunction(vptr.getPointer(vtableId
                * Native.POINTER_SIZE));
        func.invokeVoid(args);
    }

}

Solution

  • Nvm I solved it. There are a few problems with the above code but the main one is that the table ids are all off because I forgot to take into account that the IMMDeviceEnumerator interface inherits 3 methods from the IUnknown interface, so you have to add 3 to each table id argument.