【问题标题】:Delete a FolderItem删除文件夹项
【发布时间】:2014-03-27 16:31:47
【问题描述】:

我想删除存储在 USB 闪存驱动器(实际上是 android 设备的 sd 卡)中的文件。

我要求用户将应用程序的文件夹指向 sd 卡内,我持有一个指向它的 Shell32.Folder 对象。

我可以在文件之间进行迭代(FolderItem 对象),但是如何使用Shell32 类删除文件?

通常的 File.Delete(filePath) 不起作用,因为 FolderItem.Path 是 BSTR 数据类型,所以也许关键是从一种类型转换为另一种类型,但我找不到这样做的方法。

有什么想法吗?

编辑 1:

FolderItem.Path数据:

“:: {20D04FE0-3AEA-1069-A2D8-08002B30309D} \\\?\ USB#vid_04e8&pid_6860&ms_comp_mtp&GT-P5100#7&392be4e4&0&0000#{6ac27878-a6fa-4155-BA85-f98f491d4f33} \ SID- {10001,SECZ9519043CHOHB,12530364416 }\{015C008D-013D-0145-D300-D300CB009500}\{015C00EE-013D-0145-0201-37012C010901}\{025901D2-029D-020F-DE01-FE010D02A601}"

这不是File.Delete 的有效路径。关于如何转换的任何想法?

编辑 2:

打开浏览文件夹窗口的方法,因此用户可以指出应用程序目录,在他的 android 设备的 SD 卡内,所以我可以迭代文件夹和文件,并与服务器同步一些数据。由于Win8和Shell32之间的问题而这样做:

public Shell32.Folder GetShell32NameSpace(Object folder)
{
    Type shellAppType = Type.GetTypeFromProgID("Shell.Application");
    Object shell = Activator.CreateInstance(shellAppType);
    return (Shell32.Folder)shellAppType.InvokeMember("NameSpace", System.Reflection.BindingFlags.InvokeMethod, null, shell, new object[] { folder });
}

使用上述方法,获取文件夹:

IntPtr windowHandle = new WindowInteropHelper(this).Handle;
Folder androidPath  = BrowseForFolder(windowHandle.ToInt32(), "App's data folder", 0, 0);

我创建一个文件并将其复制到应用程序的“数据”文件夹中,以便设备可以识别同步操作、告诉用户并关闭应用程序:

byte[] data = new UTF8Encoding(true).GetBytes("sync_cable");

if (androidPath != null)
{
    foreach (FolderItem dir in androidPath.Items())
    {
        if (dir.IsFolder && dir.Name == "data")
        {
            Folder dataFolder = GetShell32NameSpace(dir.Path);
            if (dataFolder != null)
            {
                string tempPath = Path.GetTempPath();
                string path     = Path.Combine(tempPath, flagFileName);
                File.WriteAllBytes(path, data);

                Folder localPath = GetShell32NameSpace(tempPath);
                if (localPath != null)
                {
                    foreach (FolderItem file in localPath.Items())
                    {
                        if (file.Name.Contains(flagFileName))
                        {
                            dataFolder.CopyHere(file);
                            break;
                        }
                    }
                }
            }

            break;
        }
    }
}

同步后,我想删除文件,让应用程序可以正常运行:

foreach (FolderItem file in dataFolder.Items())
{
    if (file.Name.Contains(flagFileName))
    {
        // THIS THROWS AN 'INVALID PATH' EXCEPTION
        // FileInfo fi = new FileInfo(file.Path);
        // fi.Delete();

        // THIS ALSO THROWS AN 'INVALID PATH' EXCEPTION            
        // File.Delete(file.Path);

        break;
    }
}

如 EDIT 1 中所述,我要删除的文件的file.Path 具有以下格式:

“:: {20D04FE0-3AEA-1069-A2D8-08002B30309D} \\\?\ USB#vid_04e8&pid_6860&ms_comp_mtp&GT-P5100#7&392be4e4&0&0000#{6ac27878-a6fa-4155-BA85-f98f491d4f33} \ SID- {10001,SECZ9519043CHOHB,12530364416 }\{015C008D-013D-0145-D300-D300CB009500}\{015C00EE-013D-0145-0201-37012C010901}\{025901D2-029D-020F-DE01-FE010D02A601}"

编辑 3:

按照这里的建议,我尝试使用 P/Invoke 删除文件,但没有成功。

[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DeleteFile(string lpFileName);

调用方法:

foreach (FolderItem file in dataFolder.Items())
{
    if (file.Name.Contains(flagFileName))
    {
        // ...
        bool deleted = DeleteFile(file.Path);
        // ...
    }
}

方法返回false,文件没有被删除。我在事件查看器中查看了有关发生了什么的线索,但什么也没找到。

【问题讨论】:

  • 这不是它的工作方式。当您添加对 shell32.dll 的引用时,您将使用 CLR 中内置的 COM 互操作。它会自动在 BSTR 和 System.String 之间来回转换
  • 谢谢@HansPassant,我明白了。因此,FolderItem.Path 已经是一个托管字符串,但它的内容不是 File.Delete 工作的形式。在这种情况下,我该如何转换这条路径?甚至使用 shell 删除文件?
  • ToString() 怎么样?看来你应该从那里得到一个String
  • @Hogan,数据已经是一个字符串,我编辑了问题,以便更清楚我想要完成的工作。
  • 你试过Path吗?

标签: c# bstr shell32


【解决方案1】:

路径中看起来像“\?\”的部分告诉你这是一条很长的路径。遗憾的是,CLR 无法处理长路径(仅限于 MAX_PATH = 260 废话)。我相信你可以使用 WinAPI 的 DeleteFile 来完成你想要的。

http://msdn.microsoft.com/en-us/library/windows/desktop/aa363915(v=vs.85).aspx

使用 PInvoke,一切就绪。

http://pinvoke.net/default.aspx/kernel32/DeleteFile.html?diff=y

【讨论】:

【解决方案2】:

guid 前缀(“::{20D04FE0-3AEA-1069-A2D8-08002B30309D}”)是 MyComputer 的标准连接点(请参阅 https://msdn.microsoft.com/en-us/library/windows/desktop/cc144096(v=vs.85).aspx)。

我怀疑该文件无法使用 shell API DeleteFile 等方法删除,因为它不在文件系统中(您可以使用 FolderItem.IsFileSystem 检查它),因为 android 设备是使用 MTP 连接的。

我已经能够使用 FolderItem.InvokeVerb("Delete") 删除此类文件。这会产生一个确认对话框,可以使用某些版本的 SendKeys(例如免费软件 Nirsoft 的 Nircmd.exe)将其关闭。以下是 VBA 示例:

Private Sub SampleDelete()

Const sFolder = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\?\usb#vid_054c&pid_04cb#10fbd475671683#{6ac27878-a6fa-4155-ba85-f98f491d4f33}\SID-{10001,00000000000000000000000005671683,7513636864}\{00000007-0000-0000-0000-000000000000}\{000057F1-0000-0000-66A5-884D99020600}"
Const sFileName = "test.txt"

' requires reference to Shell32.dll
Dim oShell  As New Shell32.Shell
Dim oFolder As Shell32.Folder
Dim oFile As Shell32.FolderItem

Set oFolder = oShell.Namespace(sFolder)
If Not oFolder Is Nothing Then
    Set oFile = oFolder.ParseName(sFileName)
    If Not oFile Is Nothing Then
        Debug.Print "File system? " & oFile.IsFileSystem
        ' dismiss the confirmation dialog
        SendKeys "~"
        ' Alternatively use an external function, like this freeware utility
        ' Shell "nircmd.exe sendkey enter press", vbHide
        oFile.InvokeVerb "Delete"
    Else
        Debug.Print "File not found"
    End If
Else
    Debug.Print "Folder not found"
End If

Set oShell = Nothing
Set oFolder = Nothing
Set oFile = Nothing

End Sub

【讨论】:

    猜你喜欢
    • 2023-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-13
    • 1970-01-01
    • 2017-01-06
    • 2017-07-10
    相关资源
    最近更新 更多