我有一种在Internet Explorer中打开URL的方法。如果已打开Internet Explorer,则应在新选项卡中打开。如果没有,它应该打开一个新的Internet Explorer。
我的代码:
public static void OpenURL(string Url)
{
bool already_navigated = false;
ShellWindows instances = new ShellWindows();
//Check if there is an Internet Explorer
if (instances.Count > 0)
{
foreach (InternetExplorer ie in instances)
{
if (ie.Name == "Windows Internet Explorer")
{
if (!already_navigated)
{
//Navigate and open in New Tab
already_navigated = true;
ie.Navigate(Url, 0x10000);
//Bring window to front
IntPtr hwnd = (IntPtr)ie.HWND;
WindowHandler.Window w = new WindowHandler.Window(hwnd, "Internet Explorer");
w.Minimize();
w.Restore();
}
}
}
}
//No internet explorer found!
if (!already_navigated)
{
//Start new Internet Explorer
Process proc = Process.Start("IExplore.exe", Url);
}
}
这很棒!但是当我调用这段代码时,我的记忆会在无限循环中增加,直到我得到 OutOfMemoryException ......
经过一些试验和错误后,我发现此代码会引发异常:
public static void OpenURL(string Url)
{
bool already_navigated = false;
ShellWindows instances = new ShellWindows();
/*
//Check if there is an Internet Explorer
if (instances.Count > 0)
{
foreach (InternetExplorer ie in instances)
{
if (ie.Name == "Windows Internet Explorer")
{
if (!already_navigated)
{
//Navigate and open in New Tab
already_navigated = true;
ie.Navigate(Url, 0x10000);
//Bring window to front
IntPtr hwnd = (IntPtr)ie.HWND;
WindowHandler.Window w = new WindowHandler.Window(hwnd, "Internet Explorer");
w.Minimize();
w.Restore();
}
}
}
}
//No internet explorer found!
if (!already_navigated)
{
//Start new Internet Explorer
Process proc = Process.Start("IExplore.exe", Url);
}*/
}
此代码不包含
public static void OpenURL(string Url)
{
bool already_navigated = false;
/*
ShellWindows instances = new ShellWindows();
//Check if there is an Internet Explorer
if (instances.Count > 0)
{
foreach (InternetExplorer ie in instances)
{
if (ie.Name == "Windows Internet Explorer")
{
if (!already_navigated)
{
//Navigate and open in New Tab
already_navigated = true;
ie.Navigate(Url, 0x10000);
//Bring window to front
IntPtr hwnd = (IntPtr)ie.HWND;
WindowHandler.Window w = new WindowHandler.Window(hwnd, "Internet Explorer");
w.Minimize();
w.Restore();
}
}
}
}*/
//No internet explorer found!
if (!already_navigated)
{
//Start new Internet Explorer
Process proc = Process.Start("IExplore.exe", Url);
}
}
这只留下一个结论,问题在于:
ShellWindows instances = new ShellWindows();
但是经过一些谷歌搜索,我找不到有类似问题的人。所以我不确定我做错了什么,或者我对这个问题是否正确。
有谁知道发生了什么事?
答案 0 :(得分:2)
ShellWindows是一个COM对象。你依靠垃圾收集器来释放RCWs(Runtime Callable Wrappers),由终结器处理。
但是当你的代码只调用OpenUrl()但没有做任何其他事情时,这将不会很好。垃圾收集器仅在分配托管对象时运行。如果你不这样做,并且你发布的代码没有做太多的事情,那么你将面临用尽非托管内存的重大风险,即COM对象所使用的内存。 / p>
该条件易于诊断,请使用Perfmon.exe并查看.NET CLR Memory的性能计数器。 “Gen 0 Collections”计数器显示gen 0垃圾收集的执行频率。如果该代码在代码运行时没有经常更改,则可能会出现OOM kaboom。
这就是为什么GC.Collect()存在,计算你创建的“ie”实例的数量,并以某种幻数调用Collect。 Marshal.ReleaseComObject()也可以帮助,通常更麻烦然后它值得,但应该在这里没有麻烦。并且在循环中使用 break 。
ShellWindows的另一个方面是它是一个公寓线程COM对象。一个昂贵的词意味着它不是线程安全的。这在COM中很重要,它代表对象处理线程安全。如果此代码不在Winforms或WPF应用程序的主线程上运行,您将很容易遇到麻烦。像控制台模式应用程序或服务。或者,如果您在工作线程上运行此代码。
对于公寓线程COM对象来说,这不是一个幸福的家,它需要一个STA线程来满足线程安全要求。 COM将负责这一点,它将创建一个 new 线程,为对象提供一个安全的家。当您创建大量长寿的对象时,这可能会失控。易于诊断,打开非托管调试,并密切关注Debug + Windows + Threads窗口。我们不知道你的其余代码在做什么,但是当代码运行了一段时间后,看到数百个线程就会出现问题。每个线程为其堆栈吞吐一兆字节的虚拟内存,耗费32位进程耗费不超过几万。通过在Main()方法上应用[STAThread]属性或在启动它之前调用Thread上的SetApartmentState()来修复它。
答案 1 :(得分:1)
此代码应与您合作:
public static void OpenURL(string Url)
{
var t = Type.GetTypeFromProgID("Shell.Application");
dynamic o = Activator.CreateInstance(t);
try
{
var instances = o.Windows();
// Check if there is an Internet Explorer
if (instances.Count > 0)
{
for (int i = 0; i < instances.Count; i++)
{
var ie = instances.Item(i);
if (ie == null) continue;
var path = System.IO.Path.GetFileName((string)ie.FullName);
if (path.ToLower() == "iexplore.exe")
{
//Navigate and open in New Tab
ie.Navigate(Url, 0x10000);
return;
}
}
}
}
finally
{
Marshal.FinalReleaseComObject(o);
}
//No internet explorer found. Start a new onr
Process.Start("IExplore.exe", Url);
}