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 ***************************************