在.NET 4.0应用程序(WPF)中,我们使用SHGetfileInfo来获取目录树的shell图标。 因为在某些情况下(比如无法访问的networking驱动器或软盘驱动器)需要相当长的一段时间,所以我们希望在一个线程中执行此 *** 作,然后在读取图标时更新图标。
调用基本相同,现在只是在一个线程内执行。 因为有人说线程必须是STA才能工作,所以我们使用Thread而不是ThreadPool来进行testing,结果相同。 使用ThreadPool也没有工作。
SHGetfileInfo成功(返回1),但结构中的hIcon成员为零。
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; }
非常相同的代码从GUI线程工作正常。 为了使这个函数能够从一个单独的线程中工作,或者使它在不阻塞GUI线程的情况下工作,需要做些什么?
获取任务栏中所有窗口的句柄
我应该使用什么gacutil.exe?
检查networking驱动器上是否存在目录
windows文件名中不允许使用哪些字符,如何限制它们在C#中input
在没有文件下载对话框的情况下在Webbrowser控件中打开办公文档
更新:这个代码基本上是这样的:
var thread = new System.Threading.Thread(() => { var result = GetIcon(\”C:\\\”); // … do something with the result }); thread.SetApartmentState(System.Threading.ApartmentState.STA); thread.Start();
如果只剩下线程委托中的行,它就可以正常工作(当然,在GUI线程上)。
更新:现在,我们只需调用SHGetfileInfo来使其工作。 这样做的好处是,原来的问题(带有文件视图的页面在所有的图标被加载之前都没有显示出来)已经解决了,尽pipe这意味着每个图标都会挂起页面。 但是至less用户现在看到了一些事情正在发生。 我们仍然在寻找解决问题的实际办法。
使用凭证的UNCpath
创buildwindows服务启动之间的依赖关系
光标不改变自定义控制出口
.net 4.0开发的程序在重启后有时无法启动
wnd.Show(Me)不能在WPF中工作
我不认为有任何问题。 您不需要使用SetApartmentState。 根据文档,你需要调用CoInitialize或者oleInitialize,但是我认为WPF应该已经调用了它。
下面我创建了一个简单的WPF应用程序。 这工作正常。 SHGetfileInfo在与UI线程不同的线程上运行,而shfi.hIcon不为零。
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,(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); }
只是成功地得到了这样的工作。 在Visual Studio之外运行时,它以前一直崩溃。 在此之前,我们通常会取回默认图标,而不是正确的文件类型(因为我们在文件图标之后,而不是目录图标)。
要考虑的因素的总结。
正如已经讨论的那样,与Shell进行交互需要使用一个STA消息泵。 BackgrounDWorker在这里还不够。
初始化SHfileINFO时, 将字符串属性(显示名称和类型名称)设置为string.Empty@H_404_52@ 。 这在大多数样本中都没有显示,但是没有做到这一点就导致了我们的崩溃。 (在调试模式下,这意味着我们返回的第一个图标是错误的,这可能与您的问题相同。)
检查你的interop声明是否正确。 例如, SHfileINFO类应该具有[StructLayout(LayoutKind.Sequential,CharSet = CharSet.Unicode,Pack = 4)]属性。
STA线程上的后台任务的TaskScheduler
我们使用任务并行库,所以需要一个TaskScheduler来安排在合适的后台线程上工作。 以下代码示例适用于公开可用于此目的的TaskScheduler属性的类。
请注意,在我们的应用程序的整个生命周期中,我们有一个这个类的实例,所以我们没有实现Idisposable。 如果你想创建/销毁这些,你需要处理。
namespace Mynamespace { using System; using System.ComponentModel.Composition; using System.Threading; using System.Threading.Tasks; using System.windows; using System.windows.Threading; /// <summary> /// Exposes a <see cref=\”TaskScheduler\”/> that schedules its work on a STA background thread. /// </summary> [Export] public class StaTaskSchedulerSource { /// <summary> /// A window that is used for message pumPing. /// </summary> private Window window; /// <summary> /// Thread on which work is scheduled. /// </summary> private Thread thread; /// <summary> /// The <see cref=\”TaskScheduler\”/> exposed by this class. /// </summary> private TaskScheduler taskScheduler; /// <summary> /// Initializes a new instance of the <see cref=\”StaTaskSchedulerSource\”/> class. /// </summary> public StaTaskSchedulerSource() { using (ManualresetEvent re = new ManualresetEvent(false)) { this.thread = new Thread( () => { this.window = new Window(); re.Set(); dispatcher.Run(); }); this.thread.IsBackground = true; this.thread.SetApartmentState(ApartmentState.STA); this.thread.Start(); re.WaitOne(); } this.window.dispatcher.Invoke( new Action( () => { this.taskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); })); } /// <summary> /// Gets a <see cref=\”TaskScheduler\”/> that schedules work on a background STA /// thread. /// </summary> public TaskScheduler TaskScheduler { get { return this.taskScheduler; } } } }
当然,这整个事情只是使用调度程序来调用另一个线程上的调用。 这可能有点慢。 所以如果处理很多图标,最好把它们分组。 此外,我们缓存已经检索到的图标,所以我们不需要再次使用Win Shell。
SafeIconHandle
你也可以找到下面的图标处理包装器有用。 它来自SafeHandle,并确保您的图标在任何情况下都被正确销毁。
namespace UnmanagedResourcelib { using System; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using Microsoft.Win32.SafeHandles; /// <summary> /// A <see cref=\”SafeHandle\”/> implementation for HICON icon handles. /// </summary> [SecurityPermission(SecurityAction.inheritanceDemand,UnmanagedCode = true)] [SecurityPermission(SecurityAction.Demand,UnmanagedCode = true)] internal class SafeIconHandle : SafeHandleZeroOrMinusOneIsInvalID { /// <summary> /// Prevents a default instance of the <see cref=\”SafeIconHandle\”/> class from being created. /// </summary> private SafeIconHandle() : base(true) { } /// <summary> /// Initializes a new instance of the <see cref=\”SafeIconHandle\”/> class. /// </summary> /// <param name=\”nativeHandle\”>The HICON to wrap.</param> /// <param name=\”ownsHandle\”><c>true</c> if finalization of this object should cause the icon to be destroyed.</param> public SafeIconHandle(IntPtr nativeHandle,bool ownsHandle) : base(ownsHandle) { this.handle = nativeHandle; } /// <inheritdoc /> [ReliabilityContract(Consistency.WillNotCorruptState,Cer.MayFail)] overrIDe protected bool ReleaseHandle() { return NativeMethods.DestroyIcon(this.handle); } /// <summary> /// Exposes windows API call to destroy an icon. /// </summary> [SuppressUnmanagedCodeSecurity] internal static class NativeMethods { /// <summary> /// Destroys an icon and frees any memory the icon occupIEd. /// </summary> /// <param name=\”hIcon\”>A handle to the icon to be destroyed.</param> /// <returns><c>true</c> if the function succeeds.</returns> [System.Runtime.InteropServices.Dllimport(\”user32.dll\”,SetLastError = true)] public static extern bool DestroyIcon(IntPtr hIcon); } } }
总结
以上是内存溢出为你收集整理的在线程中调用SHGetFileInfo以避免UI冻结全部内容,希望文章能够帮你解决在线程中调用SHGetFileInfo以避免UI冻结所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
请登录后查看评论内容