// Filename: PPInterface.cpp // Created by: atrestman (14Sept09) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) Carnegie Mellon University. All rights reserved. // // All use of this software is subject to the terms of the revised BSD // license. You should have received a copy of this license along // with this source code in a file named "LICENSE." // //////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "PPInterface.h" #include "PPPandaObject.h" #include "PPBrowserObject.h" #include "PPInstance.h" #include "P3DActiveXCtrl.h" #include #include "Mshtml.h" PPInterface::PPInterface( ) { } PPInterface::~PPInterface( ) { } HRESULT PPInterface::GetIdOfName( IDispatch* pDisp, CString& ptName, DISPID* dispID ) { if( !pDisp ) { return E_FAIL; } OLECHAR* pElementName = ptName.AllocSysString(); HRESULT hr = pDisp->GetIDsOfNames(IID_NULL, &pElementName, 1, LOCALE_USER_DEFAULT, dispID); SysFreeString( pElementName ); return hr; } HRESULT PPInterface::Invoke(int nType, IDispatch* pDisp, CString& ptName, VARIANT* pvResult, int cArgs, VARIANT* params) { if( !pDisp ) { return E_FAIL; } DISPPARAMS dp = { NULL, NULL, 0, 0 }; DISPID dispidNamed = DISPID_PROPERTYPUT; DISPID dispID; CComPtr pDispEx; HRESULT hr = GetIdOfName( pDisp, ptName, &dispID ); if ( DISP_E_UNKNOWNNAME == hr ) { hr = pDisp->QueryInterface( IID_IDispatchEx, ( void** )&pDispEx ); if ( SUCCEEDED( hr ) && pDispEx ) { OLECHAR* pElementName = ptName.AllocSysString(); hr = pDispEx->GetDispID( pElementName, fdexNameEnsure, &dispID ); SysFreeString( pElementName ); } } if ( FAILED( hr ) ) { return hr; } // Allocate memory for arguments... VARIANT *pArgs = new VARIANT[ cArgs + 1 ]; // Reversing the arguments!!! // NOTE: http://msdn.microsoft.com/en-us/library/cc237569(PROT.10).aspx // pDispParams: MUST point to a DISPPARAMS structure that defines the arguments passed to the method. // Arguments MUST be stored in pDispParams->rgvarg in reverse order, so that the first argument is // the one with the highest index in the array. Byref arguments MUST be marked in this array // as VT_EMPTY entries, and stored in rgVarRef instead. for( int i = 0; i < cArgs; i++ ) { pArgs[i] = params[ cArgs - 1 - i ]; } // Build DISPPARAMS dp.cArgs = cArgs; dp.rgvarg = pArgs; // Handle special-case for property-puts! if( nType & DISPATCH_PROPERTYPUT || nType & DISPATCH_PROPERTYPUTREF ) { dp.cNamedArgs = 1; dp.rgdispidNamedArgs = &dispidNamed; } // Make the call! if ( pDispEx ) { hr = pDispEx->InvokeEx(dispID, LOCALE_USER_DEFAULT, nType, &dp, pvResult, NULL, NULL); if ( FAILED( hr ) ) { pDispEx->DeleteMemberByDispID( dispID ); } } else { hr = pDisp->Invoke( dispID, IID_NULL, LOCALE_USER_DEFAULT, nType, &dp, pvResult, NULL, NULL ); } delete [] pArgs; return hr; } HRESULT PPInterface::GetHtmlDocDispatch( CComPtr& pDispScript ) { HRESULT hr = S_OK; CComPtr pOleClientSite = GetClientSte( ); if (pOleClientSite == NULL) { return E_FAIL; } CComPtr pOleContainer; hr = pOleClientSite->GetContainer(& pOleContainer ); ASSERT( SUCCEEDED( hr ) && pOleContainer ); CComPtr pHtmlDoc; hr = pOleContainer->QueryInterface( IID_IHTMLDocument, ( void** )&pHtmlDoc ); ASSERT( SUCCEEDED( hr ) && pHtmlDoc ); // Get the script object (this returns the script object, NOT the script // element(s) that the get_scripts method does). hr = pHtmlDoc->get_Script( &pDispScript ); ASSERT( SUCCEEDED( hr ) && pDispScript ); CComPtr pDispExScript; hr = pDispScript->QueryInterface( IID_IDispatchEx, ( void** )&pDispExScript ); ASSERT( SUCCEEDED( hr ) && pDispExScript ); pDispScript = pDispExScript; CComPtr pTypeInfo; hr = pDispScript->GetTypeInfo( 0, 0, &pTypeInfo ); return hr; } HRESULT PPInterface::HasProperty( CComPtr& pDispatch, CString& name ) { HRESULT hr = S_OK; if ( pDispatch == NULL ) { hr = GetHtmlDocDispatch( pDispatch ); } DISPID dispID; if ( SUCCEEDED( hr ) ) { hr = GetIdOfName( pDispatch, name, &dispID ); } return hr; } HRESULT PPInterface::CallMethod( CComPtr& pDispatch, CString& name, COleVariant& varResult, int numParams, COleVariant* params ) { HRESULT hr = S_OK; if ( pDispatch == NULL ) { hr = GetHtmlDocDispatch( pDispatch ); } if ( SUCCEEDED( hr ) ) { hr = Invoke( DISPATCH_METHOD, pDispatch, name, &varResult, numParams, params ); } return hr; } HRESULT PPInterface::GetProperty( CComPtr& pDispatch, CString& name, COleVariant& varResult ) { HRESULT hr = S_OK; if ( pDispatch == NULL ) { hr = GetHtmlDocDispatch( pDispatch ); } if ( SUCCEEDED( hr ) ) { hr = Invoke( DISPATCH_PROPERTYGET, pDispatch, name, &varResult, 0, NULL ); } return hr; } HRESULT PPInterface::SetProperty( CComPtr& pDispatch, CString& name, COleVariant& varValue, COleVariant& varResult ) { HRESULT hr = S_OK; if ( pDispatch == NULL ) { hr = GetHtmlDocDispatch( pDispatch ); } if ( SUCCEEDED( hr ) ) { if ( varValue.vt == VT_DISPATCH ) { hr = Invoke( DISPATCH_PROPERTYPUTREF, pDispatch, name, &varResult, 1, &varValue ); } else { hr = Invoke( DISPATCH_PROPERTYPUT, pDispatch, name, &varResult, 1, &varValue ); } } return hr; } HRESULT PPInterface::EvalExpression( CComPtr& pDispatch, CString& expression, COleVariant& varResult ) { HRESULT hr = S_OK; if ( pDispatch == NULL ) { hr = GetHtmlDocDispatch( pDispatch ); } if ( SUCCEEDED( hr ) ) { COleVariant param( expression ); hr = Invoke( DISPATCH_METHOD, pDispatch, CString("eval"), &varResult, 1, ¶m ); } return hr; } HRESULT PPInterface::P3DHasMethod( P3D_object* p3dObject, CString& name, bool& result ) { if ( !p3dObject ) { p3dObject = GetP3DObject( ); if ( !p3dObject ) { return E_FAIL; } } result = P3D_OBJECT_HAS_METHOD( p3dObject, name ); return S_OK; } HRESULT PPInterface::P3DCallMethod( P3D_object* p3dObject, CString& name, DISPPARAMS FAR* pdispparams, VARIANT FAR* varResult ) { if ( !p3dObject ) { p3dObject = GetP3DObject( ); if ( !p3dObject ) { return E_FAIL; } } P3D_object** params = new P3D_object*[pdispparams->cArgs]; for ( UINT i = 0; i < pdispparams->cArgs; i++ ) { // Reversing the arguments!!! // http://msdn.microsoft.com/en-us/library/cc237569(PROT.10).aspx // pDispParams: MUST point to a DISPPARAMS structure that defines the arguments passed to the method. // Arguments MUST be stored in pDispParams->rgvarg in reverse order, so that the first argument is // the one with the highest index in the array. Byref arguments MUST be marked in this array // as VT_EMPTY entries, and stored in rgVarRef instead. COleVariant vaArg( pdispparams->rgvarg[pdispparams->cArgs - 1 - i] ); params[i] = variant_to_p3dobj( &vaArg ); } bool needResult( false ); if ( varResult ) { needResult = true; } P3D_object* p3dObjectResult = P3D_OBJECT_CALL( p3dObject, name, needResult, params, pdispparams->cArgs ); if ( p3dObjectResult ) { p3dobj_to_variant( varResult, p3dObjectResult ); P3D_OBJECT_DECREF( p3dObjectResult ); } for ( UINT i = 0; i < pdispparams->cArgs; i++ ) { P3D_OBJECT_DECREF( params[i] ); } delete [] params; return S_OK; } HRESULT PPInterface::P3DGetProperty( P3D_object* p3dObject, CString& name, VARIANT FAR* varResult ) { if ( !p3dObject ) { p3dObject = GetP3DObject( ); if ( !p3dObject ) { return E_FAIL; } } P3D_object* p3dObjectResult = P3D_OBJECT_GET_PROPERTY( p3dObject, name ); if ( p3dObjectResult ) { p3dobj_to_variant( varResult, p3dObjectResult ); P3D_OBJECT_DECREF( p3dObjectResult ); } return S_OK; } HRESULT PPInterface::P3DSetProperty( P3D_object* p3dObject, CString& name, DISPPARAMS FAR* pdispparams, bool& result ) { if ( !p3dObject ) { p3dObject = GetP3DObject( ); if ( !p3dObject ) { return E_FAIL; } } COleVariant vaArg( pdispparams->rgvarg ); P3D_object* param = variant_to_p3dobj( &vaArg ); result = P3D_OBJECT_SET_PROPERTY( p3dObject, name, true, param ); P3D_OBJECT_DECREF( param ); if (!result) { return E_FAIL; } return S_OK; } void PPInterface::p3dobj_to_variant(VARIANT* result, P3D_object* object) { if ( !result ) { return; } switch ( P3D_OBJECT_GET_TYPE( object ) ) { case P3D_OT_undefined: { result->vt = VT_EMPTY; break; } case P3D_OT_none: { result->vt = VT_EMPTY; break; } case P3D_OT_bool: { result->vt = VT_BOOL; result->bVal = P3D_OBJECT_GET_BOOL( object ); break; } case P3D_OT_int: { result->vt = VT_I4; result->lVal = P3D_OBJECT_GET_INT( object ); break; } case P3D_OT_float: { result->vt = VT_R4; result->fltVal = P3D_OBJECT_GET_FLOAT( object ); break; } case P3D_OT_string: { int size = P3D_OBJECT_GET_STRING(object, NULL, 0); char *buffer = new char[size]; P3D_OBJECT_GET_STRING(object, buffer, size); int wsize = MultiByteToWideChar(CP_UTF8, 0, buffer, size, NULL, 0); WCHAR *wbuffer = new WCHAR[wsize + 1]; MultiByteToWideChar(CP_UTF8, 0, buffer, size, wbuffer, wsize); wbuffer[wsize] = 0; result->vt = VT_BSTR; result->bstrVal = SysAllocString(wbuffer); delete[] buffer; delete[] wbuffer; break; } case P3D_OT_object: { PPBrowserObject *ppBrowserObject = (PPBrowserObject *)object; PPandaObject* ppPandaObject = new PPandaObject( this, ppBrowserObject ); result->vt = VT_DISPATCH; result->pdispVal = ppPandaObject; break; } default: { result->vt = VT_EMPTY; break; } } } P3D_object* PPInterface::variant_to_p3dobj(COleVariant* variant) { if ( !variant ) { return P3D_new_none_object(); } switch( variant->vt ) { case VT_VOID: { return P3D_new_undefined_object(); break; } case VT_EMPTY: { // return P3D_new_none_object(); // A.T. Panda really expect undefined object here return P3D_new_undefined_object(); break; } case VT_BOOL: { return P3D_new_bool_object( variant->bVal ); break; } case VT_I2: { return P3D_new_int_object( variant->iVal ); break; } case VT_I4: { return P3D_new_int_object( variant->lVal ); break; } case VT_I8: { return P3D_new_int_object( variant->llVal ); break; } case VT_R4: { return P3D_new_float_object( variant->fltVal ); break; } case VT_R8: { return P3D_new_float_object( variant->dblVal ); break; } case VT_BSTR: { BSTR bstr = variant->bstrVal; UINT blen = SysStringLen(bstr); int slen = WideCharToMultiByte(CP_UTF8, 0, bstr, blen, 0, 0, NULL, NULL); char *sbuffer = new char[slen]; WideCharToMultiByte(CP_UTF8, 0, bstr, blen, sbuffer, slen, NULL, NULL); P3D_object *object = P3D_new_string_object(sbuffer, slen); delete[] sbuffer; return object; break; } case VT_DISPATCH: { // The following commented-out code crashes IE7: /* CComPtr pDispObject( variant->pdispVal ); CComPtr pTypeInfo; HRESULT hr = pDispObject->GetTypeInfo( 0, 0, &pTypeInfo ); if ( SUCCEEDED( hr ) && pTypeInfo ) { TYPEATTR* pTypeAttr; hr = pTypeInfo->GetTypeAttr( &pTypeAttr ); pTypeInfo->ReleaseTypeAttr( pTypeAttr ); } */ return new PPBrowserObject( this, variant->pdispVal ); break; } default: { return P3D_new_undefined_object(); break; } } } CString PPInterface::get_repr(COleVariant& variant) { std::strstream repr; repr << "IDispatch"; switch( variant.vt ) { case VT_VOID: { repr << ":VT_VOID"; break; } case VT_EMPTY: { repr << ":VT_EMPTY"; break; } case VT_BOOL: { repr << ":VT_BOOL:" << variant.bVal; break; } case VT_I2: { repr << ":VT_I2:" << variant.iVal; break; } case VT_I4: { repr << ":VT_I4:" << variant.lVal; break; } case VT_I8: { repr << ":VT_I8:" << variant.llVal; break; } case VT_R4: { repr << ":VT_R4:" << variant.fltVal; break; } case VT_R8: { repr << ":VT_R8:" << variant.dblVal; break; } case VT_BSTR: { CString str( variant ); repr << ":VT_BSTR:" << str; break; } case VT_DISPATCH: { if ( variant.pdispVal ) { CComPtr pDispObject( variant.pdispVal ); CComPtr pTypeInfo; HRESULT hr = pDispObject->GetTypeInfo( 0, 0, &pTypeInfo ); if ( SUCCEEDED( hr ) && pTypeInfo ) { TYPEATTR* pTypeAttr; hr = pTypeInfo->GetTypeAttr( &pTypeAttr ); if ( pTypeAttr ) { OLECHAR szGuid[40] = { 0 }; int nCount = ::StringFromGUID2( pTypeAttr->guid, szGuid, sizeof( szGuid ) ); CString guid( szGuid, nCount ); repr << ":VT_DISPATCH:GUID:" << guid; pTypeInfo->ReleaseTypeAttr( pTypeAttr ); } else { repr << ":VT_DISPATCH:" << variant.pdispVal; } } else { repr << ":VT_DISPATCH:" << variant.pdispVal; } } else { repr << ":UNKNOWN"; } break; } default: { repr << ":UNKNOWN"; break; } } return CString ( repr.str(), repr.pcount() ); }