读书人

兄弟帮忙看一段获取IHTMLDocument2接口

发布时间: 2012-01-31 21:28:41 作者: rapoo

兄弟帮忙看一段获取IHTMLDocument2接口的代码,MSDN上面找的
原本是想获取INTERNET EXPLORER_SERVER里面的文本,用了下面这个函数,获取接口正常,但一使用接口就出错。后来我直接尝试获取接口的原函数,还是不行,执行到spDoc->put_bgColor( CComVariant("red") );这一行就死掉了,麻烦高手帮我看一下,下面是我在MSDN上面找到的代码,一起贴上来,

还有说一下我用的是WIN7+VS2008,IE的窗口句柄是直接用Spy++得到的,

BOOL CALLBACK MyEnumChildProc(HWND hwnd,LPARAM lParam)
{
TCHAR buf[100];

::GetClassName( hwnd, (LPTSTR)&buf, 100 );
if ( _tcscmp( buf, _T("Internet Explorer_Server") ) == 0 )
{
*(HWND*)lParam = hwnd;
return FALSE;
}
else
return TRUE;
}

void OnGetDocInterface(HWND hWnd)
{
CoInitialize( NULL );
// Explicitly load MSAA so we know if it's installed
HINSTANCE hInst = ::LoadLibrary( _T("OLEACC.DLL") );
if ( hInst != NULL )
{
if ( hWnd != NULL )
{
HWND hWndChild=NULL;
// Get 1st document window
::EnumChildWindows( hWnd, MyEnumChildProc, (LPARAM)&hWndChild );
if ( hWndChild )
{
CComPtr<IHTMLDocument2> spDoc;
LRESULT lRes;

UINT nMsg = ::RegisterWindowMessage( _T("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**)&spDoc );
if ( SUCCEEDED(hr))
{
// Change background color to red
spDoc->put_bgColor( CComVariant("red") );
}
}
} // else document not ready
} // else Internet Explorer is not running
::FreeLibrary( hInst );
} // else Active Accessibility is not installed
CoUninitialize();
}

[解决办法]
ObjectFromLresult 仅限项目内部使用,MSDN的解释:Neither clients nor servers should call this function.

实际上这个API本来不应该公开,它只能用于当前线程中创建的组件,跨线程不能用,跨进程更不用说。
[解决办法]
事实上,这种方法是可以用的,获取【其他进程】的doc进行操作没有任何问题!

我曾经用这种方法写过一个自动玩qq网页游戏怪物对对碰的辅助工具(使用IE打开qq怪物对对碰游戏,然后用工具选中该IE窗口,就可以自动玩)

C/C++ code
HRESULT CMyQQGameToolDlg::GetIHTMLDocument2FromHWND(IHTMLDocument2 ** pDoc2){    LRESULT lRes;    UINT nMsg = ::RegisterWindowMessage( _T("WM_HTML_GETOBJECT") );    ::SendMessageTimeout(m_hIE, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (DWORD*)&lRes );    LPFNOBJECTFROMLRESULT pfObjectFromLresult = (LPFNOBJECTFROMLRESULT)::GetProcAddress( m_hInstMSAA, "ObjectFromLresult");    if (NULL == pfObjectFromLresult)    {        ...    }    HRESULT hr;    hr = (*pfObjectFromLresult)( lRes, IID_IHTMLDocument, 0, (void**)pDoc2 );    if (FAILED(hr))    {        ...    }    return S_OK;}调用处的代码     // GET IE hwnd     ...     CComPtr<IHTMLDocument2> spDoc2;     HRESULT hr = E_FAIL;     hr = GetIHTMLDocument2FromHWND(&spDoc2);     /////////////////////////////////////////////////////     // 直接操作spDoc2会有异常!      // 再QI一次获取另外的接口就可以操作了      //      CComQIPtr<IHTMLDocument3> spDoc3 = spDoc2;    hr = spDoc3->getElementById(... 


[解决办法]
转一篇文章,希望对你有用
不要在线程之间传递原始接口指针我咨询的首批COM项目之一就涉及到一个包含100,000行代码的分布式应用程序,该程序是由美国西海岸的一个大型软件公司编写的。该应用程序在多个机器上创建了数十个COM对象,并从客户端进程启动的背景线程中调用这些对象。开发小组遇到问题了,调用要么消失得无影无踪,要么在没有明显原因的情况下失败。他们给我演示的最惊人的症状是:当一个调用无法返回时,在同一台机器上启动其他支持COM的应用程序(包括MicrosoftPaint等)会频繁导致这些应用程序被锁定。检查他们的代码后发现,他们违反了COM并发的一个基本规则,就是说,如果一个线程要与另一个线程共享一个接口指针,它应首先封送该接口指针。如果有必要,封送接口指针可使COM创建一个新的代理(以及一个新的信道对象,将代理和存根结对),以允许从另一个单元向外调用。不通过封送而将原始接口指针(内存中的一个32位地址)传递给另一个线程,会绕过COM的并发机制,并且如果发送和接收的线程位于不同的单元中,将出现各种不良行为。(在Windows2000中,由于两个对象可以共享一个单元,但又位于不同的上下文中,因此如果线程位于同一个单元中,可能会使您陷入困境。)典型的症状包括调用失败和返回RPC_E_WRONG_THREAD_ERROR。WindowsNT4.0和更高版本可以使用一对名为CoMarshalInterThreadInte**ceInStream和CoGetInte**ceAndReleaseStream的API函数,在线程之间轻松地封送接口指针。假定您应用程序中的一个线程(线程A)创建了一个COM对象,继而接收了一个IFoo接口指针,并且同一进程中的另一个线程(线程B)想调用这个对象。在准备将接口指针传递给线程B时,线程A应该封送该接口指针,如下所示:CoMarshalInterThreadInte**ceInStream(IID_IFoo,pFoo,&pStream);在CoMarshalInterThreadInte**ceInStream返回后,线程B就可以安全地取消封送该接口指针:IFoo*pFoo;CoGetInte**ceAndReleaseStream(pStream,IID_IFoo,(void**)&pFoo);在这些示例中,pFoo是一个IFoo接口指针,pStream是一个IStream接口指针。COM在调用CoMarshalInterThreadInte**ceInStream时初始化IStream接口指针,然后在CoGetInte**ceAndReleaseStream内部使用和释放该接口指针。实际上,您通常要使用一个事件或其他同步化基元来协调这两个线程的行为—例如,让线程B知道接口指针已准备好,可以取消封送。请注意,以这种方式封送接口指针不会出现任何问题,因为COM有足够的智能,在不需要进行封送时不会去封送(或重新封送)指针。如果在线程之间传递接口指针时这样做,使用COM就轻松多了。如果调用CoMarshalInterThreadInte**ceInStream和CoGetInte**ceAndReleaseStream看起来太麻烦,您还可以通过将接口指针放在全局接口表(GIT)中,并让其他线程去那里检索它们,从而实现在线程之间传递接口指针。从GIT中检索到的接口指针在被检索时会自动封送。更多信息,请参阅IGlobalInte**ceTable中的文档。

读书人网 >VC/MFC

热点推荐