Буква диска в идентификатор экземпляра устройства
Как мне перейти от буквы диска к идентификатору экземпляра устройства?
Мой процесс начинается с сообщения о прибытии устройства. Мне удалось получить букву диска из сообщения о прибытии и открыть лоток для DVD.
Я искал различные пункты настройки API; но я не нашел ничего, что могло бы привести меня от буквы диска к идентификатору экземпляра устройства.
Решение на C# или VB.NET было бы идеальным, но я готов выяснить это на любом другом языке, пока я вижу вызовы API.
Заранее спасибо...
2 ответа
Вы не можете сделать это напрямую.
Ссылка для использования STORAGE_DEVICE_NUMBER
, Вы можете использовать DeviceIoControl с IOCTL_STORAGE_GET_DEVICE_NUMBER
на вашем устройстве имя, чтобы заполнить эту структуру. Поместите это значение в одну сторону.
Затем вам нужно получить информацию об устройстве в вашей системе, используя SetupDiGetClassDevs
установив GUIDS в качестве оценочного, указав диски, в которых вы заинтересованы. Затем перечислите устройства, использующие SetupDiEnumDeviceInfo
, Затем перечислите интерфейсы, используя SetupDiEnumDeviceInterfaces
и, наконец, получить информацию, используя SetupDiGetDeviceInterfaceDetail
, В этой возвращенной структуре вы можете получить DevicePath, который вы можете использовать для получения STORAGE_DEVICE_NUMBER
как указано выше. Сопоставьте это с STORAGE_DEVICE_NUMBER
из вашей буквы диска, и теперь вы связали букву драйвера с вашей структурой. Уф! Внутри этой структуры находится DevInst.
Я знаю, что это годы спустя, но мне пришлось это сделать, и поиск привел меня сюда, и ответ @DanDan сработал. Чтобы сэкономить будущим людям много работы, я подумал, что верну немного и представлю технику более подробно. Вам все равно придется написать немного кода, но часть, которую я нашел трудной, находится ниже в виде кода:
Как упоминал DanDan, идея состоит в том, чтобы использовать CreateFile
а также DeviceIoControl
получить винду STORAGE_DEVICE_NUMBER
для диска, связанного с путем к файлу, а затем используйте API установки для перечисления дисковых устройств, пока мы не найдем одно, чей экземпляр устройства равен SDN.
Во-первых, вот краткое изложение того, как получить STORAGE_DEVICE_NUMBER
с пути (например, c:\\users\\bob
);
- Удалите путь к корню (например, до
C:
) и добавьте к нему\\\\.\\
так что у тебя есть\\\\.\\C:
- Откройте этот путь, используя
CreateFileW
с, чтобы получить метаданные - Использовать
DeviceIoControl
с участиемIOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
получить экстенты - Получить
DiskNumber
член из первой степени вернулся. - Закройте файл
- Открыть
\\\\.\\PhysicalDrive<n>
где<n>
это то, что DiskNumber из первого экстента - Использовать
DeviceIoControl
с кодомIOCTL_STORAGE_GET_DEVICE_NUMBER
чтобы заставить его заполнитьSTORAGE_DEVICE_NUMBER
структура как вывод - Использовать
SetupDiGetClassDevs
с аргументами&GUID_DEVCLASS_DISKDRIVE
а такжеDICGF_PRESENT
получить все диски в системе - В цикле используйте
SetupDiEnumDeviceInfo
чтобы получитьSP_DEVINFO_DATA
повторно (в списке устройств, возвращенном на шаге #8 выше) и вызовите функцию ниже, чтобы определить, какое из них, если таковое имеется, соответствуетSTORAGE_DEVICE_NUMBER
для уступки пути.
(Это отредактировано, чтобы удалить мои настраиваемые служебные классы прямо на веб-странице SO, поэтому я мог ввести ошибки / опечатки)
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://stackru.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;
}
Надеюсь, это избавит кого-то от головной боли
Я знаю, что уже поздно для тебя, но не для всех ^^
У меня была такая же потребность, и это основная линия того, как я это сделал:
-Вы должны иметь окно для получения устройства прибытия и удаления (как вы сказали)
-Тогда вы создаете DeviceNotificationFilter, инициированный для dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE
-Тогда в цикле сообщений вашего окна вы ищете VM_DEVICECHANGE
-Если вы получите его, если wParam == DBT_DEVICEARRIVAL, используйте lParam, чтобы проверить, является ли это DBT_DEVTYPE_VOLUME (я получал здесь букву и тип диска) или DBT_DEVTYPE_DEVICEINTERFACE (там вы можете использовать свой lcastek для получения из структуры ввода).
Когда вы подключаете диск, сначала вы получаете DEVINTERFACE, а затем другой. Я даю только основную строку, потому что я сделал это давным-давно, и у меня нет кода здесь, а также я нашел много фрагментов кода в сети (давно, так что теперь должно быть больше ^^^) Может быть, MSDN дать полный пример кода, чтобы сделать это сейчас.
Если вы прочитаете это и вам потребуется дополнительная информация, я отвечу или сделаю полный документальный ответ, если многим это нужно.
Надеюсь, это поможет некоторым из вас.