Click here to Skip to main content
Email Password   helpLost your password?

Sample Image - usbeject1.gif

Introduction

Although removing a USB stick using the Windows shell is quite easy, it tends to be quite difficult programmatically. There are a lot of underlying notions, at the frontier of kernel driver development, one has to understand to achieve what may seem a simple task. When I started this work, I really did not think where it would lead me to. I sure did not think I would have to switch between kernel driver control codes, the Windows setup and Configuration Manager APIs, WMI, etc...

Well, this is the main reason for this article, I think the subject really deserves one. The result is a demo .NET Winforms project that includes reusable source code.

And by the way, as a side benefit, I will also explain how to read the hardware serial number of those disks, a recurring question on newsgroups and forums.

Background

First of all, let's talk about the various Windows API involved here, apart from Win32, and there are many:

  1. The Windows DDK (Driver Development Kit): A driver development kit that provides a build environment, tools, driver samples, and documentation to support driver development for Windows.
  2. The Setup API: An underused and not very well known Windows API. One of the reasons it is underused is because it is part of the DDK, but in fact, these are general Setup routines, which are used in installation applications, and there are many very interesting gems in there, like the CM_Request_Device_Eject function, which is the heart of the UsbEject project.
  3. The DeviceIoControl function: The "Open, Sesame" user mode function to the depths of kernel mode cave. Tons of things (a large part of it is undocumented, or not well document) can be done using this.
  4. WMI (Windows Management Instrumentation): The UsbEject project does not actually uses WMI because WMI does not handle device ejection, as far as I know. I could have used a hybrid approach using WMI for disk management and the rest for device Ejection. I will leave this as an exercise to the reader.
  5. Windows Messages: The WM_DEVICECHANGE Window message is used in this sample GUI application to visually refresh the tree when something happens to the device manager tree.

Now, let's introduce a few terms. The definitions here are my own, not official ones (it is actually quite hard to find official definitions for all these).

  1. Physical Disks: Well, as the name implies, it is the real piece of hardware an end user manipulates. In the case of a USB disk, it is the stick itself.
  2. Volumes: There are actually two types of volumes: volumes as they are understood by Windows, and volumes as we know them, that is, drive letters, from A: to Z:, also called logical disks. A volume can span multiple physical disks.
  3. Devices: The Windows DDK defines both volumes and physical disks as being devices. The code reflects this. Device is a base type, and Volume derives from it.

Using the code

The project is a Visual Studio 2005 (.NET 2.0) Windows form project. There is a folder called Library that contains the heart of it, an object oriented API to handle USB disks. The classes there are described in the following class diagram:

Sample Image - usbeject2.gif

As you can see, there are 5 main classes (each class is defined in its own respective .cs file):

  1. DeviceClass: An abstract class that represents a physical device class. It has a list of devices in this class
  2. DiskDeviceClass: Represents all disk devices in the system
  3. VolumeDeviceClass: Represents all volume devices in the system
  4. Device: Represents a generic device of any type (disk, volume, etc...). Note there is no Disk class, because in this project, a Disk has no specific property, compared to a Device. Note also the code has been designed so it could be extended to other devices, not only volumes and disks
  5. Volume: Represents a Windows volume. A volume has a LogicalDrive property which is filled if it has a drive letter assigned

This is how you can eject all USB volumes for example:

VolumeDeviceClass volumeDeviceClass = new VolumeDeviceClass();
foreach (Volume device in volumeDeviceClass.Devices)
{
    // is this volume on USB disks?

    if (!device.IsUsb)
        continue;

    // is this volume a logical disk?

    if ((device.LogicalDrive == null) || (device.LogicalDrive.Length == 0))
        continue;

    device.Eject(true); // allow Windows to display any relevant UI

}

Points of Interest

CM_Request_Device_Eject function

This is the SetupApi function that ejects a device (any device that can be ejected). It takes a device instance handle (or devInst) as input. The function behaves differently if the second argument pVetoType is provided or not. If it is provided, the Windows shell will not present the end user with any dialog, and the eject operation will either succeed or fail silently (with an error code, called the Veto). If not, the Windows shell may display the traditional messages or dialog boxes, or balloon windows to inform the end user (Note: Windows 2000 may always display a message under certain circumstances, even when the second argument is not provided) that something interesting has happened.

The main problem is therefore "For a given disk letter, what is the device to eject?"

The device to eject must be a Disk device, not a Volume device (at least for USB disks). So the general algorithm is the following:

  1. Determine all available logical disks, using .NET's Environment.GetLogicalDrives.
  2. For each logical disk (drive letter), determine the real volume name, using Win32's GetVolumeNameForVolumeMountPoint.
  3. For each volume device, determine if there is a matching logical disk.
  4. For each volume device, determine physical disk devices composing (using their number) the volume device (as there may be more than one physical disk per volume), using the DDK's IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS IO control code.
  5. Because the Plug-And-Play (PNP) Configuration Manager builds a device hierarchy, a physical disk device is not in general the device to eject, so for each physical disk device, determine what is the device to eject among its ascendant devices. The device to eject is the first one in the hierarchy with the CM_DEVCAP_REMOVABLE capability.

GetVolumeNameForVolumeMountPoint function

To match Windows volumes with logical disks (algorithm step 3), there is a trick to know. First of all, let me say that all Windows volume can be uniquely addressed with a specific syntax of the form "\\?\Volume{GUID}\" where GUID is the GUID that identifies the volume.

When enumerating volumes devices (using the Volume Device Class Guid GUID_DEVINTERFACE_VOLUME, do not worry, the VolumeDeviceClass wraps it), the volume device path can be used directly in a call to GetVolumeNameForVolumeMountPoint, if "\" is appended to it, although it is not strictly speaking a volume name.

Enumerating all devices for a given class

I have reproduced here a code snippet that shows how .NET P/Invoke interop is used to enumerate all physical devices for a given type.

int index = 0;
while (true)
{
    // enumerate device interface for a given index

    SP_DEVICE_INTERFACE_DATA interfaceData = new SP_DEVICE_INTERFACE_DATA();
    if (!SetupDiEnumDeviceInterfaces(
        _deviceInfoSet, null, ref _classGuid, index, interfaceData))
    {
        int error = Marshal.GetLastWin32Error();
        // this is not really an error...

        if (error != Native.ERROR_NO_MORE_ITEMS)
            throw new Win32Exception(error);
        break;
    }

    SP_DEVINFO_DATA devData = new SP_DEVINFO_DATA();
    int size = 0;
    // get detail for all the device interface

    if (!SetupDiGetDeviceInterfaceDetail(
        _deviceInfoSet, interfaceData, IntPtr.Zero, 0, ref size, devData))
    {
        int error = Marshal.GetLastWin32Error();
        if (error != Native.ERROR_INSUFFICIENT_BUFFER)
        throw new Win32Exception(error);
    }

    // allocate unmanaged Win32 buffer

    IntPtr buffer = Marshal.AllocHGlobal(size);
    SP_DEVICE_INTERFACE_DETAIL_DATA detailData =
                    new SP_DEVICE_INTERFACE_DETAIL_DATA();
    detailData.cbSize = Marshal.SizeOf(
                        typeof(Native.SP_DEVICE_INTERFACE_DETAIL_DATA));

    // copy managed struct buffer into unmanager win32 buffer

    Marshal.StructureToPtr(detailData, buffer, false);

    if (!SetupDiGetDeviceInterfaceDetail(
        _deviceInfoSet, interfaceData, buffer, size, ref size, devData))
    {
        Marshal.FreeHGlobal(buffer); // don't forget to free memory

        throw new Win32Exception(Marshal.GetLastWin32Error());
    }

    // a bit of voodoo magic. This code is not 64 bits portable :-)

    IntPtr pDevicePath = (IntPtr)((int)buffer + Marshal.SizeOf(typeof(int)));
    string devicePath = Marshal.PtrToStringAuto(pDevicePath);
    Marshal.FreeHGlobal(buffer);
    index++;
}

Refreshing the UI when a disk is inserted or removed

There are multiple ways of doing this. I have chosen the most simple one: capture the WM_DEVICECHANGE Windows message. You just have to override the default Window procedure of any Winform in your application, like this:

protected override void WndProc(ref Message m)
{
    if (m.Msg == Native.WM_DEVICECHANGE)
    {
        if (!_loading)
        {
            LoadItems(); // do the refresh work here

        }
    }
    base.WndProc(ref m);
}

A word on Serial Numbers

This is not strictly speaking related to the Eject subject, but I have seen many questions about this too, so I will talk a bit about hard disk's "serial numbers". When speaking about Windows volumes and disks, there are actually (at least) two serial numbers:

  1. A volume software serial number, assigned during the formatting process. This 32bits value can be read easily using the GetVolumeInformation regular Win32 function.
  2. A disk vendor's hardware serial number: This serial number is setup by the vendor during the manufacturing process. It's a string. Of course, it cannot be changed. Unfortunately, you have to know that serial numbers are optional for USB devices, so USB storage sticks may not have one, and indeed, many do not.

Ok, so the question is "how do I read the hardware serial number?". There are at least two ways:

  1. WMI: By far, the easiest way, although it looks like black magic first. Here is an example of such a code (not provided in the project .zip package).

    // browse all USB WMI physical disks
    
    foreach(ManagementObject drive in new ManagementObjectSearcher(
        "select * from Win32_DiskDrive where InterfaceType='USB'").Get())
    {
        // associate physical disks with partitions
    
        foreach(ManagementObject partition in new ManagementObjectSearcher(
            "ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" + drive["DeviceID"]
              + "'} WHERE AssocClass = 
                    Win32_DiskDriveToDiskPartition").Get())
        {
            Console.WriteLine("Partition=" + partition["Name"]);
    
            // associate partitions with logical disks (drive letter volumes)
    
            foreach(ManagementObject disk in new ManagementObjectSearcher(
                "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='"
                  + partition["DeviceID"]
                  + "'} WHERE AssocClass =
                    Win32_LogicalDiskToPartition").Get())
            {
                Console.WriteLine("Disk=" + disk["Name"]);
            }
        }
    
        // this may display nothing if the physical disk
    
        // does not have a hardware serial number
    
        Console.WriteLine("Serial="
         + new ManagementObject("Win32_PhysicalMedia.Tag='"
         + drive["DeviceID"] + "'")["SerialNumber"]);
    }
    
  2. Using raw techniques described here. I have not ported this to C# although it is perfectly feasible.

Remarks

  1. The source code has been designed using the .NET Framework 2.0, but should work on the .NET framework 1.1, although this has not been tested. I do not think it can work on 64 bits CLR as is because of interop structs size that may vary. It could be ported quite easily though.
  2. The code has been tested on Windows XP SP2, and Windows Server 2003, not on Windows 2000, but it should work. I don't think the code can work or can be ported on Windows 9x.
  3. I have not researched the security implications of all this. In particular, the Windows user calling the API must obviously have some rights to be able to access physical devices. I have left these experiments as an exercise for the reader.
You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralExcellent Article
rroostan
0:08 26 Aug '09  
Just wanted to say thanks for an excellent article, well researched and well written. And thanks for sharing the code too; well done indeed.
I need to be able to eject a USB safely so that our idiot users don't have to mess around clicking the mouse button to do it themselves. And this fits the bill nicely. Thanks a lot!
GeneralEject on single drive causes multiple drives to eject
Member 4557095
20:03 3 Jun '09  
I have tried this with a number of USB drives connected via a usb hub. When I choose "eject" for the desired drive it causes other USB drives to be ejected as well.
The drives I'm using are all single sticks (only 1 volume on each stick) and I have tried with various brands and combinations of brands. It seems that the call to CM_Request_Device_Eject causes more than just the device with the handle specified to be ejected.

Has anybody else experienced this? or know of a fix?
GeneralRe: Eject on single drive causes multiple drives to eject
fengqingyang
0:42 13 Jun '09  
I have the same problem
the code can't find any usb device after ejected one,but the usb devices are still exist and windows can find them,
and if I use windows to ejectd one first,the demo is still lost all usb devices
GeneralRe: Eject on single drive causes multiple drives to eject
kirimsini1
20:51 23 Jun '09  
Try to eject the parent of the device

So, instead of device.Eject(true), use device.Parent.Eject(true);
GeneralRe: Eject on single drive causes multiple drives to eject
melmakrani
2:28 4 Mar '10  
Thanks dude, I was having the same problem...
QuestionUSB Hub Problem
Andreas Feil
22:43 7 Apr '09  
Hi!

Thank you for this class it works perfectly!
But I get some issues with my USB-hub.

I use a hub where I plug in an USB-Stick. Now when I remove the stick the entire hub is removed.
This is not quite what I want. I just want to remove the stick.

Has anyone a suggestion?

Thanks!
GeneralWrong use of error codes and Win32Exception?
Albertony
2:17 7 Apr '09  
I'm not an expert on this, but I think the use of return values in combination with the throwing of Win32Exceptions are wrong..

The PnP Configuration Manager functions, those starting with CM_ (e.g. "CM_Get_Parent" and "CM_Request_Device_Eject") does not return the regular native error codes. They return a set of separate error codes, defined in cfgmgr32.h. According to MSDN documentation: "If the operation succeeds, the function returns CR_SUCCESS. Otherwise, it returns one of the CR_-prefixed error codes defined in cfgmgr32.h"

The existing code tests if the return value is other than 0 (which is the value of CR_SUCCESS), and if it is then it's thrown in a Win32Exception like this:
if (hr != 0)
throw new Win32Exception(hr);
But the Win32Exception constructor takes the input argument to be a native error code, so in the case of a veto, the return value 23 (CR_REMOVE_VETOED) from CM_Request_Device_Eject will result in an exception which states "Data error (cyclic redundancy check)".

So if I'm correct, the exception should be thrown with an error message instead, e.g.:

throw new Win32Exception("PnP Configuration Manager error code: " + hr);
To explicit test for some of the PnP return values, you can add the following enum to Native.cs:
//From cfgmgr32.h
internal enum PNP_ERROR_CODES
{
CR_SUCCESS = 0x00,
CR_DEFAULT = 0x01,
CR_OUT_OF_MEMORY = 0x02,
CR_INVALID_POINTER = 0x03,
CR_INVALID_FLAG = 0x04,
CR_INVALID_DEVNODE = 0x05,
CR_INVALID_DEVINST = CR_INVALID_DEVNODE,
CR_INVALID_RES_DES = 0x06,
CR_INVALID_LOG_CONF = 0x07,
CR_INVALID_ARBITRATOR = 0x08,
CR_INVALID_NODELIST = 0x09,
CR_DEVNODE_HAS_REQS = 0x0A,
CR_DEVINST_HAS_REQS = CR_DEVNODE_HAS_REQS,
CR_INVALID_RESOURCEID = 0x0B,
CR_DLVXD_NOT_FOUND = 0x0C,
CR_NO_SUCH_DEVNODE = 0x0D,
CR_NO_SUCH_DEVINST = CR_NO_SUCH_DEVNODE,
CR_NO_MORE_LOG_CONF = 0x0E,
CR_NO_MORE_RES_DES = 0x0F,
CR_ALREADY_SUCH_DEVNODE = 0x10,
CR_ALREADY_SUCH_DEVINST = CR_ALREADY_SUCH_DEVNODE,
CR_INVALID_RANGE_LIST = 0x11,
CR_INVALID_RANGE = 0x12,
CR_FAILURE = 0x13,
CR_NO_SUCH_LOGICAL_DEV = 0x14,
CR_CREATE_BLOCKED = 0x15,
CR_NOT_SYSTEM_VM = 0x16,
CR_REMOVE_VETOED = 0x17,
CR_APM_VETOED = 0x18,
CR_INVALID_LOAD_TYPE = 0x19,
CR_BUFFER_SMALL = 0x1A,
CR_NO_ARBITRATOR = 0x1B,
CR_NO_REGISTRY_HANDLE = 0x1C,
CR_REGISTRY_ERROR = 0x1D,
CR_INVALID_DEVICE_ID = 0x1E,
CR_INVALID_DATA = 0x1F,
CR_INVALID_API = 0x20,
CR_DEVLOADER_NOT_READY = 0x21,
CR_NEED_RESTART = 0x22,
CR_NO_MORE_HW_PROFILES = 0x23,
CR_DEVICE_NOT_THERE = 0x24,
CR_NO_SUCH_VALUE = 0x25,
CR_WRONG_TYPE = 0x26,
CR_INVALID_PRIORITY = 0x27,
CR_NOT_DISABLEABLE = 0x28,
CR_FREE_RESOURCES = 0x29,
CR_QUERY_VETOED = 0x2A,
CR_CANT_SHARE_IRQ = 0x2B,
CR_NO_DEPENDENT = 0x2C,
CR_SAME_RESOURCES = 0x2D,
CR_NO_SUCH_REGISTRY_KEY = 0x2E,
CR_INVALID_MACHINENAME = 0x2F,
CR_REMOTE_COMM_FAILURE = 0x30,
CR_MACHINE_UNAVAILABLE = 0x31,
CR_NO_CM_SERVICES = 0x32,
CR_ACCESS_DENIED = 0x33,
CR_CALL_NOT_IMPLEMENTED = 0x34,
CR_INVALID_PROPERTY = 0x35,
CR_DEVICE_INTERFACE_ACTIVE = 0x36,
CR_NO_SUCH_DEVICE_INTERFACE = 0x37,
CR_INVALID_REFERENCE_STRING = 0x38,
CR_INVALID_CONFLICT_LIST = 0x39,
CR_INVALID_INDEX = 0x3A,
CR_INVALID_STRUCTURE_SIZE = 0x3B
}

Generalhelp me
mohanshanthi
22:25 17 Feb '09  
Hi, how to use your utiliyy in LAN .
Generalgreat article
mohanshanthi
19:33 16 Feb '09  
Hi,
Hope you are doing well.Your article is working well. How to use your utility in LAN connection. Please reply me.
GeneralMemory Leak?
chenqifu
22:40 18 Dec '08  
I have used the program in my project, it can implement all functions.
But when I call it repeatedly for 1 hour, more than 200 MB memory will be occupied by the program.
What's the matter?
Thank you!
GeneralRe: Memory Leak?
chenqifu
15:19 21 Dec '08  
I have find out the problem, the issue is forgetting to dispose disks in Volume:Disks in Volume.cs

A solution: Add a destruction method to the DeviceClass.
protected bool disposed = false;

~DeviceClass()
{
CleanUp(false);
}

public void Dispose()
{
CleanUp(true);
GC.SuppressFinalize(this);
}

protected void CleanUp(bool disposing)
{
if (!disposed)
{
if (_deviceInfoSet != IntPtr.Zero)
{
Native.SetupDiDestroyDeviceInfoList(_deviceInfoSet);
_deviceInfoSet = IntPtr.Zero;
}
}
disposed = true;
}

QuestionSeem to have a bug
qpzhou
19:28 11 Dec '08  
Great atricle! But it seems to have a bug.
1.Insert usb memory → USB1(DSmile
2.Insert usb memory → USB2(ESmile
3.eject USB1 → OK (Use your demo)
4.eject USB2 → OK
5.Insert usb memory again → USB2(ESmile
6.Insert usb memory again → USB1(DSmile
7.eject USB1 → NG (USB2 has been ejected)
GeneralLicense Information for Eject USB
bmccanna
8:58 9 Sep '08  
Hi Simon,

When you have a moment, please clarify what license governs the use of Eject USB.

Thanks,
Bryan

bryan
GeneralRe: License Information for Eject USB
Simon Mourier
9:47 9 Sep '08  
Hi Bryan,

Well basically, the license is free. You can do whatever you want with it, commercial or not, open source or not. You can cite me (and that would be nice Smile ) but it's *not* required.

The only thing is: if it does not work, or do what you want, I cannot be responsible for it Shucks
I think it's a BSD like license, but don't take my word for it.
GeneralRe: License Information for Eject USB
bmccanna
7:12 19 Sep '08  
Thanks for your response.
GeneralWill not run in 64 bit mode - must explicitly set x84 in compile options
surf uk
5:57 3 Aug '08  
Brilliant example code and reusable code! I was horrified when it would not run on my win xp pro 64bit machine, but it works if you set the compile options to x84.

Any idea why it fails to work with 64bit?

not suitable for idiots

GeneralRe: Will not run in 64 bit mode - must explicitly set x84 in compile options
Simon Mourier
9:50 9 Sep '08  
Hi,

Read my remarks D'Oh! . I knew the code would not run on a 64 bits platform, but it can actually be modified to be able to run on both platforms in native code (Any Cpu). I think it's just a few bits here and there.

I don't have the time for this, but maybe someone has Big Grin
GeneralRe: Will not run in 64 bit mode - must explicitly set x84 in compile options
Tinus1926
8:52 7 Oct '09  
Found the issue with 64 bits.

Change the property Devices in DeviceClass.cs into:

/// <summary>
/// Gets the list of devices of this device class.
/// </summary>
public List<Device> Devices
{
get
{
if (_devices == null)
{
_devices = new List<Device>();
int index = 0;
while (true)
{
Native.SP_DEVICE_INTERFACE_DATA interfaceData = new Native.SP_DEVICE_INTERFACE_DATA();
interfaceData.cbSize = (UInt32)Marshal.SizeOf(interfaceData);

if (!Native.SetupDiEnumDeviceInterfaces(_deviceInfoSet, IntPtr.Zero, ref _classGuid, (UInt32)index, ref interfaceData))
{
int error = Marshal.GetLastWin32Error();
if (error != Native.ERROR_NO_MORE_ITEMS)
throw new Win32Exception(error);
break;
}
Native.SP_DEVINFO_DATA devData = new Native.SP_DEVINFO_DATA();
IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(devData));
Marshal.StructureToPtr(devData, p, true);
UInt32 size = 0;
if (!Native.SetupDiGetDeviceInterfaceDetail(_deviceInfoSet, ref interfaceData, IntPtr.Zero, 0, ref size, p))
{
int error = Marshal.GetLastWin32Error();
if (error != Native.ERROR_INSUFFICIENT_BUFFER)
throw new Win32Exception(error);
}
Native.SP_DEVICE_INTERFACE_DETAIL_DATA detailDataBuffer = new Native.SP_DEVICE_INTERFACE_DETAIL_DATA();
if (IntPtr.Size == 8) // for 64 bit operating systems
{
detailDataBuffer.cbSize = 8;
}
else
{
detailDataBuffer.cbSize = 4 + Marshal.SystemDefaultCharSize; // for 32 bit systems
}
IntPtr pBuf = Marshal.AllocHGlobal(Marshal.SizeOf(detailDataBuffer));
Marshal.StructureToPtr(detailDataBuffer, pBuf, true);

if (!Native.SetupDiGetDeviceInterfaceDetail(_deviceInfoSet, ref interfaceData, pBuf, size, ref size, p))
{
int error = Marshal.GetLastWin32Error();
if (error != Native.ERROR_INSUFFICIENT_BUFFER)
throw new Win32Exception(error);
}
devData = (Native.SP_DEVINFO_DATA)Marshal.PtrToStructure(p, typeof(Native.SP_DEVINFO_DATA));
Marshal.FreeHGlobal(p);

detailDataBuffer = (Native.SP_DEVICE_INTERFACE_DETAIL_DATA)Marshal.PtrToStructure(pBuf, typeof(Native.SP_DEVICE_INTERFACE_DETAIL_DATA));
Marshal.FreeHGlobal(pBuf);

string devicePath = detailDataBuffer.DevicePath;
Native.STORAGE_DEVICE_NUMBER storageDeviceNumber = GetDeviceNumber(devicePath);
Device device = CreateDevice(this, devData, devicePath, storageDeviceNumber.DeviceNumber);
_devices.Add(device);

index++;
}
_devices.Sort();
}
return _devices;
}
}

and replace in native.cs the following parts:

[StructLayout(LayoutKind.Sequential)]
internal struct SP_DEVICE_INTERFACE_DATA
{
internal UInt32 cbSize;
internal Guid interfaceClassGuid;
internal UInt32 flags;
internal UIntPtr reserved;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal struct SP_DEVICE_INTERFACE_DETAIL_DATA
{
internal Int32 cbSize;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
internal string DevicePath;
}

[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern bool SetupDiGetDeviceInterfaceDetail(
IntPtr deviceInfoSet,
ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
IntPtr deviceInterfaceDetailData,
UInt32 deviceInterfaceDetailDataSize,
ref UInt32 requiredSize,
IntPtr deviceInfoData);
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern bool SetupDiEnumDeviceInterfaces(
IntPtr deviceInfoSet,
IntPtr deviceInfoData,
ref Guid interfaceClassGuid,
UInt32 memberIndex,
ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);
GeneralRe: Will not run in 64 bit mode - must explicitly set x84 in compile options
mark gooding
0:25 13 Nov '09  
Hi is there anything missing with this solution of a 64bit compatible version?

I get compile errors:
....\DeviceClass.cs(142,26): error CS0426: The type name 'STORAGE_DEVICE_NUMBER' does not exist in the type 'UsbEject.Library.Native'
....\DeviceClass.cs(142,70): error CS0103: The name 'GetDeviceNumber' does not exist in the current context

Have you missed some other changes you made.

Cheers,

mark
QuestionRe: Will not run in 64 bit mode - must explicitly set x84 in compile options
KerryLGrant
6:21 11 Mar '10  
I get the same compile errors. Anybody get around this??? Thanks!
QuestionRediscover an ejected drive?
pkruger
8:17 24 Jul '08  
Is there a why to rediscover (mount) a usb drive that has been ejected, but is still inserted into a usb port?
GeneralHow can eject a USB Disk using drive letter ?
Tu
23:20 14 Jul '08  
Hi,

I don't want to use Device struct for ejecting. Please tell me know how can I eject using drive letter ? Cab you show me source demo about that ?

Thanks !
GeneralRe: How can eject a USB Disk using drive letter ?
sworkam
9:57 27 Jul '09  
Here's a function that can eject a drive by letter, but you need all of the classes exception MainForm.cs.
public static string Eject(string driveLetter)
{
VolumeDeviceClass volumeDeviceClass = new VolumeDeviceClass();

foreach (Volume device in volumeDeviceClass.Devices)
{
if (device.LogicalDrive.ToUpper()[0] == driveLetter.ToUpper()[0])
{
return device.Eject(true);
}
}

return "";
}
I put this in a new class and compiled everything into a DLL. It works fine for me, although the DLL is around 15 KB.
QuestionEjecting USB - COM - Serial Port
Sharoma
2:32 10 Jul '08  
Hi, I was wondering if this code can be extended to eject USB to COM-Serial-Port.

Thanks
AnswerRe: Ejecting USB - COM - Serial Port
Simon Mourier
9:51 9 Sep '08  
I have no idea, but D'Oh! how would you eject your COM serial port?


Last Updated 22 Mar 2006 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010