CDHtmlDialog::DisconnectDHtmlElementEvents的怪异问题导致内存访问错误

13妖 2014-08-05 05:35:16
今天在用CDHtmlDialog的时候遇到一个怪异问题,当Navigate打开一个本地htm文件时报内存访问冲突,至今不知道怎么解决,或者说不知道问题出在哪里。
工程中有2个类使用到,其中A类是正常的,而B类100%出现这个问题。

debug下报错位置:
void CDHtmlDialog::DisconnectDHtmlElementEvents()
{
const DHtmlEventMapEntry* pEventMap = GetDHtmlEventMap(); //此处报错,内存访问冲突

if (!pEventMap)
return;

int i;

// disconnect from element events
for (i=0; i<m_SinkedElements.GetSize(); i++)
{
CDHtmlElementEventSink *pSink = m_SinkedElements[i];
AtlUnadvise(pSink->m_spunkElem, __uuidof(IDispatch), pSink->m_dwCookie);
delete pSink;
}
m_SinkedElements.RemoveAll();

// disconnect from control events
for (i=0; i<m_ControlSinks.GetSize(); i++)
{
DisconnectFromConnectionPoint(m_ControlSinks[i]->m_spunkObj,
m_ControlSinks[i]->m_iid, m_ControlSinks[i]->m_dwCookie);
delete m_ControlSinks[i];
}
m_ControlSinks.RemoveAll();
return;
}


查看this指针,发现此时this已经被删了!~~~oxfreefeee之类的,hwnd都是0x00000005什么的了。

因CDHtmlDialog是COM,所以调用堆栈前部分都不可见,且是消息触发,跟玩完。
查看调用堆栈:
1、CDHtmlDialog::_OnBeforeNavigate2()
2、CDHtmlDialog::OnBeforeNavigate()
3、CDHtmlDialog::DisconnectDHtmlEvents()
4、CDHtmlEventSink::DisconnectFromConnectionPoint()
5、CDHtmlDialog::DisconnectDHtmlElementEvents()

在CDHtmlDialog::_OnBeforeNavigate2()加断点,证明了A类、B类进入次数、数据的格式是一样的(格式一样,其实是废话,内存地址、Navigate的url当然不一样)

此处贴出CDHtmlDialog::DisconnectDHtmlEvents()
void CDHtmlDialog::DisconnectDHtmlEvents()
{
CComPtr<IHTMLDocument2> sphtmlDoc;
GetDHtmlDocument(&sphtmlDoc);

if (sphtmlDoc == NULL)
return;
//先调用DisconnectFromConnectionPoint
DisconnectFromConnectionPoint(sphtmlDoc, __uuidof(HTMLDocumentEvents), m_dwDHtmlEventSinkCookie);
//再调用DisconnectDHtmlElementEvents
DisconnectDHtmlElementEvents();
}


跟踪到DisconnectFromConnectionPoint,执行前,查看this指针数据正常,执行后this指针被删除了!想必元凶在此,跟进去。
void CDHtmlEventSink::DisconnectFromConnectionPoint(IUnknown *punkObj, REFIID riid, DWORD& dwCookie)
{
AtlUnadvise(punkObj, riid, dwCookie);
}
//再跟进去
ATLINLINE ATLAPI AtlUnadvise(
_Inout_ IUnknown* pUnkCP,
_In_ const IID& iid,
_In_ DWORD dw)
{
if(pUnkCP == NULL)
return E_INVALIDARG;

CComPtr<IConnectionPointContainer> pCPC;
CComPtr<IConnectionPoint> pCP;
HRESULT hRes = pUnkCP->QueryInterface(__uuidof(IConnectionPointContainer), (void**)&pCPC);
if (SUCCEEDED(hRes))
hRes = pCPC->FindConnectionPoint(iid, &pCP);
if (SUCCEEDED(hRes))
hRes = pCP->Unadvise(dw);//到此为止了,vs进不去了
return hRes;
}


A类执行DisconnectFromConnectionPoint函数,返回S_OK,this指针未被修改。
B类执行DisconnectFromConnectionPoint函数,返回S_OK,this指针被修改,地址被删。导致在CDHtmlDialog::DisconnectDHtmlElementEvents()中调用GetDHtmlEventMap()报内存访问错误。

所以,问题出在Unadvise,确切的说是CComPtr<IHTMLDocument2>::Unadvise。
搜了下资料,Unadvise只是将源端、接收端连接断开

请问各位大大,Unadvise内部到底发生了什么事情导致this指针被更改了?
...全文
173 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
向立天 2014-08-11
  • 打赏
  • 举报
回复
两个一样的类 一个好用一个不好用? 用好用的类打开不好用的htm的文件试试呢?
mlqxj35674 2014-08-11
  • 打赏
  • 举报
回复
你的AB两个类都引用了同一个资源Htm么
mlqxj35674 2014-08-11
  • 打赏
  • 举报
回复
检查htm文件路径是否正确,无论是外部文件还是内存资源,路径要正确
13妖 2014-08-11
  • 打赏
  • 举报
回复
5分钟后结贴散分
13妖 2014-08-11
  • 打赏
  • 举报
回复
好吧,不再纠结这个问题了,留给后人吧。 最后换了实现方法,不用Navigate,改用Refresh。因为Refresh不用断开连接,绕过问题。此外Refresh方式速度更快,更平稳。
13妖 2014-08-11
  • 打赏
  • 举报
回复
引用 7 楼 mlqxj35674 的回复:
你的AB两个类都引用了同一个资源Htm么
不是同一个htm 路径肯定正确,因为第一次Navigate正常,这个问题只会在第二次Navigate时由于需要断开之前的链接导致
13妖 2014-08-06
  • 打赏
  • 举报
回复
为何Unadvise以后CDHtmlEventSink实例对象地址都变了?
13妖 2014-08-06
  • 打赏
  • 举报
回复
redui 2014-08-06
  • 打赏
  • 举报
回复
虽然你写得很详细了,但还不足以分析出原因。最重要的是执行顺序,断开连接、释放的过程必须跟连接和初始化的过程完全相反,你可以在关键地方输出信息,看看系统都干了哪些事,重要的是必须把对话框类的创建和释放过程也要包含在监视流水账里面
13妖 2014-08-05
  • 打赏
  • 举报
回复
引用 1 楼 happyparrot 的回复:
没用过这玩意。是个OCX控件吗?
是ocx,在vs2012里已经作为一种对话框基类了,在为对话框“新增类”时,下拉框选项之一
快乐鹦鹉 2014-08-05
  • 打赏
  • 举报
回复
没用过这玩意。是个OCX控件吗?

3,055

社区成员

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

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