Search code examples
javawinapicomjna

A way of accessing Windows MMDevice API from Java?


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...


Solution

  • 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.

    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
    }
    

    I used the implementation of IWbemContext in JNA as a template above. You can consult that class for example COM function mappings.