DumontEXE 0.0.1
QAxFactory.cpp
00001 
00002 #include "../DumontEXE.h"
00003 
00004 #include "QAxFactory.h"
00005 
00006 #include <QObject>
00007 #include <QMetaMethod>
00008 
00009 static QPointer<QAxFactory> gAxFactory;
00010 QAxFactory * qAxFactory(void)
00011 {
00012   Q_ASSERT( !gAxFactory.isNull() ); // assert (and die) if factory is not assigned.
00013 
00014   return( gAxFactory );
00015 }
00016 
00017 
00018 #define DISPATCH_METHODGET (DISPATCH_METHOD+DISPATCH_PROPERTYGET)
00019 
00020 static const char * WFlags( WORD wFlags )
00021 {
00022   switch( wFlags )
00023   { case DISPATCH_METHOD        : return( "DISPATCH_METHOD"         );
00024     case DISPATCH_PROPERTYGET   : return( "DISPATCH_PROPERTYGET"    );
00025     case DISPATCH_PROPERTYPUT   : return( "DISPATCH_PROPERTYPUT"    );
00026     case DISPATCH_PROPERTYPUTREF: return( "DISPATCH_PROPERTYPUTREF" );
00027     case DISPATCH_METHODGET     : return( "DISPATCH_METHOD/GET"     );
00028   }
00029   return( "wFlags unknown" );
00030 }
00031 
00032 static QString ParseInputParams( QAxArgument * ga, VARIANTARG * va )
00033 {
00034   /*
00035   ** Act upon the var type that was passed in from the COM invoke
00036   **  function (originating from vbScript most likely) and parse that input
00037   **  value into something Qt can swallow.
00038   **
00039   */
00040   switch( va->vt )
00041   {
00042     /*
00043     ** vb VARIANT_BOOL -> Qt bool
00044     **
00045     */
00046     case VT_BOOL:
00047     {
00048       ga->set( (bool) va->boolVal );
00049       return("bool");
00050     }
00051 
00052     /*
00053     ** vb INT -> Qt int
00054     **
00055     */
00056     case VT_I2:
00057     {
00058       ga->set( (long) va->iVal );
00059       return("long");
00060     }
00061 
00062     case VT_I4:
00063     {
00064       ga->set( (long) va->lVal );
00065       return("long");
00066     }
00067 
00068     /*
00069     ** vb DBL -> Qt double
00070     **
00071     */
00072     case VT_R8:
00073     {
00074       ga->set( va->dblVal );
00075       return("double");
00076     }
00077 
00078     /*
00079     ** vb BSTR -> Qt QString
00080     **
00081     */
00082     case VT_BSTR:
00083     {
00084       ga->set( QString::fromWCharArray(va->bstrVal) );
00085       return("QString");
00086     }
00087 
00088     case VT_BYREF + VT_BSTR:
00089     {
00090       ga->set( QString::fromWCharArray(*va->pbstrVal) );
00091       return("QString");
00092     }
00093 
00094     /*
00095     ** vb DATE -> Qt QDate
00096     **
00097     */
00098     case VT_DATE:
00099     {
00100       BSTR dbuf; VarBstrFromDate( va->date, 0, 0, &dbuf );
00101       ga->set( QDateTime::fromString( QString::fromWCharArray(dbuf), "M/d/yyyy h:mm:ss AP" ) );
00102       return("QDateTime");
00103     }
00104 
00105     case VT_BYREF + VT_DATE:
00106     {
00107       BSTR dbuf; VarBstrFromDate( *va->pdate, 0, 0, &dbuf );
00108       ga->set( QDateTime::fromString( QString::fromWCharArray(dbuf), "M/d/yyyy h:mm:ss AP" ) );
00109       return("QDateTime");
00110     }
00111 
00112     /*
00113     ** vb IDISPATCH -> Qt QCocDispatch
00114     **
00115     ** BUGBUG: This thing needs to be subclassed so that we don't have it imbedded
00116     **  directly into the Factory components, and, rather, the application can
00117     **  override this interpretation.
00118     **
00119     */
00120     case VT_DISPATCH:
00121     {
00122       ga->set( va->pdispVal );
00123       return("IDispatch*");
00124     }
00125 
00126     /*
00127     ** vb VARIANT* -> vb VARIANT
00128     **
00129     ** Note that in a reference to another variant, simply call the same ParseInputParams
00130     **  method again with a pointer to the referenced variant.  It should convert the
00131     **  then dereferenced type correctly and there should be only one dereference step
00132     **  required.
00133     **
00134     */
00135     case VT_BYREF + VT_VARIANT:
00136     {
00137       return( ParseInputParams( ga, va->pvarVal ) );
00138     }
00139 
00140     /*
00141     ** This is an undefined translation.  For now it will pop-up a dialog window indicating
00142     **  the nature of the failure (as far as we're able to diagnose it here).
00143     **
00144     */
00145     default:
00146     {
00147       qDebug
00148       ( "va.vt 0x%x not handled",
00149         va->vt
00150       );
00151       return( "<invalid>" );
00152     }
00153 
00154   } // endswitch( COM_VARIANT_TYPE )
00155 
00156 } // endstatic QString ParseInputParams( DumontArgument & ga, VARIANTARG & va )
00157 
00158 
00159 
00160 
00161 long QComObject::s_instanceCount = 0;
00162 
00163 HRESULT __stdcall QComObject::QueryInterface( const IID& iid, void ** ppv )
00164 {
00165   /*
00166   ** Always wipe the callers reference pointer.  This way if we decide
00167   **  there's an error, then we can just return with that error code
00168   **  and not have to also worry about wiping his pointer.
00169   **
00170   */
00171   *ppv = NULL;
00172 
00173 //    qDebug( "QComObject(%s)::QueryInterface %s", d?d-> metaObject()-> className():"?", QUuid(iid).toString().toAscii().data() );
00174 
00175   /*
00176   ** Check the ID requested against known ID numbers for the Class 
00177   **  Factory.
00178   **
00179   */
00180   if( ::IsEqualIID( iid, IID_IUnknown  ) ||
00181       ::IsEqualIID( iid, IID_IDispatch ) )
00182   {
00183     /*
00184     ** The ID he requested from is good.  So, we can give him a pointer
00185     **  to us (which is what he requested).  Go ahead and cast this pointer
00186     **  to the IClassFactory type to be sure everything is laid out
00187     **  properly.  In other words, the .this. pointer might be ok, but if
00188     **  we cast .this. to a .IClassFactory. then we're sure it's ok.
00189     **
00190     */
00191     *ppv = (IDispatch*) this;
00192 
00193 //      if( ::IsEqualIID( iid, IID_IUnknown  ) ) qDebug("iid unknown");
00194 //      if( ::IsEqualIID( iid, IID_IDispatch ) ) qDebug("iid dispatch");
00195 
00196     /*
00197     ** Increment the reference counter to us.
00198     **
00199     */
00200     AddRef();
00201 
00202     /*
00203     ** Let him know the result was successful.
00204     **
00205     */
00206     return( S_OK );
00207 
00208   } // endif( ..valid ID requested.. )
00209 
00210   /*
00211   ** Something not working out as planned.
00212   **
00213   */
00214   return( E_NOINTERFACE );
00215 
00216 } // endvirtual HRESULT __stdcall QueryInterface( const IID& iid, void ** ppv )
00217 
00218 
00219 ULONG __stdcall QComObject::AddRef(void)
00220 {
00221   ::InterlockedIncrement(&m_ref);
00222 
00223 //     qDebug( "QComObject(%s)::AddRef %lu", d?d-> metaObject()-> className():"?", m_ref );
00224 
00225   return( m_ref );
00226 }
00227 
00228 ULONG __stdcall QComObject::Release(void)
00229 {
00230   ::InterlockedDecrement( &m_ref );
00231 
00232 //    qDebug( "QComObject(%s)::Release %lu", d?d-> metaObject()-> className():"?", m_ref );
00233 
00234   if( m_ref == 0 )
00235   {
00236     delete this;
00237     return 0;
00238   }
00239 
00240   return( m_ref );
00241 }
00242 
00243 LONG __stdcall QComObject::GetTypeInfoCount( UINT * o_count )
00244 {
00245 //    qDebug( "GetTypeInfoCount" );
00246 
00247   o_count = 0;
00248 
00249   return( S_OK );
00250 }
00251 
00252 LONG __stdcall QComObject::GetTypeInfo( UINT, LCID, ITypeInfo** )
00253 {
00254 //    qDebug( "GetTypeInfo" );
00255 
00256   return( S_OK );
00257 }
00258 
00259 HRESULT __stdcall QComObject::GetIDsOfNames
00260 (
00261   const IID & iid,
00262   BSTR      * arrayNames,
00263   UINT        countNames,
00264   LCID,         // Localization is not supported.
00265   DISPID    * arrayDispIDs
00266 )
00267 {
00268 //  qDebug( "QComObject(%s)::GetIDsOfNames", d?d-> metaObject()-> className():"?" );
00269 
00270   if( !::IsEqualIID( iid, IID_NULL ) )
00271   {
00272 //    TRAC("GetIDsOfNames call failed -- bad IID.");
00273     return DISP_E_UNKNOWNINTERFACE;
00274   }
00275 
00276   /*
00277   ** Try to find this method anywhere within the property list
00278   **  or method list or enum list until its found.
00279   **
00280   */
00281   QString name = QString::fromWCharArray(*arrayNames);
00282 
00283   /*
00284   ** If the name he is looking for is "IsValid" then
00285   **  we will implement a special override
00286   **
00287   */
00288   if( name.toLower() == "objectisvalid" )
00289   {
00290     arrayDispIDs[0] = 0x00020000;
00291     return( S_OK );
00292   }
00293 
00294   /*
00295   ** Make sure we're pointing to something.
00296   **
00297   */
00298   if( !d ) 
00299   {
00300     qCritical( "GetIDsOfNames no object pointer for call to '%s'.", name.toAscii().data() );
00301     return( E_FAIL );
00302   }
00303 
00304   /*
00305   ** Scan for methods
00306   **
00307   */
00308   for( int i=0; i < d-> metaObject()-> methodCount(); i++ )
00309   {
00310     /*
00311     ** Get the method signature out (includes the parameters)
00312     **
00313     */
00314     QString signature( d-> metaObject()-> method(i).signature() );
00315 
00316 //      qDebug( "method signature %s", signature.toAscii().data() );
00317 
00318     /*
00319     ** Reduce the signature down to just the method name - we have to
00320     **  do an exact match on that so we don't run into a situation where
00321     **  we're looking for something like 'var' and what we find is
00322     **  globalVar or some cosmo-portion.
00323     **
00324     */
00325     signature = signature.left(signature.indexOf('('));
00326 
00327 //      qDebug( "searching %s, %d %s", name.toAscii().data(), i, signature.toAscii().data() );
00328 
00329     /*
00330     ** Do a case-insensitive search.  Why?  Why not just use case-sensitive
00331     **  and give vbScript a little extension?  Because, vb, in all its infinite
00332     **  confusion will NOT send method name requests WITHOUT changing the case
00333     **  from your original source code.  To keep things interesting, it will even
00334     **  not do this randomly.  The method name 'scriptCode' will probably come
00335     **  unmolested, but 'FIELDNAME' and 'fieldname' and fIELDnAME' will ALWAYS
00336     **  (mostly) come across 'FieldName'... and don't ax me why.  Thanks Bill!
00337     **
00338     */
00339     if( signature.toLower() == name.toLower() )
00340     {
00341       arrayDispIDs[0] = 0x00010000 | i;
00342       return( S_OK );
00343     }
00344   }
00345 
00346   /*
00347   ** Scan for properties
00348   **
00349   */
00350 //  for( int i=0; i<d->metaObject()->propertyCount(); i++ )
00351 //  {
00352 //    QString property(d->metaObject()->property(i).name());
00353 //    if( property.indexOf(name,Qt::CaseInsensitive) != -1 )
00354 //    {
00355 //      arrayDispIDs[0] = 0x00020000 | i;
00356 //      return( S_OK );
00357 //    }
00358 //  }
00359 
00360   /*
00361   ** If nothing was found in the metaObject() try finally
00362   **  the type library.
00363   **
00364   */
00365   HRESULT hr = E_FAIL;
00366 //    if( m_ITypeInfo )
00367 //      hr = m_ITypeInfo->GetIDsOfNames
00368 //      ( arrayNames,
00369 //        countNames,
00370 //        arrayDispIDs
00371 //      );
00372 //    else
00373 //      qDebug( "no m_ITypeInfo" );
00374 
00375   if( hr != S_OK )
00376     qDebug( "GetIDsOfNames not finding '%s'.", name.toAscii().data() );
00377 
00378   return( hr );
00379 
00380 } // endHRESULT __stdcall QCosDumontDLL::GetIDsOfNames
00381 
00382 HRESULT __stdcall QComObject::Invoke
00383 (
00384   DISPID dispidMember,
00385   const IID & iid,
00386   LCID lcid,
00387   WORD wFlags,
00388   DISPPARAMS * pDispParams,
00389   VARIANT * pvr,
00390   EXCEPINFO * pExcepInfo,
00391   UINT * pArgErr
00392 )
00393 {
00394   if( !::IsEqualIID( iid, IID_NULL ) )
00395   {
00396     return DISP_E_UNKNOWNINTERFACE;
00397   }
00398 
00399   ::SetErrorInfo( 0, NULL );
00400 
00401   /*
00402   ** This is a special handler for the '.ObjectIsValid' property/function
00403   **  test.  It allows the client to test if the object pointer it received
00404   **  back actually points to something meaningful.  This is important for
00405   **  two reasons.  One, it may have received a good object pointer initially
00406   **  when it requested the object, but then if the object destroyed itself
00407   **  behind the scenes for some reason, thus making the object pointer go
00408   **  bad.  We can check for that here using this method without crashing
00409   **  the system.
00410   **
00411   ** \par vbScript Example
00412   ** \code
00413   ** dim dexe: set dexe = createObject("Dumont.EXE")
00414   ** dim app: set app = dexe.app( "KES.mwp", 1 )
00415   ** if( app is nothing ) then
00416   **   dexe.debug "app is nothing" 
00417   ** else
00418   **   dexe.debug "app is something" 
00419   **   if( app.ObjectisValid ) then           ' test for valid objects done here
00420   **     dexe.debug "app is valid"
00421   **   else
00422   **     dexe.debug "app is not valid"
00423   **   end if
00424   ** end if
00425   ** \endcode
00426   **
00427   */
00428   if( dispidMember == 0x00020000 )
00429   {
00430     pvr->vt = VT_BOOL; 
00431     pvr->boolVal = d? true:false;
00432     return( S_OK );
00433   }
00434 
00435   if( !d ) return( DISP_E_UNKNOWNINTERFACE );
00436 
00437   /*
00438   ** Assume we succeed and if anything craps out as we go then we just
00439   **  change the .hr. to suit.
00440   **
00441   */
00442   HRESULT hr = S_OK;
00443 
00444   if( dispidMember > 0x60000000 ) // typelib invoke
00445   {
00446 //      hr = m_ITypeInfo->Invoke
00447 //      (
00448 //        static_cast<IDispatch*>(this),
00449 //        dispidMember, 
00450 //        wFlags, 
00451 //        pDispParams,
00452 //        pvr, 
00453 //        pExcepInfo, 
00454 //        pArgErr
00455 //      ); 
00456 
00457   } // endif( typelib invoke )
00458 
00459   else // manual invoke
00460   {
00461     /*
00462     ** wFlags 
00463     **  DISPATCH_METHOD         == 1
00464     **  DISPATCH_PROPERTYGET    == 2
00465     **  DISPATCH_METHODGET      == 3
00466     **  DISPATCH_PROPERTYPUT    == 4
00467     **  DISPATCH_PROPERTYPUTREF == 8
00468     **
00469     */
00470     switch( dispidMember & 0xffff0000 )
00471     {
00472       /*
00473       ** When this value is set in the upper bytes of the dispidMember
00474       **  then we are signaling that this dispidMember is related to
00475       **  a Qt method implementation.
00476       **
00477       **
00478       */
00479       case 0x00010000: // Qt method
00480       {
00481         /*
00482         ** The method index is represented in the lower-4-bytes of the dispidMember.
00483         **  Get a handle to that method.
00484         **
00485         */
00486         QMetaMethod method( d-> metaObject()-> method(dispidMember&0xffff) );
00487 
00488         /*
00489         ** Get the methodName as a QByteArray so that we can use it for comparison
00490         **  operations.
00491         **
00492         */
00493         methodName = method.signature();
00494 
00495         /*
00496         ** The method name needs to be trimmed before we use it in the invoke method.
00497         **  But, before we do that, we're goingto use it to reassemble a new signature
00498         **  based upon the vb parameter list.  So, for now, include the first ( in the
00499         **  method signature.
00500         **
00501         */
00502         methodName = methodName.left(methodName.indexOf('(')+1);
00503 
00504         /*
00505         ** Clear the parameters.
00506         **
00507         */
00508         for( uint i=0; i<10; i++ )
00509           ga[i].clear();
00510 
00511         /*
00512         ** Pull the arguments out of the array passed in and stuff them one-by-one
00513         **  into the arguments for our QInvoke function.  Please note that the
00514         **  arguments come out of the passed-in array in reverse order.  In other 
00515         **  words, if there are 3 arguments, then the first one will be rgvarg[2]
00516         **  followed by rgvarg[1] followed by rgvarg[0].  Note that this really
00517         **  screwed up me up trying to get COM going for a long time - don't ax me
00518         **  why m$ did it that way!  Anyway, notice then that there are two index
00519         **  variables in use here, .i. for our regular Qt argument index, and .ai.
00520         **  for the "argument in" index.  The Qt index is incremented while the
00521         **  COM argument index is decremented.
00522         **
00523         ** Also, this loop will reassemble the method signature so that we can use
00524         **  it to find the correct return type for this method.
00525         **
00526         */
00527         if( pDispParams->cArgs > 0 )
00528         {
00529           int ai = pDispParams->cArgs;
00530           for( uint i=0; i<pDispParams->cArgs; i++ )
00531           {
00532             --ai;
00533             methodName.append( ParseInputParams( &ga[i], &pDispParams->rgvarg[ai] ) + "," );
00534 
00535           } // endfor( EVERY_COM_PARAMETER_IN )
00536 
00537           /*
00538           ** Chop off the trailing "," from the parameter list.
00539           **
00540           */
00541           methodName.truncate( methodName.length() - 1 );
00542 
00543         } // endif( ..parameters_exist.. )
00544 
00545         /*
00546         ** Add a closing paren.
00547         **
00548         */
00549         methodName.append( ")" );
00550 
00551         /*
00552         ** Given that we now have a method signature based upon the vb parameters,
00553         **  see if we can find a match in this Qt object.
00554         **
00555         */
00556         int methodIdx = d-> metaObject()-> indexOfMethod( methodName );
00557 
00558         /*
00559         ** If the index is bad, then we were unable to find any matching methods
00560         **  and we need to quit with an error.
00561         **
00562         */
00563         if( methodIdx == -1 )
00564         {
00565           qDebug
00566           ( 
00567             "%s:%d:Invoke fail: %s(\"%s\").%s' not found", 
00568             __FILE__, 
00569             __LINE__, 
00570             d-> metaObject()-> className(),
00571             qPrintable(d-> objectName()), methodName.data() 
00572           );
00573           return( E_FAIL );
00574         }
00575 
00576         /*
00577         ** The methodTypeName represents the text-equivalent of the method return
00578         **  value.
00579         **
00580         */
00581         methodTypeName = d-> metaObject()-> method(methodIdx).typeName();
00582 
00583 
00584 
00585 #ifdef __SHOW_METHOD_CALL__
00586   QString errm = (methodTypeName==""?"void":methodTypeName) + " " + 
00587       d-> metaObject()-> className() + "::" + 
00588       methodName.left( methodName.indexOf("(") ) + 
00589       "(";
00590   for( int i=0; i<10; i++ )
00591   { if( ga[i].isValid() )
00592     { if( i>0 ) errm += ",";
00593       errm += (QString(ga[i].typeName()) + QString("=") + ga[i].toString());
00594     }
00595   }
00596   errm += ");";
00597   qDebug
00598   ( "invoke: %s",
00599     errm.toAscii().data()
00600   );
00601 #endif
00602 
00603         /*
00604         ** This procedure allows us to call a method using the object-meta-class 
00605         **  method-invoke method, using a re-entrant method, and also be able
00606         **  to receive return values.  The key here is Qt widget slots cannot be
00607         **  called from other threads, and Qt also does not know that when a
00608         **  COM method is being called from the operating system, that it is,
00609         **  in fact, a different thread.  So, we "invent" a new thread mechanism
00610         **  and make our calls through that, effectively informing Qt that a
00611         **  thread switch has taken place.
00612         **
00613         */
00614         QComCallback callback(this);
00615         callback.start();
00616         while( callback.isRunning() )
00617           qApp-> processEvents();
00618 
00619         /*
00620         ** If there was a problem with the call then disassemble all the parameters
00621         **  as they were used and format them into a RealFunctionName( withParams );
00622         **  so we can post that as a realistic error message, and help the user of this
00623         **  thing understand what might have gone wrong.
00624         **
00625         ** This routine could also scan the QMetaData info and list all methods that 
00626         **  look similar, and report them kind of the way the gcc compiler does when
00627         **  a function is requested that can't quite be found.
00628         **
00629         */
00630         if( !invokeResult )
00631         {
00632           QString errm = methodTypeName + " " + methodName + "(";
00633           for( int i=0; i<10; i++ )
00634           { if( ga[i].isValid() )
00635             { if( i>0 ) errm += ",";
00636               errm += (QString(ga[i].typeName()) + QString("=") + ga[i].toString());
00637             }
00638           }
00639           errm += ");";
00640           qDebug
00641           ( "invoke fail method '%s'::0x%lx(%ld), wFlags=(%x %s)",
00642             errm.toAscii().data(),
00643             dispidMember,
00644             dispidMember&0xffff,
00645             wFlags,
00646             WFlags(wFlags)
00647           );
00648 
00649           /*
00650           ** Indicate a failure - we're not entirely sure why, though.
00651           **
00652           */
00653           return( E_FAIL );
00654 
00655         }
00656         else // if( result ok )
00657         {
00658           /*
00659           ** If there is no pvr then the caller doesn't want a return value.
00660           **
00661           */
00662           if( !pvr ) return( S_OK );
00663 
00664 //          qDebug( "constructing return value for %s", methodTypeName.data() );
00665 
00666           /*
00667           ** Get the return type out of the answer and place it in the pointer
00668           **  to the var-result (pvr) that we were given by COM (aka vbScript).
00669           **
00670           */
00671           if( methodTypeName == "bool"      ) { pvr->vt = VT_BOOL; pvr->boolVal = ra.ra_bool; }
00672           if( methodTypeName == "long"      ) { pvr->vt = VT_I4;   pvr->lVal    = ra.ra_long; }
00673           if( methodTypeName == "double"    ) { pvr->vt = VT_R8;   pvr->dblVal  = ra.ra_double; }
00674           if( methodTypeName == "QString"   )
00675           { 
00676             /*
00677             ** BUGBUG: I can never keep straight when a string
00678             **  is to have a vbCrLf or a vbLf or a vbCr or a combination therin!  It drives me
00679             **  nuts!!!  This code was intended to clean up strings from Qt that didn't have
00680             **  properly formatted vbCrLf strings because various programs will tolerate these
00681             **  extra line terminators and others will not.  This just ended up creating a mess
00682             **  when trying to format an outlook docket item.  It was coming back with a 0x0a
00683             **  and then this code was turning that into a 0x0d0a, and then, somehow, by the 
00684             **  time the thing got written to disk it became a 0x0d0d0a!!!! go figure!  I'll 
00685             **  leave it alone until I figure out what's needed and where.
00686             **
00687             */
00688 //            qDebug( "before:" );
00689 //            dumont::dumpString( ra.ra_QString );
00690             QString vbStr = /* dumont::vbString( */ ra.ra_QString /*)*/ ;
00691 //            qDebug( "after" );
00692 //            dumont::dumpString( vbStr );
00693 
00694             pvr->vt = VT_BSTR; pvr->bstrVal = ::SysAllocString( (const OLECHAR*) vbStr.utf16() ); 
00695           }
00696           if( methodTypeName == "QDate"     )
00697           { 
00698             QString qDateString = ra.ra_QDate.toString("M/d/yyyy");
00699             double newDate;
00700             VarDateFromStr( (OLECHAR*) qDateString.utf16(), 0, 0, &newDate );
00701             pvr->vt = VT_DATE;
00702             pvr->date = newDate;
00703           }
00704 
00705           if( methodTypeName == "QTime" )
00706           {
00707             QString qDateString = ra.ra_QTime.toString("h:mm:ss AP");
00708             double newDate;
00709             VarDateFromStr( (OLECHAR*) qDateString.utf16(), 0, 0, &newDate );
00710             pvr->vt = VT_DATE;
00711             pvr->date = newDate;
00712           }
00713 
00714           if( methodTypeName == "QDateTime" )
00715           {
00716             QString qDateString = ra.ra_QDateTime.toString("M/d/yyyy h:mm:ss AP");
00717             double newDate;
00718             VarDateFromStr( (OLECHAR*) qDateString.utf16(), 0, 0, &newDate );
00719             pvr->vt = VT_DATE;
00720             pvr->date = newDate;
00721           }
00722 
00723           /*!
00724           ** \todo This function needs some major improvement.  It's trying to convert
00725           **  a QVariant into a vbScript variant.  Not sure how to do that.  For the 
00726           **  moment, we'll just convert it to a string, but that may not be practical
00727           **  as the functionality of this API grows.
00728           **
00729           */
00730           if( methodTypeName == "QVariant" )
00731           {
00732             QString vbStr = ra.ra_QVariant.toString() /*)*/ ;
00733             pvr->vt = VT_BSTR; pvr->bstrVal = ::SysAllocString( (const OLECHAR*) vbStr.utf16() ); 
00734           }
00735 
00736 // BUGBUG: work needs to be done here
00737           if( methodTypeName.endsWith('*') )
00738           {
00739             pvr->vt = VT_DISPATCH;
00740 
00741             if( ra.rp_void )
00742             {
00743               QComObject * wrapper = new QComObject( (QObject*)ra.rp_void );
00744               pvr->pdispVal = (IDispatch*) wrapper;
00745             }
00746             else
00747             {
00748               pvr->pdispVal = NULL;
00749             }
00750           }
00751 
00752         }
00753 
00754         break;
00755 
00756       } // endcase 0x00010000: // Qt method
00757 
00758       case 0x00020000: // Qt property
00759       {
00760 //            qDebug( "qt property" );
00761     //        qDebug( "cArgs %d", pDispParams->cArgs );     // Number of arguments
00762     //  param.rgvarg            = varg;  // Arguments
00763     //        qDebug( "cNamedArgs %d", pDispParams->cNamedArgs );     // Number of named args
00764     //  param.rgdispidNamedArgs = NULL;  // Named arguments
00765 
00766         QMetaProperty property( d->metaObject()->property(dispidMember & 0xffff) );
00767     //        const char * propName = property.name();
00768 
00769         switch( wFlags )
00770         {
00771           case DISPATCH_METHOD:
00772           {
00773 //                qDebug( "property dispatch for %s", property.name() );
00774             break;
00775           }
00776 
00777           case DISPATCH_PROPERTYGET:
00778           {
00779 //                qDebug( "propertyget for %s", property.name() );
00780 
00781             QVariant vr = d->property(property.name());
00782 
00783 //                qDebug( "varResult for %s = %d", property.name(), vr.type() );
00784 
00785              switch( vr.type() )
00786              {
00787                case QVariant::Bool:        { pvr->vt = VT_BOOL; pvr->boolVal = vr.toBool();           break; }
00788                case QVariant::Char:        { pvr->vt = VT_I1;   pvr->cVal    = vr.toChar().toAscii(); break; }
00789 //               case QVariant::Date:
00790 //               case QVariant::DateTime:
00791 //               case QVariant::Time:        { pvr->vt = VT_DATE; pvr->date    = vr.
00792                case QVariant::Double:      { pvr->vt = VT_R8;   pvr->dblVal  = vr.toDouble();  break; }
00793                case QVariant::Int:         { pvr->vt = VT_I2;   pvr->iVal    = vr.toInt();     break; }
00794                case QVariant::UInt:        { pvr->vt = VT_UINT; pvr->uintVal = vr.toUInt();    break; }
00795 
00796                case QVariant::String:
00797                case QVariant::StringList:
00798                {
00799                  pvr->vt = VT_BSTR;
00800                  pvr->bstrVal = ::SysAllocString( (const OLECHAR*) vr.toString().utf16() );
00801                  break;
00802                }
00803 
00804                default: qDebug( "Invoke:type not handled" );
00805              }
00806 
00807             break;
00808 
00809           } // case DISPATCH_PROPERTYGET:
00810 
00811           case DISPATCH_PROPERTYPUT:
00812           {
00813 //                qDebug( "propertyput for %s", property.name() );
00814 
00815           int ai = pDispParams->cArgs;
00816 //          qDebug( "%d args", ai );
00817           for( uint i=0; i<pDispParams->cArgs; i++ )
00818           {
00819             switch( pDispParams->rgvarg[--ai].vt )
00820             {
00821               case VT_BSTR:
00822               {
00823 //                qDebug( "BSTR in" );
00824     //            ga[i] = Q_ARG(QString, QString::fromWCharArray(pDispParams->rgvarg[ai].bstrVal) );
00825                 break;
00826               }
00827 
00828               default:
00829               {
00830 //                qDebug( "rgvarg[%d].vt %d not handled", ai, pDispParams->rgvarg[ai].vt );
00831                 break;
00832               }
00833             }
00834           }
00835 
00836 
00837             d->setProperty( property.name(), QVariant() );
00838             break;
00839 
00840           } // endcase DISPATCH_PROPERTYPUT:
00841 
00842           case DISPATCH_PROPERTYPUTREF:
00843           {
00844 //            qDebug( "propertyputref for %s", property.name() );
00845             break;
00846 
00847           } // endcase DISPATCH_PROPERTYPUTREF:
00848 
00849         } // endswitch( wFlags )
00850 
00851         break;
00852 
00853       } // endcase 0x00020000: // property
00854 
00855     } // endswitch( dispidMember & 0xffff0000 )
00856 
00857   } // endelse manual invoke
00858 
00859   return hr;
00860 
00861 } // endHRESULT __stdcall QCosDumontDLL::Invoke
00862 
00863 /*
00864 ** This constructs a COM object that acts as an interface wrapper for a regular
00865 **  qt object.  It also increments a global object counter so that this package
00866 **  can know how many objects exist.
00867 ** 
00868 */
00869 QComObject::QComObject( QObject * object, bool destroyObject )
00870 {
00871 //  qDebug( "co p %s %p", qPrintable(object-> objectName()), object-> parent() );
00872 
00873   s_instanceCount++;
00874   m_ref = 0;
00875   d = object;
00876   m_destroyObject = destroyObject;
00877 
00878   /*
00879   ** Reparent the object to us, so that if the object handed back to
00880   **  us does not have a parent, it will be destroyed when we get 
00881   **  destroyed.
00882   **
00883   */
00884   if( object )
00885   {
00886 //    qDebug( "object %s %s %p", object-> metaObject()-> className(), qPrintable(object-> objectName()), object-> parent() );
00887     if( !object-> parent() )
00888     {
00889       int i = object-> metaObject()-> indexOfClassInfo("DestroyObject");
00890       if( i == -1 )
00891       {
00892   //      qDebug( "reparenting" );
00893         object-> setParent(this);
00894       }
00895     }
00896   }
00897 
00898 //  qDebug("QComObject(%s,%s,%ld)", d-> metaObject()-> className(), destroyObject?"true":"false", s_comObjectCount );
00899 
00900 }
00901 
00902 
00903 /*
00904 ** BUGBUG: this destructor is doing a poor job of destroying the objects
00905 **  created by this COM interface.  So we've hacked into it to not destroy
00906 **  the main window of the application, since that one particular object
00907 **  has no parent.  All the rest of the objects should have a parent so it
00908 **  should be no problem.
00909 **
00910 */
00911 QComObject::~QComObject()
00912 {
00913 //  qDebug( "QComObject::~QComObject" );
00914 
00915   s_instanceCount--;
00916 
00917   /*
00918   ** If we don't have a d pointer, we return
00919   **
00920   */
00921   if( !d ) return;
00922 
00923   /*
00924   ** Look for a class info
00925   **
00926   */
00927   int i = d-> metaObject()-> indexOfClassInfo("DestroyObject");
00928 
00929   /*
00930   ** If there is a class info, then we have to check to see if
00931   **  it indicates we should delete or not.
00932   **
00933   */
00934   if( i != -1 )
00935   {
00936     /*
00937     ** We have a "DestroyObject" class info.  If it says 'no', then
00938     **  we are not going to destroy it.  If it doesn't say no, then
00939     **  it says something else, probably 'yes' in which case we
00940     **  are going to destroy it.
00941     **
00942     */
00943     if( QString( d-> metaObject()-> classInfo(i).value() ) == "no" ) return;
00944   }
00945   else
00946   {
00947     /*
00948     ** Normally we don't delete objects when they have a parent.  However,
00949     **  the "DestroyObject" class info can override this condition. 
00950     **
00951     */
00952     if( d-> parent() ) return;
00953   }
00954 
00955   /*
00956   ** None of the tests indicate we shouldn't delete.  So, go ahead and delete.
00957   **
00958   */
00959 //  qDebug( "~QComObject::destroying(%s,%ld)", d-> metaObject()-> className(), s_comObjectCount );
00960   delete d;
00961 
00962 }
00963 
00964 long QComObject::instanceCount()
00965 {
00966   return( s_instanceCount );
00967 }
00968 
00969 
00970 QComCallback::QComCallback( QComObject * i_callback )
00971 :
00972   QThread()
00973 {
00974   m_callback = i_callback;
00975 }
00976 
00977 void QComCallback::run(void)
00978 {
00979   if( QMetaObject::invokeMethod
00980   (
00981     m_callback,
00982     "mainThreadInvoke",
00983     Qt::BlockingQueuedConnection
00984   ) )
00985   {
00986     // call succeeded
00987   }
00988   else
00989   {
00990     // call failed
00991   }
00992 }
00993 
00994 
00995 
00996 /*!
00997 ** \brief Qt Class Factory
00998 **
00999 ** This is the class factory that gets exposed to Windows.  It takes care
01000 **  of dispatching createObject calls to our 'real' factory (the one exposed
01001 **  to the rest of the Qt application).
01002 **
01003 */
01004 class QClassFactory
01005 :
01006   public IClassFactory
01007 {
01008   public:
01009 
01010     // IUnknown
01011 
01012     /*!
01013     ** \brief Standard QueryInterface
01014     **
01015     ** This procedure is pretty boiler-plate.  It simply checks the
01016     **  requested ID from the caller, and it must either be the standard
01017     **  IUnknown interface or the IClassFactory interface to be awarded
01018     **  a reply.
01019     **
01020     */
01021     HRESULT __stdcall QueryInterface( const IID& iid, void ** ppv )
01022     {
01023       /*
01024       ** Always wipe the callers reference pointer.  This way if we decide
01025       **  there's an error, then we can just return with that error code
01026       **  and not have to also worry about wiping his pointer.
01027       **
01028       */
01029       *ppv = NULL;
01030 
01031 //      qDebug( "QClassFactory::QueryInterface %s", QUuid(iid).toString().toAscii().data() );
01032 
01033       /*
01034       ** Check the ID requested against known ID numbers for the Class 
01035       **  Factory.
01036       **
01037       */
01038       if( (::IsEqualIID( iid, IID_IUnknown ))      ||
01039           (::IsEqualIID( iid, IID_IClassFactory )) )
01040       {
01041         /*
01042         ** The ID he requested from is good.  So, we can give him a pointer
01043         **  to us (which is what he requested).  Go ahead and cast this pointer
01044         **  to the IClassFactory type to be sure everything is laid out
01045         **  properly.  In other words, the .this. pointer might be ok, but if
01046         **  we cast .this. to a .IClassFactory. then we're sure it's ok.
01047         **
01048         */
01049         *ppv = (IClassFactory*) this;
01050 
01051 //        if( ::IsEqualIID( iid, IID_IUnknown      ) ) qDebug("iid unknown");
01052 //        if( ::IsEqualIID( iid, IID_IClassFactory ) ) qDebug("iid class factory");
01053 
01054         /*
01055         ** Increment the reference counter to us.
01056         **
01057         */
01058         AddRef();
01059 
01060         /*
01061         ** Let him know the result was successful.
01062         **
01063         */
01064         return( S_OK );
01065 
01066       } // endif( ..valid ID requested.. )
01067 
01068       /*
01069       ** Something not working out as planned.
01070       **
01071       */
01072       return( E_NOINTERFACE );
01073 
01074     } // endvirtual HRESULT __stdcall QueryInterface( const IID& iid, void ** ppv )
01075 
01076 
01077     /*!
01078     ** \brief Add Reference
01079     **
01080     ** Since we are a factory, we never get deleted, because we have been registered
01081     **  with the windows operating system... and therefore we must remain static.  Reference
01082     **  counting, though required, isn't done here.
01083     */
01084     ULONG __stdcall AddRef(void)
01085     {
01086       return( 1 );
01087     }
01088 
01089     /*!
01090     ** \brief Release Reference
01091     **
01092     ** Do *no* reference counting.
01093     **
01094     ** /see AddRef
01095     **
01096     */
01097     ULONG __stdcall Release(void)
01098     {
01099       return( 1 );
01100     }
01101 
01102     // IClassFactory
01103 
01104     /*!
01105     ** \brief Create COM object Instance by ID
01106     **
01107     ** This function gets called by the hosting application to create an
01108     **  instance of a specific COM object by way of its ID.
01109     **
01110     */
01111     HRESULT __stdcall CreateInstance
01112     (
01113             IUnknown *  pUnkOuter, 
01114       const IID      &  iid,
01115       void           ** ppv
01116     )
01117     {
01118       /*
01119       ** Assume an error by clearing the callers handle.  If things
01120       **  work out then ppv will get set to something meaningful.
01121       **
01122       */
01123       *ppv = NULL;
01124 
01125 //      qDebug( "QClassFactory::CreateInstance %s", QUuid(iid).toString().toAscii().data() );
01126 
01127       /*
01128       ** We are not supporting aggregation (yet) so return
01129       **  the appropriate error code.
01130       **
01131       */
01132       if( pUnkOuter )
01133       {
01134         return( CLASS_E_NOAGGREGATION );
01135       }
01136 
01137 //      qDebug( "createInstance" );
01138       DumontEXE * dexe = DumontEXE::createObject(NULL);
01139 //      qDebug( "dexe p1 %p", dexe-> parent() );
01140 //      qDebug( "done createInstance" );
01141 
01142 //      qDebug( "createComObject" );
01143       QComObject * object = new QComObject( dexe, true );
01144 //      qDebug( "dexe p2 %p", dexe-> parent() );
01145 //      qDebug( "done createComObject" );
01146 
01147 
01148       HRESULT hr = object->QueryInterface( iid, ppv );
01149       if( FAILED(hr) )
01150       {
01151         object->Release();
01152       }
01153 
01154       /*
01155       ** We should have returned above with no error, so if we
01156       **  got down here then return thusly.
01157       **
01158       */
01159       return( hr );
01160 
01161     } // endvirtual HRESULT __stdcall CreateInstance
01162 
01163     HRESULT __stdcall LockServer( BOOL lock )
01164     {
01165       if( lock ) ::InterlockedIncrement( &s_serverLocks );
01166       else       ::InterlockedDecrement( &s_serverLocks );
01167 
01168 //      qDebug( "QClassFactory::LockServer %s", lock? "True":"False" );
01169 
01170       #ifdef _OUTPROC_SERVER_
01171         if( !s_serverLocks )
01172         {
01173 //          CloseExe();  //@local
01174         }
01175       #endif
01176 
01177       return NOERROR;
01178     }
01179 
01180     // Constructor
01181 
01182     /*
01183     ** This constructor is a hack job.  While it accepts a pointer to the meta
01184     **  object, and is capable of performing the CoRegisterClassObject function
01185     **  it is still a hack job.  We need a wrapper for a QObject that can provide
01186     **  the IDispatch interface as well as a IClassFactory for that object.
01187     **
01188     */
01189     QClassFactory( const QMetaObject * metaObject )
01190     {
01191 //      qDebug("QClassFactory::QClassFactory");
01192 
01193       s_serverLocks = 0;
01194 
01195       m_metaObject = metaObject;
01196 
01197       QUuid clsid;
01198 //      qDebug( "ClassInfoCount=%d", metaObject-> classInfoCount() );
01199       for( int i=0; i<metaObject-> classInfoCount(); i++ )
01200       {
01201         QMetaClassInfo classInfo = metaObject->classInfo(i);
01202         if( QString("ClassID") == classInfo.name() )
01203         {
01204           clsid = classInfo.value();
01205         }
01206       }
01207 
01208       if( !clsid.isNull() )
01209       {
01210         HRESULT hr = ::CoRegisterClassObject
01211         (
01212           clsid,
01213           static_cast<IClassFactory*>( this ),
01214           CLSCTX_LOCAL_SERVER,
01215           REGCLS_MULTIPLEUSE,
01216 //          REGCLS_MULTI_SEPARATE, 
01217           &dwRegister
01218         );
01219 
01220         if (FAILED(hr))
01221         {
01222 //          return false;
01223         }
01224       }
01225     }
01226 
01227     virtual ~QClassFactory()
01228     {
01229       ::CoRevokeClassObject( dwRegister );
01230 //      qDebug("~QClassFactory");
01231     }
01232 
01233   protected:
01234 
01235   private:
01236 
01237     const QMetaObject * m_metaObject;
01238 
01239     static LONG s_serverLocks;
01240 
01241     DWORD dwRegister;
01242 
01243 }; // endclass QClassFactory
01244 
01245 LONG QClassFactory::s_serverLocks  = 0;
01246 
01247 
01248 
01249 
01250 
01251 
01252 
01253 
01254 
01255 QAxFactory::QAxFactory( const QUuid & libid, const QUuid & appid )
01256 {
01257 //  qDebug("QAxFactory()");
01258 
01259   /*
01260   ** Assert (and die) if the factory is already assigned.
01261   **
01262   */
01263   Q_ASSERT( gAxFactory.isNull() );
01264 
01265   /*
01266   ** Initialize COM
01267   **
01268   */
01269   ::CoInitialize(NULL);
01270 
01271   /*
01272   ** Set the global factory pointer.
01273   **
01274   */
01275   gAxFactory = this;
01276 
01277 } // endQAxFactory::QAxFactory( const QUuid & libid, const QUuid & appid )
01278 
01279 QAxFactory::~QAxFactory( void )
01280 {
01281 //  qDebug("~QAxFactory()");
01282 
01283   /*
01284   ** Kill the global factory reference pointer.
01285   **
01286   */
01287   gAxFactory = NULL;
01288 
01289   /*
01290   ** Shutdown COM
01291   **
01292   */
01293   ::CoUninitialize();
01294 
01295 }
01296 
01297 QUuid QAxFactory::appID() const
01298 {
01299   return( QUuid() );
01300 }
01301 
01302 QUuid QAxFactory::classID( const QString & key ) const
01303 {
01304   return( QUuid() );
01305 }
01306 
01307 bool QAxFactory::createObjectWrapper( QObject * object, IDispatch ** wrapper )
01308 {
01309   return( false );
01310 }
01311 
01312 QUuid QAxFactory::eventsID( const QString & key ) const
01313 {
01314   return( QUuid() );
01315 }
01316 
01317 QString QAxFactory::exposeToSuperClass( const QString & key ) const
01318 {
01319   return( QString() );
01320 }
01321 
01322 bool QAxFactory::hasStockEvents( const QString & key ) const
01323 {
01324   return( false );
01325 }
01326 
01327 QUuid QAxFactory::interfaceID( const QString & key ) const
01328 {
01329   return( QUuid() );
01330 }
01331 
01332 bool QAxFactory::isService( void ) const
01333 {
01334   return( false );
01335 }
01336 
01337 void QAxFactory::registerClass( const QString & key, QSettings * settings ) const
01338 {
01339 
01340 }
01341 
01342 bool QAxFactory::stayTopLevel( const QString & key ) const
01343 {
01344   return( false );
01345 }
01346 
01347 QUuid QAxFactory::typeLibID( ) const
01348 {
01349   return( QUuid() );
01350 }
01351 
01352 void QAxFactory::unregisterClass( const QString & key, QSettings * settings ) const
01353 {
01354 
01355 }
01356 
01357 bool QAxFactory::validateLicenseKey( const QString & key, const QString & licenseKey ) const
01358 {
01359   return( false );
01360 }
01361 
01362 
01363 // static functions
01364 
01365 bool QAxFactory::isServer( void )
01366 {
01367   return( false );
01368 }
01369 
01370 bool QAxFactory::registerActiveObject( QObject * object )
01371 {
01372   return( false );
01373 }
01374 
01375 QString QAxFactory::serverDirPath( void )
01376 {
01377   #ifdef _OUTPROC_SERVER_
01378     return( QApplication::applicationDirPath() );
01379   #else
01380     return( QString() );
01381   #endif
01382 }
01383 
01384 QString QAxFactory::serverFilePath( void )
01385 {
01386   #ifdef _OUTPROC_SERVER_
01387     return( QApplication::applicationFilePath() );
01388   #else
01389     return( QString() );
01390   #endif
01391 }
01392 
01393 QClassFactory * m_classFactory;
01394 
01395 bool QAxFactory::startServer( ServerType type )
01396 {
01397   m_classFactory = new QClassFactory( &DumontEXE::staticMetaObject );
01398 
01399   return( true );
01400 }
01401 
01402 bool QAxFactory::stopServer( void )
01403 {
01404   if( m_classFactory ) delete m_classFactory;
01405 
01406   m_classFactory = NULL;
01407 
01408   return( false );
01409 }
01410 
01411 void QAxFactory::invoke( void * comObject )
01412 {
01413 }
01414 
01415 
01416 
01417 #ifdef _INPROC_SERVER_
01418 
01419 //
01420 // Get class factory
01421 //
01422 STDAPI DllGetClassObject
01423 (
01424   const CLSID &  clsid,
01425   const IID   &  iid,
01426   void        ** ppv
01427 )
01428 {
01429   return( qAxFactory()-> GetClassObject( clsid, iid, ppv ) );
01430 
01431   return retVal;
01432 
01433 } // endSTDAPI DllGetClassObject
01434 
01435 
01436 
01437 #endif
01438 
 All Classes Namespaces Functions Variables Enumerations Enumerator Properties




~ ~ ~ ~ ~ ~
Source Code without Comments is like a Cranberry Garland
without the berries. Comment your Code!
 
Commence Database User Support Group Forum
http://newsgroup.showoff-db.org/
~ ~ ~ ~ ~ ~
Author: Mark Petryk
Lorimark Solutions, LLC
mark@lorimarksolutions.com