0

次のイベントがある埋め込みIE7/8HTMLページ内に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;
 }

ありがとう!

4

2 に答える 2

2

これは IE のバグではありません。気になることがたくさんあるので、気になった順に並べてみます。

  1. なぜこれを行うのですか: T* pT = static_cast<T*>(this);? これを行う必要はありません。Lock()Unlock()がオブジェクトのメソッドである場合は、それらを呼び出すだけです。
  2. なぜあなたは電話Lock()をしているのUnlock()ですか?彼らは何をしますか?すべての IE COM オブジェクト (つまり、拡張機能の COM オブジェクト) は STA です。それらがシングルスレッドの場合、なぜロックを行うのですか?
  3. int nConnections = m_vec.GetSize();this:を this: に変更する必要がありますconst int nConnections = m_vec.GetSize();が、実際にはクラッシュには関係ありません。
  4. これは完全に間違っています: IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);. COM オブジェクトを自分でキャストしないでください。呼び出して、成功のために返されることsp->QueryInterface(IID_IDispatch, (void**)&pDispatch);を確認する必要があります。HRESULTS_OK が返された場合、out パラメータは非 NULL であることが保証されるため、NULL をチェックする必要はありません。
  5. ;VariantClear()を呼び出す必要はありません。CComVariantの要点CComVariantは、それがあなたのためにそれを行うということです。standard を使用していたとしても、ここでは (使用前に)VARIANT呼び出しますが、 (これは使い終わった後) ではありません。VariantInit()VariantClear()
  6. sで new と delete を使用しないでくださいCComVariant。要点はCComVariant、範囲外になったときに内部でメモリ管理を行うことです。正しいアプローチは、s の配列を宣言することです。これは、2 番目のコード ブロックでCComVariantスタックベースの s の配列を宣言した方法と同様です。VARIANT次に、delete ステートメントを削除します。スタック割り当て配列で delete を呼び出しているため、2 番目の例がクラッシュしない理由がわかりません。運が良かっただけだと思います。
  7. CComVariant(a) あなたは s を所有しておらず、BSTR渡されているため、おそらく他の誰かがそれらを解放しているので、 まったく使用すべきではないと思います。CComVairantそれが範囲外になったとき、それらの悪い男の子はそうするでしょうSysFreeString()、そして(b)はsDISPPARAMSを取りませんVARIANT、それはVARIANTARGsを取り、それらは同じものではありません.
  8. HRESULT返されるを確認する必要がありInvoke()ます。失敗した場合は、イベントが適切に発生しなかったことを意味するため、返される内容は初期化されていませんvarResult.scode
  9. また、複数の接続を反復しているためscode、最後の接続のみを返しています。1 つが失敗した場合、次のものが成功します。本当に何を返したいですか? それを処理する方法を理解する必要があります。以下の例では、それを大まかに説明しています。

これが私がそれをした方法です:

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);
}
于 2010-06-16T18:35:04.690 に答える
0

これは既知の 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 (クラッシュを避けるために従来の動作を無効にします)

複数のパラメーターを渡し、パラメーターが削除する必要がある型 (たとえば、割り当てられていない数値ではなく文字列) である場合にのみ発生します。

于 2016-05-24T22:48:52.387 に答える