[英]IE attachEvent on object tag causes memory corruption
I've an ActiveX Control within an embedded IE7/8 HTML page that has the following event [id(1)] HRESULT MessageReceived([in] BSTR id, [in] BSTR json)
. 我在嵌入式IE7 / 8 HTML页面中有一个ActiveX控件,该页面具有以下事件
[id(1)] HRESULT MessageReceived([in] BSTR id, [in] BSTR json)
。 On Windows the event is registered with OCX.attachEvent("MessageReceived", onMessageReceivedFunc)
. 在Windows上,事件是使用
OCX.attachEvent("MessageReceived", onMessageReceivedFunc)
。
Following code fires the event in the HTML page. 以下代码在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;
}
After I enabled gflags.exe with application verifier, the following strange behaviour occur: After Invoke() that is executing the JavaScript callback, the BSTR from pvars[1] is copied to pvars[0] for some unknown reason!? 在使用应用程序验证程序启用gflags.exe之后,会发生以下奇怪的行为:在执行JavaScript回调的Invoke()之后,出于某些未知原因,将pvars [1]的BSTR复制到pvars [0]! The delete[] of pvars causes a double free of the same string then which ends in a heap corruption.
pvars的delete []导致没有相同字符串的double释放,然后结束堆损坏。
Does anybody has an idea whats going on here? 有人知道这里发生了什么吗? Is this a IE bug or is there a trick within the OCX Implementation that I'm missing?
这是IE错误还是OCX实施中缺少我的窍门?
If I use the tag like: 如果我使用如下标记:
<script for="OCX" event="MessageReceived(id, json)" language="JavaScript" type="text/javascript">
window.onMessageReceivedFunc(windowId, json);
</script>
... the strange copy operation does not occur. ...不会发生奇怪的复制操作。
The following code also seem to be ok due to the fact that the caller of Fire_MessageReceived() is responsible for freeing the BSTRs. 由于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;
}
Thanks! 谢谢!
This is not an IE bug. 这不是IE错误。 There are a lot of things going on here that concern me, so I'll list them in the order I encountered them.
这里发生了很多与我有关的事情,所以我将按照遇到它们的顺序列出它们。
T* pT = static_cast<T*>(this);
T* pT = static_cast<T*>(this);
? Lock()
and Unlock()
are methods in your object, just call them. Lock()
和Unlock()
是对象中的方法,则只需调用它们即可。 Lock()
and Unlock()
? Lock()
和Unlock()
? What do they do? int nConnections = m_vec.GetSize();
int nConnections = m_vec.GetSize();
to this: const int nConnections = m_vec.GetSize();
const int nConnections = m_vec.GetSize();
, but this really doesn't have any bearing on your crash. IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);
IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);
. sp->QueryInterface(IID_IDispatch, (void**)&pDispatch);
sp->QueryInterface(IID_IDispatch, (void**)&pDispatch);
and check the HRESULT
it returns for success. HRESULT
。 Then you don't have to check for NULL, since if it returns S_OK the out param is guaranteed to be non-NULL. VariantClear()
on a CComVariant
; CComVariant
上调用VariantClear()
; the whole point of CComVariant
is that it does it for you. CComVariant
的全部要点就是为您做到了。 Even if you were using a standard VARIANT
, you would call VariantInit()
here (before you use it), not VariantClear()
(which is for after you're done with it). VARIANT
,也要在使用前在此处调用VariantInit()
),而不是VariantClear()
(在使用VariantClear()
之后再VariantClear()
)。 CComVariant
s. CComVariant
上使用new和delete。 The whole point of CComVariant
is that it will do memory management for you internally when it goes out of scope. CComVariant
的全部要点是,当超出范围时,它将为您内部进行内存管理。 The correct approach is to declare an array of CComVariant
s, similar to the way you declared a stack-based array of VARIANT
s in your second code block. CComVariant
数组,类似于在第二个代码块中声明一个基于堆栈的VARIANT
数组的方法。 Then just get rid of the delete statement. CComVariant
at all, since (a) you don't own the BSTR
s, they're passed in, so presumably someone else is freeing them. CComVariant
,因为(a)您不拥有BSTR
,它们已传入,因此大概有人在释放它们。 CComVairant
will SysFreeString()
those bad-boys when it goes out of scope, and (b) DISPPARAMS
doesn't take VARIANT
s, it takes VARIANTARG
s and they're not the same thing. CComVairant
超出范围时,它将使SysFreeString()
坏CComVairant
,并且(b) DISPPARAMS
不采用VARIANT
,而是采用VARIANTARG
并且它们不是同一回事。 HRESULT
that Invoke()
returns. Invoke()
返回的HRESULT
。 If it failed, that means your event didn't properly fire, so what you return in varResult.scode
is uninitialized. varResult.scode
返回的varResult.scode
未初始化。 scode
of the last one. scode
最后一位。 If one fails, then the next one succeeds, what do you really want to return? Here is how I would have done it: 这是我应该做的:
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);
}
This sounds like a known IE bug. 这听起来像一个已知的IE错误。 Add the FEATURE_LEGACY_DISPPARAMS feature control key and set its value to false.
添加FEATURE_LEGACY_DISPPARAMS功能控制键,并将其值设置为false。
HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Internet Explorer\\Main\\FeatureControl\\FEATURE_LEGACY_DISPPARAMS or HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Internet Explorer\\Main\\FeatureControl DWORD name: [exe name] DWORD value: 0 (disable legacy behavior to avoid crash) 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(禁用旧式行为以避免崩溃)
Happens only when you pass more than one parameter and the parameters are types that need to be deleted (strings for example as opposed to numbers which aren't allocated). 仅当您传递多个参数且参数是需要删除的类型(例如,与未分配的数字相对的字符串)时才发生。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.