Search code examples
c#.netwpfwindowsplinq

Can't get file icon for files associated with Windows apps when using AsParallel()


I want to display the icons associated with files. This isn't a problem for file types associated with normal desktop applications but only for those associated with (metro/modern) apps.

If a file type is associated with an app and I am using AsParallel(), I get only the default unknown file type icon. To clarify, I don't get null or an empty icon, but the default icon that shows an empty paper sheet. Without AsParallel() I get the correct icon.

I tried several other ways to get the icon, e.g., SHGetFileInfo() or calling ExtractAssociatedIcon() directly via the dll. The behavior was always the same.

Example: If 'Adobe Acrobat' is the default application for PDF files, I get the correct Adobe PDF icon in both cases. If the built-in (modern UI) app 'Reader' from Windows 8 or 10 is the default app, I get the unknown file type icon when AsParallel() is applied.

MCVE

XAML:

<Window x:Class="FileIconTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <TextBox x:Name="TxtFilename" Text="x:\somefile.pdf"/>
        <Button Click="Button_Click">Go</Button>
        <Image x:Name="TheIcon" Stretch="None"/>
    </StackPanel>
</Window>

Corresponding code:

private void Button_Click(object sender, RoutedEventArgs e)
{
    var list = new List<string>();
    list.Add(TxtFilename.Text);

    var icons = list.AsParallel().Select(GetIcon); // problem with apps
//  var icons = list.Select(GetIcon);              // works always
    TheIcon.Source = icons.First();
}

public static ImageSource GetIcon(string filename)
{
    var icon = System.Drawing.Icon.ExtractAssociatedIcon(filename);
    var iSource = Imaging.CreateBitmapSourceFromHIcon(icon.Handle, Int32Rect.Empty,
        BitmapSizeOptions.FromEmptyOptions());
    iSource.Freeze();
    return iSource;
}

Usage note: Use only one of the two variants. If both are executed, even with different variables, the problem may not be reproducible.


Solution

  • That's because SHGetFileInfo (or ExtractAssociatedIcon, which uses SHGetFileInfo internally) only works on a STA thread (single thread apartment). On an MTA thread (multiple thread apartment), it just returns the default icon. AsParallel uses worker threads from the thread pool, which are MTA.