我在嵌入式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 []导致双重释放相同的字符串,然后以堆损坏结束。
有人知道这里有什么想法吗?这是一个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;
}
谢谢!
答案 0 :(得分:2)
这不是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 param保证为非NULL。VariantClear()
上致电CComVariant
; CComVariant
的全部意义在于它为你做到了。即使您使用的是标准VARIANT
,也可以在此处(在使用之前)调用VariantInit()
,而不是VariantClear()
(这是在您完成之后)。CComVariant
的重点在于,当它超出范围时,它将在内部对你进行内存管理。正确的方法是声明一个CComVariant
的数组,类似于在第二个代码块中声明基于堆栈的CComVariant
数组的方式。然后摆脱删除声明。我不确定为什么你的第二个例子没有崩溃,因为你在堆栈分配的数组上调用delete。我怀疑你很幸运。 VARIANT
,因为(a)你没有拥有CComVariant
,它们被传入,所以大概是其他人正在释放它们。当BSTR
超出范围时,CComVairant
将会SysFreeString()
这些坏男孩,(b)DISPPARAMS
不会VARIANT
,VARIANTARG
s而且他们不是一回事。HRESULT
返回的Invoke()
。如果失败,则表示您的活动未正确触发,因此您在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);
}
答案 1 :(得分:0)
这听起来像一个已知的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(禁用遗留行为以避免崩溃)
仅当您传递多个参数且参数是需要删除的类型时才会发生(例如,字符串与未分配的数字相对)。