I'm looking for detection of MTP devices through Windows API, not just the enumeration of already connected devices.
For a start, consider looking at:
Elaborating on MSalters' answer, you might be helped by the answer to this question - C++ Win32 Not receiving DBT_DEVICEARRIVAL or DBT_DEVICEREMOVECOMPLETE on WM_DEVICECHANGE
The basic summary is as follows:
- In handling the
WM_CREATE
message, callRegisterDeviceNotification
, either passing a USB class GUID on which to filter as the second parameter, orDEVICE_NOTIFY_ALL_INTERFACE_CLASSES
as the third parameter to work with all USB device classes - In handling the
WM_DEVICECHANGE
message, cast the message'slParam
field to aPDEV_BROADCAST_HDR
struct, and if itsdbch_devicetype
field indicates it is aDBT_DEVTYP_DEVICEINTERFACE
, further cast it to aPDEV_BROADCAST_DEVICEINTERFACE
; meanwhile use thewParam
field to determine whether the message is associated with aDBT_DEVICEARRIVAL
,DBT_DEVICEREMOVECOMPLETE
, orDBT_DEVNODES_CHANGED
event (lParam
will beNULL
in the last case) - In handling the
WM_CLOSE
message, callUnregisterDeviceNotification
with the handle returned from step 1.
In addition to the above linked SO question, I have also referenced Detecting Media Insertion or Removal and Registering for device notification. I believe the question references them as well.
Below is a full listing of my main source, initially based on the stock Windows Desktop Wizard project template, Application type Desktop Application (.exe) with Precompiled header selected. Not my cleanest code, kind of a Frankenstein of the above references, but it gets the point across.
// 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.
*/
;
}
}
Accompanying headers:
// 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
framework.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"
I found that for my device, connecting and disconnecting with DEVICE_NOTIFY_ALL_INTERFACE_CLASSES
resulted in many class GUIDs being indicated, different based on whether USB debugging was enabled in Developer options. Without USB debugging:
- {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
With USB debugging:
- {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
As you can see, the only common GUID between the two sets is GUID_DEVINTERFACE_USB_DEVICE, which is what I have used in the above sample.
© 2022 - 2024 — McMap. All rights reserved.