簡體   English   中英

IE對象標簽上的attachEvent導致內存損壞

[英]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錯誤。 這里發生了很多與我有關的事情,所以我將按照遇到它們的順序列出它們。

  1. 為什么這樣做: T* pT = static_cast<T*>(this); 您永遠不必這樣做。 如果Lock()Unlock()是對象中的方法,則只需調用它們即可。
  2. 為什么要調用Lock()Unlock() 他們在做什么? 所有IE COM對象(意味着擴展的COM對象)都是STA。 如果它們是單線程的,為什么要進行鎖定?
  3. 您應該更改: int nConnections = m_vec.GetSize(); 為此: const int nConnections = m_vec.GetSize(); ,但這確實與崩潰無關。
  4. 這是完全錯誤的: IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p); 不要自己轉換COM對象。 您需要調用sp->QueryInterface(IID_IDispatch, (void**)&pDispatch); 並檢查它成功返回的HRESULT 然后,您不必檢查NULL,因為如果它返回S_OK,則保證out參數為非NULL。
  5. 您不必在CComVariant上調用VariantClear() CComVariant的全部要點就是為您做到了。 即使您使用的是標准VARIANT ,也要在使用前在此處調用VariantInit() ),而不是VariantClear() (在使用VariantClear()之后再VariantClear() )。
  6. 不要在CComVariant上使用new和delete。 CComVariant的全部要點是,當超出范圍時,它將為您內部進行內存管理。 正確的方法是聲明一個CComVariant數組,類似於在第二個代碼塊中聲明一個基於堆棧的VARIANT數組的方法。 然后,只需刪除delete語句即可。 我不確定為什么第二個例子不會崩潰,因為您是在堆棧分配的數組上調用delete的。 我懷疑你只是幸運。
  7. 我認為您根本不應該使用CComVariant ,因為(a)您不擁有BSTR ,它們已傳入,因此大概有人在釋放它們。 CComVairant超出范圍時,它將使SysFreeString()CComVairant ,並且(b) DISPPARAMS不采用VARIANT ,而是采用VARIANTARG並且它們不是同一回事。
  8. 您應該檢查Invoke()返回的HRESULT 如果失敗,則意味着您的事件未正確觸發,因此您在varResult.scode返回的varResult.scode未初始化。
  9. 此外,由於你遍歷多個連接,你只返回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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM