12.8 32-bit files

------------------

12.8.1 32-bit code under DOS

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To compile a 32-bit DOS program use the command-line switch /d32. You

will to use a DOS extender as well, such as DOS4GW or ZRDX. The line

stub=path_name_stub_file in c--.ini indicates the path to and name of the

stub, for example:

stub=c:\c--\zrdx.exe

Without this line, the compiler will generate a 32-bit EXE file

without a DOS extender. If you use /ns instead of /d32, the line with

stub in c--.ini is invalidated and the EXE file again has no DOS

extender.

For a 32-bit DOS file you can use the compiler directives

?parsecommandline TRUE/FALSE or the extended variant ?argc TRUE/FALSE.

There is also support for ?atexit TRUE/FALSE.

The LE format is now used for 32-bit DOS files. Since this is a

standard format almost any stub which supports this format can be used.

LE files can be compressed with programs like upx.exe.

If you use a stub which then loads DOS4GW, there should be a special

signature at the beginning of the program. The compiler automatically

generates it on encountering the command-line switch /DOS4GW. You must

use this switch if you use 4GS as a stub.

There is also support for a block of code which uses a DPMI service

for switching and operating in 32-bit mode. The source text of this block

is foiund in startup.h-- and is compiled if the option /stub=dpmi is

found on the command line or if stub=dpmi is in c--.ini. A drawback of

this method of switching and operating in 32-bit mode is that the DMPI

service must be part of the boot sequence. Since the program is loaded as

an ordinary DOS program and only during operation does it switch to

32-bit mode, the program size is limited by amount of free DOS memory. An

advantage is the small size of the executable.

12.8.2 32-bit code under Windows

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To compile a Windows program use the command-line switch /w32.

If API functions are use in a program they must first be declared, as

follows:

extern WINAPI "DLL_name"

{

returncode procname1();

returncode procname2();

procname3();

}

where:

DLL_name is the file name and extension of the DLL library where the

functions are located

returncode is the type of return from the API functions, by default dword.

Windows programs have one feature that should not be overlooked - all

the parameters are transferred to the stack functions in inverse order

(so-called C style), but the stack is freed of parameters in these

functions themselves. This gives a hybrid of C and pascal function -

stdcall.

The switch /w32c is used to create Windows console programs.

Using the command-line option /j0 or the directive '#jumptomain NONE'

causes the program to be compiled without using the startup code

described in startup.h--.

Startup code for Windows programs is of the following format:

hThisInst=GetModuleHandleA(0);

#ifdef __CONSOLE__

hStdOut=GetStdHandle(-11);

#endif

lpszArgs=GetCommandLineA();

#ifdef __environ;

environ=GetEnvironmentStringsA();

#endif

main();

ExitProcess(EAX);

The global variables hThisInst therefore contain the handle of the

file being run, and lpszArgs contains the address of the file's command

line. Using the command-line options /p or /argc, or the directive

'#parsecommandline TRUE' or 'argc TRUE' in the beginning of the file

causes the compiler to create additional code which splits the command

line into parts. When compiling a console program, there is one

additional global variable hStdOut, which keeps the handl of the standard

output (to screen). If the option /env was specified during compilation,

the address for accessing the program's environment is stored in the

global variable 'environ'.

After 'main' is completed, 'ExitProcess' is executed, and it is

transfered to register EAX as a parameter. In order to terminate a

program, it is sufficient to exit 'main' after first loading the needed

return code into EAX.

Some compilers create DLL's in which the names of the exported

functions are of the format:

ProcName@8

The string after @ indicates the stack size with the parameters

transferred to the function.

Such functions are declared as follows:

extern WINAPI "name.dll"

{

ProcName@8 ;

}

i. e., without parentheses. When such a function is used in a

program, the name must be written without the suffix @8 -

ProcName(param1,param2);

12.8.3 Call API functions by ordinal number

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In dynamic linked libraries (DLLs) each function has in addition to a

name a unique ordinal number. Therefore API functions can be called not

only by name but by ordinal number. In theory the file will be loaded

faster when called by number. Since the output file does not contain

lists of function names which are called by numbers, the output file will

be slightly smaller.

To compile a file which will call API functions by number:

1. Give the compiler permission using the command-line switch /wo or

wo in c--.ini

2. Inform the compiler of the ordinal number for each function name.

Functions to which no number was assigned will be called by name. There

are two ways to set up a correspondence between function names and

numbers:

a) automatically using 'ind=name.dll' on the command line, which causes

the compiler to scan this library and import all function names and

numbers. (You can only import from PE format libraries).

b) manually list the API function and its number in the declaration, by

placing a period after the function name and then its number. Here is an

example of how to declare an APR function with its number:

extern WINAPI "user32.dll"

{

............

long MessageBoxA.429();

............

}

DLL libraries sometimes contain functions with numbers but no names.

You cannot call them by name, but you can do so by number (if you know

what the function is and does, of course). When declaring the API

function you have to think of a unique name for it and indicate the

actual number. Then you can refer to it by the name that you have made

up. If you accidentally compile this file without the switch '/wo' when

running the program you will be told that there is no such name in the

library.

Unfortunately there is no guarantee that the number of a particular

function will not differ in different versions of dynamic libraries, so

be careful when calling the function by its number.

12.8.4 Creating DLL's under Windows

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Dynamically linked libraries can be used to obtain smaller programs

and to speed up compilation. Drawbacks of their use include the need for

the DLL files to be present on the computer and the fact that the

programs take a little longer to load.

In order to make a function accessible to other programs, the source

code must contain before the function name the keyword _export, for

example:

void _export testproc()

{

....

}

To create a DLL, you must write a file containing functions with the

keyword _export. Auxiliary functions which may be needed by the exported

functions to work, need not be declared as _export. Therefore this file

must be compiled with the switch /dll, which gives a complete dynamically

linked library.

12.8.5 Initializing DLLs while loading

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Sometimes when working with DLL functions you need to initialize some

variable with values which depend on the current state of the operating

system, for example to obtain the descriptor of this library.

In all other cases control a stub code is generated and control is

not passed to main. In fact main is not needed in this case.

When creating DLL files, main should look somewhat different than in

other cases:

dword main ( dword hInstDLL, reason, reserv )

{

...

}

12.8.6 Compiling resources

~~~~~~~~~~~~~~~~~~~~~~~~~~~

The resource compiler built into C-- is not as capable as specialized

resource compilers but should be sufficient for most uses.

It is simplest to list what this built-in compiler cannot do. The

resource operators VERSION are not processed, nor are resources defined

by the user. If needed, data entered using these operators can be entered

using RCDATA instead. Many resource operators require the parameters

'loading' and 'memory'. There is no support here for these parameters,

which the compiler will simply skip over.

There are two ways to force C-- to process resources:

1. Include in the directive an '#include' with extension '.rc'. Such

files are considered as resource files. A resource file must be included

in a project only after including Windows headers files.

2. Resources can be placed anywhere in the body of the source code.

The code of the resources should start with the '#pragma resource start'

and end with '#pragma resource end'. Resources can be divided into parts

and these parts can be placed anywhere convenient. (But do not place them

in the commentary block and then be surprised when they do not get

compiled). The compiler will collect and compile the parts.

The names of the operators can be written in upper or lower case, but

the names of the identifiers are case-sensitive. Directives and

commentaries can be used in the text of the resources.

There is nothing to stop you from using compiler resources from other

languages. It is important that the syntax of the resource file

correspond to the compiler.