[英]IE attachEvent on object tag causes memory corruption
我在嵌入式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)
{
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;
}
在使用应用程序验证程序启用gflags.exe之后,会发生以下奇怪的行为:在执行JavaScript回调的Invoke()之后,出于某些未知原因,将pvars [1]的BSTR复制到pvars [0]! pvars的delete []导致没有相同字符串的double释放,然后结束堆损坏。
有人知道这里发生了什么吗? 这是IE错误还是OCX实施中缺少我的窍门?
如果我使用如下标记:
<script for="OCX" event="MessageReceived(id, json)" language="JavaScript" type="text/javascript">
window.onMessageReceivedFunc(windowId, json);
</script>
...不会发生奇怪的复制操作。
由于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;
}
谢谢!
这不是IE错误。 这里发生了很多与我有关的事情,所以我将按照遇到它们的顺序列出它们。
T* pT = static_cast<T*>(this);
? 您永远不必这样做。 如果Lock()
和Unlock()
是对象中的方法,则只需调用它们即可。 Lock()
和Unlock()
? 他们在做什么? 所有IE COM对象(意味着扩展的COM对象)都是STA。 如果它们是单线程的,为什么要进行锁定? int nConnections = m_vec.GetSize();
为此: const int nConnections = m_vec.GetSize();
,但这确实与崩溃无关。 IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);
。 不要自己转换COM对象。 您需要调用sp->QueryInterface(IID_IDispatch, (void**)&pDispatch);
并检查它成功返回的HRESULT
。 然后,您不必检查NULL,因为如果它返回S_OK,则保证out参数为非NULL。 CComVariant
上调用VariantClear()
; CComVariant
的全部要点就是为您做到了。 即使您使用的是标准VARIANT
,也要在使用前在此处调用VariantInit()
),而不是VariantClear()
(在使用VariantClear()
之后再VariantClear()
)。 CComVariant
上使用new和delete。 CComVariant
的全部要点是,当超出范围时,它将为您内部进行内存管理。 正确的方法是声明一个CComVariant
数组,类似于在第二个代码块中声明一个基于堆栈的VARIANT
数组的方法。 然后,只需删除delete语句即可。 我不确定为什么第二个例子不会崩溃,因为您是在堆栈分配的数组上调用delete的。 我怀疑你只是幸运。 CComVariant
,因为(a)您不拥有BSTR
,它们已传入,因此大概有人在释放它们。 CComVairant
超出范围时,它将使SysFreeString()
坏CComVairant
,并且(b) DISPPARAMS
不采用VARIANT
,而是采用VARIANTARG
并且它们不是同一回事。 Invoke()
返回的HRESULT
。 如果失败,则意味着您的事件未正确触发,因此您在varResult.scode
返回的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);
}
这听起来像一个已知的IE错误。 添加FEATURE_LEGACY_DISPPARAMS功能控制键,并将其值设置为false。
HKEY_LOCAL_MACHINE \\ SOFTWARE \\ Wow6432Node \\ Microsoft \\ Internet Explorer \\ Main \\ FeatureControl \\ FEATURE_LEGACY_DISPPARAMS或HKEY_LOCAL_MACHINE \\ SOFTWARE \\ Microsoft \\ Internet Explorer \\ Main \\ FeatureControl DWORD名称:[exe名称] DWORD值:0(禁用旧式行为以避免崩溃)
仅当您传递多个参数且参数是需要删除的类型(例如,与未分配的数字相对的字符串)时才发生。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.