IE attachEvent на объектном теге вызывает повреждение памяти - PullRequest
0 голосов
/ 16 июня 2010

У меня есть элемент управления ActiveX на встроенной HTML-странице IE7 / 8 со следующим событием [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 с помощью верификатора приложения, следующее странное поведениепроисходит: после Invoke (), который выполняет обратный вызов JavaScript, BSTR из pvars [1] копируется в pvars [0] по неизвестной причине !?Удаление [] из pvars приводит к двойному освобождению той же строки, что приводит к повреждению кучи.

У кого-нибудь есть идея, что здесь происходит?Это ошибка 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;
 }

Спасибо!

Ответы [ 2 ]

2 голосов
/ 16 июня 2010

Это не ошибка 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. Вам не нужно звонить VariantClear() на CComVariant; весь смысл CComVariant в том, что он делает это для вас. Даже если бы вы использовали стандартный VARIANT, вы бы назвали здесь VariantInit() (до его использования), а не VariantClear() (это после того, как вы покончили с ним).
  6. Не используйте new и удаляйте на CComVariant s. Весь смысл CComVariant состоит в том, что он будет выполнять управление памятью для вас внутри, когда выйдет за рамки. Правильный подход заключается в объявлении массива CComVariant s, аналогично тому, как вы объявили массив на основе стека VARIANT s во втором блоке кода. Тогда просто избавьтесь от оператора delete. Я не уверен, почему ваш второй пример не дает сбоя, так как вы вызываете delete для выделенного стека массива. Я подозреваю, что тебе просто везет.
  7. Я не думаю, что вам вообще следует использовать CComVariant, поскольку (а) вам не принадлежит BSTR s, они переданы, так что, вероятно, кто-то еще их освобождает. CComVairant будут SysFreeString() теми плохими парнями, когда он выйдет за рамки, и (b) DISPPARAMS не займет VARIANT с, потребуется VARIANTARG с, и они не одно и то же.
  8. Вы должны проверить HRESULT, которое Invoke() возвращает. Если это не удалось, это означает, что ваше событие не сработало должным образом, поэтому то, что вы возвращаете в 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);
}
0 голосов
/ 25 мая 2016

Это звучит как известная ошибка IE.Добавьте управляющий ключ функции FEATURE_LEGACY_DISPPARAMS и установите для него значение false.

HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ Microsoft \ Internet Explorer \ Main \ FeatureControl \ FEATURE_LEGACY_DISPPARAMS \ Main_Works \ \ \\ \\\ \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\:?: [exe name] Значение DWORD: 0 (отключить устаревшее поведение, чтобы избежать сбоя)

Происходит только при передаче более одного параметра, а параметры являются типами, которые необходимо удалить (например, строки, а не числакоторые не выделены).

...