INF: How to Call __CRT_INIT() Without Linking in CRTEXE.OBJ

ID Number: Q72650

5.10 6.00 6.00a

OS/2

Summary:

One of the restrictions placed on an application that uses the C

run-time DLL is that the .EXE and all its application-specific DLLs

must use the DLL implementation of the C run-time library. In some

situations, it is not desirable for the .EXE to do this. For instance,

an application that executes DLLs that are unknown at build time may

have its own run-time DLL.

For that to happen, a function called __CRT_INIT() must be called in

the run-time DLL to initialize it. This is usually done in the module

CRTEXE.OBJ, which is linked with the .EXE. However, it can also be

done through function pointers by using the OS/2 APIs DosLoadModule(),

to load the run-time DLL, and DosGetProcAddr(), to retrieve the

address of the function. Once the address is known, the routine can be

called with the proper arguments and the run-time DLL will be

initialized.

The sample code below illustrates how this works. It is important to

note that this method should rarely be needed and should not be used

to overcome poor program design. It is also important to read the

comments in the code because they illustrate the pitfalls that can

occur with this procedure, and how to avoid them.

Sample Code

-----------

/* Module: EXETEST.C

*

* Compile options needed: /AL /FPa

*

* Purpose: Illustrates how to initialize the DLL version of the C

* run-time library without linking CRTEXE.OBJ into the .EXE.

*

* PseudoCode:

*

* A. DosLoadModule - Load the run-time DLL.

*

* B. DosGetProcAddr - Get address for __CRT_INIT().

*

* C. Using the address from DosGetProcAddress, call __CRT_INIT().

*

* The parameters are eight words (ints):

*

* 1. Offset of argc

* 2. Offset of argv

* 3. Offset of envp

* 4. Segment of the above

* 5. STKHQQ value

* 6. Offset of command line in environ segment

* 7. Segment of environ

* 8. Wildcard expansion flag

*

* D. If that is successful, call the application DLL functions that

* were originally linked with CRT DLL.

*

* E. Before terminating, get the address of exit routine in the

* run-time DLL. Call it with status (if needed) to shut down the

* run-time DLL.

*

* F. DosFreeModule - Release the run-time DLL.

*

* Notes:

*

* 1. The environ segment must have at least the following three bytes:

*

* 00 FF 00

*

* 'FF' is the boundary between the environment and the command

* line. If you want to actually have either, just replace with

* the desired values. The environment is any number of null

* terminated strings. The command line is one null terminated

* string. In the minimum configuration listed above, the offset of

* the command line would be set to the third byte.

*

* 2. STKHQQ is the initial stack size used by the run time. 1024

* should be more than enough.

*

* 3. Argv and envp must be far pointers. They will be updated after

* calling __CRT_INIT() to reflect the run-time DLL's version of

* these values.

*

* 4. Argc is an int. It too will be updated to reflect the actual arg

* count.

*

* 5. Wildcard expansion flag is nonzero if you want the run-time

* function to expand wildcards in the command line.

*

* 6. __CRT_INIT() is called with Pascal conventions (right to left).

*

* 7. There are four separate termination functions:

*

* a. exit(code): Performs all the C termination functions and

* terminates the process with the return code

* supplied by the user.

*

* b. _exit(code): Performs a quick exit routine that does not

* do certain 'high-level' exit processing. The

* _exit routine terminates the process with

* the return code supplied by the user.

*

* c. _cexit(): Performs the same C lib termination processing

* as exit(code) but returns control to the caller

* when done (that is, does NOT terminate the process).

*

* d. _c_exit(): Performs the same C lib termination processing

* as _exit(code) but returns control to the caller

* when done (that is, does NOT terminate the process).

*

* Multithread notes:

*

* 1. exit()/_cexit() should NEVER be called when thread locks are

* held. The exit() routine can make calls that try to get

* thread locks.

*

* 2. _exit()/_c_exit() can be called from anywhere, with or

* without locks held. _exit() should always 'work' (that is,

* the process should always terminate successfully).

*

* 3. Only one thread is allowed into the exit code routines.

*

* Entry:

* exit(), _exit()

* int status - exit status (0-255)

*

* _cexit(), _c_exit()

* <no input>

*

* Exit:

* exit(), _exit()

* <EXIT to OS>

*

* _cexit(), _c_exit()

* Return to caller

*

* 8. The .EXE MUST be built with alternate math; otherwise, there

* will be conflicts in the floating-point library. This precludes

* using the LLIBCMT (multithreaded) library. However, a DLL

* linked with LLIBCDLL can be used if multiple threads are required.

*/

#include <stdio.h>

#define INCL_DOSMODULEMGR

#define INCL_DOSMEMMGR

#include <os2.h>

void main(void);

void (_pascal _far *pfnCRT_INIT)(int WildFlag, int SegEnv,

int OffsetCmdLineInEnv, int STKHQQ,

int DataSeg, int OffsetEnvp,

int OffsetArgv, int OffsetArgc);

void (_far *pfnCRT_TERM)(void);

UCHAR szFailName[CCHMAXPATH];

USHORT cbFileName = CCHMAXPATH;

PSZ pszRunTimeModName = "CEXAMPLE";

PSZ pszRunTimeInit = "__CRT_INIT";

PSZ pszRunTimeTerm = "__cexit"; // Terminate all. Return to caller.

HMODULE hmod1;

PSZ ArgvCRT = NULL, EnvpCRT = NULL;

int ArgcCRT = 0;

SEL SegEnvCRT;

PSZ CmdLineCRT;

void main(void)

{

int rc;

printf("Inside the Main .EXE\n");

// First try to find the C run-time DLL.

if (0 != (rc = DosLoadModule (szFailName, cbFileName,

pszRunTimeModName, &hmod1 )))

{

printf("RC: %d\tUnable to load %s... Terminating\n",

rc, pszRunTimeModName);

return;

}

// Then try to find the run-time initialization routine.

if (0 != (rc =

DosGetProcAddr(hmod1, pszRunTimeInit, &pfnCRT_INIT)))

{

printf("RC: %d\tUnable to find %s in %s... Terminating\n",

rc, pszRunTimeInit, pszRunTimeModName);

return;

}

// Allocate an empty segment to hold the fake environment.

if (0 != (rc = DosAllocSeg(16, &SegEnvCRT, SEG_NONSHARED)))

{

printf("RC: %d\tUnable to Allocate Environment Segment..."\

"Terminating\n", rc);

return;

}

// Set up the command line (basically, init the env to null).

CmdLineCRT = MAKEP(SegEnvCRT, 0);

CmdLineCRT[0] = 0x00;

CmdLineCRT[1] = 0xFF;

CmdLineCRT[2] = 0x00;

// Call the run-time init function with the fake variables in

// place. (These could be the real thing with a little work.)

pfnCRT_INIT(0, SegEnvCRT, 2, 1024, HIUSHORT(&ArgvCRT),

LOUSHORT(&EnvpCRT), LOUSHORT(&ArgvCRT), LOUSHORT(&ArgcCRT));

// The same load mechanism can be used here to load the

// application DLL and get the entry point address.

// **** Place app DLL DosLoadModule here. ****

// **** Place app DLL DosGetProcAddr calls here. ****

// **** Place app DLL calls here. ****

// Now that we are done with the run-time DLL, we need to

// terminate it. For that we need to call the proper

// termination function. If necessary, pass it the return

// code.

if (0 != (rc = DosGetProcAddr(hmod1, pszRunTimeTerm,

(PPFN)&pfnCRT_TERM)))

{

printf("RC: %d\tUnable to find %s in %s...\n", rc,

pszRunTimeTerm, pszRunTimeModName);

printf("Possible problem with Run-time DLL...GP Fault!\n");

return;

}

pfnCRT_TERM();

// If needed, free the app DLL. (The run-time DLL will not

// terminate if there is still a connection to it.)

// **** Place app DLL DosFreeModule here. ****

// Then, let go of the run-time DLL.

if (0 != (rc = DosFreeModule(hmod1)))

printf ("RC: %d - DosFreeModule CRT DLL\n", rc);

// Finally, free the environment segment.

if (0 != (rc = DosFreeSeg(SegEnvCRT)))

printf ("RC: %d - DosFreeSet Env Seg\n", rc);

printf("Done ...\n"); // All done!

}

Additional reference words: 5.10 6.00 6.00a