任何用于检测 MTP 设备的 Windows API



我正在寻找通过Windows API检测MTP设备,而不仅仅是枚举已连接的设备。

首先,请考虑查看:

  1. 视窗便携式设备
  2. Windows 便携式设备开发工具包和下载
  3. 便携式设备 COM API 示例

这将是DBT_DEVICEARRIVAL事件。

详细阐述 MSalters 的答案,这个问题的答案可能会对您有所帮助 - C++ Win32 未收到DBT_DEVICEARRIVAL或DBT_DEVICEREMOVECOMPLETE WM_DEVICECHANGE

基本总结如下:

  1. 在处理WM_CREATE消息时,调用 RegisterDeviceNotification ,传递要筛选的 USB 类 GUID 作为第二个参数,或DEVICE_NOTIFY_ALL_INTERFACE_CLASSES作为第三个参数以处理所有 USB 设备类
  2. 在处理WM_DEVICECHANGE消息时,将消息的lParam字段转换为PDEV_BROADCAST_HDR结构,如果其dbch_devicetype字段指示它是DBT_DEVTYP_DEVICEINTERFACE,则进一步将其转换为PDEV_BROADCAST_DEVICEINTERFACE;同时使用wParam字段确定消息是否与DBT_DEVICEARRIVALDBT_DEVICEREMOVECOMPLETEDBT_DEVNODES_CHANGED事件相关联(在最后一种情况下将NULL lParam
  3. 在处理WM_CLOSE消息时,使用步骤 1 返回的句柄调用UnregisterDeviceNotification

除了上面链接的SO问题外,我还参考了检测媒体插入或移除和注册设备通知。 我相信这个问题也参考了它们。

下面是我的主要来源的完整列表,最初基于股票 Windows 桌面向导项目模板,应用程序类型桌面应用程序 (.exe) 选择了预编译标头。 不是我最干净的代码,有点像上述参考文献的弗兰肯斯坦,但它明白了这一点。

// DevDetectDemo.cpp : Defines the entry point for the application.
//
#include "pch.h"
#include "framework.h"
#include "DevDetectDemo.h"
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst;                                // current instance
WCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name
// Forward declarations of functions included in this code module:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);
BOOL                DoRegisterDeviceInterfaceToHwnd(GUID, HWND, HDEVNOTIFY*);
void                Main_OnDeviceChange(HWND hwnd, WPARAM wParam, LPARAM lParam);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);
    // TODO: Place code here.
    // Initialize global strings
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_DEVDETECTDEMO, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);
    // Perform application initialization:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_DEVDETECTDEMO));
    MSG msg;
    // Main message loop:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (int) msg.wParam;
}

//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_DEVDETECTDEMO));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_DEVDETECTDEMO);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    return RegisterClassExW(&wcex);
}
//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // Store instance handle in our global variable
   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
   if (!hWnd)
   {
      return FALSE;
   }
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);
   return TRUE;
}
//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Processes messages for the main window.
//
//  WM_COMMAND  - process the application menu
//  WM_PAINT    - Paint the main window
//  WM_DESTROY  - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static HDEVNOTIFY hDeviceNotify;
    switch (message)
    {
    case WM_CREATE:
        //
        // This is the actual registration., In this example, registration 
        // should happen only once, at application startup when the window
        // is created.
        //
        // If you were using a service, you would put this in your main code 
        // path as part of your service initialization.
        //
        if (!DoRegisterDeviceInterfaceToHwnd(
            GUID_DEVINTERFACE_USB_DEVICE,
            hWnd,
            &hDeviceNotify))
        {
            // Terminate on failure.
            ExitProcess(1);
        }
        break;
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // Parse the menu selections:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: Add any drawing code that uses hdc here...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DEVICECHANGE:
        Main_OnDeviceChange(hWnd, wParam, lParam);
        break;
    case WM_CLOSE:
        UnregisterDeviceNotification(hDeviceNotify);
        DestroyWindow(hWnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;
    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}
BOOL DoRegisterDeviceInterfaceToHwnd(
    IN GUID InterfaceClassGuid,
    IN HWND hWnd,
    OUT HDEVNOTIFY* hDeviceNotify
)
// Routine Description:
//     Registers an HWND for notification of changes in the device interfaces
//     for the specified interface class GUID. 
// Parameters:
//     InterfaceClassGuid - The interface class GUID for the device 
//         interfaces. 
//     hWnd - Window handle to receive notifications.
//     hDeviceNotify - Receives the device notification handle. On failure, 
//         this value is NULL.
// Return Value:
//     If the function succeeds, the return value is TRUE.
//     If the function fails, the return value is FALSE.
// Note:
//     RegisterDeviceNotification also allows a service handle be used,
//     so a similar wrapper function to this one supporting that scenario
//     could be made from this template.
{
    DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
    ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
    NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
    NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    NotificationFilter.dbcc_classguid = InterfaceClassGuid;
    *hDeviceNotify = RegisterDeviceNotification(
        hWnd,                       // events recipient
        &NotificationFilter,        // type of device
        DEVICE_NOTIFY_WINDOW_HANDLE // type of recipient handle; can also be
                                    // DEVICE_NOTIFY_ALL_INTERFACE_CLASSES to
                                    // ignore filter and notify of all devices
    );
    if (NULL == *hDeviceNotify)
    {
        return FALSE;
    }
    return TRUE;
}
/*------------------------------------------------------------------
   Main_OnDeviceChange( hwnd, wParam, lParam )
   Description
      Handles WM_DEVICECHANGE messages sent to the application's
      top-level window.
--------------------------------------------------------------------*/
void Main_OnDeviceChange(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
    TCHAR szMsg[256];
    switch (wParam)
    {
    case DBT_DEVICEARRIVAL:
        // Check whether a device was inserted.
        if (lpdb->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
        {
            PDEV_BROADCAST_DEVICEINTERFACE lpdbd = (PDEV_BROADCAST_DEVICEINTERFACE)lpdb;
            GUID guid = lpdbd->dbcc_classguid;
            StringCchPrintf(szMsg, sizeof(szMsg) / sizeof(szMsg[0]),
                TEXT("Device %s Media with class GUID {%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX} has arrived.n"),
                lpdbd->dbcc_name,
                guid.Data1, guid.Data2, guid.Data3,
                guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
                guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
            MessageBox(hwnd, szMsg, TEXT("WM_DEVICECHANGE"), MB_OK);
        }
        break;
    case DBT_DEVICEREMOVECOMPLETE:
        // Check whether a device was removed.
        if (lpdb->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
        {
            PDEV_BROADCAST_DEVICEINTERFACE lpdbd = (PDEV_BROADCAST_DEVICEINTERFACE)lpdb;
            GUID guid = lpdbd->dbcc_classguid;
            StringCchPrintf(szMsg, sizeof(szMsg) / sizeof(szMsg[0]),
                TEXT("Device %s Media with class GUID {%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX} was removed.n"),
                lpdbd->dbcc_name,
                guid.Data1, guid.Data2, guid.Data3,
                guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
                guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
            MessageBox(hwnd, szMsg, TEXT("WM_DEVICECHANGE"), MB_OK);
        }
        break;
    case DBT_DEVNODES_CHANGED:
        // Check whether a device has been added to or removed from the system.
        StringCchPrintf(szMsg, sizeof(szMsg) / sizeof(szMsg[0]),
            TEXT("Device was was added/removed.n"));
        MessageBox(hwnd, szMsg, TEXT("WM_DEVICECHANGE"), MB_OK);
        break;
    default:
        /*
          Process other WM_DEVICECHANGE notifications for other
          devices or reasons.
        */
        ;
    }
}

随附的标题:

// pch.h: This is a precompiled header file.
// Files listed below are compiled only once, improving build performance for future builds.
// This also affects IntelliSense performance, including code completion and many code browsing features.
// However, files listed here are ALL re-compiled if any one of them is updated between builds.
// Do not add files here that you will be updating frequently as this negates the performance advantage.
#ifndef PCH_H
#define PCH_H
// add headers that you want to pre-compile here
#include "framework.h"
#include <Dbt.h>
#include <strsafe.h>
#include <initguid.h>
#include <Usbiodef.h>
#pragma comment(lib, "user32.lib")
#endif //PCH_H

框架.h

#pragma once
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>

DevDetectDemo.h

#pragma once
#include "resource.h"

我发现对于我的设备,与DEVICE_NOTIFY_ALL_INTERFACE_CLASSES连接和断开连接会导致指示许多类 GUID,具体取决于是否在开发人员选项中启用了 USB 调试。 没有USB调试:

  • {6AC27878-A6FA-4155-BA85-F98F491D4F33} - GUID_DEVINTERFACE_WPD
  • {6BDD1FC6-810F-11D0-BEC7-08002BE2092F} - GUID_DEVINTERFACE_IMAGE
  • {A5DCBF10-6530-11D2-901F-00C04FB951ED} - GUID_DEVINTERFACE_USB_DEVICE
  • {F33FDC04-D1AC-4E8E-9A30-19BBD4B108AE} - PAP 设备接口类

使用USB调试:

  • {A5DCBF10-6530-11D2-901F-00C04FB951ED} - GUID_DEVINTERFACE_USB_DEVICE
  • {DEE824EF-729B-4A0E-9C14-B7117D33A817} - USB设备类
  • {F72FE0D4-CBCB-407D-8814-9ED673D0DD6B} - ANDROID_USB_CLASS_ID

如您所见,两个集合之间唯一通用的 GUID 是 GUID_DEVINTERFACE_USB_DEVICE,这是我在上面的示例中使用的。

相关内容

  • 没有找到相关文章

最新更新