问题 在C#中枚举Windows便携设备


我试图使用Windows Portable Devices API和此API提供的PortableDeviceManager在Windows上枚举连接的便携式设备。

我已经在MSDN文档之后实现了设备ID的枚举 链接 和各种博客 链接,但它们都会导致同样的问题 - 我只能得到它给我的ID  设备有多个连接时。

这是我正在使用的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记忆棒和一台数码相机。当两者都处于活动状态时,将仅返回我的相机的设备ID。当我取消激活相机时,将返回可移动USB记忆棒的设备ID。

是否有任何有这种API经验的人可以指出我做错的方向?


11057
2018-05-28 13:54


起源

对我来说,我尝试使用 PortableDeviceApiLib (COM)但是没有编译我发现任何完整的源样本都没有工作。我尝试过WMI Win32ext_WPD  但这对我来说很难。 - PreguntonCojoneroCabrón


答案:


巴丹吉林,

看看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并使用以下命令重新组装interop:

    ilasm pdapi.il /dll /output=Interop.PortableDeviceApiLib.dll

重建您的项目。你现在可以先打个电话 GetDevices 用一个 NULL 用于获取设备数量的参数,然后使用数组再次调用它以获取设备ID。

希望这可以帮助。


14
2018-05-29 07:34



谢谢Christophe!我不确定我是如何错过这一段的 - 我之前已经通过博文了!我已经得到它来报告正确数量的设备,但是我遇到了第二次调用GetDevices()时挂起的应用程序的问题。 - Jaran
要解决调用GetDevices()挂起的问题,我按照这里的说明操作: codeproject.com/Messages/3187340/... 并替换为“instance void GetDevices([in] [out] string [] marshal(lpwstr [])pPnPDeviceIDs,”而不是“instance void GetDevices([in] [out] string [] marshal([])pPnPDeviceIDs,” 。 - Jaran
你如何阻止Interop重建为旧的? - simon.d
@Jaran的评论帮助我解决了我的“Marshaler限制:过长的弦乐”。错误。我发布了IL,我最终在这里: gist.github.com/4327544 - rikkit
什么是WMI Win32ext_WPD?不好的文件googleing。仅供参考: squadratechnologies.wordpress.com/2013/07/24/... - Kiquenet


下面的一行Power-shell脚本取消安装连接的USB电缆 Windows便携设备(WPD) 来自Windows操作系统(XP至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 =   '三星公司的SGH-I747'”

不好的文件谷歌,没有找到更多2参考。

也许通过以下方式移植到C#:

var oScope = new ManagementScope(@"\\" + MachineName + @"\root\cimv2");

参考:

http://squadratechnologies.wordpress.com/2013/07/24/windows-powershellvbscript-to-un-mount-a-smart-phone-or-tablet/


1
2017-08-26 08:46





也许我错了,但行 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中生成错误的Interop 问题 - 看起来默认的互操作程序集不正确。也许成功的方法是从某人那里获得正确的互操作程序集并使用它。或者在托管代码(C#/ VB.NET / ...)和本机代码之间使用另一个桥梁。如果你在C ++中足够好,那么C ++ \ CLI可能是个好方法。

我发现 便携式设备库 CodePlex上的项目 - 也许它是正确的桥梁。


0
2018-05-28 14:04



通常我会同意你的看法,但是API没有提供这样的方法。它要求您传递一个字符串指针。我的理解是你将指针传递给数组的头部,GetDevices方法将使用cDevices数量的设备ID填充数组,从该地址开始。 - Jaran
@Jaran:问题是,“字符串”是.NET中的引用类型,因此如果从数组传递对第一个字符串的引用,则与数组中的第二个字符串无关。图像 - 一个字符串(一个引用)可以是0-N字符串数组。那么,哪个字符串(引用)应该是“第一个”的下一个? - TcKs
为什么他们传递指针?他们只是可以返回结果。我只是好奇。 - OneWorld
通常,字符串值可能很大。所以只传递参考更便宜。在这种情况下,您必须询问设备管理器API的作者。 - TcKs


WPD-.NET的包装 上 github上  - 可以成功枚举设备和复制文件。这样你就不必浪费时间编码了。你可以打开,连接和复制......

https://github.com/kumushoq/WPD-.NET-Wrapper/blob/2d502d883b981b8bc57d32389e8280877632f6de/WindowsPortableDeviceNet/Utility.cs


0
2017-07-03 09:39



对我来说,不是编译, PortableDeviceApiLib; 是COM吗?我需要在Windows 7 x64中安装什么? - PreguntonCojoneroCabrón


只是对已接受答案的更新。

正确的替换如下。

GetDevices([in][out] string& marshal( lpwstr) pPnPDeviceIDs,

GetDevices([in][out] string[] marshal( lpwstr[]) pPnPDeviceIDs,

按照 安德鲁特雷瓦罗


0
2017-11-12 01:31





我迟到了,但这对我有用:

  1. 下载此NuGet包: 便携式设备

  2. 添加对这4个COM库的引用:

    • PortableDeviceClassExtension
    • PortableDeviceConnectApi
    • PortableDeviceTypes
    • PortableDeviceApi
  3. 拿下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();

0
2017-10-02 15:11