Specific APIs:spc

Creating Xbase++ DLLs with the C-API Foundation

The two functions F2Bin() and Bin2F() from the example project in the previous section are linked into a DLL file. The result is an ordinary DLL which can be used by an Xbase++ application. However, at this stage it is not possible to call the two functions using the macro operator. The reason is that the linker translates a function's symbol into its address and the symbol is no longer available at runtime. To solve this problem, two approaches are possible:

Declaration of function symbols

A function's symbol can be created in the PRG source code using the REQUEST declaration. This causes the compiler to add a function's name to the symbol table and the linker to search for this symbol.

REQUEST F2BIN 
REQUEST BIN2F 

The two declarations make sure that the symbols F2BIN and BIN2F exist at runtime of an Xbase++ application. It is also guaranteed that the linker will link the DLL file to the EXE when C-API functions are only called in the PRG code via the macro operator.

This approach, however, has the disadvantage that a DLL must be linked statically to the EXE since it is the linker who creates the function symbols. It is not possible to link the file F2BIN.DLL dynamically, load it at runtime with DllLoad() and call the contained functions by passing their symbolic names to DllCall(). For this, a different approach must be used.

Registration of function symbols

If C-API functions are to be contained in dynamically loadable DLL files (DllLoad()/DllUnload()), it becomes necessary to register the function symbols as soon as the DLL is loaded with DllLoad(). Likewise, the symbols must be unregistered before the DLL is released using DllUnload(). For this, two functions are available whose prototypes are given below:

/* registers function symbols */ 
XPPAPIRET XPPAPIENTRY _conRegisterF(XppRegisterFunction *funcs, 
                                    int numOfFuncs, 
                                    XppRegisterTable *tbl); 

/* unregisters function symbols */ 
XPPAPIRET XPPAPIENTRY _conUnRegisterF(XppRegisterTable *tbl); 

The functions contained in the DLL are declared along with their symbolic names in a static table and additional information required for unregistration is saved to the static variabel of the XppRegisterTable type:

XppRegisterFunction MyFuncs = { { "F2BIN", F2BIN } , 
                                { "BIN2F", BIN2F } }; 

XppRegisterTable MyFuncsInfo; 

After these two declarations, the function symbols can be registered:

_conRegisterF(&MyFuncs, 
              sizeof(MyFuncs) / sizeof(XppRegisterFunction), 
              &MyFuncsInfo); 

The next function call is used to unregister the function symbols:

_conUnRegisterF(&MyFuncsInfo); 

L:DLL registration]

To guarantee the availability of function symbols when a DLL is loaded, and to automatically release the symbols when the DLL is unloaded, it is recommended to call the two functions _conRegisterF() and _conUnRegisterF(). Here are some ideas on how to initialize a C-DLL:

Call the two functions _conRegisterF() and _conUnRegisterF() from the Xbase++ application. This requires to encapsulate both function calls in the C-code:

int f2b_register() { 
        return _conRegisterF(&MyFuns, 
                             sizeof(MyFuncs) / sizeof(XppRegisterFunction), 
                             &MyFuncsInfo); 
} 

int f2b_unregister() { 
        return _conUnRegisterF(&MyFunsInfo); 
} 

The PRG source code then looks like this:

DllLoad( "F2BIN.DLL" ) 
DllCall( "F2BIN.DLL", DLL_CDECL, "f2b_register" ) 
.... 
DllCall( "F2BIN.DLL", DLL_CDECL, "f2b_unregister" ) 
DllUnLoad( "F2BIN.DLL" ) 

When a C-API DLL is created in this way, it can be used dynamically just as any other Xbase++ DLL. The disadvantage is obviously the required extra call into the DLL.

Another possibility is to put the calls into Xbase++ - INIT/EXIT procedures either of the EXE program if the DLL will be statically linked or into an Xbase++ - DLL, if the C-DLL is accompanied by such an Xbase++ - DLL. The advantage over the previous approach is the usage of the implicit call to to INIT/EXIT procedures everytime a DLL gets loaded for ensuring correct symbol initialization.

INIT PROCEDURE DLL1 
    DllLoad( "F2BIN.DLL" ) 
    DllCall( "F2BIN.DLL", DLL_CDECL, "f2b_register" ) 
RETURN 

If the C-DLL must stand on its own feets, i.e. shall be usable without any extra Xbase++ - code, use _conRegisterDll() / _conUnRegisterDll() from the initialization routine which is called by the operating system when the DLL is (un)loaded. This is the most recommended way of making a DLL written in C behave like a Xbase++ - DLL.

  unsigned long _System _DLL_InitTerm( unsigned long modHandle , 
                                       unsigned long modeFlag, 
                                       LPVOID dummy ) 
  { 
    switch( modeFlag ) 
      { 
      case DLL_PROCESS_ATTACH: 
          if (!_conRegisterDll(modHandle, &myDllRecord)) 
             return 0; 
        break; 

      case DLL_PROCESS_DETACH: 
          _conUnRegisterDll(modHandle); 
        break; 
        
      } 
    return 1; 
  } 

Please consult your C-compiler manual for more information on programming DLL startup routines.

Feedback

If you see anything in the documentation that is not correct, does not match your experience with the particular feature or requires further clarification, please use this form to report a documentation issue.