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

Posted

技术标签:

【中文标题】任何用于检测 MTP 设备的 Windows API?【英文标题】:Any Windows API for detecting MTP devices? 【发布时间】:2016-11-20 16:46:42 【问题描述】:

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

【问题讨论】:

我不知道这意味着什么 - 您将如何检测未连接到计算机的设备?您的问题不是特定的 API,而是了解 Windows 如何管理设备。即插即用是自动的。 是的,我想知道用于设备插件检测的 api。每当有任何类型的设备插头时,我都会收到通知。 AFAIK 实际上不可能监听 MTP 设备 但是您通常可以监听设备事件,您只需要在每个事件上过滤掉您想要的事件,然后应该是这样。 MSalters 的回答将为您提供指导 - this 将向您介绍该主题。 如何通过 API 阻止这些 MTP 设备?? ....什么? “通过 API 阻止” 是什么意思? 【参考方案1】:

首先,请考虑查看:

    Windows Portable Devices Windows Portable Devices Development Kits and Downloads Portable Devices COM API Sample

【讨论】:

【参考方案2】:

那就是DBT_DEVICEARRIVAL 事件。

【讨论】:

【参考方案3】:

详细说明 MSalters 的答案,这个问题的答案可能会对您有所帮助 - C++ Win32 Not receiving DBT_DEVICEARRIVAL or DBT_DEVICEREMOVECOMPLETE on WM_DEVICECHANGE

基本总结如下:

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

除了上面链接的 SO 问题外,我还引用了 Detecting Media Insertion or Removal 和 Registering for device notification。我相信这个问题也引用了他们。

以下是我的主要源代码的完整列表,最初基于库存的 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 device interface class

带USB调试:

A5DCBF10-6530-11D2-901F-00C04FB951ED - GUID_DEVINTERFACE_USB_DEVICE DEE824EF-729B-4A0E-9C14-B7117D33A817 - UsbDevice Class F72FE0D4-CBCB-407D-8814-9ED673D0DD6B - android_USB_CLASS_ID

如您所见,这两组之间唯一共同的 GUID 是 GUID_DEVINTERFACE_USB_DEVICE,这是我在上面的示例中使用的。

【讨论】:

targetver.h 和 resource.h 是什么? @DonaldDuck 不是这些方面的专家;它们通常与项目模板一起生成。就我目前的理解而言,targetver.h 用于定义每个目标规范(例如操作系统版本)正在使用的 Win32 API 定义的子集; resource.h 定义了应用程序的资源标识符,例如图标或本地化字符串,您可能会在随附的 resource.rc 编译资源文件中找到这些内容。

以上是关于任何用于检测 MTP 设备的 Windows API?的主要内容,如果未能解决你的问题,请参考以下文章

解决Windows无法加载MTP设备

如何管理 MTP 便携式设备上的文件?

用java访问mtp设备

Optec新品首发| MTP/MPO衰减回路器

MTP MPO MINI回路器

mtp模式下无法数据恢复,如何调整成为可移动存储器?