使用AsParallel()时,无法获取与Windows应用程序相关的文件图标
本文关键字:应用程序 Windows 图标 文件 获取 AsParallel 使用 | 更新日期: 2023-09-27 18:07:10
我想显示与文件相关的图标。对于与普通桌面应用程序相关的文件类型,这不是问题,但只有与(metro/modern)应用程序相关的文件类型才会出现问题。
如果文件类型与应用程序和相关联,我使用AsParallel()
,我只得到默认的未知文件类型图标。澄清一下,我没有得到null
或空图标,而是默认的图标,显示一个空的纸张。没有AsParallel()
我得到正确的图标。
我尝试了几种其他方法来获得图标,例如,SHGetFileInfo()
或通过dll直接调用ExtractAssociatedIcon()
。行为总是一样的。
示例:如果' adobeacrobat '是PDF文件的默认应用程序,那么在两种情况下我都会得到正确的adobepdf图标。如果来自Windows 8或10的内置(现代UI)应用程序"Reader"是默认应用程序,当应用AsParallel()
时,我会得到未知的文件类型图标。
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;
}
用法注意:只使用两个变体中的一个。如果两者都执行,即使使用不同的变量,问题也可能无法重现。
这是因为SHGetFileInfo
(或ExtractAssociatedIcon
,它在内部使用SHGetFileInfo
)只在STA线程(单线程公寓)上工作。在MTA线程(多线程公寓)中,它只返回默认图标。AsParallel
使用线程池中的工作线程,即MTA。
Thomas的回答基本上是正确的,使用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();