Level Extreme platform
Subscription
Corporate profile
Products & Services
Support
Legal
Français
Articles
Search: 

Developing FLL libraries
Roberto C. Ianni, July 1, 2004
One of the great limitations I find in VFP is the lack of low-level manipulation I can obtain from it. For many, this might not be an inconvenience, but several times I have encountered a problem which can't be solved with Fox, and it is then that I use a DLL or an FLL.
Summary
One of the great limitations I find in VFP is the lack of low-level manipulation I can obtain from it. For many, this might not be an inconvenience, but several times I have encountered a problem which can't be solved with Fox, and it is then that I use a DLL or an FLL.
Description
Visual FoxPro is a language that has conquered its place in my heart, when it comes to choosing a development language. Perhaps that is because I come from a tradition of passing through dBASE III; Clipper 5.x; Fox DOS and VFP? I don't really think so. I believe it conquered this place because it is a really noble tool, with an object model, inheritance, polymorphism and even a database engine which can be envied by many languages.

Notwithstanding, there are times when I feel limited with VFP. One of the great limitations I find in it is the lack of low-level manipulation I can obtain from it. For many, this might not be an inconvenience, but several times I have encountered a problem which can't be solved with Fox, and it is then that I use a DLL or an FLL. This is what we will try to do today.

To develop a library of this type, we will have to use Visual C++. In my case, I will use VC++ 7.0 in Spanish, but it can be done, without any limitation, with VC++ 6.0.

A little bit of history

Since Fox version 2.0, in the DOS environment, this language has the possibility of using PLB libraries, and in Windows, FLL libraries, With these libraries, the Fox can be powered to really high limits, up to the point where you can develop any type of low-level routine which would be practically impossible otherwise.

Objectives

I consider the following points to be the objectives of this article:

  • FLL vs. DLL.
  • Developing a basic FLL.
  • Passing and receiving parameters.
    • Returning parameters from the function.
  • Advanced development with an FLL.
    • MultiThreading.
    • Access to VFP data.

FLL vs. DLL

An FLL is basically a DLL. However, it exposes two structures of its own ("FoxInfo" and "FoxTable") where it specifies the functions it exports, and that can be used from VFP. The advantage of an FLL over a DLL is that it can easily manage native Fox data, as if we were within a method of a VFP class; a disadvantage is that the library can only be used from VFP, unlike a DLL that can be used from any development tool that is capable of using the Win32 API.

On the other hand, as we said, the FLL can manage cursors and tables easily, and even in a manner similar to the Fox; but it can also send native Fox error messages to the calling application, and even use the API of VFP itself.

The FoxInfo structure is used to communicate VFP with the FLL functions. With this structure, VFP determines the name of the function, and the number and type of its parameters. The FoxTable structure is a linked list that follows the FoxInfo structures, but we will analyze it later.

Developing an FLL

To develop an FLL we will use, as I had already stated, Visual C++, but first we need sume utilities that VFP itself already provides us. These utilities are simply two files:

    Pro_ext.h and Winapims.lib

These files are located in the API directory, in the VFP installation. They participate in our project and have to be copied into the directory where it is created.

Creating the FLL/DLL project:

  1. Open Microsoft Visual Studio.NET.
  2. In the File menu, select New and then Project.
  3. Select "Visual C++ Projects".
  4. In the list of templates, select Win32 Console Application or other Win32 project. (Win32 Project)".
  5. In the dialog, select "Application configuration".
  6. Select Dynamic link library (DLL).
  7. Check the option "Empty project".
  8. Click on "Finish".

    Now, you have to add a new file, so that you can start working on it.

  9. In the File menu, select New followed by File.
  10. Select "Visual C++ projects".
  11. In the list of templates, select "Create a C++ source code file. (C++ (.cpp) file)"
  12. Click on "Open".

With these simple steps, we already have a DLL project made with VC++, but now you must modify it so that it respects the FLL structure, so that it can be read from Fox.

Figure 1: Creating the project

Figure 2: Creating the file

But before we start programming, we must analyze the FoxInfo and FoxTable structures, to see how they work, so that when we start coding, they don't seem strange to us.

FoxInfo Structure

FoxInfo is the structure that is used as a vehicle to communicate the library's internal functions, and their parameter descriptions, between it and Visual FoxPro. A generic FoxInfo structure looks like this:

FoxInfo arrayname[ ] = 
   {   {funcName1, FPFI function1, parmCount1, parmTypes1}
       {funcName2, FPFI function2, parmCount2, parmTypes2}
       {funcNameN, FPFI functionN, parmCountN, parmTypesN} 
   };
  • arrayname

    Variable of type FoxInfo, that contains the functions exposed to VFP, the pointers to the library's internal functions and the number of parameters received by the function.

  • funcName

    Contains the name of the function exposed to VFP. With this name, the user can invoke the function from Visual FoxPro.

  • function

    Pointer to the library function. This is the function's name within the library. It has to be written carefully, for C++ distinguishes between uppercase and lowercase.

  • parmCount

    Specifies the number of parameters that the function can receive, or any of the following constants:

    Value Description
    INTERNAL This is a function that can't be called from VFP, but only used from within the library.
    CALLONLOAD This type of function is called automatically when the function is loaded. This type of function can't call any function that returns results to Visual FoxPro.
    CALLONUNLOAD This type of function is called automatically when the function is released from VFP. This type of function can't call any function that returns results to Visual FoxPro.

  • parmTypes

    Specifies the data type of each parameter. The valid data types are the following:

    Value Description
    "" The function doesn't receive parameters.
    "?" The function accepts any type of parameter; the function itself is responsible for validating the type of the parameter received.
    "C" Parameter of type Character
    "D" Parameter of type Date
    "I" Parameter of type Integer
    "L" Parameter of type Logical
    "N" Parameter of type Numeric
    "R" Reference
    "T" Parameter of type Date and Time
    "Y" Parameter of type Currency
    "O" Parameter of type Object

    You have to include as many types of parameters as the function receives. For example, if you have a function that receives a logical value and a date value, you have to specify parmTypes as "LD". If a parameter is optional, you have to prefix it with a period; however, only the final parameters can be omitted.

FoxTable Structure

The FoxTable structure is a list that keeps information of all the FoxInfo structures that it has for a certain library:

FoxTable _FoxTable = {nextLibrary, infoCount,infoPtr};
  • nextLibrary

    Pointer used internally by Visual FoxPro; it should be initialized to zero.

  • infoCount

    The number of external Visual FoxPro routines defiend in this library.

  • infoPtr

    Address of the first element of an array of FoxInfo structures. This name must match the name listed in the FoxInfo instruction.

FoxInfo myFoxInfo[] =
{
    {"MyFunction", (FPFI) InternalName, 0, ""},
};
extern "C" {
FoxTable _FoxTable =
{
    (FoxTable *) 0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
};

Adapting the DLL to an FLL

As a first step, to achieve this, we use the two files which VFP provides, copying them to the directory where the project was created.

Next, we have to configure the project. There are four steps that we have to carry out in the configuration:

First, we will access the configuration. For this purpose, we start through the menu "Project\Properties", next we select the option "C/C++", and from here, "Code generation". At this point, we remain on "Runtime library", and select, from the list, "

Multiprocess DLL (/MD)".

Figure 3: Project configuration

As a second step, we have to select the option "Advanced", and remain on "Calling convention"; from the list, we select "__fastcall (/Gr)"

Figure 4: Calling convention

As a third step, we have to define the output file. We do this selecting the option "Links", and within this, "General". Here, we select "Result file", and modify the default name; for our case, we would change "BasicFll.dll" to "BasicFll.fll". We do this so that we don't have to rename the file after compiling it.

Figure 5: Output file

The fourth and last step is to add one of the files which Fox provides: "winapims.lib". We do this selecting the option "Entry", we stay on "Additional dependencies" and select the file "winapims.lib".

Figure 6: Linking winapims.lib

Remember that you have to do these configuration steps for every possible compilation mode associated with the project, "Release", "Debug", etc.

With the files in place and the project configured, we can start to program. We will do so on the basis of the blank file we created, defining the basic structure which is required so that the DLL can be recognized as an FLL.

#include "pro_ext.h"
void FAR OnInit(ParamBlk FAR *parm)
{
   MessageBox( NULL, "OnInit", "BasicFll", MB_OK);
}

void FAR OnDestroy(ParamBlk FAR *parm)
{
   MessageBox( NULL, "OnDestroy", "BasicFll", MB_OK);
}

FoxInfo myFoxInfo[] =
{
   {"Init", (FPFI) OnInit, CALLONLOAD, ""},
   {"Destroy", (FPFI) OnDestroy, CALLONUNLOAD, ""},
};
extern "C" {
   FoxTable _FoxTable =
   {
      (FoxTable *) 0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
   };
}

With these steps, we have developed the basic structure of our FLL. Now, we can press "F7" if we have our .NET profile configured as "Visual C++ 6", or we can access the command through the menu "Generate\Generate BasicFll".

Now we only have to use the library from VFP; for this purpose, we use the command SET LIBRARY.

Set Library To "BasicFll.fll"

However, if we use the library, we will encounter an error message like the following:

Figure 7: No functions in the FLL

The reason is that we haven't exposed any function in our library. Therefore, before we can use it from VFP, we have to define and export a function so that it can be used. We will do this with a simple function that creates a file on drive "C:" in the machine, and saves a message.

For this purpose, we have to modify the FoxInfo structure, adding the new function, and then declare it. It will look like this:

FoxInfo myFoxInfo[] =
{
   {"Init", (FPFI) OnInit, CALLONLOAD, ""},
   {"Destroy", (FPFI) OnDestroy, CALLONUNLOAD, ""},
   {"CreateFile", (FPFI) CreateFile, 0, ""},
};

void FAR CreateFile(ParamBlk FAR *parm)
{
   FILE *fo;
   if( (fo = fopen( "C:\\BasicFll.txt", "a+")) != NULL )
   {
      char msg[] = "I have been called from VFP\r\n";
      fwrite( msg, sizeof( char ), strlen(msg), fo );
      fclose( fo );
      _RetInt(1,10);   // Successful exit.-
   }
   else
      _RetInt(0,10);   // Exit with error.-
}
Since in the function CreateFile we have been using two functions from a standard C++ library, we have to include the header file at the beginning of the file.

Figure 8: Loading and unloading the FLL
    

#include 
#include "pro_ext.h"
Now we can recompile the library and use it from VFP. Note that when we finish executing the SET LIBRARY to load the library, it will accept the message which we put into the init; the same happens when the library is unloads:

Once the function is loaded, we can call our function so that it creates the file in case it doesn't exist, and then save our nice message.

? CreateFile()
The function returns 1 (one) if it works correctly, and 0 (zero) if it fails.

We hereby fulfill one of our objectives, which is to create our first basic FLL, so that we can understand how it works and how it is interpreted by VFP. So, if you haven't yet fled in fright, we can continue with this article.

Passing and receiving parameters

VFP can transfer parameters to the FLL by value or by reference. By default, it will respect what is specified in the command SET UDFPARMS, but you can force a parameter to be passed by reference, with the prefix "@", or force the value to be passed by value, enclosing it in parentheses.

In the library, type numer and type of parameters are defined in the FoxInfo structure, but the real function defined in the library only receives a parameter of type ParmBlk, which is a pointer to the parameter block received from VFP. In the following example, we can see how the function "CreateFile" is defined in the FoxInfo structure; this function receives two parameters, but the real function only receives one, a pointer to a parameter block.

FoxInfo myFoxInfo[] =
{
   {"CreateFile", (FPFI) CreateFile, 2, "CC"},
};

void FAR CreateFile(ParamBlk FAR *parm)
{
}
We can find the structure of ParamBlk in the file pro_ext.h; this structure is as follows:
// A paramter list to a library function.
typedef struct {
   short   pCount;         /* Number of Parameters PASSED.*/
   FoxParameter   p[1];      /* pCount Parameters.*/
} ParamBlk;
The structure FoxParameter is the union of two other structures; one is a Value and one a Locator. The first one controls the parameters passed by value, and the second one, the parameters passed by reference.

The structures are defined in the file Pro_ext.h with the following properties:

/* A parameter to a library function.         */
typedef union {
Value val;
Locator loc;
} Parameter

// An expression's value.
Typedef struct {
char        ev_type;
char        ev_padding;
short       ev_width;

unsigned    ev_length;
long        ev_long;
double      ev_real;
CCY         ev_currency;
MHANDLE     ev_handle;
ULONG       ev_object;
} Value;

typedef struct {
char  l_type;
short l_where, /* Database number or -1 for memory */
l_NTI,      /* Variable name table offset*/
l_offset,  /* Index into database*/
l_subs,  /* # subscripts specified 0 <= x <= 2 */
l_sub1, l_sub2; /* subscript integral values */
} Locator;
The following table was obtained from the VFP help file, and shows the type of values that the structure Value can areceive, depending on the type of value passed from Fox.

Data types Field in the structure Value
Character ev_type 'C'
  ev_length length of string
  ev_handle MHANDLE to string
Numeric ev_type 'N'
  ev_width Display width
  ev_length Decimal places
  ev_real Double precision
Integer ev_type 'I'
  ev_width Display width
  ev_long Large integer
Date ev_type 'D'
  ev_real Date1
Date Time ev_type 'T'
  ev_real Date + (seconds/86400.0)
Currency ev_type 'Y'
  ev_width Display width
  ev_currency Value2 of type Currency
Logical ev_type 'L'
  ev_length 0 or 1
Memo ev_type 'M'
  ev_wdith FCHAN
  ev_long Length of memo field
  ev_real Offset of memo field
General ev_type 'G'
  ev_wdith FCHAN
  ev_long Length of general field
  ev_real Offset of general field

Data type Structure field Value
Object ev_type 'O'
  ev_object Object identifier
Null ev_type '0' (zero)
  ev_long Data type

The following table is also from the VFP help, and shows what types of values the structure Locator can receive when a parameter is passed by reference from VFP.

Locator field Use of the field
l_type 'R'
l_where Number of table that contains this field, or -1 for a variable
l_NTI Index of the name table. For internal use of Visual FoxPro.
l_offset Number of field in the table. Internal use of Visual FoxPro.
l_subs Only for variables, the number of indices (0 - 2).
l_sub1 Only for variables, the first index if l_subs is not equal to 0.
l_sub2 Only for variables, the second index if l_subs is equal to 2.

Now, after getting a feel of the structures, we realice that the function "CreateFile" is responsible of managing the parameters; it will have to access the parameters one position as a time, as with an array.

To try this out, we create a new project called FllParam. The way it is created and configured is the same as we already saw, to be able to pass it parameters and receive values.

#include 
#include "pro_ext.h"

char FAR *NullTerminate(Value FAR *cVal)
{
    char *NewChar;
    if (!_SetHandSize(cVal->ev_handle, cVal->ev_length + 1))
       _Error(182);
   ((char FAR *) _HandToPtr(cVal->ev_handle))[cVal->ev_length] = '\0';
    NewChar = (char FAR *) _HandToPtr(cVal->ev_handle);
    return NewChar; 
}

void FAR CrearArchivo(ParamBlk FAR *parm)
{
   char FAR *sFileName = NullTerminate(&parm->p[0].val);  
    char FAR *sMsg      = NullTerminate(&parm->p[1].val);
   FILE *fo;
   if( (fo = fopen( sFileName, "a+")) != NULL )
   {
      fwrite( sMsg, sizeof( char ), strlen(sMsg), fo );
      fclose( fo );
      _RetInt(1,10);   // Exit successfully
   }
   else
      _RetInt(0,10);   // Exit with error.
}

FoxInfo myFoxInfo[] =
{
    {"CreateFile", (FPFI) CreateFile, 2, "CC"},
};
extern "C" {
FoxTable _FoxTable =
{
    (FoxTable *) 0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
};
} 
Ww can immediately see that the new library is similar to BasicFll; the difference is that now, function CreateFile manages to parameters by value; the first is the filename, and the second, the message to be saved in the file. The differences reside in the FoxInfo structure, where the function is now listed as receiving two parameters, both of type character; the other difference is in the real function "CreateFile", where these parameters are used.

If we run the following instructions from VFP, we can see that now we can pass the parameters we define, and that they really do what we intended.

set library to "FllParam.fll"
? CreateFile("C:\LogRoberto.txt", "Ianni - Message from VFP (With parameters)")
set library to
We can see that on disk "C" of the PC where the instruction was executed, the new file has been created, with the message we passed as a parameter.

Now, we want to analyze the changes made in more detail. You can see that the parameters are in the array "parm->p", received as parameter, as we have seen, but when we assign the parameter value to our local variables, we do it through an internal function called NullTerminate.

char FAR *sFileName = NullTerminate(&parm->p[0].val);  
We do this so that the character string received as a parameter will always end with the character 0 (zero), which indicates, to C++, that the string has finished. If we now analyze the function NullTerminate, we will find some calls to the functions _SetHandSize; _HandToPtr and _Error. These functions are compiled within the file winapims.lib; they are functions of the Visual FoxPro API, for the following purposes:

_SetHandSize: Used to change the amound of memory assigned to the memory block controller. This is similar to redimensioning the size of the variable.

_HandToPtr: Converts a VFP memory controller to a FAR (32-bit) pointer, that points to the memory assigned to this controller. In summary, a pointer to the variable.

_Error: Indicates the error specified by the value 'code', when Visual FoxPro executes.

char FAR *NullTerminate(Value FAR *cVal)
{
    char *NewChar;
    if (!_SetHandSize(cVal->ev_handle, cVal->ev_length + 1))
       _Error(182);
   ((char FAR *) _HandToPtr(cVal->ev_handle))[cVal->ev_length] = '\0';
    NewChar = (char FAR *) _HandToPtr(cVal->ev_handle);
    return NewChar; 
}

What our function does, then, is the following: first, redimension the string received as a parameter, then, add an additional character, which will be used to place the string terminator required by C++. Then, it uses the function _HandtoPtr to know which is the initial memory position used by the string; on the basis of this position, it goes to the right as many bytes as the redimensioned string has, and in this position, it marks the end of the string. Finally, it uses the function _HandToPtr to obtain the starting position of the string, assigning it to a variable and returning it to the calling function.

In this function we also see that if we don't redimension the variable, we will return to VFP with an error indicating insufficient memory.

We have thus resolved the problem of passing parameters by value from VFP to the function, but we still have to resolve the problem of passing by reference; that is, pass a value, have the function modify it, and return it, modified, to VFP. Let's remember that for this purpose we have the assistance of the Locator structure, in the parameters. Let's see an example.

void FAR MiniProper(ParamBlk FAR *parm)
{
   char FAR *pString;
   Value val;
   _Load(&parm->p[0].loc, &val);

   // Verify if it is a character
   if (val.ev_type != 'C')
      _Error(9); // "Data type mismatch"

   pString = (char*)_HandToPtr(val.ev_handle);
   if( val.ev_length > 0 )
      if ('a' <= *pString && *pString <= 'z')
         pString[0] = pString[0] - 32;

   _Store(&parm->p[0].loc, &val);
   _FreeHand(val.ev_handle);
}

FoxInfo myFoxInfo[] = {
   {"MiniProper", (FPFI) MiniProper, 1, "R"},
};
extern "C" {
FoxTable _FoxTable = {
(FoxTable FAR *) 0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
};
}
With this function we can pass a variable from VFP, and have the function take responsibility of modifying it and returning it modified to Fox. We can see that the development of the function basically consists of four fundamental parts.

The first one is its declaration in the FoxInfo structure. Here, we have to define the parameter type as "R".

{"MiniProper", (FPFI) MiniProper, 1, "R"},
The second, third and fourth steps are calls to VFP API function, to manage the string passed as a parameter. These are:
  • _Load
  • _Store
  • _FreeHand

The function _Load copies the value of the variable passed by reference to a variable of type "Value", so that we can work with it in the normal manner.

Next, function _Store does the opposite of _Load. It copies the value of a variable of type "Value" to a reference variable. Finally, the function _FreeHand frees a memory block.

Now that we know what these functions do, the code block is quite simple in the way it works. If we observe a little more, we will see that what the function does is replace the first letter of the string by the same letter in uppercase, the equivalent of the Proper() function in VFP, but only with the first letter.

Var = "Ianni, Roberto"
? MiniProper( @Var )
? Var && Ianni, Roberto

Returning parameters from the function

As we have seen up until now, in our examples, the function CreateFile() returns an integer value (1 or 0) to VFP, indicating the the function finished its work correctly, or not. But this is not the only the only type of value an FLL can return. It can return values of type string, float, logical, etc.

To return these values, we can't use the native C or C++ commands; instead, we have to make use of the VFP API functions. Here is a list of functions to use:

Function Description
_RetChar(char *string) Returns a string character, which has to terminate with a null character.
_RetCurrency(CCY cval, int width) Returns a value of type Currency.
_RetDateStr(char *string) Returns a value of type Date. The date is specified in the format dd/mm/yy[yy].
_RetDateTimeStr(char *string) Returns a value of type DateTime. Specified in the format mm/dd/yy[yy] hh:mm:ss.
_RetFloat(double flt, int width, int dec) Returns a value of type Float.
_RetInt(long ival, int width) Returns a value of type Integer.
_RetLogical(int flag) Returns a value of type Logical. Zero is interpreted as FALSE. Any value other than zero is interpreted as TRUE.

Once we know these details, we can create a few small functions that return this type of information, to see how they would be coded. For this purpose, we create a single project called FllRetVals, that returns this data type based on the examples in the VFP manuals.

#include "pro_ext.h"
#include 

void FAR ReturnChar(ParamBlk FAR *parm)
{
   char message[] = "Hello, I am Roberto";
   _RetChar(message);
}

void FAR ReturnCurrency(ParamBlk FAR *parm)
{
   CCY money;
   money.HighPart = (long) (parm->p[0].val.ev_real*10000 / pow(2,32));
   money.LowPart = (unsigned long) 
    ((parm->p[0].val.ev_real - (double) (money.HighPart * pow(2,32)/10000.0)) *10000);
   _RetCurrency(money,25);
}

void FAR ReturnDate(ParamBlk FAR *parm)
{
   MHANDLE mh;
   char FAR *instring;
   if ((mh = _AllocHand(parm->p[0].val.ev_length + 1)) == 0)
   {
   _Error(182); // "Insufficient memory"
   }
   _HLock(parm->p[0].val.ev_handle);
   instring = (char*)_HandToPtr(parm->p[0].val.ev_handle);
   instring[parm->p[0].val.ev_length] = '\0';
   _RetDateStr(instring);
   _HUnLock(parm->p[0].val.ev_handle);
}

void FAR ReturnDateTime(ParamBlk FAR *parm)
{
   MHANDLE mh;
   char FAR *instring;
   if ((mh = _AllocHand(parm->p[0].val.ev_length + 1)) == 0) {
   _Error(182); // "Insufficient memory"
   }
   _HLock(parm->p[0].val.ev_handle);
   instring = (char*)_HandToPtr(parm->p[0].val.ev_handle);
   instring[parm->p[0].val.ev_length] = '\0';
   _RetDateTimeStr(instring);
   _HUnLock(parm->p[0].val.ev_handle);
}
void FAR ReturnFloat(ParamBlk FAR *parm)
{
   _RetFloat(parm->p[0].val.ev_real, 20, 4);
}

void FAR ReturnInt(ParamBlk FAR *parm)
{
   _RetInt(rand(),10);
}

void FAR ReturnLogical(ParamBlk FAR *parm)
{
   _RetLogical(rand() % 2);
}

FoxInfo myFoxInfo[] = {
   {"ReturnChar", (FPFI) ReturnChar, 0, ""},
   {"ReturnCurrency", (FPFI) ReturnCurrency, 1, "N"},
   {"ReturnDate", (FPFI) ReturnDate, 1, "C"},
   {"ReturnDateTime", (FPFI) ReturnDateTime, 1, "C"},
   {"ReturnFloat", (FPFI) ReturnFloat, 1, "D"},
   {"ReturnInt", (FPFI) ReturnInt, 0, ""},
   {"ReturnLogical", (FPFI) ReturnLogical, 0, ""},
};
extern "C" {
FoxTable _FoxTable = {
(FoxTable FAR *) 0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
};
}
If we call these examples from VFP, we obtain something like this:

SET LIBRARY TO FllRetVals.fll
? ReturnChar()                         && Hello, I am Roberto
? ReturnCurrency(200.1)                && 200.1000
? ReturnDate("06/06/04")               && 04/06/04
? ReturnDateTime("06/06/04 12:07am")   && 06/06/04 12:07AM
? ReturnFloat({06/06/04})              && 2416638.0000
? ReturnInt()                          && 1..2..2..9
? ReturnLogical()                      && .T. .T. .F. .F. .F. .T. .T. .F.
SET LIBRARY TO

Conclusion

You see that developing an FLL isn't complex, you only have to know some C++ and know what it is you want to do.

At this point, we should have all the concepts clear, about how to develop an FLL, and how to interact with it, from VFP. In the following article, we will explain some advanced techniques for programming an FLL that allows multiple threads, access to Fox data, and other things.

Source Code

Roberto C. Ianni, Banco Hipotecario
Roberto Ianni (Buenos Aires, Argentina) is a professional software developer for the last 4 years. He has programmed in C++, VFP, VC++, C#, and has lots of experience in VFP and VC++. He currently works in the Banco Hipotecario as an Analyst-Developer.
More articles from this author
Roberto C. Ianni, December 1, 2004
Today we are going to talk about data compression. This is something we do frequently, something which is useful for many different tasks.
Roberto C. Ianni, June 1, 2004
The "EventLog" or "Event viewer" is something we use on a daily basis, to obtain information about what happened to our computer at a certain moment; for example, when an application generates an error, most of us will intuitively look at the EventLog to see what information it left for us.
Roberto C. Ianni, May 1, 2004
The "EventLog" or "Event viewer" is something we use every day, to obtain information about what happened to our computer at a certain moment. For example, when an application generates an error, most of us instinctively go to the EventLog to see what information it left for us.
Roberto C. Ianni, August 1, 2004
As an objective for this issue, I want to propose the following points, to complete the first part of the development of an FLL. Advanced development with an FLL; Accessing VFP data; Executing VFP commands from a FLL; MultiThreading; FLL with VFP 9.0; Executing Assembler from VFP.
Roberto C. Ianni, October 1, 2004
This article is the continuation of "Sending email through Visual FoxPro without additional components", published last March. Back when I wrote this article, I didn't think it would get such a huge response, but it did, hence, due to the mails coming from everywhere I decided to write again on that...
Roberto C. Ianni, November 1, 2004
In this second part we will learn how to: Implement MIME, Attach binary files, the final implementation of the WSendMail class, and the use of the WSendMail class.
Roberto C. Ianni, May 1, 2005
Windows Control Panel holds the configuration from several of the operating system's applications, and from some other applications that provide an applet to appear there. Learn how to build this kind of applets to give your application a professional configuration mechanism.
Roberto C. Ianni, April 1, 2005
The intention of this article is to finish with the use of POP3 (Post Office Protocol 3), implementing everything we have seen so far.
Roberto C. Ianni, January 1, 2005
This article is the beginning of receiving email from VFP; this is complemented with the articles you have already seen about sending email from VFP. To be able to do this, we will have to use the POP3 protocol (Post Office Protocol 3).
Roberto C. Ianni, February 1, 2005
We will continue some of the points mentioned in the previous article, in order to be able to implement message reception. The points to be developed are: POP3 Authentication (both Flat and MD5 authentication methods) and EMail parsing.
Roberto C. Ianni, March 1, 2004
Many of us send email using third-party tools such as Outlook Express or Microsoft Outlook, among others, and most likely have encountered registration problems in the client, licencing problems, OLE errors that only occurr at the client's site but not in our developer environment. Also problems if ...
Roberto C. Ianni, August 1, 2006
The present article is one of those that Martín Salías calls "low level fox", the general idea is to develop a service for the operative system whose only goal will be to run a VFP application in charge of handling the business logic.