Creating ActiveX Components with the Visual FoxPro API/LCK
Page Contents
Introduction
Creating An ActiveX Control
Adding Features to the ActiveX Control
Integrating LCK Capability
The Windows API
Performance
AcitveX Controls versus FLLs
Summary
Introduction
Microsoft® Visual FoxPro 5.0 and its ancestors such as Microsoft®
FoxPro for MS-DOS and Microsoft® FoxBASE+® have always included a way
to extend programmability with extensions to other languages. Developers can
take advantage of this extensibility to directly address machine hardware,
call operating system functions, create exceptionally fast low-level routines,
and so on.
The FoxBASE+ LOAD and CALL functions let developers create functions written
in Assembly code (.COM or .BIN modules) to do specialized tasks such as take a
string as a parameter and display it on the screen in a much larger font, no
small feat in the pre-graphical user interface MS-DOS era. The LOAD and CALL
functions had many drawbacks: the Assembly code had to be very carefully
written, parameter passing was extremely limited, it was difficult to write in
C, and it could not be used by products other than FoxBASE+.
FoxPro for MS-DOS later introduced the Library Construction Kit (LCK). The LCK
consists of two key files; a header file included in a C program and a LIB
file with which the C program is linked. The LCK allows functions to be
written in C and C++; functions that can be called from a FoxPro program just
as any user-defined function (UDF) is called. Argument passing is the same as
a FoxPro UDF, allowing complex UDFs to be created. The FoxPro LCK is a vast
improvement over the FoxBASE+ .COM modules that were LOADed and CALLed.
One of the most powerful features of the FoxPro LCK is the ability to create
functions written in C and C++ that are callable from FoxPro. Another powerful
feature is the ability of the same functions to also call back into FoxPro to
manipulate records in a table, the editing environment, FoxPro variables, and
so on. There are over 100 callback functions within FoxPro that the C
and C++ programs can call. FOXTOOLS.FLL uses several of these callback
functions.
Visual FoxPro Version 5.0 extends the capabilities of the LCK one step
further. It enables C and C++ programmers to create ActiveX controls that can
take advantage of the hundreds of Visual FoxPro callback functions. An ActiveX
control can be written in several ways so that it:
Creating An ActiveX Control
It's easy to create an ActiveX control because Microsoft® Visual
C® version 2.0 and greater provides the OLE Control Wizard now integrated
into the Application Wizard.
To create an ActiveX control:
For the examples used in this white paper, the ActiveX control is named
FOXOCX. Use the Hierarchical Tree View control in the Microsoft® Developer
Studio Class view (a pane of the Project window) with the FOXOCX classes to
see the various classes, methods, and properties that the wizard generated for
the control.
It's easy to run your ActiveX control from within Visual FoxPro or any
other ActiveX control host such as TSTCON32.EXE which is included with Visual
C. In Visual C, press F5 or execute the project (if the project isn't
built yet, Visual C prompts you to build it). Visual C then prompts for an
executable file to run, and you can enter the path in two ways:
At this point the ActiveX control doesn't have many features, but you can
create a new form in Visual FoxPro and insert the FOXOCX ActiveX control on
it. If you place a breakpoint on CFoxocxCtrl::OnDraw, then the Visual C
debugger is activated whenever this breakpoint is hit; it occurs whenever the
ActiveX control needs to be drawn.
The following sample code shows how you can instantiate the ActiveX control in
Visual FoxPro with a few lines of code in a program.
PUBLIC ox ox = CREATEOBJECT('form') ox.ADDOBJECT('ocx','myocx') ox.ocx.Visible=.t. ox.Show DEFINE CLASS myocx AS OLECONTROL oleclass = 'foxocx.foxocxctrl.1' ENDDEFINE
Adding Features to the ActiveX Control
Let's add some properties and methods to the sample ActiveX control. To do this:
Note that the wizard automatically prompts for the parameter types and return
values.
Once you've added these, you can see them in the class view, and you can
double-click them to see some of the code that was added by the wizard. You
can also double-click them from within the Class wizard to see or modify the
code.
Note that OLE uses UNICODE, so some of the strings you pass will appear as
wide chars. You can convert them to MultiByte chars with the
WideCharToMultiByte function which takes CP_ACP as the first parameter for
conversion to ANSI chars. To convert in the opposite direction, use the
MultiByteToWideChar function.
Integrating LCK Capability
You can change the build settings for the ActiveX control to give it the
ability to use the Visual FoxPro LCK. Let's add a method called Data to
our sample ActiveX control that takes a single string parameter (LPCSTR
dbfname: long pointer to a constant string). The function is passed a
parameter that is the full path to the Tastrade Customer table, CUSTOMER.DBF,
and it returns a value based on a calculation on numeric data in the table.
First you need to make the compile options of the ActiveX control compatible
with the LCK.
Next, add OCXAPI.LIB, included with Visual FoxPro 5.0, to the Object/Library
modules on the link page. Be sure that the paths to these files are listed in
the Directories dialog, displayed by choosing Options from the Tools menu so
Visual C can locate them.
Now add the following line to your CPP file (included with Visual FoxPro 5.0).
include "pro_ext.h"
Add the following lines to the constructor of your control. This call initializes the LCK.
if (!_OCXAPI(AfxGetInstanceHandle(),DLL_PROCESS_ATTACH)) { ::MessageBox(0,"This ActiveX control can only be hosted by Visual Foxpro","",0); //Here you can do whatever you want when the host isn't Visual FoxPro: // you might want to reject loading or you might want to set a property //saying that the host isn't Visual FoxPro and the control will use other means // to achieve its purpose. }
If you get the message "LINK : warning LNK4098: defaultlib
"MSVCRT" conflicts with use of other libs; use
/NODEFAULTLIB:library," change the Ignore libraries textbox to msvcrt in
the Category Input dialog, displayed by choosing Build from the Settings menu,
and then choosing Link Page.
Now you can run the ActiveX control in TSTCON32.EXE and the MessageBox is
displayed.
The Data method looks like this:
long CFoxocxCtrl::data(LPCTSTR dbfname) { Value val; Locator loc; char szBuff[100]; wsprintf(szBuff,"use %s",dbfname); if (_Execute(szBuff)) { ::MessageBox(0,szBuff,"Execute failed",0); } _Execute("myvar = reccount()"); // get the record count NTI nti = _NameTableIndex("myvar"); // find the variable in the name tab _FindVar(nti,-1,&loc); // get a locator for it _Load(&loc,&val); // get its value wsprintf(szBuff,"%s has %d records",dbfname,val.ev_long); ::MessageBox(0,szBuff,"",0); return val.ev_long; }
The Windows API
Visual FoxPro allows you to call many of the thousands of functions available from the Win32 API directly with the DECLARE DLL command. You can also call third-party 32-bit DLLs with DECLARE DLL. To call 16-bit or 32-bit DLLs, use RegFN( ) and CallFN( ) in FOXTOOLS.FLL. Calls to these functions are limited to Visual FoxPro data types such as string and integer. Many of the Win32 API functions require more complex data types such as structures and pointers. Structures can be simulated using a string of bytes of the proper length. However, this is awkward, and there are many functions in the Win32 API that can't be called at all from Visual FoxPro. Use Visual C or Visual C++ to easily access all of these Win32 API functions.
Performance
It's important to note that performance gains from using the LCK can be
tremendous, but your performance improvements depend on the nature of your
code.
The following sample programs are written in Visual C and Visual FoxPro. They
open the Customer table and total the Max_Ord_Amt field 100 times, and then
return the total. The Visual C program is just a modification of the Data
method described above. There are easier ways to get the total of a numeric
field (for example, with the SUM command), but these samples illustrate the
differences in execution time between Visual C and Visual FoxPro code when a
task is file intensive.
CLEAR CLEAR ALL mfile = HOME() + "samples\tastrade\data\customer.dbf" PUBLIC ox ox = CREATEOBJECT("form") ox.ADDOBJECT("ocx","myocx") ox.ocx.Visible=.t. *ox.Show ACTIVATE SCREEN num = 100 DO WHILE !CHRSAW() CLOSE DATA start = SECONDS() USE (mfile) FOR i = 1 TO num m.sum = 0 SCAN m.sum = m.sum + max_order_amt ENDSCAN ENDFOR Finish = SECONDS() - start ?"Fox",sum,num,finish ENDDO ch = INKEY() CLOSE DATABASES DO WHILE !CHRSAW() start = SECONDS() kk=ox.ocx.data(mfile,num) finish = SECONDS() - start ?"lck", kk,num, finish ENDDO //C Code: long CFoxocxCtrl::data(LPCTSTR dbfname,long num) { Value val; Locator loc; char szBuff[100]; NTI nti; int sum; wsprintf(szBuff,"use %s",dbfname); if (_Execute(szBuff)) { ::MessageBox(0,szBuff,"Execute failed",0); } nti = _NameTableIndex("max_order_amt"); _FindVar(nti,0,&loc); for ( ; num ; num--) { _DBRewind(-1); // go top sum = 0; while (!(_DBStatus(-1) & DB_EOF)) { //DO WHILE !EOF() _Load(&loc,&val); sum += val.ev_currency.LowPart / 10000; _DBSkip(-1,1); // skip } } return sum; }
The following examples are computationally intensive, do not perform file I/O
operations, and demonstrate that Visual C code is much faster than Visual
FoxPro code. These examples calculate the number of prime numbers below a
certain value. The two parameters passed to the examples are the value and the
number of iterations to perform.
Note that the Visual FoxPro code and the Visual C code are identical except
for syntax. You can make the Visual C code even faster if you create a
non-debug version with full optimization.
DO WHILE !CHRSAW() start=SECONDS() ans = doprimes(100,iter) * ans =ox.ocx.primes(100,iter) finish = SECONDS() - start ?ans,iter,finish ENDDO PROCEDURE Doprimes(upto,num) FOR iloop = 1 TO num cnt = 0 FOR i = 3 TO upto STEP 2 flag = .t. FOR j = 2 TO SQRT(i) IF (INT(i/j) * j = i) flag = .f. EXIT ENDIF ENDFOR IF flag cnt=cnt+1 ENDIF ENDFOR ENDFOR RETURN cnt // the OCX method "primes" #include "math.h" long CFoxocxCtrl::primes(long upto, long iter) { int i,j,flag,cnt; for ( ; iter ; iter--) { cnt = 0; for (i= 3 ; i< upto ; i+=2) { for (flag = TRUE, j = 2 ; j<=sqrt(i) ; j++) { if (int(i/j) * j == i) { flag = FALSE; break; } } if (flag == TRUE) { cnt++; } } } return cnt; }
ActiveX Controls versus FLLs
There are several advantages when using ActiveX controls rather than the
traditional Visual FoxPro FLLs. (An FLL is a DLL with a few extra Visual
FoxPro specific characteristics). For example, the FOXTLIB ActiveX control
used by the Visual FoxPro 5.0 Class Browser can be hosted by applications
other than Visual FoxPro. FLLs can only be hosted by Visual FoxPro which means
the third-party market for ActiveX controls is much greater. There are many
more third-party books, development tools, and developers for ActiveX controls
than for FLLs.
To use an FLL, use the SET LIBRARY TO command. This loads the FLL and makes
all the functions that are exported by the FLL available to a running Visual
FoxPro program. Note that there is potential for name conflicts when more than
one FLL is loaded.
Many applications that are written in Fox are modeless such as the Visual
FoxPro 5.0 Class Browser. You can run multiple instances of the Class Browser
and you can switch between it and the Form Designer, Database Designer, or
another Visual FoxPro application. When the Class Browser is the active
application, it needs access to some Visual C functions to read OLE type
library information from ActiveX controls, VFP.EXE, and so on. When another
window is brought to the foreground, the Class Browser is inactive, and the
Visual C functions should not be in scope. Thus, the SET LIBRARY TO and
RELEASE LIBRARY commands would need to be placed in the Activate, Deactivate
events of the Class Browser. This is cumbersome and slow.
Instead, the Class Browser uses an ActiveX control, called FOXTLIB, which
enables the Class Browser to read and write OLE type library information. The
source code to FOXTLIB is distributed with Visual FoxPro 5.0. By using the
FOXTLIB ActiveX control, the Visual C functions are automatically encapsulated
and scoped to the Class Browser, making it a more encapsulated object, thus
increasing efficiency.
Note that you can use the return value of _OCXAPI to determine if Visual
FoxPro or another application is the ActiveX control host. The ActiveX control
can behave differently depending on its host.
You can test to verify that an ActiveX control is valid by determining what
functions the control exports. Use the Visual C LINK and DUMP options to
display the functions the ActiveX control exports:
D:\devcon\foxocx\DEBUG>link /dump /exports foxocx.ocx Microsoft (R) COFF Binary File Dumper Version 3.10.6011 Copyright (C) Microsoft Corp 1992-1996. All rights reserved. Dump of file foxocx.ocx File Type: DLL Section contains the following Exports for FOXOCX.OCX 0 characteristics 323F921A time date stamp Tue Sep 17 23:09:30 1996 0.00 version 1 ordinal base 5 number of functions 5 number of names ordinal hint name 5 0 @DispatchAPI@4 (000024AC) 1 1 DllCanUnloadNow (00003DA0) 2 2 DllGetClassObject (00003D20) 3 3 DllRegisterServer (000010B7) 4 4 DllUnregisterServer (00001188) Summary 2000 .data 1000 .idata 1000 .rdata 1000 .reloc 2000 .rsrc 3000 .text
The DllRegisterServer and DllUnregisterServer are exported by all OLE In Process servers that are self-registering. Thus, you can tell an ActiveX control to register itself from within any Visual FoxPro program:
DECLARE INTEGER DllRegisterServer IN <path to ActiveX control> = DllRegisterServer()
The @DispatchAPI@4 export is the entry point into the ActiveX control that the Visual FoxPro ActiveX control uses to make function callbacks. If your ActiveX control is Visual FoxPro enabled, you should see this export.
Summary
Visual FoxPro and its ancestors have always had ways of utilizing other languages such as Assembly and C. With the evolution of OLE and ActiveX controls comes an architecture that allows the development of code modules that can be accessed uniformly by any host. Using the Visual FoxPro LCK and creating Visual FoxPro enabled ActiveX controls is a great way to take advantage of OLE architecture and extend the power of Visual FoxPro.
The Virtual Library of WWW Development
Copyright ?
1996-97 Pro WEBDesign
/ Gustavo - webfox@cyberservices.com
All Rights Reserved
This Page was Launched on Friday, March 28, 1997