How to get the Drive Letter for the DevicePath - c++

I am using Win32 API.
Really i do not understand how to get the drive letter for DevicePath of a USB stick .
can you pls explain it to me
( what i have is SP_DEVICE_INTERFACE_DETAIL_DATA DevicePath
using this Device path i get VID AND PID of the usb device
my device path looks like below
"\?\usb#vid_1a8d&pid_1000#358094020874450#{a5dcbf10-6530-11d2-901f-00c04fb951ed}"
Is there any way to to map DRIVE LETTER to my DEVICE PATH
so please help me to map drive letter to DevicePath )
Thanks for any help.

The link I provided in your other question gives you all the information you need to do this. In semi-pseudocode:
DiskDevice = CreateFile(DiskDevicePath);
DiskDeviceNumber = DeviceIoControl(DiskDevice, IOCTL_STORAGE_GET_DEVICE_NUMBER);
for each VolumeDevicePath in GetLogicalDriveStrings
VolumeDevice = CreateFile(VolumeDevicePath);
VolumeDeviceNumber = DeviceIoControl(VolumeDevice, IOCTL_STORAGE_GET_DEVICE_NUMBER);
if(VolumeDeviceNumber == DiskDeviceNumber)
// volume (i.e. "G:") corresponding to VolumeDevicePath resides on disk (i.e. "XYZ USB Storage Device") corresponding to DiskDevicePath
I'm not 100% sure (it's been a while), but I think that the Disk device (GUID_DEVINTERFACE_DISK) is a child of the USB device (GUID_DEVINTERFACE_USB_DEVICE). In any event, I think DiskDevicePath needs to be the path of the Disk device (not the USB device).

Take a look at this, maybe it'll help (I don't think there's an easy way to do it ...)
http://msdn.microsoft.com/en-us/library/cc542456(VS.85).aspx

Related

Finding Bluetooth RSSI Value In Visual C++ Win 10

I have done a lot of researching about this stuff but could not get clear answer. I found the bluetooth device's name and NAP and SAP addresses with WSALookupServiceNext(...) function but can't access the RSSI value. Is there specific function or something else that i can use the find the RSSI value?
PS: BthReadRSSI function requires extra library that could not found on internet.
https://learn.microsoft.com/en-us/previous-versions/windows/embedded/ee495452(v=winembedded.80)#requirements
while (WSALookupServiceNext(hLookup, LUP_RETURN_NAME | LUP_RETURN_ADDR, &dwSize, pwsaResults) == 0)
{
btAddr = ((SOCKADDR_BTH *)pwsaResults->lpcsaBuffer->RemoteAddr.lpSockaddr)->btAddr;
// Verify the returned name
bHaveName = (pwsaResults->lpszServiceInstanceName) && *(pwsaResults->lpszServiceInstanceName);
}
TO get RSSI value on Win 10 you must use WinRT API. Also your device must be connected somehow.
The native C++ code that uses WinRT to red RSSI is too long to publish it here.
The easy way is to use thrid party library. For example Bluetooth Framework. Or if you would like contact me by Skype (mike.petrichenko). I can not promise that I give you ready solution but I can guid you into right direction so you can get your own code.

GetLogicalDrives plus additional information C++

I need help with one of my programs that pulls out available drives on a system and prints various information about the drives. I am using VC++ and am fairly new to C++ and need some high level inputs or example code from experienced programmers.
Here is my current source code:
#include "stdafx.h"
#include Windows.h
#include stdio.h
#include iostream
using namespace std;
int main()
{
// Initial Dummy drive
WCHAR myDrives[] = L" A";
// Get the logical drive bitmask (1st drive at bit position 0, 2nd drive at bit position 1... so on)
DWORD myDrivesBitMask = GetLogicalDrives();
// Verifying the returned drive mask
if(myDrivesBitMask == 0)
wprintf(L"GetLogicalDrives() failed with error code: %d\n", GetLastError());
else {
wprintf(L"This machine has the following logical drives:\n");
while(myDrivesBitMask) {
// Use the bitwise AND with 1 to identify
// whether there is a drive present or not.
if(myDrivesBitMask & 1) {
// Printing out the available drives
wprintf(L"drive %s\n", myDrives);
}
// increment counter for the next available drive.
myDrives[1]++;
// shift the bitmask binary right
myDrivesBitMask >>= 1;
}
wprintf(L"\n");
}
system("pause");
}
`
-Here is the output-
This machine has the following logical drives:
drive C
drive D
drive E
drive F
drive G
drive H
drive I
I need to output additional information about each drive (perhaps an example will tell the story in a shorter amount of time):
Drive – C:\
Drive Type: Fixed
Drive Ready Status: True
Volume Label: Boot Drive
File System Type : NTFS
Free Space: 30021926912
Total Drive Size: 240055742464
Drive – D:\
Drive Type: Fixed
Drive Ready Status: True
Volume Label: Application Data
File System Type : NTFS
Free Space: 42462507008
Total Drive Size: 240054693888
Which methods, libs api, etc. can I use to pull out drive type, drive status, volume label, file system type, free space, and total drive size?
*Side note, I noticed a defect with my pre-processor directives, specifically within the standard I/O header files. I know that is not the recommended way using printf and cout is type safe and the proper route to go but I couldn't figure out how to format output in cout as you would do in wprintf(L"drive %s\n", myDrives);.... so how would you do this with cout??
Thanks in advance.
You want to look at functions such as GetVolumeInformation to retrieve file system information such as free space and volume name.
GetDriveType will give you some basic information about the drive type, but USB thumb sticks and flash readers can give surprising results.
I'm not sure what you mean by "ready status". If you mean whether there is a valid volume in the drive, then you can try CreateFile with a path of "\\.\C:" to try and open the volume. If it fails then there is no volume (disk) present. This will be of use for SD card readers. To do this without an error dialog appearing you will need to call SetErrorMode(SEM_NOOPENFILEERRORBOX) first.
To check whether a drive is ready you may also use GetDiskFreeSpaceEx. If this fails, the drive is not ready/usable.
Here is some example code: http://pinvoke.net/default.aspx/coredll/GetDiskFreeSpaceEx.html

How to reliably and quickly get the MAC address of a network card given its device instance ID

Given a device instance ID for a network card, I would like to know its MAC address. Example device instance ID on my system for integrated Intel Gigabit card:
PCI\VEN_8086&DEV_10CC&SUBSYS_00008086&REV_00\3&33FD14CA&0&C8
So far, the algorithm I have used works as follows:
Call SetupDiGetClassDevs with DIGCF_DEVICEINTERFACE.
Call SetupDiEnumDeviceInfo to get the returned device in a SP_DEVINFO_DATA.
Call SetupDiEnumDeviceInterfaces with GUID_NDIS_LAN_CLASS to get a device interface.
Call SetupDiGetDeviceInterfaceDetail for this returned device interface. This gets us the device path as a string: \\?\pci#ven_8086&dev_10cc&subsys_00008086&rev_00#3&33fd14ca&0&c8#{ad498944-762f-11d0-8dcb-00c04fc3358c}\{28fd5409-15bd-4c06-b62f-004d3a06f852}
At this point we have an address to the network card driver's interface. Open it with CreateFile using the result from #4.
Call DeviceIoControl with IOCTL_NDIS_QUERY_GLOBAL_STATS and OID of OID_802_3_PERMANENT_ADDRESS to get the MAC address.
This usually works, and has been used successfully on quite a large number of machines. However, it appears that a very select few machines have network drivers that aren't responding properly to the DeviceIoControl request in step #6; the problem persists even after updating network card drivers to the latest. These are newer, Windows 7-based computers. Specifically, DeviceIoControl completes successfully, but returns zero bytes instead of the expected six bytes containing the MAC address.
A clue seems to be on the MSDN page for IOCTL_NDIS_QUERY_GLOBAL_STATS:
This IOCTL will be deprecated in later operating system releases. You
should use WMI interfaces to query miniport driver information. For
more information see, NDIS Support for WMI.
-- perhaps newer network card drivers are no longer implementing this IOCTL?
So, how should I get this working? Is it possible there's an oversight in my approach and I'm doing something slightly wrong? Or do I need to take a much more different approach? Some alternate approaches seem to include:
Query Win32_NetworkAdapter WMI class: provides needed information but rejected due to horrible performance. See Fast replacement for Win32_NetworkAdapter WMI class for getting MAC address of local computer
Query MSNdis_EthernetPermanentAddress WMI class: appears to be the WMI replacement for IOCTL_NDIS_QUERY_GLOBAL_STATS and queries the OID directly from the driver - and this one works on the troublesome network driver. Unfortunately, the returned class instances only provide the MAC address and the InstanceName, which is a localized string like Intel(R) 82567LM-2 Gigabit Network Connection. Querying MSNdis_EnumerateAdapter yields a list which relates the InstanceName to a DeviceName, like \DEVICE\{28FD5409-15BD-4C06-B62F-004D3A06F852}. I'm not sure how to go from the DeviceName to the plug-and-play device instance ID (PCI\VEN_8086......).
Call GetAdaptersAddresses or GetAdaptersInfo (deprecated). The only non-localized identifier I can find in the return value is the adapter name, which is a string like {28FD5409-15BD-4C06-B62F-004D3A06F852} - same as the DeviceName returned by the WMI NDIS classes. So again, I can't figure out how to relate it to the device instance ID. I'm not sure if it would work 100% of the time either - e.g. for adapters without TCP/IP protocol configured.
NetBIOS method: requires specific protocols to be set up on the card so won't work 100% of time. Generally seems hack-ish, and not a way to relate to device instance ID anyway that I know of. I'd reject this approach.
UUID generation method: rejected for reasons I won't elaborate on here.
It seems like if I could find a way to get the "GUID" for the card from the device instance ID, I'd be well on my way with one of the remaining two ways of doing things. But I haven't figured out how yet. Otherwise, the WMI NDIS approach would seem most promising.
Getting a list of network cards and MAC addresses is easy, and there are several ways of doing it. Doing it in a fast way that lets me relate it to the device instance ID is apparently hard...
EDIT: Sample code of the IOCTL call if it helps anyone (ignore the leaked hFile handle):
HANDLE hFile = CreateFile(dosDevice.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
wcout << "GetMACAddress: CreateFile on " << dosDevice << " failed." << endl;
return MACAddress();
}
BYTE address[6];
DWORD oid = OID_802_3_PERMANENT_ADDRESS, returned = 0;
//this fails too: DWORD oid = OID_802_3_CURRENT_ADDRESS, returned = 0;
if (!DeviceIoControl(hFile, IOCTL_NDIS_QUERY_GLOBAL_STATS, &oid, sizeof(oid), address, 6, &returned, NULL)) {
DWORD err = GetLastError();
wcout << "GetMACAddress: DeviceIoControl on " << dosDevice << " failed." << endl;
return MACAddress();
}
if (returned != 6) {
wcout << "GetMACAddress: invalid address length of " << returned << "." << endl;
return MACAddress();
}
The code fails, printing:
GetMACAddress: invalid address length of 0.
So the DeviceIoControl returns non-zero indicating success, but then returns zero bytes.
Here's one way to do it:
Call GetAdaptersAddresses to get a list of IP_ADAPTER_ADDRESSES structs
Iterate over each adapter and get its GUID from the AdapterName field (I'm not sure if this behaviour is guaranteed, but all the adapters in my system have a GUID here, and the documentation says the AdapterName is permanent)
For each adapter read the registry key from HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\<the adapter GUID>\Connection\PnPInstanceID (if it exists) (got this idea from here; searching on Google that key seems to be well documented, so it's not likely to change)
From this key you get the device ID for the adapter (something like: PCI\VEN_14E4&DEV_16B1&SUBSYS_96B11849&REV_10\4&2B8260C3&0&00E4)
Do this for each adapter until you find a match. When you get your match just go back to the IP_ADAPTER_ADDRESSES and look at the PhysicalAddress field
Get a beer (optional)
It wouldn't be Windows if there weren't a million ways to do something!
I wound up using SetupDiGetDeviceRegistryProperty to read SPDRP_FRIENDLYNAME. If that's not found, then I read SPDRP_DEVICEDESC instead. Ultimately, this gets me a string like "VirtualBox Host-Only Ethernet Adapter #2". I then match this against the InstanceName property in the WMI NDIS classes (MSNdis_EthernetPermanentAddress WMI class). Both properties must be read in case there are multiple adapters sharing the same driver (i.e. "#2", "#3", etc.) - if there's only one adapter then SPDRP_FRIENDLYNAME isn't available, but if there is more than one then SPDRP_FRIENDLYNAME is required to differentiate them.
The method makes me a little nervous because I'm comparing what seems like a localized string, and there's no documentation that I've found that guarantees what I'm doing will always work. Unfortunately, I haven't found any better ways that are documented to work, either.
A couple other alternate methods involve groveling in undocumented registry locations. One method is spencercw's method, and the other would be to read SPDRP_DRIVER, which is the name of a subkey under HKLM\SYSTEM\CurrentControlSet\Control\Class. Underneath the driver key, look for the Linkage\Export value which then seems like it could be matched to the DeviceName property of the MSNdis_EnumerateAdapter class. But there's no documentation I could find that says these values can be legally matched. Furthermore, the only documentation I found about Linkage\Export was from the Win2000 registry reference and explicitly said that applications shouldn't rely on it.
Another method would be to look at my original question, step 4: "SetupDiGetDeviceInterfaceDetail for this returned device interface". The device interface path actually can be used to reconstruct the device path. Start with device interface path: \\?\pci#ven_8086&dev_10cc&subsys_00008086&rev_00#3&33fd14ca&0&c8#{ad498944-762f-11d0-8dcb-00c04fc3358c}\{28fd5409-15bd-4c06-b62f-004d3a06f852}. Then, remove everything before the final slash, leaving you with: {28fd5409-15bd-4c06-b62f-004d3a06f852}. Finally, prepend \Device\ to this string and match it against the WMI NDIS classes. Again, however, this seems to be undocumented and relying on an implementation detail of a device interface path.
In the end, the other methods I investigated had their own undocumented complications that sounded at least as serious as matching the SPDRP_FRIENDLYNAME / SPDRP_DEVICEDESC strings. So I opted for the simpler approach, which was to just match those strings against the WMI NDIS classes.
I guess you want to get the MAC address in order to implement some sort of DRM, inventory, or classification system, since you tried to get the permanent MAC address instead of the current one.
You seem to forget that there's even an administratively super-imposed MAC address (in other words: a "forced" MAC address).
Some drivers let you do this from the Device Property page, under the Advanced tab (for example: my Marvell network adapter let me do this), while some others don't let you do that (read: they don't support that property).
However, it all ends in a Registry value: HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\xxxx\NetworkAddress, with a REG_SZ type.
Here you can set a different MAC address than the original one, in the form "01020304abcd" (6 bytes, plain hexadecimal, without : separators or 0x prefix).
After you set it, reboot the machine, and on power-up the new MAC address will have effect.
I happen to have a motherboard with two Marvell integrated NICs, and a NETGEAR USB WiFi NIC. The Marvell one supports changing the MAC address: if you set the NetworkAddress value in the Registry, you see the new value in the driver properties page, too, and it has effect immediately, without the need to restart (if you change it from device Property Page).
Here follows the results of reading the MAC address with different methods:
GetAdaptersInfo: new MAC address
IOCTL_NDIS_QUERY_GLOBAL_STATS: original MAC address
MSNdis_EthernetPermanentAddress: original MAC address
I tried adding the NetworkAddress value in the Registry for the NETGEAR USB WiFi NIC, and the results are:
GetAdaptersInfo: new MAC address
IOCTL_NDIS_QUERY_GLOBAL_STATS: new MAC address
MSNdis_EthernetPermanentAddress: new MAC address
The original MAC addres is gone.
So, in order to not be fooled by a "malicious" user, you always need to check the HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\xxxx\NetworkAddress Registry value. If that is set, i guess it's better to not trust that Network Adapter at all, since it is up to the driver implementation to decide what will be presented to you using the different methods.
Some background for getting to that Registry key:
Microsoft documentation about the HKLM\SYSTEM\CurrentControlSet\Class key
According to the Microsoft documentation on that page,
There is a subkey for each class that is named using the GUID of the
setup class
So we choose the {4D36E972-E325-11CE-BFC1-08002BE10318} subkey (aka GUID_DEVCLASS_NET, defined in <devguid.h>, and further documented here)
Again, according to Microsoft documentation,
Each class subkey contains other subkeys known as software keys (or, driver keys) for each device instance of that class installed in the system. Each of these software keys is named by using a device instance ID, which is a base-10, four-digit ordinal value
The xxxx part is a 4-character textual representation of a positive integer, starting from 0
So, you can traverse the subkeys up from 0000, 0001, 0002, up to the number of network adapters in your system.
The documentation stops here: I didn't find any other documentation about the different registry values, or such.
However, in each of these subkeys, you can find REG_SZ values that can help you link the GetAdaptersInfo(), MSNdis_EthernetPermanentAddress, Win32_NetworkAdapter, and Device Instance ID worlds (and this answers your question).
The Registry values are:
DeviceInstanceID: its value is, no surprise, the Device Instance ID
NetCfgInstanceId: its value is the AdapterName member of the IP_ADAPTER_INFO struct, returned by GetAdaptersInfo(). It is also the GUID member of the Win32_NetworkAdapter WMI class.
Don't forget the NetworkAddress one: should a valid MAC address exist here, a driver may report it as the MAC address in use by GetAdaptersInfo(), MSNdis_EthernetPermanentAddress, and IOCTL_NDIS_QUERY_GLOBAL_STATS!
Then, as you already said, the only connection between the MSNdis_EthernetPermanentAddress WMI Class and the rest of the "world" is by its InstanceName member. You can relate it to the Description member of the IP_ADAPTER_INFO struct, returned by GetAdaptersInfo(). Although it may be a localized name, it seems to be unique for the system (For my two integrated Marvell NICs, the second one has a " #2" appended to its name).
Final note:
Said all the above, the user could choose to disable WMI...

Is it possible to detect the presence of an USB expansion card in Linux?

I would like to write some C++ program that can detect the presence of an USB expansion card, or an SD card reader without it necessarily having anything plugged in to it. Is this possible? In Linux?
if you know the exact vendor id and/or product id you could search for it, like this:
for (bus = busses; bus; bus = bus->next)
for (dev = bus->devices; dev; dev = dev->next)
if ((dev->descriptor.idVendor == vendor) && (dev->descriptor.idProduct == product))
return dev;
libusb tutorial
Yes, You can get the idVendor and idProduct by a simple dmesg. And then, put a search condition for it just shown above by a knowledgeble man. If you want to dig deeper, and if you have a linux, then you can explore usb.h present in your <kernel_source>/drivers/usb/core.
There is a structure : struct usb_device{}.
If you yet more keen to explore then, you should check out driver.c and hub.c for methods like announce_device() which prints the USB device details after a dmesg. Explore it for good! :)

How to determine if a CD is in CD-ROM drive

I was wondering if there is a Windows API that can tell me if there is a empty CD is inside a CD-ROM/DVD-Rom drive.
I've already looked at GetVolumeInformation(), but that only brings back information on the actual CD-ROM drive, not the media that is inside it, if there's even a CD in it or not.
All I'm trying to do is see if there's a cd in the drive or not and return a boolean value.
Thanks for any help
Call GetFileAttributes() on the root directory. GetFileAttributes() is quite optimized as it's commonly used as a check whether a file or directory exists.
Another method is to call GetDiskFreeSpace, which (despite its name) also returns the total disk size.
The Shell also has some useful functions. SHGetDriveMedia will tell you what's loaded, but you should first call GetDriveType().
The API you're looking for is most probably IMAPI.
I think this article here could be of help for you http://msdn.microsoft.com/en-us/magazine/cc163992.aspx
You can use DeviceIoControl API passing IOCTL_STORAGE_CHECK_VERIFY as the dwIoControlCode. This will check if a particular disc drive is ready i. e. has a disc in it and, of course, the tray is closed. Check the DeviceIoControl MSDN documentation for more info on this function.
// FROM VISTA to ....
BOOL CDYesNo(WCHAR letter) {// like F
DWORD pdwMediaContent=0;
BOOL ret =1;
WCHAR catw[3]; // create wchar string
catw[0]=letter;
catw[1]=L':';
catw[2]=NULL; `enter code here`
HRESULT rcd=SHGetDriveMedia( catw, &pdwMediaContent);
//rcd normally is S_OK, maybe tested.
if(pdwMediaContent ==0) ret=0;
return ret; // 0=empty
}