Microsoft KB Archive/104512

= LONG: Calling C Routines from Basic -- Part 2 of 2 =

Article ID: 104512

Article Last Modified on 8/16/2005

-

APPLIES TO


 * Microsoft Visual Basic for MS-DOS
 * Microsoft Cinemania 97 Standard Edition
 * Microsoft QuickBasic 4.5 for MS-DOS

-



This article was previously published under Q104512



... continued from part 1 ...

COMMON BLOCKS
You can pass individual members of a Basic common block in an argument list, just as you can any data. However, you can also give a C routine access to the entire COMMON block at once.

C can reference the items of a COMMON block by first declaring a structure with fields that correspond to the COMMON block variables. Having defined a structure with the appropriate fields, the C routine must then get the address of the COMMON block.

To pass the address of the COMMON block, pass the address of the first variable in the block. The C routine should expect to receive a structure by reference.

Passing arrays through the COMMON block is done in a similar fashion. However, only static arrays can be passed to C through COMMON.

Microsoft does not support passing dynamic arrays through COMMON to C (since this depends upon a Microsoft proprietary dynamic array descriptor format that changes from version to version). Dynamic arrays can be passed to C only as parameters in a CALL statement.

CALLING MS-DOS I/O ROUTINES DOES NOT AFFECT Basic CURSOR POSITION
C Routines linked with a Basic program that does screen output (by using printf, puts, and so on) do not update the cursor position after returning to the calling Basic program. (NOTE: This also applies to using the CALL INTERRUPT statement in Basic).

For example, after doing the following, the next PRINT statement goes directly after the last Basic PRINT statement, ignoring the new line position from calling the C routine:
 * 1) Doing a PRINT from Basic
 * 2) Calling a C routine that does some string display functions (printf)
 * 3) Returning to Basic

This is expected behavior. C routines should not change the Basic cursor position.

DEBUGGING MIXED-LANGUAGE PROGRAMS
CodeView is useful when trying to debug mixed-language programs. With it, you can trace through the source code of both C and Basic.

To compile programs for use with CodeView, use the /Zi switch on the compile line for both the C and Basic compilers. Then when linking, use the /CO switch.

CodeView is a multilanguage source code debugger supplied with the Professional Edition of Microsoft Visual Basic version 1.0 for MS-DOS.

COMPILING AND LINKING THE SAMPLE PROGRAMS
Following the appendix is a series of examples that demonstrate the interlanguage calling capabilities between Basic and C.

When compiling the sample Basic programs, you can compile with or without the /O (stand-alone library) option. The following uses the stand-alone library to produce a stand-alone Basic program:

BC /O Basicmodulename;

The following uses the run-time library, so that the compiled program needs the run-time library on disk when it is executed:

BC Basicmodulename

When compiling the sample C programs, one of the following compile lines should be used:

CL /c /AM Cmodulename

NOTE: For convenience, the medium memory model is used for the C programs. If the large memory model were used instead (/AL instead of /AM), all pointers would have to be explicitly defined as near.

To link the programs, use the following link line:

LINK /NOE Basicmodulename Cmodulename;

APPENDIX: COMMON PITFALLS
The following common pitfalls are all explained in more detail in the main text. Use this appendix as a checklist when you encounter problems while doing mixed-language programming:  Make certain the version numbers of the two languages are compatible. Make certain that a C routine is declared with CDECL in Basic or that in C the PASCAL directive is used when defining the procedure. This ensures the same calling convention is being used. Make sure that the graphic libraries are not included in the C libraries. If the graphic libraries are included, duplicate definition errors will occur. Use the /NOE switch when linking to avoid duplicate definition errors. If duplicate definition errors still occur, also use the /NOD switch when linking. Watch for incompatible C functions such as system and getenv. When passing strings to C, check for two things:

 If a C function will use the string, it should be null-terminated (CHR$(0)).</li> SSEGADD should be used instead of VARPTR to pass the actual address of the string. VARPTR returns the offset to the string descriptor, not to the string itself.</li></ol> </li> The C routine should be compiled in the medium or large memory model. If the large memory model is used, then all pointers to Basic variables must be explicitly defined as near.</li> When using VARSEG, VARPTR, SADD or SSEGADD to pass addresses to assembly language, it is important to check the function definition. Since Basic normally passes all parameters by reference, any parameter that is an address should be declared using BYVAL. If BYVAL is not used, Basic will create a temporary variable to hold the address, then pass a pointer to this variable (in effect, pass a pointer to a pointer).</li> If you enter the libraries on the link line, then the Basic libraries must precede all others.</li></ol>

Basic Program
DECLARE SUB NumericNear CDECL (a%,b&,c!,d#)

a% = 32767 b& = 32769 c! = 123.312 d# = 129381.333#

CLS CALL NumericNear(a%, b&, c!, d#) LOCATE 5, 1 END

C Routine

 * 1) include <stdio.h>

void NumericNear(int *a, long *b, float *c, double *d) { printf("INTEGER %d\n", *a); printf("LONG   %ld\n", *b); printf("FLOAT  %f\n", *c); printf("DOUBLE %lf\n", *d); }

Output
INTEGER 32767

LONG 32769

FLOAT 123.311996

DOUBLE 129381.333000

Basic Program
DECLARE SUB NumericFar CDECL (_       BYVAL p1o AS INTEGER, BYVAL p1s AS INTEGER,_        BYVAL p2o AS INTEGER, BYVAL p2s AS INTEGER,_        BYVAL p3o AS INTEGER, BYVAL p3s AS INTEGER,_        BYVAL p4o AS INTEGER, BYVAL p4s AS INTEGER)

a% = 32767 b& = 32769 c! = 123.312 d# = 129381.333# CLS CALL NumericFar(VARPTR(a%), VARSEG(a%),_               VARPTR(b&), VARSEG(b&),_                VARPTR(c!), VARSEG(c!),_                VARPTR(d#), VARSEG(d#))

LOCATE 5, 1 END

C Routine
void NumericFar(int far *a, long far *b, float far *c, double far *d) {
 * 1) include <stdio.h>

printf("INTEGER %d\n", *a); printf("LONG    %ld\n", *b); printf("FLOAT   %f\n", *c); printf("DOUBLE  %lf\n", *d); }

Output
INTEGER 32767

LONG 32769

FLOAT 123.311996

DOUBLE 129381.333000

Basic Program
DECLARE SUB NumericValue CDECL (_           BYVAL p1 AS INTEGER,_            BYVAL p2 AS LONG,_            BYVAL p3 AS SINGLE,_            BYVAL p4 AS DOUBLE)

a% = 32767 b& = 32769 c! = 123.312 d# = 129381.333# CLS CALL NumericValue(a%, b&, c!, d#) LOCATE 5, 1 END

C Routine
void NumericValue(int a, long b, float c, double d) { printf("INTEGER %d\n", a); printf("LONG    %ld\n", b); printf("FLOAT   %f\n", c); printf("DOUBLE  %lf\n", d); }
 * 1) include <stdio.h>

Output
INTEGER 32767

LONG 32769

FLOAT 123.311996

DOUBLE 129381.333000

Basic
DECLARE SUB test CDECL (BYVAL a%, BYVAL b%)

CALL test(ASC("A"), ASC("B")) END

C Routine
void test(char a, char b) {
 * 1) include <stdio.h>

printf("%c %c\n",a,b); }

Output
A B

Basic Program
DECLARE SUB StringFar CDECL (BYVAL addr AS LONG, ln AS INTEGER) CLS a$ = "This is a test" + CHR$(0) CALL StringFar(SSEGADD(a$), LEN(a$))

LOCATE 20, 1 END

C Routine
void StringFar(char far *a, int *len) { int i;  printf("The string is : %Fs\n\n", a); printf(" Index      Value       Character\n");
 * 1) include <stdio.h>

for (i=0;i < *len; i++) {

printf(" %2d          %3d            %c\n", i, a[i], a[i]); } }

Output
The string is : This is a test

Basic Program
DECLARE SUB StringNear CDECL (_           BYVAL p1o AS INTEGER,_            p3 AS INTEGER)

DIM a AS STRING * 15 CLS a = "This is a test" + CHR$(0) CALL StringNear(VARPTR(a), LEN(a)) LOCATE 20, 1 END

C Routine
void StringNear(char *a, int *len) { int i;  printf("The string is : %s \n\n",a); printf(" Index      Value       Character\n"); for (i=0;i < *len; i++) { printf(" %2d          %3d            %c\n", i, a[i], a[i]); } }
 * 1) include <stdio.h>

Output
The string is : This is a test

Basic Program
DECLARE SUB StringFar CDECL (_           BYVAL p1o AS INTEGER,_            BYVAL p1s AS INTEGER,_            p3 AS INTEGER)

DIM a AS STRING * 15 CLS a = "This is a test" + CHR$(0) CALL StringFar(VARPTR(a), VARSEG(a), LEN(a)) END

C Routine
void StringFar(char far *a, int *len) { int i;  printf("The string is : %Fs\n\n", a); printf(" Index      Value       Character\n"); for (i=0;i < *len; i++) { printf(" %2d          %3d            %c\n", i, a[i], a[i]); } }
 * 1) include <stdio.h>

Output
The string is : This is a test

Basic Program
DECLARE SUB CSUB CDECL

TYPE fixstringtype

B AS STRING * 26

END TYPE CALL CSUB END

SUB BASSUB(B AS fixstringtype) PRINT B.B  PRINT LEN(B.B) END SUB

C Routine

 * 1) include <string.h>

extern void pascal bassub(char *basfixstring); char thestring[26]; void csub { strcpy(thestring, "This is the string"); bassub(thestring);

}

Output
This is the string:

26

Basic Program
TYPE record a AS INTEGER b AS STRING * 20 c AS SINGLE

END TYPE

DECLARE SUB TypeReference CDECL (p1 AS record) CLS DIM element AS record element.a = 128 element.b = DATE$ + CHR$(0) element.c = 39.6 CALL TypeReference(element) LOCATE 4, 1 END

C Routine
struct record { int a;  char b[20]; float c; };
 * 1) include <stdio.h>

void TypeReference(struct record *element) { printf("Record.A = %d\n", element->a); printf("Record.B = %s\n", element->b); printf("Record.C = %f\n", element->c); }

Output
Record.A = 128

Record.B = 02-02-1988

Record.C = 39.599998

Basic Program
TYPE record a AS INTEGER b AS STRING * 20 c AS SINGLE

END TYPE

DECLARE SUB TypeReference CDECL (BYVAL p1o AS INTEGER, _  BYVAL p1s AS INTEGER) CLS DIM element AS record element.a = 128 element.b = DATE$ + CHR$(0) element.c = 39.6 CALL TypeReference(VARPTR(element), VARSEG(element)) LOCATE 4, 1 END

C Routine
struct record { int a;  char b[20]; float c; };
 * 1) include <stdio.h>

void TypeReference(struct record far *element) { printf("Record.A = %d\n", element->a); printf("Record.B = %s\n", element->b); printf("Record.C = %f\n", element->c); }

Output
Record.A = 128

Record.B = 02-02-1988

Record.C = 39.599998

Basic Program
DECLARE SUB IntArray CDECL (BYVAL p1 AS INTEGER, BYVAL p2 AS INTEGER) DEFINT A-Z DIM i AS INTEGER DIM array(10) AS INTEGER CLS FOR i = 1 TO 10 array(i) = i

NEXT i 'Array must be a FAR pointer, so offset and segment must be passed: CALL IntArray(VARPTR(array(0)), VARSEG(array(0))) LOCATE 13, 1 PRINT "Back in Basic" FOR i = 1 TO 10 PRINT i, array(i)

NEXT i END

C Routine
void IntArray (int far *array) { int i;  printf("Index         Value\n"); for (i = 0; i <= 10; i++) { printf(" %d         %d\n", i, array[i]); array[i] = array[i] + 100; } }
 * 1) include <stdio.h>

Basic Program
DECLARE SUB LongArray CDECL (_           BYVAL p1 AS INTEGER,_            BYVAL p2 AS INTEGER) DEFINT A-Z DIM i AS LONG DIM array(10) AS LONG CLS FOR i = 1 TO 10 array(i) = i + 100

NEXT i 'Array must be a FAR pointer, so offset and segment must be passed: CALL LongArray(VARPTR(array(0)), VARSEG(array(0))) LOCATE 13, 1 PRINT "Back in Basic" FOR i = 1 TO 10 PRINT i, array(i)

NEXT i END

C Routine
void LongArray(long far *array) { int i;  printf("Index         Value\n"); for (i=0; i < 11; i++) { printf(" %d          %ld\n", i, array[i]); array[i] = array[i] + 100; } }
 * 1) include <stdio.h>

Output
Index Value 0 0 1 101 2 102 3 103 4 104 5 105 6 106 7 107 8 108 9 109 10 110 Back in Basic 1 201 2 202 3 203 4 204 5 205 6 206 7 207 8 208 9 209 10 210

Basic Program
DECLARE SUB FloatArray CDECL (BYVAL p1 AS INTEGER, BYVAL p2 AS INTEGER) DEFINT A-Z DIM i AS SINGLE DIM array(10) AS SINGLE CLS FOR i = 1 TO 10 array(i) = i + 100

NEXT i 'Array must be a FAR pointer, so offset and segment must be passed: CALL FloatArray(VARPTR(array(0)), VARSEG(array(0))) LOCATE 13, 1 PRINT "Back in Basic" FOR i = 1 TO 10 PRINT i, array(i)

NEXT i END

C Routine
void FloatArray(float far *array) { int i;  printf("Index         Value\n"); for (i=0; i < 11; i++) { printf(" %d          %f\n", i, array[i]); array[i] = array[i]+100; } }
 * 1) include <stdio.h>

Output
Index Value 0 0.000000 1 101.000000 2 102.000000 3 103.000000 4 104.000000 5 105.000000 6 106.000000 7 107.000000 8 108.000000 9 109.000000 10 110.000000 Back in Basic 1 201 2 202 3 203 4 204 5 205 6 206 7 207 8 208 9 209 10 210

Basic Program
DECLARE SUB DoubleArray CDECL (BYVAL p1 AS INTEGER, BYVAL p2 AS INTEGER) DEFINT A-Z DIM i AS DOUBLE DIM array(10) AS DOUBLE CLS FOR i = 1 TO 10 array(i) = i + 100

NEXT i 'Array must be a FAR pointer, so offset and segment must be passed: CALL DoubleArray(VARPTR(array(0)), VARSEG(array(0))) LOCATE 13, 1 PRINT "Back in Basic" FOR i = 1 TO 10 PRINT i, array(i)

NEXT i END

C Routine
void DoubleArray(double far *array) { int i;  printf("Index         Value\n"); for (i=0; i < 11; i++) { printf(" %d          %lf\n", i, array[i]); array[i] = array[i] + 100; } }
 * 1) include <stdio.h>

Output
Index Value 0 0.000000 1 101.000000 2 102.000000 3 103.000000 4 104.000000 5 105.000000 6 106.000000 7 107.000000 8 108.000000 9 109.000000 10 110.000000 Back in Basic 1 201 2 202 3 203 4 204 5 205 6 206 7 207 8 208 9 209 10 210

Basic Program
DECLARE SUB StringFar CDECL (length%, num%, BYVAL p3o AS INTEGER,_  BYVAL p3s AS INTEGER)

DIM array(10) AS STRING * 10 CLS length% = 10 num% = 3 FOR i = 0 TO 10 array(i) = STRING$(9, 65 + i) + CHR$(0)

NEXT i CALL StringFar(length%, num%, VARPTR(array(0)), VARSEG(array(0))) END

C Routine
void StringFar(int *len, int *num, char far *array) { int i;  printf("The string length is : %d \n\n",*len); printf("The number of elements is : %d \n\n",*num); printf(" Index       String\n"); for (i=0; i < *num; i++) { printf(" %2d         %Fs\n", i, array); array=array+*len; } }
 * 1) include <stdio.h>

Output
The string length is : 10

The number of elements is : 3

Index String 0 AAAAAAAAA 1 BBBBBBBBB 2 CCCCCCCCC

Basic Program
TYPE record a AS INTEGER b AS STRING * 20 c AS SINGLE

END TYPE

DECLARE SUB TypeArray CDECL (BYVAL p1o AS INTEGER, BYVAL p1s AS INTEGER) DIM element(10) AS record CLS FOR I = 0 TO 10 element(I).a = 128 + I  element(I).b = STR$(I) + ". " + DATE$ + CHR$(0) element(I).c = 39.6 * I

NEXT I CALL TypeArray(VARPTR(element(0)), VARSEG(element(0))) END

C Routine

 * 1) include <stdio.h>

struct record { int a;  char b[20]; float c;

};

void TypeArray(struct record far *element) { int i;  for (i=0; i<3; i++) { printf("Record[%d].A = %d\n", i, element->a); printf("Record[%d].B = %Fs\n", i, element->b); printf("Record[%d].C = %f\n", i, element->c); printf("\n"); element++; } }

Output
Record[0].A = 128

Record[0].B = 0. 02-02-1988

Record[0].C = 0.000000

Record[1].A = 129

Record[1].B = 1. 02-02-1988

Record[1].C = 39.599998

Record[2].A = 130

Record[2].B = 2. 02-02-1988

Record[2].C = 79.199997

Basic Program
DECLARE SUB TwoIntArray CDECL (BYVAL p1o AS INTEGER, BYVAL p1s AS INTEGER)

DIM x(4, 4) AS INTEGER CLS FOR i = 0 TO 4 <pre class="fixed_text">  FOR j = 0 TO 4

x(i, j) = i * 10 + j

<pre class="fixed_text">  NEXT NEXT CALL TwoIntArray(VARPTR(x(0, 0)), VARSEG(x(0, 0))) LOCATE 5, 1 END

C Routine
struct two_int_array { int a[5][5]; };
 * 1) include <stdio.h>

void TwoIntArray(struct two_int_array far *x) { int i,j; for (i = 0; i < 5; i++) { for (j = 0; j < 5; j++) { printf(" %3d   ", x->a[i][j]); }     printf("\n"); } }

Output
0 10 20 30 40 1 11 21 31 41 2 12 22 32 42 3 13 23 33 43 4 14 24 34 44

Basic Program
DECLARE SUB RCommon CDECL (BYVAL p1o AS INTEGER, BYVAL p1s AS INTEGER)

COMMON SHARED element1 AS INTEGER, element2 AS STRING * 20,_ <pre class="fixed_text">  element3 AS SINGLE

element1 = 23 element2 = "DATE : " + DATE$ + CHR$(0) element3 = 309.03 CALL RCommon(VARPTR(element1), VARSEG(element1)) END

C Routine

 * 1) include <stdio.h>

struct common_block { // structure ooks like Basic common block int a;  char b[20]; float c; };

void RCommon(struct common_block far *pointer) { printf("Element1 = %d\n", pointer->a); printf("Element2 = %Fs\n", pointer->b); printf("Element3 = %f\n", pointer->c); }

Output
Element1 = 23 Element2 = DATE : 02-02-1988 Element3 = 309.029999

Basic Program
DECLARE SUB StringFar CDECL (BYVAL p1o AS INTEGER, BYVAL p1s AS INTEGER,_  p3 AS INTEGER)

DIM array AS STRING * 15 CLS array = "This is a test" + CHR$(0) CALL StringFar(VARPTR(array), VARSEG(array), LEN(array)) LOCATE 20,20 PRINT array END

C Routine
void StringFar(char far *a, int *len) { int i;  printf("The string is : %Fs\n\n",a); printf(" Index      Value       Character\n"); for (i = 0;i < *len; i++) { printf(" %2d       %3d      %c\n", i, a[i], a[i]); }  /* This loop writes over the end of the string */ for (i = 10; i < *len; i++) { a[i] = 64;   // ASCII value for '@' } }
 * 1) include <stdio.h>

Output
The string is : This is a test

Index Value Character 0 84 T 1 104 h 2 105 i 3 115 s 4 32 5 105 i 6 115 s 7 32 8 a 9 32 10 116 t 11 101 e 12 115 s 13 116 t

This is a @@@@

Basic Program
DECLARE FUNCTION cintfunc% CDECL DECLARE FUNCTION clongfunc& CDECL DECLARE FUNCTION csinglefunc! CDECL DECLARE FUNCTION cdoublefunc# CDECL

PRINT "Integer: "; cintfunc PRINT "Long : "; clongfunc PRINT "Single : "; csinglefunc PRINT "Double : "; cdoublefunc

C Routines
int cintfunc(void) { int theint = 32767; return(theint); }

long clongfunc(void) { <pre class="fixed_text">  long thelong = 32769; return(thelong); }

float csinglefunc(void) { <pre class="fixed_text">  float thefloat = 123.312F; return(thefloat); }

double cdoublefunc(void) { <pre class="fixed_text">  double thedouble = 129381.123; return(thedouble); }

Output
Integer: 32767 Long : 32769 Single : 123.312 Double : 129381.123

Additional query words: VBmsdos 1.00

Keywords: KB104512

-

[mailto:TECHNET@MICROSOFT.COM Send feedback to Microsoft]

© Microsoft Corporation. All rights reserved.