Получение расширенной информации о подключенном флэш-накопителе
Требуется получить максимум информации о подключенном устройстве. Мои познания ограничены только GetVolumeInformation(), но я знаю, что через SetupDi…/DeviceIoControl() тоже что-то можно получить. Однако, экспресс-изучение этих функций конкретных результатов не дало. Например, интересует, как получить имя устройства, которое пишется в диспетчере устройств (обычно производитель).
Т.к. на приличных форумах у приличных людей заведено делиться найденными решениями, привожу результаты своих стараний, многочасовых поисков и несколько менее многочасовых проб и ошибок.
Код:
STORAGE_DEVICE_NUMBER sdnSrc;
char szDevName[8]="\\\\.\\";DWORD dwBytes;
lstrcat(szDevName,szDrive);
szDevName[6]=0;
HANDLE hDev=CreateFile(szDevName,0,FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,0,OPEN_EXISTING,0,0);
if(hDev!=(HANDLE)-1){
if(!DeviceIoControl(hDev,IOCTL_STORAGE_GET_DEVICE_NUMBER,0,0,&sdnSrc,sizeof sdnSrc,&dwBytes,0))
MessageBox(hDlg,"DeviceIoControl(Src)",0,MB_ICONERROR);
CloseHandle(hDev);
}else
MessageBox(hDlg,"CreateFile(Src)",0,MB_ICONERROR);
GUID guid=GUID_DEVINTERFACE_DISK;
HDEVINFO hDevs=SetupDiGetClassDevs(&guid,0,0,DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
if(hDevs!=(HANDLE)-1){
bool bSearch=true;DWORD i=0;SP_DEVINFO_DATA DevData={sizeof SP_DEVINFO_DATA};
while(bSearch){
if(SetupDiEnumDeviceInfo(hDevs,i,&DevData)){
DWORD j=0;SP_DEVICE_INTERFACE_DATA DevIface={sizeof SP_DEVICE_INTERFACE_DATA};
while(bSearch){
if(SetupDiEnumDeviceInterfaces(hDevs,&DevData,&guid,j,&DevIface)){
SetupDiGetDeviceInterfaceDetail(hDevs,&DevIface,0,0,&dwBytes,0);
SP_DEVICE_INTERFACE_DETAIL_DATA *pDevIfDetail=(SP_DEVICE_INTERFACE_DETAIL_DATA*)HeapAlloc(hHeap,0,dwBytes);
pDevIfDetail->cbSize=sizeof SP_DEVICE_INTERFACE_DETAIL_DATA;
SetupDiGetDeviceInterfaceDetail(hDevs,&DevIface,pDevIfDetail,dwBytes,0,0);
HANDLE hTest=CreateFile(pDevIfDetail->DevicePath,0,FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,0,OPEN_EXISTING,0,0);
HeapFree(hHeap,0,pDevIfDetail);
if(hTest!=(HANDLE)-1){
STORAGE_DEVICE_NUMBER sdn;
if(DeviceIoControl(hTest,IOCTL_STORAGE_GET_DEVICE_NUMBER,0,0,&sdn,sizeof sdn,&dwBytes,0)){
if(sdn.DeviceNumber==sdnSrc.DeviceNumber){
SetupDiGetDeviceRegistryProperty(hDevs,&DevData,SPDRP_FRIENDLYNAME,0,0,0,&dwBytes);
char *szName=(char*)HeapAlloc(hHeap,0,dwBytes);
SetupDiGetDeviceRegistryProperty(hDevs,&DevData,SPDRP_FRIENDLYNAME,0,(PBYTE)szName,dwBytes,0);
char *szBuf=(char*)HeapAlloc(hHeap,0,lstrlen(szFormat)+lstrlen(szVolName)+lstrlen(szVolFS)+dwBytes+15);
wsprintf(szBuf,szFormat,szVolName,dwVolSN,szVolFS,(DWORD)dwTNF.QuadPart/1048576,(DWORD)dwTNB.QuadPart/1048576,szName);
HeapFree(hHeap,0,szName);
SetDlgItemText(hDlg,LblDevInfo,szBuf);
HeapFree(hHeap,0,szBuf);
bSearch=false;
}
}else
MessageBox(hDlg,"DeviceIoControl(Test)",0,MB_ICONERROR);
CloseHandle(hTest);
}else
MessageBox(hDlg,"CreateFile(Test)",0,MB_ICONERROR);
}else if(GetLastError()==ERROR_NO_MORE_ITEMS)
break;
j++;
}
}else if(GetLastError()==ERROR_NO_MORE_ITEMS)
break;
i++;
}
SetupDiDestroyDeviceInfoList(hDevs);
}else
MessageBox(hDlg,"Невозможно получить список устройств",0,MB_ICONERROR);
char szDevName[8]="\\\\.\\";DWORD dwBytes;
lstrcat(szDevName,szDrive);
szDevName[6]=0;
HANDLE hDev=CreateFile(szDevName,0,FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,0,OPEN_EXISTING,0,0);
if(hDev!=(HANDLE)-1){
if(!DeviceIoControl(hDev,IOCTL_STORAGE_GET_DEVICE_NUMBER,0,0,&sdnSrc,sizeof sdnSrc,&dwBytes,0))
MessageBox(hDlg,"DeviceIoControl(Src)",0,MB_ICONERROR);
CloseHandle(hDev);
}else
MessageBox(hDlg,"CreateFile(Src)",0,MB_ICONERROR);
GUID guid=GUID_DEVINTERFACE_DISK;
HDEVINFO hDevs=SetupDiGetClassDevs(&guid,0,0,DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
if(hDevs!=(HANDLE)-1){
bool bSearch=true;DWORD i=0;SP_DEVINFO_DATA DevData={sizeof SP_DEVINFO_DATA};
while(bSearch){
if(SetupDiEnumDeviceInfo(hDevs,i,&DevData)){
DWORD j=0;SP_DEVICE_INTERFACE_DATA DevIface={sizeof SP_DEVICE_INTERFACE_DATA};
while(bSearch){
if(SetupDiEnumDeviceInterfaces(hDevs,&DevData,&guid,j,&DevIface)){
SetupDiGetDeviceInterfaceDetail(hDevs,&DevIface,0,0,&dwBytes,0);
SP_DEVICE_INTERFACE_DETAIL_DATA *pDevIfDetail=(SP_DEVICE_INTERFACE_DETAIL_DATA*)HeapAlloc(hHeap,0,dwBytes);
pDevIfDetail->cbSize=sizeof SP_DEVICE_INTERFACE_DETAIL_DATA;
SetupDiGetDeviceInterfaceDetail(hDevs,&DevIface,pDevIfDetail,dwBytes,0,0);
HANDLE hTest=CreateFile(pDevIfDetail->DevicePath,0,FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,0,OPEN_EXISTING,0,0);
HeapFree(hHeap,0,pDevIfDetail);
if(hTest!=(HANDLE)-1){
STORAGE_DEVICE_NUMBER sdn;
if(DeviceIoControl(hTest,IOCTL_STORAGE_GET_DEVICE_NUMBER,0,0,&sdn,sizeof sdn,&dwBytes,0)){
if(sdn.DeviceNumber==sdnSrc.DeviceNumber){
SetupDiGetDeviceRegistryProperty(hDevs,&DevData,SPDRP_FRIENDLYNAME,0,0,0,&dwBytes);
char *szName=(char*)HeapAlloc(hHeap,0,dwBytes);
SetupDiGetDeviceRegistryProperty(hDevs,&DevData,SPDRP_FRIENDLYNAME,0,(PBYTE)szName,dwBytes,0);
char *szBuf=(char*)HeapAlloc(hHeap,0,lstrlen(szFormat)+lstrlen(szVolName)+lstrlen(szVolFS)+dwBytes+15);
wsprintf(szBuf,szFormat,szVolName,dwVolSN,szVolFS,(DWORD)dwTNF.QuadPart/1048576,(DWORD)dwTNB.QuadPart/1048576,szName);
HeapFree(hHeap,0,szName);
SetDlgItemText(hDlg,LblDevInfo,szBuf);
HeapFree(hHeap,0,szBuf);
bSearch=false;
}
}else
MessageBox(hDlg,"DeviceIoControl(Test)",0,MB_ICONERROR);
CloseHandle(hTest);
}else
MessageBox(hDlg,"CreateFile(Test)",0,MB_ICONERROR);
}else if(GetLastError()==ERROR_NO_MORE_ITEMS)
break;
j++;
}
}else if(GetLastError()==ERROR_NO_MORE_ITEMS)
break;
i++;
}
SetupDiDestroyDeviceInfoList(hDevs);
}else
MessageBox(hDlg,"Невозможно получить список устройств",0,MB_ICONERROR);
Если кто-то не понял, или ещё какие-то вопросы будут — обращайтесь ☺
Функция SPDRP_MFG
строка REG_SZ - производитель устройства.
0. Где взять DeviceInfoSet?
1. Где взять GUID?
Я просто ни разу вообще не работал с SetupAPI :D Поэтому для меня это в новинку, а в MSDN всё как-то запутано.
Код:
HDEVINFO hDevs=SetupDiGetClassDevs(&GUID_DEVINTERFACE_DISK,0,0,DIGCF_PRESENT);
if(hDevs!=(HANDLE)-1){
DWORD i=0;SP_DEVINFO_DATA DevData={sizeof SP_DEVINFO_DATA};
while(true){
if(SetupDiEnumDeviceInfo(hDevs,i,&DevData)){
…
}else if(GetLastError()==ERROR_NO_MORE_ITEMS)
break;
i++;
}
SetupDiDestroyDeviceInfoList(hDevs);
}
if(hDevs!=(HANDLE)-1){
DWORD i=0;SP_DEVINFO_DATA DevData={sizeof SP_DEVINFO_DATA};
while(true){
if(SetupDiEnumDeviceInfo(hDevs,i,&DevData)){
…
}else if(GetLastError()==ERROR_NO_MORE_ITEMS)
break;
i++;
}
SetupDiDestroyDeviceInfoList(hDevs);
}
Код:
//Codec Windows-1251
//Список
#include <cstdio>
#include <clocale>
#include <windows.h>
#include <setupapi.h> //прилинковать стат. библиотеку libsetupapi.a (для MinGW)
int main() {
setlocale(0, "");
//если нет заголовочника, то можно вручную так:
CONST GUID GUID_DEVINTERFACE_DISK = {0x53f56307L, 0xb6bf, 0x11d0,
0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b
};
HDEVINFO lst = SetupDiGetClassDevsA(&GUID_DEVINTERFACE_DISK, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
if (INVALID_HANDLE_VALUE == lst)
return -3000;
DWORD k = 0;
SP_DEVINFO_DATA DeviceInfoData;
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);//нужно
const DWORD BufferSize = 1024; //должно хватить
char Buffer[BufferSize];
while (SetupDiEnumDeviceInfo(lst, k, &DeviceInfoData)) {
if (SetupDiGetDeviceRegistryPropertyA(lst, &DeviceInfoData,
SPDRP_FRIENDLYNAME, NULL, (BYTE *)Buffer, BufferSize, NULL)
)
printf("%s\n", Buffer);
else if (
SetupDiGetDeviceRegistryPropertyA(lst, &DeviceInfoData,
SPDRP_DEVICEDESC, NULL, (BYTE *)Buffer, BufferSize, NULL)
)
printf("%s\n", Buffer);
k++;
}
SetupDiDestroyDeviceInfoList(lst);//не забывать закрыть
printf("\n\nНажмите клавишу...\n");
getchar();
return 0;
}
//Список
#include <cstdio>
#include <clocale>
#include <windows.h>
#include <setupapi.h> //прилинковать стат. библиотеку libsetupapi.a (для MinGW)
int main() {
setlocale(0, "");
//если нет заголовочника, то можно вручную так:
CONST GUID GUID_DEVINTERFACE_DISK = {0x53f56307L, 0xb6bf, 0x11d0,
0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b
};
HDEVINFO lst = SetupDiGetClassDevsA(&GUID_DEVINTERFACE_DISK, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
if (INVALID_HANDLE_VALUE == lst)
return -3000;
DWORD k = 0;
SP_DEVINFO_DATA DeviceInfoData;
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);//нужно
const DWORD BufferSize = 1024; //должно хватить
char Buffer[BufferSize];
while (SetupDiEnumDeviceInfo(lst, k, &DeviceInfoData)) {
if (SetupDiGetDeviceRegistryPropertyA(lst, &DeviceInfoData,
SPDRP_FRIENDLYNAME, NULL, (BYTE *)Buffer, BufferSize, NULL)
)
printf("%s\n", Buffer);
else if (
SetupDiGetDeviceRegistryPropertyA(lst, &DeviceInfoData,
SPDRP_DEVICEDESC, NULL, (BYTE *)Buffer, BufferSize, NULL)
)
printf("%s\n", Buffer);
k++;
}
SetupDiDestroyDeviceInfoList(lst);//не забывать закрыть
printf("\n\nНажмите клавишу...\n");
getchar();
return 0;
}
Проблема в том, что у меня код как раз такой же в целом, но первая попытка Enum'а возвращает мне нифига. И вот тут я просто офигеваю!
Флаг DIGCF_DEVICEINTERFACE используете?
(MSDN я, конечно, читал, но в силу того, что эта тема для меня нова, тамошние слова для меня малопонятны пока что :))
Я этот код сделал из своего трехгодичной давности (для которого наверняка заготовку в инете нашел). Он у меня перечислял все устройства в системе в виде а-ля диспетчер устройств. Просто вставил в него гуид из вашего кода. Такой вот гибрид. У меня он хард и флеху определил. Флаг важен, но уже не помню, почему. Кажется в винде две категории гуидов, для одной из них этот флаг. Но, боюсь наврать, уже все забыть успел :)
Чюдиса! Друг спас друга!
Видимо, такую комбинацию я не использовал, а зря =) По крайней мере, дома этот код заработал. Что ж, не поблагодарить за это было бы весьма неприлично, поэтому спасибо за хоть и маленький, но очень значимый вклад ☺
И тогда такой вопрос — а как можно определить, какая буква назначена устройству? Потому что в моём коде информация получается по щелчку на путь устройства (например, F:), т.е. мне нужно где-то найти совпадение.
По поводу перевода из букв в гуид - скорее всего с помощью WinIoCtl. Так навскидку мне слабо.
Блин, вопрос то о другом был.. Но скорей всего через тот-же IoCtl.
Код:
HDEVINFO hDevs=SetupDiGetClassDevs(&GUID_DEVINTERFACE_…,0,0,DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
if(hDevs!=(HANDLE)-1){
DWORD i=0,dwBytes;SP_DEVINFO_DATA DevData={sizeof SP_DEVINFO_DATA};
while(true){
if(SetupDiEnumDeviceInfo(hDevs,i,&DevData)){
SetupDiGetDeviceRegistryProperty(hDevs,&DevData,SPDRP_…,0,0,0,&dwBytes);
char *szProp=(char*)HeapAlloc(hHeap,0,dwBytes);
SetupDiGetDeviceRegistryProperty(hDevs,&DevData,SPDRP_…,0,(PBYTE)szProp,dwBytes,&dwBytes);
MessageBox(hDlg,szProp,0,MB_ICONINFORMATION);
HeapFree(hHeap,0,szProp);
}else if(GetLastError()==ERROR_NO_MORE_ITEMS)
break;
i++;
}
SetupDiDestroyDeviceInfoList(hDevs);
}
if(hDevs!=(HANDLE)-1){
DWORD i=0,dwBytes;SP_DEVINFO_DATA DevData={sizeof SP_DEVINFO_DATA};
while(true){
if(SetupDiEnumDeviceInfo(hDevs,i,&DevData)){
SetupDiGetDeviceRegistryProperty(hDevs,&DevData,SPDRP_…,0,0,0,&dwBytes);
char *szProp=(char*)HeapAlloc(hHeap,0,dwBytes);
SetupDiGetDeviceRegistryProperty(hDevs,&DevData,SPDRP_…,0,(PBYTE)szProp,dwBytes,&dwBytes);
MessageBox(hDlg,szProp,0,MB_ICONINFORMATION);
HeapFree(hHeap,0,szProp);
}else if(GetLastError()==ERROR_NO_MORE_ITEMS)
break;
i++;
}
SetupDiDestroyDeviceInfoList(hDevs);
}
Имя устройства я получаю с помощью комбинации GUID_DEVINTERFACE_DISK / SPDRP_FRIENDLYNAME, а путь к устройству — через GUID_DEVINTERFACE_VOLUME / SPDRP_PHYSICAL_DEVICE_OBJECT_NAME (потом с помощью QueryDosDevice я могу из имени тома получить путь к устройству). Но как мне определить, что какой-то том находится на устройстве?
Upd.: рассуждая логически, можно перечислять все диски, а потом перечислять тома, принадлежащие этим дискам. Но это возможно только в случае иерархической структуры, но пока я такой возможности не нашёл.
Код:
HDEVINFO hDevs=SetupDiGetClassDevs(&GUID_DEVINTERFACE_VOLUME,0,0,DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
if(hDevs!=(HANDLE)-1){
DWORD i=0,dwBytes;SP_DEVINFO_DATA DevData={sizeof SP_DEVINFO_DATA};
while(true){
if(SetupDiEnumDeviceInfo(hDevs,i,&DevData)){
ULONG ul;
if(!CM_Get_DevNode_Status(&ul,&ul,DevData.DevInst,0)){
DEVINST hDIParent;
if(!CM_Get_Parent(&hDIParent,DevData.DevInst,0)){
CM_Get_Device_ID_Size(&ul,hDIParent,0);
char *szDevId=(char*)HeapAlloc(hHeap,0,++ul);
if(!CM_Get_Device_ID(hDIParent,szDevId,ul,0)){
HDEVINFO hParentDev=SetupDiCreateDeviceInfoList(0,0);
if(SetupDiOpenDeviceInfo(hParentDev,szDevId,0,0,&DevData)){
SetupDiGetDeviceRegistryProperty(hParentDev,&DevData,SPDRP_ENUMERATOR_NAME,0,0,0,&dwBytes);
char *szName=(char*)HeapAlloc(hHeap,0,dwBytes);
SetupDiGetDeviceRegistryProperty(hParentDev,&DevData,SPDRP_ENUMERATOR_NAME,0,(PBYTE)szName,dwBytes,&dwBytes);
MessageBox(hDlg,szName,0,MB_ICONINFORMATION);
HeapFree(hHeap,0,szName);
}else
MessageBox(hDlg,"SetupDiOpenDeviceInfo",0,MB_ICONERROR);
SetupDiDestroyDeviceInfoList(hParentDev);
}else
MessageBox(hDlg,"CM_Get_Device_ID",0,MB_ICONERROR);
HeapFree(hHeap,0,szDevId);
}else
MessageBox(hDlg,"CM_Get_Parent",0,MB_ICONERROR);
}else
MessageBox(hDlg,"CM_Get_DevNode_Status",0,MB_ICONERROR);
}else if(GetLastError()==ERROR_NO_MORE_ITEMS)
break;
i++;
}
SetupDiDestroyDeviceInfoList(hDevs);
}
if(hDevs!=(HANDLE)-1){
DWORD i=0,dwBytes;SP_DEVINFO_DATA DevData={sizeof SP_DEVINFO_DATA};
while(true){
if(SetupDiEnumDeviceInfo(hDevs,i,&DevData)){
ULONG ul;
if(!CM_Get_DevNode_Status(&ul,&ul,DevData.DevInst,0)){
DEVINST hDIParent;
if(!CM_Get_Parent(&hDIParent,DevData.DevInst,0)){
CM_Get_Device_ID_Size(&ul,hDIParent,0);
char *szDevId=(char*)HeapAlloc(hHeap,0,++ul);
if(!CM_Get_Device_ID(hDIParent,szDevId,ul,0)){
HDEVINFO hParentDev=SetupDiCreateDeviceInfoList(0,0);
if(SetupDiOpenDeviceInfo(hParentDev,szDevId,0,0,&DevData)){
SetupDiGetDeviceRegistryProperty(hParentDev,&DevData,SPDRP_ENUMERATOR_NAME,0,0,0,&dwBytes);
char *szName=(char*)HeapAlloc(hHeap,0,dwBytes);
SetupDiGetDeviceRegistryProperty(hParentDev,&DevData,SPDRP_ENUMERATOR_NAME,0,(PBYTE)szName,dwBytes,&dwBytes);
MessageBox(hDlg,szName,0,MB_ICONINFORMATION);
HeapFree(hHeap,0,szName);
}else
MessageBox(hDlg,"SetupDiOpenDeviceInfo",0,MB_ICONERROR);
SetupDiDestroyDeviceInfoList(hParentDev);
}else
MessageBox(hDlg,"CM_Get_Device_ID",0,MB_ICONERROR);
HeapFree(hHeap,0,szDevId);
}else
MessageBox(hDlg,"CM_Get_Parent",0,MB_ICONERROR);
}else
MessageBox(hDlg,"CM_Get_DevNode_Status",0,MB_ICONERROR);
}else if(GetLastError()==ERROR_NO_MORE_ITEMS)
break;
i++;
}
SetupDiDestroyDeviceInfoList(hDevs);
}
Как тогда система определяет (в окне безопасного извлечения устройств), к какому устройству относятся тома?
Значит IOCTL_STORAGE_GET_DEVICE_NUMBER лазейка. +1
Типа того. Увы, от томов к устройствам у меня перейти не получилось, так же, как и перечислять GUID_DEVINTERFACE для USB (там я вряд ли бы такое провернул, хотя попробовать стоило бы). Но я рад тому, чего достиг ☺
Вот видимо поэтому многие винду не любят, что у них что ни возьми, без бубна редко :( Хорошо, что MSDN получше стал за последние годы.
Просто у всего есть своя логика, и её надо понять. Для меня эта тема была новой, поэтому пришлось долго и упорно разбираться. В конце концов, это лишь один из способов, и тот факт, что их несколько, даже лучше — если нет возможности реализовать задуманное одним способом, найдётся как минимум ещё один ;-)
Насчёт получше не знаю. Как и раньше — не хватает какой-нибудь ключевой информации или пояснений; и хорошо, если в комментариях найти можно :D
В конце концов, это лишь один из способов, и тот факт, что их несколько, даже лучше — если нет возможности реализовать задуманное одним способом, найдётся как минимум ещё один ;-)
Я бы сказал еще один и даже больше, которыми тоже нельзя :) Можно на самом деле, но иногда слишком все запутано.
В MSDN сейчас некоторые разделы дописаны до вполне исчерпывающего состояния. Например, работа с COM портами. Было бы во всем так.