You can extend the native capabilities of Visual FoxPro by taking advantage of the facilities of ActiveX controls (.ocx files), ActiveX objects, and dynamic-link libraries (DLLs). External libraries allow you to access not only the capabilities of other programs, but of Windows itself. For example, you can use an ActiveX control to read and update the Windows registry directly, or it can call system-level functions by linking to one of the DLLs in Windows.
If the functionality you need isn't already available in an external library, you can create your own ActiveX control in C++ using a 32-bit compiler such as Microsoft Visual C++® version 4.0 or later, or with the Microsoft Visual Basic® Control Creation Edition version 5.0. For details, see Chapter 28, Accessing the Visual FoxPro API.
This chapter discusses:
In most cases, Visual FoxPro provides all the tools you need to complete your application. However, occasionally you might find that an application requires additional functionality not already available in Visual FoxPro. In those cases, you can reach outside Visual FoxPro and take advantage of the capabilities of external libraries.
Visual FoxPro allows you to access these kinds of external libraries:
Before you use any library, you must be familiar with the conventions used to access its controls or functions. For example, if you want to include an ActiveX control on a form, you must know what properties, events, and methods you can use to manage the control. For an ActiveX control, you can use a Visual FoxPro Class Browser to determine the properties, events, and methods you can use. Similarly, if you want to call a function in a .dll file, you must know the function name, the number and data types of the parameter it requires, and the data type of its return value. In general, you can obtain this type of information from the documentation that accompanies a library, whether in a book or a Help system. For information about system .dll files for Windows, you can refer to the Software Development Kit (SDK) appropriate to your version of Windows.
You can use any ActiveX control that is available on your computer. To use an ActiveX control, you add it to a form, then set its properties, write handlers for its events, or call its methods. You can add an ActiveX control to a form using the Form Controls toolbar or the OLE Container control, or by using code. For details about adding an ActiveX control in the Form Designer, see Chapter 16, Adding OLE.
You can create an ActiveX control in code in much the same way you would create any Visual FoxPro control. However, before creating the control you must determine the name of the control’s class library, which is stored in the Windows registry. If you have no other way to determine the class library name, use the Form Designer to create the control (as described in the previous section), and then get the control’s OLEClass property.
ActiveX objects can be created directly with CREATEOBJECT( ), and don't require an instance of a form.
To create an ActiveX control in code
olecontrol
as the class. You must pass the control’s class library name as the third parameter of the AddObject method.For example, the following program creates a new form and adds an outline control to it:
oMyForm = CREATEOBJECT("form")
oMyForm.AddObject("oleOutline","olecontrol", ;
"MSOutl.Outline")
After you've created the form and control, you can display the form by calling its Show method, and display the control by setting its Visible property to true:
oMyForm.oleOutline.Visible = .T.
oMyForm.Show
Some ActiveX controls aren't designed primarily to be used interactively by a user. For example, a timer control doesn't support methods for user interaction. Even then, you can still create the control on a form because the control will usually make available a default visible component, such as an icon. Frequently you will not be able to change or resize the icon.
If you don’t want your application to display the icon for non-interactive controls, you can hide the control by setting the Visible property of its OLE container control to false, or set its Left property to a negative value (such as –100) that moves it off the visible portion of the screen. Alternatively, you can place the control on a form that's never made visible (that is, for which the Show method is never called). In all cases, you can still call the control’s methods as if the control were visible.
If the functionality you require is available in a DLL, you can link to the library and call its functions. Before calling a DLL function, you must determine its calling protocol, including the name of the function, the number and data types of its parameters, and the data type of its return value.
In Visual FoxPro, you can only use DLLs that have been written for a 32-bit environment. However, if you require access to a 16-bit DLL, you can call it using functions available in Foxtools.fll. For details, see Help for Foxtools (Foxtools.chm).
To call a DLL function
Note If you specify WIN32API for the library name, Visual FoxPro searches for the 32-bit Windows DLL function in Kernel32.dll, Gdi32.dll, User32.dll, Mpr.dll, and Advapi32.dll.
For example, the following program registers the GetActiveWindow( ) function from the Windows USER system DLL, which displays the handle of the Visual FoxPro main window. The GetActiveWindow( ) takes no parameters, but returns a single integer:
DECLARE INTEGER GetActiveWindow IN win32api
MESSAGEBOX(STR( GetActiveWindow() ) )
The DLL containing the function you're registering must be available in the default directory, in the Windows or System directories, or along the DOS path.
If the function you want to call has the same name as another function already available in Visual FoxPro (either a native function or a DLL function previously declared), you can assign an alias to the function with the duplicate name, then call it using the alias.
DECLARE INTEGER GetActiveWindow IN win32api AS GetWinHndl
MESSAGEBOX(STR( GetWinHndl() ) )
Linked DLL functions remain available until you quit Visual FoxPro, so you only need to declare them once per session. If you don't intend to call the functions in a DLL again, you can issue the CLEAR DLLS command to remove it from memory and free resources.
Note Issuing CLEAR DLLS clears all declared DLL functions from memory.
When you register a DLL function, you must specify the number and data types of its parameters. By default, data is passed by value. You can force a parameter to be passed by reference by including an at sign (@) in front of the parameter.
In general, DLL functions follow the data type conventions used for C, which differ from those used in Visual FoxPro. For example, DLL functions do not support a data type for a date or for currency. If the data you're passing to a DLL function is in a data type not supported by the function, you must convert it to an appropriate type before passing it. For example, you can convert a date to a numeric Julian format using commands such as the following:
cDate = sys(11, date())
nDate = val( cDate )
Some DLL functions require more complex parameters, such as structures or arrays. If the function requires a pointer to a structure, you must determine the layout of the structure, then emulate it as a string in Visual FoxPro before passing it or receiving it from the DLL function. For example, the Windows system function GetSystemTime( ) expects a pointer to a structure consisting of eight words or unsigned 16-bit integers indicating the year, month, day, and so on. The structure is defined this way:
typedef struct _SYSTEMTIME {
WORD wYear ;
WORD wMonth ;
WORD wDayOfWeek ;
WORD wDay ;
WORD wHour ;
WORD wMinute ;
WORD wSecond ;
WORD wMilliseconds ;
} SYSTEMTIME
To pass data between Visual FoxPro and the GetSystemTime( ) function, you must create a 40-byte string buffer (consisting initially of spaces) and then pass the address of this string to the function for it to fill in. When the string is returned, you must parse it in 2-byte increments to extract the individual fields of the structure. The following fragment illustrates how you could extract three of the fields from the structure:
DECLARE INTEGER GetSystemTime IN win32api STRING @
cBuff=SPACE(40)
=GetSystemTime(@cBuff)
tYear = ALLTRIM(STR(ASC(SUBSTR(cBuff,2)) * ;
256 + ASC(SUBSTR(cBuff,1))))
tMonth = ALLTRIM(STR(ASC(SUBSTR(cBuff,4)) * ;
256 + ASC(SUBSTR(cBuff,3))))
tDOW = ALLTRIM(STR(ASC(SUBSTR(cBuff,6)) * ;
256 + ASC(SUBSTR(cBuff,5))))
For more information, you can examine the sample form Systime.scx in the Visual Studio …\Samples\Vfp98\Solution\Winapi directory. For other examples of how to pass parameters to DLL functions, see the program Registry.prg in the Visual Studio …\Samples\Vfp98\Classes directory.
If the data you're working with in Visual FoxPro is in an array, you must loop through the array and concatenate it into a single string representing a C-style array before passing it to the DLL function. If the Windows function expects 16-bit or 32-bit values, you must convert the values to their hex equivalents before concatenating them into string. When you pass the string containing the array data, Visual FoxPro passes the address of the string variable to the DLL, which can then manipulate it as an array. For an example of this, see the sample form Syscolor.scx in the Visual Studio …\Samples\Vfp98\Solution\Winapi directory.
Like a DLL, a Visual FoxPro library (.fll file) contains functions you can call as you would any other function. Because .fll files are created specifically to be called from Visual FoxPro, it's generally easier to pass parameters to and from .fll functions.
To use a Visual FoxPro library, you specify the name of the .fll file, then call the function normally. Unlike registering DLL functions, you don't need to register individual functions within the .fll file, nor do you need to specify information about the parameters or data types used by the function.
Note If you want to use an .fll library from an earlier version of Visual FoxPro, the library must be recompiled to work with Visual FoxPro version 5.0.
To call an .fll function
For example, the following program calls a function from the Foxtools.fll library to determine what type of drive the C: drive is:
SET LIBRARY TO "C:\Program Files\Microsoft ;
Visual Studio\Vfp98\Foxtools.fll"
? DriveType("C:")
If you need to register more than one .fll file, include the ADDITIVE keyword in the SET LIBRARY command. If you don’t, the previously-registered .fll file is cleared and replaced by the one most recently registered.
If a function name conflicts with that of another function already available in Visual FoxPro, the last function defined takes precedence. If the function name in a linked library has the same name as that of an intrinsic Visual FoxPro function, the Visual FoxPro function takes precedence.
The functions in an .fll file remain available until you quit Visual FoxPro, so you only need to register them once per session. If you don't intend to call the functions in a .fll file again, issue RELEASE LIBRARY, RELEASE ALL, or SET LIBRARY TO to remove it from memory and free resources.