在线程中调用的SHGetFileInfo避免UI冻结 [英] Calling SHGetFileInfo in thread to avoid UI freeze

查看:217
本文介绍了在线程中调用的SHGetFileInfo避免UI冻结的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在一个.NET 4.0应用程序(WPF),我们使用的SHGetFileInfo 来获得外壳的图标为一个目录树。因为这需要在某些情况下,相当长的一段时间(即一个网络驱动器无法访问或软盘驱动器),我们想要做这一个线程,然后更新图标,当它被读入。

In a .NET 4.0 application (WPF) we're using SHGetFileInfo to obtain shell icons for a directory tree. Since this takes quite some time in some cases (i.e. for a network drive that is unreachable or for a floppy drive), we wanted to do this in a thread and then update the icon when it has been read in.

呼叫基本相同,它现在只是一个线程中执行。因为有人说,线程必须是 STA 这个工作,我们使用了线程,而不是线程池进行检测,具有相同的结果。使用线程池也没有工作。

The call is basically the same, it is now just executed within a thread. Because someone said that the thread must be STA for this to work, we used Thread rather than ThreadPool for testing, with the same results. Using ThreadPool also did not work.

的SHGetFileInfo 成功(返回1),但在结构上惠康成员是零。

SHGetFileInfo succeeds (returns 1), but the hIcon member in the structure is zero.

IntPtr GetIcon(string name)
{
    Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO();
    uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES | Shell32.SHGFI_SMALLICON;

    Shell32.SHGetFileInfo(
        name, System.IO.Directory.Exists(name) ? Shell32.FILE_ATTRIBUTE_DIRECTORY : Shell32.FILE_ATTRIBUTE_NORMAL,
        ref shfi, 
        (uint) System.Runtime.InteropServices.Marshal.SizeOf(shfi), 
        flags );
    return shfi.hIcon;
}

而在同code正常工作从GUI线程。什么必须做,从一个单独的线程使函数的工作,或者,然而,使它不会阻塞GUI线程工作?

The very same code works fine from the GUI thread. What has to be done to make the function work from a separate thread, or, however, make it work without blocking the GUI thread?

更新:解决这个问题的code基本上是这样的:

Update: The code around this is basically this:

var thread = new System.Threading.Thread(() => {
    var result = GetIcon("C:\\");
    // ... do something with the result
});
thread.SetApartmentState(System.Threading.ApartmentState.STA);
thread.Start();

如果只有线代表中的线留在,它工作正常(但GUI线程,当然)。

if only the lines within the thread delegate are left in, it works fine (but on the GUI thread, of course).

更新:现在,我们只需调用调用的SHGetFileInfo ,使其工作。这具有(未显示与该文件视图页面,直到所有的图标都被​​装载)的原问题已经解决的优点,但它意味着该页挂起每个图标。但至少现在用户看到的东西是怎么回事。我们仍然在寻找一个实际的解决问题的办法。

Update: For now, we just Invoke the call to SHGetFileInfo to make it work. This has the advantage that the original problem (the page with the file view was not displayed until all the icons have been loaded) has been solved, though it means that the page hangs for each icon. But at least the user now sees that something is going on. We're still looking for an actual solution to the problem.

推荐答案

我不认为有任何问题。你并不需要使用SetApartmentState。按照文档你需要呼吁的CoInitialize或OleInitialize,但我认为这应该被称为为你反正用WPF。

I don't think there's any problem. You don't need to use SetApartmentState. According to the documentation you do need to have called CoInitialize or OleInitialize, but I think this should have been called for you anyway by WPF.

我创建了下面一个简单的WPF应用程序。这工作得很好。的SHGetFileInfo运行在不同的线程到UI线程和shfi.hIcon不为零。

I created a simple WPF application below. This works fine. SHGetFileInfo runs on a different thread to the UI thread and shfi.hIcon is not zero.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
        Task<IntPtr> task = Task.Factory.StartNew(() => GetIcon("C:\\"));
    }

    private IntPtr GetIcon(string name)
    {
        Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);

        var shfi = new Shell32.SHFILEINFO();
        uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES | Shell32.SHGFI_SMALLICON;

        Shell32.SHGetFileInfo(
            name,
            Directory.Exists(name) ? Shell32.FILE_ATTRIBUTE_DIRECTORY : Shell32.FILE_ATTRIBUTE_NORMAL,
            ref shfi,
            (uint) Marshal.SizeOf(shfi),
            flags);

        Debug.WriteLine(shfi.hIcon);

        return shfi.hIcon;
    }
}


public class Shell32
{
    public const int MAX_PATH = 256;

    // Browsing for directory.
    public const uint BIF_RETURNONLYFSDIRS = 0x0001;
    public const uint BIF_DONTGOBELOWDOMAIN = 0x0002;
    public const uint BIF_STATUSTEXT = 0x0004;
    public const uint BIF_RETURNFSANCESTORS = 0x0008;
    public const uint BIF_EDITBOX = 0x0010;
    public const uint BIF_VALIDATE = 0x0020;
    public const uint BIF_NEWDIALOGSTYLE = 0x0040;
    public const uint BIF_USENEWUI = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX);
    public const uint BIF_BROWSEINCLUDEURLS = 0x0080;
    public const uint BIF_BROWSEFORCOMPUTER = 0x1000;
    public const uint BIF_BROWSEFORPRINTER = 0x2000;
    public const uint BIF_BROWSEINCLUDEFILES = 0x4000;
    public const uint BIF_SHAREABLE = 0x8000;

    public const uint SHGFI_ICON = 0x000000100; // get icon
    public const uint SHGFI_DISPLAYNAME = 0x000000200; // get display name
    public const uint SHGFI_TYPENAME = 0x000000400; // get type name
    public const uint SHGFI_ATTRIBUTES = 0x000000800; // get attributes
    public const uint SHGFI_ICONLOCATION = 0x000001000; // get icon location
    public const uint SHGFI_EXETYPE = 0x000002000; // return exe type
    public const uint SHGFI_SYSICONINDEX = 0x000004000; // get system icon index
    public const uint SHGFI_LINKOVERLAY = 0x000008000; // put a link overlay on icon
    public const uint SHGFI_SELECTED = 0x000010000; // show icon in selected state
    public const uint SHGFI_ATTR_SPECIFIED = 0x000020000; // get only specified attributes
    public const uint SHGFI_LARGEICON = 0x000000000; // get large icon
    public const uint SHGFI_SMALLICON = 0x000000001; // get small icon
    public const uint SHGFI_OPENICON = 0x000000002; // get open icon
    public const uint SHGFI_SHELLICONSIZE = 0x000000004; // get shell size icon
    public const uint SHGFI_PIDL = 0x000000008; // pszPath is a pidl
    public const uint SHGFI_USEFILEATTRIBUTES = 0x000000010; // use passed dwFileAttribute
    public const uint SHGFI_ADDOVERLAYS = 0x000000020; // apply the appropriate overlays
    public const uint SHGFI_OVERLAYINDEX = 0x000000040; // Get the index of the overlay

    public const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
    public const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;

    [DllImport("Shell32.dll")]
    public static extern IntPtr SHGetFileInfo(
        string pszPath,
        uint dwFileAttributes,
        ref SHFILEINFO psfi,
        uint cbFileInfo,
        uint uFlags
        );

    #region Nested type: BROWSEINFO

    [StructLayout(LayoutKind.Sequential)]
    public struct BROWSEINFO
    {
        public IntPtr hwndOwner;
        public IntPtr pidlRoot;
        public IntPtr pszDisplayName;
        [MarshalAs(UnmanagedType.LPTStr)] public string lpszTitle;
        public uint ulFlags;
        public IntPtr lpfn;
        public int lParam;
        public IntPtr iImage;
    }

    #endregion

    #region Nested type: ITEMIDLIST

    [StructLayout(LayoutKind.Sequential)]
    public struct ITEMIDLIST
    {
        public SHITEMID mkid;
    }

    #endregion

    #region Nested type: SHFILEINFO

    [StructLayout(LayoutKind.Sequential)]
    public struct SHFILEINFO
    {
        public const int NAMESIZE = 80;
        public IntPtr hIcon;
        public int iIcon;
        public uint dwAttributes;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)] public string szDisplayName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NAMESIZE)] public string szTypeName;
    };

    #endregion

    #region Nested type: SHITEMID

    [StructLayout(LayoutKind.Sequential)]
    public struct SHITEMID
    {
        public ushort cb;
        [MarshalAs(UnmanagedType.LPArray)] public byte[] abID;
    }

    #endregion
}

/// <summary>
/// Wraps necessary functions imported from User32.dll. Code courtesy of MSDN Cold Rooster Consulting example.
/// </summary>
public class User32
{
    /// <summary>
    /// Provides access to function required to delete handle. This method is used internally
    /// and is not required to be called separately.
    /// </summary>
    /// <param name="hIcon">Pointer to icon handle.</param>
    /// <returns>N/A</returns>
    [DllImport("User32.dll")]
    public static extern int DestroyIcon(IntPtr hIcon);
}

这篇关于在线程中调用的SHGetFileInfo避免UI冻结的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆