I am trying to check does any disk is present in drive A: (after my program installs i need to ensure that computer won't boot from installation diskette). I've tried using _access method (undefined reference...), FILE* and making directory inside diskette and remove it after checking. Unfortunately DOS displays ugly piece of text about putting disk in drive (Destroying my TUI and making user think that diskette in drive is important). So how to suppress this message, or safely check does disk is present in drive?
Possibly BIOS INT 13H 16H: Detect Media Change - it has a status:
80H = diskette drive not ready or not installed
Which may solve your problem - I lack the antique hardware and software to test it personally.
#include <dos.h>
unsigned int DetectMediaChange()
{
union REGS regs;
regs.h.ah = 0x16; // Detect Media Change
regs.h.dl = 0; // Drive A
int86( 0x13, ®s, ®s ); // BIOS Disk I/O INT 13h
return regs.h.ah ; // Status : 00H = diskette change line not active
// 01H = invalid drive number
// 06H = either change line is not supported or
// disk change line is active (media was swapped)
// 80H = diskette drive not ready or not installed
// else= BIOS disk error code if CF is set to CY
}
Okay, I've figured it out:
char far * bufptr;
union REGS inregs, outregs;
struct SREGS segregs;
char buf [1024];
avaliable(){
redo:
segread(&segregs);
bufptr = (char far *) buf;
segregs.es = FP_SEG(bufptr);
inregs.x.bx = FP_OFF(bufptr);
inregs.h.ah = 2;
inregs.h.al = 1;
inregs.h.ch = 0;
inregs.h.cl = 1;
inregs.h.dh = 0;
inregs.h.dl = 0;
int86x(0x13, &inregs, &outregs, &segregs);
return outregs.x.cflag;
}
Returns true if disk is in drive.
i have a problem. I making simple OS kernel with this tutorial: http://wiki.osdev.org/Bare_Bones#Linking_the_Kernel
but,if i want to link files boot.o and kernel.o, gcc compiler returns this error:
boot.o: In function `start':
boot.asm:(.text+0x6): undefined reference to `kernel_main'
collect2.exe: error: ld returned 1 exit status.
sources of files:
boot.asm
; Declare constants used for creating a multiboot header.
MBALIGN equ 1<<0 ; align loaded modules on page boundaries
MEMINFO equ 1<<1 ; provide memory map
FLAGS equ MBALIGN | MEMINFO ; this is the Multiboot 'flag' field
MAGIC equ 0x1BADB002 ; 'magic number' lets bootloader find the header
CHECKSUM equ -(MAGIC + FLAGS) ; checksum of above, to prove we are multiboot
; Declare a header as in the Multiboot Standard. We put this into a special
; section so we can force the header to be in the start of the final program.
; You don't need to understand all these details as it is just magic values that
; is documented in the multiboot standard. The bootloader will search for this
; magic sequence and recognize us as a multiboot kernel.
section .multiboot
align 4
dd MAGIC
dd FLAGS
dd CHECKSUM
; Currently the stack pointer register (esp) points at anything and using it may
; cause massive harm. Instead, we'll provide our own stack. We will allocate
; room for a small temporary stack by creating a symbol at the bottom of it,
; then allocating 16384 bytes for it, and finally creating a symbol at the top.
section .bootstrap_stack
align 4
stack_bottom:
times 16384 db 0
stack_top:
; The linker script specifies _start as the entry point to the kernel and the
; bootloader will jump to this position once the kernel has been loaded. It
; doesn't make sense to return from this function as the bootloader is gone.
section .text
global _start
_start:
; Welcome to kernel mode! We now have sufficient code for the bootloader to
; load and run our operating system. It doesn't do anything interesting yet.
; Perhaps we would like to call printf("Hello, World\n"). You should now
; realize one of the profound truths about kernel mode: There is nothing
; there unless you provide it yourself. There is no printf function. There
; is no <stdio.h> header. If you want a function, you will have to code it
; yourself. And that is one of the best things about kernel development:
; you get to make the entire system yourself. You have absolute and complete
; power over the machine, there are no security restrictions, no safe
; guards, no debugging mechanisms, there is nothing but what you build.
; By now, you are perhaps tired of assembly language. You realize some
; things simply cannot be done in C, such as making the multiboot header in
; the right section and setting up the stack. However, you would like to
; write the operating system in a higher level language, such as C or C++.
; To that end, the next task is preparing the processor for execution of
; such code. C doesn't expect much at this point and we only need to set up
; a stack. Note that the processor is not fully initialized yet and stuff
; such as floating point instructions are not available yet.
; To set up a stack, we simply set the esp register to point to the top of
; our stack (as it grows downwards).
mov esp, stack_top
; We are now ready to actually execute C code. We cannot embed that in an
; assembly file, so we'll create a kernel.c file in a moment. In that file,
; we'll create a C entry point called kernel_main and call it here.
extern kernel_main
call kernel_main
; In case the function returns, we'll want to put the computer into an
; infinite loop. To do that, we use the clear interrupt ('cli') instruction
; to disable interrupts, the halt instruction ('hlt') to stop the CPU until
; the next interrupt arrives, and jumping to the halt instruction if it ever
; continues execution, just to be safe.
cli
.hang:
hlt
jmp .hang
kernel.c
#if !defined(__cplusplus)
#include <stdbool.h> /* C doesn't have booleans by default. */
#endif
#include <stddef.h>
#include <stdint.h>
/* Check if the compiler thinks if we are targeting the wrong operating system. */
#if defined(__linux__)
#error "You are not using a cross-compiler, you will most certainly run into trouble"
#endif
/* This tutorial will only work for the 32-bit ix86 targets. */
#if !defined(__i386__)
#error "This tutorial needs to be compiled with a ix86-elf compiler"
#endif
/* Hardware text mode color constants. */
enum vga_color
{
COLOR_BLACK = 0,
COLOR_BLUE = 1,
COLOR_GREEN = 2,
COLOR_CYAN = 3,
COLOR_RED = 4,
COLOR_MAGENTA = 5,
COLOR_BROWN = 6,
COLOR_LIGHT_GREY = 7,
COLOR_DARK_GREY = 8,
COLOR_LIGHT_BLUE = 9,
COLOR_LIGHT_GREEN = 10,
COLOR_LIGHT_CYAN = 11,
COLOR_LIGHT_RED = 12,
COLOR_LIGHT_MAGENTA = 13,
COLOR_LIGHT_BROWN = 14,
COLOR_WHITE = 15,
};
uint8_t make_color(enum vga_color fg, enum vga_color bg)
{
return fg | bg << 4;
}
uint16_t make_vgaentry(char c, uint8_t color)
{
uint16_t c16 = c;
uint16_t color16 = color;
return c16 | color16 << 8;
}
size_t strlen(const char* str)
{
size_t ret = 0;
while ( str[ret] != 0 )
ret++;
return ret;
}
static const size_t VGA_WIDTH = 80;
static const size_t VGA_HEIGHT = 25;
size_t terminal_row;
size_t terminal_column;
uint8_t terminal_color;
uint16_t* terminal_buffer;
void terminal_initialize()
{
terminal_row = 0;
terminal_column = 0;
terminal_color = make_color(COLOR_LIGHT_GREY, COLOR_BLACK);
terminal_buffer = (uint16_t*) 0xB8000;
for ( size_t y = 0; y < VGA_HEIGHT; y++ )
{
for ( size_t x = 0; x < VGA_WIDTH; x++ )
{
const size_t index = y * VGA_WIDTH + x;
terminal_buffer[index] = make_vgaentry(' ', terminal_color);
}
}
}
void terminal_setcolor(uint8_t color)
{
terminal_color = color;
}
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y)
{
const size_t index = y * VGA_WIDTH + x;
terminal_buffer[index] = make_vgaentry(c, color);
}
void terminal_putchar(char c)
{
terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
if ( ++terminal_column == VGA_WIDTH )
{
terminal_column = 0;
if ( ++terminal_row == VGA_HEIGHT )
{
terminal_row = 0;
}
}
}
void terminal_writestring(const char* data)
{
size_t datalen = strlen(data);
for ( size_t i = 0; i < datalen; i++ )
terminal_putchar(data[i]);
}
void kernel_main()
{
terminal_initialize();
/* Since there is no support for newlines in terminal_putchar yet, \n will
produce some VGA specific character instead. This is normal. */
terminal_writestring("Hello\n");
}
It looks like you’re using GCC on Microsoft® Windows® (for example, with Cygwin), judging from the collect2.exe reference. This means your native executable format, which you appear to be using, prepends an underscore to C identifiers to keep them separate from assembly identifiers, which is something most object formats, but not the ELF format wide-spread under modern Unix, does.
If you change your call to _kernel_main, the link error will likely go away.
But please note this line, quoted from your question:
#error "This tutorial needs to be compiled with a ix86-elf compiler"
You’re violating a basic tenet of the tutorial you’re using. I suggest you get a GNU/Linux or BSD VM for i386 (32-bit), and run the tutorial within that.
I'm developing on an AD Blackfin BF537 DSP running uClinux. I have a total of 32MB SD-RAM available. I have an ADC attached, which I can access using a simple, blocking call to read().
The most interesting part of my code is below. Running the program seems to work just fine, I get a nice data package that I can fetch from the SD-card and plot. However, if I comment out the float calculation part (as noted in the code), I get only zeroes in the ft_all.raw file. The same occurs if I change optimization level from -O3 to -O0.
I've tried countless combinations of all sorts of things, and sometimes it works, sometimes it does not - earlier (with minor modifications to below), the code would only work when optimization was disabled. It may also break if I add something else further down in the file.
My suspicion is that the data transferred by the read()-function may not have been transferred fully (is that possible, even though it returns the correct number of bytes?). This is also the first time I initialize pointers using direct memory adresses, and I have no idea how the compiler reacts to this - perhaps I missed something, here?
I've spent days on this issue now, and I'm getting desperate - I would really appreciate some help on this one! Thanks in advance.
// Clear the top 16M memory for data processing
memset((int *)0x01000000,0x0000,(size_t)SIZE_16M);
/* Prep some pointers for data processing */
int16_t *buffer;
int16_t *buf16I, *buf16Q;
buffer = (int16_t *)(0x1000000);
buf16I = (int16_t *)(0x1600000);
buf16Q = (int16_t *)(0x1680000);
/* Read data from ADC */
int rbytes = read(Sportfd, (int16_t*)buffer, 0x200000);
if (rbytes != 0x200000) {
printf("could not sample data! %X\n",rbytes);
goto end;
} else {
printf("Read %X bytes\n",rbytes);
}
FILE *outfd;
int wbytes;
/* Commenting this region results in all zeroes in ft_all.raw */
float a,b;
int c;
b = 0;
for (c = 0; c < 1000; c++) {
a = c;
b = b+pow(a,3);
}
printf("b is %.2f\n",b);
/* Only 12 LSBs of each 32-bit word is actual data.
* First 20 bits of nothing, then 12 bits I, then 20 bits
* nothing, then 12 bits Q, etc...
* Below, the I and Q parts are scaled with a factor of 16
* and extracted to buf16I and buf16Q.
* */
int32_t *buf32;
buf32 = (int32_t *)buffer;
uint32_t i = 0;
uint32_t n = 0;
while (n < 0x80000) {
buf16I[i] = buf32[n] << 4;
n++;
buf16Q[i] = buf32[n] << 4;
i++;
n++;
}
printf("Saving to /mnt/sd/d/ft_all.raw...");
outfd = fopen("/mnt/sd/d/ft_all.raw", "w+");
if (outfd == NULL) {
printf("Could not open file.\n");
}
wbytes = fwrite((int*)0x1600000, 1, 0x100000, outfd);
fclose(outfd);
if (wbytes < 0x100000) {
printf("wbytes not correct (= %d) \n", (int)wbytes);
}
printf(" done.\n");
Edit: The code seems to work perfectly well if I use read() to read data from a simple file rather than the ADC. This leads me to believe that the rather hacky-looking code when extracting the I and Q parts of the input is working as intended. Inspecting the assembly generated by the compiler confirms this.
I'm trying to get in touch with the developer of the ADC driver to see if he has an explanation of this behaviour.
The ADC is connected through a SPORT, and is opened as such:
sportfd = open("/dev/sport1", O_RDWR);
ioctl(sportfd, SPORT_IOC_CONFIG, spconf);
And here are the options used when configuring the SPORT:
spconf->int_clk = 1;
spconf->word_len = 32;
spconf->serial_clk = SPORT_CLK;
spconf->fsync_clk = SPORT_CLK/34;
spconf->fsync = 1;
spconf->late_fsync = 1;
spconf->act_low = 1;
spconf->dma_enabled = 1;
spconf->tckfe = 0;
spconf->rckfe = 1;
spconf->txse = 0;
spconf->rxse = 1;
A bfin_sport.h file from Analog Devices is also included: https://gist.github.com/tausen/5516954
Update
After a long night of debugging with the previous developer on the project, it turned out the issue was not related to the code shown above at all. As Chris suggested, it was indeed an issue with the SPORT driver and the ADC configuration.
While debugging, this error messaged appeared whenever the data was "broken": bfin_sport: sport ffc00900 status error: TUVF. While this doesn't make much sense in the application, it was clear from printing the data, that something was out of sync: the data in buffer was on the form 0x12000000,0x34000000,... rather than 0x00000012,0x00000034,... whenever the status error was shown. It seems clear then, why buf16I and buf16Q only contained zeroes (since I am extracting the 12 LSBs).
Putting in a few calls to usleep() between stages of ADC initialization and configuration seems to have fixed the issue - I'm hoping it stays that way!
I have been experimenting with the devkitARM toolchain for NDS homebrew recently. Something I would like better understand, however, is how to control sprite animation speeds. The only way I know of doing this is by 'counting frames'. For example, the following code could be placed into the "animate_simple" example included with devkitpro:
int main(void) {
int frame = 0;
...
while(1) {
...
if(++frame > 9)
frame = 0;
}
return 0;
}
This is generally fine, but it ensures that all the animation initialized in the main loop runs at a set speed. How would I go about having two different sprites, each animating at different speeds? Any insight would be greatly appreciated.
Use a separate frame counter for each sprite. For example, you can create a sprite struct:
typedef struct _Sprite Sprite;
struct _Sprite
{
int frame;
int count;
int delay; /* handles the speed of animation */
int limit;
};
// initialize all fields
void sprite_update(Sprite* s)
{
if ( ( s->count++ % s->delay ) == 0 ) )
{
if ( s->frame++ > s->limit ) s->frame = 0;
}
}
Give delay small value for faster animation, and a larger value for slow animation.
Sample Usage:
Sprite my_spr, my_spr_2;
/* initialize the fields, or write some function for initializing them. */
my_spr.delay = 5; /* fast */
my_spr_2.delay = 10; /* slow */
/* < in Main Loop > */
while(1){
...
sprite_update(&my_spr);
sprite_update(&my_spr_2);
}
Note:
Since you are targeting only one platform, the best way to control animation speed is to monitor the frame rate ( or "counting frames" as you call it ). Good thing about programming for consoles is, you don't have to set timed delays. Since all console devices of the same model usually run at the same speed, so the animation speed you get on your machine ( or emulator ) will be the same everyone gets. Different Processors Speeds are a real headache when programming for the PC.
How to list physical disks in Windows?
In order to obtain a list of "\\\\.\PhysicalDrive0" available.
#WMIC
wmic is a very complete tool
wmic diskdrive list
provide a (too much) detailed list, for instance
for less info
wmic diskdrive list brief
#C
Sebastian Godelet mentions in the comments:
In C:
system("wmic diskdrive list");
As commented, you can also call the WinAPI, but... as shown in "How to obtain data from WMI using a C Application?", this is quite complex (and generally done with C++, not C).
#PowerShell
Or with PowerShell:
Get-WmiObject Win32_DiskDrive
Update Feb. 2022, Microsoft announces in "Windows 10 features we're no longer developing"
The WMIC tool is deprecated in Windows 10, version 21H1 and the 21H1 General Availability Channel release of Windows Server.
This tool is superseded by Windows PowerShell for WMI.
Note: This deprecation only applies to the command-line management tool. WMI itself is not affected.
One way to do it:
Enumerate logical drives using GetLogicalDrives
For each logical drive, open a file named "\\.\X:" (without the quotes) where X is the logical drive letter.
Call DeviceIoControl passing the handle to the file opened in the previous step, and the dwIoControlCode parameter set to IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS:
HANDLE hHandle;
VOLUME_DISK_EXTENTS diskExtents;
DWORD dwSize;
[...]
iRes = DeviceIoControl(
hHandle,
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
NULL,
0,
(LPVOID) &diskExtents,
(DWORD) sizeof(diskExtents),
(LPDWORD) &dwSize,
NULL);
This returns information of the physical location of a logical volume, as a VOLUME_DISK_EXTENTS structure.
In the simple case where the volume resides on a single physical drive, the physical drive number is available in diskExtents.Extents[0].DiskNumber
This might be 5 years too late :). But as I see no answer for this yet, adding this.
We can use Setup APIs to get the list of disks ie., devices in the system implementing GUID_DEVINTERFACE_DISK.
Once we have their device paths, we can issue IOCTL_STORAGE_GET_DEVICE_NUMBER to construct "\\.\PHYSICALDRIVE%d" with STORAGE_DEVICE_NUMBER.DeviceNumber
See also SetupDiGetClassDevs function
#include <Windows.h>
#include <Setupapi.h>
#include <Ntddstor.h>
#pragma comment( lib, "setupapi.lib" )
#include <iostream>
#include <string>
using namespace std;
#define START_ERROR_CHK() \
DWORD error = ERROR_SUCCESS; \
DWORD failedLine; \
string failedApi;
#define CHK( expr, api ) \
if ( !( expr ) ) { \
error = GetLastError( ); \
failedLine = __LINE__; \
failedApi = ( api ); \
goto Error_Exit; \
}
#define END_ERROR_CHK() \
error = ERROR_SUCCESS; \
Error_Exit: \
if ( ERROR_SUCCESS != error ) { \
cout << failedApi << " failed at " << failedLine << " : Error Code - " << error << endl; \
}
int main( int argc, char **argv ) {
HDEVINFO diskClassDevices;
GUID diskClassDeviceInterfaceGuid = GUID_DEVINTERFACE_DISK;
SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData;
DWORD requiredSize;
DWORD deviceIndex;
HANDLE disk = INVALID_HANDLE_VALUE;
STORAGE_DEVICE_NUMBER diskNumber;
DWORD bytesReturned;
START_ERROR_CHK();
//
// Get the handle to the device information set for installed
// disk class devices. Returns only devices that are currently
// present in the system and have an enabled disk device
// interface.
//
diskClassDevices = SetupDiGetClassDevs( &diskClassDeviceInterfaceGuid,
NULL,
NULL,
DIGCF_PRESENT |
DIGCF_DEVICEINTERFACE );
CHK( INVALID_HANDLE_VALUE != diskClassDevices,
"SetupDiGetClassDevs" );
ZeroMemory( &deviceInterfaceData, sizeof( SP_DEVICE_INTERFACE_DATA ) );
deviceInterfaceData.cbSize = sizeof( SP_DEVICE_INTERFACE_DATA );
deviceIndex = 0;
while ( SetupDiEnumDeviceInterfaces( diskClassDevices,
NULL,
&diskClassDeviceInterfaceGuid,
deviceIndex,
&deviceInterfaceData ) ) {
++deviceIndex;
SetupDiGetDeviceInterfaceDetail( diskClassDevices,
&deviceInterfaceData,
NULL,
0,
&requiredSize,
NULL );
CHK( ERROR_INSUFFICIENT_BUFFER == GetLastError( ),
"SetupDiGetDeviceInterfaceDetail - 1" );
deviceInterfaceDetailData = ( PSP_DEVICE_INTERFACE_DETAIL_DATA ) malloc( requiredSize );
CHK( NULL != deviceInterfaceDetailData,
"malloc" );
ZeroMemory( deviceInterfaceDetailData, requiredSize );
deviceInterfaceDetailData->cbSize = sizeof( SP_DEVICE_INTERFACE_DETAIL_DATA );
CHK( SetupDiGetDeviceInterfaceDetail( diskClassDevices,
&deviceInterfaceData,
deviceInterfaceDetailData,
requiredSize,
NULL,
NULL ),
"SetupDiGetDeviceInterfaceDetail - 2" );
disk = CreateFile( deviceInterfaceDetailData->DevicePath,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
CHK( INVALID_HANDLE_VALUE != disk,
"CreateFile" );
CHK( DeviceIoControl( disk,
IOCTL_STORAGE_GET_DEVICE_NUMBER,
NULL,
0,
&diskNumber,
sizeof( STORAGE_DEVICE_NUMBER ),
&bytesReturned,
NULL ),
"IOCTL_STORAGE_GET_DEVICE_NUMBER" );
CloseHandle( disk );
disk = INVALID_HANDLE_VALUE;
cout << deviceInterfaceDetailData->DevicePath << endl;
cout << "\\\\?\\PhysicalDrive" << diskNumber.DeviceNumber << endl;
cout << endl;
}
CHK( ERROR_NO_MORE_ITEMS == GetLastError( ),
"SetupDiEnumDeviceInterfaces" );
END_ERROR_CHK();
Exit:
if ( INVALID_HANDLE_VALUE != diskClassDevices ) {
SetupDiDestroyDeviceInfoList( diskClassDevices );
}
if ( INVALID_HANDLE_VALUE != disk ) {
CloseHandle( disk );
}
return error;
}
The answer is far simpler than all the above answers. The physical drive list is actually stored in a Registry key which also gives the device mapping.
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\disk\Enum
Count is the number of PhysicalDrive# and each numbered Registry Value is the corresponding physical drive.
For example, Registry Value "0" is PhysicalDrive0. The value is the actual device PhysicalDrive0 is mapped to. The value contained here can be passed into CM_Locate_DevNode within parameter pDeviceID to use the plug and play services. This will allow you to gather a wealth of information on the device. Such as the properties from Device Manager like "Friendly Display Name" if you need a name for the drive, serial numbers and more.
There is no need for WMI services which may not be running on the system or other hackery and this functionality has been present in Windows since at least 2000 and continues to be the case in Windows 10.
I've modified an open-source program called "dskwipe" in order to pull this disk information out of it. Dskwipe is written in C, and you can pull this function out of it. The binary and source are available here: dskwipe 0.3 has been released
The returned information will look something like this:
Device Name Size Type Partition Type
------------------------------ --------- --------- --------------------
\\.\PhysicalDrive0 40.0 GB Fixed
\\.\PhysicalDrive1 80.0 GB Fixed
\Device\Harddisk0\Partition0 40.0 GB Fixed
\Device\Harddisk0\Partition1 40.0 GB Fixed NTFS
\Device\Harddisk1\Partition0 80.0 GB Fixed
\Device\Harddisk1\Partition1 80.0 GB Fixed NTFS
\\.\C: 80.0 GB Fixed NTFS
\\.\D: 2.1 GB Fixed FAT32
\\.\E: 40.0 GB Fixed NTFS
The only correct answer is the one by #Grodriguez, and here's a code that he was too lazy to write:
#include <windows.h>
#include <iostream>
#include <bitset>
#include <vector>
using namespace std;
typedef struct _DISK_EXTENT {
DWORD DiskNumber;
LARGE_INTEGER StartingOffset;
LARGE_INTEGER ExtentLength;
} DISK_EXTENT, *PDISK_EXTENT;
typedef struct _VOLUME_DISK_EXTENTS {
DWORD NumberOfDiskExtents;
DISK_EXTENT Extents[ANYSIZE_ARRAY];
} VOLUME_DISK_EXTENTS, *PVOLUME_DISK_EXTENTS;
#define CTL_CODE(DeviceType, Function, Method, Access) \
(((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
#define IOCTL_VOLUME_BASE ((DWORD)'V')
#define METHOD_BUFFERED 0
#define FILE_ANY_ACCESS 0x00000000
#define IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS CTL_CODE(IOCTL_VOLUME_BASE, 0, METHOD_BUFFERED, FILE_ANY_ACCESS)
int main() {
bitset<32> drives(GetLogicalDrives());
vector<char> goodDrives;
for (char c = 'A'; c <= 'Z'; ++c) {
if (drives[c - 'A']) {
if (GetDriveType((c + string(":\\")).c_str()) == DRIVE_FIXED) {
goodDrives.push_back(c);
}
}
}
for (auto & drive : goodDrives) {
string s = string("\\\\.\\") + drive + ":";
HANDLE h = CreateFileA(
s.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_RANDOM_ACCESS, NULL
);
if (h == INVALID_HANDLE_VALUE) {
cerr << "Drive " << drive << ":\\ cannot be opened";
continue;
}
DWORD bytesReturned;
VOLUME_DISK_EXTENTS vde;
if (!DeviceIoControl(
h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
NULL, 0, &vde, sizeof(vde), &bytesReturned, NULL
)) {
cerr << "Drive " << drive << ":\\ cannot be mapped into physical drive";
continue;
}
cout << "Drive " << drive << ":\\ is on the following physical drives: ";
for (int i = 0; i < vde.NumberOfDiskExtents; ++i) {
cout << vde.Extents[i].DiskNumber << ' ';
}
cout << endl;
}
}
I think that installation of Windows Driver Development Kit is quite a lengthy process, so I've included the declarations one needs to use DeviceIoControl for this task.
The only sure shot way to do this is to call CreateFile() on all \\.\Physicaldiskx where x is from 0 to 15 (16 is maximum number of disks allowed). Check the returned handle value. If invalid check GetLastError() for ERROR_FILE_NOT_FOUND. If it returns anything else then the disk exists but you cannot access it for some reason.
GetLogicalDrives() enumerates all mounted disk partitions, not physical drives.
You can enumerate the drive letters with (or without) GetLogicalDrives, then call QueryDosDevice() to find out which physical drive the letter is mapped to.
Alternatively, you can decode the information in the registry at HKEY_LOCAL_MACHINE\SYSTEM\MountedDevices. The binary data encodings there are not obvious, however. If you have a copy of Russinovich and Solomon's book Microsoft Windows Internals, this registry hive is discussed in Chapter 10.
Thic WMIC command combination works fine:
wmic volume list brief
If you only need to look at the existing disks, this one will suffice:
powershell "get-physicaldisk"
Might want to include the old A: and B: drives as you never know who might be using them!
I got tired of USB drives bumping my two SDHC drives that are just for Readyboost.
I had been assigning them to High letters Z: Y: with a utility that will assign drive letters to devices as you wish. I wondered.... Can I make a Readyboost drive letter A: ? YES!
Can I put my second SDHC drive letter as B: ? YES!
I've used Floppy Drives back in the day, never thought that A: or B: would come in handy for
Readyboost.
My point is, don't assume A: & B: will not be used by anyone for anything
You might even find the old SUBST command being used!
Here is a new solution of doing it with doing WMI calls.
Then all you need to do is just to call :
queryAndPrintResult(L"SELECT * FROM Win32_DiskDrive", L"Name");
I just ran across this in my RSS Reader today. I've got a cleaner solution for you. This example is in Delphi, but can very easily be converted to C/C++ (It's all Win32).
Query all value names from the following registry location:
HKLM\SYSTEM\MountedDevices
One by one, pass them into the following function and you will be returned the device name. Pretty clean and simple! I found this code on a blog here.
function VolumeNameToDeviceName(const VolName: String): String;
var
s: String;
TargetPath: Array[0..MAX_PATH] of WideChar;
bSucceeded: Boolean;
begin
Result := ”;
// VolumeName has a format like this: \\?\Volume{c4ee0265-bada-11dd-9cd5-806e6f6e6963}\
// We need to strip this to Volume{c4ee0265-bada-11dd-9cd5-806e6f6e6963}
s := Copy(VolName, 5, Length(VolName) - 5);
bSucceeded := QueryDosDeviceW(PWideChar(WideString(s)), TargetPath, MAX_PATH) <> 0;
if bSucceeded then
begin
Result := TargetPath;
end
else begin
// raise exception
end;
end;
If you want "physical" access, we're developing this API that will eventually allows you to communicate with storage devices. It's open source and you can see the current code for some information. Check back for more features:
https://github.com/virtium/vtStor
Make a list of all letters in the US English Alphabet, skipping a & b. "CDEFGHIJKLMNOPQRSTUVWXYZ". Open each of those drives with CreateFile e.g. CreateFile("\\.\C:"). If it does not return INVALID_HANDLE_VALUE then you got a 'good' drive. Next take that handle and run it through DeviceIoControl to get the Disk #. See my related answer for more details.