JNA 检测音频设备 Arrival/Remove

JNA Detect Audio Device Arrival/Remove

我看到了一些关于此的问题,但其中 none 对我需要做的事情很有帮助。

我正在使用 JNA 来帮助我检测 USB 设备何时 inserted/removed。

DBT_DEVICEARRIVALDBT_DEVICEREMOVECOMPLETE 事件在 inserting/removing USB 驱动器(加密狗)时收到。但是当inserting/removing一个Audio接口时这些事件都没有收到,只有DBT_DEVNODES_CHANGES收到了。

DBT_DEVNODES_CHANGES 不包含有关设备的任何信息,因为 lParam 始终为 0。

我想我需要做的是检测这些 DBT_DEVNODES_CHANGES 事件,并根据它们枚举我的 USB 设备以确定发生了什么变化。 ?但是我怎样才能通过 JNA 访问我所有的 USB 设备?

我的代码目前看起来如下(精简):

    package com.company;

    import com.sun.jna.Callback;
    import com.sun.jna.FromNativeContext;
    import com.sun.jna.Native;
    import com.sun.jna.Pointer;
    import com.sun.jna.platform.win32.*;
    import com.sun.jna.win32.StdCallLibrary;
    import com.sun.jna.win32.W32APIOptions;
    import libs.User32;

    import javax.swing.*;

    public class MyForm
    {
        private JPanel top_panel;

        public static final com.sun.jna.platform.win32.Guid.GUID GUID_DEVINTERFACE_USB_DEVICE = new Guid.GUID(new byte[] {
            (byte)0xA5, (byte)0xDC, (byte)0xBF, 0x10, 0x65, 0x30, 0x11, (byte)0xD2, (byte)0x90, 0x1F, 0x00, (byte)0xC0, 0x4F, (byte)0xB9, 0x51, (byte)0xED
        });

        public interface MyUser32 extends User32 
        {
            public static final MyUser32 MYINSTANCE = (MyUser32)      Native.loadLibrary("user32", MyUser32.class, W32APIOptions.UNICODE_OPTIONS);
            public static final int GWLP_WNDPROC = -4;
            public int SetWindowLong(WinDef.HWND hWnd, int nIndex, Callback callback);
            public int RegisterDeviceNotification(WinNT.HANDLE hRecipient, Pointer NotificationFilter, int Flags);
        }

        public interface MyWinUser extends WinUser 
        {        
            public static final int WM_DEVICECHANGE = 0x0219;
            public static final int DBT_DEVICEARRIVAL = 0x8000;
            public static final int DBT_DEVICEREMOVECOMPLETE = 0x8004;
            public static final int DBT_DEVNODES_CHANGED = 0x0007;

        }

        //Create the callback interface
        public interface MyListener extends StdCallLibrary.StdCallCallback 
        {
            public WinDef.LRESULT callback(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM uParam, WinDef.LPARAM lParam);
        }

        public static MyListener listener = new MyListener()
        {
            public WinDef.LRESULT callback(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM uParam, WinDef.LPARAM lParam)
            {
                if (uMsg == MyWinUser.WM_DEVICECHANGE)
                {
                   if(uParam.intValue() == MyWinUser.DBT_DEVICEARRIVAL)
                   {
                       System.out.println("Device was added");
                   }

                   if(uParam.intValue() == MyWinUser.DBT_DEVICEREMOVECOMPLETE)
                   {
                       System.out.println("Device was removed");
                   }

                   if(uParam.intValue() == MyWinUser.DBT_DEVNODES_CHANGED)
                   {                    
                       System.out.println("Device node changed" + lParam.longValue());
                   }
                   return new WinDef.LRESULT(1);
            }
            return new WinDef.LRESULT(0);
        }
    };

    public static void main(String[] args) 
    {
        JFrame frame = new JFrame("MyForm");
        frame.setContentPane(new MyForm().top_panel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);

        WinDef.HWND hWnd = new WinDef.HWND();
        hWnd.setPointer(Native.getWindowPointer(frame));        
        MyUser32.MYINSTANCE.SetWindowLong(hWnd,MyUser32.GWLP_WNDPROC,listener);


    }
}

编辑:我已经开始着手在收到 Arrival/Removal 事件后获取我的设备,并手动检测连接或移除了哪个设备。首先调用 SetupDiGetClassDevs 工作正常,我可以使用 SetupDiEnumDeviceInfo 进行迭代。之后,我调用 SetupDiEnumDeviceInterfaces 来获取 SP_DEVICE_INTERFACE_DATA,我在调用 SetupDiGetDeviceInterfaceDetail 时需要它,这最终应该让我获得有关设备的供应商、产品等信息。

问题是,我在调用 SetupDiEnumDeviceInfo 时立即得到 ERROR_NO_MORE_ITEMS。

你可以在下面看到我的代码:

    com.sun.jna.Pointer enume =  new Pointer(0);
    WinNT.HANDLE hDevInfo = SetupAPI.INSTANCE.SetupDiGetClassDevs(null,enume,hWnd.getPointer(),SetupAPI.DIGCF_PRESENT | SetupAPI.DIGCF_ALLCLASSES);


    SetupAPI.SP_DEVINFO_DATA spDevInfoData = new SetupAPI.SP_DEVINFO_DATA();
    spDevInfoData.cbSize = spDevInfoData.size();
    int i = 0;
    while(SetupAPI.INSTANCE.SetupDiEnumDeviceInfo(hDevInfo, i, spDevInfoData))
    {
        i++;
        IntByReference requiredSize = null;
        SetupAPI.SP_DEVICE_INTERFACE_DATA  devInterfaceData =  new SetupAPI.SP_DEVICE_INTERFACE_DATA();

        int j = 0;
        while(SetupAPI.INSTANCE.SetupDiEnumDeviceInterfaces(hDevInfo,spDevInfoData.getPointer(),spDevInfoData.InterfaceClassGuid,j,devInterfaceData))
        {
            j++;
            if(Native.getLastError() == ERROR_NO_MORE_ITEMS)
            {
                System.out.println("No more items to enumerate");
            }
        }

        SetupAPI.SP_DEVINFO_DATA deviceInfoData = new SetupAPI.SP_DEVINFO_DATA();
        deviceInfoData.cbSize = deviceInfoData.size();

        boolean test2 = SetupAPI.INSTANCE.SetupDiGetDeviceInterfaceDetail(hDevInfo,devInterfaceData,null,0,requiredSize,deviceInfoData);

        int error = Native.getLastError();
        test2 = true;
    }

我无法用当前的实现解决我的问题,但我很幸运偶然发现了一个实现了我所需要的东西的库。因为我不能简单地 post a link (违反规则)我 post 完整的 class 这里,因为方法与我自己的完全不同,我无法指出部分帮助我让我自己的实现工作的代码。

/* Copyright (c) 2012 Tobias Wolf, All Rights Reserved
* 
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* 
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.  
*/

package com.sun.jna.platform.win32;

import com.sun.jna.WString;
import com.sun.jna.platform.win32.DBT;
import com.sun.jna.platform.win32.DBT.DEV_BROADCAST_DEVICEINTERFACE;
import com.sun.jna.platform.win32.DBT.DEV_BROADCAST_HANDLE;
import com.sun.jna.platform.win32.DBT.DEV_BROADCAST_HDR;
import com.sun.jna.platform.win32.DBT.DEV_BROADCAST_OEM;
import com.sun.jna.platform.win32.DBT.DEV_BROADCAST_PORT;
import com.sun.jna.platform.win32.DBT.DEV_BROADCAST_VOLUME;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HMODULE;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.platform.win32.WinUser.HDEVNOTIFY;
import com.sun.jna.platform.win32.WinUser.MSG;
import com.sun.jna.platform.win32.WinUser.WNDCLASSEX;
import com.sun.jna.platform.win32.WinUser.WindowProc;
import com.sun.jna.platform.win32.Wtsapi32;

// TODO: Auto-generated Javadoc
/**
* The Class Win32WindowTest.
*/
public class Win32WindowDemo implements WindowProc {

/**
 * Instantiates a new win32 window test.
 */
public Win32WindowDemo() {
    // define new window class
    WString windowClass = new WString("MyWindowClass");
    HMODULE hInst = Kernel32.INSTANCE.GetModuleHandle("");

    WNDCLASSEX wClass = new WNDCLASSEX();
    wClass.hInstance = hInst;
    wClass.lpfnWndProc = Win32WindowDemo.this;
    wClass.lpszClassName = windowClass;

    // register window class
    User32.INSTANCE.RegisterClassEx(wClass);
    getLastError();

    // create new window
    HWND hWnd = User32.INSTANCE
            .CreateWindowEx(
                    User32.WS_EX_TOPMOST,
                    windowClass,
                    "My hidden helper window, used only to catch the windows events",
                    0, 0, 0, 0, 0,
                    null, // WM_DEVICECHANGE contradicts parent=WinUser.HWND_MESSAGE
                    null, hInst, null);

    getLastError();
    System.out.println("window sucessfully created! window hwnd: "
            + hWnd.getPointer().toString());

    Wtsapi32.INSTANCE.WTSRegisterSessionNotification(hWnd,
            Wtsapi32.NOTIFY_FOR_THIS_SESSION);

    /* this filters for all device classes */
    // DEV_BROADCAST_HDR notificationFilter = new DEV_BROADCAST_HDR();
    // notificationFilter.dbch_devicetype = DBT.DBT_DEVTYP_DEVICEINTERFACE;

    /* this filters for all usb device classes */
    DEV_BROADCAST_DEVICEINTERFACE notificationFilter = new DEV_BROADCAST_DEVICEINTERFACE();
    notificationFilter.dbcc_size = notificationFilter.size();
    notificationFilter.dbcc_devicetype = DBT.DBT_DEVTYP_DEVICEINTERFACE;
    notificationFilter.dbcc_classguid = DBT.GUID_DEVINTERFACE_USB_DEVICE;

    /*
     * use User32.DEVICE_NOTIFY_ALL_INTERFACE_CLASSES instead of
     * DEVICE_NOTIFY_WINDOW_HANDLE to ignore the dbcc_classguid value
     */
    HDEVNOTIFY hDevNotify = User32.INSTANCE.RegisterDeviceNotification(
            hWnd, notificationFilter, User32.DEVICE_NOTIFY_WINDOW_HANDLE);

    getLastError();
    if (hDevNotify != null)
        System.out.println("RegisterDeviceNotification was sucessfully!");

    MSG msg = new MSG();
    while (User32.INSTANCE.GetMessage(msg, hWnd, 0, 0) != 0) {
        User32.INSTANCE.TranslateMessage(msg);
        User32.INSTANCE.DispatchMessage(msg);
    }

    User32.INSTANCE.UnregisterDeviceNotification(hDevNotify);
    Wtsapi32.INSTANCE.WTSUnRegisterSessionNotification(hWnd);
    User32.INSTANCE.UnregisterClass(windowClass, hInst);
    User32.INSTANCE.DestroyWindow(hWnd);

    System.out.println("program exit!");
}

/*
 * (non-Javadoc)
 * 
 * @see
 * com.sun.jna.platform.win32.User32.WindowProc#callback(com.sun.jna.platform
 * .win32.WinDef.HWND, int, com.sun.jna.platform.win32.WinDef.WPARAM,
 * com.sun.jna.platform.win32.WinDef.LPARAM)
 */
public LRESULT callback(HWND hwnd, int uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WinUser.WM_CREATE: {
        onCreate(wParam, lParam);
        return new LRESULT(0);
    }
    case WinUser.WM_DESTROY: {
        User32.INSTANCE.PostQuitMessage(0);
        return new LRESULT(0);
    }
    case WinUser.WM_SESSION_CHANGE: {
        this.onSessionChange(wParam, lParam);
        return new LRESULT(0);
    }
    case WinUser.WM_DEVICECHANGE: {
        LRESULT lResult = this.onDeviceChange(wParam, lParam);
        return lResult != null ? lResult :
            User32.INSTANCE.DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    default:
        return User32.INSTANCE.DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}

/**
 * Gets the last error.
 * 
 * @return the last error
 */
public int getLastError() {
    int rc = Kernel32.INSTANCE.GetLastError();

    if (rc != 0)
        System.out.println("error: " + rc);

    return rc;
}

/**
 * On session change.
 * 
 * @param wParam
 *            the w param
 * @param lParam
 *            the l param
 */
protected void onSessionChange(WPARAM wParam, LPARAM lParam) {
    switch (wParam.intValue()) {
    case Wtsapi32.WTS_CONSOLE_CONNECT: {
        this.onConsoleConnect(lParam.intValue());
        break;
    }
    case Wtsapi32.WTS_CONSOLE_DISCONNECT: {
        this.onConsoleDisconnect(lParam.intValue());
        break;
    }
    case Wtsapi32.WTS_SESSION_LOGON: {
        this.onMachineLogon(lParam.intValue());
        break;
    }
    case Wtsapi32.WTS_SESSION_LOGOFF: {
        this.onMachineLogoff(lParam.intValue());
        break;
    }
    case Wtsapi32.WTS_SESSION_LOCK: {
        this.onMachineLocked(lParam.intValue());
        break;
    }
    case Wtsapi32.WTS_SESSION_UNLOCK: {
        this.onMachineUnlocked(lParam.intValue());
        break;
    }
    }
}

/**
 * On console connect.
 * 
 * @param sessionId
 *            the session id
 */
protected void onConsoleConnect(int sessionId) {
    System.out.println("onConsoleConnect: " + sessionId);
}

/**
 * On console disconnect.
 * 
 * @param sessionId
 *            the session id
 */
protected void onConsoleDisconnect(int sessionId) {
    System.out.println("onConsoleDisconnect: " + sessionId);
}

/**
 * On machine locked.
 * 
 * @param sessionId
 *            the session id
 */
protected void onMachineLocked(int sessionId) {
    System.out.println("onMachineLocked: " + sessionId);
}

/**
 * On machine unlocked.
 * 
 * @param sessionId
 *            the session id
 */
protected void onMachineUnlocked(int sessionId) {
    System.out.println("onMachineUnlocked: " + sessionId);
}

/**
 * On machine logon.
 * 
 * @param sessionId
 *            the session id
 */
protected void onMachineLogon(int sessionId) {
    System.out.println("onMachineLogon: " + sessionId);
}

/**
 * On machine logoff.
 * 
 * @param sessionId
 *            the session id
 */
protected void onMachineLogoff(int sessionId) {
    System.out.println("onMachineLogoff: " + sessionId);
}

/**
 * On device change.
 * 
 * @param wParam
 *            the w param
 * @param lParam
 *            the l param
 * @return the result. Null if the message is not processed.
 */
protected LRESULT onDeviceChange(WPARAM wParam, LPARAM lParam) {
    switch (wParam.intValue()) {
    case DBT.DBT_DEVICEARRIVAL: {
        return onDeviceChangeArrival(lParam);
    }
    case DBT.DBT_DEVICEREMOVECOMPLETE: {
        return onDeviceChangeRemoveComplete(lParam);
    }
    case DBT.DBT_DEVNODES_CHANGED: {
        //lParam is 0 for this wParam
        return onDeviceChangeNodesChanged();
    }
    default:
        System.out
                .println("Message WM_DEVICECHANGE message received, value unhandled.");
    }
    return null;
}

protected LRESULT onDeviceChangeArrivalOrRemoveComplete(LPARAM lParam, String action) {
    DEV_BROADCAST_HDR bhdr = new DEV_BROADCAST_HDR(lParam.longValue());
    switch (bhdr.dbch_devicetype) {
    case DBT.DBT_DEVTYP_DEVICEINTERFACE: {
        // see http://msdn.microsoft.com/en-us/library/windows/desktop/aa363244.aspx
        DEV_BROADCAST_DEVICEINTERFACE bdif = new DEV_BROADCAST_DEVICEINTERFACE(bhdr.getPointer());
        System.out.println("BROADCAST_DEVICEINTERFACE: " + action);
        System.out.println("dbcc_devicetype: " + bdif.dbcc_devicetype);
        System.out.println("dbcc_name: " + bdif.getDbcc_name());
        System.out.println("dbcc_classguid: "
                + bdif.dbcc_classguid.toGuidString());
        break;
    }
    case DBT.DBT_DEVTYP_HANDLE: {
        // see http://msdn.microsoft.com/en-us/library/windows/desktop/aa363245.aspx
        DEV_BROADCAST_HANDLE bhd = new DEV_BROADCAST_HANDLE(bhdr.getPointer());
        System.out.println("BROADCAST_HANDLE: " + action);
        break;
    }
    case DBT.DBT_DEVTYP_OEM: {
        // see http://msdn.microsoft.com/en-us/library/windows/desktop/aa363247.aspx
        DEV_BROADCAST_OEM boem = new DEV_BROADCAST_OEM(bhdr.getPointer());
        System.out.println("BROADCAST_OEM: " + action);
        break;
    }
    case DBT.DBT_DEVTYP_PORT: {
        // see http://msdn.microsoft.com/en-us/library/windows/desktop/aa363248.aspx
        DEV_BROADCAST_PORT bpt = new DEV_BROADCAST_PORT(bhdr.getPointer());
        System.out.println("BROADCAST_PORT: " + action);
        break;
    }
    case DBT.DBT_DEVTYP_VOLUME: {
        // see http://msdn.microsoft.com/en-us/library/windows/desktop/aa363249.aspx
        DEV_BROADCAST_VOLUME bvl = new DEV_BROADCAST_VOLUME(bhdr.getPointer());
        int logicalDriveAffected = bvl.dbcv_unitmask;
        short flag = bvl.dbcv_flags;
        boolean isMediaNotPhysical = 0 != (flag & DBT.DBTF_MEDIA/*value is 1*/);
        boolean isNet = 0 != (flag & DBT.DBTF_NET/*value is 2*/);
        System.out.println(action);
        int driveLetterIndex = 0;
        while (logicalDriveAffected != 0) {
            if (0 != (logicalDriveAffected & 1)) {
                System.out.println("Logical Drive Letter: " +
                    ((char) ('A' + driveLetterIndex)));
            }
            logicalDriveAffected >>>= 1;
            driveLetterIndex++;
        }
        System.out.println("isMediaNotPhysical:"+isMediaNotPhysical);
        System.out.println("isNet:"+isNet);
        break;
    }
    default:
        return null;
    }
    // return TRUE means processed message for this wParam.
    // see http://msdn.microsoft.com/en-us/library/windows/desktop/aa363205.aspx
    // see http://msdn.microsoft.com/en-us/library/windows/desktop/aa363208.aspx
    return new LRESULT(1);
}

protected LRESULT onDeviceChangeArrival(LPARAM lParam) {
    return onDeviceChangeArrivalOrRemoveComplete(lParam, "Arrival");
}

protected LRESULT onDeviceChangeRemoveComplete(LPARAM lParam) {
    return onDeviceChangeArrivalOrRemoveComplete(lParam, "Remove Complete");
}

protected LRESULT onDeviceChangeNodesChanged() {
    System.out.println("Message DBT_DEVNODES_CHANGED");
    // return TRUE means processed message for this wParam.
    // see http://msdn.microsoft.com/en-us/library/windows/desktop/aa363211.aspx
    return new LRESULT(1);
}

/**
 * On create.
 * 
 * @param wParam
 *            the w param
 * @param lParam
 *            the l param
 */
protected void onCreate(WPARAM wParam, LPARAM lParam) {
    System.out.println("onCreate: WM_CREATE");
}

/**
 * The main method.
 * 
 * @param args
 *            the arguments
 */
public static void main(String[] args) {
    new Win32WindowDemo();
}
}