调用IHTMLDocument2::get_body导致MFC程序崩溃,求问题排查
问题分析与解决方案
从你提供的崩溃信息和代码来看,最可能的问题是接口IID使用错误,导致获取的IHTMLDocument2指针实际无效,进而调用get_body时触发访问违规。
核心问题:IID不匹配
你的代码中调用ObjectFromLresult时使用了IID_IHTMLDocument,但get_body是IHTMLDocument2接口的专属方法。IID_IHTMLDocument是早期的HTML文档接口,和IHTMLDocument2不属于同一接口体系——哪怕HRESULT返回S_OK,获取到的spDoc指针也无法正确映射到IHTMLDocument2,后续调用成员函数自然会触发空指针类的访问崩溃。
修正后的代码
把IID_IHTMLDocument替换为IID_IHTMLDocument2,同时补充必要的错误检查(这些检查能帮你快速定位其他潜在问题):
HINSTANCE hInst = ::LoadLibrary(_T("OLEACC.DLL")); if (hInst == NULL) { // 处理OLEACC.DLL加载失败 return; } HRESULT coInitHr = CoInitialize(NULL); // 处理COM初始化的常见情况:已经初始化过或者成功 if (coInitHr != S_OK && coInitHr != S_FALSE) { ::FreeLibrary(hInst); return; } if (parentWindow != NULL && childWindow != NULL) { CComPtr<IHTMLDocument2> spDoc; UINT nMsg = ::RegisterWindowMessage(_T("WM_HTML_GETOBJECT")); if (nMsg == 0) { // 注册WM_HTML_GETOBJECT消息失败 goto Cleanup; } DWORD lRes; BOOL sendResult = ::SendMessageTimeout(childWindow, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, &lRes); if (!sendResult) { // 发送消息超时或失败 goto Cleanup; } LPFNOBJECTFROMLRESULT pfObjectFromLresult = (LPFNOBJECTFROMLRESULT)::GetProcAddress(hInst, "ObjectFromLresult"); if (pfObjectFromLresult != NULL) { HRESULT hr = (*pfObjectFromLresult)(lRes, IID_IHTMLDocument2, 0, (void**)&spDoc); if (SUCCEEDED(hr)) { CComPtr<IHTMLElement> pHTMLElement; hr = spDoc->get_body(&pHTMLElement); if (SUCCEEDED(hr)) { BSTR bstrText; hr = pHTMLElement->get_innerText(&bstrText); if (SUCCEEDED(hr)) { edit1->SetWindowTextW(bstrText); SysFreeString(bstrText); // 必须手动释放BSTR避免内存泄漏 } } } } } Cleanup: ::FreeLibrary(hInst); // 只有当当前线程是COM初始化的发起者时,才执行Uninitialize if (coInitHr == S_OK) { CoUninitialize(); }
其他需要检查的点
- 窗口句柄有效性:确保
childWindow确实是WebBrowser控件(或IE内核的HTML窗口)的句柄,你可以用Spy++工具验证窗口类名是否为Internet Explorer_Server。如果句柄对应非HTML窗口,自然无法获取有效文档对象。 - COM线程模型:MFC程序默认使用单线程单元(STA),
CoInitialize(NULL)符合这个模型,但如果你的代码在多线程环境下执行,需要确保线程的COM初始化状态正确。 - 权限限制:如果目标窗口属于高权限进程(比如管理员权限),当前进程权限不足时,
SendMessageTimeout可能无法成功获取文档对象,需要考虑权限提升或其他跨进程通信方案。
调试建议
如果修正IID后仍然崩溃,可以在调用get_body前添加断言:
ATLASSERT(spDoc != NULL); ATLASSERT(spDoc.p != NULL);
这能帮你确认spDoc的内部指针是否有效。如果断言失败,说明ObjectFromLresult返回的指针本身就有问题,需要重新检查childWindow的有效性或SendMessageTimeout的返回结果。
内容的提问来源于stack exchange,提问作者nizzard0094
相关产品推荐
相关产品推荐

