/* dllaccess.c
 * Example for accessing functions in MS-Windows .DLL library 
 * from FlagShip. These examples demonstrates common access to
 * C or Delphi functions in a .DLL library. You need to know
 * at least prototypes of these functions. 
 *
 * How-to: Copy this source to your e.g. mydllaccess.c source, 
 * modify the real function names, and compile by
 *   FlagShip myapp*.prg mydllaccess.c -Mmymain -o myapp.exe
 *
 * The following examples are intentionally kept simple and clearly,
 * but at least basic C knowledge is required here. See also the
 * FlagShip manual section EXT.OpenC for further details.
 *
 * Notes for the code below:
 * 1) FlagShip function names in C are in lowercase and with max. 10 chars
 *    The number of passed parameters is in argc, parameters (FSvar *) are 
 *    in argv[0] for the first param upto argv[argc -1] for the last param
 * 2) Prototype the calling function. Defining inproper parameter types
 *    or less/more parameters will most probably result in segmentation
 *    fault (protection fault). The calling convention __cdecl or __stdcall
 *    (or sometimes __fastcall) depends on how the library was compiled. 
 *    Most C and C++ compilers uses __cdecl; Delphi, Pascal and some C uses 
 *    __stdcall, which is used also in Microsoft Win32 API. In doubt, try
 *    __cdecl (the default) first.
 * 3) all C functions (and variables) are case sensitive
 */
 
#include <windows.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include "FSopenc.h"                   // FlagShip Open-C API
 
static HINSTANCE hinstLib = NULL;      // holds ptr to .DLL lib

/*******************************************************************
 * FlagShip function
 *   lOk := OpenDLL(cDllName) 
 * returns .T.=ok, .F.=failure
 * cDllName is e.g. "mydll.dll" or "C:\mydir\mydll.dll" etc.
 *
 * This OpenDLL() must be invoked before any other .DLL functions 
 * are accessed.
 */ 
UDF_DECL(opendll)                      // FlagShip function, note 1
{
   char *cDllName;
   if(argc < 1 || !IS_VAR_CHR(argv[0]) || IS_VAR_EMPTY(argv[0]))
      return FALSE_VAR;
   cDllName = VAR_CHR(argv[0]);
   
   hinstLib = LoadLibrary(cDllName); 
   if (hinstLib == NULL) 
      return FALSE_VAR;                // could not open dll
   return TRUE_VAR;                    // ok
}

/*******************************************************************
 * FlagShip function
 *   ok := CloseDLL() 
 * returns .T.=ok, .F.=failure
 *
 * You may invoke CloseDLL() when no further .DLL functions are
 * required to free memory. Otherwise is the library unloaded 
 * when the app ends.
 */ 
UDF_DECL(closedll)                     // FlagShip function, note 1
{
   if (hinstLib == NULL)               // dll was not loaded yet
      return FALSE_VAR;
   FreeLibrary(hinstLib);
   return TRUE_VAR;                    // ok
}

/*******************************************************************
 * FlagShip function
 *   nRet := InitMyDll() 
 * returns numeric value of the called .DLL function Initdll() or 
 * an error code -1 or -2 on failure.
 *
 * This example calls the .DLL function declared as
 *    int Initdll(void)    
 * which has no parameters and returns an integer value which is 
 * passed back to the FlagShip application. 
 */ 
UDF_DECL(initmydll)                          // FlagShip function, note 1
{ 
   typedef int (__cdecl   *INITDLL)(void);   // prototype Initdll(), note 2
// typedef int (__stdcall *INITDLL)(void);   // prototype Initdll(), note 2

   int ret = 0;                              // return value from Initdll()
   INITDLL ptrProc;                          // ptr to .DLL function Initdll()
   
   if (hinstLib == NULL) 
      return SET_VAR_INT(VAR_NEW, -1);       // -1 = OpenDll() not invoked yet
   ptrProc = (INITDLL)GetProcAddress(hinstLib, "Initdll"); // ptr to Initdll(), note 3
   if(ptrProc == NULL) 
      return SET_VAR_INT(VAR_NEW, -2);       // -2 = function n/a in .DLL
   ret = (ptrProc)();                        // call Initdll()
   return SET_VAR_INT(VAR_NEW, ret);         // return value converted to FS variable
}

/*******************************************************************
 * FlagShip function
 *    ok := ProcessMyFunction(nPar1, [nPar2], [cPar3])
 * returns .T.=ok, .F.=failure
 * 
 * This example calls the .DLL function declared as
 *    void MyFunction(int par1, double par2, LPSTR par3)
 * which needs 3 parameters and has no return value.
 */ 
UDF_DECL(processmyf)                         // abbreviated! see note 1
{
   typedef void (__cdecl   *MYFUNCT)(int, double, char *);  // note 2
// typedef void (__stdcall *MYFUNCT)(int, double, char *);  // note 2

   int    iPar1;
   double dPar2;
   char  *cPar3;                             // char* is same as LPSTR
   MYFUNCT ptrProc;                          // ptr to .DLL function MyFunction()
   
   if (hinstLib == NULL) 
      return FALSE_VAR;                      // dll not loaded yet
      
   if(argc < 1 || !IS_VAR_NUM(argv[0]))
      return FALSE_VAR;                      // nPar1 is mandatory
   iPar1 = VAR_INTNUM(argv[0]);              // passed nPar1 value
   
   if(argc >= 2 && IS_VAR_NUM(argv[1]))
      dPar2 = VAR_FPNUM(argv[1]);            // passed nPar2 value
   else
      dPar2 = 0.0;                           // default for nPar2

   if(argc >= 3 && IS_VAR_CHR(argv[2]) && !IS_VAR_EMPTY(argv[2]))
      cPar3 = VAR_CHR(argv[2]);              // passed cPar3 != ""
   else
      cPar3 = "Hello";                       // default for cPar3
   
   ptrProc = (MYFUNCT)GetProcAddress(hinstLib, "MyFunction"); // get MyFunction() address, note 3
   if(ptrProc == NULL) 
      return FALSE_VAR;                      // function n/a in .DLL
      
// fprintf(stderr, "calling dll: MyFunction(%d,%f,'%s')\n", iPar1, dPar2, cPar3);  // test  
   
   (ptrProc)(iPar1, dPar2, cPar3);           // call MyFunction(...)
   return TRUE_VAR;                          // ok
}

/*******************************************************************
 * FlagShip function
 *    cRet := MyConvert(cPar1)
 * returns converted string <cPar1> or "" on failure.
 * 
 * This example calls the .DLL function declared as
 *    void MyConvert(char *cIn, char *cOut)
 * which converts string cIn into cOut, where the size of cOut
 * may be twice of cIn and needs to be provided by the caller. 
 */ 
UDF_DECL(myconvert)                          // FlagShip function, note 1
{
   typedef void (__cdecl   *MYCONVERT)(char *, char *);  // note 2
// typedef void (__stdcall *MYCONVERT)(char *, char *);  // note 2

   char  *cIn, *cBuff;                       // char* is same as LPSTR
   FSvar *vRet = VAR_NEW;                    // returned FlagShip variable
   MYCONVERT ptrProc;                        // ptr to .DLL function MyConvert()
   
   if (hinstLib == NULL) 
      return NULLSTR_VAR;                    // dll not loaded yet
      
   if(argc < 1 || !IS_VAR_CHR(argv[0]) || IS_VAR_EMPTY(argv[0]))
      return NULLSTR_VAR;                    // cPar1 is mandatory and != ""
      
   ptrProc = (MYCONVERT)GetProcAddress(hinstLib, "MyConvert"); // get MyConvert() address, note 3
   if(ptrProc == NULL) 
      return NULLSTR_VAR;                    // function n/a in .DLL
      
   cIn   = VAR_CHR(argv[0]);                 // ptr to passed cPar1 string
   cBuff = malloc(strlen(cIn) *2 +1);        // assign buffer on heap for output
   if(cBuff == NULL) 
      return NULLSTR_VAR;                    // not enough memory
   (ptrProc)(cIn, cBuff);                    // call MyConvert(...)
   SET_VAR_CHR(vRet, cBuff);                 // copy output to FlagShip variable
   free(cBuff);                              // free the buffer on heap
   return vRet;                              // return converted string
}

/* eof */

