Internet explorer 对象标记上的IE attachEvent导致内存损坏
我在嵌入式IE7/8 HTML页面中有一个ActiveX控件,该页面具有以下事件Internet explorer 对象标记上的IE attachEvent导致内存损坏,internet-explorer,activex,idispatch,object-tag,connection-points,Internet Explorer,Activex,Idispatch,Object Tag,Connection Points,我在嵌入式IE7/8 HTML页面中有一个ActiveX控件,该页面具有以下事件[id(1)]HRESULT MessageReceived([in]BSTR id,[in]BSTR json)。在Windows上,事件是通过OCX.attachEvent(“MessageReceived”,onMessageReceivedFunc)”注册的 下面的代码在HTML页面中触发事件 HRESULT Fire_MessageReceived(BSTR id, BSTR json) { CCo
[id(1)]HRESULT MessageReceived([in]BSTR id,[in]BSTR json)
。在Windows上,事件是通过OCX.attachEvent(“MessageReceived”,onMessageReceivedFunc)”注册的
下面的代码在HTML页面中触发事件
HRESULT Fire_MessageReceived(BSTR id, BSTR json)
{
CComVariant varResult;
T* pT = static_cast<T*>(this);
int nConnectionIndex;
CComVariant* pvars = new CComVariant[2];
int nConnections = m_vec.GetSize();
for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
{
pT->Lock();
CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
pT->Unlock();
IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);
if (pDispatch != NULL)
{
VariantClear(&varResult);
pvars[1] = id;
pvars[0] = json;
DISPPARAMS disp = { pvars, NULL, 2, 0 };
pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);
}
}
delete[] pvars; // -> Memory Corruption here!
return varResult.scode;
}
HRESULT Fire_MessageReceived(BSTR id,BSTR json)
{
变异结果;
T*pT=静态施法(本);
int-nConnectionIndex;
CComVariant*pvars=新的CComVariant[2];
int nConnections=m_vec.GetSize();
对于(nConnectionIndex=0;nConnectionIndexLock();
CComPtr sp=m_vec.GetAt(nConnectionIndex);
pT->Unlock();
IDispatch*pDispatch=重新解释铸件(sp.p);
if(pDispatch!=NULL)
{
VariantClear(&varResult);
pvars[1]=id;
pvars[0]=json;
DISPPARAMS disp={pvars,NULL,2,0};
pDispatch->Invoke(0x1,IID\u NULL,LOCALE\u USER\u DEFAULT,DISPATCH\u METHOD,&disp,&varResult,NULL,NULL);
}
}
删除[]pvars;//->此处内存损坏!
返回varResult.scode;
}
在我使用application verifier启用gflags.exe后,出现以下奇怪行为:
在执行JavaScript回调的Invoke()之后,出于未知原因,将来自pvars[1]的BSTR复制到pvars[0!?pvars的delete[]导致同一字符串的double free,然后导致堆损坏
有人知道这里发生了什么吗?这是一个IE bug还是OCX实现中有我遗漏的技巧?
如果我像这样使用标签:
<script for="OCX" event="MessageReceived(id, json)" language="JavaScript" type="text/javascript">
window.onMessageReceivedFunc(windowId, json);
</script>
onMessageReceivedFunc(windowId,json);
。。。奇怪的复制操作不会发生
由于Fire_MessageReceived()的调用方负责释放BSTR,因此下面的代码似乎也可以
HRESULT Fire_MessageReceived(BSTR srcWindowId, BSTR json)
{
CComVariant varResult;
T* pT = static_cast<T*>(this);
int nConnectionIndex;
VARIANT pvars[2];
int nConnections = m_vec.GetSize();
for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
{
pT->Lock();
CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
pT->Unlock();
IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);
if (pDispatch != NULL)
{
VariantClear(&varResult);
pvars[1].vt = VT_BSTR;
pvars[1].bstrVal = srcWindowId;
pvars[0].vt = VT_BSTR;
pvars[0].bstrVal = json;
DISPPARAMS disp = { pvars, NULL, 2, 0 };
pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);
}
}
delete[] pvars;
return varResult.scode;
}
HRESULT Fire_MessageReceived(BSTR SRCSWindowId,BSTR json)
{
变异结果;
T*pT=静态施法(本);
int-nConnectionIndex;
变异pvars[2];
int nConnections=m_vec.GetSize();
对于(nConnectionIndex=0;nConnectionIndexLock();
CComPtr sp=m_vec.GetAt(nConnectionIndex);
pT->Unlock();
IDispatch*pDispatch=重新解释铸件(sp.p);
if(pDispatch!=NULL)
{
VariantClear(&varResult);
pvars[1]。vt=vt_BSTR;
pvars[1].bstrVal=srcWindowId;
pvars[0]。vt=vt_BSTR;
pvars[0].bstrVal=json;
DISPPARAMS disp={pvars,NULL,2,0};
pDispatch->Invoke(0x1,IID\u NULL,LOCALE\u USER\u DEFAULT,DISPATCH\u METHOD,&disp,&varResult,NULL,NULL);
}
}
删除[]个PVAR;
返回varResult.scode;
}
谢谢 这不是IE漏洞。这里有很多事情与我有关,所以我会按遇到的顺序列出它们
为什么要这样做:T*pT=static\u cast(这个)代码>?你不应该这样做。如果对象中的方法是Lock()
和Unlock()
,只需调用它们即可
为什么要调用Lock()
和Unlock()
?他们是干什么的?所有IE COM对象(即扩展的COM对象)都是STA。如果它们是单线程的,为什么要进行锁定
您应该更改此设置:intnConnections=m_vec.GetSize()代码>到此:const int nConnections=m_vec.GetSize()代码>,但这与您的崩溃没有任何关系
这是完全错误的:IDispatch*pDispatch=reinterpret\u cast(sp.p)代码>。不要自己投射COM对象。您需要调用sp->QueryInterface(IID_IDispatch,(void**)和pDispatch)
并检查它返回的HRESULT
是否成功。然后您不必检查NULL,因为如果它返回S_OK,out参数保证为非NULL
您不必在CComVariant
上调用VariantClear()
;CComVariant
的全部意义在于它为您提供了帮助。即使您使用的是标准的VARIANT
,您也会在这里(使用它之前)调用VariantInit()
,而不是VariantClear()
(在使用它之后)
不要在CComVariant
s上使用new和delete。CComVariant
的全部要点是,当它超出范围时,它将在内部为您进行内存管理。正确的方法是声明CComVariant
s数组,类似于在第二个代码块中声明基于堆栈的VARIANT
s数组。然后去掉delete语句。我不知道为什么第二个示例没有崩溃,因为您在堆栈分配的数组上调用delete。我猜你只是走运了李>
我认为您根本不应该使用CComVariant
,因为(a)您不拥有BSTR
s,它们被传入,所以可能是其他人在释放它们CComVairant
将SysFreeString()
那些坏小子当它超出范围时,并且(b)disparams
不使用VARIANT
s,它使用VARIANTARG
s,它们不是同一件事
您应该检查Invoke()
返回的HRESULT
。如果失败,这意味着您的事件未正确触发,因此您在varResult.scode
中返回的内容未初始化
此外,由于您在多个连接上进行迭代,因此只返回最后一个连接的scode
。如果一个失败了,那么下一个成功了,你真正想要的回报是什么?你必须弄清楚如何处理这个问题——我在下面的例子中已经把它掩盖了
以下是我将如何做到这一点:
HRESULT Fire_MessageReceived(BSTR srcWindowId, BSTR json) {
CComVariant varResult;
VARIANTARG vars[2];
const int nConnections = m_vec.GetSize();
for (int i = 0; i < nConnections; ++i) {
Lock();
CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
Unlock();
IDispatch* pDispatch;
HRESULT hr = sp->QueryInterface(IID_IDispatch, (void**)&pDispatch);
if (SUCCEEDED(hr)) {
pvars[1].vt = VT_BSTR;
pvars[1].bstrVal = srcWindowId;
pvars[0].vt = VT_BSTR;
pvars[0].bstrVal = json;
DISPPARAMS disp = { pvars, NULL, ARRAYSIZE(vars), 0 };
hr = pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);
}
}
return (SUCCEEDED(hr) ? varResult.scode : hr);
}
HRESULT Fire_MessageReceived(BSTR SRCSWindowId,BSTR json){
变异结果;
VARIANTARG变量[2];
const int nConnections=m_vec.GetSize();
为了