简体   繁体   English

IE对象标签上的attachEvent导致内存损坏

[英]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. 这里发生了很多与我有关的事情,所以我将按照遇到它们的顺序列出它们。

  1. Why are you doing this: T* pT = static_cast<T*>(this); 为什么这样做: T* pT = static_cast<T*>(this); ? You shouldn't ever have to do this. 您永远不必这样做。 If Lock() and Unlock() are methods in your object, just call them. 如果Lock()Unlock()是对象中的方法,则只需调用它们即可。
  2. Why are you calling Lock() and Unlock() ? 为什么要调用Lock()Unlock() What do they do? 他们在做什么? All IE COM objects (which means your extension's COM objects) are STA. 所有IE COM对象(意味着扩展的COM对象)都是STA。 If they're single threaded, why are you doing locking? 如果它们是单线程的,为什么要进行锁定?
  3. You should change this: 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. ,但这确实与崩溃无关。
  4. This is completely wrong: IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p); 这是完全错误的: IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p); . Don't cast COM objects yourself. 不要自己转换COM对象。 You need to call 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. 然后,您不必检查NULL,因为如果它返回S_OK,则保证out参数为非NULL。
  5. You don't have to call 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() )。
  6. Don't use new and delete on the 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. 然后,只需删除delete语句即可。 I'm not sure why you're second example doesn't crash, since you're calling delete on a stack-allocated array. 我不确定为什么第二个例子不会崩溃,因为您是在堆栈分配的数组上调用delete的。 I suspect you're just getting lucky. 我怀疑你只是幸运。
  7. I don't think you should use 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并且它们不是同一回事。
  8. You should check the 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未初始化。
  9. Also, since you're iterating over multiple connections, you're only returning the scode of the last one. 此外,由于你遍历多个连接,你只返回scode最后一位。 If one fails, then the next one succeeds, what do you really want to return? 如果一个失败,那么下一个成功,您真正想要返回什么? You have to figure out how to handle that—I've glossed it over in my example below. 您必须弄清楚如何处理该问题-在下面的示例中,我将其加以掩盖。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM