home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD 24 / PCPLUS115.iso / install / extern.pak / DBEXTERN.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  19.7 KB  |  673 lines

  1. //============================================================================
  2. //
  3. // dbextern.cpp
  4. //
  5. // C++ part of the example that shows how the EXTERN system can be used.
  6. //
  7. // For a description of this example, see the dbextern.prg file.
  8. //
  9. //============================================================================
  10.  
  11.    // Include some headers.
  12. #include "dbasevar.h"
  13.  
  14. //============================================================================
  15. //
  16. //  You can create an instance of class CSession in DBaseInitInstance() 
  17. //  and set it up. In subsequent call backs, a pointer to this instance 
  18. //  can be retrieved with a call to GetSession().  Data that is kept on 
  19. //  a per-instance basis (DBaseVars for example) should be kept in the 
  20. //  session object.
  21. //
  22. //============================================================================
  23.  
  24. class CSession {
  25. public:
  26.    DVar ThisSessionLocalObject;
  27.  
  28.    CSession(){
  29.        // Initialize objects local to this session.
  30.  
  31.        time_t t = time( NULL );
  32.        ThisSessionLocalObject->SetZString( ctime( &t ) );
  33.    }
  34.    ~CSession(){
  35.        // Destroy objects local to this session.
  36.    }
  37.  
  38.    char * UseLocalObject(){
  39.        // Use objects local to this session.
  40.        return ThisSessionLocalObject->String();
  41.    }
  42. };
  43.  
  44.  
  45. extern "C" {
  46. //============================================================================
  47. //
  48. //  Usual DLL functions.
  49. //
  50. //============================================================================
  51.  
  52. int FAR PASCAL LibMain(HINSTANCE, WORD, WORD, LPSTR){
  53.  
  54.    return 1;
  55. }
  56. int CALLBACK WEP(int /*nParam*/){return(1);}
  57.  
  58. //
  59. //  This function is called every time an Instance of dBASE loads the DLL.  
  60. //  If for some reason the DLL determines that it cannot load, it can return 
  61. //  an error.  If the DLL loads properly it should return DBASE_INIT_OK.
  62. //
  63.  
  64. int CALLBACK DBaseInitInstance(void){
  65.  
  66.    asm int 3;     // break point for the debugger.
  67.  
  68.    DBase()->SetSession(new CSession());
  69.  
  70.        //
  71.        // Possible Error return Values
  72.        //
  73.        //return DBASE_INIT_NO_MULTI_INSTANCE;
  74.        //return DBASE_INIT_ERROR;
  75.  
  76.    return DBASE_INIT_OK;
  77. }
  78.  
  79. //
  80. //  DBaseExitInstance() is called when an instance of dBASE terminates or 
  81. //  manually unloads a DLL through the RELEASE DLL command.  This give the 
  82. //  DLL a chance to free any resources associated with a running instance.
  83. //
  84.  
  85. void CALLBACK DBaseExitInstance(void){
  86. }
  87.  
  88. //============================================================================
  89. //
  90. // Example # 1.
  91. //
  92. // Some simple examples using the different types.
  93. // 
  94. //============================================================================
  95.  
  96. BOOL _export _pascal TypeTest1( char * Str, int Int, DoubleType Double ) {
  97.    if( strcmp( Str, "string" ) != 0 || Int != 123 || Double != 321.123L )
  98.        return 0;
  99.    return 1;
  100. }
  101.  
  102. DoubleType _export _cdecl TypeTest2( char * Str, long Long, DBaseVar *Var ) {
  103.    return strtod( Str, NULL ) + (DoubleType) Long + Var->Double();
  104. }
  105.  
  106. //============================================================================
  107. //
  108. // Example # 3.
  109. //
  110. // dBASE objects can be of various types. With the Type() function you
  111. // can recognize what kind of object you're dealing with.
  112. // 
  113. //============================================================================
  114.  
  115. char* _export _pascal WhichType( DBaseVar *var ) {
  116.  
  117.    static char buffer[25];
  118.  
  119.        // Get the type and put it in buffer.
  120.    switch( var->Type() ){
  121.    case 'L':                          // Logical
  122.        strcpy( buffer, "Logical " );
  123.        strcat( buffer, var->Logical() ? ".T." : ".F.");
  124.        break;
  125.    case 'N':                          // Numeric double
  126.        strcpy( buffer, "Double " );
  127.        gcvt(var->Double(),10,buffer+7);
  128.        break;
  129.    case 'I':                          // Numeric long
  130.        strcpy( buffer, "Long " );
  131.        ltoa(var->Long(), buffer+5, 10);
  132.        break;
  133.    case 'C':                          // String
  134.        strcpy( buffer, "String " );
  135.        strcat( buffer, var->String() );
  136.        break;
  137.    case 'O':                          // Object
  138.        strcpy( buffer, "Object" );
  139.        break;
  140.    case 'F':                          // Function or code block
  141.        strcpy( buffer, "Code" );
  142.        break;
  143.    default:                           // Anybody else
  144.       buffer[0] = var->Type();
  145.       buffer[1] = ' ';
  146.       strcpy( buffer+2, ":Unknown Type" );
  147.    }
  148.        // Return the type.
  149.    return buffer;
  150. }
  151.  
  152. //============================================================================
  153. //
  154. // Example # 4.
  155. //
  156. // You can modify dBASE objects passed in.  Since they are passed by
  157. // reference, changes made here to objects show also on the dBASE side.
  158. // It is also possible to use the CVAR type as the return type of dBASE
  159. // functions.  On the dBASE side you simply declare the function
  160. // with CVAR as the return type:
  161. //
  162. //   EXTERN CDECL CVAR ModifyAndAdd1( CVAR, CVAR ) dbextern.dll
  163. //   EXTERN CVAR ModifyAndAdd2( CVAR, CVAR ) dbextern.dll
  164. //
  165. // but on the C/C++ side you use void as the return type of the function
  166. // and list the CVAR return variable as the first parameter for CDECL
  167. // functions or as the last parameter for PASCAL functions. 
  168. //
  169. //============================================================================
  170.  
  171. void _export _cdecl ModifyAndAdd1( DBaseVar *ReturnValue, 
  172.                                    DBaseVar *D1, DBaseVar *D2 ) {
  173.  
  174.    ReturnValue->SetDouble( D1->Double() + (DoubleType)D2->Long() );
  175.    D1->SetZString( "Changed to a string" );
  176.    D2->SetLogical( 1 );
  177. }
  178.  
  179. void _export _pascal ModifyAndAdd2( DBaseVar *D1, DBaseVar *D2,
  180.                                     DBaseVar *ReturnValue ) {
  181.  
  182.    ReturnValue->SetDouble( D1->Double() + (DoubleType)D2->Long() );
  183.    D1->SetZString( "Changed in ModifyAndAdd2" );
  184.    D2->SetLogical( 1 );
  185. }
  186.  
  187. //============================================================================
  188. //
  189. // Example # 5.
  190. //
  191. // Parts of dBASE class objects are accessible on this side.
  192. // This example shows one way of how to implement Windows API calls
  193. // that require the use of structs.
  194. //
  195. //============================================================================
  196.  
  197. void _export _cdecl WSetRect( DBaseVar *rect, WORD left, WORD top, 
  198.                                WORD right, WORD bottom ){
  199.    RECT wrect;
  200.    DVar Temp;
  201.  
  202.        // Call the Windows API.
  203.    SetRect( &wrect, left, top, right, bottom );
  204.  
  205.        // Fill in the DBaseVar.
  206.    rect->SetProperty( "left", DVar( (long)(wrect.left) ) );
  207.    rect->SetProperty( "top", DVar( (long)(wrect.top) ) );
  208.    rect->SetProperty( "right", DVar( (long)(wrect.right) ) );
  209.    rect->SetProperty( "bottom", DVar( (long)(wrect.bottom) ) );
  210. }
  211.  
  212. void _export _pascal WIntersectRect( DBaseVar *result, 
  213.                                      DBaseVar *rect1, DBaseVar *rect2 ){
  214.    RECT one, two, three;
  215.    DVar Temp;
  216.  
  217.        // Copy rect1 to one.
  218.    rect1->Property( "left", Temp );
  219.    one.left = Temp->Long(); 
  220.    rect1->Property( "top", Temp );
  221.    one.top = Temp->Long(); 
  222.    rect1->Property( "right", Temp );
  223.    one.right = Temp->Long(); 
  224.    rect1->Property( "bottom", Temp );
  225.    one.bottom = Temp->Long(); 
  226.  
  227.        // Copy rect2 to two.
  228.    rect2->Property( "left", Temp );
  229.    two.left = Temp->Long(); 
  230.    rect2->Property( "top", Temp );
  231.    two.top = Temp->Long(); 
  232.    rect2->Property( "right", Temp );
  233.    two.right = Temp->Long(); 
  234.    rect2->Property( "bottom", Temp );
  235.    two.bottom = Temp->Long();
  236.  
  237.        // Make the Windows API call
  238.    IntersectRect( &three, &one, &two );
  239.  
  240.        // Copy three to result.
  241.    Temp->SetLong( three.left );
  242.    result->SetProperty( "left", Temp );
  243.    Temp->SetLong( three.top );
  244.    result->SetProperty( "top", Temp );
  245.    Temp->SetLong( three.right );
  246.    result->SetProperty( "right", Temp );
  247.    Temp->SetLong( three.bottom );
  248.    result->SetProperty( "bottom", Temp );
  249. }
  250.  
  251. //============================================================================
  252. //
  253. // Example # 6.
  254. //
  255. // Through the use of code blocks dBASE code can be executed on the fly
  256. // inside the DLL. The 2 objects that are passed in are compared, changed,
  257. // and compared again, all through codeblocks.
  258. //
  259. //============================================================================
  260.  
  261. BOOL _cdecl AreEqual( DBaseVar *one, DBaseVar * two ){
  262.  
  263.    DVar Temp, Result;
  264.  
  265.        // Check that the types are equal.
  266.    if( one->Type() != two->Type() )
  267.        return 0;
  268.  
  269.        // Compare them and return the result.
  270.    Temp->SetCodeBlock( "{|a,b| a=b}" );
  271.    Temp->RunCodeBlock( Result,2,&one );
  272.    return Result->Logical();
  273. }
  274.  
  275. char * _export _pascal CheckAndChange( DBaseVar *one, DBaseVar *two ) {
  276.  
  277.    BOOL Result1, Result2;
  278.    DVar Temp1, Temp2, Temp3( 3.0L );
  279.  
  280.        // Temp3Ptr is used to pass a DVar to a function that
  281.        // expects a DBaseVar**.
  282.    DBaseVar * Temp3Ptr = Temp3;
  283.  
  284.        // Announce that we arrived.
  285.    Temp1->SetCodeBlock( "{ ;? 'Inside CheckAndChange' }" );
  286.    Temp1->RunCodeBlock( Temp2, 0, (DVar*)0 );
  287.  
  288.        // Check if they are equal coming in.
  289.    Result1 = AreEqual( one, two );
  290.  
  291.        // Change the 2 objects through codeblocks.
  292.    Temp1->SetCodeBlock( "{ 4.0 }" );
  293.    Temp1->RunCodeBlock( one, 0, (DVar*)0 );
  294.    Temp1->SetCodeBlock( "{ |a| a }" );
  295.    Temp1->RunCodeBlock( two, 1, &Temp3Ptr );
  296.  
  297.        // Check that they are not equal anymore.
  298.    Result2 = AreEqual( one, two );
  299.  
  300.        // Return the result.
  301.    if( Result1 == 1 && Result2 == 0 )
  302.        return "passed"; else return "failed";
  303. }
  304.  
  305. //============================================================================
  306. //
  307. // Example # 7.
  308. //
  309. // dBASE arrays can be accessed in the DLL.  This example computes the
  310. // average of the members of an array created on the dBASE side.  
  311. // The DLL function is called without any parameters.  To be able to
  312. // get at the array, the DLL function is executed through a function
  313. // pointer that is a member of the same object as the array.  Inside
  314. // the DLL you can get the "this" pointer of the object the caller
  315. // belongs to, and through that "this" pointer you can access the 
  316. // array members.
  317. // Normally ofcourse you can just pass the array into a DBaseVar.
  318. //
  319. //============================================================================
  320.  
  321. DoubleType _export _cdecl ComputeAverage(){
  322.  
  323.    DoubleType d = 0;
  324.    long iSize;
  325.    int iCount=0;
  326.    int i = 1; 
  327.    DVar pValue;
  328.    DVar Size;
  329.  
  330.        // pSize is used to pass Size to functions expecting a DBaseVar**.
  331.    DBaseVar *pSize = Size;
  332.    
  333.        // Initialize pThis with the "this" of the object the caller
  334.        // belongs to.
  335.    DVar pThis(DBase()->GetThis());
  336.  
  337.        // If we're not inside the context of a memberfunction return.
  338.    if(pThis == 0) return 0;
  339.  
  340.        // Get the Size of the array of the object.
  341.    pThis->Property("SIZE",Size);
  342.    iSize = Size->Long();
  343.  
  344.        // Loop thru the array and total the elements.
  345.    while( i < iSize){
  346.       Size->SetLong(i);
  347.       pThis->Element(pValue,1,&pSize);
  348.       DoubleType d1;
  349.       d += pValue->Double();
  350.       iCount++;
  351.       i++;
  352.    }
  353.        // Return the average.
  354.    return d/iCount;
  355. }
  356.  
  357. //============================================================================
  358. //
  359. // Example # 8.
  360. //
  361. // This example puts into "practice" a majority of the available 
  362. // memberfunctions of the DBaseVar class.
  363. //
  364. //============================================================================
  365.  
  366. long _pascal _export TestABunch( DBaseVar *Var ){
  367.  
  368.    DVar Elems1[6], Elems2[6];
  369.    DVar Temp;
  370.    long Errors = 0;
  371.  
  372.        // Set up 6 different objects. Check returns of Set.. functions.
  373.    if( Elems1[0]->SetLogical( 1 ) != 1 )
  374.        Errors += 1;
  375.    if( Elems1[1]->SetDouble( 2.0L ) != 2.0L )
  376.        Errors += 2;
  377.    if( Elems1[2]->SetLong( 3L ) != 3L )
  378.        Errors += 4;
  379.    if( strcmp( Elems1[3]->SetZString( "Hi" ), "Hi" ) != 0 )
  380.        Errors += 8;
  381.    if( Elems1[4]->SetLong( 100L ) != 100L )
  382.        Errors += 16;
  383.    if( Elems1[5]->SetDouble( 5.5L ) != 5.5L )
  384.        Errors += 32;
  385.  
  386.        // pParams is used to be able to pass params in one block.
  387.    DBaseVar *pParams[2];             
  388.    DVar      Param1;
  389.    DVar      Param2;
  390.  
  391.        // Set up the array of params.
  392.    pParams[0] = Param1;
  393.    pParams[1] = Param2;
  394.  
  395.        // Make a 2 by 3 array.
  396.    Temp->SetCodeBlock("{|a,b| new array(a,b)}");
  397.    Param1->SetLong(2);
  398.    Param2->SetLong(3);
  399.    Temp->RunCodeBlock(Var,2,pParams);
  400.  
  401.        // Fill in the array.
  402.    for( int i=0; i<6; i++ ){
  403.        Param1->SetLong( 1 + i % 2 );    // 1 - 2
  404.        Param2->SetLong( 1 + i % 3 );    // 1 - 3 
  405.        Var->SetElement( 2,pParams,Elems1[i] );
  406.    }
  407.  
  408.        // Read back the array into a different variable.
  409.    for( i=0; i<6; i++ ){
  410.        Param1->SetLong( 1 + i % 2 );    // 1 - 2 
  411.        Param2->SetLong( 1 + i % 3 );    // 1 - 3
  412.        Var->Element( Elems2[i],2,pParams );
  413.    }
  414.  
  415.        // Now check if it worked.
  416.    Temp->Set( Elems2[0] );
  417.    if( Temp->Logical() != 1 || Temp->Type() != 'L' )
  418.        Errors += 64;
  419.    if( !AreEqual( Elems1[0], Elems2[0] ) )
  420.        Errors += 64;
  421.    Temp->Set( Elems2[1] );
  422.    if( Temp->Double() != 2.0L || Temp->Type() != 'N' )
  423.        Errors += 128;
  424.    if( !AreEqual( Elems1[1], Elems2[1] ) )
  425.        Errors += 128;
  426.    Temp->Set( Elems2[2] );
  427.    if( Temp->Long() != 3L || Temp->Type() != 'I' )
  428.        Errors += 256;
  429.    if( !AreEqual( Elems1[2], Elems2[2] ) )
  430.        Errors += 256;
  431.    Temp->Set( Elems2[3] );
  432.    if( strcmp( Temp->String(), "Hi" ) != 0 || Temp->Type() != 'C' )
  433.        Errors += 512;
  434.    if( !AreEqual( Elems1[3], Elems2[3] ) )
  435.        Errors += 512;
  436.    Temp->Set( Elems2[4] );
  437.    if( Temp->Long() != 100L || Temp->Type() != 'I' )
  438.        Errors += 1024;
  439.    if( !AreEqual( Elems1[4], Elems2[4] ) )
  440.        Errors += 1024;
  441.    Temp->Set( Elems2[5] );
  442.    if( Temp->Double() != 5.5L || Temp->Type() != 'N' )
  443.        Errors += 2048;
  444.    if( !AreEqual( Elems1[5], Elems2[5] ) )
  445.        Errors += 2048;
  446.    
  447.        // And check Var using properties.
  448.    Var->Property( "SIZE", Temp );
  449.    if( Temp->Long() != 6 )
  450.        Errors += 4096;
  451.    Var->Property( "DIMENSIONS", Temp );
  452.    if( Temp->Long() != 2 )
  453.        Errors += 4096;
  454.  
  455.    return Errors;
  456. }
  457.  
  458. //============================================================================
  459. //
  460. // Example # 9.
  461. //
  462. // This example grabs the DOS environment, puts it in a string and sends
  463. // it over to the dBASE side.
  464. //
  465. //============================================================================
  466.  
  467. void _export _cdecl GetEnvString(DBaseVar *pVarReturn){
  468.  
  469.    char *pEnviron = *environ;
  470.  
  471.        // Find the length and set it.
  472.    while(strlen(pEnviron) != 0){
  473.       pEnviron += strlen(pEnviron)+1;
  474.    }
  475.    int environLen = pEnviron - *environ;
  476.  
  477.        // Make room for the string and copy it.
  478.    pVarReturn->SetStringLen(environLen);
  479.    memcpy(pVarReturn->StringBuffer(),*environ,environLen);
  480.  
  481.        // Go through the string and change the zero characters into blanks. 
  482.    int count = 0;
  483.    pEnviron = pVarReturn->StringBuffer();
  484.  
  485.    while( count < (environLen - 1) ) {
  486.        if( *( pEnviron + count ) == 0x0 )
  487.            *( pEnviron + count ) = ' ';
  488.        count++;
  489.    }
  490. }
  491.  
  492. //============================================================================
  493. //
  494. // Example # 10.
  495. //
  496. // This example is called with a variable number of parameters using
  497. // the func(...) syntax.
  498. //
  499. //============================================================================
  500.  
  501. DoubleType _cdecl _export AddNumbers( int pCount, DBaseVar *pFirstNum){
  502.  
  503.        // pFirstNum points to the parameters.
  504.    DBaseVar **ppVars = &pFirstNum;
  505.    DoubleType d = 0;
  506.  
  507.        // Go thru the parameters and add them.
  508.    while(pCount--){
  509.       d += (*ppVars)->Double();
  510.       ppVars++;
  511.    }
  512.    return d;
  513. }
  514.  
  515. //============================================================================
  516. //
  517. // Example 11.
  518. //
  519. // This example shows how a dBASE object can be combined with an "hidden"
  520. // C++ sister object.  Each dBASE object has its own unique C++ object.
  521. // This allows a dBASE object to "remember and store" its own details 
  522. // specific to the DLL in the C++ object instead of having to save it
  523. // in the dBASE object. Examples are the storing of filehandles, and 
  524. // filepointers when the DLL is servicing files.
  525. // Another possibility is a form of late binding where at the time of 
  526. // creation of the C++ part a choice can be made from several derived 
  527. // classes based on a common parent object. 
  528. //
  529. //============================================================================
  530.  
  531. //
  532. // Common parent object
  533. //
  534. class DLLObject {
  535. protected:
  536.    char Message[60];
  537.    char ID[30];
  538. public:
  539.    DLLObject() {}
  540.    virtual char * Doit() = 0;
  541. };
  542.  
  543. //
  544. // ObjectA
  545. //
  546. class ObjectA : public DLLObject {
  547. public:
  548.    ObjectA( char *In ) { strcpy( ID, In ); }
  549.    char * Doit();
  550. };
  551.  
  552. char * ObjectA::Doit() {
  553.    strcpy( Message, ID );
  554.    strcat( Message, "you can read this" );
  555.    return Message;
  556. }
  557.  
  558. //
  559. // ObjectB
  560. //
  561. class ObjectB : public DLLObject {
  562. public:
  563.    ObjectB( char * In ) { strcpy( ID, In ); }
  564.    char * Doit();
  565. };
  566.  
  567. char * ObjectB::Doit() {
  568.    strcpy( Message, ID );
  569.    strcat( Message, "siht daer t'nac uoy" );
  570.    return Message;
  571. }
  572.  
  573. //
  574. // Helper function to retrieve the C++ this pointer from 
  575. // the calling dBASE object.
  576. //
  577. DLLObject *GetCPlusPlusThis() {
  578.  
  579.        // Local vars. Get the this of the dBASEWin object.
  580.    DVar CThis, DBaseThis( DBase()->GetThis() );
  581.  
  582.        // Get the this of the corresponding C++ object.
  583.    DBaseThis->Property( "cthis", CThis );
  584.  
  585.    return (DLLObject*)(CThis->Long());
  586. }
  587.  
  588. //
  589. // Initializes the C++ object. Inserts the C++ this pointer into
  590. // the calling dBASE object.
  591. //
  592. int _export _pascal DLLObjectInit( int Number ) {
  593.                    
  594.    DLLObject * ThisDLLObject;
  595.    char Message[50];
  596.    static int Count = 0;
  597.  
  598.        // Get the this of the calling dBASE object.
  599.    DVar CThis( DBase()->GetThis() );
  600.  
  601.        // Assign unique id, store object type.
  602.    sprintf( Message, "ID# %d, Type: Object #%d ", ++Count, Number );
  603.  
  604.        // Choose what kind of C++ object to create.
  605.    if( Number == 1 ) {
  606.  
  607. #ifdef NO_EXCEPTIONS
  608.  
  609.            // Create new C++ object.
  610.        ThisDLLObject = new ObjectA( Message );
  611.        if( ThisDLLObject == NULL )
  612.            return 0;
  613.  
  614.            // Sneak this pointer into the dBASE object.
  615.        CThis->SetProperty( "cthis", DVar( (long) ThisDLLObject ) );
  616.    }
  617.    else {
  618.            // Create new C++ object.
  619.        ThisDLLObject = new ObjectB( Message );
  620.        if( ThisDLLObject == NULL )
  621.            return 0;
  622.  
  623.            // Sneak this pointer into the dBASE object.
  624.        CThis->SetProperty( "cthis", DVar( (long) ThisDLLObject ) );
  625.  
  626. #else  // NO_EXCEPTIONS
  627.  
  628.        try {
  629.                // Create new C++ object.
  630.            ThisDLLObject = new ObjectA( Message );
  631.                // Sneak this pointer into the dBASE object.
  632.            CThis->SetProperty( "cthis", DVar( (long) ThisDLLObject ) );
  633.        }
  634.        catch( ... ) {
  635.            return 0;
  636.        }
  637.    }
  638.    else {
  639.        try {
  640.                // Create new C++ object.
  641.            ThisDLLObject = new ObjectB( Message );
  642.                // Sneak this pointer into the dBASE object.
  643.            CThis->SetProperty( "cthis", DVar( (long) ThisDLLObject ) );
  644.        }
  645.        catch( ... ) {
  646.            return 0;
  647.        }
  648.  
  649. #endif  // NO_EXCEPTIONS
  650.  
  651.    }
  652.  
  653.    return 1;
  654. }
  655.  
  656. //
  657. // Helper function to call the Doit function tied to the 
  658. // calling dBASE object.
  659. //
  660. char * _export _pascal Doit() {
  661.    return GetCPlusPlusThis()->Doit();
  662. }
  663.  
  664. //
  665. // Helper function to delete the C++ object tied to the calling dBASE object.
  666. //
  667. void _export _pascal Release() {
  668.    delete GetCPlusPlusThis();
  669. }
  670.  
  671.    // extern "C"
  672. }
  673.