0

다음 이벤트가 포함 된 IE7/8 HTML 페이지에 ActiveX 컨트롤이 있습니다. [id(1)] HRESULT MessageReceived([in] BSTR id, [in] BSTR json). Windows에서 이벤트는 OCX.attachEvent("MessageReceived", onMessageReceivedFunc)으로 등록됩니다.개체 태그의 IE attachEvent로 인해 메모리 손상이 발생합니다.

다음 코드는 HTML 페이지에서 이벤트를 발생시킵니다. I는 애플리케이션 검증 부와 gflags.exe를 활성화

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; 
} 

후 다음 비정상적인 동작이 수행 자바 스크립트 콜백을 실행하고()를 호출, pvars에서 BSTR은 [1] pvars 복사 후 [0]에 대한 어떤 알려지지 않은 이유!? pvars의 delete []는 동일한 문자열을 두 번 비우고 힙 손상으로 끝나게합니다.

아무도 아이디어가 있습니까? 이것은 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

이것은 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 표준을 사용한다고하더라도 VariantClear()이 아니라 VariantInit()을 사용하기 전에 (사용하기 전에) 전화 할 것입니다.
  6. CComVariant에서 새 문자 및 삭제를 사용하지 마십시오. CComVariant의 요점은 범위를 벗어나면 내부적으로 메모리 관리를 수행한다는 것입니다. 정확한 접근법은 두 번째 코드 블록에 VARIANT의 스택 기반 배열을 선언 한 것과 비슷한 CComVariant 배열을 선언하는 것입니다. 그런 다음 delete 문을 제거하십시오. 스택 할당 배열에서 delete를 호출하기 때문에 두 번째 예제가 충돌하지 않는 이유는 확실하지 않습니다. 네가 운 좋은 것 같아.
  7. 을 사용하지 않아도됩니다. (a) 사용자가 BSTR을 소유하고 있지 않아 전달되었으므로 다른 사람이 무료로 제공하고 있기 때문입니다. CComVairant이 범위를 벗어나면 나쁜 소년은 DISPPARAMSVARIANT이 아니므로 VARIANTARG 초가 걸리며 그 (것)들은 같지 않습니다.
  8. HRESULTInvoke()이 표시되어 있는지 확인해야합니다. 실패하면 이벤트가 제대로 실행되지 않았으므로 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

광범위한 의견을 보내 주셔서 감사합니다. "delete [] pvars;" 내 두 번째 코드 예제에서는 복사 실수였습니다. 우리가 사용하는 구현에 상관없이 Invoke() 메모리가 말하기 전에 문제의 근원이 있습니다 : pvars [0] = "a"; pvars [1] = "b"; ... Invoke() 메모리가 나온 후 ... pvars [0] = "b"; pvars [1] = "b"; ... 누군가가 배열의 문자열을 복사했습니다. 나는 IE가 이것을하고 있다고 생각한다. 예, 우리는 CComVariant * 대신 VARIANT를 사용하여 코드 또는 두 번째 코드 (delete [] pvars 제외)로 메모리 손상을 피할 수 있습니다. 그러나 문자열은 여전히 ​​attachEvent()를 사용할 때만 잘못 복사됩니다. – Lars

+0

Andrew W. Troelsen의 "Developer 's Workshop to COM and ATL 3.0"과 같은 모든 실수가있는 것 같습니다. 이 책의 적어도 하나의 예는 topic 초보자의 코드와 똑같이 생겼습니다 (그리고 위에서 언급 한 모든 발행물을 가지고 있습니다). – vond

0

이 알려진 IE 버그 같은 소리. FEATURE_LEGACY_DISPPARAMS 기능 컨트롤 키를 추가하고 값을 false로 설정하십시오. [EXE 이름] DWORD 값 : 마이크로 소프트 \ Internet Explorer를 \ 홈페이지 \ FeatureControl을 \ 마이크로 소프트 \ Internet Explorer를 \ 홈페이지 \ FeatureControl을 DWORD 이름 \ 또는 HKEY_LOCAL_MACHINE \ 소프트웨어 FEATURE_LEGACY_DISPPARAMS \

HKEY_LOCAL_MACHINE \ 소프트웨어 \ Wow6432Node 0 (해제

두 개 이상의 매개 변수를 전달할 때만 발생하며 매개 변수는 삭제해야하는 유형 (예 : 할당되지 않은 숫자가 아닌 문자열)입니다.