[英]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.