Recently, I was try to mount iSCSI volume to windows. This document is about create partition and format it programmatically using C/C++.
After googling and coding and testing, as to my conclusion, there are 4 solutions to complete the job.
1, System internal tool
It seems that the system-internal tool set provide a dll which contains some undocumented API from MS, to format a disk, but I don’t like the way it do and actually, I can’t reach the introduction page, seems it is been remove by MS.
2, Using DeviceIoControl API
This seem to be an old fashion. Windows provide a set of IO control command communicate to the low-layer driver, using CREATE_DISK to initialize a raw disk, and IOCTL_DISK_SET_DRIVE_LAYOUT_EX to setup partition, but I can’t find a way to format it. That is wired, as it provide IOCTL_DISK_FORMAT_TRACKS_EX, which operate for floppy disk devices only, why?
3, WMI COM interface and Win32_Volume
As DeviceIoControl API could not accomplish my job, I found another set of API, much more modern, in script-style way. MS introduce WMI interface for a while, but I never using it cause I am on Linux for a long time and before that, I am working on a customized OS for even longer time. MS provide this set of interface I think majorly for all script-base application, many some for Power Shell, VB, C#, and some C++. It looks sweet, but… Let’s take a look at it closely and step by step.
- Create IWbemLocator instance;
- Connect to “ROOT\\CIMV2“, setup run-time environment;
- Execute a WQL query: “SELECT * FROM Win32_Volume” to get an enumeration of logic volumes(partitions) in the system;
- Walk through the the enumerator, select a target volume, and make it happen.
Wow, sounds nice and clear, but wait, what is error WBEM_E_ILLEGAL_OPERATION means when I try to get the “Format” method in step d? At step d, I could get all the property from Win32_Volume instance, but no execution right. WBEM_E_ILLEGAL_OPERATION, “User requested an illegal operation, such as spawning a class from an instance”. I don’t really know what the hell is going on. If I am using IWbemServices->GetObject(L”Win32_Volume”, …) to get an instance, then I do find the “Format” method, but I don’t think I dare to execute the method, because I don’t know which “volume object” I am about to kill. I get this object without context, should I give it correct context one by one? No, I won’t do that, and don’t know how any way.
I see lot of samples about Win32_Volume interface, written in Power Shell as script or C# management code. Looks simple, I believe they work, but not for C++. Maybe C++ is the one really out of fashion. But in person, I don’t like any script for any reason, and I believe that MS forget C++ user this time.
4, Vds service
VDS interface is the last one I found during research, it includes all the interfaces I need but also has a fatal problem: seems that it could not operate on a uninitialized disk, at lease I did not figure out the right way to do that. So, finally, I have to use both DeviceIoControl(CREATE_DISK, IOCTL_DISK_SET_DRIVE_LAYOUT_EX) to create partition, and IVdsVolumeMF to format it, to accomplish my work..
Here is the skeleton code to manage a RAW disk in C/C++:
- All start from a physical disk device name, say “\\.\PhysicalDriver2”, which provide by iSCSI API. Using this name to create a handle by CreateFile(LegacyName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
- using DeviceIoControl to initialize raw disk and create GPT partition.
- IOCTL_DISK_CREATE_DISK to initialize a raw disk,
- wait for operation to complete, by listening WM_DEVICECHANGE, when wParam = DBT_DEVNODES_CHANGED for twice.
- IOCTL_DISK_UPDATE_PROPERTIES to flush changes
- IOCTL_DISK_SET_DRIVE_LAYOUT_EX, set the new layout(partition). I create a new GPT partition, using CoCreateGuid to make a new GUID for the disk. This is the key identification for the following format procedure to recognize the disk and format volume on it.
- IOCTL_DISK_UPDATE_PROPERTIES to flush changes again.
- Here we have an new partition(s), then try to format it silently in back ground using VDS
- load IVdsServiceand wait for service to be ready by WaitForServiceReady
- Query VDS_QUERY_SOFTWARE_PROVIDERS IVdsSwProvider
- For each IVdsSwProvider, query IVdsPack
- For each IVdsPack, query IVdsDisk
- For each IVdsDisk, I check GUID, partition type (is VDS_PST_GPT or not), and bustype(is VDSBusTypeiScsi or not). the GUID is the one created in section 2.D. If every thing match perfectly, then next step;
- IVdsDisk->GetPack->QueryVolumes to get an enumerator of all partitions (volumes) on the disk, for each volume, QueryInterface(IID_IVdsVolumeMF)to get IVdsVolumeMF interface, then you know what should be done. Using IVdsAsync to wait for format operation to be done, that is quite straight and easy. Finally, if we need to be, set the access path for new volume using QueryAccessPaths, DeleteAccessPath and AddAccessPath.
- One thing need to mention here: when a new volume created in system, it is possible that the system bring up a windows prompt for “Format disk?”, that is not what I expecting, I need to format the volume using my special parameter. To disable this prompt, need to stop/restart server “ShellHWDetection“, there is a sample about how to control a service, it really works.