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:
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:
Note If you want to use an .fll library from a version of Visual FoxPro earlier than 5.0, the library must be recompiled to work with Visual FoxPro 6.0.
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
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
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.
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
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++.
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:
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
};
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
};
}
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.
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.
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. |
|
Character type parameter |
|
Date type parameter |
|
Integer type parameter |
|
Logical type parameter |
|
Numeric type parameter |
|
Reference |
|
DateTime type parameter |
|
Currency type parameter |
|
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")
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
};
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
#INCLUDE
to include the Pro_ext.h file along with any other required header files._OCXAPI(AfxGetInstanceHandle(),DLL_PROCESS_ATTACH);
_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.
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;
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;
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 |
|
‘C’ |
|
string length | |
|
MHANDLE to string | |
Numeric |
|
‘N’ |
|
Display width | |
|
Decimal places | |
|
Double precision | |
Integer |
|
‘I’ |
|
Display width | |
|
Long integer | |
Date |
|
‘D’ |
|
Date1 | |
Date Time |
|
‘T’ |
|
Date + (seconds/86400.0) | |
Currency |
|
‘Y’ |
|
Display width | |
|
Currency value2 | |
Logical |
|
‘L’ |
|
0 or 1 | |
Memo |
|
‘M’ |
|
FCHAN | |
|
Length of memo field | |
|
Offset of memo field | |
General |
|
‘G’ |
|
FCHAN | |
|
Length of general field | |
|
Offset of general field | |
Object |
|
‘O’ |
|
Object identifier | |
Null |
|
‘0’ (zero) |
|
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.
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;
The following table is a guide to the fields in the Locator structure.
Locator field | Field use |
|
'R' |
|
The number of the table containing this field, or – 1 for a variable. |
|
Name Table Index. Visual FoxPro internal use. |
|
Field number within table. Visual FoxPro internal use. |
|
For variables only, the number of subscripts (0 – 2). |
|
For variables only, the first subscript if l_subs is not 0. |
|
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.
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
};
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.
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;
}
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)
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.
The following data types are used in Visual FoxPro API routines.
Data type | Description |
|
The number of a line in an open file in an editing window. The first line is 1. |
|
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. |
|
File channel. Every file opened by Visual FoxPro, or through the API by using _FCreate( ) and _FOpen( ), is assigned an FCHAN. |
|
A 32-bit pointer to a function returning an integer. |
|
A unique identifier assigned to a single command on a menu. |
|
A unique identifier assigned to a menu. |
|
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( ). |
|
Name table index. Every variable and table field’s name has an entry in this table. |
|
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.
The primary data structures used in the Visual FoxPro API library are listed in the following table.
Structure | Description |
|
A structure used to describe what the system is doing at a given time. |
|
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. |
|
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. |
|
A structure used to access parameter values (FLL) or Visual FoxPro variables or fields (FLL and ocx). |
|
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. |
|
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. |
|
A structure that defines the horizontal and vertical coordinates of a single point on the screen. Coordinates are specified in rows and columns. |
|
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. |
|
A structure used to access parameter values (FLL) or Visual FoxPro variables or fields (FLL and OCX). |
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;
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.
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
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
};
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.
The following rules apply to ownership of handles and the responsibility for freeing them:
ev_type = 'C'
). All the other data types store their values in the Value structure itself, while loading a character string puts an MHANDLE in the ev_handle
of the Value structure.ParamBlk
.Caution When you write an external routine that calls functions, be careful to follow all rules and check the return results. A stray pointer or handle reference could damage the Visual FoxPro internal data structures, causing an immediate abnormal termination or delayed problems, which could result in data loss.
After creating a project, you're ready to build and debug it.
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
To establish project settings
OCXAPI.LIB
from the Visual FoxPro API directory.WINAPIMS.LIB
from the Visual FoxPro API directory.To make sure the compiler can find the necessary files
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
-or-
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.
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++
For example, type:
C:\Program Files\Microsoft Visual Studio\Vfp98\Vfp6.exe.
For more information about debugging in Visual C++, see the Visual C++ documentation set.
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
Note Always remove any breakpoints specified in your debugger before you release your product.