MFC WebBrowser控件:模拟Ctrl + N需要多少(正常)代码行?

时间:2013-10-14 19:56:26

标签: c++ internet-explorer mfc webbrowser-control hosted-app

更新:答案:需要两行正常的代码。谢谢Noseratio!

我把头撞在键盘上的时间超过了我在托管的浏览器控制应用程序中试图模拟IE的Ctrl + N行为。不幸的是,由于我从下面的代码示例中抽象出来的复杂性,我不能让IE自己做Ctlr + N.所以我必须手动完成。

请记住,我正在运行托管浏览器。所以通常情况下,在新窗口中打开链接会在我的应用程序中的新“标签”内打开它(它不是一个标签,而是另一个窗口......但外观方面它是一个标签)。但是,Ctrl + N是不同的 - 在这里,预计在按下时会启动一个完全成熟的IE窗口。

我认为我的问题在于解决问题 - 不可否认,我是WebBrowser控件的新手,我发现这很令人讨厌。无论如何,我在过去的一天里搜索过互联网,但无法提出一个优雅的解决方案。

基本上,理想的解决方案是在WebBrowser控件或其附属库中调用“NewWindow”函数;但是,我能够找到* On * NewWindow方法,它们是事件处理程序,而不是事件信号器。据我所知,大多数时候,用户将创建事件......但是程序化模拟呢?

我尝试了一种SENDMESSAGE方法,在那里我可以使用OnNewWindow事件使用的ID ......最终只有崩溃。也许我可以回去让它发挥作用,但我想确认这种方法甚至值得我花时间。

接下来的方法,其中应该是最强的,但遗憾的是没有成功,如下所示:

Navigate2(GetLocationURL().GetBuffer(), BrowserNavConstants::navOpenInNewWindow);

如果不是因为新窗口会在后台打开,在任务栏中闪烁,那将会非常有效。需要点击才能把它带到前面。

我试图以无数种方式绕过限制,包括获取当前上下文的调度程序,然后使用该IDispatch对象调用OnNewWindow2。然后我将在调度对象上为IWebBrowser控件调用QueryInterface。然后webBrowser控件(可能在新窗口的控制下)可以导航到原始上下文的页面。但是......这也是一个非常混乱的解决方案,最终会导致崩溃。

最后,我使用手动调用JavaScript来获得所需的行为。真??对于我的问题,是否真的没有比下面的代码更优雅的解决方案?

        if ((pMsg->wParam == 'N') && (GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_SHIFT) & 0x8000) && !(GetKeyState(VK_MENU) & 0x8000))
        {
            LPDISPATCH pDisp = CHtmlView::GetHtmlDocument();
            IHTMLDocument2 *pDoc;
            if (SUCCEEDED(pDisp->QueryInterface(IID_IHTMLDocument2, (void **)&pDoc))) 
            {
                IHTMLWindow2* pWnd;
                pDoc->get_parentWindow(&pWnd);
                BSTR bStrLang = ::SysAllocString(L"JavaScript");
                CString sCode(L"window.open(\"");
                sCode.Append(GetLocationURL().GetBuffer());
                sCode.Append(L"\");");
                BSTR bStrCode = sCode.AllocSysString();
                COleVariant retVal;
                pWnd->execScript(bStrCode, bStrLang, retVal);
                ::SysFreeString(bStrLang);
                ::SysFreeString(bStrCode);
                pDoc->Release();
            }
            pDisp->Release();

我发现很难相信我必须求助于这样的hackery,以便在用户按下Ctrl + N时打开一个新窗口这么简单。

请堆叠溢出,请指出我忽略的明显的事情。

1 个答案:

答案 0 :(得分:3)

IE中的Ctrl-N在同一会话中启动一个新窗口。在您的情况下,window.openwebBrowser.Navigate2会在新会话上创建一个窗口,因为它将由iexplore.exe进程运行,该进程与您的应用分开。会话是按进程共享的,这是底层UrlMon库的工作方式。因此,您将丢失新窗口的所有cookie和身份验证缓存。另一方面,当您在自己的应用程序流程中创建一个托管WebBrowser控件的新窗口时,您将保留会话。

如果此类行为符合您的需求,请先尝试使用Navigate2方法,然后再进行AllowSetForegroundWindow(ASFW_ANY)调用。如果新窗口仍未正确接收焦点,您可以尝试创建InternetExplorer.Application进程外COM对象的实例,并使用相同的IWebBrowser2接口自动执行它。下面是一个简单的C#应用​​程序,对我来说工作正常,新窗口正确地被带到前台,没有焦点问题。用MFC做同样的事情应该不是问题。

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace IeApp
{
    public partial class MainForm : Form
    {
        // get the underlying WebBrowser ActiveX object;
        // this code depends on SHDocVw.dll COM interop assembly,
        // generate SHDocVw.dll: "tlbimp.exe ieframe.dll",
        // and add as a reference to the project

        public MainForm()
        {
            InitializeComponent();
        }

        private void NewWindow_Click(object sender, EventArgs e)
        {
            AllowSetForegroundWindow(ASFW_ANY);
            // could do: var ie =  new SHDocVw.InternetExplorer()
            var ie = (SHDocVw.InternetExplorer)Activator.CreateInstance(Type.GetTypeFromProgID("InternetExplorer.Application"));
            ie.Visible = true;
            ie.Navigate("http://www.example.com");
        }

        const int ASFW_ANY = -1;
        [DllImport("user32.dll")]
        static extern bool AllowSetForegroundWindow(int dwProcessId);
    }
}