C# WPF中调用Shell32.Shell.Windows()耗时过长是什么原因?
C#调用Shell32枚举资源管理器窗口启动阶段延迟问题解答
延迟根因
- WPF的
OnStartup方法执行时,应用的主消息循环尚未完成初始化。Shell32.Shell属于STA COM组件,接口调用依赖Windows消息泵完成COM跨套间封送处理,消息泵未就绪时调用会触发COM内部的超时等待逻辑,7-15秒就是COM调用的默认超时阈值。 - Windows外壳会优先为已完成启动的应用分发COM调用请求,处于启动阶段的应用的外壳COM请求会被暂时挂起,直到系统判定应用启动完成,进一步加剧了延迟。
优化方案
方案1:调整调用时机
将枚举逻辑放到主窗口的Loaded事件中执行,此时应用消息泵已完全启动,COM调用不会触发超时:
// App.xaml.cs 示例代码 protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); MainWindow mainWindow = new MainWindow(); mainWindow.Loaded += (sender, args) => { Shell32.Shell o = new Shell32.Shell(); dynamic allWindows = o.Windows(); for (int i = 0; i < allWindows.Count; i++) { dynamic item = allWindows.Item(i); string location = item.LocationURL as string; Console.WriteLine("Full path: {0}", location); } }; mainWindow.Show(); }
方案2:独立STA线程执行
如果需要在启动阶段就获取枚举结果,可以新建独立STA线程执行逻辑,避免阻塞主线程启动流程:
protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); Thread shellQueryThread = new Thread(() => { List<string> pathList = new List<string>(); Shell32.Shell o = new Shell32.Shell(); dynamic allWindows = o.Windows(); for (int i = 0; i < allWindows.Count; i++) { dynamic item = allWindows.Item(i); string location = item.LocationURL as string; if (!string.IsNullOrEmpty(location)) { pathList.Add(location); } } // 如需更新UI,调度回主线程执行 Application.Current.Dispatcher.BeginInvoke(new Action(() => { foreach (var path in pathList) { Console.WriteLine("Full path: {0}", path); } })); }); // 必须设置为STA线程才能调用Shell32 COM组件 shellQueryThread.SetApartmentState(ApartmentState.STA); shellQueryThread.IsBackground = true; shellQueryThread.Start(); }
方案3:替换dynamic为强类型调用
提前添加Shell32的COM引用,用强类型接口代替dynamic调用,可减少运行时动态绑定的额外开销:
// 强类型写法无需dynamic运行时解析 Shell32.Shell shell = new Shell32.Shell(); Shell32.Windows allWindows = shell.Windows(); foreach (Shell32.InternetExplorer window in allWindows) { // 资源管理器窗口会复用IE的COM接口 string location = window.LocationURL; Console.WriteLine("Full path: {0}", location); }
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

