Перечисление переносных устройств Windows в C#
Я пытаюсь перечислить подключенные переносные устройства в Windows с помощью API переносимых устройств Windows и PortableDeviceManager, предоставляемых этим API.
Я реализовал перечисление идентификаторов устройств, следуя ссылке на документацию MSDN и ссылкам на различные блоги, но все они приводят к одной и той же проблеме - я могу получить его только для того, чтобы дать мне идентификатор одного устройства, когда подключено несколько.
Вот фрагмент кода C#, который я использую:
PortableDeviceManagerClass deviceManager = new PortableDeviceManagerClass();
deviceManager.RefreshDeviceList();
uint numberOfDevices = 1;
deviceManager.GetDevices(null, ref numberOfDevices);
if (numberOfDevices == 0)
{
return new string[0];
}
string [] deviceIds = new string[numberOfDevices];
deviceManager.GetDevices(ref deviceIds[0], ref numberOfDevices);
return deviceIds;
К моему компьютеру подключены два устройства, одно съемное USB-устройство памяти и одно цифровое фотоаппарат. Когда оба активны, будет возвращен только идентификатор устройства моей камеры. Когда я деактивирую камеру, возвращается идентификатор устройства съемного USB-накопителя.
Есть кто-нибудь с опытом работы с этим API, который может указать мне, что я делаю неправильно?
6 ответов
Jaran,
Взгляните на следующий пост команды WPD, в котором упоминается, как можно исправить сборку взаимодействия.
http://blogs.msdn.com/b/dimeby8/archive/2006/12/05/enumerating-wpd-devices-in-c.aspx
Просто чтобы быть полным, я упомяну ответ здесь:
Это связано с жестким ограничением. Этот пример кода обнаружит только одно устройство. Вам необходимо вручную исправить сборку взаимодействия.
Разберите сборку PortableDeviceApi Interop с помощью команды:
ildasm Interop.PortableDeviceApiLib.dll /out:pdapi.il
Откройте IL в блокноте и найдите следующую строку:
instance void GetDevices([in][out] string& marshal( lpwstr) pPnPDeviceIDs,
Замените все экземпляры приведенной выше строки следующей строкой:
instance void GetDevices([in][out] string[] marshal([]) pPnPDeviceIDs,
Сохраните IL и соберите взаимодействие, используя команду:
ilasm pdapi.il /dll /output=Interop.PortableDeviceApiLib.dll
Перестрой свой проект. Теперь вы можете сначала позвонить GetDevices
с NULL
параметр, чтобы получить количество устройств, а затем вызвать его снова с массивом для получения идентификаторов устройств.
Надеюсь это поможет.
Просто обновленная информация о принятом ответе.
Правильная замена заключается в следующем.
GetDevices([in][out] string& marshal( lpwstr) pPnPDeviceIDs,
в
GetDevices([in][out] string[] marshal( lpwstr[]) pPnPDeviceIDs,
По Эндрю Треварроу.
В одной строке приведенного ниже сценария Power-shell отключается USB-кабель, подключенный к переносному устройству Windows (WPD) из операционной системы Windows (XP thr W8/2012)
И на тот случай, если вы еще не начали играть с Powershell, вот эквивалентный VBScript (может быть, можно портировать на C#):
Set objWMIService = GetObject ("winmgmts:\\.\root\cimv2")
Set colItems = objWMIService.ExecQuery ("Select * from Win32ext_WPD Where strFriendlyName = 'SAMSUNG-SGH-I747'")
For Each objItem in colItems
Set objWMIWPDStatic = objWMIService.Get("Win32ext_WPD")
Set objInParam = objWMIWPDStatic.Methods_("EjectDevice").inParameters.SpawnInstance_()
objInParam.Properties_.Item("strObjectDeviceId") = objItem.strId
Set objOutParams = objWMIService.ExecMethod("Win32ext_WPD", "EjectDevice", objInParam)
Exit For
Next
Замените "SAMSUNG-SGH-I747" на имя телефона / планшета, которое вы видите в проводнике Windows.
О Win32ext_WPD
"Выберите * из Win32ext_WPD, где strFriendlyName = 'SAMSUNG-SGH-I747'"
Не вполне документ Google, не найдено более 2 ссылок.
Возможно портировать на C# используя:
var oScope = new ManagementScope(@"\\" + MachineName + @"\root\cimv2");
Ссылка:
WPD-.NET-Wrapper на github - может успешно перечислять устройства и копировать файлы. так что вам не придется тратить время на кодирование. Вы можете просто открыть, подключить и скопировать...
Я опаздываю на вечеринку, но вот что сработало для меня:
Загрузите этот пакет NuGet: PortableDevices
Добавьте ссылки на эти 4 библиотеки COM:
- PortableDeviceClassExtension
- PortableDeviceConnectApi
- PortableDeviceTypes
- PortableDeviceApi
Возьми DLL
obj\Debug
и положить их вbin\Debug
:- Interop.PortableDeviceClassExtension.dll
- Interop.PortableDeviceConnectApiLib.dll
- Interop.PortableDeviceTypesLib.dll
- Interop.PortableDeviceApiLib.dll
Теперь вы можете использовать следующую функцию для отображения списка всех устройств, хотя FriendlyName
похоже не работает (возвращает пустую строку):
private IDictionary<string, string> GetDeviceIds()
{
var deviceIds = new Dictionary<string, string>();
var devices = new PortableDeviceCollection();
devices.Refresh();
foreach (var device in devices)
{
device.Connect();
deviceIds.Add(device.FriendlyName, device.DeviceId);
Console.WriteLine(@"DeviceId: {0}, FriendlyName: {1}", device.DeviceId, device.FriendlyName);
device.Disconnect();
}
return deviceIds;
}
Следующим шагом является получение содержимого с устройства, что делается так:
var contents = device.GetContents();
Может я ошибаюсь, но линия deviceManager.GetDevices(ref deviceIds[0], ref numberOfDevices);
не имеет смысла для меня. Если вы хотите заполнить массив строк, вы должны передать весь массив, а не только одну строку.
Я нашел пост на blgs.msdn.com, где передается весь массив.
string[] deviceIDs = new string[cDevices];
devMgr.GetDevices(deviceIDs, ref cDevices);
Однако это только предположение. Я надеюсь, это поможет вам.
РЕДАКТИРОВАТЬ: я нашел несколько примеров кода, где передается весь массив вместо первого элемента: например: http://social.msdn.microsoft.com/forums/en-US/vbgeneral/thread/22288487-caf3-4da5-855f-6447ad9fa48d
И я обнаружил, что импорт PortableDeviceApiLib генерирует неправильное взаимодействие в VB.NET - похоже, сборка взаимодействия по умолчанию не правильная. Возможно, удачный способ - получить правильную сборку взаимодействия от кого-то и использовать ее. Или используйте какой-то другой мост между управляемым кодом (C#/VB.NET/...) и собственным кодом. C++\CLI может быть хорошим способом, если вы достаточно хороши в C++.
Я нашел проект Portable Device Lib на CodePlex - возможно, это правильный мост.