Any Windows API for detecting MTP devices?
Asked Answered
D

3

1

I'm looking for detection of MTP devices through Windows API, not just the enumeration of already connected devices.

Deoxygenate answered 18/7, 2016 at 7:12 Comment(7)
I have no idea what this means - how would you detect a device that's not connected to your computer? Your problem isn't a particular API, it's understanding how Windows manages devices. Plug&Play is automatic.Donia
yeah I want to know the api for device plugin detection. Whenever any type of device plug I would get notify about that.Deoxygenate
AFAIK it isnt actually possible to listen for MTP-devices but you can listen for device-events in general, you just need to filter out your desired ones on each event and that should be it. The answer from MSalters will guide you - this will introduce you to the topic in general.Welby
What about blocking these MTP-devices through API??Deoxygenate
.... what? What is "blocking through API" even supposed to mean?Welby
any API for blocking mobile devices?Deoxygenate
Who are you talking to? What are you trying to say? Please let someone help you translate, your current words dont make much sense ...Welby
D
0

That would be the DBT_DEVICEARRIVAL event.

Donia answered 18/7, 2016 at 10:58 Comment(0)
O
0

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:

  1. In handling the WM_CREATE message, call RegisterDeviceNotification, either passing a USB class GUID on which to filter as the second parameter, or DEVICE_NOTIFY_ALL_INTERFACE_CLASSES as the third parameter to work with all USB device classes
  2. In handling the WM_DEVICECHANGE message, cast the message's lParam field to a PDEV_BROADCAST_HDR struct, and if its dbch_devicetype field indicates it is a DBT_DEVTYP_DEVICEINTERFACE, further cast it to a PDEV_BROADCAST_DEVICEINTERFACE; meanwhile use the wParam field to determine whether the message is associated with a DBT_DEVICEARRIVAL, DBT_DEVICEREMOVECOMPLETE, or DBT_DEVNODES_CHANGED event (lParam will be NULL in the last case)
  3. In handling the WM_CLOSE message, call UnregisterDeviceNotification 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:

With USB debugging:

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.

Overripe answered 19/8, 2021 at 16:47 Comment(2)
What's targetver.h and resource.h?Insidious
@DonaldDuck Not an expert in these; they generally come generated with the project template. As best I currently understand them, targetver.h serves to define what subset of the Win32 API definitions are being used per target specifications (e.g. OS version); resource.h defines the application's resource identifiers, so things like the icon or localized strings, what you might find in the accompanying resource.rc compiled resource file.Overripe

© 2022 - 2024 — McMap. All rights reserved.