只在多线程中执行出错(MSHTML.DLL):0xC0000005:Access Violation

hk572 2012-10-21 02:37:54
程序获取webbrowser IHTMLDocument2 获取页面信息。

但是只要放在多线程中执行就出出错 程序直接内存出错退出。
调试 发现错误在 spInputElement.GetPropertyByName( L"name", &vName ); 这句 进入里面单步执行 出现(MSHTML.DLL):0xC0000005:Access Violation。

而在主线程中执行就没错。

请问什么原因呢

代码如下:
void MyFunc(LPVOID pDlg)
{
CTestDlg* pDlg2=(CTestDlg*)pDlg;
IHTMLDocument2*pHTMLDocument2=(IHTMLDocument2*)(pDlg2->m_brow.GetDocument());
HRESULT hr;
USES_CONVERSION;
CComQIPtr< IHTMLElementCollection > spElementCollection;
hr = pHTMLDocument2->get_forms( &spElementCollection ); //取得表单集合
if ( FAILED( hr ) )
{

return;
}
long nFormCount=0; //取得表单数目
hr = spElementCollection->get_length( &nFormCount );
if ( FAILED( hr ) )
{
return;
}
CComQIPtr< IHTMLFormElement > spFormElement;
for(long i=0; i<nFormCount; i++)
{
IDispatch *pDisp = NULL; //取得第 i 项表单
hr = spElementCollection->item( CComVariant( i ), CComVariant(), &pDisp );
if ( FAILED( hr ) ) continue;
spFormElement = pDisp;
pDisp->Release();
long nElemCount=0; //取得表单中 域 的数目
hr = spFormElement->get_length( &nElemCount );
if ( FAILED( hr ) ) continue;
for(long j=0; j<nElemCount; j++)
{
CComDispatchDriver spInputElement; //取得第 j 项表单域
hr = spFormElement->item( CComVariant( j ), CComVariant(), &spInputElement );
if ( FAILED( hr ) ) continue;

CComVariant vName,vVal,vType; //取得表单域的 名,值,类型
hr = spInputElement.GetPropertyByName( L"name", &vName ); /***执行此举程序内存错误***/
if( FAILED( hr ) ) continue;
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////
void CTestDlg::OnButtonStart()
{
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)MyFunc,this,0,&ThreadID); //内存泄露
MyFunc(this); //没问题
}
...全文
432 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
hk572 2012-10-21
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 的回复:]

CComGITPtr<IHTMLDocument2*> pgit改成CComGITPtr<IHTMLDocument2> pgit
[/Quote]

解决了 ,谢谢
I_ask_who 2012-10-21
  • 打赏
  • 举报
回复
CComGITPtr<IHTMLDocument2*> pgit改成CComGITPtr<IHTMLDocument2> pgit
hk572 2012-10-21
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 的回复:]
原因是COM的对象都生活在套间里,拥有线程相关性。就是你的gui主线程如果要共享一个COM接口,比如说是IHTMLDocument2*,不可以直接就把指针copy或者共享给子线程,需要向系统一个服务注册一个cookie,DWORD格式,然后子线程询问系统把这个cookie还原成接口指针。


C/C++ code


//比如你的主线程:
IHTMLDocument2*pHTMLDo……
[/Quote]

刚才在别人机器上用 vs2010编译一下 出现
error C2664: “ATL::CComGITPtr<T>::CComGITPtr(T *)”: 不能将参数 1 从“IHTMLDocument2 *”转换为“IHTMLDocument2 **”
1> with
1> [
1> T=IHTMLDocument2 *
1> ]
1> 与指向的类型无关;转换要求 reinterpret_cast、C 样式转换或函数样式转换


I_ask_who 2012-10-21
  • 打赏
  • 举报
回复
麻烦了,vc6没有CComGITPtr,atl7开始的功能,你只有使用原生态了:
参考书上(Inside ATL),把IMyInterface改成你要的接口即可,g_pGIT可以线程共享:

//注册并且获得cookie
HRESULT RegisterMyInterface(IMyInterface* pmi, DWORD* pdwCookie) {
// this is usually a global
IGlobalInterfaceTable* g_pGIT = NULL;
HRESULT hr = ::CoCreateInstance(CLSID_StdGlobalInterfaceTable,
NULL,
CLSCTX_INPROC_SERVER,
IID_IGlobalInterfaceTable,
(void**)&g_pGIT);
ATLASSERT(SUCCEEDED(hr));
hr = g_pGIT->RegisterInterfaceInGlobal(pmi,
__uuidof(pmi), pdwCookie);
return hr;
}
//通过cookie取得接口
HRESULT ReadMyInterface(DWORD dwCookie) {
// ... GIT pointer obtained elsewhere
IMyInterface* pmi = NULL;
hr = g_pGIT->GetInterfaceFromGlobal(dwCookie,
__uuidof(pmi), (void**)&pmi);
// use pmi as usual
return hr;
}
//注销cookie
g_pGIT->RevokeInterfaceFromGlobal(m_dwCookie);


hk572 2012-10-21
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 的回复:]

原因是COM的对象都生活在套间里,拥有线程相关性。就是你的gui主线程如果要共享一个COM接口,比如说是IHTMLDocument2*,不可以直接就把指针copy或者共享给子线程,需要向系统一个服务注册一个cookie,DWORD格式,然后子线程询问系统把这个cookie还原成接口指针。

C/C++ code


//比如你的主线程:
IHTMLDocument2*pHTMLDocume……
[/Quote]

请问CComGITPtr在哪里定义的 我用的VC6没有CComGITPtr
I_ask_who 2012-10-21
  • 打赏
  • 举报
回复
原因是COM的对象都生活在套间里,拥有线程相关性。就是你的gui主线程如果要共享一个COM接口,比如说是IHTMLDocument2*,不可以直接就把指针copy或者共享给子线程,需要向系统一个服务注册一个cookie,DWORD格式,然后子线程询问系统把这个cookie还原成接口指针。



//比如你的主线程:
IHTMLDocument2*pHTMLDocument2=(IHTMLDocument2*)(pDlg2->m_brow.GetDocument());
CComGITPtr<IHTMLDocument2*> pgit(pHTMLDocument2);
DWORD dwCookie=pgit.Detach();
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)MyFunc,dwCookie,0,&ThreadID); //内存泄露
//MyFunc(this); //没问题



//你的子线程:
void MyFunc(DWORD cookie){
OleInitialize(NULL);
CComGITPtr<IHTMLDocument2> pgit(cookie);
CComPtr<IHTMLDocument2> pHTMLDocument2;
if(FAILED(pgit.CopyTo(&pHTMLDocument2))){
AtlTrace(_T("Failed in GIT to PTR"));
CoUninitialize();
return;
}
pgit.Revoke();
//你的代码
...
CoUninitialize();
}


hk572 2012-10-21
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 的回复:]

发现你的线程居然没有OleInitialize()语句,而且跨线程复用COM指针是不允许的,必需要CComGITPtr来帮忙。
[/Quote]

网上这方便的资料太少了,能具体说下如何做吗
I_ask_who 2012-10-21
  • 打赏
  • 举报
回复
发现你的线程居然没有OleInitialize()语句,而且跨线程复用COM指针是不允许的,必需要CComGITPtr来帮忙。
hk572 2012-10-21
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 的回复:]
spFormElement->item( CComVariant( j ), CComVariant(), &spInputElement );
可能有错,你虽然SUCCESS(hr)了,还需检查spInputElement是否为空
而且你的调用和手册上好像有误差,param2才是index
[/Quote]
spInputElement不为空。代码应该没问题。
是不是多线程和主线程 调用函数上有什么不同呢。
I_ask_who 2012-10-21
  • 打赏
  • 举报
回复
spFormElement->item( CComVariant( j ), CComVariant(), &spInputElement );
可能有错,你虽然SUCCESS(hr)了,还需检查spInputElement是否为空
而且你的调用和手册上好像有误差,param2才是index
mzmbird 2012-10-21
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 的回复:]
应该需要用委托吧
[/Quote]
对委托不了解,能说下用委托为什么能解决问题的原理吗
yxwsbobo 2012-10-21
  • 打赏
  • 举报
回复
应该需要用委托吧

3,055

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC HTML/XML
社区管理员
  • HTML/XML社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧