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

用法注意:只使用两个变体中的一个。如果两者都执行,即使使用不同的变量,问题也可能无法重现。

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

这是因为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();