3

更新:答案:需要两行正常的代码。谢谢诺塞拉蒂奥!

我在键盘上敲了几个小时,比我想在托管的浏览器控制应用程序中模拟 IE 的 Ctrl+N 行为还要多。不幸的是,由于我从下面的代码示例中抽象出来的复杂性,我不能让 IE 自己做 Ctlr+N。所以我必须手动完成。

请记住,我正在运行托管浏览器。因此,通常情况下,在新窗口中打开链接实际上会在我的应用程序中的一个新“选项卡”中打开它(它实际上不是一个选项卡,而是另一个窗口......但从外观上看,它是一个选项卡)。然而,Ctrl+N 是不同的——在这里,一个成熟的 IE 窗口会在按下时启动。

我认为我的问题在于提出问题 - 诚然,我是 WebBrowser 控件的新手,我发现它很讨厌。无论如何,过去一天我已经在 Internet 上进行了搜索,但无法提出一个优雅的解决方案。

基本上,理想的解决方案是在 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();

我很难相信我必须求助于这样的骇客来获得像当用户按下 Ctrl+N 时打开一个新窗口这样简单的东西。

请stackoverflow,请指出我忽略的显而易见的事情。

4

1 回答 1

3

IE 中的 Ctrl-N 在同一会话中启动一个新窗口。在您的情况下,window.open或者webBrowser.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);
    }
}
于 2013-10-15T06:47:25.470 回答