'=========================================================================== ' Subject: LINKING C TO BASIC Date: 07-15-97 (16:03) ' Author: Tika Carr Code: Text ' Origin: t.carr@pobox.com Packet: FAQS.ABC '=========================================================================== ======================================== [ QuickBasic 4.5 Tutorial ] [ Interfacing QuickBasic with C Programs ] [ Copyright (c) 1997 by Tika Carr ] ======================================== (Please read disclaimer at the end of this tutorial.) While QuickBasic is quite powerful for most applications, there are times when a programmer would get more speed or efficiency when using a C function, but would rather not write the whole program in C. In this tutorial, I hope to cover some of the basics of interfacing C programs with QuickBasic 4.5. This can't be done as easily with QBasic because QBasic cannot use .OBJ files of compiled C programs. It MIGHT be done somehow, but the methods are beyond the scope of this tutorial. If you would like to use your QuickBasic .OBJ files in your C programs, unfortunately this isn't possible. Reason is because the .OBJ files that QuickBasic creates contains calls to QuickBasic functions, statements and keywords (like PRINT, CLS and so on). The code for that is in the run time library and therefore cannot be transferred in the .OBJ file for use in a C compiler. However, you CAN use .OBJ files from some C compilers and assemblers in QuickBasic. This tutorial will focus on how to use C functions in QuickBasic. INFORMING QUICKBASIC OF A C FUNCTION ==================================== Keep in mind that C doesn't really have any SUBroutines, only functions. Subroutines can be written as 'void' functions which will behave as a QuickBasic SUB (does not return a value). To let QuickBasic know you want to use an external routine, you still have to DECLARE it. For example, let's suppose we have a C function called PrintIt and passing to it 3 variables: void PrintIt(int x, int y, char *text) Some knowledge of how C handles passing of parameters is necessary when interfacing with QuickBasic. For variables that you are passing just the value of (not a pointer, ie. the variable doesn't have an asterisk (*) preceding it), you must let QuickBasic know its being passed by value by using BYVAL. SEG is used for parameter variables that are pointers (as in char *text above) SEGment, meaning pass the segment ADDRESS of the variable, not the value). Here's an example using our above PrintIt function: DECLARE SUB PrintIt(BYVAL x AS INTEGER, BYVAL y AS INTEGER,_ SEG text AS STRING) (Note that the underscore _ is used to indicate that the second line is really part of the first, and should be joined there. Just wasn't enough room on a line to fit it all in.) This example won't work yet but we'll get to why in a moment. This example just shows how to pass the parameter's value or address. It is also important to be sure and state what type of variable is used: If declared in C as use this in QuickBasic ----------------------------------------------------- int AS INTEGER char AS STRING (note that QB doesn't accept AS STRING * n in a DECLARE) double AS DOUBLE float AS SINGLE long AS LONG In the case of a C function that passes a value: int PrintIt(int x, int y, char *text) You would use: DECLARE FUNCTION PrintIt% (BYVAL x AS INTEGER, BYVAL y AS INTEGER,_ SEG text AS STRING) Here note the % after PrintIt. This tells you its an INTeger. See the QuickBasic online help and Manual for the symbols for different variable types and match them accordingly. A type of 'void' is to be declared as a SUB and not a function. Now that we know how to declare the parameters, we have to be sure they are in the right order. When QuickBasic sees a function written in C, it reads the parameters in _backwards_. For instance, taking our example: void PrintIt(int x, int y, char *text) QuickBasic looks at it like this: SUB PrintIt(text$, y%, x%) There are two ways to get QuickBasic to see the parameters in the right order. If you wrote the C program yourself, you can use the pascal or, on some compilers, _pascal keyword to tell the C Compiler that the parameters are to be passed in Pascal order (which is the same way QuickBasic uses). Here's how we'd rewrite the above C function: void pascal PrintIt(int x, int y, char *text) Some C compilers (namely Borland and some others) also let you set all the parameters to be passed using Pascal order by a setting in the compiler's IDE configuration. This can cause some problems, though, if you have other functions in the C source that are going to also use the same function that is to be used in QuickBasic. When doing this type of programming, you will have to be sure when you use pascal only on the functions that are not used by other ones in the C source, or else leave it off and declare it as CDECL in the QuickBasic program (discussed in a moment). There are other ways around this too, which is beyond the scope of this tutorial. Most C programmers would know of these other options. When you compile the function in C that you used the pascal keyword in, it will be all set so that QuickBasic can read it. Then just declare it in QuickBasic as: DECLARE SUB PrintIt(BYVAL x AS INTEGER, BYVAL y AS INTEGER,_ SEG text AS STRING) However, there are times when you are told an .OBJ file was compiled in C but you may not have the actual source code to it, and therefore can't make any changes. So, do we just declare the parameters in backwards order? Well, not quite. QuickBasic has CDECL which will let you tell it that you are using a C function: C function: void PrintIt(int x, int y, char *text) QuickBasic Declaration: DECLARE SUB PrintIt CDECL (BYVAL x AS INTEGER, BYVAL y AS INTEGER,_ SEG text AS STRING) This lets QuickBasic know that the parameters are actually being passed in reverse order, or CDECLare (C-DECLARE). COMPILING AND LINKING IN THE C FUNCTION ======================================== If you are writing the C function yourself, you may want to be double sure that you don't have it compile in any debug information. Some compilers have IDEs that are set to add in debug info so that you can step through your C program, much like the Debug in QuickBasic does. To be best compatible with QuickBasic, you will want this turned off. Also be sure your compiler will create Microsoft-compatible .OBJ files. Microsoft products like Quick C will do this but some other compilers may also be Microsoft compatible. Sometimes you may need to compile the program using the assembler that comes with the compiler, and not the compiler itself (as sometimes may be the case with Borland C++ 3.1, for example). You'll need to be familiar with your compiler and its IDE and/or other configurations (if any) to get the best results. If nothing works, then its probably because the .OBJ files are just not compatible. After you created (or obtained) an .OBJ file of the C function, you will need to link them into your program. First be sure that LINK.EXE and LIB.EXE are in your PATH or current directory where the .OBJ files are and also the BQLB45.LIB, and possibly the QB.LIB (if you plan to use INTERRUPTs in your program too). I normally make the QLB file first: LINK /Q CPROG1.OBJ CPROG2.OBJ CPROG2.OBJ, CPROG.QLB,,BQLB45.LIB; This creates the CPROG.QLB file and a .MAP file that you can delete (but sometimes may be helpful to look at in case of problems). If you are going to use interrupts, change the BQLB45.LIB; above to: BQLB45.LIB+QB.LIB; Next make the Library so that you can also compile your program: LIB CPROG.LIB+CPROG1.OBJ+CPROG2.OBJ+CPROG3.OBJ; Or, if planning to use interrupts too: LIB CPROG.LIB+CPROG1.OBJ+CPROG2.OBJ+CPROG3.OBJ+QB.LIB; This makes your CPROG.LIB file. Now just start QB with: QB /L CPROG.QLB and make the declarations as explained earlier in this tutorial. Its best to create this as a .BI file and '$INCLUDE: 'CPROG.BI' for example, in your program. You can also use more than one $INCLUDE in your program. This is useful if you need to also use QB.BI for INTERRUPTs. PROBLEMS IN LINKING THE OBJ FILE TO A QLB ----------------------------------------- L1101 invalid object module This is probably one of the more common ones, and most of the time is because your OBJ file is not compatible somehow with QuickBasic. Normally, it may be because you inadvertently included some of the C compiler's Debug information (as explained previously). If this don't help, try compiling using the assembler or compiling to assembly source and using a Microsoft compatible assembler to compile the .OBJ file, if possible. There are times though when there doesn't seem to be a fix. Those are the ones that just aren't compatible. Best try another C compiler and/or assembler if that happens. L2029 unresolved externals This is another common problem when linking. This occurs for basically the same reason as the L1101 error above does, but may also be something in your C program. Does anything in your C program call any external functions that are not included in the compiling? Perhaps from another program or source? You will have to compile that source too and include the OBJ file in. Another common problem is you may find "printf" or other standard library functions to be one of the unresolved externals. This means that the .OBJ file is made so that the C Compiler knows where and what printf is. But, QuickBasic does not. One solution you may want to try if your compiler supports it, is to compile to an assembly language source, then compile that to an .OBJ file with an assembler. Basically, you want to be sure that the .OBJ is pure machine language, and nothing external being called that isn't going to be included in your QuickBasic library. L2041 stack plus data exceed 64K This sometimes occurs if you compiled the program using an incompatible memory model. For best results, use the medium, compact or small memory model to compile C functions that will be used in QuickBasic. These are the most common ones you might run into. For other linker errors (and more detailed descriptions of these) see your QuickBasic manual, section 2, pp. 420 - 431. ERRORS IN RUNNING THE QUICKBASIC PROGRAM ---------------------------------------- One of the most common ones is that QuickBasic will say that the sub or function is not found in the Quick Library. In that case, be sure you loaded the Quick Library. You may also want to double check all your steps and try compiling and linking again. If you used an assembler to compile the source, perhaps the assembler did not specify the function as '.public'. You may have to use a text editor to make that change in the assembly source code and re-assemble it. Another common error is that QuickBasic may find something wrong with the list of parameters. Be sure you have it declared right, and are sure of the way the parameters are seen in QuickBasic. Maybe you didn't use CDECL in QuickBasic or pascal in your C function. You can't do both. You can only use CDECL in QuickBasic's DECLARE =OR= use the pascal keyword in the C function. Doing both puts us in the same position of one seeing the list of parameters in reverse order than what was intended. ******* DISCLAIMER ******* The author of this article cannot guarantee the usability or suitability of the information presented herein for any particular purpose. In addition, the user of the information in this article agrees not to hold the author, moderator or any other direct or indirect agent liable in any way for any damages, loss of data, or other consequences arising from use of this information. If laws in your area do not allow this type of disclaimer than DON'T USE THIS INFORMATION! While I have made every conscious effort to ensure the information in this tutorial is accurate, the end result depends on the person making use of the information presented here. Use the information in this tutorial at your own risk. ******* CONTACT INFORMATION ******* As of 7/11/1997, comments, questions and suggestions, can be directed to: FidoNet: Tika Carr 1:2613/601 or 1:2613/313 Internet: t.carr@pobox.com ===================================================================== All compilers and products mentioned are copyright and/or trademarks of their respective owners.