IWebBrowser2::Quit在某些资源管理器标签页上不起作用
我正在尝试用一个文件夹来关闭资源管理器的标签页。要在C#中做到这一点,我有这段漂亮的代码:
[ComImport, Guid("9BA05972-F6A8-11CF-A442-00A0C90A8F39"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IShellWindows : IEnumerable
{
int Count { get; }
object Item(object index);
new IEnumerator GetEnumerator();
}
public void CloseExplorerWindowsForPath(string path)
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return;
_logger.LogInformation("Closing Explorer windows for path: {}", path);
// ShellWindows is an STA COM object. Calling it from the MTA thread pool
// (used by gRPC) causes Quit() to be silently ignored by Explorer because
// the cross-apartment message is never pumped. A dedicated STA thread is required.
Exception? threadException = null;
var staThread = new Thread(() =>
{
var shellType = Type.GetTypeFromCLSID(new Guid("9BA05972-F6A8-11CF-A442-00A0C90A8F39"));
if (shellType == null) return;
dynamic shellWindows = Activator.CreateInstance(shellType)!;
try
{
var toClose = new List<dynamic>();
foreach (dynamic window in shellWindows)
{
try
{
string location = window.LocationURL;
// Convertir l'URL file:/// en chemin normal
string windowPath = Uri.UnescapeDataString(
new Uri(location).LocalPath);
_logger.LogDebug("{} against {}", windowPath, path);
// Fermer si le chemin correspond ou est un sous-dossier
if (windowPath.StartsWith(path, StringComparison.OrdinalIgnoreCase))
{
_logger.LogDebug("Adding window with path {} to close list.", windowPath);
toClose.Add(window);
}
}
catch { /* Ignorer les fenêtres inaccessibles */ }
}
foreach (dynamic window in toClose)
{
try { window.Quit(); }
catch (Exception e) { _logger.LogDebug(e.Message); }
}
}
catch (Exception ex)
{
threadException = ex;
}
});
staThread.SetApartmentState(ApartmentState.STA);
staThread.Start();
staThread.Join();
if (threadException is not null)
throw threadException;
}
大体上能起作用。但有一个标签页,无论我把它设置在哪里,它都不肯关掉。我没有异常。可能的原因是什么?显然Stack Overflow想让我多写点字,于是就有了这一段。
解决方案
嗯,我有时确实能重现……这很奇怪,但下面给出一个应该能起作用的变通方法,它使用 IWebBrowser2::HWND 和 PostMessage,让拥有者窗口来关闭:
var staThread = new Thread(() =>
{
var shellType = Type.GetTypeFromCLSID(new Guid("9BA05972-F6A8-11CF-A442-00A0C90A8F39"))!;
dynamic shellWindows = Activator.CreateInstance(shellType!)!;
// we need to loop here because some windows may not close immediately
do
{
var toClose = new List<dynamic>();
foreach (var window in shellWindows)
{
// add your logic here...
toClose.Add(window);
}
if (toClose.Count == 0)
break;
foreach (var window in toClose)
{
nint hwnd;
try
{
hwnd = (nint)window.HWND;
}
catch
{
continue;
}
const uint WM_CLOSE = 0x0010;
PostMessage(hwnd, WM_CLOSE, 0, 0);
}
} while (true);
});
staThread.SetApartmentState(ApartmentState.STA);
staThread.Start();
staThread.Join();
[DllImport("user32")]
public static extern nint PostMessage(nint hWnd, uint Msg, nint wParam, nint lParam);
PS:你对 IShellWindows 的定义没用,因为你使用了 dynamic(IDispatch undercovers)。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。