我正在研究下面一段来自WatiN的代码,它处理启动和附加到Internet Explorer:
private static IEBrowser CreateIEPartiallyInitializedInNewProcess(Uri uri)
{
var m_Proc = CreateIExploreInNewProcess(uri);
var helper = new AttachToIeHelper();
var action = new TryFuncUntilTimeOut(TimeSpan.FromSeconds(Settings.AttachToBrowserTimeOut))
{
SleepTime = TimeSpan.FromMilliseconds(500)
};
var ie = action.Try(() =>
{
m_Proc.Refresh();
var mainWindowHandle = m_Proc.MainWindowHandle;
// return mainWindowHandle != IntPtr.Zero ? GetIWebBrowser2Directly(mainWindowHandle) : null;
return mainWindowHandle != IntPtr.Zero
? helper.FindIEPartiallyInitialized(new AttributeConstraint("hwnd", mainWindowHandle.ToString()))
: null;
});
if (ie != null) return ie._ieBrowser;
// if (ie != null) return new IEBrowser(ie);
throw new BrowserNotFoundException("IE", "Timeout while waiting to attach to newly created instance of IE.", Settings.AttachToBrowserTimeOut);
}
WatiN所做的是启动Internet Explorer并等待它获得。mainwindowwhandle(这是在Internet Explorer中显示内容的"窗口"的句柄)。一旦它获得了这个窗口句柄,它就会获得一个用户桌面上正在运行的所有IWebBrowser2窗口的列表,并尝试用IWebBrowser2集合中的一个(如果有的话)窗口句柄来匹配进程的. mainwindowwhandle。
这种方法最重要的问题是IWebBrowser2。HWND属性(需要与。mainwindowwhandle进行比较)可以非常有问题,不稳定和喜怒无常,因为当您试图访问它时,它每隔一次都会抛出InvalidCastException(至少在我正在运行测试的机器上)。这样的操作也有开销。
这是我对任何可能更了解我在windows编程的人的问题:既然hwnd无论如何都会匹配,为什么我们不使用。mainwindowwhandle值来检索所需的IWebBrowser2马上(见上面的注释代码)通过使用以下方法(灵感来自代码WatiN本身在ShellWindow2.cs中使用):
private static IWebBrowser2 GetIWebBrowser2Directly(IntPtr embeddedWebBrowserWindowHandle)
{
IHTMLDocument2 document2 = UtilityClass.TryFuncIgnoreException(() => IEUtils.IEDOMFromhWnd(embeddedWebBrowserWindowHandle));
if (document2 == null) return null;
IHTMLWindow2 parentWindow = UtilityClass.TryFuncIgnoreException(() => document2.parentWindow);
if (parentWindow == null) return null;
return UtilityClass.TryFuncIgnoreException(() => ShellWindows2.RetrieveIWebBrowser2FromIHtmlWindw2Instance(parentWindow));
}
(作为旁注,我们甚至可以创建一个代理对象,在我的另一篇文章中描述,来缓存窗口句柄,以避免请求IWebBrowser2。
这对我来说很好。我看不到hwnd之间的任何冲突或不匹配-不知道是否有一个角落的情况,我可能会错过。我很想在WatiN论坛上问这个问题,但我想先在程序员中心问一下,以防我遗漏了一些明显的东西。
提前感谢大家。任何提示都很感谢。
欢呼,多明尼克
我开始深入研究Internet Explorer的内部窗口结构,并提出了以下层次结构(当然,不相关的窗口是允许的):
IEFrame
|
——TabWindowClass-1——convert-> FirstIWebBrowser2
|
——TabWindowClass-2——convert-> SecondIWebBrowser2
|
…|
——TabWindowClass-Nth——convert——> Nth-IWebBrowser2
通过测试,我在Windows7 + IE9(9.0.8112.16421)中得出了以下发现
-
有趣的是(和反直觉)IWebBrowser2。
-
IEFrame->HWND与IWebBrowser2的ANY相同。HWND属性在同一internet explorer进程中。即使我们在同一个ie进程中打开多个选项卡也是如此。
-
。internet explorer进程的mainwindowandle属性(当我们以编程方式启动internet explorer时)与IEFrame->HWND相同,因此也与IWebBrowser2对象相同。
-
活动的选项卡可以通过使用IEFrame的HWND立即检索(使用我在原始帖子中概述的方法)。
关于为什么上述hwnd-layout成立,我最有根据的猜测是:
-
Redmond的工作人员在IEFrame和IWebBrowser2之间进行了这种hwrd连接,因为实际上一次只有一个活动选项卡窗口(而用户被给予X个选项卡的错觉)。还是…
-
因为需要保持与先前版本的IE已有代码的向后兼容性,并且该代码正在使用IEFrame的HWND来获取它的IWebBrowser2对象。
在任何一种情况下,我觉得可能有一个内部错误在实现这个HWND布线时,它涉及到从IWebBrowser2接口访问它,导致InvalidCastException。
任何对这个问题有更多见解的人都可以随意写一两行。希望这些对你有帮助。
欢呼,多明尼克