INF: How to Create Shared Data for a DLL or .EXE

ID Number: Q72419

5.10 6.00 6.00a

OS/2

Summary:

Allocating a shared data segment is often desirable when writing a

dynamic link library (DLL) or .EXE. For instance, there may be state

information that you want to share between all instances of the DLL in

memory, or you may want to share data between two separate instances

of an .EXE.

There are several ways to implement this depending upon your data

requirements. If you want the memory to be allocated dynamically, you

can use OS/2 API calls. To create the memory, use DosAllocShrSeg() for

OS/2 versions 1.x and DosAllocSharedMem() for OS/2 version 2.0. To

access the memory, use DosGetShrSeg() and DosGetSharedMem(),

respectively. With this method, you control the size and availability

of the memory at run time. The drawback to this method is that you

must use pointers to store and retrieve data in the segment.

More Information:

If you are using Microsoft C version 6.0 or later, you can declare a

static variable based on a shared segment. For example

int _based (_segname("MyData")) MyInt;

declares an integer that resides in the segment named "MyData". As

long as you declare that segment to be of class SHARED in the module

definition file (.DEF) for the .EXE or DLL, it will be shared by all

processes that define it.

Finally, if you want to store data in a shared segment without having

to use the OS/2 APIs or the _based operator, you can declare a segment

created by the compiler as shared. First, compile the data you want in

the shared segment with the /ND (name data segment) option followed by

the segment name. Assuming a segment name of "MYDATA," the compiler

will create the following three segments:

Segment name: MYDATA

Contains: Initialized global data

Initialized static data

Strings

Segment name: MYDATA_BSS

Contains: Uninitialized static data

Segment name: MYDATA_CONST

Contains: Read-only and floating-point constants

Segment values for far data

The compiler will also create a group called MYDATA_GROUP, which

contains these three segments. Because they are all in a group, all

three segments must then be declared as SHARED in the .DEF file for

the .EXE or DLL. For this example, the .DEF file must contain the

following:

SEGMENTS

MYDATA class 'FAR_DATA' SHARED

MYDATA_BSS class 'FAR_DATA' SHARED

MYDATA_CONST class 'FAR_DATA' SHARED

The sample code below illustrates this method. There are two DLLs, a

main program, a .DEF file, and a .CMD file for building everything. An

important point to remember is that the module compiled with /ND

cannot have any C run-time calls in it.

Sample Code 1

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

/* DLL1.C

*

* Declare an array of chars that will be residing in shared memory.

* This module must be compiled with /ND and the segment name added

* to the .DEF file as shared for that to happen.

* In addition, the data must be initialized to be placed in the

* default data segment... (named by /ND)

*

* GetDllData merely returns a pointer to the block. No run time

* can be used unless linked with CDLLOBJS.LIB version of run time.

*/

char array[480] = "Some text";

char far * _loadds _export GetDllData(void);

char far * _loadds _export GetDllData(void)

{

return(array);

}

Sample Code 2

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

/* DLL2.C

*

* Provide a way to test that the DLL is actually loaded.

* Print message.

*/

#include <stdio.h>

void _export _loadds DllFunc(void);

void _export _loadds DllFunc(void)

{

printf("Inside DLL Function...\n");

}

Sample Code 3

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

/* MAIN.C

*

* Test that the array resides in a shared memory segment. If there

* are no arguments, initialize the first 240 bytes of the array to

* "PROG1"; if there is one command-line argument, initialize the

* second 240 bytes to "PROG2". Print out the memory over and over

* every second until a key press is detected.

*/

#define INCL_DOSPROCESS

#include <os2.h>

#include <stdio.h>

#include <string.h>

#include <memory.h>

#include <conio.h>

extern char array[];

char far * _loadds _export GetDllData(void);

void _loadds _export DllFunc(void);

void main(int argc, char * argv[]);

void main(int argc, char * argv[])

{

int x;

char far *DllData;

if (argc == 2)

for ( x = 240; x < 480; x+=6)

memcpy( &array[x], "Prog2", 6);

else

if (argc == 1)

for ( x = 0; x < 240; x+=6)

memcpy( &array[x], "Prog1", 6);

DllFunc();

DllData = GetDllData(); // Get a pointer to the shared block

while (!kbhit()) // and print it out till a key is

{ // pressed

printf("Memory: ");

for (x = 0; x < 480; x++)

printf("%c", DllData[x]);

printf("\n");

DosSleep(1000L);

}

}

Sample Code 4

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

; THEDLL.DEF

LIBRARY THEDLL INITINSTANCE

DATA MULTIPLE

SEGMENTS

MYSEG CLASS 'FAR_DATA' SHARED

MYSEG_BSS CLASS 'FAR_DATA' SHARED

MYSEG_CONST CLASS 'FAR_DATA' SHARED

EXPORTS

_DllFunc

_GetDllData

_array

Sample Makefile

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

rem MAKECODE.CMD

@echo off

echo Compile DLL1.C with /ND to declare the shared segment...

cl /Zi /Od /ML /Gs /c /NDMYSEG DLL1.C

echo Compile DLL2.C with standard options...

cl /Zi /Od /ML /Gs /c DLL2.C

echo Link the two together with LLIBCDLL.LIB and THEDLL.DEF...

link /CO /NOD /NOI /MAP DLL2+DLL1,THEDLL.DLL,,LLIBCDLL+OS2,THEDLL.DEF;

echo Since we need to export data, run IMPLIB on the .DEF file...

IMPLIB THEDLL.LIB THEDLL.DEF

echo Compile the module for the .EXE using standard large model...

cl /Od /Zi /AL /Gs /c MAIN.C

echo Link with the DLL's import library.

link /CO /NOD /NOI /MAP MAIN,MAIN.EXE,,LLIBCEP+OS2+THEDLL;

echo Now, start the two processes in different screen groups.

echo The first with no arguments, the second with one...

echo start "Test Program 1" /fs MAIN

echo start "Test Program 2" /fs MAIN second