如何使用 USB and/or WPD 将文件从 Android 设备传输到 PC

How to transfer files from Android device to PC using USB and/or WPD

我正在尝试通过电缆将 android 设备连接到(Windows 7 64 位)PC,然后以编程方式将一些文件从 android 检索到 pc。

(注意:我需要这个用于特定设备,Moverio BT-200,我遇到了驱动程序问题;所以不建议使用 adb ; -) 我知道这是简单快捷的方法,但它不可行)

我发现我的设备可以被视为 Windows 便携式设备(WPD). I found some really good code examples in C# that enables me to detect WPDs, to enumerate their contents, and to transfer the contents. I also found some code in C++, which does all of the above and much more。只要我连接单个 SD 卡或 USB,所有这些示例都非常有效密钥(即:只要我有一个设备被 Windows 识别并获得一个字母作为正确的驱动器),我就会得到当前文件的完整列表及其绝对路径。但是,如果我尝试连接一个 android 设备,并列出内容,我得到一些我不明白的东西:

embt2
SD Card
o15F9A
o15F9B
o15F9C
o15F9D
o15F9E
...etc
Internal Storage
o1
o2
o3
o4
o5
o6
oD1F
oD20
oD24
o7
o8
o1E78
o9
...etc

怎么可能?浏览 C# 代码(第二个 link,上面),我发现在某些时候,代码创建了几个 GUID 对象,每个对象都有一些略有不同的参数:

// Identify the property to retrieve
var property = new _tagpropertykey();
property.fmtid = new Guid(0x26D4979A, 0xE643, 0x4626, 0x9E, 0x2B,
                                      0x73, 0x6D, 0xC0, 0xC9, 0x2F, 0xDC);
property.pid = 12;

[...]

// Get the name of the object
string name;
var property = new _tagpropertykey();
property.fmtid = new Guid(0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC,
                                  0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C);
property.pid = 4;

[...]

// Get the type of the object
property = new _tagpropertykey();
property.fmtid = new Guid(0xEF6B490D, 0x5CD8, 0x437A, 0xAF, 0xFC,
                                  0xDA, 0x8B, 0x60, 0xEE, 0x4A, 0x3C);
property.pid = 7;

[...]

var folderType = new Guid(0x27E2E392, 0xA111, 0x48E0, 0xAB, 0x0C,
                                  0xE1, 0x77, 0x05, 0xA0, 0x5F, 0x85);
var functionalType = new Guid(0x99ED0160, 0x17FF, 0x4C44, 0x9D, 0x98,
                                      0x1D, 0x7A, 0x6F, 0x94, 0x19, 0x21);

但我无法弄清楚这些十进制值是如何工作的。在线文档似乎很少。我在设备管理器 ({eec5ad98-8080-425f-922a-dabf3de3f69a} ) 中找到了我的设备 guid,但每次我尝试用我自己的 guid 替换其中一个时,我都会收到 COMException。

我找对地方了吗?我是否需要设置一些 GUID 或其他东西?

我将这个问题标记为 C# 和 C++,因为我找到了这两种语言的一些代码示例,但我愿意用任何语言解决问题 (java, python, . ..)

这些奇怪的文件名是怎么回事? 你发布的第一个列表中有很多以 o 开头后跟十六进制数的条目是 PTP/MTP 唯一项目标识符 允许您引用远程设备上的文件项目。这些是正常的并且是 PTP 和 MTP 协议的一部分,它们是 PTP 和 MTP 协议中与完整项目枚举和文件传输系统一起使用的众多数据之一。

所有 GUID 和细微变化有何关系? 这基本上是 USB HID(硬件识别)协议在幕后的工作方式 - 特别是 Microsoft 实现它的方式。 GUID 是 Microsoft 代码,它 link 到 USB 驱动程序中的 HID 路径,进而调用 USB 设备以获取信息(获取或设置信息)。每个 HID 命令还使用 PID(产品标识符)来寻址 HID 路径或命令中的特定内存位置,因此在 HID 命令中传递不同的 PID 会导致 getting/setting USB 设备上的不同数据。

有点悲伤和可怕的是微软如何将这种非常低级的交互暴露给高级工程师和程序。他们的文档确实没有提供很多有用的背景信息或解释这些东西是什么和意味着什么。如果您想要更可靠的示例代码和背景,我强烈建议您查看 Christophe Geer's blogs about using WPD in C#/.Net - they really helped me out a lot. See his follow-up blog on transferring content 以获得更多好东西。

另一条可能有用的建议 - 您可以浏览整个 PID 范围以查看其中的信息类型。只需将您的窥视和戳记包裹在 try/catch 块中,以防它们失败。我围绕 Christophe Geer 的代码构建了一个额外的小工具,可以查看他示例中所有 HID GUID 的 PID 0-31,并发现了很多额外的有用信息。

郑重声明,我最终使用了一些可怕的 hack 来解决我的问题。 因为我知道存储我要查找的文件的路径,所以我使用了 Christophe Geer 博客中的函数 EnumerateContent,并修改以检查当前文件夹是否具有我要查找的名称:

例如,如果我感兴趣的文件在 Sd Card/path/to/directory/ 中,那么我调整代码如下:我调用方法 EnumerateContents 而不是调用方法 EnumerateContents =14=]:

private static void EnumerateContentsInTargetDirectory(ref IPortableDeviceContent content, PortableDeviceFolder parent)
{
    // Get the properties of the object
    IPortableDeviceProperties properties;
    content.Properties(out properties);

    // Enumerate the items contained by the current object
    IEnumPortableDeviceObjectIDs objectIds;
    content.EnumObjects(0, parent.Id, null, out objectIds);

    uint fetched = 0;
    do
    {
        string objectId;

        objectIds.Next(1, out objectId, ref fetched);
        if (fetched > 0)
        {
            var currentObject = WrapObject(properties, objectId);

            if (currentObject is PortableDeviceFolder)
            {
                if (currentObject.Name.Equals("SD Card") || currentObject.Name.Equals("path") || currentObject.Name.Equals("to"))
                {
                    parent.Files.Add(currentObject);
                    EnumerateContentsInTargetDirectory(ref content, (PortableDeviceFolder)currentObject);
                }
                else if (currentObject.Name.Equals("directory"))
                {
                    parent.Files.Add(currentObject);
                    // This is the same original method of Christophe Geer.
                    EnumerateContents(ref content, (PortableDeviceFolder)currentObject);
                }
            }
        }
    } while (fetched > 0);

}