IT-Consultant: Frederick J. Harris > Discussion

TCLib Now Uses Msvcrt.dll

(1/6) > >>

Frederick J. Harris:
Big Doins in TCLib World!

     As a result of an exchange I had in a C++ forum I decided to download Mark Russinovich’s Sysinternals “Process Explorer” utility…

www.sysinternals.com

…to examine the dlls loaded into various Windows’ processes.  I had been laboring under the assumption that my use of TCLib in linking was removing the necessity of the loading of msvcrt.dll into my processes.  That’s actually partly true.  Let me explain. 

     In the seminal work on this topic of eliminating the C Runtime library to minimize program size by Matt Pietrek in the January 2001 issue of “Microsoft Systems Journal” he states this…


--- Quote ---What About the C++ Runtime Library DLL?

      Alert readers might say, "Hey Matt! Why don't you just use the DLL version of the runtime library?" In the past, I could make the argument that there was no consistently named version of the C++ runtime library DLL available on Windows® 95, Windows 98, Windows NT 3.51, Windows NT® 4.0, and so forth. Luckily, we've moved past those days, and in most cases you can rely on MSVCRT.DLL being available on your target machines.

      Making this switch and recompiling Hello.CPP, the resulting executable is now only 16KB. Not bad, but you can do better. More importantly, you're just shifting all of this unneeded code to someplace else (that is, to MSVCRT.DLL). In addition, when your program starts up, another DLL will have to be loaded and initialized. This initialization includes items like locale support, which you may not care about. If MSVCRT.DLL suits your needs, then by all means use it. However, I believe that using a stripped-down, statically linked runtime library still has merit.

      I may be tilting at windmills here, but my e-mail conversations with readers show that I'm not alone. There are people out there who want the leanest possible code. In this day of writeable CDs, DVDs, and fast Internet connections, it's easy not to worry about code size. However, the best Internet connection I can get at home is only 24Kbps. I hate wasting time downloading bloated controls for a Web page.

      As a matter of principle, I want my code to have as small a footprint as possible. I don't want to load any extra DLLs that I don't really need. Even if I might need a DLL, I'll try to delayload it so that I don't incur the cost of loading it until I use the DLL. Delayloading is a topic I've described in previous columns, and I strongly encourage you to become familiar with it. See Under the Hood in the December 1998 issue of MSJ for starters.

--- End quote ---

     So perhaps I can be partly excused for my lack of knowledge.  It was my reading of the above that caused me to assume that msvcrt.dll wasn’t being loaded if I used Matt’s techniques.  Which as a matter of fact is absolutely true for console processes.  And that’s all Matt Pietrek was really writing about, i.e., the use of such techniques as his LibCTiny.lib in small utility type programs.  It was I that came up with the idea that these techniques could be used to minimize the size of ALL C++ programs – including GUIs.  For the situation is simply this – msvcrt.dll seems to be loaded into every GUI process – even the very simplest ones that seemingly make no calls on stdio.h or string.h functions such as this very simplest one…


--- Code: ---// cl Form1.cpp /O1 /Os /GS- TCLib.lib kernel32.lib user32.lib
// cl Form1.cpp /O1 /Os /GS- /link kernel32.lib user32.lib gdi32.lib
#define UNICODE        //  3,584 Bytes x64 UNICODE TCLib.lib
#define _UNICODE       // 84,992 Bytes With C Standard Library Loaded (LIBCMT.LIB)
#include <windows.h>
#include "tchar.h"

LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 if(msg==WM_DESTROY)
 {
    PostQuitMessage(0);
    return 0;
 }

 return (DefWindowProc(hwnd, msg, wParam, lParam));
}

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevIns, LPTSTR lpszArgument, int iShow)
{
 WNDCLASSEX wc={0};
 MSG messages;
 HWND hWnd;

 wc.lpszClassName = _T("Form1");
 wc.lpfnWndProc   = fnWndProc;
 wc.cbSize        = sizeof(WNDCLASSEX);
 wc.hInstance     = hInstance;
 wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,_T("Form1"),_T("Form1"),WS_OVERLAPPEDWINDOW|WS_VISIBLE,200,100,325,300,HWND_DESKTOP,0,hInstance,0);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;
}

--- End code ---


     When compiled with TCLib.lib these are the dlls Mark Russinovich’s “Process Explorer” tells me are associated with it within its process….

Bcrypt.dll
Bcryptprimitives.dll
Clbcatq.dll
Combase.dll
Dwmapi.dll
Gdi32.dll
Imm32.dll
Kernel.appcore.dll
Kernel32.dll
KernelBase.dll
Locale.nls
Msctf.dll
Msvcrt.dll                               <<<<< !!!
Ntdll.dll
Ole32.dll
Oleacc.dll
Oleaccrc.dll
Oleaut32.dll
Profapi.dll
Rpcrt4.dll
Sechose.dll
SHCore.dll
SortDefault.nls
Sxs.dll
Tiptsf.dll
Twinapi.appcore.dll
Twinapi.dll
UIAutomationCore.dll
User32.dll
Userenv.dll
Uxtheme.dll

     Quite a list, isn’t it?  And among those listed is good ‘ole msvcrt.dll!  For comparison, here are the “Process Explorer” results for my Demo1.cpp which is just the canonical  Dennis Ritchie “Hello, World!” program compiled with the last version of TCLib.lib I presented here.  First the program…


--- Code: ---// cl Demo1.cpp /O1 /Os /GS- TCLib.lib kernel32.lib
// 2,048 bytes x86 ASCII, 2,048 bytes x86 UNICODE VC15
// 3,072 bytes x64 ASCII, 3,072 bytes x64 UNICODE VC19
#define  UNICODE
#define  _UNICODE
#include <windows.h>
#include "stdio.h"
#include "tchar.h"

int _tmain()
{
 _tprintf(_T("Hello, World!\n"));
 getchar();

 return 0;
}

--- End code ---
     

And here are the “Process Explorer” results…

Gdi32.dll
Imm32.dll
Kernel32.dll
KernelBase.dll
Localew.nls
Ntdll.dll
User32.dll

     Certainly a lot smaller!  Notably absent from the above list is msvcrt.dll.  So indeed, for console processes my use of TCLib.lib does greatly minimize program executable size while at the same time eliminating a large dll from being loaded into the process.  But for GUI apps msvcrt.dll seems to always be loaded.  At least for every program I’ve examined and certainly for all those linked with my TCLib.lib.  And by the way, I checked a PowerBASIC Console Compiler “Hello, World!” Console program and msvcrt.dll is being loaded into that, as well as a lot of other stuff more nearly like a GUI program.

     For me, this changes everything.  It’s a game changer.  I’ve been struggling for months on end trying to come up with ways of duplicating functionality I needed from the C Runtime without its being loaded and here its been sitting in memory all the time just begging to be used!

     Its kind of like not wanting to get wet during a rainstorm so you go out and buy an umbrella and dutifully carry it around with you everywhere you go, then when it starts to finally rain you fail to open it and still get drenched even though you have the umbrella in your hand!  What’s the sense?  If you’ve got the umbrella in your hand and you don’t want to get wet why not open it?  If msvcrt.dll is default loaded into every GUI process and there isn’t anything you can do about it then why not use it rather than struggling to write code that’s already loaded into your process?

     By the way, and as something of an interesting aside, in every version of the PowerBASIC compilers I bought from PB Dll 6 and Console Compiler 2 circa 1998 or so onwards up to version 9 of PB Win Bob Zale included msvcrt.dll in the \bin subdirectory of his installations alongside his compilers themselves and other files in his compiler toolchain.  I do believe it was lacking in PB Win 10 and PB CC 6.  By that time though msvcrt.dll was an official Windows installation file accorded ‘system protection’ status.  So I believe it may be something of an open question as to how much of the PowerBASIC Compiler’s features are dependent on the C Runtime.  Interesting question, I believe.   

continued...

Frederick J. Harris:
     Way back in my first postings on this topic when I was still struggling with floating point issues and before I developed my FltToCh() function I dynamically loaded msvcrt.dll and obtained function pointers to several of the printf functions I needed.  At that point I stated that I was cheating by doing that, and that I would endeavor to come up with a workaround, which I eventually did (my FltToCh() function).  But now I’m thinking, “What’s the point?”  Msvcrt.dll is loaded in any case, whether I load it or not.  What actually happened when I did a LoadLibrary() on msvcrt.dll in my early work is that the reference count on an already loaded dll was incremented.  The dll was already loaded.  Check out the docs on LoadLibrary() if you have any doubt about what I’m saying.  LoadLibrary() only loads a library if it is not already loaded.  Otherwise Windows simply increments the reference count on the already loaded object.

     To bring home to you my reader the significance of all this let me review and bring you up to speed on the issues.  Take for instance this simple C program to output the string …

“My Name Is Fred And I Weight 214.5 Pounds And Am 5 Feet 11 Inches Tall!”


--- Code: ---// cl Test1a.cpp
// 122,880 bytes
#include <stdio.h>

int main()
{
 double dblWeight = 214.5;
 int    iFeet     = 5;
 int    iInches   = 11;
 char   szName[]  = "Fred";
 char   szBuffer[128];
 
 sprintf(szBuffer,"My Name Is %s And I Weight %5.1f Pounds And Am %d Feet %d Inches Tall!",szName,dblWeight,iFeet,iInches);
 printf("%s\n",szBuffer);
 getchar();
 
 return 0;
}

--- End code ---

     The above code using the C Runtime will produce the desired result.  Note I did it a little circuitously by using sprintf and printf instead of just printf.  Sprintf writes the results to a specified buffer first rather than directly to the console.  But note further there are %s format specifiers for character strings, %d format specifiers for integral class numbers and %f format specifiers for ‘real’ numbers, i.e., floating points. 

     The above wouldn’t work with my TCLib.lib without modifications.  The reason it wouldn’t work is because I used Matt Pietrek’s original sprintf/printf implementation which passed on all the processing to user32.dll’s wvsprintf() which correctly interprets all format specifiers except those for floating point numbers.  My solution to that issue for TCLib, as you may recall, was to create a FltToCh() function which converted floating point numbers to either asci or wide character strings.  That function had a good number or parameters, but basically, one passed into it a buffer where the results were to be written, and the floating point number one wished to convert.  Then, once having converted the float to a string, one could output it using Matt’s sprintf/printf code because now one had a variable type, i.e., %s, which could be processed by his ‘simplified’ but crippled function.  Here would be my TCLib interpretation of that…


--- Code: ---// cl Test1b.cpp /O1 /Os /GS- TCLib.lib kernel32.lib
// 5,120 bytes
#include <windows.h>
#include "stdio.h"
extern "C" int _fltused = 1;

int main()
{
 double dblWeight = 214.5;
 int    iFeet     = 5;
 int    iInches   = 11;
 char   szName[]  = "Fred";
 char   szBuffer[128];
 char   szWeight[16];
 
 FltToCh(szWeight,dblWeight,6,1,'.',true);
 sprintf(szBuffer,"My Name Is %s And I Weight %s Pounds And Am %d Feet %d Inches Tall!",szName,szWeight,iFeet,iInches);
 printf("%s\n",szBuffer);
 getchar();
 
 return 0;
}

--- End code ---

     Please examine the code carefully to see the differences.  In Test1b I needed to create another buffer which I named szWeight to contain the converted result of dblWeight converted to a string.  That buffer was used in my FltToCh() function call.  Of course, in Test1a none of that was needed.  And note the sprintf() call now has a %s format specifier for a string rather than the %f for the double.  Now, if you are working on a real program that has no floating points or only a few, then the above protocol might not be overly arduous to employ considering the code size reductions my TCLib.lib makes for you.  But in my own work I ran into a major program of mine with not one or a few floating point output statements, but rather thousands and thousands of them!  I worked on it for three or four days converting them, and only got about half way through them all when I threw up my hands in despair and realized that this was a ‘no go’.  It was time for Plan B.

     It would have been ridiculous to proceed.  The whole purpose of TCLib.lib was to minimize executable program size.  And due to the difficult situation with floating point outputs I was having to add thousands of extra lines of code to a program which I had hoped to make smaller by the use of my TCLib.lib!

     I had looked at Matt Pietrek's implementation of the printf family of functions which lacked floating point support many times with an eye to how they could be modified to enable floating point support just like the real printf functions in the C Runtime.  And I always knew I could do it.  But I also knew it would be a truly nasty programming job requiring a lot of code and many days of work.  I had studied the CRT sources and knew what was involved.  Even Microsoft hated to tackle it as evidenced by the article I gave a reference to much earlier something along the lines of  "The Great C Runtime Refactoring".  So every time I looked at it and thought about it I decided to just live with what I had.  But now I knew I had to do it, and I did.  It took about a week.  And right about when I had a working prototype - not yet refactored though, I came upon my realization or discovery or enlightenment that msvcrt.dll was loaded into every GUI Windows process!  So another wasted week I guess, because as soon as I realized that I immediately knew what I would do.  But before getting into that, let me present my working prototype of an sprintf implementation which would finally allow me to escape the nastiness of Matt Pietrek's partial and crippled implementation.  The following program uses my Parse function to parse the format string based on the '%' character which is a field delimiter.  The program then loops through each delimited string and chooses what to do based on whether a string - s, 64 bit unsigned - Iu, 64 bit signed - Id, 32 bit unsigned - u, 32 bit signed - d, floating point - f, or char - c is encountered.  The 'choosing what to do part' affects how the vararg parameters are removed from the stack.  So yea, its involved.  Here's the above program using my implementation of the format specifiers.  Its hundreds of lines of code, and I'm more grateful than I can tell that I don't have to use it now, but I just wanted you all to get an idea of the complexity of the issues.  So if you aren’t interested in this code in any way but want to get on to my new TCLib.lib, then just skim over this...


--- Code: ---// cl Test9.cpp /O1 /Os /GS- TCLib.lib kernel32.lib
#include <windows.h>
#include "string.h"
#include "stdio.h"
#include "stdlib.h"
#include <stdarg.h>
typedef SSIZE_T ssize_t;
extern "C" int _fltused=1;


struct FmtSpecs
{
 BOOL       blnRightJustified;
 int        iDummy;
 short int  iFmtSpecPos;
 short int  iFieldWidth;         
 short int  iType;               
 short int  iDecimalPoints;
};

// FmtSpecs::iType
//=================
// 1 = int;
// 2 = __int64;
// 3 = unsigned int;
// 4 = unsigned __int64;
// 5 = double;
// 6 = string
// 7 = char


size_t ParseCount(const char* pString, char cDelimiter)
{
 int iCtr=0;                        //reflects # of strings delimited
 const char* p;                     //by delimiter.

 p=pString;
 while(*p)
 {
  if(*p==cDelimiter)
     iCtr++;
  p++;
 }

 return ++iCtr;
}


void Parse(char** pStrings, const char* pDelimitedString, char cDelimiter, size_t iParseCount)
{
 char* pBuffer=(char*)malloc(strlen(pDelimitedString)+1);
 if(pBuffer)
 {
    const char* c=pDelimitedString;
    char* p=pBuffer;
    while(*c)
    {
       if(*c==cDelimiter)
          *p=0;
       else
          *p=*c;
       p++, c++;
    }
    *p=0, p=pBuffer;
    for(size_t i=0; i<iParseCount; i++)
    {
        pStrings[i]=(char*)malloc(strlen(p)+1);
        strcpy(pStrings[i],p);
        p=p+strlen(pStrings[i])+1;
    }
 }
}


int iGetFieldWidth(char* pChars, size_t iFound, BOOL blnRhtJus)
{
 char szBuffer[4];
 
 if(iFound==0)
    return 0;
 if(iFound==1 && blnRhtJus==FALSE)
    return 0; 
 memset(szBuffer,0,4); 
 if(blnRhtJus)
 {
    switch(iFound)
    {
      case 1:
        szBuffer[0]=pChars[0];
        szBuffer[1]=0;
        break;
      case 2:
        szBuffer[0]=pChars[0];
        szBuffer[1]=pChars[1];
        szBuffer[2]=0;
        break;
    }
 }
 else
 {
    switch(iFound)
    {
      case 2:
        szBuffer[0]=pChars[1];
        szBuffer[1]=0;
        break;
      case 3:
        szBuffer[0]=pChars[1];
        szBuffer[1]=pChars[2];
        szBuffer[2]=0;
        break;
    }
 }
 
 return atoi(szBuffer);
}


bool blnFindFormatSpec(char* pFmtSpec, FmtSpecs& fs)
{
 if(*pFmtSpec=='-')
    fs.blnRightJustified=FALSE;
 else
    fs.blnRightJustified=TRUE; 
 for(size_t i=0; i<7; i++)
 {
     if(strncmp(pFmtSpec+i,"u",1)==0)
     {
        fs.iType=3;
        fs.iFieldWidth=iGetFieldWidth(pFmtSpec,i,fs.blnRightJustified);
        fs.iFmtSpecPos=i;
        return true;
     }
     if(strncmp(pFmtSpec+i,"d",1)==0)
     {
        fs.iType=1;
        fs.iFieldWidth=iGetFieldWidth(pFmtSpec,i,fs.blnRightJustified);
        fs.iFmtSpecPos=i;
        return true;
     }
     if(strncmp(pFmtSpec+i,"Iu",2)==0)
     {
        fs.iType=4;
        fs.iFieldWidth=iGetFieldWidth(pFmtSpec,i,fs.blnRightJustified);
        fs.iFmtSpecPos=i+1;
        return true;
     }
     if(strncmp(pFmtSpec+i,"Id",2)==0)
     {
        fs.iType=2;
        fs.iFieldWidth=iGetFieldWidth(pFmtSpec,i,fs.blnRightJustified);
        fs.iFmtSpecPos=i+1;
        return true;
     }
     if(strncmp(pFmtSpec+i,".",1)==0)
     {
        char szBuffer[4];
        memset(szBuffer,0,4);
        fs.iType=5;
        fs.iFieldWidth=iGetFieldWidth(pFmtSpec,i,fs.blnRightJustified);
        char* pChar;
        if(strncmp(pFmtSpec+i+2,"f",1)==0)  // it'll only be one digit then
        {
           pChar=pFmtSpec+i+1;
           szBuffer[0]=*pChar;
           szBuffer[1]=0;
           fs.iDecimalPoints=atoi(szBuffer);
           fs.iFmtSpecPos=i+2;
        }
        if(strncmp(pFmtSpec+i+3,"f",1)==0)  // it'll only be one digit then
        {
           pChar=pFmtSpec+i+1;
           szBuffer[0]=*pChar;
           szBuffer[1]=*(pChar+1);
           szBuffer[2]=0;
           fs.iDecimalPoints=atoi(szBuffer);
           fs.iFmtSpecPos=i+3;
        }
        return true;
     }
     if(strncmp(pFmtSpec+i,"f",1)==0)
     {
        fs.iType=5;
        fs.iFieldWidth=0;
        fs.iFmtSpecPos=i;
        return true;
       
     }
     if(strncmp(pFmtSpec+i,"s",1)==0)
     {
        fs.iType=6;
        fs.iFieldWidth=iGetFieldWidth(pFmtSpec,i,fs.blnRightJustified);
        fs.iFmtSpecPos=i;
        return true;
     }
     if(strncmp(pFmtSpec+i,"c",1)==0)
     {
        fs.iType=7;
        fs.iFieldWidth=iGetFieldWidth(pFmtSpec,i,fs.blnRightJustified);
        fs.iFmtSpecPos=i;
        return true;
     }
 }
 
 return false;
}




// FmtSpecs::iType
//=================
// 1 = int;
// 2 = __int64;
// 3 = unsigned int;
// 4 = unsigned __int64;
// 5 = double;
// 6 = string
// 7 = char


int MySPrintF(char* pBuffer, const char* pFmtStr, ...)
{
 size_t iParseCount=0,iLen=0,iStrLen=0;
 char** pFmtSpecs=NULL;
 char szBuffer[80];
 char*  pCh=NULL;
 char szTmp1[80];
 va_list args;
 FmtSpecs fs;
 
 memset(&fs,0,sizeof(fs));
 iParseCount=ParseCount(pFmtStr,'%');
 pFmtSpecs=(char**)malloc(iParseCount*sizeof(char*));
 if(pFmtSpecs)
 {
    Parse(pFmtSpecs,pFmtStr,'%',iParseCount);
    va_start(args, pFmtStr);
    for(size_t i=0; i<iParseCount; i++)
    {
        iLen=strlen(pFmtSpecs[i]);
        char* pStr=pFmtSpecs[i];
        if(i==0)
        {
           if(iLen)
              strcpy(pBuffer,pFmtSpecs[i]);
        }
        else
        {
           memset(&fs,0,sizeof(fs));
           blnFindFormatSpec(pFmtSpecs[i],fs);
           switch(fs.iType)
           {
              case 1: // %d -- 32 bit int
              {
                 itoa(va_arg(args,int),szTmp1,10);
                 iStrLen=strlen(szTmp1);
                 if(fs.iFieldWidth==0)
                 {
                    strcat(pBuffer,szTmp1);  // worked for iFieldWidth=0
                    pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                    strcat(pBuffer,pStr);
                 }                 
                 else
                 {
                    SSIZE_T iNeeded=fs.iFieldWidth-iStrLen;
                    if(fs.blnRightJustified)
                    {
                       memset(szBuffer,' ',iNeeded);
                       szBuffer[iNeeded]=0;
                       strcat(szBuffer,szTmp1);
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);
                    }
                    else
                    {
                       strcpy(szBuffer,szTmp1);
                       for(size_t j=iStrLen; j<fs.iFieldWidth; j++)
                           szBuffer[j]=' ';
                       szBuffer[fs.iFieldWidth]=0;
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);                       
                    }                   
                 }
                 break;
              }
              case 2: // %Id -- 64 bit int
              {
                 _i64toa(va_arg(args,ssize_t),szTmp1,10);
                 iStrLen=strlen(szTmp1);
                 if(fs.iFieldWidth==0)
                 {
                    strcat(pBuffer,szTmp1);  // worked for iFieldWidth=0
                    pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                    strcat(pBuffer,pStr);
                 }
                 else
                 {
                    SSIZE_T iNeeded=fs.iFieldWidth-iStrLen;
                    if(fs.blnRightJustified)
                    {
                       memset(szBuffer,' ',iNeeded);
                       szBuffer[iNeeded]=0;
                       strcat(szBuffer,szTmp1);
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);
                    }
                    else
                    {
                       strcpy(szBuffer,szTmp1);
                       for(size_t j=iStrLen; j<fs.iFieldWidth; j++)
                           szBuffer[j]=' ';
                       szBuffer[fs.iFieldWidth]=0;
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);                       
                    }                   
                 }                 
                 break;
              }
              case 3: // %u  -- 32 bit unsigned int
              {
                 itoa(va_arg(args,unsigned int),szTmp1,10);
                 iStrLen=strlen(szTmp1);
                 if(fs.iFieldWidth==0)
                 {
                    strcat(pBuffer,szTmp1);  // worked for iFieldWidth=0
                    pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                    strcat(pBuffer,pStr);
                 }                 
                 else
                 {
                    SSIZE_T iNeeded=fs.iFieldWidth-iStrLen;
                    if(fs.blnRightJustified)
                    {
                       memset(szBuffer,' ',iNeeded);
                       szBuffer[iNeeded]=0;
                       strcat(szBuffer,szTmp1);
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);
                    }
                    else
                    {
                       strcpy(szBuffer,szTmp1);
                       for(size_t j=iStrLen; j<fs.iFieldWidth; j++)
                           szBuffer[j]=' ';
                       szBuffer[fs.iFieldWidth]=0;
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);                       
                    }                   
                 }
                 break;
              }
              case 4: // %Iu  -- 64 bit unsigned int
              {
                 _ui64toa(va_arg(args,size_t),szTmp1,10);
                 iStrLen=strlen(szTmp1);
                 if(fs.iFieldWidth==0)
                 {
                    strcat(pBuffer,szTmp1);  // worked for iFieldWidth=0
                    pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                    strcat(pBuffer,pStr);
                 }
                 else
                 {
                    SSIZE_T iNeeded=fs.iFieldWidth-iStrLen;
                    if(fs.blnRightJustified)
                    {
                       memset(szBuffer,' ',iNeeded);
                       szBuffer[iNeeded]=0;
                       strcat(szBuffer,szTmp1);
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);
                    }
                    else
                    {
                       strcpy(szBuffer,szTmp1);
                       for(size_t j=iStrLen; j<fs.iFieldWidth; j++)
                           szBuffer[j]=' ';
                       szBuffer[fs.iFieldWidth]=0;
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);                       
                    }                   
                 }
                 break;
              }             
              case 5:
              {
                 if(fs.iFieldWidth==0)
                 {
                    FltToCh(szTmp1,va_arg(args,double),20,6,'.',false);
                    iStrLen=strlen(szTmp1);
                    for(size_t j=0; j<iStrLen; j++)
                    {
                        if(szTmp1[j]==' ')
                        {
                           szBuffer[j]=0;       
                           break;
                        }
                        else
                           szBuffer[j]=szTmp1[j];                     
                    }
                    strcat(pBuffer,szBuffer);
                    pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                    strcat(pBuffer,pStr);
                 }
                 else
                 {
                    if(fs.blnRightJustified==TRUE)
                       FltToCh(szTmp1,va_arg(args,double),fs.iFieldWidth+1,fs.iDecimalPoints,'.',true);
                    else
                       FltToCh(szTmp1,va_arg(args,double),fs.iFieldWidth+1,fs.iDecimalPoints,'.',false);
                    strcat(pBuffer,szTmp1);
                    pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                    strcat(pBuffer,pStr);
                 }               
                 break;
              }
              case 6:   
              {
                 strcpy(szTmp1,va_arg(args,char*));
                 iStrLen=strlen(szTmp1);
                 if(fs.iFieldWidth==0)
                 {
                    strcat(pBuffer,szTmp1);
                    pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                    strcat(pBuffer,pStr);
                 }
                 else
                 {
                    SSIZE_T iNeeded=fs.iFieldWidth-iStrLen;
                    if(fs.blnRightJustified)
                    {
                       memset(szBuffer,' ',iNeeded);
                       szBuffer[iNeeded]=0;
                       strcat(szBuffer,szTmp1);
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);
                    }
                    else
                    {                     
                       strcpy(szBuffer,szTmp1);
                       for(size_t j=iStrLen; j<fs.iFieldWidth; j++)
                           szBuffer[j]=' ';
                       szBuffer[fs.iFieldWidth]=0;
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);           
                    }
                 }               
                 break;
              }
              case 7:
              {
                 char c=va_arg(args,char);
                 char* pChar=&c;
                 strcpy(szTmp1,pChar);
                 iStrLen=strlen(szTmp1);
                 if(fs.iFieldWidth==0)
                 {
                    strcat(pBuffer,szTmp1);
                    pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                    strcat(pBuffer,pStr);
                 }
                 else
                 {
                    SSIZE_T iNeeded=fs.iFieldWidth-iStrLen;
                    if(fs.blnRightJustified)
                    {
                       memset(szBuffer,' ',iNeeded);
                       szBuffer[iNeeded]=0;
                       strcat(szBuffer,szTmp1);
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);
                    }
                    else
                    {                     
                       strcpy(szBuffer,szTmp1);
                       for(size_t j=iStrLen; j<fs.iFieldWidth; j++)
                           szBuffer[j]=' ';
                       szBuffer[fs.iFieldWidth]=0;
                       strcat(pBuffer,szBuffer);
                       pStr=pFmtSpecs[i]+fs.iFmtSpecPos+1;
                       strcat(pBuffer,pStr);           
                    }
                 }         
                 break;
              }             
           }  // switch(fs.iType)
        }  // end if(i==0)       
    }
    for(size_t i=0; i<iParseCount; i++)
        free(pFmtSpecs[i]);   
    free(pFmtSpecs);
 }
 
 return 0;
}


int main()
{
 int    iFeet     = 5;
 int    iInches   = 11;
 double dblWeight = 214.54321;
 char   szName[]  = "Fred";
 char   szBuffer[128];
 
 MySPrintF(szBuffer, "My Name Is %s And I Weight %5.1f Pounds And Am %d Feet %d Inches Tall!\n", szName, dblWeight, iFeet, iInches);
 printf("%s",szBuffer);
 getchar();
 
 return 0;
}

// My Name Is Fred And I Weight 214.5 Pounds And Am 5 Feet 11 Inches Tall!

--- End code ---

continued...

Frederick J. Harris:
    I'd like to be able to tell you the above program could be built with all the code I've already provided in previous posts on the topic, but the sad fact is I had to add several more C Runtime functions to what I already had, namely these...


--- Code: ---extern "C" char*    __cdecl itoa    (int             iNumber, char*      pszBuffer, int radix);
extern "C" wchar_t* __cdecl _itow   (int             iNumber, wchar_t*   pszBuffer, int radix);
extern "C" char*    __cdecl _i64toa (_int64          iNumber, char*      pszBuffer, int radix);
extern "C" wchar_t* __cdecl _i64tow (_int64          iNumber, wchar_t*   pszBuffer, int radix);
extern "C" char*    __cdecl _ui64toa(unsigned _int64 iNumber, char*      pszBuffer, int radix);
extern "C" wchar_t* __cdecl _ui64tow(unsigned _int64 iNumber, wchar_t*   pszBuffer, int radix);

--- End code ---

     Those additions were necessary to convert %u, %d, %Iu and %Id numbers to asci/wide buffers, and I implemented them in terms of my FltToCh() function, which as you can see, became the cornerstone of a lot of my work.  Here they are if you want them...


--- Code: ---//==============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                             By Fred Harris, June 2016
//
//       cl itoa.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//==============================================================================================
#include "stdlib.h"
#include "stdio.h"

void SetNullChar(char* p)
{
 while(true)
 {
   if(*p==' ')
   {
      *p=0;
      break;
   }
   p++;
 }
}

void SetNullWChar(wchar_t* p)
{
 while(true)
 {
   if(*p==L' ')
   {
      *p=0;
      break;
   }
   p++;
 }
}

char* itoa(int iNumber, char* pszBuffer, int radix)
{
 FltToCh(pszBuffer,(double)iNumber,12,0,'.',false);
 SetNullChar(pszBuffer);
 return pszBuffer;
}

wchar_t* _itow(int iNumber, wchar_t* pszBuffer, int radix)
{
 FltToWch(pszBuffer,(double)iNumber,12,0,L'.',false);
 SetNullWChar(pszBuffer);
 return pszBuffer;
}

char* _i64toa(_int64 iNumber, char* pszBuffer, int radix)
{
 FltToCh(pszBuffer,(double)iNumber,20,0,'.',false);
 SetNullChar(pszBuffer);
 return pszBuffer;
}

char* _ui64toa(unsigned _int64 iNumber, char* pszBuffer, int radix)
{
 FltToCh(pszBuffer,(double)iNumber,20,0,'.',false);
 SetNullChar(pszBuffer);
 return pszBuffer;
}

wchar_t* _i64tow(_int64 iNumber, wchar_t* pszBuffer, int radix)
{
 FltToWch(pszBuffer,(double)iNumber,20,0,L'.',false);
 SetNullWChar(pszBuffer);
 return pszBuffer;
}

wchar_t* _ui64tow(unsigned _int64 iNumber, wchar_t* pszBuffer, int radix)
{
 FltToWch(pszBuffer,(double)iNumber,20,0,L'.',false);
 SetNullWChar(pszBuffer);
 return pszBuffer;
}

--- End code ---

     With that you actually should be able to build that.  As I said I finally got that working right before my massive illumination on msvcrt.dll being loaded into every GUI process, so its not really done.  There’s a lot of redundant code that could likely be refactored and so forth.  But I doubt I’ll ever make any further use or refinement of it, as I now intend to use msvcrt.dll for whatever I need.

     So the conclusion I’ve come to is this.  I might as well just do a LoadLibrary() on msvcrt.dll to get the HINSTANCE of the process (which is already loaded into my process by Windows) so I can make GetProcAddress() calls on whatever C Runtime functionality I need.  What I did was simply redefine every stdio function I needed as a function pointer instead of a function, thereby obtaining any C Runtime functionality I needed.  For example, the use of the symbol ‘printf’ defined as a function pointer is exactly the same as the use of the real function printf. 

     One might further reason that one might just as well do that for every C Runtime function I finally implemented myself in my last TCLib postings, i.e., all the string.h functions too.  Reality sets in there and it didn’t work.  But it absolutely worked for everything I needed from stdio.  Here’s the new and modified code, for example, for  crt_win_a.cpp, which is my implementation of the WinMainCRTStartup() function, which is actually the entry point for a Win32/64 asci GUI process…   


--- Code: ---//========================================================================================
//                 Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                              By Fred Harris, January 2016
//
// cl crt_win_a.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//========================================================================================
#include <windows.h>
#pragma comment(linker, "/defaultlib:kernel32.lib")
#pragma comment(linker, "/nodefaultlib:libc.lib")
#pragma comment(linker, "/nodefaultlib:libcmt.lib")
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
extern "C" void __cdecl InitStdio(HINSTANCE);

extern "C" void __cdecl WinMainCRTStartup(void)
{
 HINSTANCE hLib=NULL;
 int iReturn;

 hLib=LoadLibrary("msvcrt.dll");
 if(hLib)
 {
    InitStdio(hLib);
    iReturn = WinMain(GetModuleHandle(NULL),NULL,NULL,SW_SHOWDEFAULT);
    FreeLibrary(hLib);   
 }
 ExitProcess(iReturn);
}

--- End code ---

     You can see above I do a LoadLibrary() on msvcrt.dll which doesn’t really load the library into memory because its already there, but it does return to me the HINSTANCE of the library.  With that I call my InitStdio(hLib) function passing the HINSTANCE of msvcrt.dll, which is where the ‘magic’ occurs.  Here is that, which you might find interesting (it’s a bit bizarre, I admit!)…


--- Code: ---//=========================================================================================================
//                                             InitStdio.cpp
//                                         Fred Harris, June 2016
//
//         cl InitStdio.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//=========================================================================================================
#include <windows.h>

struct      FILE
{
 char*      _ptr;
 int        _cnt;
 char*      _base;
 int        _flag;
 int        _file;
 int        _charbuf;
 int        _bufsiz;
 char*      _tmpfname;
};

extern "C"  void InitStdio(HINSTANCE hLib);

//          From stdio.h
char        (__cdecl* getchar )(                                               );
FILE*       (__cdecl* fopen   )(const char* pszFile, const char* pszMode       );
FILE*       (__cdecl* _wfopen )(const wchar_t* pszFile, const wchar_t* pszMode );
int         (__cdecl* printf  )(const char* pFormat, ...                       );
int         (__cdecl* wprintf )(const wchar_t* pFormat, ...                    );
int         (__cdecl* fprintf )(FILE* fp, const char* format, ...              );
int         (__cdecl* fwprintf)(FILE* fp, const wchar_t* format, ...           );
int         (__cdecl* fscanf  )(FILE* fp, const char* pFormat, ...             );
int         (__cdecl* fwscanf )(FILE* fp, const wchar_t* pFormat, ...          );
int         (__cdecl* sprintf )(char* buffer, const char* format, ...          );
int         (__cdecl* swprintf)(wchar_t* buffer, const wchar_t* format, ...    );
char*       (__cdecl* fgets   )(char* pBuffer, int iSize, FILE* fp             );
wchar_t*    (__cdecl* fgetws  )(wchar_t* pBuffer, int iSize, FILE* fp          );
void        (__cdecl* rewind  )(FILE* fp                                       );
int         (__cdecl* fclose  )(FILE* fp                                       );

void InitStdio(HINSTANCE hLib)
{
 //          From stdio.h
 getchar  = (char     (__cdecl*)(                                              )) GetProcAddress(hLib,"getchar" );
 fopen    = (FILE*    (__cdecl*)(const char* pszFile, const char* pszMode      )) GetProcAddress(hLib,"fopen"   );
 _wfopen  = (FILE*    (__cdecl*)(const wchar_t* pszFile, const wchar_t* pszMode)) GetProcAddress(hLib,"_wfopen" );
 printf   = (int      (__cdecl*)(const char* pFormat,    ...                   )) GetProcAddress(hLib,"printf"  );
 wprintf  = (int      (__cdecl*)(const wchar_t* pFormat, ...                   )) GetProcAddress(hLib,"wprintf" );
 fprintf  = (int      (__cdecl*)(FILE* fp, const char* format, ...             )) GetProcAddress(hLib,"fprintf" );
 fwprintf = (int      (__cdecl*)(FILE* fp, const wchar_t* format, ...          )) GetProcAddress(hLib,"fwprintf");
 fscanf   = (int      (__cdecl*)(FILE* fp, const char* pFmt, ...               )) GetProcAddress(hLib,"fscanf"  );
 fwscanf  = (int      (__cdecl*)(FILE* fp, const wchar_t* pFmt, ...            )) GetProcAddress(hLib,"fwscanf" );
 sprintf  = (int      (__cdecl*)(char* buffer, const char* pFormat, ...        )) GetProcAddress(hLib,"sprintf" );
 swprintf = (int      (__cdecl*)(wchar_t* buffer, const wchar_t* pFormat, ...  )) GetProcAddress(hLib,"swprintf");
 fgets    = (char*    (__cdecl*)(char* pBuffer, int iSize, FILE* fp            )) GetProcAddress(hLib,"fgets"   );
 fgetws   = (wchar_t* (__cdecl*)(wchar_t* pBuffer, int iSize, FILE* fp         )) GetProcAddress(hLib,"fgetws"  );
 rewind   = (void     (__cdecl*)(FILE* fp                                      )) GetProcAddress(hLib,"rewind"  );
 fclose   = (int      (__cdecl*)(FILE* fp                                      )) GetProcAddress(hLib,"fclose"  );
}

--- End code ---

     So what’s going on in the code above is that right after the FILE struct is defined I define a mess of function pointers with names exactly like the stdio.h functions I desperately need such as printf, wprintf, sprintf, etc.  Then in the InitStdio() function I do GetProcAddress() on all those functions exported from msvcrt.dll.  The use of those function pointers will be indistinguishable from the use of the real functions of the same name in msvcrt.dll. 

     It didn’t work with the string.h functions.  Apparently, MS VC knew I was up to some kind of monkey business.  The compiler apparently has built in definitions of the string.h functions, for, when I tried to pull that trick above, it complained and pointed out to me the exact line in string.h where the entity I was trying to re-define as a function pointer was actually a function of that same name.  I thought that was curious being as I wasn’t including the version of string.h that came with my compiler.  So I decided to temporarily remove the string.h that came with my compiler to see what it would do.  Guess what?  Since it couldn’t find that string.h that came with that compiler installation, it looked for another installation!  On the particular box I was using at that time I had both VC6 and VC9 installed (VS 6 and VS 2008).  So my next compile it found the string.h from my VC6 installation, and reported to me the exact line number in that string.h file where the symbol in question was defined as a function.  So at that point, really impressed with the determination of the compiler to prevent me from doing what I wanted, I realized that its determination was greater than mine, so I gave up.  I will admit it would have been interesting to see what it would do if I removed that string.h file too! J  Maybe start searching for GCC installations!

     So I just used the string.h and stdlib.h functions of my previous work, none of which amounted to more than a dozen or so lines of code, with many of them only a line or two, and called it ‘good enough’.  The net result of this change on code size is that any program using floating points will end up being about 1.5 k smaller than my previous versions (those FltToCh() functions I had to implement involved several hundreds of lines of code).  Very simple “Hello, World!” type programs not using floating points may be a half k to a k larger, presumably due to the overhead of calling InitStdio(). 

     I hate to have to do this, but I’ll post all the required code.  A lot of it is exactly the same as code I’ve already posted, so that’s why I’m saying I hate to have to post it all.  But enough has changed to make it problematic to just post an item here or an item there that has changed.  I may miss something.  Or you might get a wrong version of something or other from my previous posts or not know where to find something.  And I wasted a couple hours trying to find all the bits and pieces and provide links as to where they can be found.  And it was horrible enough finding stuff that I decided to post everything afresh.  So you’ll need to grab crt_win_a.cpp and InitStdio.cpp as posted just above.  The new TCLib.mak file is this….


--- Code: ---PROJ       = TCLib

OBJS       = crt_con_a.obj crt_con_w.obj crt_win_a.obj crt_win_w.obj InitStdio.obj InitMath.obj\
             crt_dll.obj newdel.obj alloc.obj alloc2.obj allocsup.obj  strlen.obj memcpy.obj \
             strcpy.obj strncpy.obj strcmp.obj _stricmp.obj _strnicmp.obj _strrev.obj strncmp.obj \
             _atoi64.obj atof.obj abs.obj memset.obj strchr.obj strcat.obj memcmp.obj atol.obj
       
CC         = CL
CC_OPTIONS = /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN

$(PROJ).LIB: $(OBJS)
    LIB /NODEFAULTLIB /machine:x64 /OUT:$(PROJ).lib $(OBJS)

.CPP.OBJ:
    $(CC) $(CC_OPTIONS) $<

--- End code ---

Here is InitMath.cpp, which just grabs pow() from Math.h...


--- Code: ---//=========================================================================================================
//                                              InitMath.cpp
//                                         Fred Harris, July 2016
//
//         cl InitMath.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//=========================================================================================================
#include <windows.h>
extern "C" void InitMath(HINSTANCE hLib);
double (__cdecl* pow)(const double dblBase, const double dblExponent);

void InitMath(HINSTANCE hLib)
{
 pow = (double (__cdecl*)(const double dblBase, const double dblExponent))GetProcAddress(hLib,"pow");
}

--- End code ---

continued...

Frederick J. Harris:
Here are the rest of the *.cpp files as I now have them in about the same order as in the make file…


--- Code: ---//========================================================================================
//                 Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                              By Fred Harris, January 2016
//
// cl crt_con_a.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//========================================================================================
#include <windows.h>
#pragma comment(linker, "/defaultlib:kernel32.lib")
#pragma comment(linker, "/nodefaultlib:libc.lib")
#pragma comment(linker, "/nodefaultlib:libcmt.lib")
extern "C" void __cdecl InitStdio(HINSTANCE);
extern "C" void __cdecl InitMath (HINSTANCE);
extern "C" int  __cdecl main();

extern "C" void __cdecl mainCRTStartup()
{
 HINSTANCE hLib=NULL;
 int iReturn=0;
 
 hLib=LoadLibrary("msvcrt.dll");
 if(hLib)
 {
    InitStdio(hLib);
    InitMath (hLib);
    iReturn=main();
    FreeLibrary(hLib);   
 }
 ExitProcess(iReturn);
}

--- End code ---



--- Code: ---//========================================================================================
//                 Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                              By Fred Harris, January 2016
//
// cl crt_con_w.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//========================================================================================
#include <windows.h>
#pragma comment(linker, "/defaultlib:kernel32.lib")
#pragma comment(linker, "/nodefaultlib:libc.lib")
#pragma comment(linker, "/nodefaultlib:libcmt.lib")
extern "C" void __cdecl InitStdio(HINSTANCE);
extern "C" void __cdecl InitMath(HINSTANCE);
extern "C" int __cdecl  wmain();

extern "C" void __cdecl wmainCRTStartup()
{
 HINSTANCE hLib=NULL;
 int iReturn;
 
 hLib=LoadLibrary("msvcrt.dll");
 if(hLib)
 {
    InitStdio(hLib);
    InitMath (hLib);
    iReturn=wmain();
    FreeLibrary(hLib);   
 }
 ExitProcess(iReturn);
}

--- End code ---
 


--- Code: ---//========================================================================================
//                 Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                              By Fred Harris, January 2016
//
// cl crt_win_a.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//========================================================================================
#include <windows.h>
#pragma comment(linker, "/defaultlib:kernel32.lib")
#pragma comment(linker, "/nodefaultlib:libc.lib")
#pragma comment(linker, "/nodefaultlib:libcmt.lib")
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
extern "C" void __cdecl InitStdio(HINSTANCE);
extern "C" void __cdecl InitMath(HINSTANCE);

extern "C" void __cdecl WinMainCRTStartup(void)
{
 HINSTANCE hLib=NULL;
 int iReturn;

 hLib=LoadLibrary("msvcrt.dll");
 if(hLib)
 {
    InitStdio(hLib);
    InitMath (hLib);   
    iReturn = WinMain(GetModuleHandle(NULL),NULL,NULL,SW_SHOWDEFAULT);
    FreeLibrary(hLib);   
 }
 ExitProcess(iReturn);
}

--- End code ---


--- Code: ---//========================================================================================
//                 Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                              By Fred Harris, January 2016
//
// cl crt_win_w.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//========================================================================================
#include <windows.h>
#pragma comment(linker, "/defaultlib:kernel32.lib")
#pragma comment(linker, "/nodefaultlib:libc.lib")
#pragma comment(linker, "/nodefaultlib:libcmt.lib")
int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int);
extern "C" void __cdecl InitStdio(HINSTANCE);
extern "C" void __cdecl InitMath(HINSTANCE);

extern "C" void __cdecl wWinMainCRTStartup(void)
{
 HINSTANCE hLib=NULL;
 int iReturn;

 hLib=LoadLibrary("msvcrt.dll");
 if(hLib)
 {
    InitStdio(hLib);
    InitMath (hLib);
    iReturn = wWinMain(GetModuleHandle(NULL),NULL,NULL,SW_SHOWDEFAULT);
    FreeLibrary(hLib);   
 }
 ExitProcess(iReturn);
}

--- End code ---



--- Code: ---// cl crt_dll.cpp /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
#include <windows.h>
#pragma comment(linker, "/defaultlib:kernel32.lib")
extern BOOL WINAPI DllMain(HINSTANCE hDllHandle, DWORD dwReason, LPVOID lpreserved);
extern "C" void __cdecl InitStdio(HINSTANCE);
extern "C" void __cdecl InitMath(HINSTANCE);

extern "C" BOOL WINAPI _DllMainCRTStartup(HINSTANCE  hDllHandle, DWORD dwReason, LPVOID  lpreserved)
{
 HINSTANCE hLib=NULL;
 BOOL retcode=FALSE;

 hLib=LoadLibrary("msvcrt.dll");
 if(hLib)
 {
    InitStdio(hLib);
    InitMath (hLib);
    retcode = DllMain(hDllHandle, dwReason, lpreserved);
    FreeLibrary(hLib);   
 }
 
 return retcode;
}

--- End code ---



--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//
//                          LIBCTINY -- Matt Pietrek 2001
//                           MSDN Magazine, January 2001
//
//                              With Help From Mike_V
//                       
//                           By Fred Harris, January 2016
//
//                    cl newdel.cpp /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>

void* __cdecl operator new(size_t s)
{
 return HeapAlloc(GetProcessHeap(), 0, s);
}

void  __cdecl operator delete(void* p)
{
 HeapFree(GetProcessHeap(), 0, p);
}

void* operator new [] (size_t s)
{
 return HeapAlloc(GetProcessHeap(), 0, s);
}

void operator delete [] (void* p)
{
 HeapFree(GetProcessHeap(), 0, p);
}

--- End code ---



--- Code: ---//===============================================================
//    Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                 By Fred Harris, January 2016
//
//     cl alloc.cpp /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//===============================================================
#include <windows.h>
#include "malloc.h"

void* __cdecl malloc(size_t size)
{
 return HeapAlloc(GetProcessHeap(), 0, size);
}

void __cdecl free(void* pMem)
{
 HeapFree(GetProcessHeap(), 0, pMem);
}

--- End code ---
 
 

--- Code: ---//===============================================================
//    Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                 By Fred Harris, January 2016
//
// cl alloc2.cpp /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//===============================================================
#include <windows.h>
#include "malloc.h"

void* __cdecl realloc(void* pMem, size_t size)
{
 if(pMem)
    return HeapReAlloc(GetProcessHeap(), 0, pMem, size);
 else
    return HeapAlloc(GetProcessHeap(), 0, size);
}

void* __cdecl calloc(size_t nitems, size_t size)
{
 return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nitems * size);
}

--- End code ---



--- Code: ---//===============================================================
//    Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                 By Fred Harris, January 2016
//
// cl allocsup.cpp /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//===============================================================
#include <windows.h>
#include "malloc.h"

void* __cdecl _nh_malloc(size_t size, int nhFlag)
{
 return HeapAlloc(GetProcessHeap(), 0, size);
}

size_t __cdecl _msize(void* pMem)
{
 return HeapSize(GetProcessHeap(), 0, pMem);
}

--- End code ---



--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                      cl strlen.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>
#include "string.h"

size_t __cdecl strlen(const char* strSource)
{
 return lstrlenA(strSource);
}

size_t __cdecl wcslen(const wchar_t* strSource)
{
 return lstrlenW(strSource);
}

--- End code ---



--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                     cl memcpy.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
//#include "memory.h"    // for some reason you can't put the prototype for this in
                         // memory.h

extern "C" void* __cdecl memcpy(void* pDest, void* pSrc, size_t iCount)
{
 char* pDestination=(char*)pDest;
 char* pSource=(char*)pSrc;

 for(size_t i=0; i<iCount; i++)
     pDestination[i]=pSource[i];

 return pDest;
}

--- End code ---



--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                      cl strcpy.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>
#include <string.h>

char* __cdecl strcpy(char* strDestination, const char* strSource)
{
 return lstrcpyA(strDestination, strSource);
}

wchar_t* __cdecl wcscpy(wchar_t* strDestination, const wchar_t* strSource)
{
 return lstrcpyW(strDestination, strSource);
}

--- End code ---



--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                 cl strncpy.cpp /O1 /Os /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>
#include "string.h"


char* __cdecl strncpy(char* strDest, const char* strSource, size_t iCount)   
{                                              // 3rd param is size_t
 size_t iLenSrc=strlen(strSource);             // strlen returns size_t                                           
 lstrcpynA(strDest,strSource,(int)iCount);     // lstrcpyn wants an int here for 3rd param                                           
 strDest[iCount-1]=strSource[iCount-1];        // so try cast
 if(iCount>iLenSrc)
 {
    for(size_t i=iLenSrc; i<iCount; i++)
        strDest[i]=0;
 }

 return strDest;
}


wchar_t* __cdecl wcsncpy(wchar_t* strDest, const wchar_t* strSource, size_t iCount)   
{                                           // 3rd param is size_t
 size_t iLen=wcslen(strSource);             // strlen returns size_t                                           
 lstrcpynW(strDest,strSource,(int)iCount);  // lstrcpyn wants an int here for 3rd param                                           
 strDest[iCount-1]=strSource[iCount-1];     // so try cast
 if(iCount>iLen)
 {
    for(size_t i=iLen; i<iCount; i++)
        strDest[i]=0;
 }

 return strDest;
}

--- End code ---


--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                      cl strcmp.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>
#include "string.h"

int __cdecl strcmp(const char* string1, const char* string2)   
{
 return lstrcmpA(string1,string2);
}

int __cdecl wcscmp(const wchar_t* string1, const wchar_t* string2)   
{
 return lstrcmpW(string1,string2);
}

--- End code ---


--- Code: ---//===============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//       cl strncmp.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//===============================================================================================
#include <windows.h>
#include "malloc.h"
#include "string.h"

int __cdecl strncmp(const char* str1, const char* str2, size_t count)
{
 for(size_t i=0; i<count; i++)
 {
     if(str1[i]<str2[i])
        return -1;
     if(str1[i]>str2[i])
        return 1;
 }
 
 return 0;
}

int __cdecl wcsncmp(const wchar_t* str1, const wchar_t* str2, size_t count)
{
 for(size_t i=0; i<count; i++)
 {
     if(str1[i]<str2[i])
        return -1;
     if(str1[i]>str2[i])
        return 1;
 }
 
 return 0;
}

--- End code ---



--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                     cl _stricmp.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>
#include "string.h"

int __cdecl _stricmp(const char* string1, const char* string2)   
{
 return lstrcmpiA(string1,string2);
}

int __cdecl _wcsicmp(const wchar_t* string1, const wchar_t* string2)   
{
 return lstrcmpiW(string1,string2);
}

--- End code ---




--- Code: ---// _strnicmp.cpp
//==============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//     cl _strnicmp.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//==============================================================================================
#include <windows.h>
#include "malloc.h"
#include "string.h"

int __cdecl _strnicmp(const char* str1, const char* str2, size_t count)
{
 size_t iLen1=strlen(str1);
 size_t iLen2=strlen(str2);
 if(count>iLen1)
    return -1;
 if(count>iLen2)
    return 1;
 char* pStr1=(char*)malloc(count+1);
 char* pStr2=(char*)malloc(count+1);
 strncpy(pStr1,str1,count);
 strncpy(pStr2,str2,count);
 pStr1[count]=0;
 pStr2[count]=0;
 int iReturn=lstrcmpiA(pStr1,pStr2);
 free(pStr1);
 free(pStr2);

 return iReturn;
}

int __cdecl _wcsnicmp(const wchar_t* str1, const wchar_t* str2, size_t count)
{
 size_t iLen1=wcslen(str1);
 size_t iLen2=wcslen(str2);
 if(count>iLen1)
    return -1;
 if(count>iLen2)
    return 1;
 wchar_t* pStr1=(wchar_t*)malloc(count*2+2);
 wchar_t* pStr2=(wchar_t*)malloc(count*2+2);
 wcsncpy(pStr1,str1,count);
 wcsncpy(pStr2,str2,count);
 pStr1[count]=0;
 pStr2[count]=0;
 int iReturn=lstrcmpiW(pStr1,pStr2);
 free(pStr1);
 free(pStr2);

 return iReturn;
}

--- End code ---



--- Code: ---//========================================================
// Developed As An Addition To Matt Pietrek's LibCTiny.lib
//              By Fred Harris, January 2016
//
//     cl _strrev.cpp /Oi /c /W3 /DWIN32_LEAN_AND_MEAN
//========================================================
#include <windows.h>
#include "string.h"

char* __cdecl _strrev(char* pStr)
{
 size_t iLen,iHalf;
 char a,b;

 iLen=strlen(pStr), iHalf=iLen/2;
 for(size_t i=0; i<iHalf; i++)
 {
     a=pStr[i], b=pStr[iLen-i-1];
     pStr[i]=b, pStr[iLen-i-1]=a;
 }

 return pStr;
}

wchar_t* __cdecl _wcsrev(wchar_t* pStr)
{
 size_t iLen,iHalf;
 wchar_t a,b;

 iLen=wcslen(pStr), iHalf=iLen/2;
 for(size_t i=0; i<iHalf; i++)
 {
     a=pStr[i], b=pStr[iLen-i-1];
     pStr[i]=b, pStr[iLen-i-1]=a;
 }

 return pStr;
}

--- End code ---



--- Code: ---//===============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//       cl strncmp.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//===============================================================================================
#include <windows.h>
#include "malloc.h"
#include "string.h"

int __cdecl strncmp(const char* str1, const char* str2, size_t count)
{
 for(size_t i=0; i<count; i++)
 {
     if(str1[i]<str2[i])
        return -1;
     if(str1[i]>str2[i])
        return 1;
 }
 
 return 0;
}

int __cdecl wcsncmp(const wchar_t* str1, const wchar_t* str2, size_t count)
{
 for(size_t i=0; i<count; i++)
 {
     if(str1[i]<str2[i])
        return -1;
     if(str1[i]>str2[i])
        return 1;
 }
 
 return 0;
}

--- End code ---



--- Code: ---//==============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                             By Fred Harris, March 2016
//
//       cl _atoi64.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//==============================================================================================
#include "stdlib.h"


_int64 __cdecl _atoi64(const char* pStr)
{
 char c,cNeg=NULL;           // c holds the char; cNeg holds the '-' sign.
 _int64 lTotal=0;            // The running total.

 while(*pStr==32 || *pStr==8)
    pStr++; 
 if(*pStr=='-')
 {
    cNeg='-';
    pStr++;
 }
 while(*pStr)
 {
    if(*pStr>=48 && *pStr<=57)
    {
       c=*pStr++;
       lTotal=10*lTotal+(c-48); // Add this digit to the total.
    }
    else
       break;
 }
 if(cNeg=='-')               // If we have a negative sign, convert the value.
    lTotal=-lTotal;
 
 return lTotal;
}


_int64 __cdecl _wtoi64(const wchar_t* pStr)
{
 wchar_t c,cNeg=NULL;        // c holds the char; cNeg holds the '-' sign.
 _int64 lTotal=0;            // The running total.

 while(*pStr==32 || *pStr==8)
    pStr++; 
 if(*pStr==L'-')
 {
    cNeg=L'-';
    pStr++;
 }
 while(*pStr)
 {
    if(*pStr>=48 && *pStr<=57)
    {
       c=*pStr++;
       lTotal=10*lTotal+(c-48); // Add this digit to the total.
    }
    else
       break;
 }
 if(cNeg==L'-')              // If we have a negative sign, convert the value.
    lTotal=-lTotal;
 
 return lTotal;
}

--- End code ---



--- Code: ---//==============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                             By Fred Harris, May 2016
//
//        cl atof.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//==============================================================================================
#include <windows.h>
#include "stdlib.h"
typedef SSIZE_T ssize_t;

double __cdecl atof(const char* pStr)
{
 ssize_t lTotal   = 0;
 char* pDecPt     = NULL;
 char c,cNeg      = NULL;
 double dblReturn;
 size_t iDiff;

 while(*pStr==32 || *pStr==8 || *pStr==48)
    pStr++;
 if(*pStr=='-')
 {
    cNeg='-';
    pStr++;
 }
 while(*pStr)
 {
    if(*pStr=='.')
    {
       pDecPt=(char*)pStr;
       pStr++;
    }
    else
    {
       if(*pStr>=48 && *pStr<=57)
       {
          c=*pStr++;
          lTotal=10*lTotal+(c-48); // Add this digit to the total.
       }
       else
          break;
    }   
 }
 if(pDecPt)
    iDiff=(int)(pStr-pDecPt-1);
 else
    iDiff=0;
 if(cNeg=='-')                  // If we have a negative sign, convert the value.
    lTotal=-lTotal;
 dblReturn=(double)lTotal;
 for(size_t i=0; i<iDiff; i++)
     dblReturn=dblReturn/10;

 return dblReturn;
}

double __cdecl _wtof(const wchar_t* pStr)
{
 ssize_t lTotal = 0;
 wchar_t* pDecPt=NULL;
 wchar_t c,cNeg=NULL;
 double dblReturn;
 size_t iDiff;

 while(*pStr==32 || *pStr==8 || *pStr==48)
    pStr++;
 if(*pStr==L'-')
 {
    cNeg=L'-';
    pStr++;
 }
 while(*pStr)
 {
    if(*pStr==L'.')
    {
       pDecPt=(wchar_t*)pStr;
       pStr++;
    }
    else
    {
       if(*pStr>=48 && *pStr<=57)
       {
          c=*pStr++;
          lTotal=10*lTotal+(c-48); // Add this digit to the total.
       }
       else
          break;   
    }
 }
 if(pDecPt)
    iDiff=(int)(pStr-pDecPt-1);
 else
    iDiff=0;
 if(cNeg==L'-')                  // If we have a negative sign, convert the value.
    lTotal=-lTotal;
 dblReturn=(double)lTotal;
 for(size_t i=0; i<iDiff; i++)
     dblReturn=dblReturn/10;

 return dblReturn;
}

--- End code ---



--- Code: ---//==============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                             By Fred Harris, March 2016
//
//         cl abs.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//==============================================================================================
#include "stdlib.h"

int __cdecl abs(int n)
{
 if(n<0)
    return -n;
 else
    return n;
}
 
_int64 __cdecl _abs64(__int64 n)
{
 if(n<0)
    return -n;
 else
    return n;
}

long labs(long n)
{
 if(n<0)
    return -n;
 else
    return n;
}

--- End code ---



--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                     cl memset.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include "memory.h"

void* __cdecl memset(void* p, int c, size_t count)
{
 char* pCh=(char*)p;
 for(size_t i=0; i<count; i++)
     pCh[i]=(char)c;
 return p;
}

wchar_t* __cdecl wmemset(wchar_t* p, wchar_t c, size_t count)
{
 for(size_t i=0; i<count; i++)
     p[i]=c;
 return p;
}

--- End code ---



--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By James C. Fuller March 2016
//
//                      cl strchr.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>
#include "string.h"

char * __cdecl _strchr (const char *s, int c)
{
    do {
        if (*s == c)
        {
            return (char*)s;
        }
    } while (*s++);
    return (0);
}

wchar_t * __cdecl _wcschr (const wchar_t *s, int c)
{
    do {
        if (*s == c)
        {
            return (wchar_t*)s;
        }
    } while (*s++);
    return (0);
}


--- End code ---



--- Code: ---//===================================================================
//   Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                By Fred Harris, January 2016
//
//         cl strcat.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//===================================================================
#include <windows.h>
#include "string.h"

char* __cdecl strcat(char* strDest, const char* strSource)   
{                                           // 3rd param is size_t
 return lstrcatA(strDest, strSource);
}

wchar_t* __cdecl wcscat(wchar_t* strDest, const wchar_t* strSource)   
{                                           // 3rd param is size_t
 return lstrcatW(strDest, strSource);
}

--- End code ---



--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                            By Fred Harris, January 2016
//
//                     cl memcpy.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
//#include "memory.h"    // for some reason you can't put the prototype for this in
                         // memory.h

extern "C" void* __cdecl memcpy(void* pDest, void* pSrc, size_t iCount)
{
 char* pDestination=(char*)pDest;
 char* pSource=(char*)pSrc;

 for(size_t i=0; i<iCount; i++)
     pDestination[i]=pSource[i];

 return pDest;
}

--- End code ---


--- Code: ---//=====================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                              By Fred Harris, April 2016
//
//                     cl memcmp.cpp /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================

extern "C" int __cdecl memcmp(const void* buf1, const void* buf2, size_t iCount)
{
 char* p1=(char*)buf1;
 char* p2=(char*)buf2;
 for(size_t i=0; i<iCount; i++)
 {
     if(*p1<*p2)
        return -1;
     if(*p1>*p2)
        return 1;
     p1++, p2++;
 }

 return 0;
}

--- End code ---


--- Code: ---//==============================================================================================
//               Developed As An Addition To Matt Pietrek's LibCTiny.lib
//                             By Fred Harris, March 2016
//
//        cl atol.cpp /D "_CRT_SECURE_NO_WARNINGS" /c /W3 /DWIN32_LEAN_AND_MEAN
//==============================================================================================
#include "stdlib.h"


long __cdecl atol(const char* pStr)
{
 char c,cNeg=NULL;           // c holds the char; cNeg holds the '-' sign.
 long lTotal=0;              // The running total.

 while(*pStr==32 || *pStr==8)
    pStr++; 
 if(*pStr=='-')
 {
    cNeg='-';
    pStr++;
 }
 while(*pStr)
 {
    if(*pStr>=48 && *pStr<=57)
    {
       c=*pStr++;
       lTotal=10*lTotal+(c-48); // Add this digit to the total.
    }
    else
       break;
 }
 if(cNeg=='-')               // If we have a negative sign, convert the value.
    lTotal=-lTotal;
 
 return lTotal;
}


int __cdecl atoi (const char* pStr)
{
 return ((int)atol(pStr));
}


long __cdecl _wtol(const wchar_t* pStr)
{
 wchar_t c,cNeg=NULL;        // c holds the char; cNeg holds the '-' sign.
 long lTotal=0;              // The running total.

 while(*pStr==32 || *pStr==8)
    pStr++; 
 if(*pStr==L'-')
 {
    cNeg=L'-';
    pStr++;
 }
 while(*pStr)
 {
    if(*pStr>=48 && *pStr<=57)
    {
       c=*pStr++;
       lTotal=10*lTotal+(c-48); // Add this digit to the total.
    }
    else
       break;
 }
 if(cNeg==L'-')              // If we have a negative sign, convert the value.
    lTotal=-lTotal;
 
 return lTotal;
}


int __cdecl _wtoi (const wchar_t* pStr)
{
 return ((int)_wtol(pStr));
}

--- End code ---

continued...

Frederick J. Harris:
     Unfortunately, my String Class had to change too, as several of the members (a constructor and operator=) take floats which must be converted to null terminated strings with my old FltToCh() function – now superceded by ordinary sprintf C Runtime usage.   And then there are several Format() members in the same situation.  So here is Strings.h…


--- Code: ---// Strings.h                                                    // NULL Terminated String Class With Method Naming Conventions After BASIC Model Languages.  Implemented For
#ifndef Strings_h                                               // x86 / x64, Asci / Unicode (For x86 Builds Comment Out x64 #define Below Left).
#define Strings_h

#ifndef ssize_t
typedef SSIZE_T ssize_t;                                        // ssize_t is defined in GCC, but isn't defined in VC 9-15, but rather SSIZE_T.  For symetry we'll define it.
#endif

#define MINIMUM_ALLOCATION                   16                 // allocate at least this for every String
#define EXPANSION_FACTOR                      2                 // repeated concatenations will keep doubling buffer
#define INTEGRAL_CONVERSIONS                                    // allows direct assignment of integral numeric values to Strings, e.g., String s(12345) or s=54321;  It can be remmed out.
#define FLOATING_POINT_CONVERSIONS                              // allows direct assignment of floating point values to Strings, e.g., String s(3.14159) or s=2.98;  It can be remmed out.
#define FORMATTING                                              // put commas every three places, rounding, left/right justification, specify field sizes (padding), etc.  It can be remmed out.
#define PLUS_EQUAL                                              // For += Functionality With String Concatenation;  It can be remmed out.
#define CONSOLE_OUTPUT                                          // support console output, i.e., enable String::Print();  It can be remmed out.
#define FILE_OUTPUT                                             // Support Writing To FILEs;  It can be remmed out.
#define x64

class String
{
 public:                                                        // Constructors (10)
 String();                                                      // Uninitialized Constructor
 String(const size_t iSize, bool blnNullOut);                   // Constructor creates String of size iSize and optionally nulls out
 String(const size_t iCount, const TCHAR c);                    // Constructor initializes String with size_t # of TCHARs
 String(const TCHAR ch);                                        // Constructor creates a String initialized with a char, e.g., String c('A');
 String(const TCHAR* pStr);                                     // Constructor: Initializes with char*, e.g. s1 = "PowerBASIC! Compile Without Compromise!"
 String(const String& strAnother);                              // Constructor creates String initialized with another already existing String, e.g., String s2(s1);
 #ifdef INTEGRAL_CONVERSIONS
    String(int iNumber);                                        // Constructor creates String initialized with an int, e.g., String s1(2468); kind of Str$(2468) in PowerBASIC
    String(unsigned int uNumber);                               // Constructor creates String initialized with an unsigned int, e.g., String s1(2468); kind of Str$(2468) in PowerBASIC
    #ifdef x64
       String(size_t  uiNumber);                                // Constructor creates String from 64 bit unsigned number, e.g., String strBigNum(12345678987654);
       String(ssize_t iNumber);                                 // Constructor creates String from 64 bit signed number, e.g., String strBigNum(-12345678987654);
    #endif
 #endif
 #ifdef FLOATING_POINT_CONVERSIONS
    String(double dblNumber);                                   // Constructor creates String from floating point number, e.g., String s(3.14159);
 #endif
 String& operator=(const TCHAR c);                              // Assign a char to a String, e.g., String s='A';
 String& operator=(const TCHAR* pStr);                          // Assign a character string to a String Object, e.g.,  String s1 = "Compile Without Compromise";
 String& operator=(const String& strAnother);                   // Assign an already existing String to this one, e.g., String s2 = s1;
 String& Make(const TCHAR, size_t);                             // Returns reference to this with iCount ch TCHARs in it
 #ifdef INTEGRAL_CONVERSIONS
    String& operator=(int iNumber);                             // Assign an int converted to a String to this, e.g., String s1 = -123456789;
    String& operator=(unsigned int uNumber);                    // Assign an unsigned int converted to a String to this, e.g., String s1 =  123456789;
    #ifdef x64
       String& operator=(size_t  uNumber);                      // Assign a 64 bit unsigned quantity converted to a String to this, e.g., String s2 =  12345678987654;
       String& operator=(ssize_t iNumber);                      // Assign a 64 bit   signed quantity converted to a String to this, e.g., String s2 = -12345678987654;
    #endif
 #endif
 #ifdef FLOATING_POINT_CONVERSIONS
    String& operator=(double dblNumber);                        // Assign a double converted to a String to this, e.g., String strDouble = 3.14159;
 #endif
 String operator+(const TCHAR ch);                              // Concatenates or adds a character to an already existing String, e.g., s1 = s1 + 'A';
 String operator+(const TCHAR* pChar);                          // Concatenates or adds a character array (char*) to an already existing String, e.g., s1 = s1 + lpText;
 String operator+(String& s);                                   // Concatenates or adds another String to this one, e.g., s1 = s1 + s2;
 #ifdef PLUS_EQUAL
    String& operator+=(const TCHAR ch);                         // Add TCHAR to this
    String& operator+=(const String&);                          // Adds a String to this and assigns it to left of equal sign
    String& operator+=(const TCHAR*);                           // Adds a TCHAR* to this and assigns it to left of equal sign
 #endif
 bool operator==(String s);                                     // Compares two Strings for case sensitive equality
 bool operator==(const TCHAR* pChar);                           // Compares a String against a char* for case sensitive equality
 bool operator!=(TCHAR* pChar);                                 // Compares a String against a char* for case sensitive inequality
 void LTrim();                                                  // Removes leading white space by modifying existing String
 void RTrim();                                                  // Removes trailing white space by modifying existing String
 void Trim();                                                   // Removes leading or trailing white space from existing String
 String UCase();                                                // Returns *this in upper case
 String Left(size_t iCntChars);                                 // Returns a String consisting of left-most iCntChars of this
 String Right(size_t iCntChars);                                // Returns a String consisting of right-most iCntChars of this
 String Mid(size_t iStart, size_t iCount);                      // Returns a String consisting of iCount characters of this starting at one based iStart
 int ParseCount(const TCHAR delimiter);                         // Returns count of delimited fields as specified by char delimiter, i.e., comma, space, tab, etc.
 void Parse(String* pStr, TCHAR delimiter, size_t iParseCount); // Parses this based on delimiter.  Must pass in 1st parameter String* to sufficient number of Strings
 String Remove(const TCHAR* pCharsToRemove);                    // Returns A String With All The chars In A char* Removed (Individual char removal)
 String Remove(const TCHAR* pStrToRemove, bool bCaseSensitive); // Returns a String with 1st parameter removed.  2nd is bool for case sensitivity.
 String Replace(TCHAR* pToRevove, TCHAR* pReplacement);         // Replaces pToRemove with pReplacement in new String.  Replacement can cause String to grow
 bool blnSuccess();                                             // true if String memory allocation succeeded; false otherwise

 int InStr                                                      // Returns one based position of pStr in this by case sensitivity and left/right starting position
 (
  const TCHAR* pStr,                                            // pStr -- TCHAR* To Character String To Search For
  bool  blnCaseSensitive,                                       // true / case sensitive; false / case insensitive
  bool  blnStartLeft                                            // Can Specify Search Start From Beginning Or End
 );

 int InStr                                                      // Returns one based position of String in this by case sensitivity and left/right starting position
 (
  const String& str,                                            // String To Search For
  bool  blnCaseSensitive,                                       // true / case sensitive; false / case insensitive
  bool  blnStartLeft                                            // Can Specify Search Start From Beginning Or End
 );

 #ifdef FORMATTING
    void Format                                                 // dblNumber converted to String, in iArrayCount Buffer Size, with iDecPlaces right of decimal, left or right justified.
    (
     double dblNumber,                                          // Double To Be Formatted.
     size_t iFieldSize,                                         // Field Size In Characters.
     size_t iDecimalPlaces,                                     // Number of decimal places to right of decimal
     TCHAR  cDecimalSeperator,                                  // In US We Use '.'.  In Europe mostly this -- ','
     bool   blnRightJustified                                   // Left/Right Justify Result.
    );

    void Format                                                 // double converted to Str, in iArCnt Buf Size, with iDecPlaces right of dec, with cSeperator for thousands, l/r justified.
    (
     double dblNumber,                                          // double to be converted to String
     size_t iFieldSize,                                         // See above.
     size_t iDecimalPlaces,                                     // Number of decimal places to right of decimal
     TCHAR  cThousandsSeperator,                                // Period, comma, whatever
     TCHAR  cDecimalSeperator,                                  // Period, comma, whatever
     bool   blnRightJustified                                   // true is right justified; false left justified in iArraySizeCountOfObjects buffer
    );

    void Format                                                 // For integral numbers; can specify justification, field width, and seperator for thousands place
    (
     ssize_t iNumber,                                           // Integral number to format
     size_t  iFieldSize,                                        // See above.
     TCHAR   cThousandsSeperator,                               // Comma, period, whatever
     bool    blnRightJustified                                  // true is right justified; false left justified in iArraySizeCountOfObjects buffer
    );
 #endif

 #ifdef CONSOLE_OUTPUT
    void Print(bool blnCrLf);                                   // Outputs String with or without CrLf
    void Print(const TCHAR* pText, bool blnCrLf);               // Parameter #1 - leading text literal const; Parameter #2 - with/without CrLf
 #endif
 #ifdef FILE_OUTPUT
    void Print(FILE*, bool);                                    //Outputs String To FILE With Or Without CrLf.
    void Print(FILE*, TCHAR*, bool);                            //Outputs String To FILE With Leading Text String With Or Without CrLf.
 #endif
 ssize_t iVal();                                                // Returns integral value of String
 size_t Len();                                                  // accessor for String::iLen member
 size_t Capacity();                                             // Will be one less than underlying memory allocation
 TCHAR* lpStr();                                                // Same as std::string.c_str().  Returns pointer to underlying Z String
 ~String();                                                     // String Destructor

 private:
 TCHAR*    lpBuffer;                                            // Buffer controlled by String
 size_t    iLen;                                                // Keeps track of present length of String
 size_t    iCapacity;                                           // Keeps track of present capacity of String.  Will be one less element than length of this->lpBuffer memory allocation
 size_t    blnSucceeded;                                        // True if String Method Allocation/Invocation Succeeded; False Otherwise
};

String operator+(TCHAR* lhs, String& rhs);
String Str(double dblNum);                                      // Usage: String s1 = "Pi = " + Str(3.14159); // Output: Pi = 3.14159
#ifdef x64
   String Str(int iNum);                                        // Returns 32 bit int converted to String
   String Str(unsigned int iNum);                               // Returns 32 bit unsigned int converted to String
   String Str(SSIZE_T iNum);                                    // Returns 64 bit signed integral number converted to String
   String Str(size_t iNum);                                     // Returns 64 bit unsigned integral number converted to String
#else
   String Str(int iNum);                                        // Returns 32 bit int converted to String
   String Str(unsigned int iNum);                               // Returns 32 bit unsigned int converted to String
#endif
#endif
// End Strings.h

--- End code ---

     Before I post Strings.cpp I really need to make some comments on the above, because I changed stuff.  The three formatting members as I had them in my last postings were weird and awkward.  I believe I improved them considerably.  The deal is this.  Before, I had to use my FltToCh() function to do the conversion from a floating point value in 8 byte binary format to an asci or wide character representation.  FltToCh() was something of a low level C type function one of whose parameters was the size of the character buffer where the converted characters were to be written/stored.  In my original implementation of my String::Format() functions I left that parameter like it was, which turns out to be awkward in high level usage.  What makes a lot more sense is to simply specify a field size and number of decimal places – right or left justified, which the member will return in String Class format.  In other words, exactly like the format specifiers in the printf family of functions.  For example, suppose you are outputting a column of dollar amounts where the dollar amounts will never exceed $ 1000.00.  For that you may wish to specify a field width of 8 with 2 decimal points.  A field width of 8 would allow for numbers up to 10000.00 to be output (if not using the version that uses thousands separators.  In still other words, it works exactly like printf where it would be specified like so…


--- Code: ---printf(“%8.2f”,dblAmount); 

--- End code ---

     So now it leaves all the internal work of allocating buffers and so on to the internals of the member, and returns to the caller a String Object of the correct field size for further use in your high level program.  So here is Strings.cpp…

continued...

Navigation

[0] Message Index

[#] Next page

Go to full version