Internet explorer 对象标记上的IE attachEvent导致内存损坏

Internet explorer 对象标记上的IE attachEvent导致内存损坏,internet-explorer,activex,idispatch,object-tag,connection-points,Internet Explorer,Activex,Idispatch,Object Tag,Connection Points,我在嵌入式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) { CCo

我在嵌入式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;
 }
HRESULT Fire_MessageReceived(BSTR id,BSTR json)
{
变异结果;
T*pT=静态施法(本);
int-nConnectionIndex;
CComVariant*pvars=新的CComVariant[2];
int nConnections=m_vec.GetSize();
对于(nConnectionIndex=0;nConnectionIndexLock();
CComPtr sp=m_vec.GetAt(nConnectionIndex);
pT->Unlock();
IDispatch*pDispatch=重新解释铸件(sp.p);
if(pDispatch!=NULL)
{
VariantClear(&varResult);
pvars[1]=id;
pvars[0]=json;
DISPPARAMS disp={pvars,NULL,2,0};
pDispatch->Invoke(0x1,IID\u NULL,LOCALE\u USER\u DEFAULT,DISPATCH\u METHOD,&disp,&varResult,NULL,NULL);
}
}
删除[]pvars;//->此处内存损坏!
返回varResult.scode;
}
在我使用application verifier启用gflags.exe后,出现以下奇怪行为: 在执行JavaScript回调的Invoke()之后,出于未知原因,将来自pvars[1]的BSTR复制到pvars[0!?pvars的delete[]导致同一字符串的double free,然后导致堆损坏

有人知道这里发生了什么吗?这是一个IE bug还是OCX实现中有我遗漏的技巧?

如果我像这样使用标签:

<script for="OCX" event="MessageReceived(id, json)" language="JavaScript" type="text/javascript">
    window.onMessageReceivedFunc(windowId, json);
</script>

onMessageReceivedFunc(windowId,json);
。。。奇怪的复制操作不会发生

由于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;
 }
HRESULT Fire_MessageReceived(BSTR SRCSWindowId,BSTR json)
{
变异结果;
T*pT=静态施法(本);
int-nConnectionIndex;
变异pvars[2];
int nConnections=m_vec.GetSize();
对于(nConnectionIndex=0;nConnectionIndexLock();
CComPtr sp=m_vec.GetAt(nConnectionIndex);
pT->Unlock();
IDispatch*pDispatch=重新解释铸件(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\u NULL,LOCALE\u USER\u DEFAULT,DISPATCH\u METHOD,&disp,&varResult,NULL,NULL);
}
}
删除[]个PVAR;
返回varResult.scode;
}

谢谢

这不是IE漏洞。这里有很多事情与我有关,所以我会按遇到的顺序列出它们

  • 为什么要这样做:
    T*pT=static\u cast(这个)?你不应该这样做。如果对象中的方法是
    Lock()
    Unlock()
    ,只需调用它们即可
  • 为什么要调用
    Lock()
    Unlock()
    ?他们是干什么的?所有IE COM对象(即扩展的COM对象)都是STA。如果它们是单线程的,为什么要进行锁定
  • 您应该更改此设置:
    intnConnections=m_vec.GetSize()到此:
    const int nConnections=m_vec.GetSize(),但这与您的崩溃没有任何关系
  • 这是完全错误的:
    IDispatch*pDispatch=reinterpret\u cast(sp.p)。不要自己投射COM对象。您需要调用
    sp->QueryInterface(IID_IDispatch,(void**)和pDispatch)
    并检查它返回的
    HRESULT
    是否成功。然后您不必检查NULL,因为如果它返回S_OK,out参数保证为非NULL
  • 您不必在
    CComVariant
    上调用
    VariantClear()
    CComVariant
    的全部意义在于它为您提供了帮助。即使您使用的是标准的
    VARIANT
    ,您也会在这里(使用它之前)调用
    VariantInit()
    ,而不是
    VariantClear()
    (在使用它之后)
  • 不要在
    CComVariant
    s上使用new和delete。
    CComVariant
    的全部要点是,当它超出范围时,它将在内部为您进行内存管理。正确的方法是声明
    CComVariant
    s数组,类似于在第二个代码块中声明基于堆栈的
    VARIANT
    s数组。然后去掉delete语句。我不知道为什么第二个示例没有崩溃,因为您在堆栈分配的数组上调用delete。我猜你只是走运了
  • 我认为您根本不应该使用
    CComVariant
    ,因为(a)您不拥有
    BSTR
    s,它们被传入,所以可能是其他人在释放它们
    CComVairant
    SysFreeString()
    那些坏小子当它超出范围时,并且(b)
    disparams
    不使用
    VARIANT
    s,它使用
    VARIANTARG
    s,它们不是同一件事
  • 您应该检查
    Invoke()
    返回的
    HRESULT
    。如果失败,这意味着您的事件未正确触发,因此您在
    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);
    }
    
    HRESULT Fire_MessageReceived(BSTR SRCSWindowId,BSTR json){
    变异结果;
    VARIANTARG变量[2];
    const int nConnections=m_vec.GetSize();
    为了