在新的浏览器进程中打开URL

时间:2021-11-20 03:38:57

I need to open a URL in a new browser process. I need to be notified when that browser process quits. The code I'm currently using is the following:

我需要在新的浏览器进程中打开一个URL。我需要在浏览器进程退出时收到通知。我目前使用的代码如下:

        Process browser = new Process();
        browser.EnableRaisingEvents = true;
        browser.StartInfo.Arguments = url;
        browser.StartInfo.FileName = "iexplore";

        browser.Exited += new EventHandler(browser_Exited);

        browser.Start();

Clearly, this won't due because the "FileName" is fixed to iexplore, not the user's default web browser. How do I figure out what the user's default web browser is?

显然,这不会到期,因为“FileName”固定为iexplore,而不是用户的默认Web浏览器。如何确定用户的默认Web浏览器是什么?

I'm running on Vista->forward. Though XP would be nice to support if possible.

我正在运行Vista->前进。虽然如果可能的话XP会很好。

A bit more context: I've created a very small stand-alone web server that serves some files off a local disk. At the end of starting up the server I want to start the browser. Once the user is done and closes the browser I'd like to quit the web server. The above code works perfectly, other than using only IE.

更多上下文:我创建了一个非常小的独立Web服务器,它可以从本地磁盘上提供一些文件。在启动服务器结束时,我想启动浏览器。用户完成并关闭浏览器后,我想退出Web服务器。除了仅使用IE之外,上面的代码工作得很好。

Thanks in advance!

提前致谢!

7 个解决方案

#1


Ok. I now have working C# code to do what I want. This will return the "command line" you should run to load the current default browser:

好。我现在有工作C#代码来做我想要的。这将返回您应该运行的“命令行”以加载当前的默认浏览器:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Linq;
using System.Text;

namespace testDefaultBrowser
{
    public enum ASSOCIATIONLEVEL
    {
        AL_MACHINE,
        AL_EFFECTIVE,
        AL_USER,
    };

    public enum ASSOCIATIONTYPE
    {
        AT_FILEEXTENSION,
        AT_URLPROTOCOL,
        AT_STARTMENUCLIENT,
        AT_MIMETYPE,
    };

    [Guid("4e530b0a-e611-4c77-a3ac-9031d022281b"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IApplicationAssociationRegistration
    {
        void QueryCurrentDefault([In, MarshalAs(UnmanagedType.LPWStr)] string pszQuery,
        [In, MarshalAs(UnmanagedType.I4)] ASSOCIATIONTYPE atQueryType,
        [In, MarshalAs(UnmanagedType.I4)] ASSOCIATIONLEVEL alQueryLevel,
        [Out, MarshalAs(UnmanagedType.LPWStr)] out string ppszAssociation);

        void QueryAppIsDefault(
            [In, MarshalAs(UnmanagedType.LPWStr)] string pszQuery,
            [In] ASSOCIATIONTYPE atQueryType,
            [In] ASSOCIATIONLEVEL alQueryLevel,
            [In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName,
            [Out] out bool pfDefault);

        void QueryAppIsDefaultAll(
            [In] ASSOCIATIONLEVEL alQueryLevel,
            [In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName,
            [Out] out bool pfDefault);

        void SetAppAsDefault(
            [In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName,
            [In, MarshalAs(UnmanagedType.LPWStr)] string pszSet,
            [In] ASSOCIATIONTYPE atSetType);

        void SetAppAsDefaultAll(
            [In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName);

        void ClearUserAssociations();
    }

    [ComImport, Guid("591209c7-767b-42b2-9fba-44ee4615f2c7")]//
    class ApplicationAssociationRegistration
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
            IApplicationAssociationRegistration reg = 
                (IApplicationAssociationRegistration) new ApplicationAssociationRegistration();

            string progID;
            reg.QueryCurrentDefault(".txt",
                ASSOCIATIONTYPE.AT_FILEEXTENSION,
                ASSOCIATIONLEVEL.AL_EFFECTIVE,
                out progID);
            Console.WriteLine(progID);

            reg.QueryCurrentDefault("http",
                ASSOCIATIONTYPE.AT_URLPROTOCOL,
                ASSOCIATIONLEVEL.AL_EFFECTIVE,
                out progID);
            Console.WriteLine(progID);
        }
    }
}

Whew! Thanks everyone for help in pushing me towards the right answer!

呼!感谢大家帮助我推动正确的答案!

#2


If you pass a path of the known file type to the (file) explorer application, it will 'do the right thing', e.g.

如果将已知文件类型的路径传递给(文件)资源管理器应用程序,它将“做正确的事情”,例如

 Process.Start("explorer.exe", @"\\path.to\filename.pdf");

and open the file in the PDF reader.

并在PDF阅读器中打开该文件。

But if you try the same thing with a URL, e.g.

但是,如果您使用URL尝试相同的操作,例如

Process.Start("explorer.exe", @"http://www.*.com/");

it fires up IE (which isn't the default browser on my machine).

它会激活IE(这不是我机器上的默认浏览器)。

I know doesn't answer the question, but I thought it was an interesting sidenote.

我知道不回答这个问题,但我认为这是一个有趣的旁注。

#3


The way to determine the default browser is explained in this blog post:

此博客文章中介绍了确定默认浏览器的方法:

http://ryanfarley.com/blog/archive/2004/05/16/649.aspx

From the blog post above:

从上面的博客文章:

private string getDefaultBrowser()
{
    string browser = string.Empty;
    RegistryKey key = null;
    try
    {
        key = Registry.ClassesRoot.OpenSubKey(@"HTTP\shell\open\command", false);

        //trim off quotes
        browser = key.GetValue(null).ToString().ToLower().Replace("\"", "");
        if (!browser.EndsWith("exe"))
        {
            //get rid of everything after the ".exe"
            browser = browser.Substring(0, browser.LastIndexOf(".exe")+4);
        }
    }
    finally
    {
        if (key != null) key.Close();
    }
    return browser;
}

#4


Ok, I think I might have found it - IApplicationAssociationRegistration::QueryCurrentDefault [1]. According to the docs this is what is used by ShellExecute. I'll post code when I get it to work, but I'd be interested if others think this is the right thing to use (BTW, I'm Vista or greater for OS level).

好吧,我想我可能已经找到了它 - IApplicationAssociationRegistration :: QueryCurrentDefault [1]。根据文档,这是ShellExecute使用的。当我开始使用代码时我会发布代码,但是如果其他人认为这是正确的用法我会感兴趣(BTW,我是Vista或更高级别的操作系统级别)。

[1]: http://msdn.microsoft.com/en-us/library/bb776336(VS.85).aspx QueryCurrentDefault

[1]:http://msdn.microsoft.com/en-us/library/bb776336(VS.85).aspx QueryCurrentDefault

#5


Ok. Been away on the conference circuit for a week, now getting back to this. I can do this with C++ now - and it even seems to behave properly! My attempts to translate this into C# (or .NET) have all failed however (Post On Question).

好。在会议电路上离开了一个星期,现在回到这里。我现在可以用C ++做到这一点 - 它甚至表现得很好!我尝试将其转换为C#(或.NET)但都失败了(Post On Question)。

Here is the C++ code for others that stumble on this question:

以下是其他人偶然发现这个问题的C ++代码:

#include "stdafx.h"
#include <iostream>
#include <shobjidl.h>
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS      // some CString constructors will be explicit

#include <atlbase.h>
#include <atlstr.h>
#include <AtlDef.h>
#include <AtlConv.h>

using namespace std;
using namespace ATL;

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr = CoInitialize(NULL);
    if (!SUCCEEDED(hr)) {
        cout << "Failed to init COM instance" << endl;
        cout << hr << endl;
    }

    IApplicationAssociationRegistration *pAAR;
    hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
        NULL, CLSCTX_INPROC, __uuidof(IApplicationAssociationRegistration),
        (void**) &pAAR);
    if (!SUCCEEDED(hr))
    {
        cout << "Failed to create COM object" << endl;
        cout << hr << endl;
        return 0;
    }

    LPWSTR progID;
    //wchar_t *ttype = ".txt";
    hr = pAAR->QueryCurrentDefault (L".txt", AT_FILEEXTENSION, AL_EFFECTIVE, &progID);
    if (!SUCCEEDED(hr)) {
        cout << "Failed to query default for .txt" << endl;
        cout << hr << endl;
    }
    CW2A myprogID (progID);
    cout << "Result is: " << static_cast<const char*>(myprogID) << endl;

    /// Now for http

    hr = pAAR->QueryCurrentDefault (L"http", AT_URLPROTOCOL, AL_EFFECTIVE, &progID);
    if (!SUCCEEDED(hr)) {
        cout << "Failed to query default for http" << endl;
        cout << hr << endl;
    }
    CW2A myprogID1 (progID);
    cout << "Result is: " << static_cast<const char*>(myprogID1) << endl;

    return 0;
}

I will post the C# code when I finally get it working!

当我终于开始工作时,我会发布C#代码!

#6


I've written this code for a project once... it keeps in mind any additional parameters set for the default browser. It was originally created to open HTML documentation in a browser, for the simple reason I always set my default program for HTML to an editor rather than a browser, and it annoys me to no end to see some program open its HTML readme in my text editor. Obviously, it works perfectly for URLs too.

我曾经为项目编写过这个代码......它记住了为默认浏览器设置的任何其他参数。它最初是为了在浏览器中打开HTML文档而创建的,原因很简单,我总是将HTML的默认程序设置为编辑器而不是浏览器,让我感到很恼火,看到某些程序在我的文本中打开HTML自述文件编辑。显然,它也适用于URL。

    /// <summary>
    ///     Opens a local file or url in the default web browser.
    /// </summary>
    /// <param name="path">Path of the local file or url</param>
    public static void openInDefaultBrowser(String pathOrUrl)
    {
        pathOrUrl = "\"" + pathOrUrl.Trim('"') + "\"";
        RegistryKey defBrowserKey = Registry.ClassesRoot.OpenSubKey(@"http\shell\open\command");
        if (defBrowserKey != null && defBrowserKey.ValueCount > 0 && defBrowserKey.GetValue("") != null)
        {
            String defBrowser = (String)defBrowserKey.GetValue("");
            if (defBrowser.Contains("%1"))
            {
                defBrowser = defBrowser.Replace("%1", pathOrUrl);
            }
            else
            {
                defBrowser += " " + pathOrUrl;
            }
            String defBrowserProcess;
            String defBrowserArgs;
            if (defBrowser[0] == '"')
            {
                defBrowserProcess = defBrowser.Substring(0, defBrowser.Substring(1).IndexOf('"') + 2).Trim();
                defBrowserArgs = defBrowser.Substring(defBrowser.Substring(1).IndexOf('"') + 2).TrimStart();
            }
            else
            {
                defBrowserProcess = defBrowser.Substring(0, defBrowser.IndexOf(" ")).Trim();
                defBrowserArgs = defBrowser.Substring(defBrowser.IndexOf(" ")).Trim();
            }
            if (new FileInfo(defBrowserProcess.Trim('"')).Exists)
                Process.Start(defBrowserProcess, defBrowserArgs);
        }
    }

#7


Short answer, you can't.

简短的回答,你不能。

If the default browser is, say, Firefox, and the user already has a Firefox instance running, it will just be opened in another window or tab of the same firefox.exe process, and even after they close your page, the process won't exit until they close every window and tab. In this case, you would receive notification of the process exiting as soon as you started it, due to the temporary firefox.exe proc that would marshal the URL to the current process. (Assuming that's how Firefox's single instance management works).

如果默认浏览器是Firefox,并且用户已经运行了Firefox实例,则只会在同一个firefox.exe进程的另一个窗口或选项卡中打开它,即使它们关闭了您的页面,该过程也会赢得' t退出,直到它们关闭每个窗口和标签。在这种情况下,由于临时firefox.exe proc会将URL封送到当前进程,因此一旦启动它就会收到退出进程的通知。 (假设Firefox的单实例管理工作原理)。

#1


Ok. I now have working C# code to do what I want. This will return the "command line" you should run to load the current default browser:

好。我现在有工作C#代码来做我想要的。这将返回您应该运行的“命令行”以加载当前的默认浏览器:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Linq;
using System.Text;

namespace testDefaultBrowser
{
    public enum ASSOCIATIONLEVEL
    {
        AL_MACHINE,
        AL_EFFECTIVE,
        AL_USER,
    };

    public enum ASSOCIATIONTYPE
    {
        AT_FILEEXTENSION,
        AT_URLPROTOCOL,
        AT_STARTMENUCLIENT,
        AT_MIMETYPE,
    };

    [Guid("4e530b0a-e611-4c77-a3ac-9031d022281b"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IApplicationAssociationRegistration
    {
        void QueryCurrentDefault([In, MarshalAs(UnmanagedType.LPWStr)] string pszQuery,
        [In, MarshalAs(UnmanagedType.I4)] ASSOCIATIONTYPE atQueryType,
        [In, MarshalAs(UnmanagedType.I4)] ASSOCIATIONLEVEL alQueryLevel,
        [Out, MarshalAs(UnmanagedType.LPWStr)] out string ppszAssociation);

        void QueryAppIsDefault(
            [In, MarshalAs(UnmanagedType.LPWStr)] string pszQuery,
            [In] ASSOCIATIONTYPE atQueryType,
            [In] ASSOCIATIONLEVEL alQueryLevel,
            [In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName,
            [Out] out bool pfDefault);

        void QueryAppIsDefaultAll(
            [In] ASSOCIATIONLEVEL alQueryLevel,
            [In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName,
            [Out] out bool pfDefault);

        void SetAppAsDefault(
            [In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName,
            [In, MarshalAs(UnmanagedType.LPWStr)] string pszSet,
            [In] ASSOCIATIONTYPE atSetType);

        void SetAppAsDefaultAll(
            [In, MarshalAs(UnmanagedType.LPWStr)] string pszAppRegistryName);

        void ClearUserAssociations();
    }

    [ComImport, Guid("591209c7-767b-42b2-9fba-44ee4615f2c7")]//
    class ApplicationAssociationRegistration
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
            IApplicationAssociationRegistration reg = 
                (IApplicationAssociationRegistration) new ApplicationAssociationRegistration();

            string progID;
            reg.QueryCurrentDefault(".txt",
                ASSOCIATIONTYPE.AT_FILEEXTENSION,
                ASSOCIATIONLEVEL.AL_EFFECTIVE,
                out progID);
            Console.WriteLine(progID);

            reg.QueryCurrentDefault("http",
                ASSOCIATIONTYPE.AT_URLPROTOCOL,
                ASSOCIATIONLEVEL.AL_EFFECTIVE,
                out progID);
            Console.WriteLine(progID);
        }
    }
}

Whew! Thanks everyone for help in pushing me towards the right answer!

呼!感谢大家帮助我推动正确的答案!

#2


If you pass a path of the known file type to the (file) explorer application, it will 'do the right thing', e.g.

如果将已知文件类型的路径传递给(文件)资源管理器应用程序,它将“做正确的事情”,例如

 Process.Start("explorer.exe", @"\\path.to\filename.pdf");

and open the file in the PDF reader.

并在PDF阅读器中打开该文件。

But if you try the same thing with a URL, e.g.

但是,如果您使用URL尝试相同的操作,例如

Process.Start("explorer.exe", @"http://www.*.com/");

it fires up IE (which isn't the default browser on my machine).

它会激活IE(这不是我机器上的默认浏览器)。

I know doesn't answer the question, but I thought it was an interesting sidenote.

我知道不回答这个问题,但我认为这是一个有趣的旁注。

#3


The way to determine the default browser is explained in this blog post:

此博客文章中介绍了确定默认浏览器的方法:

http://ryanfarley.com/blog/archive/2004/05/16/649.aspx

From the blog post above:

从上面的博客文章:

private string getDefaultBrowser()
{
    string browser = string.Empty;
    RegistryKey key = null;
    try
    {
        key = Registry.ClassesRoot.OpenSubKey(@"HTTP\shell\open\command", false);

        //trim off quotes
        browser = key.GetValue(null).ToString().ToLower().Replace("\"", "");
        if (!browser.EndsWith("exe"))
        {
            //get rid of everything after the ".exe"
            browser = browser.Substring(0, browser.LastIndexOf(".exe")+4);
        }
    }
    finally
    {
        if (key != null) key.Close();
    }
    return browser;
}

#4


Ok, I think I might have found it - IApplicationAssociationRegistration::QueryCurrentDefault [1]. According to the docs this is what is used by ShellExecute. I'll post code when I get it to work, but I'd be interested if others think this is the right thing to use (BTW, I'm Vista or greater for OS level).

好吧,我想我可能已经找到了它 - IApplicationAssociationRegistration :: QueryCurrentDefault [1]。根据文档,这是ShellExecute使用的。当我开始使用代码时我会发布代码,但是如果其他人认为这是正确的用法我会感兴趣(BTW,我是Vista或更高级别的操作系统级别)。

[1]: http://msdn.microsoft.com/en-us/library/bb776336(VS.85).aspx QueryCurrentDefault

[1]:http://msdn.microsoft.com/en-us/library/bb776336(VS.85).aspx QueryCurrentDefault

#5


Ok. Been away on the conference circuit for a week, now getting back to this. I can do this with C++ now - and it even seems to behave properly! My attempts to translate this into C# (or .NET) have all failed however (Post On Question).

好。在会议电路上离开了一个星期,现在回到这里。我现在可以用C ++做到这一点 - 它甚至表现得很好!我尝试将其转换为C#(或.NET)但都失败了(Post On Question)。

Here is the C++ code for others that stumble on this question:

以下是其他人偶然发现这个问题的C ++代码:

#include "stdafx.h"
#include <iostream>
#include <shobjidl.h>
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS      // some CString constructors will be explicit

#include <atlbase.h>
#include <atlstr.h>
#include <AtlDef.h>
#include <AtlConv.h>

using namespace std;
using namespace ATL;

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr = CoInitialize(NULL);
    if (!SUCCEEDED(hr)) {
        cout << "Failed to init COM instance" << endl;
        cout << hr << endl;
    }

    IApplicationAssociationRegistration *pAAR;
    hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
        NULL, CLSCTX_INPROC, __uuidof(IApplicationAssociationRegistration),
        (void**) &pAAR);
    if (!SUCCEEDED(hr))
    {
        cout << "Failed to create COM object" << endl;
        cout << hr << endl;
        return 0;
    }

    LPWSTR progID;
    //wchar_t *ttype = ".txt";
    hr = pAAR->QueryCurrentDefault (L".txt", AT_FILEEXTENSION, AL_EFFECTIVE, &progID);
    if (!SUCCEEDED(hr)) {
        cout << "Failed to query default for .txt" << endl;
        cout << hr << endl;
    }
    CW2A myprogID (progID);
    cout << "Result is: " << static_cast<const char*>(myprogID) << endl;

    /// Now for http

    hr = pAAR->QueryCurrentDefault (L"http", AT_URLPROTOCOL, AL_EFFECTIVE, &progID);
    if (!SUCCEEDED(hr)) {
        cout << "Failed to query default for http" << endl;
        cout << hr << endl;
    }
    CW2A myprogID1 (progID);
    cout << "Result is: " << static_cast<const char*>(myprogID1) << endl;

    return 0;
}

I will post the C# code when I finally get it working!

当我终于开始工作时,我会发布C#代码!

#6


I've written this code for a project once... it keeps in mind any additional parameters set for the default browser. It was originally created to open HTML documentation in a browser, for the simple reason I always set my default program for HTML to an editor rather than a browser, and it annoys me to no end to see some program open its HTML readme in my text editor. Obviously, it works perfectly for URLs too.

我曾经为项目编写过这个代码......它记住了为默认浏览器设置的任何其他参数。它最初是为了在浏览器中打开HTML文档而创建的,原因很简单,我总是将HTML的默认程序设置为编辑器而不是浏览器,让我感到很恼火,看到某些程序在我的文本中打开HTML自述文件编辑。显然,它也适用于URL。

    /// <summary>
    ///     Opens a local file or url in the default web browser.
    /// </summary>
    /// <param name="path">Path of the local file or url</param>
    public static void openInDefaultBrowser(String pathOrUrl)
    {
        pathOrUrl = "\"" + pathOrUrl.Trim('"') + "\"";
        RegistryKey defBrowserKey = Registry.ClassesRoot.OpenSubKey(@"http\shell\open\command");
        if (defBrowserKey != null && defBrowserKey.ValueCount > 0 && defBrowserKey.GetValue("") != null)
        {
            String defBrowser = (String)defBrowserKey.GetValue("");
            if (defBrowser.Contains("%1"))
            {
                defBrowser = defBrowser.Replace("%1", pathOrUrl);
            }
            else
            {
                defBrowser += " " + pathOrUrl;
            }
            String defBrowserProcess;
            String defBrowserArgs;
            if (defBrowser[0] == '"')
            {
                defBrowserProcess = defBrowser.Substring(0, defBrowser.Substring(1).IndexOf('"') + 2).Trim();
                defBrowserArgs = defBrowser.Substring(defBrowser.Substring(1).IndexOf('"') + 2).TrimStart();
            }
            else
            {
                defBrowserProcess = defBrowser.Substring(0, defBrowser.IndexOf(" ")).Trim();
                defBrowserArgs = defBrowser.Substring(defBrowser.IndexOf(" ")).Trim();
            }
            if (new FileInfo(defBrowserProcess.Trim('"')).Exists)
                Process.Start(defBrowserProcess, defBrowserArgs);
        }
    }

#7


Short answer, you can't.

简短的回答,你不能。

If the default browser is, say, Firefox, and the user already has a Firefox instance running, it will just be opened in another window or tab of the same firefox.exe process, and even after they close your page, the process won't exit until they close every window and tab. In this case, you would receive notification of the process exiting as soon as you started it, due to the temporary firefox.exe proc that would marshal the URL to the current process. (Assuming that's how Firefox's single instance management works).

如果默认浏览器是Firefox,并且用户已经运行了Firefox实例,则只会在同一个firefox.exe进程的另一个窗口或选项卡中打开它,即使它们关闭了您的页面,该过程也会赢得' t退出,直到它们关闭每个窗口和标签。在这种情况下,由于临时firefox.exe proc会将URL封送到当前进程,因此一旦启动它就会收到退出进程的通知。 (假设Firefox的单实例管理工作原理)。