2015-03-11 11 views
8

Ho lavorato per rilevare l'inserimento/rimozione USB. Ho implementato il codice utilizzando CreateWindowEx(), passando un WNCLASSEX che con la mia finestra elabora il callback. Inserendo e rimuovendo il mio usb, ricevo correttamente il messaggio WM_DEVICECHANGE, ma wParam è sempre impostato su DBT_DEVNODES_CHANGED.C++ Win32 Non riceve DBT_DEVICEARRIVAL o DBT_DEVICEREMOVECOMPLETE su WM_DEVICECHANGE

Non ottengo mai DBT_DEVICEARRIVAL o DBT_DEVICEREMOVECOMPLETE. Ho usato quello che sto ottenendo, ma ho davvero bisogno di essere in grado di distinguere tra l'arrivo e la rimozione del dispositivo, in modo da poter intraprendere azioni diverse a seconda di ciò che ricevo.

In questo momento, devo mettere un timer dopo aver ricevuto DBT_DEVNODES_CHANGED, e quindi testare per vedere se ci sono nuovi rimovibili sul sistema, o se qualcuno nel mio elenco non ci sono più. Sono sicuro che non è giusto, quindi ho pensato di chiederlo. Preferisco di gran lunga liberarmi del timer e semplicemente ricevere questi due messaggi. Ciò aiuterebbe molto in quello che dovevo fare. Eventuali suggerimenti?

Ecco il codice che ho per la registrazione del callback, così come il callback per sé:

NOTA: la funzione di codice aggiornato per mostrare GUID reale e la definizione della DoRegisterDeviceInterfaceToHwnd(): 2015/03/12 .):

GUID WceusbshGUID = { 0x25dbce51, 0x6c8f, 0x4a72, 0x8a,0x6d,0xb5,0x4c,0x2b,0x4f,0xc8,0x35 }; 
//GUID WusbrawGUID = {0xa5dcbf10, 0x6530, 0x11d2, 0x90, 0x1f, 0x00, 0xc0, 0x4f, 0xb9, 0x51, 0xed }; 
//GUID WusbGUID = {0x88BAE032, 0x5A81, 0x49f0, 0xBC, 0x3D, 0xA4, 0xFF, 0x13, 0x82, 0x16, 0xD6 }; 

INT_PTR WINAPI WinProcCallback(HWND __hWnd, UINT message, WPARAM wParam, LPARAM lParam); 
BOOL DoRegisterDeviceInterfaceToHwnd(GUID InterfaceClassGuid, HWND __hWnd, HDEVNOTIFY *hDeviceNotify); 

bool UsbController::startNotifyUsbAddedRemoved(QString &errmsg) 
{ 
    WNDCLASSEX wndClass; 

    wndClass.cbSize = sizeof(wndClass); 
    wndClass.style = 0; 
    wndClass.hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(0)); 
    wndClass.lpfnWndProc = reinterpret_cast<WNDPROC>(WinProcCallback); 
    wndClass.cbClsExtra = 0; 
    wndClass.cbWndExtra = 0; 
    wndClass.hIcon = LoadIcon(0, IDI_APPLICATION); 
    wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); 
    wndClass.hCursor = LoadCursor(0, IDC_ARROW); 
    wndClass.lpszClassName = WND_CLASS_NAME; 
    wndClass.lpszMenuName = NULL; 
    wndClass.hIconSm = LoadIcon(0, IDI_APPLICATION); 

    if (!RegisterClassEx(&wndClass)) 
    { 
     FormatErrorMsg("RegisterClassEx: ", errmsg); 
     return false; 
    } 

    HINSTANCE hInstance = (HINSTANCE)::GetModuleHandle(NULL); 
    __hWnd = CreateWindowEx(
        WS_EX_CLIENTEDGE | WS_EX_APPWINDOW, 
        WND_CLASS_NAME, 
        WND_APP_NAME, 
        WS_OVERLAPPEDWINDOW, // style 
        CW_USEDEFAULT, 0, 
        0, 0, 
        NULL, NULL, 
        hInstance, 
        NULL); 

    if (__hWnd == NULL) 
    { 
     FormatErrorMsg("CreateWindowEx: ", errmsg); 
     return false; 
    } 

    return true; 
} 

INT_PTR WINAPI WinProcCallback(HWND __hWnd, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    LRESULT lRet = 1; 
    static HDEVNOTIFY hDeviceNotify; 
    static HWND hEditWnd; 
    static ULONGLONG msgCount = 0; 

    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(WceusbshGUID, __hWnd, &hDeviceNotify)) 
     { 
      // Terminate on failure. 
      //ErrorHandler(TEXT("DoRegisterDeviceInterfaceToHwnd")); 
      ExitProcess(1); 
     } 

     // 
     // Make the child window for output. 
     // 
     hEditWnd = CreateWindow(TEXT("EDIT"),// predefined class 
           NULL,  // no window title 
           WS_CHILD | WS_VISIBLE | WS_VSCROLL | 
           ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL, 
           0, 0, 0, 0, // set size in WM_SIZE message 
           __hWnd,  // parent window 
           (HMENU)1, // edit control ID 
           (HINSTANCE) GetWindowLong(__hWnd, GWL_HINSTANCE), 
           NULL);  // pointer not needed 

     if (hEditWnd == NULL) 
     { 
      // Terminate on failure. 
      ExitProcess(1); 
     } 
     // Add text to the window. 
     SendMessage(hEditWnd, WM_SETTEXT, 0, 
      (LPARAM)TEXT("Registered for USB device notification...\n")); 

     break; 

    case WM_SETFOCUS: 
     SetFocus(hEditWnd); 

     break; 

    case WM_SIZE: 
     // Make the edit control the size of the window's client area. 
     MoveWindow(hEditWnd, 
        0, 0,     // starting x- and y-coordinates 
        LOWORD(lParam),  // width of client area 
        HIWORD(lParam),  // height of client area 
        TRUE);     // repaint window 

     break; 

    case WM_DEVICECHANGE: 
     { 
      // 
      // This is the actual message from the interface via Windows messaging. 
      // This code includes some additional decoding for this particular device type 
      // and some common validation checks. 
      // 
      // Note that not all devices utilize these optional parameters in the same 
      // way. Refer to the extended information for your particular device type 
      // specified by your GUID. 
      // 

      // Output some messages to the window. 
      UsbController *pusbctl; 
      switch (wParam) 
      { 
      case DBT_DEVICEARRIVAL: 
       msgCount++; 
       pusbctl = UsbController::instance(); 
       pusbctl->signalDeviceArrival(); 
       break; 
      case DBT_DEVICEREMOVECOMPLETE: 
       msgCount++; 
       pusbctl = UsbController::instance(); 
       pusbctl->signalDeviceRemoval(); 
       break; 
      case DBT_DEVNODES_CHANGED: 
       msgCount++; 
       pusbctl = UsbController::instance(); 
       pusbctl->signalDeviceAddedRemoved(); 
       break; 
      default: 
       msgCount++; 
       break; 
      } 
     } 
     break; 

    default: 
     // Send all other messages on to the default windows handler. 
     lRet = DefWindowProc(__hWnd, message, wParam, lParam); 
     break; 
    } 

    return lRet; 
} 

BOOL DoRegisterDeviceInterfaceToHwnd(GUID InterfaceClassGuid, HWND __hWnd, HDEVNOTIFY *hDeviceNotify) 
{ 
    DEV_BROADCAST_DEVICEINTERFACE NotificationFilter; 

    ZeroMemory(&NotificationFilter, sizeof(NotificationFilter)); 
    NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); 
    NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; 
    //NotificationFilter.dbcc_devicetype = DEVICE_NOTIFY_ALL_INTERFACE_CLASSES; 
    NotificationFilter.dbcc_classguid = InterfaceClassGuid; 

    *hDeviceNotify = RegisterDeviceNotification(
     __hWnd,      // events recipient 
     &NotificationFilter,  // type of device 
     DEVICE_NOTIFY_WINDOW_HANDLE // type of recipient handle 
     ); 

    if (NULL == *hDeviceNotify) 
    { 
     return FALSE; 
    } 

    return TRUE; 
} 
+0

Hai visto https://msdn.microsoft.com/en-us/library/windows/desktop/aa363215(v=vs.85).aspx?Dice che in realtà non è necessario registrarsi per ricevere notifiche di arrivo dei media. Mi chiedo se il fatto che tu ** stia ** registrando potrebbe interferire in qualche modo. –

+1

Hmm, la mia sfera di cristallo dice che stai facendo questo il modo del programmatore, semplicemente strappando il dispositivo senza passare attraverso il "Safely Remove Hardware" spiel. E poi sperare che Windows possa indovinare cosa potrebbe essere successo. Beh, lo ha fatto, te ne ha parlato. –

+0

In realtà, ho sicuramente eseguito il processo "Rimozione sicura dell'hardware". La notifica che ottengo lì ha un valore di 7, che è DBT_DEVNODES_CHANGED. Ricevo un numero di messaggi provenienti da quell'unica azione (non avendo ancora estratto il dispositivo dallo slot), che hanno tutti un valore di 7. Ovviamente, quando tiro il dispositivo, ottengo lo stesso valore. L'inserimento del dispositivo ha gli stessi esatti risultati. Ricevo un numero di messaggi e tutti hanno il valore 7 per DBT_DEVNODES_CHANGED. – bmahf

risposta

10

Se leggete la documentazione di MSDN, si dice:

Detecting Media Insertion or Removal

Windows invia a tutte le finestre di primo livello un insieme di messaggi WM_DEVICECHANGE di default quando vengono aggiunti nuovi dispositivi o supporti (come un CD o DVD) e quando vengono rimossi dispositivi o supporti esistenti. Non è necessario registrarsi per ricevere questi messaggi predefiniti. Vedere la sezione Note in RegisterDeviceNotification per i dettagli sui messaggi inviati per impostazione predefinita.

RegisterDeviceNotification function

Qualsiasi applicazione con una finestra di primo livello in grado di ricevere le notifiche di base dalla elaborazione del messaggio WM_DEVICECHANGE. Le applicazioni possono utilizzare la funzione RegisterDeviceNotification per registrarsi per ricevere le notifiche di dispositivo .
...
Gli eventi DBT_DEVICEARRIVAL e DBT_DEVICEREMOVECOMPLETE sono trasmessi automaticamente a tutte le finestre di livello superiore per i dispositivi di porta. Pertanto, non è necessario chiamare RegisterDeviceNotification per le porte e la funzione non riesce se il membro dbch_devicetype è DBT_DEVTYP_PORT.

DEV_BROADCAST_HDR structure

DBT_DEVTYP_PORT
0x00000003

dispositivo porta (seriale o parallelo).Questa struttura è una struttura DEV_BROADCAST_PORT.

Un dispositivo USB non è una porta seriale/parallela. È invece un'interfaccia dispositivo (DBT_DEVTYP_DEVICEINTERFACE). E DBT_DEVICEARRIVAL/DBT_DEVICEREMOVECOMPLETE non vengono inviati per i dispositivi DBT_DEVTYP_DEVICEINTERFACEper impostazione predefinita. Se li vuoi, devi usare RegisterDeviceNotification() per richiederli.

Sembra che il codice si basa su questo esempio MSDN:

Registering for Device Notification

In quel codice, WceusbshGUID è definito come {25dbce51-6c8f-4a72-8a6d-b54c2b4fc835}, che viene commentato come la classe GUID per accoglienza di serie driver Plug and Play USB. Secondo questa pagina MSDN:

System-Defined Device Setup Classes Available to Vendors

tale GUID è la classe GUID per dispositivi USB ActiveSync Windows CE (che è più coerente con il prefisso Wceusb... usata nel codice). Anche sulla stessa pagina è {88BAE032-5A81-49f0-BC3D-A4FF138216D6} per Dispositivo USB (tutti i dispositivi USB che non appartengono a un'altra classe).

Il seguente articolo CodeProject:

Detecting Hardware Insertion and/or Removal

Menzioni {a5dcbf10-6530-11d2-901f-00c04fb951ed} per Periferica Raw. Quello stesso guid è documentato su MSDN come GUID_DEVINTERFACE_USB_DEVICE (la cui denominazione probabilmente risale ai giorni pre-XP quando la denominazione dei guids di classe e dei GUID di interfaccia was not well separated).

Così, quando si chiama RegisterDeviceNotification() con un specifica classe guid, assicurarsi che è la classe guid corretta, come si sta per ottenere gli eventi del dispositivo solo per questa specifica tipo di dispositivo. È probabile che il tuo dispositivo USB stia utilizzando un guid di classe diverso da quello che stai registrando, ed è per questo che non ricevi gli eventi del dispositivo che ti aspetti.

Se si desidera rilevare qualsiasi dispositivo USB a prescindere dalla sua guid di classe (e ci sono diversi GUID classe USB definiti), è possibile utilizzare il flag DEVICE_NOTIFY_ALL_INTERFACE_CLASSES quando si chiama RegisterDeviceNotification(), quindi il GUID di classe verrà ignorato. Nei messaggi DBT_DEVICEARRIVAL e DBT_DEVICEREMOVECOMPLETE (supponendo che tu sia in grado di ottenerli ora), il dbcc_classguid segnalato ti dirà l'attuale guida di classe e il dbcc_name segnalato inizierà con il prefisso \\?\USB:.

Un'ultima cosa: riceverai solo i messaggi DBT_DEVICE... se un dispositivo USB viene inserito/rimosso mentre la tua app è già in esecuzione. Per rilevare se un dispositivo USB è già collegato all'avvio dell'app, è necessario utilizzare le funzioni SetupAPI (SetupDiGetClassDevs(), SetupDiEnumDeviceInterfaces(), SetupDiGetDeviceInterfaceDetail(), ecc.) Per enumerare i dispositivi disponibili.

+0

Sì, mi dispiace, mi sembra di aver omesso le definizioni per il GUID che ho usato e per la chiamata a DoRegisterDeviceInterfaceToHwnd(). Li ho appena aggiunti li dentro. – bmahf

+0

Hai provato i guidi di altre classi che ti ho dato? –

+1

Completato e testato. Con i tuoi suggerimenti, ho finito per cambiare la chiamata a RegisterDeviceNotification() in modo che io passassi DEVICE_NOTIFY_ALL_INTERFACE_CLASSES come flag, invece di DEVICE_NOTIFY_WINDOW_HANDLE. Successivamente, sono stato in grado di ottenere un valore non NULL per lParam. Questo mi ha dato paramGuid e paramName, entrambi molto utili nel dirmi cosa sto guardando, quindi posso assicurarmi che si tratti di un'unità USB, piuttosto che di un altro dispositivo collegabile come un dongle di sicurezza. Grazie per i suggerimenti. – bmahf