Using Borland C++ IDE with the Microsoft Visual BASIC Control Development Kit (CDK) I wrote this description to help anyone using Borland's C++ compiler and Integrated Development Environment (IDE) to create custom controls for use with Microsoft's Visual BASIC. The MS CDK (Control Development Kit) has a series of example programs that were written to work with Microsoft C version 6 or later. This document should help you get them to work using Borland's development products. This document assumes that you have purchased the MS CDK product. I make reference to specific line numbers within the files that were shipped to me with version 1.0 of that product. When working with Borland's IDE, you can tell what line that you are currently editing by referencing the numbers in the bottom left- hand corner of the active edit window. The numbers shown there appear in the format: : for example: 25:32 which says that your cursor is on the 32nd character of line 25. In this document, I also assume that you have installed your Borland C++ product in the drive\subdirectory C:\BORLANDC. Also that after installing the CDK, you have copied the file VBAPI.LIB to C:\BORLANDC\LIB and the file VBAPI.H to C:\BORLANDC\INCLUDE. While experimenting with this code, I created a subdirectory called TEMP under the directory for each control, copied all files for that control there, and edited the copies. It is NEVER advisable to edit your original copies of anything. Also remember that whenever you are coding at a systems-level, you should save your code frequently. If you have any comments, suggestions, or neat VBX files, please feel free to drop me a line in my Compuserve mailbox, or at my humble homestead at 1188 Morgan Ave, Williamsport, PA 17701. Throughout this document, I mention products and programs that are protected by some sort of legal nonsense or another, and I hope I can appease all legal-eagles by saying that any Borland product mentioned is trademarked and legally protected by Borland International Inc. of Scotts Valley, CA. Any Microsoft product is trademarked and legally protected by Microsoft Corporation of Redmond, WA. Anything done by you using the descriptions here should make neither them (nor me) liable for any silliness that might prevail. Brent K. Langley Compuserve Userid 70312,2142 Page 1 Using Borland C++ IDE with the Microsoft Visual BASIC Control Development Kit (CDK) GETTING THE CDK TO WORK: I have found that in getting the CDK code to work with the IDE there are 8 steps that I had to follow: 1) Change WINDOWS.H 2) Copy and Rename LibInit.Obj to C:\BORLANDC\LIB\C0VBINIT.OBJ 3) Create a Project file (.PRJ) to create the VBX/DLL file 4) Change CCINIT.C to take advantage of changes in WINDOWS.H 5) Handle any syntax errors in demo code 6) Handle any language incompatibilities in code 7) "Make" Project from IDE 8) Rename (or Copy) xxxxxxxx.DLL to xxxxxxxx.VBX Steps 1 and 2 from above only need to be done once. Steps 3 and 4 need to be done for every project, but follow the same steps. Steps 5 and 6 will be addressed separatly for each of the three example programs that need fixing. Steps 7 and 8 let you put all of the pieces together. STEP ONE: CHANGE WINDOWS.H The normal startup code for a DLL does a little processing and then calls your function called LibMain. In a .VBX file, we have to replace the normal startup code with a modified routine that passes some extra information to our control's LibMain. This means that the format for calling a VBX's LibMain is different from a normal DLL's LibMain. This causes no real hassle to MSC/SDK developers because Microsoft doesn't include a function prototype in their version of WINDOWS.H for LibMain, however, Borland does. And when Borland's compiler sees the CDK version of the LibMain function, it stops compiling with an error. We could just remove the function prototype for LibMain from Borland's WINDOWS.H, but it will help us with our syntax checking if we leave it there. My solution is to define a constant called _CDK before the #include of WINDOWS.H. Then have WINDOWS.H determine the existance of this defined constant to decide whether to use the CDK or normal version of LibMain. To do this, load C:\BORLANDC\INCLUDE\WINDOWS.H and then press Ctrl-PgDn to go to the bottom of the file. Move your cursor up to about line 3473, and you should see Borland's function prototype for LibMain. I changed this part of the code to be: #ifndef _CDK int FAR PASCAL LibMain ( HANDLE, WORD, WORD, LPSTR ); #else BOOL FAR PASCAL LibMain( HANDLE, HANDLE, unsigned short ); #endif I used "unsigned short" instead of USHORT for the third parameter of the CDK version of LibMain because USHORT isn't seen in a typedef statement until the file VBAPI.H is #include'd later. Brent K. Langley Compuserve Userid 70312,2142 Page 2 Using Borland C++ IDE with the Microsoft Visual BASIC Control Development Kit (CDK) STEP TWO: RENAME LIBINIT.OBJ TO C0VBINIT.OBJ Page 20 of the CDK Guide explains that LIBINIT.OBJ contains initialization code to replace the code from the file LIBENTRY.OBJ that is normally linked into DLL projects first. So we must ensure that the code from LIBINIT.OBJ is linked before any other code in our projects. On page 141 of Borland's C++ User Guide, we find the section of the project manager chapter titled "Overriding Libraries" states that we can have our own startup file linked first, if: 1) its name starts with C0 (the letter C followed by zero) 2) it is placed as the first file in the project. We'll worry about making a project file in a minute, but for now, lets give LibInit.OBJ a new name to conform with step one from above. While we're at it, lets also move it to a standard location (since the CDK manual says that this code would seldom need to change.) If LibInit.OBJ is in your current directory, type: COPY LIBINIT.OBJ C:\BORLANDC\LIB\C0VBINIT.OBJ You could call it anything you want (so long as it starts with "C0") but since it contains the Visual Basic INITialization code, I thought that this name was appropriate. STEP THREE: CREATE A PROJECT FILE For each control, you will make a project file that looks similar to the following: C:\BORLANDC\LIB\C0VBINIT.OBJ C:\BORLANDC\LIB\C0DC.OBJ C:\BORLANDC\LIB\VBAPI.LIB CCINIT.C program.DEF program.RC program.C . . (any other program files or libraries) . For example, the project for the CNTR control would look like: C:\BORLANDC\LIB\C0VBINIT.OBJ C:\BORLANDC\LIB\C0DC.OBJ C:\BORLANDC\LIB\VBAPI.LIB CCINIT.C CNTR.DEF CNTR.RC CNTR.C Brent K. Langley Compuserve Userid 70312,2142 Page 3 Using Borland C++ IDE with the Microsoft Visual BASIC Control Development Kit (CDK) STEP THREE: CREATE A PROJECT FILE (continued) The file C:\BORLANDC\LIB\C0DC.OBJ listed in the above projects is the normal startup code file for Borland C (that has been superseded by C0VBINIT.OBJ) and should appear immediately after C0VBINIT.OBJ in your project file. This file should actually be C0Dx.OBJ where "x" indicates your memory model that you are compiling with. Since I used the "Compact" memory model for compiling my projects, this is C0Dc.OBJ in my code examples. Since a VBX file is a type of DLL, you should let the project manager know that you will be creating a DLL. You can do this by typing Alt-O to activate the Options menu. Type A to select your Application type, and then type D to select DLL. Now when you save your project, this information will be stored with it. Besides the interactive way of building a project, you could make a batch file to create your project files, or you could copy this text to a .PRJ file, but then remember to run PRJCNVT to change the old-style text project file to Borland's new project file format. An example .BAT file called MAKEPRJ.BAT might contain: @ECHO OFF IF "%1"=="" GOTO ERROR ECHO C:\BORLANDC\LIB\C0VBINIT.OBJ > %1.PRJ ECHO C:\BORLANDC\LIB\C0DC.OBJ >> %1.PRJ ECHO C:\BORLANDC\LIB\VBAPI.LIB >> %1.PRJ ECHO CCINIT.C >> %1.PRJ ECHO %1.DEF >> %1.PRJ ECHO %1.RC >> %1.PRJ ECHO %1.C >> %1.PRJ PRJCNVT %1.PRJ GOTO END :ERROR ECHO You must specify a project name :END This uses the program PRJCNVT.EXE in your C:\BORLANDC\BIN subdirectory (probably in you PATH) to convert from a text .PRJ file to Borland's new project file format. The first ECHO statement creates a new .PRJ file, and the rest of the ECHO statements append their line of text to this file. The file is named using whatever you specify on the command line after the word MAKEPRJ. For Example: MAKEPRJ CNTR Would make the project example shown on the previous page and name it CNTR.PRJ. Remember then to set the Alt-O-A-D options when you load this into the IDE. Brent K. Langley Compuserve Userid 70312,2142 Page 4 Using Borland C++ IDE with the Microsoft Visual BASIC Control Development Kit (CDK) STEP FOUR: CHANGE CCINIT.C In step one, we modified WINDOWS.H to allow correct function prototyping of the LibMain function. In the file CCINIT.C we will take advantage of this change. This is the file where our version of LibMain is declared, so we want WINDOWS.H to use the CDK version of the prototype. This is pretty simple, all you need to do is to include the line: #define _CDK before the #include statement in the file CCINIT.C. I suggest that you type over line 6 in this file which is currently blank. This will preserve the line numbers for the rest of the code. CCINIT.C is very similar for each of the example programs in the CDK, but each does vary slightly. Please make this change for every sample control project in the CDK. STEP FIVE: HANDLE SYNTAX ERRORS STEP SIX: HANDLE ANY LANGUAGE INCOMPATIBILITIES CIRCLE1, CIRCLE2, and PUSH have no further problems so you can continue with steps seven and eight for them. However, there is still some work that needs to be done with the files for CIRCLE3, CNTR, and PIX. I prefer to address the problems for each of these programs separately, so please see their individual descriptions on the following pages. STEP SEVEN: MAKE PROJECT You're just about done! Press F9 and the IDE will create your DLL file. If you prefer to use Borland's MAKE utility, you can convert your .PRJ file from step three (above) to a .MAK file with the Borland utility PRJ2MAK and then edit this make file to tailor it to your own specifications. STEP EIGHT: RENAME .DLL FILE TO .VBX The IDE's project manager will create a file with the same filename as that of your .PRJ file. The resulting file's extension will be .DLL but the Visual BASIC system will be expecting a Visual Basic eXtension file. To accomodate Visual BASIC, just RENAME (I prefer to COPY) name.DLL to name.VBX That's all there is (as if it isn't enough!) Now you can load Visual BASIC and select Alt F D for File Add-file and specify your .VBX file. Brent K. Langley Compuserve Userid 70312,2142 Page 5 Using Borland C++ IDE with the Microsoft Visual BASIC Control Development Kit (CDK) CIRCLE3 In addition to following the steps described above (such as putting the #define _CDK in CCINIT.C) CIRCLE3 has some compatibility problems: CIRCLE3 PROBLEM 1: On line 114 of CIRCLE.C there is a call to VBCreateHsz that uses the new MS C v6 type keyword "_segment". My info on this keyword comes from an article in the March 1990 issue of Microsoft Systems Journal by Noel J. Bergman. While describing the new (at that time) version 6 of MS C, he describes briefly, on page 58, the concept of "based pointers" and the function of _segement. He says: "When _segment is used to cast a near address, the result is the current value in DS" which can be obtained in BC++ by specifying _DS or FP_SEG( (void far *) addr ). He continues: "If the address is a far address, the result is the segment for that far address." You can use the FP_SEG( addr ) macro in dos.h to get this value. A consistent way to handle both cases would be to use the FP_SEG macro and cast (void far *) on the pointer. For example: FP_SEG( (void far *) addr ) Or, since the macro does this casting for you internally, you can abbreviate this as just: FP_SEG( addr ) A side note: when looking in the include file dos.h I found that FP_SEG is using a cast operation with the word _seg !?! What is that?? Is it the same as _segment ?? It doesn't seem so. Since I couldn't find it in the documentation, lets play it safe and stick with FP_SEG. Although, if you are converting a MAJOR project, you could speed up the conversion by doing a global search and replace: changing all (_segment) to (void _seg *)(void far *) Using FP_SEG is easier to read (I think), and a little more portable. The choice is up to you, but for this example, lets change line 114 of CIRCLE.C to read: hsz = VBCreateHsz( FP_SEG(hctl), (LPSTR)lp); and then put the #include on the blank line between the includes for and (this will preserve line numbers for the further editing that we have to do...) Brent K. Langley Compuserve Userid 70312,2142 Page 6 Using Borland C++ IDE with the Microsoft Visual BASIC Control Development Kit (CDK) CIRCLE3 PROBLEM 2: In line 406 of CIRCLE.C, the reference to FlashDlgProc is meant to pass the address of a certain type of function to the function VBDialogBoxParam. It's function prototype on line 423 is: BOOL FAR PASCAL _export FlashDlgProc(HWND, USHORT, USHORT, LONG) but what is required for VBDialogBoxParam (on line 309 of VBAPI.H) is the type FARPROC that is defined on line 151 of WINDOWS.H as: typedef int (FAR PASCAL * FARPROC)(); OK, so all we need to do is cast this FARPROC type on the function FlashDlgProc. Change line 406 of CIRCLE.C to read: VBDialogBoxParam(hmodDLL, "FlashDlg", (FARPROC)FlashDlgProc, 0L); Now your CIRCLE3.PRJ should compile cleanly. Remember to rename (or copy) CIRCLE3.DLL to CIRCLE3.VBX then fire up Visual BASIC and try it out! Brent K. Langley Compuserve Userid 70312,2142 Page 7 Using Borland C++ IDE with the Microsoft Visual BASIC Control Development Kit (CDK) CNTR CNTR, the counter control uses "based pointers" in a couple of its modules so there is some conversion that needs to be done, but there is an error (BUG??) in CCINIT.C that we must fix first. In CCINIT.C on line 45, the function modifier "PASCAL" is missing from the definition of the function LibMain, so change this line from: BOOL FAR LibMain To: BOOL FAR PASCAL LibMain Also, at the top of CCINIT.C (line 6, for example) remember to place the statement: #define _CDK In CNTR.H and CNTR.C we need to remove the based pointer references and replace them with their equivalents in terms of far pointers. I'm not sure why Microsoft added this based pointer complexity to this program, unless its that they felt that it would make the code run faster (using 16 bit pointers rather than 32 bit.) But I don't think that this is the type of application that would benefit from the added complication. In CNTR.H we have some stuff to clean up. Lines 26 through 34 and also line 54 should be commented out as shown below: // _segment segCntr; // #define BP _based(segCntr) * // based pointer // #define BH BP BP // based handle // typedef VOID BH BHVOID; // void handle // typedef CHAR BH BHSTR; // handle to a string // #define CNTRDEREF(hctl) ((PCNTR)(VOID *) ... and then change line 52 from: typedef CNTR BP PCNTR; to: typedef CNTR far * PCNTR; In CNTR.C comment-out line 53 as shown below: // segCntr = (_segment) hctl; and change line 54 from: pcntr = CNTRDEREF(hctl); to: pcntr = VBDerefControl(hctl); Now CNTR should compile ok. Rename CNTR.DLL to CNTR.VBX an go! Brent K. Langley Compuserve Userid 70312,2142 Page 8 Using Borland C++ IDE with the Microsoft Visual BASIC Control Development Kit (CDK) PIX Remember to put the line: #define _CDK on line 6 (the blank line above #include ) in the file CCINIT.C as described in step 4 (above). Lines 81, 85, and 109 of PIX.C use the non-Borland _segment type for casting as described for CIRCLE3 (above). On each of these lines modify reference in the code for (_segment) hctl to appear like: FP_SEG( hctl ) and place the line #include at the top of the file PIX.C. Now this control should compile and link cleanly. Remember to rename the resultant .DLL file to a .VBX file before you load Visual BASIC and try it out. -------------------------------------------------------------- Well, I hope this made enough sense to get you going. I tried to not only tell you what to do, but to also tell you where in the various references that I found my information. I hope it helps. Also, I can't emphasize enough, for you to save your programs often. A small mistake in your code can cause a Windows Unexpected Application Error (UAE) and either kick you completely out of Visual BASIC, kick you out of Windows, reboot your machine, or freeze your machine. I have encountered all of these battle scars, but I survived and you will too! Have fun....Brent Brent K. Langley Compuserve Userid 70312,2142 Page 9