[再次修订以获得清晰度]
我有一个与网站互动的C ++程序。该网站是特定于IE的,我的程序也是如此。
我以普通的方式连接到正在运行的IE实例(进程外 - 请参阅代码)。获得IWebBrowser2
后,我可以轻松获取IHTMLDocument2
并与各个IHTMLElement
对象进行互动,填写字段并点击按钮。
但是如果网页上有调用window.showModalDialog的javascript,我就陷入困境:我需要与弹出窗口中的HTML元素进行交互,就像其他页面一样;但我似乎无法得到IWebBrowser2
。
弹出窗口始终标题为“网页对话框”,是一个Internet Explorer_TridentDlgFrame
类型的窗口,其中包含Internet Explorer_Server
。但我无法从Internet Explorer_Server
窗口获取IWebBrowser2,因为它是一个普通的IE实例。
我可以获得IHTMLDocument2Ptr
,但是当我尝试获取IWebBrowser2
时,我得到了HRESULT
E_NOINTERFACE
。
代码是非常标准的东西,如果它是一个'正常'的IE窗口,它可以正常工作
IHTMLDocument2Ptr pDoc;
LRESULT lRes;
/* hWndChild is an instance of class "Internet Explorer_Server" */
UINT nMsg = ::RegisterWindowMessage( "WM_HTML_GETOBJECT" );
::SendMessageTimeout( hWndChild, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000,
(DWORD*)&lRes );
LPFNOBJECTFROMLRESULT pfObjectFromLresult =
(LPFNOBJECTFROMLRESULT)::GetProcAddress( hInst, "ObjectFromLresult" );
if ( pfObjectFromLresult != NULL )
{
HRESULT hr;
hr = (*pfObjectFromLresult)( lRes, IID_IHTMLDocument, 0, (void**)&pDoc );
if ( SUCCEEDED(hr) ) {
IServiceProvider *pService;
hr = pDoc->QueryInterface(IID_IServiceProvider, (void **) &pService);
if ( SUCCEEDED(hr) )
{
hr = pService->QueryService(SID_SWebBrowserApp,
IID_IWebBrowser2, (void **) &pBrowser);
// This is where the problem occurs:
// hr == E_NOINTERFACE
}
}
}
如果重要,则 Vista 和 IE8 。 (我强调这一点,因为这些都引入了我的代码库中的重大变化,这在XP / IE7中运行良好。)
再次,我的目标是让每个IHTMLElement
并与之互动。我无法访问我正在自动化的应用程序的源代码。
我正在考虑盲目地向Internet Explorer_Server
窗口发送击键,但不愿意。
已编辑添加:
有人建议获取子窗口并向他们发送消息,但我很确定这不适用于Internet Explorer_Server
;根据Spy ++,没有任何子窗口。 (这不是特定于IE的.Java小程序似乎也没有子窗口。)
更新
在评论中,Simon Maurer说上面的代码对他有用,并且只是为了确保没有错别字,他非常慷慨地在pastebin上发布了一个完整的独立应用程序。当我使用他的代码时,它在同一个地方以相同的方式失败,我意识到他想要连接到底层页面,而不是弹出窗口。所以我编辑了上面的文字以消除这种歧义。
答案 0 :(得分:4)
如果您只想要IServiceProvider
,我不知道您为什么要获得IWebBrowser2
或IHTMLElement
。您可以通过调用IHTMLDocument
的{{1}}方法来获取它们。
此代码段显示了其工作原理:
get_all()
以上代码同时适用于:普通窗口或模态窗口,只需将正确的#include <Windows.h>
#include <mshtml.h>
#include <Exdisp.h>
#include <atlbase.h>
#include <SHLGUID.h>
#include <oleacc.h>
#include <comdef.h>
#include <tchar.h>
HRESULT EnumElements(HINSTANCE hOleAccInst, HWND child)
{
HRESULT hr;
UINT nMsg = ::RegisterWindowMessage(_T("WM_HTML_GETOBJECT"));
LRESULT lRes = 0;
::SendMessageTimeout(child, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (PDWORD)&lRes);
LPFNOBJECTFROMLRESULT pfObjectFromLresult = (LPFNOBJECTFROMLRESULT)::GetProcAddress(hOleAccInst, "ObjectFromLresult");
if (pfObjectFromLresult == NULL)
return S_FALSE;
CComPtr<IHTMLDocument2> spDoc;
hr = (*pfObjectFromLresult)(lRes, IID_IHTMLDocument2, 0, (void**)&spDoc);
if (FAILED(hr)) return hr;
CComPtr<IHTMLElementCollection> spElementCollection;
hr = spDoc->get_all(&spElementCollection);
if (FAILED(hr)) return hr;
CComBSTR url;
spDoc->get_URL(&url);
printf("URL: %ws\n", url);
long lElementCount;
hr = spElementCollection->get_length(&lElementCount);
if (FAILED(hr)) return hr;
printf("Number of elements: %d", lElementCount);
VARIANT vIndex; vIndex.vt = VT_I4;
VARIANT vSubIndex; vSubIndex.vt = VT_I4; vSubIndex.lVal = 0;
for (vIndex.lVal = 0; vIndex.lVal < lElementCount; vIndex.lVal++)
{
CComPtr<IDispatch> spDispatchElement;
if (FAILED(spElementCollection->item(vIndex, vSubIndex, &spDispatchElement)))
continue;
CComPtr<IHTMLElement> spElement;
if (FAILED(spDispatchElement->QueryInterface(IID_IHTMLElement, (void**)&spElement)))
continue;
CComBSTR tagName;
if (SUCCEEDED(spElement->get_tagName(&tagName)))
{
printf("%ws\n", tagName);
}
}
return S_OK;
}
int _tmain(int argc, _TCHAR* argv[])
{
::CoInitialize(NULL);
HINSTANCE hInst = ::LoadLibrary(_T("OLEACC.DLL"));
if (hInst != NULL)
{
HRESULT hr = EnumElements(hInst, (HWND)0x000F05E4); // Handle to Internet Explorer_Server determined with Spy++ :)
::FreeLibrary(hInst);
}
::CoUninitialize();
return 0;
}
传递给HWND
函数。
警告我在此示例中使用了硬编码的SendMessageTimeout
值,如果要对其进行测试,则应启动IE实例并获取HWND
的{使用Spy ++的{1}}窗口。
我还建议您使用CComPtr
来避免内存泄漏。