[comp.windows.ms.programmer] About strings in windows

risto@tuura.UUCP (Risto Lankinen) (03/08/91)

poffen@sj.ate.slb.com (Russ Poffenberger) writes:

>In article <1991Mar3.194444.26873@javelin.es.com> lwallace@javelin.es.com (Lynn Wallace) writes:
>>I've found that many of the string functions~ don't work in Windows, ...

>There are windows specific versions of most of those types of functions. ...

Hi!

There's a limited subset of C run-time functions, which don't need any static
variables or initialized data in program's DGROUP .  With just a few except-
ions, the string & memory manipulation functions fall into this category.
There's also a way to make these functions into a 'C run-time DLL' by compil-
ing the following:

(Note: Microsoft C 6.00 & Seg.Exec. Linker 5.10 are essential!)

*** CCALLS.MAK ***

CCALLS.OBJ: CCALLS.C
   CL -c -ASw -Gcsw -Ocegilswz -Zcelp CCALLS.C

CCALLS.DLL: CCALLS.DEF CCALLS.OBJ
   LINK/A:16/NOD/NOE CCALLS LIBENTRY,CCALLS.DLL,NUL,LDLLCAW LIBW,CCALLS;
   RC CCALLS.DLL

CCALLS.LIB: CCALLS.DLL
   IMPLIB CCALLS.LIB CCALLS.DLL

*** CCALLS.C ***

#include <windows.h>

int FAR PASCAL LibMain( HANDLE hInst,HANDLE hDSeg,WORD nHeapSz,LPSTR lpCmdLn )
   { return TRUE; }

BOOL FAR PASCAL WEP( WORD wParam )
   { return TRUE; }

*** CCALLS.H ***

// Declarations for intrinsic versions of C runtime functions:

#pragma  intrinsic( abs,labs )
#pragma  intrinsic( _disable,_enable )
#pragma  intrinsic( inp,inpw,outp,outpw )
#pragma  intrinsic( _lrotl,_lrotr,_rotl,_rotr )
#pragma  intrinsic( memcmp,memcpy,memset )
#pragma  intrinsic( strcat,strcmp,strcpy,strlen,strset )

// Declarations for DLL'ed C run-time functions:

typedef struct { int quot; int rem; } div_t;

int   FAR _cdecl atoi( LPSTR );
long  FAR _cdecl atol( LPSTR );
int   FAR _cdecl bdos( int,WORD,WORD );
LPSTR FAR _cdecl bsearch( LPSTR,LPSTR,WORD,WORD,\
      int(FAR _cdecl *)(LPSTR,LPSTR) );
div_t FAR _cdecl div( int,int );
int   FAR _cdecl getpid( void );
LPSTR FAR _cdecl itoa( int,LPSTR,int );
LPSTR FAR _cdecl lfind( LPSTR,LPSTR,WORD,WORD,\
      int(FAR _cdecl *)(LPSTR,LPSTR) );
LPSTR FAR _cdecl lsearch( LPSTR,LPSTR,WORD,WORD,\
      int(FAR _cdecl *)(LPSTR,LPSTR) );
LPSTR FAR _cdecl ltoa( long,LPSTR,int );
LPSTR FAR _cdecl memccpy( LPSTR,LPSTR,int,WORD );
LPSTR FAR _cdecl memchr( LPSTR,WORD,WORD );
int   FAR _cdecl memicmp( LPSTR,LPSTR,WORD );
LPSTR FAR _cdecl memmove( LPSTR,LPSTR,WORD );
void  FAR _cdecl movedata( WORD,WORD,WORD,WORD,WORD );
LPSTR FAR _cdecl strchr( LPSTR,int );
int   FAR _cdecl strcoll( LPSTR,LPSTR );
WORD  FAR _cdecl strcspn( LPSTR,LPSTR );
int   FAR _cdecl strcmpi( LPSTR,LPSTR );
int   FAR _cdecl stricmp( LPSTR,LPSTR );
LPSTR FAR _cdecl strlwr( LPSTR );
LPSTR FAR _cdecl strncat( LPSTR,LPSTR,WORD );
int   FAR _cdecl strncmp( LPSTR,LPSTR,WORD );
LPSTR FAR _cdecl strncpy( LPSTR,LPSTR,WORD );
int   FAR _cdecl strnicmp( LPSTR,LPSTR,WORD );
LPSTR FAR _cdecl strnset( LPSTR,int,WORD );
LPSTR FAR _cdecl strpbrk( LPSTR,LPSTR );
LPSTR FAR _cdecl strrchr( LPSTR,int );
LPSTR FAR _cdecl strrev( LPSTR );
WORD  FAR _cdecl strspn( LPSTR,LPSTR );
LPSTR FAR _cdecl strstr( LPSTR LPSTR );
LPSTR FAR _cdecl strupr( LPSTR );
WORD  FAR _cdecl strxfrm( LPSTR,LPSTR,WORD );
void  FAR _cdecl swab( LPSTR,LPSTR,int );
LPSTR FAR _cdecl ultoa( DWORD,LPSTR,int );
LPSTR FAR _cdecl _strdate( LPSTR );
LPSTR FAR _cdecl _strtime( LPSTR );

*** CCALLS.DEF ***

LIBRARY     CCALLS

DESCRIPTION 'Partial C run-time function DLL for Windows.'

EXETYPE     WINDOWS

CODE        PRELOAD MOVEABLE DISCARDABLE SHARED EXECUTEREAD
DATA        PRELOAD MOVEABLE SINGLE READWRITE

EXPORTS     WEP                     @1 RESIDENTNAME

            _atoi                   @10 NODATA
            _atol                   @11 NODATA
            _bdos                   @12 NODATA
            _bsearch                @13 NODATA
            _div                    @14 NODATA
            _getpid                 @15 NODATA
            _itoa                   @16 NODATA
            _lfind                  @17 NODATA
            _lsearch                @18 NODATA
            _ltoa                   @19 NODATA
            _memccpy                @20 NODATA
            _memchr                 @21 NODATA
            _memicmp                @22 NODATA
            _memmove                @23 NODATA
            _movedata               @24 NODATA
            _strchr                 @25 NODATA
            _strcoll                @26 NODATA
            _strcspn                @27 NODATA
            _strcmpi                @28 NODATA
            _stricmp                @29 NODATA
            _strlwr                 @30 NODATA
            _strncat                @31 NODATA
            _strncmp                @32 NODATA
            _strncpy                @33 NODATA
            _strnicmp               @34 NODATA
            _strnset                @35 NODATA
            _strpbrk                @36 NODATA
            _strrchr                @37 NODATA
            _strrev                 @38 NODATA
            _strspn                 @39 NODATA
            _strstr                 @40 NODATA
            _strupr                 @41 NODATA
            _strxfrm                @42 NODATA
            _swab                   @43 NODATA
            _ultoa                  @44 NODATA
            __strdate               @45 NODATA
            __strtime               @46 NODATA

*** End of CCALLS files ***

When you run the 'MAKE CCALLS.MAK', you'll get two files: CCALLS.DLL and
CCALLS.LIB .  Subsequently, you will use '#include <ccalls.h>' in your
application program and at least 'CL -Oi' with the C-compiler (to enable
the intrinsic functions).  Finally, at the linking phase, you *MUST* use
libraries 'xNOCRT LIBW CCALLS' (optionally followed with 'xLIBCyW', if
your program makes use of any of the '*','/' or '%' with at least one
DWORD operand).  In any other sense, you should be able to use the calls
as you would from any ordinary C program (making sure the CCALLS.DLL is
located in a dictionary pointed to by the 'PATH=').

Failing to use 'xNOCRT' (and using 'xLIBCyW') may make the additionally
used C run-time functions refer to the FAR calls in DLL, when they actually
expect to see a memory-model dependend version, resulting an UAE.

*** EXAMPLE.C ***

#include <windows.h>
#define  _WINDOWS
#include <ccalls.h>
   ...
   char strBuffer[256]; int iNumber;

   // Simulate nonexistent 'WriteProfileInt()':
   WriteProfileString( ... ,itoa(iNumber,strBuffer,10), ... );

*** EXAMPLE.MAK ***
   ...
   CL -c -AS -Gsw -Ois -Zp ...
   LINK/A:16/NOD/NOE, ... ,SNOCRT LIBW CCALLS, ...

*** End of example ***

A few words of explanation may be justified:

As told above, these functions don't access their (nonexistent) 'default'
data segment.  Instead, they only use the data areas pointed to by their
arguments, or use their arguments as values.  In either case, they only
need to access the stack.

The DLLs in Windows are using the caller's stack, but their own data seg.
Now, if a DLL function doesn't use any static data, it can be defined with
'NODATA' attribute.  To Windows, this means that there's no need to protect
any data segment from memory movement, and therefore there's no need for
the Windows' entry & exit code in procedures (there's no protection for code
segment, either, and therefore such a function must not call other Windows'
functions itself - but C run-time functions fortunately don't).

See also, that the DLL entry routine CCALLS.C is compiled with -ASw, but
it is linked to LLIBCyW (large model).  This, together with the proper
declarations in CCALLS.H, provide the run-time functions an access to
the segment where the actual argument(s) is (are) in.  Otherwise, they'd
use whatever segment the DS points to at the time of call, which would
work often, but not nearly always (moving to GlobalAlloc()ed memory, for
instance).  Another reason to use the large model library is, that Windows
requires any DLL function being called as FAR .  Their _cdecl nature does
not change, however, which is taken into account in the declarations.

I've gone thru all the functions that don't use their default DGROUP, but
some of the functions *may* require a special initialization call, which
this DLL doesn't execute.  In particular, I think it would be safest not
to use the function _bdos() even though it's been included.  Also, the
_getpid() makes use of Windows' GetCurrentTask(), which is a bit risky,
because the control is at Windows, but the DLL code segment has not been
protected against moving.

One more word of recommendation:  A number of the functions have a Windows
counterpart, so basically you should use them.  However, the strcpy() for
instance has an even more optimal form of an intrinsic function, which you
may prefer instead.  The intrinsic functions can even be used by themselves
without accessing the CCALLS.DLL at all (ie. using only the #pragma:s of the
definition file - the C compiler 'knows' their declarations internally).

***
Disclaimer:  Problems arising from actually making use of the information
in this article is assumed to be entirely at the user's own risk.
***

Terveisin: Risto Lankinen
-- 
Risto Lankinen / product specialist ***************************************
Nokia Data Systems, Technology Dept *  2                              2   *
THIS SPACE INTENTIONALLY LEFT BLANK * 2 -1 is PRIME!  Now working on 2 +1 *
replies: risto@yj.data.nokia.fi     ***************************************