Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 740
Stefan Hoffmann Avatar asked Aug 11 '15 00:08

Stefan Hoffmann


People also ask

How do I associate an icon file?

Right click extension whose icon you want to change and then select “Edit Selected File Type.” In the “Edit File Type” window, click the “…” button to the right of the Default Icon text field. The “Change Icon” window shows some basic icons, but click the “Browse” button to find your own icon files.

How do I change file icons in Windows 7?

Step 1: Right-click on a folder you want to customize and select "Properties." Step 2: In the "Customize" tab, go to the "Folder icons" section and click the "Change Icon" button. Step 3: Choose one of the many icons listed in the box then click OK.


2 Answers

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.

like image 90
Thomas Levesque Avatar answered Sep 22 '22 16:09

Thomas Levesque


Thomas' answer is basically correct and using STA threads solves the problem. Knowing the cause of the problem, this answer hinted me in the right direction. With a TaskScheduler that uses STA threads, I can use Parallel.ForEach() to run my code.

With the StaTaskScheduler from here (more infos: MSDN blog article), the following code solves my problem.

var list = new List<string>();
list.Add(TxtFilename.Text);

var ts = new StaTaskScheduler(Environment.ProcessorCount); // can be saved for later reuse

var icons = new ConcurrentBag<ImageSource>();
var pOptions = new ParallelOptions { TaskScheduler = ts };

Parallel.ForEach(list, pOptions, file => icons.Add(GetIcon(file)));
TheIcon.Source = icons.First();
like image 42
Stefan Hoffmann Avatar answered Sep 21 '22 16:09

Stefan Hoffmann