0

I have a program which detects usb devices on inserting and removing. On inserting a new usb drive, I get GUID of the inserted drive like this \\?\USBSTOR#Disk&Ven_JetFlash&Prod_Transcend_8GB&Rev_1100#546IYBDAPBE1075Q&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}. I want to get the the drive paths (example E:,F:) for the drive form the GUID.\

#define HID_CLASSGUID { 0x53f5630d, 0xb6bf, 0x11d0,{ 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b }}
static const GUID GuidDevInterfaceList[] = {    
    { 0x53f5630d, 0xb6bf, 0x11d0,{ 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b } }
};

LRESULT message_handler(HWND__* hwnd, UINT uint, WPARAM wparam, LPARAM lparam)
{
    
    switch (uint)
    {
    case WM_NCCREATE: // before window creation
        return true;
        break;

    case WM_CREATE: // the actual creation of the window
    {
        // you can get your creation params here..like GUID..
        LPCREATESTRUCT params = (LPCREATESTRUCT)lparam;
        GUID InterfaceClassGuid = *((GUID*)params->lpCreateParams);
        DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;    
        ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
        NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
        NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
        for (int i = 0; i < sizeof(GuidDevInterfaceList); i++)
        {
            NotificationFilter.dbcc_classguid = GuidDevInterfaceList[i];


            HDEVNOTIFY dev_notify = RegisterDeviceNotification(hwnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
            if (dev_notify == NULL) {     // Handle the error by returning correct error in LRESULT format and remove throw...   
                std::cout << "Hell" << std::endl;
            }
        }
    }
    break;

    case WM_DEVICECHANGE:
        {
    
            PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lparam;
            PDEV_BROADCAST_DEVICEINTERFACE_W lpdbv = (PDEV_BROADCAST_DEVICEINTERFACE_W)lpdb;        
            std::wstring path;
            
            if (lpdb->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
            {
                
                switch (wparam)
                {
    
                case DBT_DEVICEARRIVAL: {   //A device or piece of media has been inserted and is now available.
    
                    std::wcout << L"New device connected:\n";
                    path = std::wstring(lpdbv->dbcc_name);//Gives the GUID of inserted drive
                    break;
                }
                case DBT_DEVICEREMOVECOMPLETE:{
    
                    std::wstring pathexit;
    
                    pathexit = std::wstring(lpdbv->dbcc_name);//Gives the GUID of removed drive
                    
                    break;
                }
            }
            break;
        }
}

void UsbListener::RegisterListener()
{
    HWND hWnd = NULL;
    WNDCLASSEXW wx;
    ZeroMemory(&wx, sizeof(wx));
    wx.cbSize = sizeof(WNDCLASSEX);
    wx.lpfnWndProc = reinterpret_cast<WNDPROC>(message_handler);
    wx.hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(0));
    wx.style = CS_HREDRAW | CS_VREDRAW;
    wx.hInstance = GetModuleHandle(0);
    wx.hbrBackground = (HBRUSH)(COLOR_WINDOW);
    wx.lpszClassName = CLS_NAME;
    GUID guid = HID_CLASSGUID;
    if (RegisterClassExW(&wx))
    {
        hWnd = CreateWindowW(
            CLS_NAME, L"DeviceNotificationWindow", WS_ICONIC, 0, 0, CW_USEDEFAULT, 0, HWND_MESSAGE, NULL, GetModuleHandle(0), (void*)&guid
        );
    }
    if (hWnd == NULL)
    {
        throw std::runtime_error("Could not create message window!");
    }
    std::cout <<std::endl<< "Listening..." << std::endl;
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }   
}

I call the RegisterListener() method to start listening for usb change How do I get the drive path from GUID? (drive path example e:, f:, g:) . Note: I will only be inserting and removing usb flash drives and not devices like printer, phone etc. Edit: I do not want to get drive name/label. I want the path like drive e: or f:

MyCopy
  • 155
  • 1
  • 8
  • Does this answer your question? [Getting Volume Name from Device Path in USB devices](https://stackoverflow.com/questions/32716081/getting-volume-name-from-device-path-in-usb-devices) – Simon Mourier Jul 03 '20 at 09:54
  • `{53f56307-b6bf-11d0-94f2-00a0c91efb8b}` this is `GUID_DEVINTERFACE_DISK` - you listen for disk arrival-removal, but not for volume. for volume you need use `GUID_DEVINTERFACE_VOLUME`. on disk - can be multiple volumes - so not exist single conversion - disk to volume. so you need disk or volume ? and if volume(s) - not better register `GUID_DEVINTERFACE_VOLUME` ? then why you want exactly dos volume path like *X:* ? this exist sense only for UI show. and for this need use `IOCTL_MOUNTDEV_QUERY_DEVICE_NAME` first and then `IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH` – RbMm Jul 03 '20 at 11:45
  • I need dos name so that I can send it through socket for UI side. I want to get list of all the volume(s) associated with a flash drive when it is inserted into usb – MyCopy Jul 03 '20 at 12:09
  • This is my first winapi so I do not know exactly how to do this. Hope your suggestion GUID_DEVINTERFACE_VOLUME helps – MyCopy Jul 03 '20 at 12:11
  • I think you may be looking for https://stackoverflow.com/a/272005/15416. If not, just search StackOverflow for `GUID_DEVINTERFACE_VOLUME`. The problem with `GUID_DEVINTERFACE_DISK` is that a disk may contain 0, 1 or more volumes, so you can't simply map it to a single letter. – MSalters Jul 03 '20 at 13:16
  • @RbMm After using `GUID_DEVINTERFACE_VOLUME`, I get GUID like this `\\?\STORAGE#Volume#_??_USBSTOR#Disk&Ven_JetFlash&Prod_Transcend_8GB&Rev_1100#546IYBDAPBE1075Q&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}#{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}` . How do I get the dos names of the drives(example: E:, F:). – MyCopy Jul 08 '20 at 07:30
  • open volume, send `IOCTL_MOUNTDEV_QUERY_DEVICE_NAME` to it and then `IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH` to mount manager. but for what you want dos path ? – RbMm Jul 08 '20 at 07:43
  • To send UI the list of available usb removable volume in the machine – MyCopy Jul 08 '20 at 10:33
  • @MyCopy Does the answer work for you? – Rita Han Jul 10 '20 at 09:42
  • @Rita Han. Thanks for the answer. Give me a day. I will verify and accept if it works. – MyCopy Jul 10 '20 at 11:45
  • @MyCopy Have you tried that? – Rita Han Jul 13 '20 at 01:50
  • @RitaHan-MSFT I have tried that. It doesnot work case 'DBT_DEVICEREMOVECOMPLETE: if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)' does not trigger as 'dbch_devicetype' never receives volume. May be I registered it wrong. I will edit and include include the registration method. Please check – MyCopy Jul 13 '20 at 05:08
  • @MyCopy The code showed in my answer is all you need. It works for me without registering. *The DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE events are automatically broadcast to all top-level windows for port devices. Therefore, it is not necessary to call RegisterDeviceNotification for ports* – Rita Han Jul 13 '20 at 08:20
  • @MyCopym It seems not working for message-only window. Do you need a [message-only window](https://learn.microsoft.com/en-us/windows/win32/winmsg/window-features#message-only-windows)? Or a common window is fine for you? – Rita Han Jul 14 '20 at 05:24
  • @RitaHan-MSFT Common window is also fine – MyCopy Jul 14 '20 at 15:56

1 Answers1

0

"I want the path like drive e: or f:"

Any application with a top-level window can receive basic notifications by processing the WM_DEVICECHANGE message.

The DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE events are automatically broadcast to all top-level windows for port devices.

Volume notifications are also broadcast to top-level windows, so the function fails if dbch_devicetype is DBT_DEVTYP_VOLUME.

A message-only window (specify the HWND_MESSAGE) enables you to send and receive messages. It is not visible, has no z-order, cannot be enumerated, and does not receive broadcast messages. The window simply dispatches messages.

Refer to "RegisterDeviceNotification" and "Message-Only Windows".

So there are some limitations to a message-only window. However, for a general top-level window, it is easy to achieve. Refer to "Walkthrough: Create a traditional Windows Desktop application (C++)" for how to create a top-level window.

The following is complete sample based on Windows Desktop Application template in Visual Studio.

#include <windows.h>
#include <Dbt.h>
#include <string>
#include <strsafe.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);

void Main_OnDeviceChange(HWND hwnd, WPARAM wParam, LPARAM lParam);
char FirstDriveFromMask(ULONG unitmask);  //prototype

/*------------------------------------------------------------------
   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[80];

    switch (wParam)
    {
    case DBT_DEVICEARRIVAL:
        if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
        {
            PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
            StringCchPrintf(szMsg, sizeof(szMsg) / sizeof(szMsg[0]),
                TEXT("Drive %c: has arrived.\n"),
                FirstDriveFromMask(lpdbv->dbcv_unitmask));
            MessageBox(NULL, szMsg, L"", MB_OK);
        }
        break;

    case DBT_DEVICEREMOVECOMPLETE:
        if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
        {
            PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
            StringCchPrintf(szMsg, sizeof(szMsg) / sizeof(szMsg[0]),
                TEXT("Drive %c: has been removed.\n"),
                FirstDriveFromMask(lpdbv->dbcv_unitmask));
            MessageBox(NULL, szMsg, L"", MB_OK);
        }
        break;
    }
}

char FirstDriveFromMask(ULONG unitmask)
{
    char i;

    for (i = 0; i < 26; ++i)
    {
        if (unitmask & 0x1)
            break;
        unitmask = unitmask >> 1;
    }
    i += 'A';
    char name[] = {'\n', i, ':', '\0'};
    OutputDebugStringA(name);
    return i;
}

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_SO20200709, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // Perform application initialization:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_SO20200709));

    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_SO20200709));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_SO20200709);
    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)
{
    switch (message)
    {
    case WM_DEVICECHANGE:
    {
        Main_OnDeviceChange(hWnd, 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_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
Rita Han
  • 9,574
  • 1
  • 11
  • 24