繁体   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