更新: 答:需要两行正常的代码。谢谢鼻子比!
我在键盘上敲了几个小时,而不是试图在我的托管浏览器控制应用程序中模拟 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();
我发现很难相信我必须求助于这样的黑客行为才能获得像用户按 Ctrl+N 时打开新窗口这样简单的东西。
请堆叠溢出,请指出我忽略的明显的事情。
中的Ctrl-N在同一会话上启动一个新窗口。在您的情况下,window.open
或webBrowser.Navigate2
将在新会话上创建一个窗口,因为它将由与您的应用程序分开iexplore.exe
进程运行。会话按进程共享,这就是基础UrlMon
库的工作方式。因此,您将丢失新窗口的所有 cookie 和身份验证缓存。另一方面,当您创建一个在您自己的应用进程中托管WebBrowser
控制权的新窗口时,您将保留会话。
如果此类行为可以满足您的需求,请先尝试初始Navigate2
方法,并在该方法之前进行 AllowSetForegroundWindow(ASFW_ANY) 调用。如果新窗口仍未正确接收焦点,可以尝试创建进程外 COM 对象的实例InternetExplorer.Application
并使用相同的 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);
}
}