One common cause of the ERROR_BUSY (0x800700AA) in MTP devices is when the application reads resource data from a content object on the portable device using IStream::Read(), and does not complete the read with an IStream::Close().   Subsequent operations that eventually translate into MTP commands into the device could fail with ERROR_BUSY because the resource read operation is not yet complete.     Sometimes the failure could appear delayed -  after the resource read, there could be several successful WPD API calls before ERROR_BUSY appears, because the interim calls that succeeded were handled by the MTP driver (i.e. do not require the driver to talk to the device).

From the device's perspective, reading resource data translates to a sequence of operations involving the MTP command "GetObject".    This has to be completed or cancelled before further MTP commands to the device could proceed.   

For example, if your application does the following:


IStream* pObjectDataStream = NULL;

// 1. Open the default resource of an object with read access
HRESULT hr = pIPortableDeviceResources->GetStream(wszObjectID, 
                                                     WPD_RESOURCE_DEFAULT, 
                                                     STGM_READ, 
                                                     &cbOptimalTransferSize,
                                                     &pObjectDataStream);

// 2. Read some data from the resource stream created by GetStream
if (hr == S_OK)
{
  hr = pObjectDataStream->Read(pObjectData, cbOptimalTransferSize, &cbBytesRead);
}

// 3. Do other operations that translate into MTP commands to the device
// and they could result in 0x800700AA as the resource read operation is not complete

To fix this, ensure that IStream::Release() is called to complete or cancel the current read operation before proceeding


IStream* pObjectDataStream = NULL;

// 1. Open the default resource of an object with read access
HRESULT hr = pIPortableDeviceResources->GetStream(wszObjectID, 
                                                     WPD_RESOURCE_DEFAULT, 
                                                     STGM_READ, 
                                                     &cbOptimalTransferSize,
                                                     &pObjectDataStream);

// 2. Read some data from the resource stream created by GetStream
if (hr == S_OK)
{
  hr = pObjectDataStream->Read(pObjectData, cbOptimalTransferSize, &cbBytesRead);
}

// 3. IMPORTANT: Release the IStream if no longer using it to ensure that the read operation
// completes.   
if(pObjectDataStream)
{
    pObjectDataStream->Release();  // If using a CComPtr, set it to NULL
}

// 4. Proceed on to the other operations

If your application is reading resource data from an MTP device, and you are seeing ERROR_BUSY, check that you are always calling Release() on the IStream objects that you have opened.    This also applies if you are using smart pointers (e.g. CComPtr<IStream>), in which case you can explicitly set the pointer to NULL to tell the smart pointer to Release() itself before you proceed on to other device operations.   This will release the pointer after you have completed your resource read/write operation(s), instead of relying on the IStream smart pointer to get out of scope.   

Similarly, if your application is writing resource data to an MTP device, ensure that you're calling IStream::Commit() or IStream::Revert() to complete the write operation. 

[June 11, 2007] Thanks to drlily for pointing out that IStream::Close does not actually exist.  It should have been IStream::Release for a read IStream, and IStream::Commit, IStream::Revert, or IStream::Release for a write IStream.

 

This posting is provided "AS IS" with no warranties, and confers no rights.