By Alan O.H. (CGI Joe) Feb 10th 2001


Using 'C' routines in QuickBasic

A lot of people have been asking how to do this in the Future Software message board recently. So, out of the goodness of my heart ;), here's a short tutorial showing you how to do just that. Naturally, I'm not going to explain how to code in C, that takes a lot of time. Also, you should have a DOS Real-Mode C/C++ compiler capable of generating OBJ files. A common example would be Borland's Turbo C++ 3.0.

I will outline all of the steps involved in using C routines in QuickBasic and will be taking you through an example of how to put what we learnt to the test...

LIB files

An easy way to use our C routines in QB is through a library. Libraries are simply a collection of sub-procedures and functions. Basically, an EXE file without a "main" function or any startup code. We build libraries by writing the routines in our favourite language and then compiling or assembling the code to produce an OBJ (object) file. We can then build a LIB file using our OBJ(s). Similarly, when we write a standard program, we compile the source code into an OBJ file. It's LINK.EXE that turns the OBJ into an EXE.

So you can see that all we really have are a bundle of OBJ files (from the library and from our program). The OBJ file is a standard format. That is, an OBJ file generated by a C compiler is the same produced by BC, the QuickBasic compiler. This is easy! All we need to do is write our C routines, compile them into an OBJ, build a LIB file from it and then link it with our QuickBasic program. But wait, there's trouble ahead..

Getting around incompatibilities

When calling a Sub or Procedure, it's quite common to pass parameters. For example, when you use PSET you will need to pass an X and Y coordinate plus a colour. Unfortunately for us, C uses a different parameter order to QuickBasic. That is, QuickBasic passes our arguments from right to left (the X first then the Y and finally the colour). If PSET was a C routine it would pass the colour first then the Y then X. To the programmer, this is completely transparent and you don't have to worry about it if you're not using mixed-language programming. But we are! It's a very important issue.

Fortunately, we can get around this quite easily. One way is, when Declaring our external procedures, to use the CDECL keyword. This tells QuickBasic to pass the parameters backwards as this is a C routine. Another way is to tell the C routine that QuickBasic will be calling it. I like this way better as it hides the fact you're using C to speed up your program! :D. We can force the C routine to be compatible with QuickBasic by using the pascal keyword. (It gets it's name from the fact that Pascal passes it's parameters the same way as QuickBasic).

An example

Ok, let's write a function that takes a number, adds 10 to it, and returns the result.
Let's write the C code first:

int far pascal AddTen(int number) { int x; x = number + 10; // add 10 return (x); // return the result } IMPORTANT: You must always remember to use the far keyword in your C procedure. If you do not, your machine will crash or do something quite nasty.

Copy and paste that into a file. Now, this is important: YOU MUST GIVE THE FILE A .C EXTENSION!. If you give it a .CPP extension, it will compile in C++ mode which completely mangles our procedure names making them unusable outside of C++. I'll assume you have named the file:

TEST.C

Now, for Borland Turbo C++ 3.0, we compile the file like so: tcc -c test.c This is the command line version of the compiler so we don't need to get involved in any complicated IDE's. The '-c' option means we want to compile only. Now, you should have the file 'test.obj'.

Now, onto building the .LIB file. Enter this at the DOS prompt:

LIB mylib It will respond with: Library does not exist. Create? (y/n) Choose 'yes'. It will then prompt you for Operations. This is where you input your OBJ files. We only have one at the moment but if in the future you need to add more, simply append a '+' symbol to the OBJ filename. Enter the following:

Operations: test.obj It will then prompt you with List file:. Just press enter; we don't need this.
You should now have a lib file called 'Mylib.Lib'! Now we can go ahead and use this to link with our QuickBasic programs, but to use in the QuickBasic IDE, we'll need a QLB file. For this we use LINK:

LINK/QU mylib.lib, mylib.qlb, NULL, BQLB45.LIB LINK might prompt you for the exact path to BQLB45.LIB. By the way, BQLB45 is a required run-time library for use inside QuickBasic. The /QU switch means 'QuickLibrary'.

Ok, let's create the BAS file. Start QuickBasic like so: "qb/l mylib". Enter the following declaration at the top of the file:

DECLARE FUNCTION AddTen% (BYVAL number%) Then do a little test:

PRINT AddTen(5) The output should be 15! If not..don't blame me!

Passing Arrays

Here, I'll show you how you can very easily pass an array to a C function and have it read/write it's contents. You can even access the array in a normal fashion; just as if you had created the array in C.

Well, first off, we don't actually pass the entire array (this goes for any language/compiler). What we do is pass the start address of the array so both functions have access to the data. This is called Passing By Reference. (The other way is Passing By Value. In QuickBasic you use BYVAL). Now if you're a C programmer, you should be familiar with pointers. What we'll do is declare the function's parameter to be an int far *. For this example, we'll write some C that sets the array's contents to a few numbers..

void far pascal SetArray(int far *array) { int i; // counter for (i=0; i<10; i++) { array[i] = i * 2; // standard stuff } return; } Note that the C function can't do any error checking, so make sure you have the space to write to the array!

Ok, now compile this file and build a library in the same way as the last example. Start QuickBasic with the appropriate library and enter this code: DECLARE SUB SetArray(SEG array%) DIM MyArray%(10) SetArray MyArray%(0) FOR t% = 0 TO 9 PRINT MyArray%(t%) NEXT t% You should see 0,2,4,6,8,10,12,14,16,18 appear on the screen!

The SEG keyword in the declaration tells QuickBasic to pass a far address of a variable or array. You must always use this when passing data to external functions. Alternatively, you could also pass the Segment and Offset individually, but this means you have to type more ;)

When calling the function, we pass the first element of the array (MyArray%(0)).

I hope this has helped you some. No doubt, I've left a critical piece of information out ;). If so send me a mail or leave a message on the Future Software Messageboard and I'll gladly help.

Thanks for putting up with my seemingly random inane ramblings.

Questions, comments, requests? Mail me

Written for qbasic.qb45.com tutorials