You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 23:40:30