As a consequence of the problem described here:
WPD MTP stream hangs on commit
the UI of my WPF program is hanging (meaning it goes unresponsive while a certain task is performed), despite making the call using a BackgroundWorker. I have read something about this having something to do with the fact that the COM object on which the operations are performed is created on the UI thread, but when I break on the piece of code that hangs I see that it in fact is executed in a WorkerThread and not on MainThread, which seems to suggest that is not the problem (although I am a threading rookie).
Can anyone explain why this happens and how I should solve this UI hang?
Relevant code:
From base class, where the backgroundworker is set up:
public void Sync()
{
SyncBackgroundWorker.WorkerReportsProgress = true;
SyncBackgroundWorker.WorkerSupportsCancellation = true;
SyncBackgroundWorker.DoWork += SyncBackgroundWorker_DoWork;
SyncBackgroundWorker.ProgressChanged += SyncBackgroundWorker_ProgressChanged;
SyncBackgroundWorker.RunWorkerCompleted += SyncBackgroundWorker_RunWorkerCompleted;
SyncBackgroundWorker.RunWorkerAsync();
}
protected void SyncBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Retrieve needed files from iTunes and compose m3u playlists
var syncInfo = this.getSyncInfo();
// Save m3u files to music root on target
this.saveM3UPlaylists(syncInfo.M3Us);
}
From class that extends base class (the last line initiates the file copy):
protected override void saveM3UPlaylists(List<FileInfo> M3UPlaylists)
{
foreach (var file in this.Folder.GetFiles())
{
if (file.Extension == ".m3u")
file.Delete();
}
int numPlaylists = this.Playlists.Count;
for (int i = 0; i < numPlaylists; i++)
PortableDevices.PortableDeviceFile.CopyFileToDevice(this.Folder, makeValidFileName(this.Playlists[i].Name + ".m3u"), M3UPlaylists[i]);
}
From relevant portable device class. The targetStream.Commit(0)
part causes the actual freeze, as described in the previously given link. Note that the the COM-object (IPortableDevice) parent.Device.Content
has been previously initialized, when the connection with the device was set-up, at the start of the program.
public static void CopyFileToDevice(PortableDeviceFolder parent, string name, FileInfo file)
{
IPortableDeviceValues values = GetRequiredPropertiesForContentType(parent.Id, name, file.Length);
PortableDeviceApiLib.IStream tempStream;
uint blockSize = 0;
parent.Device.Content.CreateObjectWithPropertiesAndData(
values,
out tempStream,
ref blockSize,
null);
System.Runtime.InteropServices.ComTypes.IStream targetStream =
(System.Runtime.InteropServices.ComTypes.IStream)tempStream;
try
{
using (var sourceStream = file.OpenRead())
{
var buffer = new byte[blockSize];
int bytesRead;
do
{
bytesRead = sourceStream.Read(buffer, 0, (int)blockSize);
targetStream.Write(buffer, bytesRead, IntPtr.Zero);
} while (bytesRead > 0);
}
targetStream.Commit(0);
}
finally
{
Marshal.ReleaseComObject(tempStream);
}
parent.Refresh();
}