1

我有一种在 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();

但经过一番谷歌搜索,我找不到任何有类似问题的人。所以我不确定我是否做错了什么,或者我对这个问题是否正确。

有谁知道发生了什么?

4

2 回答 2

2

ShellWindows 是一个 COM 对象。您依靠垃圾收集器来释放 RCW(运行时可调用包装器),并由它们的终结器处理。

但是,当您的代码只调用 OpenUrl() 但不做任何其他事情时,这将不会那么好用。垃圾收集器仅在您分配托管对象时运行。如果您不这样做,并且您发布的代码并没有做太多的事情,那么您将面临耗尽非托管内存(COM 对象使用的那种内存)的重大风险。

这种情况很容易诊断,使用 Perfmon.exe 并查看 .NET CLR 内存的性能计数器。“Gen 0 Collections”计数器显示执行 gen 0 垃圾收集的频率。如果在您的代码运行时该计数器没有足够频繁地更改,那么很可能会出现 OOM 卡布姆。

这就是 GC.Collect() 存在的原因,计算您创建的“ie”实例的数量并以某种幻数调用 Collect。Marshal.ReleaseComObject() 也可以提供帮助,通常比它值得的麻烦更多,但在这里应该毫无问题地运行。并在你的循环中使用break 。

ShellWindows 的另一个方面是它是一个单元线程 COM 对象。一个昂贵的词,意味着它不是线程安全的。这在 COM 中很重要,它代表对象处理线程安全。如果此代码不在 Winforms 或 WPF 应用程序的主线程上运行,您很可能会遇到麻烦。就像控制台模式应用程序或服务一样。或者,如果您在工作线程上运行此代码。

对于一个单元线程的 COM 对象来说,这不是一个幸福的家,它需要一个 STA 线程来满足线程安全要求。COM 会解决这个问题,它会创建一个新的线程给对象一个安全的家。当您创建大量寿命过长的对象时,这可能会失控。也易于诊断,打开非托管调试并密切关注 Debug + Windows + Threads 窗口。我们不知道您的其余代码在做什么,但是当代码运行一段时间后看到那里有数百个线程意味着厄运。每个线程为其堆栈占用一兆字节的虚拟内存,耗尽一个 32 位进程并不需要超过几千块。通过在 Main() 方法上应用 [STAThread] 属性或在启动线程之前调用线程上的 SetApartmentState() 来解决此问题。

于 2013-05-23T14:43:51.253 回答
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);
}
于 2013-05-23T13:51:22.833 回答