Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# thread hangs on close file

I have a thread that calls a static method to update file properties using WindowsAPICodePack ShellPropertyWriter and BackgroundWorker. The thread calls the method below for each file in a folder of 1000+ files and hangs on the ShellPropertyWriter.close() after the 700th update or so.

Nothing to do with the file itself, tried using different files that successfully updated before.

    public static bool ShellPropertyUpdate(VideoEntry mediaEntry)
    {
        try
        {
            ShellFile mediafile = ShellFile.FromFilePath(mediaEntry.FilePath);
            ShellPropertyWriter pw = mediafile.Properties.GetPropertyWriter();
            pw.WriteProperty(SystemProperties.System.Music.Artist, mediaEntry.Actor);
            pw.WriteProperty(SystemProperties.System.Music.Genre, mediaEntry.Genre);
            pw.WriteProperty(SystemProperties.System.Rating, mediaEntry.Rating);
            pw.Close();
        }
        catch (Exception ex)
        {
           return false;
        }
        return true;
    }

    private void mnuWriteMetadataToFiles_Click(object sender, EventArgs ev)
    {
        this.WorkerThread = new BackgroundWorker();
        this.WorkerThread.DoWork += new DoWorkEventHandler(WorkerThread_WriteMetadataToFiles);
        this.WorkerThread.ProgressChanged += new ProgressChangedEventHandler(WorkerThread_ProgressChanged);
        this.WorkerThread.RunWorkerCompleted += (s, e) => WorkerThread_Completed("Writing metadata to files", s, e);
        this.WorkerThread.WorkerReportsProgress = true;
        this.WorkerThread.WorkerSupportsCancellation = true;
        this.WorkerThread.RunWorkerAsync(WMPlayer);
    }

    private void WorkerThread_WriteMetadataToFiles(object sender, DoWorkEventArgs e)
    {
        int counter = 0;
        BackgroundWorker worker = (BackgroundWorker)sender;
        MediaPlayer wmp = (MediaPlayer)e.Argument;

        // ... Loop with the foreach video in the library and write it to file.
        foreach (VideoEntry entry in wmp.Videos)
        {
            if (worker.CancellationPending)
            {
                e.Cancel = true;
            }
            else
            {
                worker.ReportProgress(counter, "Updating '" + entry.Filename + "'" + Environment.NewLine + "Processing file");
                if (VideoToFile.ShellPropertyUpdate(entry))
                {
                    result &= true;
                }
                counter++;
            }
        }
        e.Result = result;
    }
like image 770
captainmoose Avatar asked Oct 19 '25 18:10

captainmoose


1 Answers

Never heard of this assembly before, but it smells like handle exhaustion to me. Try this instead:

using (ShellFile mediafile = ShellFile.FromFilePath(mediaEntry.FilePath))
{
    ShellPropertyWriter pw = mediafile.Properties.GetPropertyWriter();
    pw.WriteProperty(SystemProperties.System.Music.Artist, mediaEntry.Actor);
    pw.WriteProperty(SystemProperties.System.Music.Genre, mediaEntry.Genre);
    pw.WriteProperty(SystemProperties.System.Rating, mediaEntry.Rating);
    pw.Close();
}

Here every file handle is closed immediately, instead of at garbage collector's discretion. ShellFile must implement IDisposable for this to work, otherwise this code will not compile. I'm fairly certain that ShellFile implements it.

like image 93
Dialecticus Avatar answered Oct 22 '25 09:10

Dialecticus