DumontEXE 0.0.1
QAxFactory.h
00001 #ifndef QAXFACTORY_H
00002 #define QAXFACTORY_H
00003 
00004 // for COM support
00005 #include <objbase.h>
00006 #include <QObject>
00007 
00008 #include <QApplication>
00009 #include <QUuid>
00010 #include <QString>
00011 #include <QSettings>
00012 #include <QStringList>
00013 #include <QPointer>
00014 #include <QThread>
00015 #include <QDate>
00016 #include <QWaitCondition>
00017 #include <QVariant>
00018 
00019 class QUuid;
00020 class QString;
00021 class QSettings;
00022 class QStringList;
00023 class QUuid;
00024 
00025 class QClassFactory;
00026 
00027 /*!
00028 ** \brief convert a variant to a GenericArgument
00029 **
00030 ** This macro was created to handle a scoping issue with QGenericArgument
00031 **  when creating them inside a loop.  This allows us to create a variant
00032 **  outside the loop, populate it, and then convert each element back to
00033 **  a GenericArgument when we send it to invokeMethod.
00034 **
00035 */
00036 //class DumArgument
00037 //{
00038 //  public:
00039 //    inline DumArgument( const char *aName = 0, const void *aData = 0)
00040 //      : _name(aName), _data(aData) {}
00041 //    inline void *data(void) const { return const_cast<void *>(_data); }
00042 //    inline const char *name(void) const { return _name; }
00043 //
00044 //    const void *_data;
00045 //    const char *_name;
00046 //};
00047 
00048 class QAxArgument
00049 {
00050   public:
00051 
00052     QAxArgument()
00053     : m_valid(false) { m_var.clear(); }
00054 
00055     void clear(void)            { m_valid = false; m_var.clear(); }
00056     void set( bool        val ) { m_var.setValue( val ); }
00057     void set( long        val ) { m_var.setValue( val ); }
00058     void set( double      val ) { m_var.setValue( val ); }
00059     void set( QString     val ) { m_var.setValue( val ); }
00060     void set( QDate       val ) { m_var.setValue( val ); }
00061     void set( QTime       val ) { m_var.setValue( val ); }
00062     void set( QDateTime   val ) { m_var.setValue( val ); }
00063     void set( IDispatch * val )
00064     {
00065       m_name      = "IDispatch *";
00066       m_IDispatch = val;
00067       m_valid     = true;
00068     }
00069 
00070     const bool  isValid(void)
00071     {
00072       if( m_var.isValid() ) return( true );
00073       return( m_valid );
00074     }
00075 
00076     const void *constData(void) 
00077     {
00078       if( m_var.isValid() ) return( m_var.constData() );
00079       return( & m_IDispatch );
00080     }
00081 
00082     const char *typeName(void)
00083     {
00084       if( m_var.isValid() ) return( m_var.typeName() );
00085       return( m_name );
00086     }
00087 
00088     QString toString(void)
00089     {
00090       if( m_var.isValid() ) return( m_var.toString() );
00091       return( QString("IDispatch *") );
00092     }
00093 
00094     QVariant     m_var;
00095     const char * m_name;
00096     const void * m_data;
00097     IDispatch  * m_IDispatch;
00098     bool         m_valid;
00099 
00100 }; // endclass DumontArgument
00101 
00102 /*
00103 ** This is a single object that will return a pointer to a data type
00104 **  capable of storing the kind of data provided for by the type
00105 **  name given.  Right now this is tied to custom object, and we
00106 **  need to eliminate that dependency.
00107 **
00108 */
00109 class QAxReturnArgument
00110 {
00111   public:
00112 
00113   void * data( const char * name )
00114   {
00115     if( !qstrcmp(name,""                       ) ) { return( &ra_bool      ); }
00116     if( !qstrcmp(name,"bool"                   ) ) { return( &ra_bool      ); }
00117     if( !qstrcmp(name,"long"                   ) ) { return( &ra_long      ); }
00118     if( !qstrcmp(name,"QString"                ) ) { return( &ra_QString   ); }
00119     if( !qstrcmp(name,"double"                 ) ) { return( &ra_double    ); }
00120     if( !qstrcmp(name,"QDate"                  ) ) { return( &ra_QDate     ); }
00121     if( !qstrcmp(name,"QTime"                  ) ) { return( &ra_QTime     ); }
00122     if( !qstrcmp(name,"QDateTime"              ) ) { return( &ra_QDateTime ); }
00123     if( !qstrcmp(name,"QVariant"               ) ) { return( &ra_QVariant  ); }
00124     if( QByteArray(name).endsWith('*')           ) { return( &rp_void      ); }
00125     qDebug( "QAxReturnArgument unable to find '%s'", name );
00126     return( 0 );
00127   }
00128 
00129   bool        ra_bool;
00130   long        ra_long;
00131   QString     ra_QString;
00132   double      ra_double;
00133   QDate       ra_QDate;
00134   QTime       ra_QTime;
00135   QDateTime   ra_QDateTime;
00136   void      * rp_void;
00137   QVariant    ra_QVariant;
00138 
00139 }; // endclass QAxReturnArgument
00140 
00141 #define QAX_ARG(i) ((ga[i].isValid())?QGenericArgument(ga[i].typeName(), ga[i].constData()):QGenericArgument())
00142 
00143 class QComObject;
00144 class QComCallback
00145 :
00146   public QThread
00147 {
00148   Q_OBJECT
00149 
00150   public:
00151 
00152     QComCallback( QComObject * callback );
00153    ~QComCallback() {}
00154 
00155   private:
00156 
00157     void run(void);
00158 
00159     QComObject * m_callback;
00160 
00161 };
00162 
00163 class QComObject
00164 :
00165   public QObject,
00166   public IDispatch
00167 {
00168   Q_OBJECT
00169 
00170   public:
00171 
00172     static long instanceCount();
00173 
00174     /*!
00175     ** \brief Standard QueryInterface
00176     **
00177     ** This procedure is pretty boiler-plate.  It simply checks the
00178     **  requested ID from the caller, and it must either be the standard
00179     **  IUnknown interface or the IClassFactory interface to be awarded
00180     **  a reply.
00181     **
00182     */
00183     virtual HRESULT __stdcall QueryInterface( const IID& iid, void ** ppv );
00184 
00185 
00186     /*!
00187     ** \brief Add Reference
00188     **
00189     ** This is a typical AddRef() function call.  It increments our internal counter
00190     **  using the windows InterlockedIncrement (to prevent threaded overwrites).
00191     **
00192     */
00193     virtual ULONG __stdcall AddRef(void);
00194 
00195     /*!
00196     ** \brief Release Reference
00197     **
00198     ** This is a typical Release() function call.  It decrements our internal counter
00199     **  using the windows InterlockedDecrement (to prevent threaded overwrites).
00200     **  If the counter reaches zero then we delete us since there are no longer
00201     **  any references to ourself.
00202     **
00203     */
00204     virtual ULONG __stdcall Release(void);
00205 
00206     virtual LONG __stdcall GetTypeInfoCount( UINT * o_count );
00207 
00208     virtual LONG __stdcall GetTypeInfo( UINT, LCID, ITypeInfo** );
00209 
00210     virtual HRESULT __stdcall GetIDsOfNames
00211     (
00212       const IID & iid,
00213       BSTR      * arrayNames,
00214       UINT        countNames,
00215       LCID,         // Localization is not supported.
00216       DISPID    * arrayDispIDs
00217     );
00218 
00219     /*!
00220     ** \brief This is a server invoke.
00221     **
00222     ** This function handles the invoke for our server component.  This takes
00223     **  method invoke requests from the client (usually vbScript) and breaks them
00224     **  all down into their components and builds a Qt compatible method invoke
00225     **  call.  It then passes control to the Qt object which performs the action
00226     **  and returns some result.  That result too is then pulled apart and forced 
00227     **  into the COM variant result (pvr).  This whole function is the key to
00228     **  exposing any standard Qt object to COM.
00229     **
00230     ** \par And now a little dissertation on this COM interface
00231     **
00232     ** The interface from vbScript begins with a call to GetIDsFromNames.  This call
00233     **  is initiated when a vb call is made to the initiated object.  A typical call
00234     **  interface looks like this:
00235     **
00236     ** \code
00237     **  dim dumont: set dumont = CreateObject("DumontDLL")
00238     **  dumont.test
00239     ** \endcode
00240     **
00241     ** That preceeding code begins with a call to GetIDsFromNames with only one
00242     **  value in the arrayNames array.  This value is a text equivalent of "test".
00243     **  Note, however, that the value can be either "test", "Test", "TEST", TeSt"
00244     **  in other words - no case sensitivity!  Also, this call to GetIDsFromNames
00245     **  does NOT include any parameter information - and here is where the trouble
00246     **  begins.
00247     **
00248     ** The first for GetIDsFromNames is to find a matching function for the text.
00249     **  This is accomplished with a case-insensitive search of all the metaObject()
00250     **  method() names (for the time-being, we're only searching the method names).
00251     **  If a matching method name is found, then its INDEX number is returned to
00252     **  the caller (in this case, the vbScript host).  It must be noted that this
00253     **  index number does not necessarily represent the index number of the method
00254     **  we're actually going to call.  Here's why:
00255     **
00256     ** Let's say the QObject we are exposing has four functions, all with different
00257     **  signatures.  Take for example:
00258     **
00259     ** \code
00260     ** class myObject : public QObject
00261     ** {
00262     **    Q_OBJECT
00263     **    public slots:
00264     **
00265     **      void test(void);
00266     **      void test(QString message);
00267     **      void test(QString message, double number);
00268     **      long test(double input, int fraction);
00269     **
00270     ** }; // endclass myObject
00271     ** \endcode
00272     **
00273     ** Obviously, due to C++ polymorphism, these are four distinct functions
00274     **  distinguished only by their parameter types.  Also, note that we have "exposed"
00275     **  these functions in the "public slots" section of the object definition.
00276     **  This exposure is what causes these functions to be made available to to the
00277     **  metaObject() system of the Qt library.  And, as stated earlier, only methods
00278     **  (public slots) are available to this COM automation library interface.
00279     **
00280     ** Since these four methods are exposed to the metaObject() system, each one of 
00281     **  them has a unique ID (or index number).  Since they are defined sequentially in
00282     **  the object definition, they will be numbered sequentially.  The Qt meta-object-
00283     **  compiler (moc) takes care of assigning these interface ID numbers.  You're 
00284     **  advised to read through that documentation yourself, but note that the ID numbers
00285     **  assigned are a composite of all the ID numbers of all the super-objects to this
00286     **  object.  So, in the example above, if QObject has 4 methods and properties
00287     **  that it exposes, our first method ID number will be the 5th number in sequence
00288     **  (or 4 in value).  What this means is that ANY method that ANY of the objects
00289     **  expose in our object tree are automatically available to this single interface!
00290     **  That's pretty powerful, when you think about it.
00291     **
00292     ** In the call to GetIDsFromNames, then, the ID number that gets returned is a function
00293     **  of two things: 1, the ID number assigned by the moc, and 2, which function got
00294     **  found by the search routine.  Since the list of method names is searched sequentially
00295     **  then the search function usually finds the first method defined.
00296     **
00297     ** If vbScript calls for dumont.test, then the ID number will be the first function
00298     **  in our definition, because this is the first matching function.  If vbScript calls
00299     **  for dumont.test "message text", 3.4 then the call to GetIDsFromNames will still
00300     **  only return the first ID number because it's only matching on the function name
00301     **  and not the parameter list.  It is this ID number that is returned back to vbScript.
00302     **
00303     ** The next call the vbScript makes to complete this funciton call is the call to
00304     **  Invoke().  This function call receives the function ID number that was found on
00305     **  the earlier call to GetIDsFromNames, and a bonus - the parameter list!  Here's the
00306     **  caviat, though.  The member ID number that we receive does not necessarily represent
00307     **  the ID number of the function we want to call, because, remember, when we went
00308     **  searching for that ID number we didn't have the parameter list available to us.
00309     **  Therefore, the ID number only represents the first function in our list of matching
00310     **  function names.
00311     **
00312     ** What we do here is we first assemble the parameter list handed to us by vbScript
00313     **  into a form that's Qt compatible.  What this means is we extract all the parameters
00314     **  from vb into a set of parameters for the Qt invoke function, making translations as
00315     **  we go.  This is the 'date-input-translation' function of the task.  It is important
00316     **  to note here that Qt can only accept ten parameters to it's slots - if you have
00317     **  more you're out of luck, and I'd suggest reformatting your function interface.
00318     **
00319     ** Once we have this parameter list assembled we can go back to our function 'name', 
00320     **  which we have because we have 'a' function ID, and assemble what is called a a
00321     **  method signature.  This method signature consists of the method name followed by a
00322     **  complete method parameter type list.  Once we have a complete method signature we
00323     **  can then perform another 'method search' to get the REAL method ID number.
00324     **
00325     ** Once we have the REAL method ID number we can then use that to get the method return
00326     **  value (referred to as the 'methodType' in this library).  Once we have all these things,
00327     **  the method name, the method parameter list, the method return type, can can then
00328     **  finally perform the actual Qt invoke function.
00329     **
00330     **
00331     */
00332     HRESULT __stdcall Invoke
00333     (
00334       /*!
00335       ** This dispidMember (dispatch ID member) was generated in the GetIDsOfNames
00336       **  call whereby the qt object in question (the one being wrapped by us) was
00337       **  queried for a matching function name.  This dispidMember number includes
00338       **  a code to indicate if this is a Qt Method, Property, or Enum, and it 
00339       **  includes the actual ID number of the MPE (method/property/enum) in 
00340       **  question.  Keep in mind that the ACTUAL method (by id) that gets called
00341       **  may be different than the actual dispidMember value provided here, because when the call
00342       **  to GetIDsOfNames was made, we didn't have the parameters that was going
00343       **  to get passed in.  Therefore, we matched only on the methodName and not
00344       **  on the full methodSignature.  Not to worry, though.  We are only using
00345       **  this dispidMember number to get back to the methodName, and when we finally
00346       **  invoke the method, Qt is going to take care of matching the method name
00347       **  and parameter list making sure we call the correct function.
00348       **  <br><br>
00349       **
00350       */
00351       DISPID dispidMember,
00352 
00353       /*!
00354       ** Interface IDentifier.  This should always be IID_NULL.
00355       **  <br><br>
00356       **
00357       */
00358       const IID  & iid,
00359 
00360       /*!
00361       ** Localization Identifier.  Localization is not supported
00362       **  <br><br>
00363       **
00364       */
00365       LCID lcid,
00366 
00367       /*!
00368       ** Type of invoke requested. The Invoke function can be called a number of 
00369       **  different ways.  How it is called depends (I suppose) largely on how the 
00370       **  expression is presented in vbScript (or whatever the hosting calling 
00371       **  language is).  The following is listed here for information purposes 
00372       **  only - we don't actually use this value in our Invoke procedure:
00373       **
00374       ** \code
00375       **  case DISPATCH_METHOD        :
00376       **  case DISPATCH_PROPERTYGET   :
00377       **  case DISPATCH_PROPERTYPUT   :
00378       **  case DISPATCH_PROPERTYPUTREF:
00379       **  case DISPATCH_METHODGET     :
00380       ** \endcode
00381       **  <br><br>
00382       **
00383       */
00384       WORD wFlags,
00385 
00386       /*!
00387       ** Dispatch Parameters.  This is a parameter header.  It carries information 
00388       **  about how many parameters are provided and pointers to arrays of where 
00389       **  those parameters can be accessed.
00390       **  <br><br>
00391       **
00392       */
00393       DISPPARAMS * pDispParams,
00394 
00395       /*!
00396       ** Dispatch Result. This is the variant result.  It should be checked to 
00397       **  point to something other than zero before it is loaded with anything.
00398       **  <br><br>
00399       **
00400       */
00401       VARIANT * pvr,
00402 
00403       /*!
00404       ** Exception Info.  This is exception info, and I don't know how to use it yet.
00405       **  <br><br>
00406       **
00407       */
00408       EXCEPINFO * pExcepInfo,
00409 
00410       /*!
00411       ** Argument Errors. This is arguments error information - and I'm not sure 
00412       **  what to do about it either.
00413       **  <br><br>
00414       **
00415       */
00416       UINT * pArgErr
00417 
00418     );
00419 
00420     /*!
00421     ** This constructs a COM object that acts as an interface wrapper for a regular
00422     **  qt object.  It also increments a global object counter so that this package
00423     **  can know how many objects exist.
00424     ** 
00425     */
00426     QComObject( QObject * object, bool destroyObject = false );
00427 
00428     /*!
00429     ** \bug this destructor is doing a poor job of destroying the objects
00430     **  created by this COM interface.  So we've hacked into it to not destroy
00431     **  the main window of the application, since that one particular object
00432     **  has no parent.  All the rest of the objects should have a parent so it
00433     **  should be no problem.
00434     **
00435     */
00436     virtual ~QComObject();
00437 
00438   public slots:
00439 
00440     /*!
00441     ** This is the thread exec() loop.  It is inactive until
00442     **  a COM event is dispatched in.  The COM event stores up the
00443     **  COM parameters and then starts this thread, causing this
00444     **  run() procedure to execute.  It is from here that the call
00445     **  is actually dispatched to the wrapped object.
00446     **
00447     */
00448     void mainThreadInvoke(void)
00449     {
00450       /*
00451       ** Call the QtObjectSlot - whatever that is.  If everything was set up 
00452       **  properly (the correct parameter types and whatnot) then this call should
00453       **  succeed no problem.  If there is ANY problem in the param types or function
00454       **  name (case) or anything it will fail and then we'll report that below.
00455       **  Whatever the method returns will be stored in the .gr. variable and we'll
00456       **  parse that out finally and give it back to the COM recipient.
00457       **
00458       */
00459       invokeResult = QMetaObject::invokeMethod
00460       ( d,
00461         methodName.left( methodName.indexOf('(') ),
00462         QGenericReturnArgument( methodTypeName.data(), ra.data(methodTypeName.data()) ),
00463         QAX_ARG(0), QAX_ARG(1), QAX_ARG(2), QAX_ARG(3), QAX_ARG(4), 
00464         QAX_ARG(5), QAX_ARG(6), QAX_ARG(7), QAX_ARG(8), QAX_ARG(9)
00465       );
00466 
00467     }
00468 
00469   protected:
00470 
00471   private:
00472 
00473     bool m_destroyObject;
00474     LONG m_ref;
00475     QPointer<QObject> d;
00476 
00477     QWaitCondition waiter;
00478 
00479     QByteArray methodName;
00480     QByteArray methodTypeName;
00481     bool invokeResult;
00482 
00483     /*
00484     ** These argument things are used in the Invoke.  They all get set up
00485     **  in the a loop which processes each vb parameter into
00486     **  a qt-compatible parameter.  ALSO, the methodName is rebuilt with
00487     **  a new signature, because the signature is a function of what VB
00488     **  wants, NOT a function of what we got when when executing our
00489     **  piddly little dispidMember lookup function (which didn't include the
00490     **  parameter lists).  Later we'll use this rebuilt methodNameWithSig
00491     **  to find the correct return value type of this function.
00492     **
00493     */
00494     QAxArgument ga[10];
00495 
00496     /*
00497     ** Set up the return argument thing so that it's ready to accept the
00498     **  correct type.
00499     **
00500     */
00501     QAxReturnArgument ra;
00502 
00503     static long s_instanceCount;
00504 
00505 }; // endclass QComObject
00506 
00507 class QAxObjectWrapper
00508 :
00509   public QObject,
00510   public IDispatch
00511 {
00512   Q_OBJECT
00513 
00514   public:
00515 
00516   protected:
00517 
00518   private:
00519 
00520 }; // endclass QAxObject
00521 
00522 class QAxFactory
00523 :
00524   public QObject
00525 {
00526   Q_OBJECT
00527 
00528   Q_CLASSINFO( "ClassHelp",      "ActiveQt Class Factory (home brew)"     )
00529   Q_CLASSINFO( "ClassID",        "{204FECC7-B10D-4365-999D-A9C03E6E451B}" )
00530   Q_CLASSINFO( "InterfaceID",    "{9FB16D50-91DC-4425-BD37-F90FAF9A7A0D}" )
00531   Q_CLASSINFO( "RegisterObject", "no"                                     )
00532 
00533   public:
00534 
00535     enum ServerType { SingleInstance, MultipleInstances };
00536 
00537     /*!
00538     ** \brief Constructor
00539     **
00540     ** The constructor must be called only once within the application.
00541     **  Calling the constructor causes the global factory pointer to be
00542     **  set.  If the global factory pointer is already set when the
00543     **  constructor is called, then the application will exit with an
00544     **  error.
00545     **
00546     */
00547     QAxFactory ( const QUuid & libid, const QUuid & appid );
00548 
00549     /*!
00550     ** \brief Destructor
00551     **
00552     */
00553     virtual ~QAxFactory();
00554 
00555     /*!
00556     ** \brief
00557     **
00558     */
00559     virtual QUuid appID( void ) const;
00560 
00561     /*!
00562     ** \brief
00563     **
00564     */
00565     virtual QUuid classID( const QString & key ) const;
00566 
00567     /*!
00568     ** \brief
00569     **
00570     */
00571     virtual QObject * createObject( const QString & key ) = 0;
00572 
00573     /*!
00574     ** \brief
00575     **
00576     */
00577     virtual bool createObjectWrapper( QObject * object, IDispatch ** wrapper );
00578 
00579     /*!
00580     ** \brief
00581     **
00582     */
00583     virtual QUuid eventsID( const QString & key ) const;
00584 
00585     /*!
00586     ** \brief
00587     **
00588     */
00589     virtual QString exposeToSuperClass( const QString & key ) const;
00590 
00591     /*!
00592     ** \brief
00593     **
00594     */
00595     virtual QStringList featureList( void ) const = 0;
00596 
00597     /*!
00598     ** \brief
00599     **
00600     */
00601     virtual bool hasStockEvents( const QString & key ) const;
00602 
00603     /*!
00604     ** \brief
00605     **
00606     */
00607     virtual QUuid interfaceID( const QString & key ) const;
00608 
00609     /*!
00610     ** \brief
00611     **
00612     */
00613     virtual bool isService( void ) const;
00614 
00615     /*!
00616     ** \brief
00617     **
00618     */
00619     virtual const QMetaObject * metaObject( const QString & key ) const = 0;
00620 
00621     /*!
00622     ** \brief
00623     **
00624     */
00625     virtual void registerClass( const QString & key, QSettings * settings ) const;
00626 
00627     /*!
00628     ** \brief
00629     **
00630     */
00631     virtual bool stayTopLevel( const QString & key ) const;
00632 
00633     /*!
00634     ** \brief
00635     **
00636     */
00637     virtual QUuid typeLibID( void ) const;
00638 
00639     /*!
00640     ** \brief
00641     **
00642     */
00643     virtual void unregisterClass( const QString & key, QSettings * settings ) const;
00644 
00645     /*!
00646     ** \brief
00647     **
00648     */
00649     virtual bool validateLicenseKey( const QString & key, const QString & licenseKey ) const;
00650 
00651     /*!
00652     ** \brief
00653     **
00654     */
00655     static bool isServer( void );
00656 
00657     /*!
00658     ** \brief
00659     **
00660     */
00661     static bool registerActiveObject( QObject * object );
00662 
00663     /*!
00664     ** \brief
00665     **
00666     */
00667     static QString serverDirPath( void );
00668 
00669     /*!
00670     ** \brief
00671     **
00672     */
00673     static QString serverFilePath( void );
00674 
00675     /*!
00676     ** \brief
00677     **
00678     */
00679     static bool startServer( ServerType type = MultipleInstances );
00680 
00681     /*!
00682     ** \brief
00683     **
00684     */
00685     static bool stopServer( void );
00686 
00687   public slots:
00688 
00689     void invoke( void * comObject );
00690 
00691   protected:
00692 
00693   private:
00694 
00695 //    static t_factoryList
00696 
00697 }; // endclass QAxFactory
00698 
00699 QAxFactory * qAxFactory(void);
00700 
00701 
00702 //typedef struct FACTORYLIST_STRUCT
00703 //{
00704 //  const char * className;
00705 //
00706 //} t_factoryList;
00707 
00708 #endif // #ifndef QAXFACTORY_H
00709 
 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