Windows CDROM Eject - windows

Does anyone know a method to programmatically close the CD tray on Windows 2000 or higher?
Open CD tray exists, but I can't seem to make it close especially under W2k.
I am especially looking for a method to do this from a batch file, if possible, but API calls would be OK.

I kind of like to use DeviceIOControl as it gives me the possibility to eject any kind of removable drive (such as USB and flash-disks as well as CD trays). Da codez to properly eject a disk using DeviceIOControl is (just add proper error-handling):
bool ejectDisk(TCHAR driveLetter)
{
TCHAR tmp[10];
_stprintf(tmp, _T("\\\\.\\%c:"), driveLetter);
HANDLE handle = CreateFile(tmp, GENERIC_READ, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
DWORD bytes = 0;
DeviceIoControl(handle, FSCTL_LOCK_VOLUME, 0, 0, 0, 0, &bytes, 0);
DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, 0, 0, 0, 0, &bytes, 0);
DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, 0, 0, 0, 0, &bytes, 0);
CloseHandle(handle);
return true;
}

Here is an easy way using the Win32 API:
[DllImport("winmm.dll", EntryPoint = "mciSendStringA", CharSet = CharSet.Ansi)]
protected static extern int mciSendString(string lpstrCommand,StringBuilder lpstrReturnString,int uReturnLength,IntPtr hwndCallback);
public void OpenCloseCD(bool Open)
{
if (Open)
{
mciSendString("set cdaudio door open", null, 0, IntPtr.Zero);
}
else
{
mciSendString("set cdaudio door closed", null, 0, IntPtr.Zero);
}
}

I noticed that Andreas Magnusson's answer didn't quite work exactly the same as Explorer's 'Eject' button did. Specifically, the drive wasn't grayed out in Explorer using Andreas' code, but was if you used the Eject command. So I did some investigating.
I ran API Monitor while running the Eject command from Explorer (Windows 7 SP1 64-bit). I also found a good (now-defunct) MSKB article 165721 titled How To Ejecting Removable Media in Windows NT/Windows 2000/Windows XP. The most interesting part of the article is quoted below:
Call CreateFile with GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, and OPEN_EXISTING. The lpFileName parameter should be \\.\X: (where X is the real drive letter). All other parameters can be zero.
Lock the volume by issuing the FSCTL_LOCK_VOLUME IOCTL via DeviceIoControl. If any other application or the system is using the volume, this IOCTL fails. Once this function returns successfully, the application is guaranteed that the volume is not used by anything else in the system.
Dismount the volume by issuing the FSCTL_DISMOUNT_VOLUME IOCTL. This causes the file system to remove all knowledge of the volume and to discard any internal information that it keeps regarding the volume.
Make sure the media can be removed by issuing the IOCTL_STORAGE_MEDIA_REMOVAL IOCTL. Set the PreventMediaRemoval member of the PREVENT_MEDIA_REMOVAL structure to FALSE before calling this IOCTL. This stops the device from preventing the removal of the media.
Eject the media with the IOCTL_STORAGE_EJECT_MEDIA IOCTL. If the device doesn't allow automatic ejection, then IOCTL_STORAGE_EJECT_MEDIA can be skipped and the user can be instructed to remove the media.
Close the volume handle obtained in the first step or issue the FSCTL_UNLOCK_VOLUME IOCTL. This allows the drive to be used by other
processes.
Andreas's answer, the MSKB article, and my API sniffing of Explorer can be summarized as follows:
CreateFile called to open the volume. (All methods).
DeviceIoControl called with FSCTL_LOCK_VOLUME. (All methods).
DeviceIoControl called with FSCTL_DISMOUNT_VOLUME. (Andreas's and MSKB methods only. Explorer does not call this for some reason. This IOCTL seems to be what affects whether the drive is grayed out in Explorer or not. I am not sure why Explorer doesn't call this).
DeviceIoControl called with IOCTL_STORAGE_MEDIA_REMOVAL and PREVENT_MEDIA_REMOVAL member set to FALSE (MSKB and Explorer methods. This step is missing from Andreas's answer).
DeviceIoControl called with IOCTL_STORAGE_EJECT_MEDIA (Andreas and MSKB article) or IOCTL_DISK_EJECT_MEDIA (Explorer; note this IOCTL was obsoleted and replaced with the STORAGE IOCTL. Not sure why Explorer still uses the old one).
To conclude, I decided to follow the procedure outlined in the MSKB article, as it seemed to be the most thorough and complete procedure, backed up with an MSKB article.

Nircmd is a very handy freeware command line utility with various options, including opening and closing the CD tray.

To close the drive tray do as described here but instead of using DeviceIoControl with IOCTL_STORAGE_EJECT_MEDIA you need to call DeviceIoControl with IOCTL_STORAGE_LOAD_MEDIA.

Related

LockFile and LockFileEx fail on physical drive [duplicate]

How do i write to a physical drive in Windows 7?
I am trying to write to a physical disk (e.g. \\.\PhysicalDrive0) in Windows 7.
This question has been asked to death, but has never been answered. It is something that used to work in Windows XP, but Microsoft intentionally broke in Windows Vista. Microsoft provides hints about how to do it, but nobody has even been able to figure it out.
It used to work
In the olden days, writing to a physical disk was allowed (as long as you were an administrator). The method to do it was even documented in a Knowledge Base article:
INFO: Direct Drive Access Under Win32
To open a physical hard drive for direct disk access (raw I/O) in a Win32-based application, use a device name of the form
\\.\PhysicalDriveN
where N is 0, 1, 2, and so forth, representing each of the physical drives in the system.
You can open a physical or logical drive using the CreateFile() application programming interface (API) with these device names provided that you have the appropriate access rights to the drive (that is, you must be an administrator). You must use both the CreateFile() FILE_SHARE_READ and FILE_SHARE_WRITE flags to gain access to the drive.
All that changed in Windows Vista, when addition security restrictions were put in place.
How do you write to a physical disk?
Many people, and many answers, on many stackoverflow questions confuse:
writing to a physical disk (e.g. \\.\PhysicalDrive0), and
writing to a logical volume (e.g. \\.\C$)
Microsoft notes the restrictions placed on both kinds of operations:
Blocking Direct Write Operations to Volumes and Disks
Write operations on a DASD (Direct access storage device) volume handle will succeed if:
the file system is not mounted, or if
The sectors being written to are the boot sectors.
The sectors being written to reside outside file system space.
The file system has been locked implicitly by requesting exclusive write access.
The file system has been locked explicitly by sending down a lock/dismount request.
The write request has been flagged by a kernel-mode driver that indicates that this check should be bypassed. The flag is called SL_FORCE_DIRECT_WRITE and it is in the IrpSp->flags field. This flag is checked by both the file system and storage drivers.
In my case i am asking about writing to a Physical, not a Logical one. Microsoft notes the new set of restrictions on writing to a physical disk handle:
Write operations on a disk handle will succeed if:
The sectors being written to do not fall within a file system.
The sectors being written to fall within a mounted file system that is locked explicitly.
The sectors being written to fall within a file system that is not mounted or the volume has no file system.
My sectors being written do fall within a file system --> fail
My sectors being written do fall within mounted, unlocked, file system --> fail
My sectors being written do fall within a file system that is mounted, and in inside a logical volume that has a file system.
The hints on how to make it work revolve around:
unmounting a file system
locking a file system
But the question is how do you unmount a file system? How do you lock a file system?
What are you doing now?
I am able to read all physical sectors of a disk; that is no problem. The problem is when i want to write to a physical sector of the disk.
The current code i have is, in pseudo-code:
void ZeroSector(Int64 PhysicalSectorNumber)
{
String diskName := '\\.\PhysicalDrive0';
DWORD desiredAccess := GENERIC_READ or GENERIC_WRITE;
//INFO: Direct Drive Access Under Win32
//https://support.microsoft.com/en-us/kb/100027
//says you nedd both
DWORD shareMode := FILE_SHARE_READ or FILE_SHARE_WRITE;
//Open the physical disk
hDisk := CreateFile(diskName, desiredAccess, shareMode,
nil, OPEN_EXISTING, 0, 0);
if hDisk = INVALID_HANDLE_VALUE
RaiseLastWin32Error();
try
{
Int32 bytesPerPhysicalSector := 4096; //Determined elsewhere using IOCTL_STORAGE_QUERY_PROPERTY+STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR
//Setup buffer for what we will be writing
Byte[] buffer = new Byte[bytesPerPhysicalSector];
//Calculate the byte offset of where the sector is
Int64 byteOffset = PhysicalSectorNumber * bytesPerPhysicalSector;
//Seek to that byte offset
SetFilePointer(hDisk, byteOffset.Lo, byteOffset.Hi, FILE_BEGIN);
//Write the buffer
DWORD numberOfBytesWritten;
if (!WriteFile(hDisk, buffer, bytesPerPhysicalSector, out numberOfBytesWritten, nil))
RaiseLastWin32Error();
}
finally
{
CloseHandle(hDisk);
}
}
Surprisingly:
i can open the physical disk for GENERIC_READ + GENERIC_WRITE access
it doesn't fail until the actual WriteFile, which fails with:
ERROR_ACCESS_DENIED
How to do what Microsoft says
Microsoft said that my write would fail, and they were right. They said that i need to explicitly lock the file system:
Write operations on a disk handle will succeed if:
The sectors being written to fall within a mounted file system that is locked explicitly.
Except i don't know how to do that.
I know i probably have to use DeviceIoControl and one of the IOCTLS to "lock" a volume. But that presents three challenges:
figuring out which volume(s) are on the physical disk selected
figuring out which IOCTL to use
figuring out how to unlock the locked volumes
Ignoring those problems, i blindly tried the LockFile API. Just before calling WriteFile:
//Try to lock the physical sector we will be writing
if (!LockFile(DiskHandle, byteOffset.Lo, byteOffset.Hi, bytesPerPhysicalSector, 0)
RaiseLastWin32Error();
That fails with:
ERROR_INVALID_FUNCTION (1)
Check out FSCTL_LOCK_VOLUME, FSCTL_DISMOUNT_VOLUME control codes. I believe you would have to enum all volumes you have on your disk, and then dismount-and-lock them. After lock succeeded, the disk is all yours.
You probably won't be able to do this on a system drive though. I'd also guess that there will be caveats with volumes that contain page files.
after I have done this, I can write to the corresponding \\.\PhysicalDrive3. I could not before:
HANDLE hVol = CreateFileA(
"\\\\.\\X:",
FILE_READ_DATA | FILE_WRITE_DATA,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD unused;
BOOL b = DeviceIoControl(hVol, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &unused, NULL);
if (!b) {
printf("%u", GetLastError());
abort();
}
...
HANDLE h = CreateFileA(
argv[1], // that's my \\physicaldrive3
FILE_READ_DATA | FILE_WRITE_DATA,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
...
bResult = WriteFile(h, buf, cb, &dwWritten, &foo);
if (!bResult) {
// used to fail without messing with vol handle
printf("Failed writing data. Error = %d.\n", GetLastError());
return 0;
}

CreateFile on Directory in NTFS fails on ReadFile

Supposedly it is possible to actually open and read directories on NTFS volumes. However, my code to try this wasn't working, so I tried google, which found me this.
The key observation there seems to be that you must use FILE_FLAG_BACKUP_SEMANTICS. So, trimming that down, I basically get:
HANDLE hFile = CreateFile(L"C:\\temp", GENERIC_READ, FILE_SHARE_READ,
0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
DWORD dwFileSize = GetFileSize(hFile, 0);
char* buf = new char[dwFileSize];
DWORD dwBytesRead = 0;
BOOL b = ReadFile(hFile, buf, dwFileSize, &dwBytesRead, 0);
Seems pretty straight-forward. Unfortunately, it doesn't work.
The CreateFile and GetFileSize both work (handle is not INVALID_HANDLE_VALUE, non-zero and plausible file size), but the ReadFile returns FALSE, dwBytesRead is zero, and GetLastError returns 1 ("Incorrect function"). Huh.
While I was typing this question, the 'Similar Questions' prompt showed me this. That business about using AdjustTokenPrivileges made a lot of sense. However, it didn't help. Adding ReadFile (and using c:\temp) to that example gives the same behavior. A closer reading of the CreateFile docs shows that even without the SE_BACKUP_NAME privilege, I should be able to open the file due to admin privileges.
I've tried a number of permutations:
Different ways of specifying the directory name (c:\temp, c:\temp\, \\.\c:\temp, \\?\c:\temp\, etc).
Different directories
Different drives
Different share options (0, FILE_SHARE_READ, FILE_SHARE_READ | FILE_SHARE_WRITE)
Different access permissions (GENERIC_READ, FILE_LIST_DIRECTORY, FILE_LIST_DIRECTORY + FILE_READ_EA + FILE_READ_ATTRIBUTES, FILE_LIST_DIRECTORY + FILE_READ_EA + FILE_READ_ATTRIBUTES + FILE_TRAVERSE)
I can't see any flags that might apply other than FILE_FLAG_BACKUP_SEMANTICS (which I assume is required), but I tried FILE_FLAG_NO_BUFFERING and a 4096 byte aligned buffer. Nope.
I'm (currently) trying 152 permutations, and none of the ReadFiles are working. What am I missing?
Is my original assumption here incorrect? Is it not really possible to 'read' from a directory? Or is there just some trick I'm still missing?
What else should I mention?
I'm running as an admin, and can do a CreateFile on the volume.
My program is 64bit, built for unicode.
Windows 7 x64
NTFS 3.1 volume
It's cloudy outside (Hey, you never know what might matter...)
If you want to open a stream then you need to include the stream name and/or type as part of the path:
c:\foo:bar A.K.A. c:\foo:bar:$DATA
c:\foo::$INDEX_ALLOCATION
The default $DATA stream is used if you don't specify a stream. $DATA stores a files "normal data".
If you want the list of files in the directory then you can use GetFileInformationByHandleEx(FileIdBothDirectoryInfo) (and NtQueryDirectoryFile on older systems).
It looks like Jonathan Potter has given the correct answer. Despite prompting, he has elected not to post his comments as an answer. So I'm going to create one based on his responses in order to close the question.
In short: "You can open a handle to a directory to do certain things, but calling ReadFile on it isn't one of them."
What things? These things. This list includes:
BackupRead
BackupSeek
BackupWrite
GetFileInformationByHandle
GetFileSize
GetFileTime
GetFileType
ReadDirectoryChangesW
SetFileTime
In summary: While you can "open" directories and "read" certain information about them, you can't actually use ReadFile. If you want to read the DirName::$INDEX_ALLOCATION information, you'll have to use a different approach.

CreateFileA fails to open HID device in Windows

EDIT: Issue reported here: https://github.com/signal11/hidapi/issues/276
Inkling is a pen-device from Wacom. (InklingReader) is an open source project that gets real-time data from it.
I'm trying to tidy up InklingReader to use HIDAPI rather than libusb (as it works at higher level: HID rather than raw USB, so is much more compact & suitable. Also libusb fails on recent OSX).
HID API a small lib: one .h, one (per-platform) .c.
My code looks like this:
unsigned short inklingVendorId = 0x056a, inklingProductId = 0x0221;
if (hid_init() == FAIL) return;
handle = hid_open(inklingVendorId, inklingProductId, nullptr);
On Windows hid_open fails. Single stepping reveals the fail-point here:
// path = "\\\\?\\hid#vid_056a&pid_0221&mi_00&col01#8&1ea90857&0&0000#"
// "{4d1e55b2-f16f-11cf-88cb-001111000030}"
//
static HANDLE open_device(const char *path, BOOL enumerate)
{
HANDLE handle;
DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ);
DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE;
// enumerate = 0
handle = CreateFileA(path,
desired_access,
share_mode,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/
0);
int err = GetLastError(); // 5 i.e. ERROR_ACCESS_DENIED
return handle; // returns 0xffffffff i.e. INVALID_HANDLE
}
Now the HIDAPI author says "HIDAPI won't work with keyboards and mice on Windows. Windows as a security measure doesn't allow the opening of Mouse and Keyboard HIDs." (here)
And if I enumerate HID devices:
struct hid_device_info *devs, *cur_dev;
devs = hid_enumerate(inklingVendorId, inklingProductId);
cur_dev = devs;
while (cur_dev) {
DBG2("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number);
DBG2("");
DBG2(" Manufacturer: %ls", cur_dev->manufacturer_string);
DBG2(" Product: %ls", cur_dev->product_string);
DBG2(" Release: %hx", cur_dev->release_number);
DBG2(" Interface: %d", cur_dev->interface_number);
DBG2(" Usage Page: %d", cur_dev->usage_page);
DBG2(" Usage: %d", cur_dev->usage);
DBG2("");
cur_dev = cur_dev->next;
}
hid_free_enumeration(devs);
... I get not one but TWO entries:
Device Found
type: 056a 0221
path: \\?\hid#vid_056a&pid_0221&mi_00&col01#8&1ea90857&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
serial_number: 2B0400001C90C22A0002DD07FE8B022A
Manufacturer: Wacom, Inc.
Product: MSC Device
Release: 1256
Interface: 0
Usage Page: 1
Usage: 2
Device Found
type: 056a 0221
path: \\?\hid#vid_056a&pid_0221&mi_00&col02#8&1ea90857&0&0001#{4d1e55b2-f16f-11cf-88cb-001111000030}
serial_number: 2B0400001C90C22A0002DD07FE8B022A
Manufacturer: Wacom, Inc.
Product: MSC Device
Release: 1256
Interface: 0
Usage Page: 13
Usage: 2
(Note: OSX only reports the SECOND entry! On OSX there is no problem!)
Comparing path:
path: \?\hid#vid_056a&pid_0221&mi_00&col01#8&1ea90857&0&0000#...
path: \?\hid#vid_056a&pid_0221&mi_00&col02#8&1ea90857&0&0001#...
As per http://www.usb.org/developers/hidpage/Hut1_12v2.pdf,
UsagePage/Usage = 1/2 = {Generic Desktop Controls}/{Mouse}.
UsagePage/Usage = 13/2 = {Digitizers}/{Pen}.
(EDIT: Sometimes the first path is the 1/2 and the second is the 13/2, other times it's swapped).
And HIDAPI is only taking the first one it finds.
So it looks like this should be the solution. The Inkling was exposing 2 'devices' and hidapi was taking the wrong (mouse) one, and Windows doesn't allow access to Mouse or Keyboard Devices.
So I tweak the code...
while (cur_dev) {
if (cur_dev->vendor_id == vendor_id &&
cur_dev->product_id == product_id &&
cur_dev->usage_page == 13)
{
... to get the correct entry, it should work right?
Nope, CreateFileA just raises a different error:
usage_page== 1 => Error code 5 (ERROR_ACCESS_DENIED)
usage_page==13 => Error code 32 (ERROR_SHARING_VIOLATION)
Meh. This is rather upsetting. I seem to be at a dead-end!
I've tried fiddling with CreateFileA's params, e.g. replacing GENERIC_READ | GENERIC_WRITE with STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE -- now it happily creates a handle. But subsequent hid_read-s fail to collect any data.
Googling, https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/af869f90-7bda-483d-ba2d-51680073fe9f/createfile-returns-invalid-handle-while-trying-to-access-hid-device-on-windows-8-desktop-app?forum=wdk seems to contain a couple of suggested workarounds:
both toaster and firefly can work in the HID stack. toaster shows how
to address the filter through a raw PDO, firefly shows how to access
it with WMI. From a C perspective, I think the raw PDO is much simpler
to code to, WMI is a bit nasty and complicated.
firefly
toaster
The author is recommending something in toaster, but it is a big CodeBase and I don't have experience with Windows Driver programming.
It looks as though I'm going to have to dig through a lot of very unfamiliar territory to get anything working, so before a start out I am asking here. If nobody answers and I figure it out, I will answer my own question.
The only other thing I can think of it is that maybe another process is already engaging this path. Maybe if I can terminate this process, the CreateFileA might succeed? Roel's libusb approach involves detaching kernel driver: https://github.com/roelj/inklingreader/blob/master/src/usb/online-mode.c#L98
PS Somewhere I read that if another process has already opened this device, our open has to match the permissions of this previous open. And I also read that Windows automatically opens all HID Devices upon detection.
Find out which process has an exclusive lock on a USB device handle
PPS maybe one idea is to try an alternative HID lib What is the best usb library to communicate with usb HID devices on Windows?
PPPS maybe I need to run my code as admin. But that's not a good solution.
I have seen similar behavior. The ERROR_SHARING_VIOLATION problem started to occur after upgrading to Windows 10 Anniversary Edition. The problem is only seen for USB HID devices connected when Windows is started. If you unplug and plug the USB device after Windows has started then CreateFile is successful. I haven't yet found a root cause or a solution.
You're right: ERROR_SHARING_VIOLATION will occur if some other app already opened this device. You need to call CreateFileW API like this:
DWORD desired_access = GENERIC_WRITE | GENERIC_READ;
DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
::CreateFileW(deviceInterfacePath, desired_access, share_mode, 0, OPEN_EXISTING, 0, 0);
If you don't provide dwShareMode then it means that you're trying to open device exclusively. Which can fail if other app (new Windows version that maybe supports these kind of devices natively) already opened this device for its use.
Note about Keyboard and Mouse devices: you can also call ::CreateFileW without even setting desired_access (use zero value): in this case you can use HidD_GetManufacturerString/HidD_GetProductString/HidD_GetSerialNumberString/HidD_GetAttributes (and maybe some others) HID methods with returned handle. But you cannot read/write data to such device. This should be useful if you need to acquire name or VID/PID for HID keyboard/mouse.
Here is list of HID device types and their access modes on Windows.

Windows device driver: FSCTL_LOCK_VOLUME on raw partition - Access is Denied

I am currently writing a Windows utility that does a similar job as Linux dd. But this utility only copy USED block instead of cloning the whole disk block by block.
The way I write it is to
*
(1) Copy MBR from disk A which contains three partitions to disk B.
(2) after step A. I am able see three raw partitions from disk management GUI. (no drive letter, no file system).
(3) Copy each partitions from A to B in a loop.*
The problem I have is:
in step (3) after I get the file handle from OpenDevice and I try to use that handle to lock a raw partition, I get Access Denied (error 5) and I am 100% sure that my program did not accidentally hold the lock somewhere else. My dev and testing environment is Windows 2003 server and my utility is executed as Admin privilege.
Here is the code snippet:
...
shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
fHandle= OpenDevice(shareMode);
res = DeviceIoControl(fHandle, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dummy, NULL);
if (res == 0)
{
DismountAndLockVolume();
}
...
in my OpenDevice function in use:
DWORD access = (fOpenMode==forWriting) ? (GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE) : GENERIC_READ| SYNCHRONIZE;
NTOpen(&fHandle, fName.c_str(), access, FILE_ATTRIBUTE_NORMAL, shareMode, FILE_OPEN,FILE_SYNCHRONOUS_IO_NONALERT|FILE_RANDOM_ACCESS|FILE_NON_DIRECTORY_FILE);
to get the file handle(fHandle).
In DismountAndLockVolume function, I try to dismount this volume and grab the lock on it again.
DeviceIoControl(fHandle, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &dummy, NULL);
DWORD shareMode = FILE_SHARE_DELETE | FILE_SHARE_WRITE | FILE_SHARE_READ;
ntStatus = OpenDevice(shareMode);
res = DeviceIoControl(fHandle, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dummy, NULL);
The problem is, when I my code try to dismount this volume, an exception is thrown due to access denied.
Then I browsed the Internet and get the following discussion thread: QUOTE
A write on a volume handle will succeed if the volume is not mounted by a
file system, or if one of the following conditions is true:
1. The sectors to be written to are boot sectors.
2. The sectors to be written to reside outside of file system space.
3. You have explicitly locked or dismounted the volume by using
FSCTL_LOCK_VOLUME or FSCTL_DISMOUNT_VOLUME.
4 . The volume has no file system. (In other words, it has been mounted
as a RAW volume.)
A write on a disk handle will succeed if one of the following conditions
is true:
1. The sectors to be written to do not fall within a volume's extents.
2. The sectors to be written to fall within a mounted volume, but you
have explicitly locked or dismounted the volume by using FSCTL_LOCK_VOLUME
or FSCTL_DISMOUNT_VOLUME.
3. The sectors to be written to fall within a volume that is not mounted
or has no file system.
The modification of some disk parts, like the boot sector ( upto 16 ), is
still allowed . But my utility relies on raw write access to the disk. I am
not able to lock volume by FSCTL_LOCK_VOLUME. Result is ACCESS DENIED. While
searching through net i come to know that kernel mode driver is only
solution. But in this group i come to know driver is not require. I work on
services and pass through SCSI, but i am not able to find the solution. If
kernel mode driver is require then what kind of driver it should be ?
Is anybody know the solution of the problem then please help me out
This thread terminates with no conclusion. Can somebody give me a hand on what was going on? Any hint for possible solution?
Millions of Thanks. I have been scratching my hair for days :-(((.

Differentiating between USB flash drive and USB hard drive on Windows

I'm trying to differentiate between a USB flash drive and a USB hard drive on Windows using the Win32 API.
The GetDriveType() function will return DRIVE_REMOVABLE if the drive is removable, and USB flash drives are of course removable. But I'm thinking that Windows probably considers USB hard drives removable as well (unfortunately I don't have access to a USB hard drive to test it out).
Thanks in advance.
If you want to determine that a device is USB device, you can open its handle and send IOCTL queries using DeviceIoControl() to get bus type a device is connected to.
EnumUsbDrivesLetters - the post is in Russian but it contains C++ source code, so the matter could be understood easily.
Cheers,
Andriy
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Method OpenVolume
// Purpose: Open volume for removal. Change to ::CreateFile(volumeName, 0, 0, 0, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, 0);
// if you just want to inquire if it's removable.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
HANDLE OpenVolume(const char& driveLetter)
{
char volumeName[8] = "";
char* volumeFormat = "\\\\.\\%c:";
sprintf(volumeName, volumeFormat, driveLetter);
HANDLE volume = ::CreateFile(volumeName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (volume == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
DWORD bytesReturned = 0;
STORAGE_HOTPLUG_INFO Info = {0};
if (::DeviceIoControl(volume, IOCTL_STORAGE_GET_HOTPLUG_INFO, 0, 0, &Info, sizeof(Info), &bytesReturned, NULL))
{
if (!(Info.MediaRemovable || Info.DeviceHotplug))
{
::CloseHandle(volume);
::SetLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
}
return volume;
}
Actually windows doesn't, GetDriveType returns 3 (DRIVE_FIXED) for both my usb hard-drives.
Windows returns DRIVE_FIXED for external USB hard drives and usually returns DRIVE_REMOVABLE for USB flash sticks. For this reason if you want to access multiple partitions on a flash memory you have to install a filter driver to tell windows it's not a DRIVE_REMOVABLE but a DRIVE_FIXED instead. Windows only "sees" the first partition on flash sticks causing a lot of trouble for ESXi boot usb stick users ;-)
I thinks the key is drive properties, eg Cylinder count. You can use WMI interface to determine such information. Here is an example http://www.computerperformance.co.uk/vbscript/wmi_disks_physical.htm
The drive type is ultimately determined by the drivers; there's no fail-safe way to make the sort of determination that you're looking for.
I can say, however, that while I have seen a USB flash stick return DRIVE_FIXED, I have never seen a normal hard drive return DRIVE_REMOVEABLE. That's not to say that it's completely impossible for that to happen, but I've never seen it.
I'd say relying on those two values is probably the closest that you're going to get.
http://en.wikipedia.org/wiki/SCSI_Pass_Through_Interface will let you send raw SCSI commands to the device - you want to send down either INQUIRY or MODE SENSE to find out what you're looking for. However, a far better alternative may be the VDS APIs, if it will provide you correct information (I'm not sure whether it will in this case)

Resources