views:

1588

answers:

4

I am trying to write a little backup program for friends and family and want it to be as simple to use a possible. I don't want to have to ask the user where to backup their data to, I just want to search for and use the first USB hard drive connected to the computer. Obtaining the unique ID of the hard drive would probably be a good idea too, just as a double check for next time the backup runs.

A: 

A few pieces of information can be gathered without too much trouble:

  • Use GetDriveType to find the first removeable drive, test if writeable media exists (which will largely rule out CD drives). May also want to look at further strings that are available when you query the drive information via win32.
  • Use libusb to see where the first storage class USB device is (will likely be a flash or hard drive)
  • This C# article points towards win32 disk drive classes you might be able to tap into.

Post your answer here when you find it!

Adam Davis
Thanks for the quick response. GetDriveInfo returns the same value for a fixed hard drive (drive C) as it does for a UDB hard drive; DRIVE_FIXED. I tried it with a USB pen drive and it reported it as DRIVE_REMOVABLE.
Stacey Richards
+1  A: 

I know your question is tagged Win32, but this is quite simple with .NET:

foreach (IO.DriveInfo drive in IO.DriveInfo.GetDrives()) {
  if ((drive.DriveType == IO.DriveType.Removable)) {
    // this is a removable drive
  }
}

See drive.Name and drive.VolumeLabel for getting the label. You can also get the size, and make an educated guess that it's a USB stick (and a big enough one) -- Removable can mean either Floppy or USB, according to the docs.

As a side note, from a UI perspective, I'd suggest the first time you find a new drive, present it to the user and ask "is this the drive you want to use for backups?". Otherwise, there is a big potential for accidentally wiping out data on a usb key that happened to be plugged in. Nothing destroys the credibility of a backup program like when it destroys your data. :)

gregmac
I'm assuming IO.DriveInfo is the same as GetDriveInfo in the win32 api. If so, it returns the same value for a USB hard drive as it does for a fixed hard drive (DRIVE_FIXED).
Stacey Richards
A: 

You need to use RegisterDeviceNotification function. Here is some pointers about how to do it. And one more sample code

You can enumerate all mass storage devices using this sample. In General look for SetupDiXXX api's.

Please note that taking in consideration dynamic nature of usb devices, using notification mechanism is mandatory IMHO. You might find your self analyzing device that already detached or missing new device that just arrived.

Ilya
Thanks for the pointer but I don't want to detect when a drive is connected or disconnected, I want to know if a connected drive is a USB one (could be plugged in before my program launches).
Stacey Richards
+1  A: 

I spent a little time looking around and found a function called SetupDiEnumDeviceInfo which did provide a solution to know whether a hard drive was removable or not but with that information I still can't (yet) map what I find back to a drive letter!

Here's what I have so far (following code creates a dll):

#include "stdafx.h"
#include <setupapi.h>
#include <devguid.h>
#include <cfgmgr32.h>
extern "C" __declspec(dllexport) int usb_hard_drives() {
  HDEVINFO hdevinfo = SetupDiGetClassDevs(&GUID_DEVCLASS_DISKDRIVE, NULL, NULL, DIGCF_PRESENT);
  if (hdevinfo == INVALID_HANDLE_VALUE) return -1;
  DWORD MemberIndex = 0;
  SP_DEVINFO_DATA sp_devinfo_data;
  ZeroMemory(&sp_devinfo_data, sizeof(sp_devinfo_data));
  sp_devinfo_data.cbSize = sizeof(sp_devinfo_data);
  int c = 0;
  while (SetupDiEnumDeviceInfo(hdevinfo, MemberIndex, &sp_devinfo_data)) {
    DWORD PropertyRegDataType;
    DWORD RequiredSize;
    DWORD PropertyBuffer;
    if (SetupDiGetDeviceRegistryProperty(hdevinfo, &sp_devinfo_data, SPDRP_CAPABILITIES, &PropertyRegDataType, (PBYTE)&PropertyBuffer, sizeof(PropertyBuffer), &RequiredSize)) {
      if (PropertyBuffer && CM_DEVCAP_REMOVABLE == CM_DEVCAP_REMOVABLE) {
        // do something here to identify the drive letter.
        c++;
      }
    }    
    MemberIndex++;
  }
  SetupDiDestroyDeviceInfoList(hdevinfo);
  return c;
}
Stacey Richards
If you have a known file located at a known location on your removable drive (the root) then, you can loop through all the drive letters looking for that file. When you find it, you know the drive letter.
BoltBait