15

I want to detect insertion/removal of a specific (Custom) USB device through a C++ application which runs in background and has no GUI.

I have seen lot of questions and their solutions RegisterDeviceNotification also sample code on MSDN

But these all application has Some Window/Form/GUI. My Application doesn't have any. How can I use this in my application?

My last option would be to create an invisible window... But is there any other way out??

Swanand
  • 4,027
  • 10
  • 41
  • 69
  • Do you want your code to be portable across various OSes? – Luc Touraille Apr 25 '13 at 12:29
  • Only Windows... But Yes, Windows XP to Windows 7 (or maybe Win 8 too!) – Swanand Apr 25 '13 at 12:31
  • 1
    Create invisible window and handle notification message in its window procedure. You need to make this in a separate thread containing message loop. – Alex F Apr 25 '13 at 12:34
  • look here http://msdn.microsoft.com/en-us/library/windows/desktop/ms644928%28v=vs.85%29.aspx as it states: "...messages sent to windows..." – kassak Apr 25 '13 at 12:39
  • It does sound like you actually want a "Service", rather than an application, in which case you can pass a "Service status handle" instead of a "HWND" to the`RegisterDeviceNotification` – Mats Petersson Apr 25 '13 at 12:41
  • Isn't there any way out without creating invisible window?? That was my last option! – Swanand Apr 25 '13 at 12:41
  • @Swanand I think the Window will only receive the event if is in focus. I'm going through the same problem right now. There doesn't seem to be an elegant solution. – Dan G Oct 12 '16 at 17:51

5 Answers5

5

Create a message-only window. Despite the name, it's really only a message queue.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • @DavidHeffernan: depends on the `dbch_devicetype`, but the messages for which you need `RegisterDeviceNotification` aren't broadcast (that's the whole point of the function, after all). – MSalters Feb 25 '14 at 15:41
  • @DavidHeffernan: Not the broadcast ones, no. But for a custom device, I don't expect broadcast messages anyway. They use pub/sub instead of broadcast. – MSalters Feb 25 '14 at 15:53
  • OK, I'm not sure of myself any more. Using a normal window appeared to solve the question here (http://stackoverflow.com/questions/22016259/struggling-with-an-empty-windows-message-queue-in-a-console-application) but I'm not sure why. I think I will delete my comments here because I'm on shaky ground. – David Heffernan Feb 25 '14 at 16:02
  • 1
    Message only windows don't receive broadcast messages. - [link](https://msdn.microsoft.com/en-us/library/windows/desktop/ms632599(v=vs.85).aspx#message_only) – Dan G Oct 12 '16 at 17:45
5

External USB Storage Device Detector


This simple C++ code can detect any pen drive, memory card & external hard drive-

#include <stdio.h>
#include <time.h>
#include <windows.h>
#include <string>

using namespace std;

string allDrives;

char getRemovableDisk();

int main(void) {
    char driveLetter = getRemovableDisk();
    while (1) {
        driveLetter = getRemovableDisk();
        if (driveLetter != '0') {
            printf("%c \n", driveLetter);
        }

        Sleep(1000);
    }

    return 0;
}

char getRemovableDisk() {
    char drive = '0';

    char szLogicalDrives[MAX_PATH];
    DWORD dwResult = GetLogicalDriveStrings(MAX_PATH, szLogicalDrives);

    string currentDrives = "";

    //cout << dwResult << endl;
    for (int i = 0; i < dwResult; i++) {
        if (szLogicalDrives[i] > 64 && szLogicalDrives[i] < 90) {
            currentDrives.append(1, szLogicalDrives[i]);
            if (allDrives.find(szLogicalDrives[i]) > 100) {
                drive = szLogicalDrives[i];
            }

        }
    }

    allDrives = currentDrives;

    return drive;
}

PS: This code snippet can detect the insertion of one new USB storage device for Windows OS. If multiple devices are inserted simultaneously in 1 second then only one will be detected. But, of course, you can achieve multiple detections too with a little code change. :)

Minhas Kamal
  • 20,752
  • 7
  • 62
  • 64
4
#define ANSI
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT   0x0501

#include <windows.h>
#include <winuser.h>
#include <initguid.h>
#include <usbiodef.h>
#include <Dbt.h>

#include <string>
#include <iostream>
#include <stdexcept>

#define HID_CLASSGUID {0x4d1e55b2, 0xf16f, 0x11cf,{ 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30}}
#define CLS_NAME "DUMMY_CLASS"
#define HWND_MESSAGE     ((HWND)-3)

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;
        memcpy(&(NotificationFilter.dbcc_classguid),&(GUID_DEVINTERFACE_USB_DEVICE), sizeof(struct _GUID));
        HDEVNOTIFY dev_notify = RegisterDeviceNotification(hwnd, &NotificationFilter,
                                                           DEVICE_NOTIFY_WINDOW_HANDLE);
        if(dev_notify == NULL)
        {
            throw std::runtime_error("Could not register for devicenotifications!");
        }
        break;
    }

    case WM_DEVICECHANGE:
    {
        PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR) lparam;
        PDEV_BROADCAST_DEVICEINTERFACE lpdbv = (PDEV_BROADCAST_DEVICEINTERFACE) lpdb;
        std::string path;
        if (lpdb->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
        {
            path = std::string(lpdbv->dbcc_name);
            switch (wparam)
            {
            case DBT_DEVICEARRIVAL:
                std::cout << "new device connected: " << path << "\n";
                break;

            case DBT_DEVICEREMOVECOMPLETE:
                std::cout << "device disconnected: " << path << "\n";
                break;
            }
        }
        break;
    }

    }
    return 0L;
}

int main(int argc, char* argv[])
{
    HWND hWnd = NULL;
    WNDCLASSEX 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 (RegisterClassEx(&wx))
    {
        hWnd = CreateWindow(CLS_NAME, "DevNotifWnd", 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 << "waiting for new devices..\n";

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}

This simple program creates a non-visible window and its message handler also receives notifications for 'RegisterDeviceNotification'.

Please make sure to link your program against user32.lib, e.g. /link user32.lib.

(Updated based on below comments from @DerekLu and @Michael Sohnen)

formiaczek
  • 385
  • 3
  • 7
  • 1
    It doesn't working now. It shows only 'waiting for new devices...' – Eric Apr 22 '21 at 08:21
  • 1
    Please remind users of this code to link user32.lib; Use: "cl.exe code_snippet.cpp /link user32.lib" – Michael Sohnen Jul 15 '21 at 18:19
  • 1
    @Eric We need to change `NotificationFilter.dbcc_classguid = InterfaceClassGuid` to `memcpy(&(NotificationFilter.dbcc_classguid),&(GUID_DEVINTERFACE_USB_DEVICE), sizeof(struct _GUID));` . And include `initguid.h` and `usbiodef.h` FYI: https://github.com/microsoft/Windows-driver-samples/blob/9f03207ae1e8df83325f067de84494ae55ab5e97/usb/usbview/uvcview.c#L992 – DerekLu Mar 10 '22 at 07:53
  • Thanks MichaelSohnen and DerekLu: I updated the original reply using your feedback! – formiaczek Mar 10 '22 at 11:22
2

This c++ code detect INSERTION and REMOVAL both of USB Storage devices.

This also detect Multiple insertion and removal of USB devices at same time.

c++ code: Tested in VISUAL STUDIO 2015

You can also check for other types of devices for removal and insertion. Just fill passed char array to other types of devices in if else of the code in function getUSBStorageDeviceList()

    #include "stdafx.h"
    #include <stdio.h>
    #include <time.h>
    #include <windows.h>
    #include <string>
    #include<iostream>

    using namespace std;

    #define MAX_LETTER 26
    char PREV_DRIVE_LIST[MAX_LETTER];
    char NEW_DRIVE_LIST[MAX_LETTER];

    /* To GET DRIVE LIST in char ARRAY */
    void getUSBStorageDeviceList(char drive[]) {

        int count = 0;

        char szLogicalDrives[MAX_PATH];
        size_t size = strlen(szLogicalDrives) + 1;
        wchar_t* text = new wchar_t[size];

        size_t outSize;
        mbstowcs_s(&outSize, text, size, szLogicalDrives, size - 1);

        DWORD dwResult = GetLogicalDriveStrings(MAX_PATH, text); // text = szLogicalDrives
        WCHAR* szSingleDrive = text;

        while (*szSingleDrive)
        {
            UINT nDriveType = GetDriveType(szSingleDrive);

        //  printf("\nFUNC: getRemovableDisk, Drive Name%d= %s", ++count, szSingleDrive);

            if (nDriveType == DRIVE_UNKNOWN) {
            //  cout << "\nDrive type : Unknown: The drive type cannot be determined." << endl;
            }
            else if (nDriveType == DRIVE_NO_ROOT_DIR) {
            //  cout << "\nDrive type : Invalid Root Directory Media: The root path is invalid." << endl;
            }
            else if (nDriveType == DRIVE_REMOVABLE) {
            //  cout << "\nDrive type :  Removable Media:" << endl;
                char letter = szSingleDrive[0];
                drive[letter - 65] = letter;
            }
            else if (nDriveType == DRIVE_FIXED) {
                //cout << "\nDrive type : Fixed Media: " << endl;
            }
            else if (nDriveType == DRIVE_REMOTE) {
                //cout << "\nDrive type : Remote Media: The drive is a remote (network) drive.." << endl;
            }
            else if (nDriveType == DRIVE_CDROM) {
                //cout << "\nDrive type : CD ROM:   The drive is a CD-ROM drive." << endl;
            }
            else if (nDriveType == DRIVE_RAMDISK) {
                //cout << "\nDrive type : RAM Disk: The drive is a RAM disk." << endl;
            }

            szSingleDrive += wcslen(szSingleDrive) + 1; // next drive 
        }
    }

    int main(void) {

        int count = 0;
        for (int i = 0; i < MAX_LETTER; i++) {
            PREV_DRIVE_LIST[i] = '0';
            NEW_DRIVE_LIST[i] = '0';
        }
        // initial drive list which is already attached 
        getUSBStorageDeviceList(PREV_DRIVE_LIST);

        while (1) {

            getUSBStorageDeviceList(NEW_DRIVE_LIST);
            count = 1;

            /* Check for insertion and removabal*/

            for (int i = 0; i < MAX_LETTER; i++) {
                // check for new drive
                if ((NEW_DRIVE_LIST[i] >= 65 && NEW_DRIVE_LIST[i] <= 89) && (PREV_DRIVE_LIST[i] == '0')) {

                    printf("\nNew Device Inserted%d : %c", count++, NEW_DRIVE_LIST[i]);
                    PREV_DRIVE_LIST[i] = NEW_DRIVE_LIST[i];
                }
            }
                // fill ALl zero 
                for (int i = 0; i < MAX_LETTER; i++) {
                    NEW_DRIVE_LIST[i] = '0';
                }
                // update NEW drive list
                getUSBStorageDeviceList(NEW_DRIVE_LIST);

                for (int i = 0; i < MAX_LETTER; i++) {
                    // check for removed drive
                    if ((PREV_DRIVE_LIST[i] >= 65 && PREV_DRIVE_LIST[i] <= 89) && (NEW_DRIVE_LIST[i] == '0')) {
                        printf("\nDevice Removed%d : %c", count++, PREV_DRIVE_LIST[i]);
                        PREV_DRIVE_LIST[i] = NEW_DRIVE_LIST[i];
                    }
            }
                Sleep(500);
        }

        return 0;
    }
jayprakash
  • 41
  • 3
0

Create a service first and in RegisterDeviceNotification, give the handle of that service instead of a window handle.

Also adjust the third parameter of RegisterDeviceNotification accordingly.

Behlül
  • 3,412
  • 2
  • 29
  • 46