Home > Back-end >  A way of accessing Windows MMDevice API from Java?
A way of accessing Windows MMDevice API from Java?

Time:01-22

I would like to use the MMDevice API from my Java app. What are my options?

I tried to use JNA. Looks like I can't use JNA Typelib parsing because there no types for this API (Is there a COM type library for Windows Core Audio). As suggested, I need to provide my own declarations of the API.

So I also tried both JNA examples with manual declarations but they give "Interface not supported HRESULT=80004002" error:

public class MMDeviceAPITest {
    public static void test1() {
        try {
            Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED);
            var obj = new Test1.MMDeviceEnumerator();  // exception E_NOINTERFACE (HRESULT: 80004002)
            // ...
        } finally {
            Ole32.INSTANCE.CoUninitialize();
        }
    }
    public static void test2() {
        try {
            Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED);
            var factory = new Factory();
            var obj = factory.createObject(Test2.MMDeviceEnumerator.class);  // exception E_NOINTERFACE (HRESULT: 80004002)
            var in = obj.queryInterface(Test2.IMMDeviceEnumerator.class);
            // ...
        } finally {
            Ole32.INSTANCE.CoUninitialize();
        }
    }
}

interface Test1 {
    class MMDeviceEnumerator extends COMLateBindingObject {
        public MMDeviceEnumerator() {
            super(new Guid.CLSID("bcde0395-e52f-467c-8e3d-c4579291692e"), true);
        }
    }
}

interface Test2 {
    @ComObject(clsId = "bcde0395-e52f-467c-8e3d-c4579291692e")
    interface MMDeviceEnumerator extends IUnknown {}  // doesn't extend IUnknown in C sources, probably it's the problem...
    @ComInterface(iid = "a95664d2-9614-4f35-a746-de8db63617e6")
    interface IMMDeviceEnumerator extends IUnknown {}
}

Any ideas how I could access this API from Java? Can I somehow create working declarations for JNA? Or use another framework maybe?

My last idea is to create/find a micro native app/library that wraps the needed COM calls, so I could call this app/library easily (via subprocesses or simple JNA declarations). I'm new to COM world, but it sounds working for me...

CodePudding user response:

The docs you linked show how to create using CoCreateInstance:

const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
hr = CoCreateInstance(
       CLSID_MMDeviceEnumerator, NULL,
       CLSCTX_ALL, IID_IMMDeviceEnumerator,
       (void**)&pEnumerator);

This should get you somewhere close with JNA. I made some changes from the implementation of IWbemContext in JNA.

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 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);
        if (COMUtils.FAILED(hres)) {
            return null;
        }

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

    // map functions as needed
}

CodePudding user response:

For some reason I can't suggest edits to the answer of Daniel Widdis. The answer worked for me, many thanks! Just wanted to show how to map one method as an example:

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 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);
        if (COMUtils.FAILED(hres)) {
            return null;
        }

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

    public static final int EDataFlow_eRender = 0;
    public static final int EDataFlow_eCapture = 1;
    public static final int EDataFlow_eAll = 2;
    public static final int EDataFlow_enum_count = 3;

    public static final int DEVICE_STATE_ACTIVE = 0x1;
    public static final int DEVICE_STATE_DISABLED = 0x2;
    public static final int DEVICE_STATE_NOTPRESENT = 0x4;
    public static final int DEVICE_STATE_UNPLUGGED = 0x8;
    public static final int DEVICE_STATEMASK_ALL = 0xF;

    public void EnumAudioEndpoints(int dataFlow, int dwStateMask, PointerByReference ppDevices) {
        WinNT.HRESULT res = (WinNT.HRESULT) _invokeNativeObject(
            3,  // `EnumAudioEndpoints` is the 3rd method of `IMMDeviceEnumeratorVtbl` in `mmdeviceapi.h`
            new Object[] { getPointer(), dataFlow, new WinDef.DWORD(dwStateMask), ppDevices},
            WinNT.HRESULT.class
        );
        COMUtils.checkRC(res);
    }

    // map other functions as needed
}
  • Related