From end user perspective, it is very likely your favorite browser extension does not work well in IE9. So unless the extension developer decides to go through the pain of adjusting it, you may now have a good reason switching to Chrome, Firefox, or Flock to name few, where the same developer may have the same extension ready and waiting for you already.
As a software vendor or developer, if you happen to have software that runs into the browser as a browser helper object (a fancy name for an extension) or application automating the browser core through the ActiveX supplied, you may be in trouble. If you added mshtml.tlb to your project, so you can handle document events or manipulate the html with early bound compiled code you are definitely in trouble. You deserve some explanation at this point. Time for a little history..
When Google released the shiny new Chrome, it came with the fastest JavaScript engine called V8. Among other things V8 had tendency of shuffling object methods around, i.e. the methods/properties attached to an object, during enumeration, did not occur in the order in which they were attached to the object. Not too bad thing in a dynamic language like JavaScript. A bad thing, if transferred to objects manipulated by a statically bound at compile time vtable references.
Though I did not invest time in inspecting html object vtables and confirming with tests the order of methods in a JavaScript manipulated object is in flux in IE9, the corrupt stack asserts seen and the V8 behavior known were good enough clues. Instead of hoping to see a solution to the problem in the released IE9, decided to take action and remove any compile time dependencies on mshtml.tlb from my code. The IUnknown, IDispatch do not seem affected by the JavaScript edits (otherwise nothing would have worked). As result one still has the option of making late bound calls through IDispatch (or IDispatchEx) Invoke.
In a small test, replacing the early bound calls with invocations through IDispatch got rid of the stack corruption asserts and yielded correct results. Still the calls looked bulky with all the variant pack/unpack and QueryInterface calls. Decidde to write a small template library that would absorb all the glue code. It was the right way to go, considering the total of 20-30 early bound calls to transform. Managed to get it all covered in a day. Attaching the code to this note. Hope the code serves you well! Should you have notes about the code feel free to post them in the comments section.
--
P.S. One thing the code does not cover is a generic reader/writer for primitive types. I only needed to handle long and BSTR types. For them, instead of something truly generic, decided to write a template specialization instead. While you may create you own specialization for let's say VARIANT_BOOL a generic enough Get/Put that handles well all but the ref variant types is the path to follow.
#pragma once
#include
#define DYNAMIC_ASSERT
// simple type assert T1 inherits from T2
templatestruct AssertOfType {
static void constraints(T1* a) { T2* b = a; ( void )b;}
AssertOfType() { void(*p)(T1*) = constraints; p = 0; }
};
class Dynamic: CComDispatchDriver {
protected:
Dynamic(void):CComDispatchDriver(){
; }
public:
Dynamic( IDispatch * pUnk ):CComDispatchDriver( pUnk ) {
; }
templatestatic HRESULT Get( IDispatch* pSrc, LPCWSTR name, T* pRet ) {
Dynamic d(pSrc); return d.Get( name, pRet ); }
templatestatic HRESULT Put( IDispatch* pSrc, LPCWSTR name, T& val ){
Dynamic d(pSrc); return d.Put( name, val ); }
// working with raw IDispatch here..
templatestatic HRESULT GetRef( IDispatch* pSrc, LPCWSTR name, T** pRet ){
Dynamic d(pSrc); return d.GetRef( name, pRet ); }
templatestatic HRESULT PutRef( IDispatch* pSrc, LPCWSTR name, T* val ){
Dynamic d(pSrc); return d.PutRef( &d, name, val ); }
templateHRESULT Get( LPCWSTR name, T* pRet ) {
// keep the following line as is, until a generic
// template version is not made available to the caller
this should not compile!!!
}
template <> HRESULT Get( LPCWSTR name, long* pRet ) {
CComVariant var;
HRESULT hr = Get(name, &var);
if ( SUCCEEDED( hr ) && ( var.vt != VT_I4 ) && ( var.vt != VT_UI4 ) ) {
hr = var.ChangeType( VT_I4 ); }
if ( SUCCEEDED( hr ) ) {
*pRet = var.lVal; }
#ifdef DYNAMIC_ASSERT
_ASSERTE( SUCCEEDED( hr ) );
#endif
return hr;
}
templateHRESULT Put( LPCWSTR name, T& val ) {
HRESULT hr = S_FALSE;
if ( !p ) { hr = E_POINTER; }
if ( !name ) { hr = E_INVALIDARG; }
CComVariant v(val);
if ( SUCCEEDED( hr ) ) {
hr = this->Put( name, v ); }
#ifdef DYNAMIC_ASSERT
_ASSERTE( SUCCEEDED( hr ) );
#endif
return hr;
}
// specialization: variant handler
template<> HRESULT Get( LPCWSTR name, VARIANT* pRet ) {
HRESULT hr = S_FALSE;
if ( !p ) { hr = E_POINTER; }
if ( !name ) { hr = E_INVALIDARG; }
if ( !pRet ) { hr = E_INVALIDARG; }
if ( SUCCEEDED( hr ) && ( ( *pRet ).vt != VT_EMPTY ) ) {
hr = ::VariantClear( pRet ); }
if ( SUCCEEDED( hr ) ) {
if ( wcscmp( name, L"__newEnum" ) == 0 ) {
hr = this->GetProperty( DISPID_NEWENUM, pRet ); }
else {
hr = this->GetPropertyByName( name, pRet ); } }
#ifdef DYNAMIC_ASSERT
_ASSERTE( SUCCEEDED( hr ) );
#endif
return hr;
}
// specialization: variant handler
template<> HRESULT Put( LPCWSTR name, VARIANT& val ) {
HRESULT hr = S_FALSE;
if ( !p ) { hr = E_POINTER; }
if ( !name ) { hr = E_INVALIDARG; }
if ( SUCCEEDED( hr ) ) {
hr = this->PutPropertyByName( name, &val ); }
#ifdef DYNAMIC_ASSERT
_ASSERTE( SUCCEEDED( hr ) );
#endif
return hr;
}
// specialization: BSTR
template <> HRESULT Get( LPCWSTR name, BSTR* pRet ) {
HRESULT hr = S_FALSE;
if ( !p ) { hr = E_POINTER; }
if ( !pRet ) { hr = E_INVALIDARG; }
if ( !name ) { hr = E_INVALIDARG; }
CComVariant var;
if ( SUCCEEDED( hr ) ) { hr = Get( name, &var ); }
if ( SUCCEEDED( hr ) ) { hr = var.ChangeType( VT_BSTR ); }
if ( SUCCEEDED( hr ) ) {
if ( *pRet ) { ::SysFreeString( *pRet ); *pRet = 0; }
*pRet = var.bstrVal; var.vt = VT_EMPTY; }
#ifdef DYNAMIC_ASSERT
_ASSERTE( SUCCEEDED( hr ) );
#endif
return hr;
}
// generic COM interface handling
templateHRESULT GetRef( LPCWSTR name, T** pRet ) {
AssertOfType< T, IUnknown >();
CComVariant var;
HRESULT hr = S_FALSE;
if ( !p ) { hr = E_POINTER; }
if ( !pRet ) { hr = E_INVALIDARG; }
if ( !name ) { hr = E_INVALIDARG; }
CComPtrpUnk;
if ( SUCCEEDED( hr ) ) {
hr = GetRef( name, &pUnk ); }
if ( SUCCEEDED( hr ) ) {
hr = GetQIPtr( pUnk, pRet ); }
#ifdef DYNAMIC_ASSERT
_ASSERTE( SUCCEEDED( hr ) );
#endif
return hr;
}
// specialization: IUnkown need different that the other COM interfaces handing
template <> HRESULT GetRef( LPCWSTR name, IUnknown** pRet ) {
CComVariant var;
HRESULT hr = S_FALSE;
if ( !p ) { hr = E_POINTER; }
if ( !pRet ) { hr = E_INVALIDARG; }
if ( !name ) { hr = E_INVALIDARG; }
if ( SUCCEEDED( hr ) ) { hr = Get( name, &var ); }
if ( ( SUCCEEDED( hr ) ) && ( !( ( var.vt == VT_DISPATCH ) || ( var.vt == VT_UNKNOWN ) ) ) ) {
hr = E_NOINTERFACE; }
if ( SUCCEEDED( hr ) ) {
CComPtrp = ( var.vt == VT_BYREF ) ? *var.ppunkVal : var.punkVal;
if ( *pRet ) { ( *pRet )->Release(); }
if ( p ) { *pRet = p.Detach(); }
else { *pRet = 0; }
}
#ifdef DYNAMIC_ASSERT
_ASSERTE( SUCCEEDED( hr ) );
#endif
return hr;
}
templateHRESULT PutRef( LPCWSTR name, T* val ){
HRESULT hr = S_FALSE;
AssertOfType< T, IUnknown >();
if ( !p ) { hr = E_POINTER; }
if ( !name ) { hr = E_INVALIDARG; }
if ( SUCCEEDED( hr ) ) {
CComVariant var( val );
hr = this->Put( name, var ); }
#ifdef DYNAMIC_ASSERT
_ASSERTE( SUCCEEDED( hr ) );
#endif
return hr;
}
// statics pointing to the instance members
template< class R > static HRESULT InvokeRef( IDispatch* pSrc, LPCWSTR name, R** pRet ) {
Dynamic d(pSrc); return d.InvokeRef(name, pRet); }
template< class T, class R > static HRESULT InvokeRef( IDispatch* pSrc, LPCWSTR name, T & arg1, R** pRet ) {
Dynamic d(pSrc); return d.InvokeRef(name, arg1, pRet); }
template< class T1, class T2, class R > static HRESULT InvokeRef( IDispatch* pSrc, LPCWSTR name, T1 & arg1, T2 & arg2, R** pRet ) {
Dynamic d(pSrc); return d.InvokeRef(name, arg1, arg2, pRet); }
template< class R > HRESULT InvokeRef( LPCWSTR name, R** pRet ) {
HRESULT hr = S_FALSE;
AssertOfType< R, IUnknown >();
if ( !p ) { hr = E_POINTER; }
if ( !pRet ) { hr = E_INVALIDARG; }
if ( !name ) { hr = E_INVALIDARG; }
CComVariant r;
if ( SUCCEEDED( hr ) ) {
hr = Invoke0(name, &r); }
if ( SUCCEEDED( hr ) ) {
hr = GetQIPtr< R >( r, pRet ); }
#ifdef DYNAMIC_ASSERT
_ASSERTE( SUCCEEDED( hr ) );
#endif
return hr;
}
template< class T, class R > HRESULT InvokeRef( LPCWSTR name, T & arg1, R** pRet ) {
HRESULT hr = S_FALSE;
AssertOfType< R, IUnknown >();
if ( !p ) { hr = E_POINTER; }
if ( !pRet ) { hr = E_INVALIDARG; }
if ( !name ) { hr = E_INVALIDARG; }
CComVariant r, varg1(arg1);
if ( SUCCEEDED( hr ) ) {
hr = Invoke1( name, &varg1, &r); }
if ( SUCCEEDED( hr ) ) {
hr = GetQIPtr< R >( r, pRet ); }
#ifdef DYNAMIC_ASSERT
_ASSERTE( SUCCEEDED( hr ) );
#endif
return hr;
}
template< class T1, class T2, class R > HRESULT InvokeRef( LPCWSTR name, T1 & arg1, T2 & arg2, R** pRet ) {
HRESULT hr = S_FALSE;
AssertOfType< R, IUnknown >();
if ( !p ) { hr = E_POINTER; }
if ( !pRet ) { hr = E_INVALIDARG; }
if ( !name ) { hr = E_INVALIDARG; }
CComVariant r, varg1(arg1), varg2(arg2);
if ( SUCCEEDED( hr ) ) {
hr = Invoke2( name, &varg1, &varg2, &r); }
if ( SUCCEEDED( hr ) ) {
hr = GetQIPtr< R >( r, pRet ); }
#ifdef DYNAMIC_ASSERT
_ASSERTE( SUCCEEDED( hr ) );
#endif
return hr;
}
protected:
template< class T > static HRESULT GetQIPtr( VARIANT & var, T** pRet ) {
AssertOfType< T, IUnknown >( );
HRESULT hr = S_FALSE;
CComPtrpUnk;
if ( SUCCEEDED( hr ) ) {
hr = GetQIPtr( var, &pUnk ); }
if ( pRet && SUCCEEDED( hr ) ) {
hr = GetQIPtr< T >( pUnk, pRet); }
return hr;
}
template<> static HRESULT GetQIPtr( VARIANT & var, IUnknown** pRet ) {
HRESULT hr = S_FALSE;
if ( !( ( var.vt == VT_DISPATCH ) || ( var.vt == VT_UNKNOWN ) ) ) {
hr = E_NOINTERFACE; }
if ( SUCCEEDED( hr ) ) {
CComPtr< IUnknown > p = ( var.vt == VT_BYREF ) ? *var.ppunkVal : var.punkVal;
if ( pRet && *pRet ) { ( *pRet )->Release(); }
if ( pRet ) {
if ( p ) { *pRet = p.Detach(); }
else { *pRet = 0; }
}
}
return hr;
}
template< class T > static HRESULT GetQIPtr( IUnknown * pUnk, T** pRet ) {
AssertOfType< T, IUnknown >();
HRESULT hr = S_OK;
CComQIPtr< T > p = pUnk;
if ( p ) {
if ( pRet ) {
if ( *pRet ) { ( *pRet )->Release(); *pRet = 0; }
*pRet = p.Detach(); } }
else if ( !pUnk ) {
if ( pRet ) {
if ( *pRet ) { ( *pRet )->Release(); }
*pRet = 0; } }
else {
hr = E_NOINTERFACE; }
return hr;
}
template<> static HRESULT GetQIPtr< IUnknown >( IUnknown * pUnk, IUnknown ** pRet ) {
if ( pRet && *pRet ) { ( *pRet )->Release(); *pRet = 0; }
if ( pRet ) { CComPtrp(pUnk); *pRet = p.Detach(); } }
};
// this should be it

No comments:
Post a Comment