user13947194
user13947194

Reputation: 402

WindowsPortableDevice IPortableDeviceContent::Delete Causes My Device to hang. How can I solve this?

I have been trying to build a file explorer of my Android phone content that would be optimized to my needs. As FindFirstFile api could not work for MTP devices I had to learn Windows Portable Device Api.

I met alot of obstacles but got most things working:

My only road block now is deleting files. I followed the MSDN sample perfectly but still met into problems. The problem is when I try to delete a file, the file gets deleted but the device hangs and becomes unresponsive. Not even Windows explorer is able to access it at that point. I have to disconnect and reconnect the device. However windows explorer is able to delete files on the device no problem.

This is my code

typedef const wchar_t *string_t;

bool wpd::Device::deleteFile(string_t file_id)
{
    PROPVARIANT file ={};
    IPortableDevicePropVariantCollection *filesToDelete = newIPortableDevicePropVariantCollection(); // Calls CoCreateInstance
    IPortableDeviceContent *devContentMgr;

    file.vt = VT_LPWSTR;
    file.pwszVal = (LPWSTR)file_id;
    filesToDelete->Add(&file);

    // IPortableDevice *dev initialized in constructor
    dev->Content(&devContentMgr);

    HRESULT hr = devContentMgr->Delete(PORTABLE_DEVICE_DELETE_NO_RECURSION,filesToDelete,NULL);

    filesToDelete->Release();
    devContentMgr->Release();

    return SUCCEEDED(hr);
}

Note that I am doing this on Windows Xp.

Upvotes: 3

Views: 421

Answers (1)

Max Zimm
Max Zimm

Reputation: 11

There's an unofficial solution. The method probably Explorer uses itself when working with WPD files.

Its nature was discovered by some Chinese developers and you can find the post on several Chinese platforms, for example here. The post describes how the developers reverse engineered the "good guy" logic (Explorer) using advanced tools like disassembling. This method was also briefly discussed in English/c# as a suggestion to implement it in a media library.

The only existing software that I have at hands using this trick is the extension PortaDev for Far Manager. And here is almost direct quote from the wpd.cpp file

#define CLSID_WPD_NAMESPACE_EXTENSION L"{35786D3C-B075-49B9-88DD-029876E11C01}"
.....
CComPtr<IPortableDevice>           _pd_device;     ///< Device
CComPtr<IPortableDeviceValues>     _pd_values;
....
        // Secret ingredient to avoid freeze after first delete operation
hr = _pd_values->SetStringValue(WPD_CLIENT_EVENT_COOKIE, CLSID_WPD_NAMESPACE_EXTENSION);
        //
hr = _pd_device->Open(device_id, _pd_values);

The CLSID_WPD_NAMESPACE_EXTENSION constant may be or not be the part of the official API, but its meaningful value might be seen with the following command-line execution (Enter into the Start-Run field)

explorer shell:::{35786D3C-B075-49b9-88DD-029876E11C01}  

which should open Portable Devices root node in Explorer probably on any Windows (tested on Windows 7, Windows 10)

The cookie feature that is used here allows registering a unique string that will be passed when notifying about different WPD operations. If you register some (unique) cookie you may detect that a notification about an operation comes from your own code and avoid extra operations (for example, re-scanning). Probably this particular cookie value (CLSID_WPD_NAMESPACE_EXTENSION) is how Explorer uses this feature and by registering it, you pretend that this operation comes from Explorer itself.

Actually, in Windows 10 you even can see the evidence with your own eyes. If you register you handler for IPortableDeviceEventCallback interface and catch the notification after some operation in Explorer, you will find this cookie (IPortableDeviceValues->GetStringValue(WPD_CLIENT_EVENT_COOKIE...).

But I could not detect the presence of the cookie in notifications in Windows 7. So while in Windows 10 there was indirect evidence of Explorer using it, for previous Windows version the evidence was scarce. But I found the steps that showed that this cookie also works in Windows 7, at least for some operations.

First, the steps to trigger the 5-minute freeze in all current MTP clients in Windows 7. The steps below don't require rebooting either for Windows or Android to be reproducible.

  • Close any visible Explorer windows
  • Disconnect your Android device (Android 11 in my case)
  • Connect your Android device, wait several seconds to make sure MTP is connected. Usually this comes with a sound.
  • Start a program that does some stream/delete operations every second. This part may be different and I suspect it may involve any operation with any part of the WPD/MTP storage hierarchy
  • Open "My Computer" (while the test program is running and using WPD API) and try to navigate to the icon of the storage of the Android device. The hanging should occur, but the exact action might be different, I saw it either while My Computer tried to show the contents or when I clicked on the device icon. This probably depends on the operations your test program is currently executing.
  • See that Explorer hangs itself and hangs the test program for 5+ minutes. Sometime after this it may show an alert about not finishing some waiting. ("The device is currently busy and its contents may not be fully displayed..." ) so this 5 minutes delay looks just like some hard-coded time out. If you stop your test program under the debugger while the hanging is in effect, you will probably find that it stops inside IPortableDevice->Open or some other WPD API call.

These steps were reproducible so several times at least I managed to enforce hanging with different operations in my test programs. After applying the change to the test program and repeating the same steps the hanging was gone.

Upvotes: 1

Related Questions