【问题标题】:Can't get file icon for files associated with Windows apps when using AsParallel()使用 AsParallel() 时无法获取与 Windows 应用程序关联的文件的文件图标
【发布时间】:2015-08-11 00:54:37
【问题描述】:

我想显示与文件关联的图标。对于与普通桌面应用程序关联的文件类型,这不是问题,但仅对与(都市/现代)应用程序关联的文件类型。

如果文件类型与应用程序相关联并且我正在使用AsParallel(),我只会得到默认的未知文件类型图标。澄清一下,我没有得到null 或空图标,而是显示空纸张的默认图标。如果没有AsParallel(),我会得到正确的图标。

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

示例:如果“Adobe Acrobat”是 PDF 文件的默认应用程序,那么在这两种情况下我都会得到正确的 Adob​​e PDF 图标。如果 Windows 8 或 10 中的内置(现代 UI)应用“阅读器”是默认应用,则在应用 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;
}

使用说明:仅使用两种变体中的一种。如果两者都执行,即使使用不同的变量,问题也可能无法重现。

【问题讨论】:

    标签: c# .net wpf windows plinq


    【解决方案1】:

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

    【讨论】:

    • 但这不应该适用于所有文件类型吗?为什么只有与应用关联的文件返回默认图标?
    • @StefanHoffmann,您的意思是与图标处理程序关联的文件类型吗?即图标可以根据文件的内容而有所不同?我想我记得这个限制仅适用于具有关联图标处理程序的文件类型,但我无法再重现该问题。
    • 不,不依赖于内容。我们以pdf文件为例。如果 Acrobat Reader 是默认应用程序,我得到 adobe pdf 图标就好了。即使使用 AsParallel()。如果 Windows 8 或 10 的内置现代 UI 应用程序“阅读器”是默认应用程序,我会得到带有 AsParallel() 的默认图标,但没有 AsParallel() 的正确图标。
    • @StefanHoffmann,我刚刚尝试使用 PDF 文件(Windows 阅读器是我的 PDF 的默认应用程序),但我无法重现该问题...我在两者中都得到了正确的图标案例
    • @StefanHoffmann,你是对的。如果我更改顺序,我现在可以重现问题。事实上,我可以看到它发生在某些文件类型而不是其他文件类型上。无论如何,如果您从 STA 线程获取图标,它总是有效的(除非您首先从 MTA 线程获取)。而且由于 SHGetFileInfo 不是线程安全的,您应该在 STA 线程上序列化调用。
    【解决方案2】:

    Thomas' answer 基本正确,使用 STA 线程可以解决问题。知道问题的原因后,this answer 向我暗示了正确的方向。通过使用 STA 线程的 TaskScheduler,我可以使用 Parallel.ForEach() 来运行我的代码。

    使用来自hereStaTaskScheduler(更多信息:MSDN blog article),以下代码解决了我的问题。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-20
      • 2011-04-23
      • 2019-08-31
      相关资源
      最近更新 更多