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.
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.
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.
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.
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.
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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With