4

I want to delete a file stored in an USB flashdrive (actually an android device's sd card).

I ask the user to point the app's folder inside the sd card, and i hold a Shell32.Folder object pointing to it.

I can iterate between the files (FolderItem objects), but how can i delete a file using Shell32 classes?

Usual File.Delete(filePath) does not work, since the FolderItem.Path is a BSTR data type, so maybe the key is to convert from one type to another, but i couldn't find a way to do this.

Any ideas?

EDIT 1:

FolderItem.Path data:

"::{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}"

This is not a valid path for File.Delete. Any ideas on how to convert that?

EDIT 2:

Method that opens the browse folder window, so the user can point out the app directory, inside his android device's SD card, so i can iterate with folders and files, and sync some data with the server. Done this way due to problems between Win8 and 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 });
}

Using the method above, to get the Folder:

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

I create and copy a file into the app's 'data' folder, so the device can recognize a syncing action, tell the user, and close the app:

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;
        }
    }
}

After the sync, i want to delete the file, so the app can function normally:

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;
    }
}

As mentioned in EDIT 1, file.Path of the file i want to delete, has the following format:

"::{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}"

EDIT 3:

As suggested here, i tried using P/Invoke to delete the file, with no success.

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

Calling the method:

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

The method returns false, and the file is not deleted. I looked in the Event Viewer for a clue about what happened, but found nothing.

Marcelo Vitoria
  • 591
  • 1
  • 5
  • 15
  • 1
    That's not the way it works. When you add a reference to shell32.dll then you use the COM interop that's built into the CLR. Which automatically converts back-and-forth between BSTR and System.String – Hans Passant Mar 27 '14 at 18:02
  • Thanks @HansPassant, i see. So, FolderItem.Path is already a managed string, but its content is not in the form that File.Delete works. In this case, how can i convert this path? or even delete the file using shell? – Marcelo Vitoria Mar 27 '14 at 20:27
  • How about `ToString()`? Seems you should get a `String` from that. – Hogan Mar 27 '14 at 21:29
  • @Hogan, the data is a string already, i edited the question so it can be more clear what i'm trying to accomplish. – Marcelo Vitoria Mar 28 '14 at 13:20
  • Have you tried `Path`? – B.K. Mar 28 '14 at 15:53
  • @B.K., i have, it throws an 'invalid path' exception for methods like `GetFullPath()` or `GetDirectoryName()`. – Marcelo Vitoria Mar 28 '14 at 16:00
  • I guess we need to see the code how you get the Folder object. – Hogan Mar 28 '14 at 20:33
  • @M42C3L0 Hmm... I'm not sure then. As Hogan mentioned, we'd have to see the implementation. – B.K. Mar 28 '14 at 21:53
  • You can't use `FileInfo` and its `Delete` method? – B.K. Mar 29 '14 at 00:57
  • @B.K @Hogan, sorry for the delay. I tried `FileInfo`'s `Delete` method, same 'invalid path' error. I edited the question, adding more code. – Marcelo Vitoria Mar 31 '14 at 14:05
  • Have you tried a P/Invoke to [`DeleteFile`](http://blogs.msdn.com/b/bclteam/archive/2007/03/26/long-paths-in-net-part-2-of-3-long-path-workarounds-kim-hamilton.aspx)? – Mgetz Mar 31 '14 at 14:06
  • Thanks @Mgetz, i tried with no success. The code is executed but returns `false`, and the file is not deleted. I looked in the Event Viewer for a clue about what happened, but found nothing. I'll update the question with this attempt. – Marcelo Vitoria Apr 02 '14 at 19:54

2 Answers2

0

The part of the path which looks like "\?\" tells you that it's a long path. Sadly the CLR cannot handle long paths (it's limited to MAX_PATH = 260 nonsense). I believe you can use the WinAPI's DeleteFile to accomplish what you want.

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

Use PInvoke and you'll be all set.

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

whoisj
  • 398
  • 1
  • 2
  • 10
  • @B.K. Odd. I did a quick search and found this blog, tested the results and they work for me on Win8. http://blogs.msdn.com/b/bclteam/archive/2007/03/26/long-paths-in-net-part-2-of-3-long-path-workarounds-kim-hamilton.aspx -- might be the ::{} prefix causing issues. – whoisj Apr 04 '14 at 21:47
0

The guid prefix ("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}") is the standard junction point for MyComputer (see https://msdn.microsoft.com/en-us/library/windows/desktop/cc144096(v=vs.85).aspx).

I suspect the file cannot be deleted using such methods as the shell API DeleteFile because it is not in the file system (you can check it using FolderItem.IsFileSystem) because the android device is connected using MTP.

I have been able to delete such files using FolderItem.InvokeVerb("Delete"). This produces a confirmation dialog which can be dismissed using some version of SendKeys (for example freeware Nirsoft's Nircmd.exe). The following is a VBA sample:

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
Dale Thompson
  • 115
  • 1
  • 7