我正在研究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并等到它得到它.MainWindowHandle(它是显示Internet Explorer内部内容的“窗口”的句柄)。一旦它抓住这个窗口句柄,它就会获得所有在用户桌面上启动并运行的IWebBrowser2窗口的列表,并尝试将该过程的.MainWindowHandle与一个(如果有的话)窗口句柄相匹配。 IWebBrowser2集合。
这种方法最重要的问题是IWebBrowser2.HWND属性(需要与.MainWindowHandle进行比较)可能非常有问题,不稳定和气质,因为它每隔一次抛出InvalidCastException你试图访问它(至少在我正在运行测试的机器上)。然后又是这种操作的开销。
对于那些可能更熟悉我在Windows编程中的人来说,这是我的问题:因为HWNDs无论如何都要匹配为什么我们不使用.MainWindowHandle值来直接检索所需的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。)
这对我很有用。我看不出HWND之间是否有任何冲突或不匹配 - 如果有一个我可能错过的角落案例,请不要。我很想在WatiN论坛上询问这个问题,但我想先在程序员中心问一下,以防我错过了一些明显的东西。
提前谢谢大家。任何提示赞赏。
干杯, 多米尼克
答案 0 :(得分:1)
我开始深入研究Internet Explorer的内部窗口结构,并提出了以下层次结构(不相关的窗口被忽略了):
IEFrame
|
- TabWindowClass-1 --convert - > FirstIWebBrowser2
|
- TabWindowClass-2 --convert - > SecondIWebBrowser2
|
...
|
- TabWindowClass-Nth --convert - >第N-的IWebBrowser2
通过测试,我在Windows7 + IE9(9.0.8112.16421)中得出以下结论
有趣的是(并且反直觉地)IWebBrowser2.HWND与它来自的TabWindowClass的HWND完全相同。
IEFrame-> HWND与同一Internet Explorer进程中的任何IWebBrowser2.HWND属性相同。即使我们在同一个Internet Explorer进程中打开了多个选项卡,也是如此。
Internet Explorer进程的Process.MainWindowHandle属性(当我们以编程方式启动Internet Explorer时)与IEFrame-> HWND相同,因此也与IWebBrowser2对象的相同。
可以立即使用IEFrame的HWND检索活动标签(使用原始帖子中概述的方法)。
我最好的教育猜测为什么上面的hwnd-layout成立:
无论是在Redmond的人们都在IEFrame和IWebBrowser2之间做了这样的接线,因为实际上一次只有一个活动的标签窗口(而用户被赋予了X个标签的错觉)。或者......
因为需要保持与以前版本IE的预先存在的代码的向后兼容性,并且该代码使用IEFrame的HWND来获取它的IWebBrowser2对象。
在任何一种情况下,我都觉得在从IWebBrowser2接口访问它时会导致HWND接线的内部错误导致InvalidCastException。
任何对此问题有更深入了解的人都可以随意放弃一两行。希望这些帮助。
干杯, 多米尼克