How to read file remotely? - windows

I have such a method for reading file by path
void test_read_file(std::string const & fileName)
{
std::ifstream file(fileName);
if (!file.is_open())
{
std::cout << "LoadFromFile FAILED to load: " << fileName << std::endl; <--- this line
}
...
}
And this method works well if I pass as a path C:\\..., but if I pass something like this \\\\my_remote_drive\\my_user\\.... it does not work and I came to line that I marked above.
It looks like a lack of permission or something like this...
What am I doing wrong?

You need to make a connection to a network resource first, with WNetAddConnection2A(Similar APIs are NetUseAdd, NPAddConnection, etc.) and this function could also redirect a local device to the network resource.
lpRemoteName:
An example for a share might be the following: \192.168.1.1\share
NETRESOURCE nr = {0};
nr.dwType = RESOURCETYPE_ANY;
nr.lpLocalName = NULL; //does not assign a drive.
nr.lpRemoteName = "\\192.168.1.1\\share";
DWORD ret = WNetAddConnection2 (&nr, NULL, NULL, CONNECT_TEMPORARY);
To assigning a Drive to a Share:
You could refer to Assigning a Drive to a Share

Related

Would it be possible to open a file on an external device without copying it in INetCache?

I'm writing a small image viewer application, which use the Shell API to access to the files.
I noticed that, every time I access an image on an external device, like my connected phone, the file is copied in a local cache, named INetCache.
Below is an example of an image path on my external device:
This PC\Apple iPhone\Internal Storage\DCIM\202005__\IMG_2768.HEIC
and the same image path from the PIDL received by the Shell when I try to open it:
C:\Users\Admin\AppData\Local\Microsoft\Windows\INetCache\IE\MD7CNXNX\IMG_2768[1].HEIC
As you can see, the file was copied in the INetCache, and even duplicated. Would it be possible to open the file from my external device without copying it in the INetCache? And if yes, how may I achieve that (if possible using the Shell API)?
UPDATE on 11.08.2022
Below is the code I use to get the PIDL from an IDataObject I receive after a drag&drop operation, which contains the file to open:
std::wstring GetPIDLFromDataObject(IDataObject* pDataObject)
{
std::wstring line = L"PIDL result:\r\n";
if (!pDataObject)
return line;
CComPtr<IShellItemArray> pShellItemArray;
if (FAILED(::SHCreateShellItemArrayFromDataObject(pDataObject, IID_PPV_ARGS(&pShellItemArray))))
return line;
CComPtr<IEnumShellItems> pShellItemEnum;
pShellItemArray->EnumItems(&pShellItemEnum);
if (!pShellItemEnum)
return line;
for (CComPtr<IShellItem> pShellItem; pShellItemEnum->Next(1, &pShellItem, nullptr) == S_OK; pShellItem.Release())
{
CComHeapPtr<wchar_t> spszName;
if (SUCCEEDED(pShellItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &spszName)))
{
line += spszName;
line += L"\r\n";
}
CComHeapPtr<ITEMIDLIST_ABSOLUTE> pIDL;
if (SUCCEEDED(CComQIPtr<IPersistIDList>(pShellItem)->GetIDList(&pIDL)))
{
UINT cb = ::ILGetSize(pIDL);
BYTE* pb = reinterpret_cast<BYTE*>(static_cast<PIDLIST_ABSOLUTE>(pIDL));
for (UINT i = 0; i < cb; i++)
{
WCHAR hexArray[4];
::StringCchPrintf(hexArray, ARRAYSIZE(hexArray), L" % 02X ", pb[i]);
line += hexArray;
}
line += L"\r\n";
}
}
return line;
}

Signing an appxbundle using CryptUIWizDigitalSign API

I'm facing a rather interesting issue in regards to Authenticode signing an UWP appxbundle file.
Some background:
The client provided us with a SafeNet USB token containing the signing certificate. The private key is not exportable, of course. I want to be able to use this certificate for our automated release builds to sign the package. Unfortunately, the token requires a PIN to be entered once per session, so for example if the build agent reboots, the build will fail. We enabled single login on the token so it's enough to unlock it once a session.
Current state:
We can use signtool on the appxbundle without any problems, given the token has been unlocked. This works well enough but breaks as soon as the machine is rebooted or the workstation is locked.
After some searching I managed to find this piece of code. This takes the signing parameters (including the token PIN) and invokes Windows API to sign the target file. I managed to compile this and it worked flawlessly for signing the installation wrapper (EXE file) - the token did not ask for PIN and was unlocked automatically by the API call.
However, when I invoked the same code on the appxbundle file, the call to CryptUIWizDigitalSign failed with error code 0x80080209 APPX_E_INVALID_SIP_CLIENT_DATA. This is a mystery to me because invoking signtool on the same bundle, with the same parameters/certificate works without problem so the certificate should be fully compatible with the package.
Does anyone have experience with something like this? Is there a way to figure out what is the root cause of the error (what is incompatible between my cert and the bundle)?
EDIT 1
In response to a comment:
The code I'm using to call the APIs (taken directly from the aforementioned SO question)
#include <windows.h>
#include <cryptuiapi.h>
#include <iostream>
#include <string>
#pragma comment (lib, "cryptui.lib")
const std::wstring ETOKEN_BASE_CRYPT_PROV_NAME = L"eToken Base Cryptographic Provider";
std::string utf16_to_utf8(const std::wstring& str)
{
if (str.empty())
{
return "";
}
auto utf8len = ::WideCharToMultiByte(CP_UTF8, 0, str.data(), str.size(), NULL, 0, NULL, NULL);
if (utf8len == 0)
{
return "";
}
std::string utf8Str;
utf8Str.resize(utf8len);
::WideCharToMultiByte(CP_UTF8, 0, str.data(), str.size(), &utf8Str[0], utf8Str.size(), NULL, NULL);
return utf8Str;
}
struct CryptProvHandle
{
HCRYPTPROV Handle = NULL;
CryptProvHandle(HCRYPTPROV handle = NULL) : Handle(handle) {}
~CryptProvHandle() { if (Handle) ::CryptReleaseContext(Handle, 0); }
};
HCRYPTPROV token_logon(const std::wstring& containerName, const std::string& tokenPin)
{
CryptProvHandle cryptProv;
if (!::CryptAcquireContext(&cryptProv.Handle, containerName.c_str(), ETOKEN_BASE_CRYPT_PROV_NAME.c_str(), PROV_RSA_FULL, CRYPT_SILENT))
{
std::wcerr << L"CryptAcquireContext failed, error " << std::hex << std::showbase << ::GetLastError() << L"\n";
return NULL;
}
if (!::CryptSetProvParam(cryptProv.Handle, PP_SIGNATURE_PIN, reinterpret_cast<const BYTE*>(tokenPin.c_str()), 0))
{
std::wcerr << L"CryptSetProvParam failed, error " << std::hex << std::showbase << ::GetLastError() << L"\n";
return NULL;
}
auto result = cryptProv.Handle;
cryptProv.Handle = NULL;
return result;
}
int wmain(int argc, wchar_t** argv)
{
if (argc < 6)
{
std::wcerr << L"usage: etokensign.exe <certificate file path> <private key container name> <token PIN> <timestamp URL> <path to file to sign>\n";
return 1;
}
const std::wstring certFile = argv[1];
const std::wstring containerName = argv[2];
const std::wstring tokenPin = argv[3];
const std::wstring timestampUrl = argv[4];
const std::wstring fileToSign = argv[5];
CryptProvHandle cryptProv = token_logon(containerName, utf16_to_utf8(tokenPin));
if (!cryptProv.Handle)
{
return 1;
}
CRYPTUI_WIZ_DIGITAL_SIGN_EXTENDED_INFO extInfo = {};
extInfo.dwSize = sizeof(extInfo);
extInfo.pszHashAlg = szOID_NIST_sha256; // Use SHA256 instead of default SHA1
CRYPT_KEY_PROV_INFO keyProvInfo = {};
keyProvInfo.pwszContainerName = const_cast<wchar_t*>(containerName.c_str());
keyProvInfo.pwszProvName = const_cast<wchar_t*>(ETOKEN_BASE_CRYPT_PROV_NAME.c_str());
keyProvInfo.dwProvType = PROV_RSA_FULL;
CRYPTUI_WIZ_DIGITAL_SIGN_CERT_PVK_INFO pvkInfo = {};
pvkInfo.dwSize = sizeof(pvkInfo);
pvkInfo.pwszSigningCertFileName = const_cast<wchar_t*>(certFile.c_str());
pvkInfo.dwPvkChoice = CRYPTUI_WIZ_DIGITAL_SIGN_PVK_PROV;
pvkInfo.pPvkProvInfo = &keyProvInfo;
CRYPTUI_WIZ_DIGITAL_SIGN_INFO signInfo = {};
signInfo.dwSize = sizeof(signInfo);
signInfo.dwSubjectChoice = CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE;
signInfo.pwszFileName = fileToSign.c_str();
signInfo.dwSigningCertChoice = CRYPTUI_WIZ_DIGITAL_SIGN_PVK;
signInfo.pSigningCertPvkInfo = &pvkInfo;
signInfo.pwszTimestampURL = timestampUrl.c_str();
signInfo.pSignExtInfo = &extInfo;
if (!::CryptUIWizDigitalSign(CRYPTUI_WIZ_NO_UI, NULL, NULL, &signInfo, NULL))
{
std::wcerr << L"CryptUIWizDigitalSign failed, error " << std::hex << std::showbase << ::GetLastError() << L"\n";
return 1;
}
std::wcout << L"Successfully signed " << fileToSign << L"\n";
return 0;
}
The certificate is a CER file (public portion only) exported from the token and the container name is taken from the token's info. As I mentioned, this works correctly for EXE files.
The signtool command
signtool sign /sha1 "cert thumbprint" /fd SHA256 /n "subject name" /t "http://timestamp.verisign.com/scripts/timestamp.dll" /debug "$path"
This also works, when I call it either manually or from the CI build when the token is unlocked. But the code above fails with the mentioned error.
EDIT 2
Thanks to all of you, I now have a working implementation! I ended up using the SignerSignEx2 API, as suggested by RbMm. This seems to work fine for both appx bundles and PE files (different parameters for each). Verified on Windows 10 with a TFS 2017 build agent - unlocks the token, finds a specified certificate in the cert store, and signs+timestamps the specified file.
I published the result on GitHub, if anyone is interested: https://github.com/mareklinka/SafeNetTokenSigner
first of all i look where CryptUIWizDigitalSign failed:
the CryptUIWizDigitalSign called SignerSignEx function, with pSipData == 0. for sign PE file (exe, dll, sys) - this is ok and will be work. but for appxbundle (zip archive file type) this parameter mandatory and must point to APPX_SIP_CLIENT_DATA: for appxbundle call stack is
CryptUIWizDigitalSign
SignerSignEx
HRESULT Appx::Packaging::AppxSipClientData::Initialize(SIP_SUBJECTINFO* subjectInfo)
at very begin of Appx::Packaging::AppxSipClientData::Initialize we can view next code:
if (!subjectInfo->pClientData) return APPX_E_INVALID_SIP_CLIENT_DATA;
this is exactly where your code fail.
instead of CryptUIWizDigitalSign need direct call SignerSignEx2 and pSipData is mandatory parameter in this case.
in msdn exist full worked example - How to programmatically sign an app package (C++)
the key point here:
APPX_SIP_CLIENT_DATA sipClientData = {};
sipClientData.pSignerParams = &signerParams;
signerParams.pSipData = &sipClientData;
the modern SignTool call SignerSignEx2 direct:
here again clear visible:
if (!subjectInfo->pClientData) return APPX_E_INVALID_SIP_CLIENT_DATA;
after this called
HRESULT Appx::Packaging::Packaging::SignFile(
PCWSTR FileName, APPX_SIP_CLIENT_DATA* sipClientData)
here at begin next code:
if (!sipClientData->pSignerParams) return APPX_E_INVALID_SIP_CLIENT_DATA;
this clear stated in msdn:
You must provide a pointer to an APPX_SIP_CLIENT_DATA structure as
the pSipData parameter when you sign an app package. You must
populate the pSignerParams member of APPX_SIP_CLIENT_DATA with
the same parameters that you use to sign the app package. To do this,
define your desired parameters on the SIGNER_SIGN_EX2_PARAMS
structure, assign the address of this structure to pSignerParams,
and then directly reference the structure's members as well when you
call SignerSignEx2.
question - why need again provide the same parameters, which used in call SignerSignEx2 ? because appxbundle is really archive, which containing multiple files. and every file need be sign. for this Appx::Packaging::Packaging::SignFile recursive call SignerSignEx2 again :
for this recursive calls pSignerParams and used - for call SignerSignEx2 with exactly the same parameters as top call

Busy inodes/dentries after umount in self-written virtual fs

I wrote a simple FS that should only statically contain one file named hello. This file should contain the string Hello, world!. I did this for educational purposes. While the fs is mounted it actually behaves like expected. I can read the file just fine.
However after unmounting I always get
VFS: Busy inodes after unmount of dummyfs. Self-destruct in 5 seconds. Have a nice day...
If I called ls on the rootdir while the fs was mounted I get
BUG: Dentry (ptrval){i=2,n=hello} still in use (-1) [unmount of dummyfs dummyfs]
on top of that.
What does this mean in detail and how can I fix it?
The mount and kill_sb routines call mount_nodev and allocate space for a struct holding the 2 inodes this FS uses.
static struct dentry *dummyfs_mount(struct file_system_type* fs_type,
int flags, const char* dev_name, void* data)
{
struct dentry *ret;
ret = mount_nodev(fs_type, flags, data, dummyfs_fill_super);
if (IS_ERR(ret)) {
printk(KERN_ERR "dummyfs_mount failed");
}
return ret;
}
static void dummyfs_kill_sb(struct super_block *sb) {
kfree(sb->s_fs_info);
kill_litter_super(sb);
}
The fill superblock method creates the 2 inodes and saves them in the struct allocated by mount:
static int dummyfs_fill_super(struct super_block *sb, void *data, int flags)
{
struct dummyfs_info *fsi;
sb->s_magic = DUMMYFS_MAGIC;
sb->s_op = &dummyfs_sops;
fsi = kzalloc(sizeof(struct dummyfs_info), GFP_KERNEL);
sb->s_fs_info = fsi;
fsi->root = new_inode(sb);
fsi->root->i_ino = 1;
fsi->root->i_sb = sb;
fsi->root->i_op = &dummyfs_iops;
fsi->root->i_fop = &dummyfs_dops;
fsi->root->i_atime = fsi->root->i_mtime = fsi->root->i_ctime = current_time(fsi->root);
inode_init_owner(fsi->root, NULL, S_IFDIR);
fsi->file = new_inode(sb);
fsi->file->i_ino = 2;
fsi->file->i_sb = sb;
fsi->file->i_op = &dummyfs_iops;
fsi->file->i_fop = &dummyfs_fops;
fsi->file->i_atime = fsi->file->i_mtime = fsi->file->i_ctime = current_time(fsi->file);
inode_init_owner(fsi->file, fsi->root, S_IFREG);
sb->s_root = d_make_root(fsi->root);
return 0;
}
The lookup method just adds the fsi->file_inode to the dentry if the parent is the root dir:
if (parent_inode->i_ino == fsi->root->i_ino) {
d_add(child_dentry, fsi->file);
}
And the iterate method just emits the dot files and the hello file when called:
if (ctx->pos == 0) {
dir_emit_dots(file, ctx);
ret = 0;
}
if (ctx->pos == 2) {
dir_emit(ctx, "hello", 5, file->f_inode->i_ino, DT_UNKNOWN);
++ctx->pos;
ret = 0;
}
The read method just writes a static string using copy_to_user. The offsets are calculated correctly and on EOF the method just returns 0. However since the problems occur even when the read method was not called I think it is out-of-scope for this already too long question.
For actually running this I use user-mode linux from the git master (4.15+x commit d48fcbd864a008802a90c58a9ceddd9436d11a49). The userland is compiled from scratch and the init process is a derivative of Rich Felker's minimal init to which i added mount calls for /proc, /sys and / (remount).
My command line is ./linux ubda=../uml/image root=/dev/ubda
Any pointers to more thorough documentation are also appreciated.
Using gdb watching the dentry->d_lockref.count I realized that the kill_litter_super call in umount was actually responsible for the dentry issues. Replacing it with kill_anon_super solved that problem.
The busy inode problem vanished too mostly except when i unmounted after immediately after mounting. Allocating the second inode lazily solved that problem too.

std::ostream to file or standard output

I would like to write my output to a file if a file name is avaliable or on the screen (stdout) otherwise. So I've read posts on this forum and found a code, which below I wrapped into a method:
std::shared_ptr<std::ostream> out_stream(const std::string & fname) {
std::streambuf * buf;
std::ofstream of;
if (fname.length() > 0) {
of.open(fname);
buf = of.rdbuf();
} else
buf = std::cout.rdbuf();
std::shared_ptr<std::ostream> p(new std::ostream(buf));
return p;
}
The code works perfectly when used in-place. Unfortunately it behaves oddly when wrapped into a separate method (as given above). Is it because the the objects defined within the method (of, buff) are destroyed once the call is finished?
I am using this part of code in several places and it really should be extracted as a separate non-repeating fragment: a method or a class. How can I achieve this?
You're correct that the problems you're having come from the destruction of of. Wouldn't something like this (untested) work?
std::shared_ptr<std::ostream>
out_stream(const std::string &fname) {
if (fname.length() > 0)
std::shared_ptr<std::ostream> p(new std::ofstream(fname));
else
std::shared_ptr<std::ostream> p(new std::ostream(std::cout.rdbuf()));
}

Drive Letter to Device Instance ID

How do I get from a drive letter to a device instance ID?
My process starts with a device arrival message. I have been successful in getting the drive letter from the arrival message and in opening the dvd tray.
I have searched the various Setup API items; but I haven't found anything that gets me from a drive letter to a device instance ID.
A solution in C# or VB.NET would be ideal, but I'm willing to figure it out from any other language as long as I can see the API calls.
Thanks in advance...
You cannot do it directly.
The link is to use STORAGE_DEVICE_NUMBER. You can use DeviceIoControl with IOCTL_STORAGE_GET_DEVICE_NUMBER on your device name to populate this structure. Put this value to one side.
You then need to get device infomation on your system using SetupDiGetClassDevs setting the GUIDS as approriate, indicicating the drives your are insterested in. Then enumerate through the devices using SetupDiEnumDeviceInfo. Then enumerate the interfaces using SetupDiEnumDeviceInterfaces and finally get the information using SetupDiGetDeviceInterfaceDetail. In this structure returned you can get a DevicePath you can use to get the STORAGE_DEVICE_NUMBER as above. Match this with the STORAGE_DEVICE_NUMBER from your drive letter, and you have now linked a driver letter to your structure. Phew! Inside this structure is a DevInst.
i know it's late for you now but not for everybody ^^
I had the same need and this is main line of how I did it:
-You need a window to receive device arrival and removal (as you said)
-Then you create a DeviceNotificationFilter initiated to dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE
-Then in the message loop of your window you look for VM_DEVICECHANGE
-When u receive it if wParam == DBT_DEVICEARRIVAL, use the lParam to check if it is a DBT_DEVTYPE_VOLUME (i was getting the letter and the type of the drive here) or a DBT_DEVTYPE_DEVICEINTERFACE ( there you can use your wellcasted lParam to get the InstanceId from the input structure).
When you connect a drive your receive DEVINTERFACE first then the other.
I only give the main line beacause i did this long time ago and i don't have the code here, and also I had found a lot of code pieces on the net (long time ago so there should be more now ^^^) maybe msdn give a full code example to do that now.
If you read this and need more informations, i'll reply or make a full documented answer if many need it.
Hope it will help some of you.
I know it's years later but I had to do this and searching brought me here and #DanDan 's answer worked. In order to save future people a lot of work, I thought I'd give back a little and present the technique a bit more explicitly. You'll still have to write a bit of code, but the part I found difficult is below as code:
As DanDan mentioned, the idea is to use CreateFile and DeviceIoControl to get the Windows STORAGE_DEVICE_NUMBER for the disk associated with a file path, and then use the Setup API to enumerate disk devices until we find one whose device instance equals the SDN.
First, here's a summary of how you get the STORAGE_DEVICE_NUMBER from the path (e.g. c:\\users\\bob);
Strip the path to the root (e.g down to C:) and prepend it with \\\\.\\ so you have \\\\.\\C:
Open that path up using CreateFileW with to get metadata
Use DeviceIoControl with IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS to get the extents
Get the DiskNumber member from the first extent returned.
Close the file
Open up \\\\.\\PhysicalDrive<n> where <n> is the that DiskNumber from the first extent
Use DeviceIoControl with code IOCTL_STORAGE_GET_DEVICE_NUMBER to get make it fill out a STORAGE_DEVICE_NUMBER struct as output
Use SetupDiGetClassDevs with arguments &GUID_DEVCLASS_DISKDRIVE and DICGF_PRESENT to get all disks on the system
In a loop, use SetupDiEnumDeviceInfo to get a SP_DEVINFO_DATA repeatedly (on the device list returned by step #8 above) and a call the function below to determine which one, if any, matches the STORAGE_DEVICE_NUMBER for the give path.
(This is edited to remove custom utility classes of mine right on the SO web page so I might have introduced errors/typos)
bool DoesDeviceInstanceEqualStorageDeviceNumber(
const std::string& devInstance,
STORAGE_DEVICE_NUMBER sdn)
{
// Open up this device instance, specifying that we want the *interfaces*.
// The interfaces are key key because examining them will let us get a
// string we can use the Win32 CreateFile function.
const auto hDevInfo = SetupDiGetClassDevsA(
nullptr,
devInstance.c_str(),
nullptr,
DIGCF_DEVICEINTERFACE | DIGCF_ALLCLASSES);
if (hDevInfo == INVALID_HANDLE_VALUE)
throws std::runtime_error("Unable to get disk devices");
DWORD dwSize = 0;
SP_DEVINFO_DATA did;
WCHAR buffer[4096];
did.cbSize = sizeof (did);
bool foundValidMatch = false;
int deviceNumber = 0;
// Iterate through all such devices, looking for one that has a storage device number that matches the given one.
while ( !foundValidMatch && SetupDiEnumDeviceInfo(hDevInfo, deviceNumber, &did))
{
deviceNumber++;
DEVPROPTYPE devPropType;
// We'll only bother comparing this one if it is fixed. Determine that.
const auto getPropResult = SetupDiGetDevicePropertyW (
hDevInfo,
&did,
&DEVPKEY_Device_RemovalPolicy, // Ask for the "removal policy"
&devPropType,
(BYTE*)buffer,
sizeof(buffer),
&dwSize,
0);
if (!getPropResult)
{
std::cerr << "Unable to to get removal policy for disk device: " << ::GetLastError() << std::endl;
continue;
}
/* This bit *would* skip removable disks, you wanted...
else if (buffer[0] != 1)
{
std::cerr << "Skipping removable disk device " << devInstance << std::endl;
continue;
}
*/
// OK this is a fixed disk so it might be the one we'll compare against
// 1. Get the very first disk interface from this particular disk device
// 2. Open a file on it
// 3. Query the resulting file for its device number.
// 4. Compare the device number to the one we determined above
// 5. If it matches ours, then we succeed. If not, continue
SP_DEVICE_INTERFACE_DATA devIntData;
devIntData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
// Get the disk interfaces
const auto result = SetupDiEnumDeviceInterfaces(
hDevInfo,
&did, //&did,
&GUID_DEVINTERFACE_DISK, // Get Disk Device Interface (from winioctl.h)
0, // We only need the very FIRST one. I think...
&devIntData);
if (!result)
continue;
DWORD dwRequiredSize = 0;
// Want to get the detail but don't yet know how much space we'll need
// Do a dummy call to find out
SetupDiGetDeviceInterfaceDetail(
hDevInfo,
&devIntData,
nullptr,
0,
&dwRequiredSize,
nullptr);
if (ERROR_INSUFFICIENT_BUFFER != ::GetLastError())
{
std::cerr << "Unable to get device interface Detail: " << ::GetLastError() << std::endl;;
}
else
{
// Get the detail data so we can get the device path and open a file.
std::vector<TCHAR> buf(dwRequiredSize);
auto pDidd = reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(buf.data());
// WARNING: HARD CODED HACK
// ------------------------
// https://stackoverflow.com/questions/10405193/vb-net-hid-setupdigetdeviceinterfacedetail-getlasterror-shows-1784-error-inv
//
// Don't ask. Just do what they tell you.
// -----------------------------------------------------------------
#ifdef BUILD_64
pDidd->cbSize = 8;
#else
pDidd->cbSize = 6;
#endif
// -----------------------------------------------------------------
if (!SetupDiGetDeviceInterfaceDetail(
hDevInfo,
&devIntData,
pDidd,
dwRequiredSize,
&dwRequiredSize,
nullptr))
{
std::cerr << "Cannot get interface detail: " << ::GetLastError());
}
else
{
// FINALLY: We now have a DevicePath that we can use to open up
// in a Win32 CreateFile() call. That will let us get the
// STORAGE_DEVICE_NUMBER and compare it to the one we were given.
const auto hFile = ::CreateFileW(pDidd->DevicePath, 0, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, NULL);
if (INVALID_HANDLE_VALUE != hFile)
{
std::cerr << "Unable to open logical volume: " + devicePath << std::endl;
continue;
}
STORAGE_DEVICE_NUMBER sdnTest;
ZeroMemory(&sdnTest, sizeof(STORAGE_DEVICE_NUMBER));
if (0 == DeviceIoControl(
hDevInfo
IOCTL_STORAGE_GET_DEVICE_NUMBER,
nullptr, // output only so not needed
0, // output only so not needed
&sdnTest,
sizeof(STORAGE_DEVICE_NUMBER),
nullptr,
nullptr))
{
std::cerr << "Unable to determine storage device number: " << ::GetLastError() << std::endl;);
}
else
{
// All this for a one-line test...
foundValidMatch = sdnTest.DeviceNumber == sdn.DeviceNumber;
}
}
}
}
SetupDiDestroyDeviceInfoList(hDevInfo);
return foundValidMatch;
}
I hope this saves someone a headache

Resources