Chapter 28: Accessing the Visual FoxPro API

If Visual FoxPro doesn't already include the features you require for your application, you can extend its capabilities by creating an ActiveX control (.ocx file) or library (.fll file) specific to Visual FoxPro, using a 32-bit compiler such as Microsoft Visual C++® version 4.0 and greater. The information in this chapter addresses both types of programs.

Note   If you're using Visual C++ version 2.x to develop an ActiveX control, you need the Control Development Kit. The procedures in this chapter assume Visual C++ version 4.0.

For information about using ActiveX controls or FLLs, see Chapter 27, Extending Visual FoxPro with External Libraries.

This chapter discusses:

Creating a Library or ActiveX Object

You can extend the capabilities of Visual FoxPro by creating programs in C or C++ that accomplish tasks required by your application. For example, if your application requires direct access to Windows facilities, you can write a C or C++ program that makes calls to the Windows API, then returns information to Visual FoxPro.

You can create three types of programs to access the Visual FoxPro API:

Each type of program has advantages. An ActiveX control:

The advantages of COM objects:

On the other hand, an .fll library:

Creating a Basic ActiveX Object

You can create COM objects with the ActiveX Template Library provided with Microsoft Visual C++ 5.0. For more information about creating COM objects with Visual C++ 5.0, search for “ATL” in the MSDN Library.

You create ActiveX controls specific to Visual FoxPro as you would any similar control. Most C++ compilers allow you to create skeletons of the control, and they can also be created with the Microsoft Visual Basic® Control Creation Edition version 5.0.

The follow sections describe the steps for creating an ActiveX control with Microsoft Visual C++for use in Visual FoxPro.

To create a project for the ActiveX control

  1. Start Microsoft Visual C++.

  2. From the File menu, choose New.

  3. In the New dialog box, choose Project Workspace.

  4. In the New Project Workspace dialog box, specify a project name.

  5. In the Type list, choose OLE ControlWizard.

  6. Choose Create, and then follow the steps in the wizard.

When the wizard is finished, you can build the ActiveX control immediately. However, you'll also need to define properties and methods for the control.

To add properties and methods to the ActiveX control

  1. From the View menu, choose ClassWizard.

  2. Choose the OLEAutomation tab.

  3. Choose Add Method or Add Property.

  4. Fill in the name, parameter, and other information required by the element you are creating, and then choose OK.

  5. Choose Edit Code to display the editor, and then enter the code that defines the property or method you're creating.

For example, to create a Version property that returns the .ocx file version as an integer (such as 101), you create the property with a return type of long, and add code similar to the following:

#define VERSION 101

long CPyCtrl::GetVersion()
{
   // set the version number here
   return VERSION;
}

Because the version number is ordinarily read-only, you wouldn't create a SetVersion( ) function.

Creating a Basic FLL Library

Because an FLL library is essentially a DLL with calls to the Visual FoxPro API, you create an FLL library by following the steps in your development environment for creating a DLL.

To create a project for the FLL library

  1. Start Microsoft Visual C/C++.

  2. From the File menu, choose New.

  3. In the New dialog box, choose Project Workspace.

  4. In the New Project Workspace dialog box, specify a project name.

  5. In the Type list, choose Dynamic-Link Library.

After creating the basic DLL structure, you add the functions you want to be able to call from Visual FoxPro. The following sections provide skeletons for creating functions in both C and C++.

Setting Up a Library Template

Each function library that you create has the same basic structure. By using a template for the structure, all you have to do is fill in the blanks that apply to your specific library routine.

There are five elements in a Visual FoxPro library template:

  1. #include statement

  2. Function definition

  3. Function code

  4. FoxInfo structure

  5. FoxTable structure

A Sample C Template

You can use the following template to create libraries written in C:

#include <Pro_ext.h>

void Internal_Name(ParamBlk *parm)
{
// function code goes here.
}

FoxInfo myFoxInfo[] = {
   {"FUNC_NAME", (FPFI) Internal_Name, 0, ""},
};

FoxTable _FoxTable = {
   (FoxTable *)0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
};

A Sample C++ Template

For C++ routines, you can use the following template. This template differs from the C template because it declares the FoxTable structure as external:

#include <Pro_ext.h>

void Internal_Name(ParamBlk  *parm)
{
// function code goes here.
}

   FoxInfo myFoxInfo[] = {
      {"FUNC_NAME", (FPFI) Internal_Name, 0, ""},
   };

extern "C" {
   FoxTable _FoxTable = {
      (FoxTable *)0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
   };
}

Using the Template

To use the header file and create a compiled library, you need:

Both of these files are installed in the API subdirectory when you install Visual FoxPro.

The function definition returns void and expects the following parameter: ParamBlk *parm. The ParamBlk structure is discussed under Passing and Receiving Parameters later in this chapter.

Other than the files listed above, the only other required elements of a Visual FoxPro library are the FoxInfo and FoxTable structures.

Using FoxInfo and FoxTable Structures

Your library functions communicate with Visual FoxPro through the FoxInfo structure. From this structure, Visual FoxPro determines the function name and the number and type of parameters. The FoxTable structure is a linked list that keeps track of the FoxInfo structures. See Pro_ext.h in the Visual FoxPro API directory for the FoxInfo and FoxTable struct definitions.

FoxInfo Structure

The FoxInfo structure is the vehicle used to communicate function names and parameter descriptions between Visual FoxPro and your library. A generic FoxInfo structure looks like this:

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

The placeholders are defined as follows:

arrayname

A variable of type FoxInfo. Note that you can include several FoxInfo structure lines in this array.

funcName

Contains the name (in uppercase and no more than 10 characters) that the Visual FoxPro user calls to invoke your function.

function

The address of your C language routine. This is the exact (case-sensitive) name you use to define your function.

parmCount

Specifies the number of parameters described in the parmTypes string or one of the following flag values.

Value Description
INTERNAL Specifies that the function cannot be called directly from Visual FoxPro.
CALLONLOAD Specifies that the routine is to be called when the library is loaded. CALLONLOAD can’t call any routine that returns results to Visual FoxPro.
CALLONUNLOAD Specifies that the routine is to be called when the library is unloaded or when the Visual FoxPro QUIT command is issued. CALLONUNLOAD cannot call any routine that returns results to Visual FoxPro.

parmTypes

Describes the data type of each parameter. The following table lists the valid values for parmTypes.

Value Description
""
No parameter
"?"
Any type can be passed. In the body of the function, you'll need to check the type of the passed parameter.
"C"
Character type parameter
"D"
Date type parameter
"I"
Integer type parameter
"L"
Logical type parameter
"N"
Numeric type parameter
"R"
Reference
"T"
DateTime type parameter
"Y"
Currency type parameter
"O"
Object type parameter

Include a type value for each parameter passed to the library. For example, if you create a function that accepts a character and a numeric parameter, substitute “CN” for parmType.

Note   To indicate that a parameter is optional, precede it with a period. Only trailing parameters can be omitted.

The following FoxInfo structure defines a library with one function — internally called dates and externally accessed as DATES — that accepts one Character type parameter:

FoxInfo myFoxInfo[] = {
   { "DATES", (FPFI) dates, 1, "C" }
};

When you've compiled the library with this FoxInfo structure and loaded it in Visual FoxPro with the SET LIBRARY TO command, you can call this function in Visual FoxPro with the following line of code:

=DATES("01/01/95")

FoxTable Structure

The FoxTable structure is a linked list that keeps track of all the FoxInfo structures you have for a given library:

FoxTable _FoxTable = {nextLibrary, infoCount,infoPtr};

where the placeholders are defined as follows:

nextLibrary

A pointer used internally by Visual FoxPro; should be initialized to 0.

infoCount

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

infoPtr

The address of the first element of an array of FoxInfo structures. This name must match the array name listed in the FoxInfo statement.

The following is an example of a FoxTable statement. If your FoxInfo array name is myFoxInfo, you'll never need to change this statement:

FoxTable _FoxTable = {
   (FoxTable  *) 0,
   sizeof( myFoxInfo) / sizeof( FoxInfo ),
   myFoxInfo
};

Adding Visual FoxPro API Calls

To integrate your program with Visual FoxPro, you can call Visual FoxPro API routines. These API routines are functions you can call from any C or C++ program, including an .ocx or .fll file, that give you access to variables, manage database operations, and accomplish many other Visual FoxPro-specific tasks.

The following table lists the general categories of API calls available in Visual FoxPro. For details about individual API functions, see API Library Routines A-Z or API Library Routines by Category.

To use the Visual FoxPro API routines, you must include the file Pro_ext.h, available in the Visual FoxPro API directory. This file includes the prototypes for the functions and structures that allow you to share information with Visual FoxPro.

If you're writing an ActiveX control, you must also add calls to initialize and clear the API.

To add Visual FoxPro API routines to your ActiveX object

  1. Use #INCLUDE to include the Pro_ext.h file along with any other required header files.

  2. In the Constructor (Init method) of the control, call _OCXAPI( ) to initialize the interface to Visual FoxPro using this code:
    _OCXAPI(AfxGetInstanceHandle(),DLL_PROCESS_ATTACH);
    
  3. Include calls to the Visual FoxPro API as required in your object.

  4. In the Destructor (Destroy method) for the object, call _OCXAPI( ) again to release the process created in the Constructor, using this code:
    _OCXAPI(AfxGetInstanceHandle(),DLL_PROCESS_DETACH);
    

For an example .ocx file that includes calls to the Visual FoxPro API, see Foxtlib.ocx. For an example of an .fll library that includes calls to the Visual FoxPro API, see the sample programs in Vfp98\Api\Samples directory that have the extension C: EVENT.C, HELLO.C, and so on.

If you use Visual FoxPro API calls in your ActiveX control, COM object, or .fll library, the code containing the calls is incompatible with other applications. You might therefore want to build one or more tests into the program to determine whether the object is being called from Visual FoxPro.

For example, if you're creating an ActiveX control using the Microsoft Foundation Classes, you can change the control’s constructor code to include a test and then alert the user if the control has been called from a program other than Visual FoxPro:

if (!_OCXAPI(AfxGetInstanceHandle(),DLL_PROCESS_ATTACH))
{
   ::MessageBox(0,"This OCX can only be hosted by Visual Foxpro","",0);
      //Here you can do whatever you want when the host isn't VFP:
      // you might want to reject loading or you 
      // might want to set a property
      // saying that the host isn't VFP and the control will use other 
      // means to achieve it's purpose.
}

If you're creating an ActiveX control using the Microsoft ActiveX Template Library, use the following code:

if (!_OCXAPI(_Module.GetModuleInstance(),DLL_PROCESS_ATTACH))
{
   ::MessageBox(0,"This OCX can only be hosted by Visual Foxpro","",0);
      //Here you can do whatever you want when the host isn't VFP:
      // you might want to reject loading or you 
      // might want to set a property
      // saying that the host isn't VFP and the control will use other 
      // means to achieve it's purpose.
}

In this example, the control doesn't exit, and will continue running after the user has acknowledged the message. The strategy you choose depends on how you anticipate the control will be used. For example, if you detect that the control is being used outside of Visual FoxPro, you can set a flag that you test at each point in the control where you call the Visual FoxPro API. If the flag indicates that the control is outside Visual FoxPro, you can branch around the API call to an alternative means of accomplishing the same task.

Passing and Receiving Parameters

When your program is called from Visual FoxPro, it can receive parameters. For example, an ActiveX control might receive parameters when one of its methods is invoked. Similarly, a Visual FoxPro program might call a function in your FLL library and pass parameters to it.

Visual FoxPro can pass parameters to your program by value or by reference. By default, parameters respect the setting made with SET UDFPARMS. Other variables (such as arrays or fields) and expressions are passed by value.

To force a parameter to be passed by reference, precede the variable reference with the @ operator. To force a parameter to be passed by value, enclose it in parentheses.

Note   In Visual FoxPro, individual array elements are always passed by value. When SET UDFPARMS is set to VALUE and no array element is specified, the array name by itself refers to the first element of the array (unless it is prefixed with @).

Because ActiveX controls and COM objects are Windows-standard programs, no special mechanism is required to pass parameters from Visual FoxPro and your program. You can write the program as if it were receiving parameters from any C or C++ program.

In contrast, functions in an FLL library use the FoxInfo structure to receive data from Visual FoxPro. The FoxInfo structure lists your library functions and the number and type of parameters they expect. For example, the following FoxInfo structure belongs to a library with one function, internally called dates, that accepts one Character parameter:

FoxInfo myFoxInfo[] = {
   { "DATES", (FPFI) dates, 1, "C" }
};

Functions you define in your libraries actually receive only one parameter, a pointer to the parameter block. This parameter block, defined in the ParamBlk structure, holds all the information about the parameters that were passed from the Visual FoxPro function call. Your function declaration follows this format:

void function_name(ParamBlk *parm)

For example, the function definition for dates is:

void dates(ParamBlk *parm)

The ParamBlk structure consists of an integer that represents the number of parameters, immediately followed by an array of parameter unions. The structure definition is included in Pro_ext.h:

/* A parameter list to a library function.      */
typedef struct {
   short int pCount;      /* number of parameters passed */
   Parameter p[1];         /* pCount parameters */
} ParamBlk;

The Parameter typedef included in the ParamBlk structure is a union of a Value structure and a Locator structure. Call by value is handled by a Value structure; call by reference is handled by a Locator structure. You use these structures to access the parameters passed to your function when the function is called in Visual FoxPro.

The following information is extracted from the file Pro_ext.h and shows the definition of the Parameter type:

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

Value Structure Definition

If a parameter is passed to your function by value, use the Value structure to access it. The following Value structure definition is extracted from the Pro_ext.h file:

// 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;

Value Structure Fields

The following table is a guide to the values you can pass and receive in the Value structure for different types of data. Only the structure fields listed for a data type are used for that data type.

Contents of Value structure for different data types

Data type Structure field Value
Character
ev_type
‘C’
ev_length
string length
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
Long 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
Currency value2
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
Object
ev_type
‘O’
ev_object
Object identifier
Null
ev_type
‘0’ (zero)
ev_long
Data type

1. The date is represented as a double-precision floating-point Julian day number calculated using Algorithm 199 from Collected Algorithms of the ACM.
2. The currency value is a long integer, with an implied decimal point in front of the last four digits.

Note   ev_length is the only true indicator of a string’s length. The string can’t have a null terminator because the string can contain embedded null characters.

Locator Structure Definition

Use the Locator structure to manipulate parameters passed by reference. The following Locator structure definition is extracted from the Pro_ext.h file:

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;

Locator Structure Fields

The following table is a guide to the fields in the Locator structure.

Locator field Field use
l_type
'R'
l_where
The number of the table containing this field, or – 1 for a variable.
l_NTI
Name Table Index. Visual FoxPro internal use.
l_offset
Field number within table. Visual FoxPro internal use.
l_subs
For variables only, the number of subscripts (0 – 2).
l_sub1
For variables only, the first subscript if l_subs is not 0.
l_sub2
For variables only, the second subscript if l_subs is 2.

Note   It's good programming practice to check for the parameter type in ev_type to help determine which fields to access from the Value structure.

An Example of Accessing Parameters in an FLL Library

The following example uses _StrCpy( ) to return a Character type to Visual FoxPro that's the concatenation of its two Character parameters. Notice that although the handle of each parameter’s Value structure is used as working memory to perform the concatenation, changes to this memory allocation don't affect the Visual FoxPro argument that was passed by value.

For an example that uses the Locator structure to manage a parameter passed by reference, see Returning a Value from an FLL Library later in this chapter.

#include <Pro_ext.h>

Example(ParamBlk *parm)
{
// make the paramBlk structure easier
// to manage by using #define shortcuts
#define p0 (parm->p[0].val)
#define p1 (parm->p[1].val)

// make sure there is enough memory
if (!_SetHandSize(p0.ev_handle, p0.ev_length + p1.ev_length))
   _Error(182); // "Insufficient memory"

// lock the handles
_HLock(p0.ev_handle);
_HLock(p1.ev_handle);

// convert handles to pointers and make sure the 
// strings are null-terminated
((char *)_HandToPtr(p0.ev_handle))[p0.ev_length] = '\0';
((char *)_HandToPtr(p1.ev_handle))[p1.ev_length] = '\0';

// concatenate strings using the API function _StrCpy
_StrCpy((char *)_HandToPtr(p0.ev_handle) + p0.ev_length,
_HandToPtr(p1.ev_handle));

// return the concatenated string to Visual FoxPro
_RetChar(_HandToPtr(p0.ev_handle));

// unlock the handles
_HUnLock(p0.ev_handle);
_HUnLock(p1.ev_handle);
}

FoxInfo myFoxInfo[] = {
   {"STRCAT", Example, 2, "CC"},
};

FoxTable _FoxTable = {
   (FoxTable *) 0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
};

Returning a Value to Visual FoxPro

The method you use to return a value from your program to Visual FoxPro depends on whether yo u're creating an ActiveX control or an FLL library.

Returning a Value from an ActiveX Control

To return a value from the ActiveX control to Visual FoxPro, use the RETURN statement in the control, passing a single value, as in the following example:

#define VERSION 101

// other code here

long CPyCtrl::GetVersion()
{
   // set the version number here in variable fVersion
   return VERSION;
}

Returning a Value from an FLL Library

To return values from an FLL library, use API functions, not native C or C++ commands. The following functions allow you to return values to Visual FoxPro.

Note   Don't use the following API function to return a value from an .ocx file; use the RETURN statement. The API return functions should only be used in FLL libraries.

Function Description
_RetChar(char *string) Sets the function return value to a null-terminated string.
_RetCurrency(CCY cval, int width) Sets the function return value to a currency value.
_RetDateStr(char *string) Sets the function return value to a date. The date is specified in mm/dd/yy[yy] format.
_RetDateTimeStr(char *string) Sets the function return value to a date and time specified in mm/dd/yy[yy] hh:mm:ss format.
_RetFloat(double flt, int width, int dec) Sets the function return value to a float value.
_RetInt(long ival, int width) Sets the function return value to a numeric value.
_RetLogical(int flag) Sets the function return value to a logical value. Zero is considered FALSE. Any non-zero value is considered TRUE.
_RetVal(Value *val) Passes a complete Visual FoxPro Value structure; any Visual FoxPro data type except for memo can be returned. You must call _RetVal( ) to return a string that contains embedded null characters or to return a .NULL. value.

Note   To return the value of an object data type, use the _RetVal() function, filling in the ev_object field in the Value structure.

The following example, Sum, accepts a reference to a numeric field in a table and uses _RetFloat to return the sum of the values in the field:

#include <Pro_ext.h>

Sum(ParamBlk *parm)
{
// declare variables
double tot = 0, rec_cnt;
int i = 0, workarea = -1; // -1 is current workarea
Value val;

// GO TOP
_DBRewind(workarea);

// Get RECCOUNT( )
rec_cnt = _DBRecCount(workarea);

// Loop through table
for(i = 0; i < rec_cnt; i++)
{ 
   //Place value of the field into the Value structure
   _Load(&parm->p[0].loc, &val);

   // add the value to the cumulative total
   tot += val.ev_real;

   // SKIP 1 in the workarea
   _DBSkip(workarea, 1);
} 

// Return the sum value to Visual FoxPro
_RetFloat(tot, 10, 4); 
}
// The Sum function receives one Reference parameter
FoxInfo myFoxInfo[] = {
   {"SUM", Sum, 1,"R"} 
};
FoxTable _FoxTable = {
   (FoxTable *) 0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
};

Assuming there's a numeric field named amount in the currently open table, the following line of code in a Visual FoxPro program calls the function:

? SUM(@amount)

Passing Parameters to Visual FoxPro API Functions

Often the Visual FoxPro API routines will require parameters of a particular Visual FoxPro data structure. The following sections provide a list of Visual FoxPro data types and additional data structures. For the actual type definitions and structure definitions, refer to the Pro_ext.h file.

Visual FoxPro API Data Types

The following data types are used in Visual FoxPro API routines.

Data type Description
EDLINE
The number of a line in an open file in an editing window. The first line is 1.
EDPOS
The offset position of a character in an open file in an editing window. The offset position of the first character in the file or memo field is 0.
FCHAN
File channel. Every file opened by Visual FoxPro, or through the API by using _FCreate( ) and _FOpen( ), is assigned an FCHAN.
FPFI
A 32-bit pointer to a function returning an integer.
ITEMID
A unique identifier assigned to a single command on a menu.
MENUID
A unique identifier assigned to a menu.
MHANDLE
A unique identifier given to every block of memory allocated by Visual FoxPro, or allocated through the API using _AllocHand( ). It can be de-referenced to its pointer using _HandToPtr( ).
NTI
Name table index. Every variable and table field’s name has an entry in this table.
WHANDLE
Window handle. A unique identifier assigned to every window opened by Visual FoxPro, or opened through the API using _WOpen( ).

Note   Because FAR pointers are not appropriate for 32-bit compilers, #define statements in Pro_ext.h redefine FAR, _far, and __far as null values.

Visual FoxPro API Data Structures

The primary data structures used in the Visual FoxPro API library are listed in the following table.

Structure Description
EventRec
A structure used to describe what the system is doing at a given time.
FoxInfo
Used in FLL libraries for communicating between Visual FoxPro and your program; not used in .ocx files. Discussed under Using FoxInfo and FoxTable Structures earlier in this chapter.
FoxTable
Used in FLL libraries for communicating between Visual FoxPro and your program; not used in .ocx files. Discussed under Using FoxInfo and FoxTable Structures earlier in this chapter.
Locator
A structure used to access parameter values (FLL) or Visual FoxPro variables or fields (FLL and ocx).
ParamBlk
Used in FLL libraries for communicating between Visual FoxPro and your program; not used in .ocx files. Discussed under Using FoxInfo and FoxTable Structures earlier in this chapter.
Parameter
Used in FLL libraries for communicating between Visual FoxPro and your program; not used in .ocx files. Discussed under Using FoxInfo and FoxTable Structures earlier in this chapter.
Point
A structure that defines the horizontal and vertical coordinates of a single point on the screen. Coordinates are specified in rows and columns.
Rect
A structure that defines the coordinates of a rectangle on the screen. The upper-left corner of the rectangle is defined by (top,left) and the lower-right corner is defined by (bottom-1,right-1). Coordinates are specified in rows and columns.
Value
A structure used to access parameter values (FLL) or Visual FoxPro variables or fields (FLL and OCX).

Accessing Visual FoxPro Variables and Fields

You can access Visual FoxPro variables or field values in your ActiveX control or FLL function, either to read them or to set them. In addition, you can create new variables that can be accessed from within Visual FoxPro.

Variables and fields are made available in Visual FoxPro in a name table, which is an array containing the names of all currently-defined variables and fields. You can access an individual element in the array using a name table index (NTI). A special API function, _NameTableIndex( ), returns the index of an existing variable or field based on a name that you provide. After you've determined the NTI for a given variable, you can read it using the _Load( ) API function or set it using the _Store( ) API function. To create a new variable, you can call the API function _NewVar( ).

To access Visual FoxPro variables or fields, you use the Value and Locator structures defined in Pro_ext.h. If you're creating an FLL library, you can use the same technique you used to access parameters passed to your functions. For details about the Value and Locator structures, see Passing and Receiving Parameters earlier in this chapter.

The following example, drawn from the Foxtlibctl.cpp program in the Vfp98\Api\Samples\Foxtlib directory, illustrates how you can use the Value and Locator structures in an ActiveX control to access Visual FoxPro variables:

long CFoxtlibCtrl::TLGetTypeAttr(long pTypeInfo, LPCTSTR szArrName)
{
  int nResult = 1;
  TYPEATTR *lpTypeAttr;
  Locator loc;
  Value val;
  OLECHAR szGuid[128];
  char *szBuff;
__try {
   if (_FindVar(_NameTableIndex(( char *)szArrName),-1,&loc)) {
      ((ITypeInfo *)pTypeInfo)->GetTypeAttr(&lpTypeAttr);
      if (_ALen(loc.l_NTI, AL_ELEMENTS) < 16) {
         _Error(631); //Array argument not of proper size.
      }

      //1 = Guid
      StringFromGUID2(lpTypeAttr->guid, (LPOLESTR )&szGuid,sizeof(szGuid));
      OLEOleToAnsiString(szGuid,&szBuff);
      val.ev_type = 'C';
      val.ev_length = strlen(szBuff);
      val.ev_handle = _AllocHand(val.ev_length);
      _HLock(val.ev_handle);
      _MemMove((char *) _HandToPtr( val.ev_handle ), szBuff, val.ev_length);
      OLEFreeString((void **)&szBuff);
      _HUnLock(val.ev_handle);
      loc.l_sub1 = 1;
      _Store(&loc,&val);
      _FreeHand(val.ev_handle);

      //2 = LCID
      loc.l_sub1 = 2;
      val.ev_type = 'I';
      val.ev_long = lpTypeAttr->lcid;
      _Store(&loc,&val);

      // code for values 3 - 16 here
      ((ITypeInfo *)pTypeInfo) -> ReleaseTypeAttr(lpTypeAttr);
      }
   } __except  (EXCEPTION_EXECUTE_HANDLER) {
      nResult = 0;
   }
return nResult;

Managing Memory

The Visual FoxPro API provides direct access to the Visual FoxPro dynamic memory manager. For API routines that request memory allocations, a memory identifier – or handle, is returned. The Visual FoxPro segment-loading architecture uses handles instead of pointers so it can manage memory more efficiently.

Note   The techniques described in this section for managing memory using the Visual FoxPro API apply to both ActiveX controls and FLL libraries.

Using Handles

A handle refers to a memory handle, which is essentially an index into an array of pointers. The pointers point to blocks of memory that Visual FoxPro knows about. Nearly all references to memory in the API are made through handles instead of the more traditional C pointers.

To allocate and use memory in your library

  1. Allocate a handle with _AllocHand( ).

  2. Lock the handle with _HLock( ).

  3. Convert the handle into a pointer with _HandToPtr( ).

  4. Reference the memory by using the pointer.

  5. Unlock the handle with _HUnLock( ).

    Note   To avoid memo file corruption, don't write to a memo file before calling _AllocMemo( ).

In order to address the allocated memory, your API routines must convert the handle to a pointer by calling the _HandToPtr( ) routine. Even if the Visual FoxPro memory manager needs to reorganize memory to obtain more contiguous memory for subsequent memory requests, the handle remains the same. Routines that grow, shrink, free, and lock memory allocations are also provided.

When you're creating external routines, try to minimize memory use. If you create an external routine that dynamically allocates memory, try to use the least amount of memory possible. Be especially careful about locking large memory allocations for long periods of time. Remember to unlock memory handles with _HUnLock( ) when they no longer need to be locked, because the performance of Visual FoxPro can be adversely affected by locked memory handles.

Caution   Excessive use of dynamic memory deprives Visual FoxPro of memory for buffers, windows, menus, and so on, and degrades performance, because the memory given to fill API requests is managed by the Visual FoxPro memory manager. Allocating large handles and retaining them could cause Visual FoxPro to run out of memory and terminate abnormally.

The Visual FoxPro environment has no memory protection. The external API routine cannot provide all the validation that's inherent in a standard Visual FoxPro program. If you corrupt memory, you receive messages such as “Transgressed handle,” “Internal consistency error,” and “Transgressed node during compaction.”

The following function from an FLL library illustrates memory allocation. The example uses _RetDateStr( ) to return a Visual FoxPro Date type (assuming that the Character parameter is a proper date):

#include <Pro_ext.h>

void dates(ParamBlk  *parm)
{
   MHANDLE mh;
   char *instring;

   if ((mh = _AllocHand(parm->p[0].val.ev_length + 1)) == 0) {
      _Error(182); // "Insufficient memory"
   }
   _HLock(parm->p[0].val.ev_handle);
   instring = _HandToPtr(parm->p[0].val.ev_handle);
   instring[parm->p[0].val.ev_length] = '\0';
   _RetDateStr(instring);
   _HUnLock(parm->p[0].val.ev_handle);
}
FoxInfo myFoxInfo[] = {
   {"DATES", (FPFI) dates, 1, "C"}
};
FoxTable _FoxTable = {
   (FoxTable *) 0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
};

Understanding Stacks

The control or library you create doesn't have a stack of its own. Instead, it uses the stack of its calling program, in this case the Visual FoxPro stack. You cannot control the size of the Visual FoxPro stack or affect the amount of stack space available to an ActiveX control or .fll file.

Under normal circumstances, this distinction isn't important. The Visual FoxPro stack is generally large enough to hold the automatic variables you might need to allocate in a control or library. If you run out of stack space, you can always allocate additional memory on the heap dynamically.

Following Handle Rules

The following rules apply to ownership of handles and the responsibility for freeing them:

Building and Debugging Libraries and ActiveX Controls

After creating a project, you're ready to build and debug it.

Building the Project

Before building, you need to establish the project settings. Some of the settings you make depend on whether you want to create a debug or release version of the control or library. As a rule, you create debug versions of the program until you're satisfied that it's working correctly, and then you create a release version.

To specify a debug or release version

  1. From the Build menu, choose Set Default Configuration.

  2. Choose whether you're creating a debug or release version of the control.

  3. Choose OK.

To establish project settings

  1. From the Build menu, choose Settings.

  2. Under Settings For, choose whether you're creating a debug or release version of the program.

  3. Click the C/C++ tab and then make these settings:
  4. Choose the Link tab and then in the Object/Library Modules text box, add one of the following libraries:
  5. Unmark Ignore all default libraries.

  6. Choose OK.

To make sure the compiler can find the necessary files

  1. From the Tools menu, choose Options.

  2. Click the Directories tab.

  3. In the Show directories for list, choose Include files.

  4. In the Directories toolbar, click the Add button.

  5. Add the directory with Pro_ext.h.

  6. In the Show directories for list, choose Library files.

  7. In the Directories toolbar, click the Add button.

  8. Add the directory with Ocxapi.lib from the Visual FoxPro API directory (when creating a control) or add the Winapims.lib from the Visual FoxPro API directory (when creating an FLL)

  9. In the Options dialog box, choose OK.

After you’ve specified the settings, you can compile and link your program.

To compile and link an .ocx file

When you compile and link the .ocx file, Visual C++ automatically registers the control on the computer on which it was built. If for any reason you must register the control manually, you can do so using the following procedure.

To register the ActiveX control

Debugging an ActiveX Control or FLL Library

Debugging the control or library in the context of a full Visual FoxPro application is more difficult than debugging it separately from the application. It's a good idea to create a simple test program to test the operation of your control or library.

Debugging with Microsoft Visual C++

Microsoft Visual C++ version 4.0 and higher offers an integrated debugging environment that makes it easy to set break points and to step through your code. You can even run Visual FoxPro from Visual C++.

To start debugging with Microsoft Visual C++

  1. From the Build menu, choose Settings.

  2. In the Project Settings dialog box, click the Debug tab.

  3. In the Executable for debug session text box, type the path followed by Vfp6.exe.

    For example, type:
    C:\Program Files\Microsoft Visual Studio\Vfp98\Vfp6.exe.

  4. Choose OK.

  5. Set a break point in your library.

  6. From the Build menu, choose Debug. Then, from the submenu choose Go.

  7. When Developer Studio displays a message that says “Vfp6.exe doesn't contain debugging information,” choose Yes to continue.

For more information about debugging in Visual C++, see the Visual C++ documentation set.

Debugging with Other Debuggers

You should be able to debug a control or library with any debugger that correctly handles an INT 3 (_BreakPoint( )) embedded in your program. You can use any debugger for symbolic debugging as long as it can do all of the following:

To debug a library

  1. Add a _BreakPoint( ) call to the routine at the point where debugging will begin.

  2. Build the control or library.

  3. Invoke your debugger.

  4. If your debugger supports symbols, load the symbol table for your library.

  5. Start Visual FoxPro.

  6. Call your library routine from Visual FoxPro.

  7. When the breakpoint is reached, make adjustments to the symbol base to align your symbols with the actual location where the library was loaded.

  8. Increment the instruction pointer (IP) register by 1 to skip over the INT 3 instruction.

  9. Continue debugging as with a normal program.

    Note   Always remove any breakpoints specified in your debugger before you release your product.