任何用于检测 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_DEVICEARRIVAL
、DBT_DEVICEREMOVECOMPLETE
或DBT_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调试:
带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?的主要内容,如果未能解决你的问题,请参考以下文章