使用AsParallel()时无法获取与Windows应用程序关联的文件的文件图标

我想显示与文件关联的图标。 对于与普通桌面应用程序相关联的文件types,这不是问题,而只适用于与(metro / modern)应用程序相关的文件types。

如果文件types与应用程序关联, 并且使用AsParallel() ,则只会获取默认的未知文件types图标。 为了说明,我不会得到null或图标,而是显示空白纸张的默认图标。 没有AsParallel()我得到正确的图标。

我尝试了其他几种方法来获取图标,例如, SHGetFileInfo()或通过dll直接调用ExtractAssociatedIcon() 。 行为总是一样的。

示例 :如果“Adobe Acrobat”是PDF文件的默认应用程序,则在这两种情况下我都会得到正确的Adobe PDF图标。 如果Windows 8或10中的内置(现代UI)应用程序“Reader”是默认应用程序,则在应用AsParallel()时,将获得未知文件types图标。

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> 

相应的代码:

 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; } 

使用说明:只使用两种变体中的一种。 如果两者都执行,即使有不同的variables,问题可能是不可重现的。

这是因为SHGetFileInfo (或ExtractAssociatedIcon ,内部使用SHGetFileInfo )只适用于STA线程(单线程单元)。 在MTA线程(多线程单元)上,它只是返回默认图标。 AsParallel使用来自线程池(MTA)的工作线程。

托马斯的答案基本上是正确的,使用STA线程解决了这个问题。 知道了问题的原因, 这个答案暗示我在正确的方向。 通过使用STA线程的TaskScheduler,我可以使用Parallel.ForEach()来运行我的代码。

从这里的StaTaskScheduler (更多信息: MSDN博客文章 ),下面的代码解决了我的问题。

 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();