如何在Java应用中调用Windows MMDevice API?
在Java中调用Windows MMDevice API的可行方案
问题背景
需要在Java应用中调用Windows的MMDevice API,但该API无对应类型库,无法通过JNA类型库解析自动生成绑定。手动声明JNA COM接口时,出现Interface not supported HRESULT=80004002错误,需寻找有效调用方案。
方案一:修复JNA的COM接口声明
错误核心原因是未正确绑定COM对象到IMMDeviceEnumerator接口,而是尝试实例化空的IUnknown子类。以下是正确的JNA声明与调用示例:
1. 声明IMMDeviceEnumerator接口
import com.sun.jna.Pointer; import com.sun.jna.platform.win32.Guid; import com.sun.jna.platform.win32.WinNT; import com.sun.jna.ptr.PointerByReference; public interface IMMDeviceEnumerator extends com.sun.jna.platform.win32.COM.IUnknown { Guid.IID IID_IMMDEVICEENUMERATOR = new Guid.IID("A95664D2-9614-4F35-A746-DE8DB63617E6"); // 声明核心方法,可根据需求扩展其他MMDevice API方法 WinNT.HRESULT GetDefaultAudioEndpoint(int dataFlow, int role, PointerByReference ppDevice); }
2. 正确实例化并调用接口
import com.sun.jna.platform.win32.Ole32; import com.sun.jna.platform.win32.WinNT; import com.sun.jna.ptr.PointerByReference; public class MMDeviceAPITest { public static void main(String[] args) { Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED); try { PointerByReference enumeratorPtr = new PointerByReference(); // 通过CoCreateInstance直接绑定到目标接口 WinNT.HRESULT hr = Ole32.INSTANCE.CoCreateInstance( new Guid.CLSID("BCDE0395-E52F-467C-8E3D-C4579291692E"), null, Ole32.CLSCTX_ALL, IMMDeviceEnumerator.IID_IMMDEVICEENUMERATOR, enumeratorPtr ); if (hr.intValue() != WinNT.S_OK) { throw new RuntimeException("CoCreateInstance失败: " + hr); } IMMDeviceEnumerator enumerator = IMMDeviceEnumerator.Converter.pointerToInterface(enumeratorPtr.getValue()); // 调用接口方法获取默认音频设备 PointerByReference devicePtr = new PointerByReference(); hr = enumerator.GetDefaultAudioEndpoint(0, 1, devicePtr); if (hr.intValue() == WinNT.S_OK) { System.out.println("成功获取默认音频设备"); // 释放COM对象 com.sun.jna.platform.win32.COM.IUnknown device = com.sun.jna.platform.win32.COM.IUnknown.Converter.pointerToInterface(devicePtr.getValue()); device.Release(); } enumerator.Release(); } finally { Ole32.INSTANCE.CoUninitialize(); } } }
关键注意点
- 必须通过
CoCreateInstance直接绑定到IMMDeviceEnumerator的IID,而非实例化空的COM对象类 - 接口需正确继承JNA的
IUnknown,方法签名需严格匹配MMDevice API的参数与返回值 - 务必调用
Release()释放COM对象,避免内存泄漏
方案二:使用第三方封装库
寻找已封装Core Audio API的Java库,可跳过手动声明COM接口的步骤。例如部分音频处理库或JNA扩展库已内置MMDevice API的绑定,可直接调用封装后的方法。
方案三:封装原生中间库
若对COM调用逻辑不熟悉,可编写C/C++ DLL封装MMDevice API的常用功能,再通过JNA调用该DLL的简单函数,规避直接处理COM接口的复杂度。
1. 编写C++封装DLL示例
#include <mmdeviceapi.h> #include <endpointvolume.h> #include <objbase.h> extern "C" __declspec(dllexport) HRESULT GetDefaultMasterVolume(float* volume) { HRESULT hr; IMMDeviceEnumerator* pEnumerator = nullptr; IMMDevice* pDevice = nullptr; IAudioEndpointVolume* pVolume = nullptr; hr = CoInitialize(nullptr); if (SUCCEEDED(hr)) { hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator); if (SUCCEEDED(hr)) { hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &pDevice); if (SUCCEEDED(hr)) { hr = pDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, nullptr, (void**)&pVolume); if (SUCCEEDED(hr)) { hr = pVolume->GetMasterVolumeLevelScalar(volume); pVolume->Release(); } pDevice->Release(); } pEnumerator->Release(); } CoUninitialize(); } return hr; }
2. Java端通过JNA调用DLL
import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.ptr.FloatByReference; public interface AudioWrapper extends Library { AudioWrapper INSTANCE = Native.load("AudioWrapper", AudioWrapper.class); int GetDefaultMasterVolume(FloatByReference volume); } public class AudioTest { public static void main(String[] args) { FloatByReference volume = new FloatByReference(); int hr = AudioWrapper.INSTANCE.GetDefaultMasterVolume(volume); if (hr == 0) { // 对应HRESULT的S_OK System.out.println("当前系统音量: " + volume.getValue()); } } }
内容的提问来源于stack exchange,提问作者SerVB
相关产品推荐
相关产品推荐

