My class for constructing application windows

Pages: 12
Oh, you apparently didn’t mind I posted a whole String class? I was hoping not to do that so it wouldn’t detract from the argument I was trying to make concerning Windows C based code being very object oriented, but at the last minute I did it to save time because my already existing example used one of my String Class implementations, and it would have taken me quite a bit more time to figure out how to substitute the C++ Stl String Class in it, with which I’m not all that familiar.

Basically, the whole reason I use C++ instead of C is because C++ allows me to make a String class (and other classes, too). That, plus By Reference parameter passing is pretty much all I really use of C++. My String class is very useful to me. Note for example the String::ParseCount() and String::Parse() functioality. Those members allow any delimited String to be parsed. For example, a String such as this…

strData="Zero, One, Two, Three, Four, Five";

can be handled very easily. Here is a program to do it. I used Code::Blocks 8.05…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <windows.h>   //ParseEx.cpp   23040 bytes
#include <tchar.h>
#include <stdio.h>
#include "Strings.h"

int main()
{
 String* strArr=NULL;
 String strData;
 int iCnt=0;

 strData="Zero, One, Two, Three, Four, Five";
 iCnt=strData.ParseCount(',');
 printf("iCnt = %u\n\n",iCnt);
 strArr=new String[iCnt];
 strData.Parse(strArr,',');
 puts("i\tstrArr[i]");
 puts("=================");
 for(int i=0; i<iCnt; i++)
 {
     printf("%d\t",i);
     strArr[i].Trim();
     strArr[i].Print(true);
 }
 delete [] strArr;

 return 0;
}

/*
iCnt = 6

i       strArr[i]
=================
0       Zero
1       One
2       Two
3       Three
4       Four
5       Five
*/


For desktop Windows coding I use PowerBASIC (www.powerbasic.com), and my String Class is kind of a hacked bad version of PowerBASIC’s awesome String handling. Here is what the exact PowerBASIC translation of the above would be…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#Compile Exe       '17408 bytes
#Dim All

Function PBMain() As Long
  Local strArr() As String
  Local strData As String
  Local iCnt As Long
  Register i As Long
  
  strData="Zero, One, Two, Three, Four, Five"
  iCnt=ParseCount(strData,",")
  Print "iCnt = " iCnt
  Print
  Redim strArr(iCnt-1)
  Parse strData, strArr(), ","
  Print " i            strArr(i)"
  Print "======================="
  For i=LBound(strArr) To UBound(strArr)
    Print i, Trim$(strArr(i))
  Next i
  Erase strArr
  Waitkey$

  PBMain=0
End Function

'iCnt =  6
'
' i            strArr(i)
'=======================
' 0            Zero
' 1            One
' 2            Two
' 3            Three
' 4            Four
' 5            Five   


I actually have three versions of my String Class. The one I posted, i.e., the MINIMAL version, I use when I want just simple functionality and small code size. It does include the Parse stuff above though. My SMALL version has a few more functions but its memory allocation is basically simple. For example, if you ran a loop through the twenty-six letters of the alphabet concatenating them together there would be twenty-six memory allocations/releases in the various operator+ overloads because it matches each memory allocation exactly to the length of the String needed, plus the terminating null.

In my FAST String class I aggressively enlarge the buffer in successive concatenation operations so as to minimize memory allocations at the expense of using more memory. Here is a GUI app that demos my String class, and I’ll attach the FAST version. I just compiled this with VC++ 9, and here are the stupid equates you need to stick in the C++ ‘Preprocessor’ tab to shut up the stupid warnings…

 
_CRT_SECURE_NO_WARNINGS;_CRT_NON_CONFORMING_SWPRINTFS


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//Strings.h
#if !defined(STRINGS_H)
#define STRINGS_H
#define EXPANSION_FACTOR      2
#define MINIMUM_ALLOCATION    8

class __declspec(dllexport) String
{
 public:
 String();                                 //Uninitialized Constructor
 String(const TCHAR);                      //Constructor Initializes String With TCHAR
 String(const TCHAR*);                     //Constructor Initializes String With TCHAR*
 String(const String&);                    //Constructor Initializes String With Another String (Copy Constructor)
 String(const int);                        //Constructor Initializes Buffer To Specific Size
 String(const TCHAR ch, const int iCount); //Constructor initializes String with int # of chars
 String& operator=(const TCHAR);           //Assigns TCHAR To String
 String& operator=(const TCHAR*);          //Assigns TCHAR* To String
 String& operator=(const String&);         //Assigns one String to another (this one)
 String& operator=(int);                   //Converts And Assigns An Integer to A String
 String& operator=(unsigned int);          //Converts And Assigns An Unsigned Integer to A String
 String& operator=(long);                  //Converts And Assigns A Long to A String
 String& operator=(DWORD);                 //Converts And Assigns A DWORD to A String
 String& operator=(double);                //Converts And Assigns A double to A String
 String& operator+(const TCHAR);           //For adding TCHAR to String
 String& operator+(const TCHAR*);          //For adding null terminated TCHAR array to String
 String& operator+(const String&);         //For adding one String to Another
 bool operator==(const String);            //For comparing Strings
 String Left(unsigned int);                //Returns String of iNum Left Most TCHARs of this
 String Right(unsigned int);               //Returns String of iNum Right Most TCHARs of this
 String Mid(unsigned int, unsigned int);   //Returns String consisting of number of TCHARs from some offset
 String& Make(const TCHAR ch, int iCount); //Creates (Makes) a String with iCount TCHARs
 String& GetModulePath();                  //Returns reference To String Containing Path of Exe/dll running
 String Remove(const TCHAR*, bool);        //Returns A String With A Specified TCHAR* Removed
 String Remove(TCHAR* pStr);               //Returns A String With All The TCHARs In A TCHAR* Removed (Individual char removal)
 String Retain(TCHAR* pStr);               //Seems to return a String with some characters retained???
 int InStr(const TCHAR);                   //Returns one based offset of a specific TCHAR in a String
 int InStr(const TCHAR*, bool);            //Returns one based offset of a particular TCHAR pStr in a String
 int InStr(const String&, bool);           //Returns one based offset of where a particular String is in another String
 void LTrim();                             //Returns String with leading spaces/tabs removed
 void RTrim();                             //Returns String with spaces/tabs removed from end
 void Trim();                              //Returns String with both leading and trailing whitespace removed
 unsigned int ParseCount(const TCHAR);     //Returns count of Strings delimited by a TCHAR passed as a parameter
 void Parse(String*, TCHAR);               //Returns array of Strings in first parameter as delimited by 2nd TCHAR delimiter
 int iVal();                               //Returns int value of a String
 int LenStr(void);                         //Returns length of string
 TCHAR* lpStr();                           //Returns address of pStrBuffer member variable
 TCHAR GetChar(unsigned int);              //Returns TCHAR at one based index
 void SetChar(unsigned int, TCHAR);        //Sets TCHAR at one based index
 void Print(bool);                         //Outputs String to Console with or without CrLf
 ~String();                                //String Destructor

 private:
 TCHAR* pStrBuffer;
 int    iAllowableCharacterCount;
};

String operator+(TCHAR* lhs, String& rhs); //global function
#endif  //#if !defined(STRINGS_H) 


I'll need to break this into parts...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
//Strings.cpp
#include  <windows.h>
#include  <tchar.h>
#include  <stdlib.h>
#include  <stdio.h>
#include  <string.h>
#include  "Strings.h"


String operator+(TCHAR* lhs, String& rhs)         //global function
{
 String sr=lhs;
 sr=sr+rhs;
 return sr;
}


String::String()    //Uninitialized Constructor
{
 pStrBuffer=new TCHAR[MINIMUM_ALLOCATION];
 pStrBuffer[0]=_T('\0');
 this->iAllowableCharacterCount=MINIMUM_ALLOCATION-1;
}


String::String(const TCHAR ch)  //Constructor: Initializes with TCHAR
{
 pStrBuffer=new TCHAR[MINIMUM_ALLOCATION];
 pStrBuffer[0]=ch;
 pStrBuffer[1]=_T('\0');
 iAllowableCharacterCount=MINIMUM_ALLOCATION-1;
}


String::String(const TCHAR* pStr)  //Constructor: Initializes with TCHAR*
{
 int iLen,iNewSize;

 iLen=_tcslen(pStr);
 iNewSize=(iLen/16+1)*16;
 pStrBuffer=new TCHAR[iNewSize];
 this->iAllowableCharacterCount=iNewSize-1;
 _tcscpy(pStrBuffer,pStr);
}


String::String(const String& s)  //Constructor Initializes With Another String, i.e., Copy Constructor
{
 int iLen,iNewSize;

 iLen=_tcslen(s.pStrBuffer);
 iNewSize=(iLen/16+1)*16;
 this->pStrBuffer=new TCHAR[iNewSize];
 this->iAllowableCharacterCount=iNewSize-1;
 _tcscpy(this->pStrBuffer,s.pStrBuffer);
}


String::String(const int iSize)  //Constructor Creates String With Custom Sized
{                                //Buffer (rounded up to paragraph boundary)
 int iNewSize;

 iNewSize=(iSize/16+1)*16;
 pStrBuffer=new TCHAR[iNewSize];
 this->iAllowableCharacterCount=iNewSize-1;
 this->pStrBuffer[0]=_T('\0');
}


String::String(const TCHAR ch, int iCount)
{
 int iNewSize;

 iNewSize=(iCount/16+1)*16;
 pStrBuffer=new TCHAR[iNewSize];
 this->iAllowableCharacterCount=iNewSize-1;
 for(int i=0; i<iCount; i++)
     pStrBuffer[i]=ch;
 pStrBuffer[iCount]=_T('\0');
}


String& String::operator=(const TCHAR ch)  //Overloaded operator = for assigning a TCHAR to a String
{
 this->pStrBuffer[0]=ch;
 this->pStrBuffer[1]=_T('\0');

 return *this;
}


String& String::operator=(const TCHAR* pStr)   //Constructor For If Pointer To Asciiz String Parameter
{
 int iLen,iNewSize;

 iLen=_tcslen(pStr);
 if(iLen<this->iAllowableCharacterCount)
    _tcscpy(pStrBuffer,pStr);
 else
 {
    delete [] pStrBuffer;
    iNewSize=(iLen/16+1)*16;
    pStrBuffer=new TCHAR[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
    _tcscpy(pStrBuffer,pStr);
 }

 return *this;
}


String& String::operator=(const String& strRight)  //Overloaded operator = for
{                                                  //assigning another String to
 int iRightLen,iNewSize;                           //a String

 if(this==&strRight)
    return *this;
 iRightLen=_tcslen(strRight.pStrBuffer);
 if(iRightLen < this->iAllowableCharacterCount)
    _tcscpy(pStrBuffer,strRight.pStrBuffer);
 else
 {
    iNewSize=(iRightLen/16+1)*16;
    delete [] this->pStrBuffer;
    this->pStrBuffer=new TCHAR[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
    _tcscpy(pStrBuffer,strRight.pStrBuffer);
 }

 return *this;
}


bool String::operator==(const String strCompare)
{
 if(_tcscmp(this->pStrBuffer,strCompare.pStrBuffer)==0)  //_tcscmp
    return true;
 else
    return false;
}


String& String::operator+(const TCHAR ch)      //Overloaded operator + (Puts TCHAR in String)
{
 int iLen,iNewSize;
 TCHAR* pNew;

 iLen=_tcslen(this->pStrBuffer);
 if(iLen<this->iAllowableCharacterCount)
 {
    this->pStrBuffer[iLen]=ch;
    this->pStrBuffer[iLen+1]='\0';
 }
 else
 {
    iNewSize=((this->iAllowableCharacterCount*EXPANSION_FACTOR)/16+1)*16;
    pNew=new TCHAR[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
    _tcscpy(pNew,this->pStrBuffer);
    delete [] this->pStrBuffer;
    this->pStrBuffer=pNew;
    this->pStrBuffer[iLen]=ch;
    this->pStrBuffer[iLen+1]='\0';
 }

 return *this;
}


String& String::operator+(const TCHAR* pChar) //Overloaded operator + (Adds TCHAR literals
{                                             //or pointers to Asciiz Strings)
 int iLen,iNewSize;
 TCHAR* pNew;

 iLen=_tcslen(this->pStrBuffer)+_tcslen(pChar);
 if(iLen<this->iAllowableCharacterCount)
 {
    if(this->pStrBuffer)
       _tcscat(this->pStrBuffer,pChar);
    else
       _tcscpy(this->pStrBuffer, pChar);
 }
 else
 {
    iNewSize=(iLen*EXPANSION_FACTOR/16+1)*16;
    pNew=new TCHAR[iNewSize];
    this->iAllowableCharacterCount = iNewSize-1;
    if(this->pStrBuffer)
    {
       _tcscpy(pNew,this->pStrBuffer);
       delete [] pStrBuffer;
       _tcscat(pNew,pChar);
    }
    else
       _tcscpy(pNew,pChar);
    this->pStrBuffer=pNew;
 }

 return *this;
}


String& String::operator+(const String& strRight)  //Overloaded operator + Adds
{                                                  //Another String to the left
 int iLen,iNewSize;                                //operand
 TCHAR* pNew;

 iLen=_tcslen(this->pStrBuffer) + _tcslen(strRight.pStrBuffer);
 if(iLen < this->iAllowableCharacterCount)
 {
    if(this->pStrBuffer)
       _tcscat(this->pStrBuffer,strRight.pStrBuffer);
    else
       _tcscpy(this->pStrBuffer,strRight.pStrBuffer);
 }
 else
 {
    iNewSize=(iLen*EXPANSION_FACTOR/16+1)*16;
    pNew=new TCHAR[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
    if(this->pStrBuffer)
    {
       _tcscpy(pNew,this->pStrBuffer);
       delete [] pStrBuffer;
       _tcscat(pNew,strRight.pStrBuffer);
    }
    else
       _tcscpy(pNew,strRight.pStrBuffer);
    this->pStrBuffer=pNew;
 }

 return *this;
}


String String::Left(unsigned int iNum)
{
 unsigned int iLen,i,iNewSize;
 String sr;

 iLen=_tcslen(this->pStrBuffer);
 if(iNum<iLen)
 {
    iNewSize=(iNum*EXPANSION_FACTOR/16+1)*16;
    sr.iAllowableCharacterCount=iNewSize-1;
    sr.pStrBuffer=new TCHAR[iNewSize];
    for(i=0;i<iNum;i++)
        sr.pStrBuffer[i]=this->pStrBuffer[i];
    sr.pStrBuffer[iNum]='\0';
    return sr;
 }
 else
 {
    sr=*this;
    return sr;
 }
}


String String::Right(unsigned int iNum)  //Returns Right$(strMain,iNum)
{
 unsigned int iLen,i,j,iNewSize;
 String sr;

 iLen=_tcslen(this->pStrBuffer);
 if(iNum<iLen)
 {
    iNewSize=(iNum*EXPANSION_FACTOR/16+1)*16;
    sr.iAllowableCharacterCount=iNewSize-1;
    sr.pStrBuffer=new TCHAR[iNewSize];
    j=0;
    for(i=iLen-iNum;i<=iLen;i++)
    {
        sr.pStrBuffer[j]=this->pStrBuffer[i];
        j++;
    }
    sr.pStrBuffer[iNum]='\0';
    return sr;
 }
 else
 {
    sr=*this;
    return sr;
 }
}
...next part...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
String String::Mid(unsigned int iStart, unsigned int iCount)
{
 unsigned int iLen,i,j,iNewSize;
 String sr;

 iLen=_tcslen(this->pStrBuffer);
 if(iStart && iStart<=iLen)
 {
    if(iCount && iStart+iCount-1<=iLen)
    {
       iNewSize=(iCount*EXPANSION_FACTOR/16+1)*16;
       sr. iAllowableCharacterCount=iNewSize-1;
       sr.pStrBuffer=new TCHAR[iNewSize];
       j=0;
       sr.pStrBuffer=new TCHAR[iNewSize];
       for(i=iStart-1;i<iStart+iCount-1;i++)
       {
           sr.pStrBuffer[j]=this->pStrBuffer[i];
           j++;
       }
       sr.pStrBuffer[iCount]='\0';
       return sr;
    }
    else
    {
       sr=*this;
       return sr;
    }
 }
 else
 {
    sr=*this;
    return sr;
 }
}


String& String::Make(const TCHAR ch, int iCount)    //Creates (Makes) a String with iCount TCHARs
{
 if(iCount>this->iAllowableCharacterCount)
 {
    delete [] pStrBuffer;
    int iNewSize=(iCount*EXPANSION_FACTOR/16+1)*16;
    this->pStrBuffer=new TCHAR[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
 }
 for(int i=0; i<iCount; i++)
     pStrBuffer[i]=ch;
 pStrBuffer[iCount]=_T('\0');

 return *this;
}


String& String::GetModulePath()
{
 TCHAR szFileName[MAX_PATH];
 TCHAR* pChar=NULL;
 DWORD dwRet=0;

 dwRet=GetModuleFileName(NULL,szFileName,MAX_PATH);
 if(dwRet)
 {
    pChar=_tcsrchr(szFileName,(int)_T('\\')); //wcsrchr
    szFileName[pChar-(TCHAR*)szFileName]=_T('\0');
    if(dwRet>(unsigned)this->iAllowableCharacterCount)
    {
       delete [] pStrBuffer;
       int iNewSize=(dwRet*EXPANSION_FACTOR/16+1)*16;
       this->pStrBuffer=new TCHAR[iNewSize];
       this->iAllowableCharacterCount=iNewSize-1;
    }
    _tcscpy(this->pStrBuffer,szFileName);
 }

 return *this;
}


String String::Remove(const TCHAR* pToRemove, bool blnCaseSensitive)
{
 int i,j,iParamLen,iReturn=0;
 bool blnFound=false;

 if(*pToRemove==0)
    return *this;
 iParamLen=_tcslen(pToRemove);
 i=0, j=0;
 do
 {
  if(pStrBuffer[i]==0)
     break;
  if(blnCaseSensitive)
     iReturn=_tcsncmp(pStrBuffer+i,pToRemove,iParamLen);  //_tcsncmp
  else
     iReturn=_tcsnicmp(pStrBuffer+i,pToRemove,iParamLen); //__tcsnicmp
  if(iReturn!=0)
  {
     if(blnFound)
        pStrBuffer[j]=pStrBuffer[i];
     j++, i++;
  }
  else   //made a match
  {
     blnFound=true;
     i=i+iParamLen;
     pStrBuffer[j]=pStrBuffer[i];
     j++, i++;
  }
 }while(1);
 if(blnFound)
    pStrBuffer[i-iParamLen]=_T('\0');
 String sr=pStrBuffer;

 return sr;
}


String String::Remove(TCHAR* pStr)
{
 unsigned int i,j,iStrLen,iParamLen;
 TCHAR *pThis, *pThat, *p;
 bool blnFoundBadChar;

 iStrLen=this->LenStr();             //The length of this
 String sr((int)iStrLen);            //Create new String big enough to contain original String (this)
 iParamLen=_tcslen(pStr);            //Get length of parameter (pStr) which contains chars to be removed
 pThis=this->pStrBuffer;
 p=sr.lpStr();
 for(i=0; i<iStrLen; i++)
 {
     pThat=pStr;
     blnFoundBadChar=false;
     for(j=0; j<iParamLen; j++)
     {
         if(*pThis==*pThat)
         {
            blnFoundBadChar=true;
            break;
         }
         pThat++;
     }
     if(!blnFoundBadChar)
     {
        *p=*pThis;
         p++;
        *p=_T('\0');
     }
     pThis++;
 }

 return sr;
}


String String::Retain(TCHAR* pStr)
{
 unsigned int i,j,iStrLen,iParamLen;
 TCHAR *pThis, *pThat, *p;
 bool blnFoundGoodChar;

 iStrLen=this->LenStr();             //The length of this
 String sr((int)iStrLen);            //Create new String big enough to contain original String (this)
 iParamLen=_tcslen(pStr);            //Get length of parameter (pStr) which contains chars to be retained
 pThis=this->pStrBuffer;             //pThis will point to this String's buffer, and will increment through string.
 p=sr.lpStr();                       //p will start by pointing to new String's buffer and will increment through new string
 for(i=0; i<iStrLen; i++)
 {
     pThat=pStr;
     blnFoundGoodChar=false;
     for(j=0; j<iParamLen; j++)
     {
         if(*pThis==*pThat)
         {
            blnFoundGoodChar=true;
            break;
         }
         pThat++;
     }
     if(blnFoundGoodChar)
     {
        *p=*pThis;
         p++;
        *p=_T('\0');
     }
     pThis++;
 }

 return sr;
}


int String::InStr(const TCHAR ch)
{
 int iLen,i;

 iLen=_tcslen(this->pStrBuffer);
 for(i=0;i<iLen;i++)
 {
     if(this->pStrBuffer[i]==ch)
        return (i+1);
 }

 return 0;
}


int String::InStr(const TCHAR* pStr, bool blnCaseSensitive)
{
 int i,iParamLen,iRange;

 if(*pStr==0)
    return 0;
 iParamLen=_tcslen(pStr);
 iRange=_tcslen(pStrBuffer)-iParamLen;
 if(iRange>=0)
 {
    for(i=0;i<=iRange;i++)
    {
        if(blnCaseSensitive)
        {
           if(_tcsncmp(pStrBuffer+i,pStr,iParamLen)==0)   //_tcsncmp
              return i+1;
        }
        else
        {
           if(_tcsnicmp(pStrBuffer+i,pStr,iParamLen)==0)  //__tcsnicmp
              return i+1;
        }
    }
 }

 return 0;
}


int String::InStr(const String& s, bool blnCaseSensitive)
{
 int i,iParamLen,iRange,iLen;

 iLen=_tcslen(s.pStrBuffer);
 if(iLen==0)
    return 0;
 iParamLen=iLen;
 iRange=_tcslen(pStrBuffer)-iParamLen;
 if(iRange>=0)
 {
    for(i=0;i<=iRange;i++)
    {
        if(blnCaseSensitive)
        {
           if(_tcsncmp(pStrBuffer+i,s.pStrBuffer,iParamLen)==0)  //_tcsncmp
              return i+1;
        }
        else
        {
           if(_tcsnicmp(pStrBuffer+i,s.pStrBuffer,iParamLen)==0) //__tcsnicmp
              return i+1;
        }
    }
 }

 return 0;
}


void String::LTrim()
{
 unsigned int i,iCt=0,iLenStr;

 iLenStr=this->LenStr();
 for(i=0;i<iLenStr;i++)
 {
     if(pStrBuffer[i]==32||pStrBuffer[i]==9)
        iCt++;
     else
        break;
 }
 if(iCt)
 {
    for(i=iCt;i<=iLenStr;i++)
        pStrBuffer[i-iCt]=pStrBuffer[i];
 }
}


void String::RTrim()
{
 unsigned int iCt=0, iLenStr;

 iLenStr=this->LenStr()-1;
 for(unsigned int i=iLenStr; i>0; i--)
 {
     if(this->pStrBuffer[i]==9||this->pStrBuffer[i]==10||this->pStrBuffer[i]==13||this->pStrBuffer[i]==32)
        iCt++;
     else
        break;
 }
 this->pStrBuffer[this->LenStr()-iCt]=0;
}


void String::Trim()
{
 this->LTrim();
 this->RTrim();
}
...finally, last part of Strings.cpp...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
unsigned int String::ParseCount(const TCHAR c)  //returns one more than # of
{                                               //delimiters so it accurately
 unsigned int iCtr=0;                           //reflects # of strings delimited
 TCHAR* p;                                      //by delimiter.

 p=this->pStrBuffer;
 while(*p)
 {
  if(*p==c)
     iCtr++;
  p++;
 }

 return ++iCtr;
}


void String::Parse(String* pStr, TCHAR delimiter)
{
 unsigned int i=0;
 TCHAR* pBuffer=0;
 TCHAR* c;
 TCHAR* p;

 pBuffer=new TCHAR[this->LenStr()+1];
 if(pBuffer)
 {
    p=pBuffer;
    c=this->pStrBuffer;
    while(*c)
    {
     if(*c==delimiter)
     {
        pStr[i]=pBuffer;
        //printf("Assigned pStr[%u] In Parse()\n",i);
        //printf("pStr[%u]=%s\n\n",i,pStr[i].lpStr());
        p=pBuffer;
        i++;
     }
     else
     {
        *p=*c;
        p++;
        *p=0;
     }
     c++;
    }
    pStr[i]=pBuffer;
    delete [] pBuffer;
 }
}


int String::iVal()
{
 return _ttoi(this->pStrBuffer);  //_ttoi
}


String& String::operator=(int iNum)
{
 if(this->iAllowableCharacterCount<16)
 {
    int iNewSize;
    delete [] this->pStrBuffer;
    iNewSize=16;
    pStrBuffer=new TCHAR[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
 }
 _stprintf(this->pStrBuffer,_T("%d"),iNum);

 return *this;
}


String& String::operator=(unsigned int iNum)
{
 if(this->iAllowableCharacterCount<16)
 {
    int iNewSize;
    delete [] this->pStrBuffer;
    iNewSize=16;
    pStrBuffer=new TCHAR[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
 }
 _stprintf(this->pStrBuffer,_T("%d"),iNum);

 return *this;
}


String& String::operator=(long iNum)
{
 if(this->iAllowableCharacterCount<16)
 {
    int iNewSize;
    delete [] this->pStrBuffer;
    iNewSize=16;
    pStrBuffer=new TCHAR[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
 }
 _stprintf(this->pStrBuffer,_T("%ld"),iNum);

 return *this;
}


String& String::operator=(DWORD iNum)
{
 if(this->iAllowableCharacterCount<16)
 {
    int iNewSize;
    delete [] this->pStrBuffer;
    iNewSize=16;
    pStrBuffer=new TCHAR[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
 }
 _stprintf(this->pStrBuffer,_T("%u"),(unsigned)iNum);

 return *this;
}


String& String::operator=(double dblNum)
{
 if(this->iAllowableCharacterCount<16)
 {
    int iNewSize;
    delete [] this->pStrBuffer;
    iNewSize=32;
    pStrBuffer=new TCHAR[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
 }
 _stprintf(this->pStrBuffer,_T("%10.14f"),dblNum);

 return *this;
}


int String::LenStr(void)
{
 return _tcslen(this->pStrBuffer);
}


TCHAR* String::lpStr()
{
 return pStrBuffer;
}


TCHAR String::GetChar(unsigned int iOffset)
{
 return this->pStrBuffer[iOffset-1];
}


void String::SetChar(unsigned int iOneBasedOffset, TCHAR tcChar)
{
 if((int)iOneBasedOffset<=this->iAllowableCharacterCount)
    this->pStrBuffer[iOneBasedOffset-1]=tcChar;
}


void String::Print(bool blnCrLf)
{
 _tprintf(_T("%s"),pStrBuffer);
 if(blnCrLf)
    _tprintf(_T("\n"));
}


String::~String()   //String Destructor
{
 delete [] pStrBuffer;
 pStrBuffer=0;
}
1
2
//frmOutput.h
LRESULT CALLBACK frmOutput(HWND, unsigned int, WPARAM, LPARAM);



1
2
3
4
5
6
7
8
9
10
11
12
13
14
//frmOutput.cpp  All this file consists of is the Window Procedure for the frmOutput
//Window Class.  A window of class frmOutput is created whose sole purpose is to be
//printed to with TextOut().  All default window procedure processing is ok so the
//window procedure frmOutput doesn't have to do anything on its own other than exist
//and pass all messages received onto DefWindowProc().  It has to exist because its
//referenced in the Class Registration code in fnWndProc_OnCreate() where the frmOutput
//class is registered.  
#include <windows.h>
#include "frmOutput.h"

LRESULT CALLBACK frmOutput(HWND hwnd, unsigned int msg, WPARAM wParam,LPARAM lParam)
{
 return (DefWindowProc(hwnd, msg, wParam, lParam));
}



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//StrDemo.h
#define IDC_CONCATENATION       1500
#define IDC_PARSE               1505
#define IDC_LEFT_RIGHT_MID      1510
#define IDC_INSTR               1515


typedef struct    WindowsEventArguments               
{
 HWND             hWnd;                               
 WPARAM           wParam;                             
 LPARAM           lParam;                             
 HINSTANCE        hIns;                               
}WndEventArgs, *lpWndEventArgs;


struct EVENTHANDLER
{
 unsigned int    Code;
 long            (*fnPtr)(lpWndEventArgs);
};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
//Main.cpp
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include "StrDemo.h"
#include "frmOutput.h"
#include "Strings.h"
EVENTHANDLER  EventHandler[3];


long fnWndProc_OnCreate(lpWndEventArgs Wea)
{
 TCHAR szClassName[]=_T("frmOutput");
 WNDCLASSEX wc;
 HWND hCtrl;

 Wea->hIns=((LPCREATESTRUCT)Wea->lParam)->hInstance;
 wc.lpszClassName=szClassName;                          wc.lpfnWndProc=frmOutput;
 wc.cbSize=sizeof (WNDCLASSEX);                         wc.style=CS_DBLCLKS;
 wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);               wc.hInstance=Wea->hIns;
 wc.hIconSm=NULL;                                       wc.hCursor=LoadCursor(NULL,IDC_ARROW);
 wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);  wc.cbWndExtra=0;
 wc.lpszMenuName=NULL;                                  wc.cbClsExtra=0;
 RegisterClassEx(&wc);
 hCtrl=CreateWindow(_T("button"),_T("String Concatenation"),WS_CHILD|WS_VISIBLE,45,20,250,25,Wea->hWnd,(HMENU)IDC_CONCATENATION,Wea->hIns,0);
 hCtrl=CreateWindow(_T("button"),_T("String Parsing And Trimming"),WS_CHILD|WS_VISIBLE,45,55,250,25,Wea->hWnd,(HMENU)IDC_PARSE,Wea->hIns,0);
 hCtrl=CreateWindow(_T("button"),_T("Good Old Left, Right, And Mid!"),WS_CHILD|WS_VISIBLE,45,90,250,25,Wea->hWnd,(HMENU)IDC_LEFT_RIGHT_MID,Wea->hIns,0);
 hCtrl=CreateWindow(_T("button"),_T("But Led's Not Forget InStr( )!"),WS_CHILD|WS_VISIBLE,45,125,250,25,Wea->hWnd,(HMENU)IDC_INSTR,Wea->hIns,0);

 return 0;
}


void btnConcatenation_OnClick(void)
{
 HWND hOutput;
 String s;
 int iY=0;
 HDC hDC;

 hOutput=CreateWindow(_T("frmOutput"),_T("String Class Minipulations In C++"),WS_OVERLAPPEDWINDOW,450,20,305,480,0,0,GetModuleHandle(0),0);
 ShowWindow(hOutput,SW_SHOWNORMAL);
 hDC=GetDC(hOutput);
 for(unsigned int i=65; i<91; i++)
 {
     s=s+i;                                   //65 Is Capital 'A'
     TextOut(hDC,0,iY,s.lpStr(),s.LenStr());  //TextOut() is the Windows Version Of Basic's 'Print'
     iY=iY+16;                                //A Spacing Of 16 Pixels Works Good For Most Normal Sized Fonts
 }
 ReleaseDC(hOutput,hDC);
}


void btnParse_OnClick(void)
{
 String* ptrStrings;
 unsigned int iCt;
 HWND hOutput;
 String s;
 int iY=0;
 HDC hDC;

 hOutput=CreateWindow(_T("frmOutput"),_T("String Class Minipulations In C++"),WS_OVERLAPPEDWINDOW,50,400,310,180,0,0,GetModuleHandle(0),0);
 ShowWindow(hOutput,SW_SHOWNORMAL);
 hDC=GetDC(hOutput);
 s=_T("Frederick,  Mary,  James,  Samuel,  Edward,  Richard,  Michael,  Joseph");  //Create comma delimited text string
 iCt=s.ParseCount(_T(','));                                                        //Count delimited substrings
 ptrStrings = new String[iCt];                                                     //Create array of String Pointers
 s.Parse(ptrStrings,_T(','));                                                      //Parse 's' based on comma delimiter
 for(unsigned int i=0; i<iCt; i++)
 {
     ptrStrings[i].LTrim();  //Comment This Out To See Effect.                     //There are some spaces in delimited strings so remove them
     TextOut(hDC,0,iY,ptrStrings[i].lpStr(),ptrStrings[i].LenStr());               //Output/Print array of strings one at a time
     iY=iY+16;
 }
 delete [] ptrStrings;
 ReleaseDC(hOutput,hDC);
}


void btnLeftRightMid_OnClick(void)
{
 HWND hOutput;
 String s1,s2;
 HDC hDC;

 hOutput=CreateWindow(_T("frmOutput"),_T("String Class Minipulations In C++"),WS_OVERLAPPEDWINDOW,800,100,325,250,0,0,GetModuleHandle(0),0);
 ShowWindow(hOutput,SW_SHOWNORMAL);
 hDC=GetDC(hOutput);
 s1=_T("George Washington Carver");
 s2=s1.Left(6);
 TextOut(hDC,20,20,s1.lpStr(),s1.LenStr());
 TextOut(hDC,20,40,s2.lpStr(),s2.LenStr());
 s2=s1.Mid(8,10);
 TextOut(hDC,20,60,s2.lpStr(),s2.LenStr());
 s2=s1.Right(6);
 TextOut(hDC,20,80,s2.lpStr(),s2.LenStr());
 ReleaseDC(hOutput,hDC);
}


void btnInStr_OnClick(void)
{
 String s1,s2,s3;
 HWND hOutput;
 int iOffset;
 HDC hDC;

 hOutput=CreateWindow(_T("frmOutput"),_T("String Class Minipulations In C++"),WS_OVERLAPPEDWINDOW,550,575,600,240,0,0,GetModuleHandle(0),0);
 ShowWindow(hOutput,SW_SHOWNORMAL);
 hDC=GetDC(hOutput);
 s1=_T("InStr('Some Text') Locates The One Based Offset Of Where Something Is At.");
 TextOut(hDC,0,0,s1.lpStr(),s1.LenStr());
 s1=_T("C++ Can Be A Real Pain In The Butt To Learn!");
 TextOut(hDC,0,16,s1.lpStr(),s1.LenStr());
 iOffset=s1.InStr(_T("real"),false);
 s2=iOffset;
 s3 =_T("The Offset Of 'real' In The Above String Is ");
 s3 = s3 + s2 + _T('.');
 TextOut(hDC,0,32,s3.lpStr(),s3.LenStr());
 s1=_T("Note In The Above Code We Used The Case Insensitive Version Of InStr().");
 TextOut(hDC,0,48,s1.lpStr(),s1.LenStr());
 s1=_T("We Also Learned How Easy It Is To Convert Numbers To Strigs!");
 TextOut(hDC,0,64,s1.lpStr(),s1.LenStr());
 s1=_T("Let's See If We Can Remove() 'Real' From Our 1st String!");
 TextOut(hDC,0,80,s1.lpStr(),s1.LenStr());
 s1=_T("C++ Can Be A Real Pain In The Butt To Learn!");
 TextOut(hDC,0,96,s1.lpStr(),s1.LenStr());
 s1.Remove(_T("Real"),true);
 TextOut(hDC,0,112,s1.lpStr(),s1.LenStr());
 s1=_T("Dieses Program funktioniert aber sehr gut, nicht wahr?");
 TextOut(hDC,0,128,s1.lpStr(),s1.LenStr());
 s2=22.0/7.0;
 s1=_T("dblPi = ");
 s1=s1+s2;
 TextOut(hDC,0,144,s1.lpStr(),s1.LenStr());
 s2.GetModulePath();
 s1=_T("String::GetModulePath() Returns ") + s2;
 TextOut(hDC,0,160,s1.lpStr(),s1.LenStr());
 ReleaseDC(hOutput,hDC);
}


long fnWndProc_OnCommand(lpWndEventArgs Wea)
{
 switch(LOWORD(Wea->wParam))
 {
  case IDC_CONCATENATION:
    btnConcatenation_OnClick();
    break;
  case IDC_PARSE:
    btnParse_OnClick();
    break;
  case IDC_LEFT_RIGHT_MID:
    btnLeftRightMid_OnClick();
    break;
  case IDC_INSTR:
    btnInStr_OnClick();
    break;
 }

 return 0;
}


long fnWndProc_OnClose(lpWndEventArgs Wea)
{
 DestroyWindow(Wea->hWnd);
	PostQuitMessage(0);

 return 0;
}


void AttachEventHandlers(void)
{
 EventHandler[0].Code=WM_CREATE,    EventHandler[0].fnPtr=fnWndProc_OnCreate;
 EventHandler[1].Code=WM_COMMAND,   EventHandler[1].fnPtr=fnWndProc_OnCommand;
 EventHandler[2].Code=WM_CLOSE,     EventHandler[2].fnPtr=fnWndProc_OnClose;
}


LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam,LPARAM lParam)
{
 WndEventArgs Wea;

 for(unsigned int i=0; i<3; i++)
 {
     if(EventHandler[i].Code==msg)
     {
        Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*EventHandler[i].fnPtr)(&Wea);
     }
 }

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


int WINAPI WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
 TCHAR szClassName[]=_T("StrDemo");
 WNDCLASSEX wc;
 MSG messages;
 HWND hWnd;

 AttachEventHandlers();
 wc.lpszClassName=szClassName;                wc.lpfnWndProc=fnWndProc;
 wc.cbSize=sizeof (WNDCLASSEX);               wc.style=CS_DBLCLKS;
 wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);     wc.hInstance=hIns;
 wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION);  wc.hCursor=LoadCursor(NULL,IDC_ARROW);
 wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;    wc.cbWndExtra=0;
 wc.lpszMenuName=NULL;                        wc.cbClsExtra=0;
 RegisterClassEx(&wc);
 hWnd=CreateWindow(szClassName,_T("StrDemo"),WS_OVERLAPPEDWINDOW,20,20,350,210,HWND_DESKTOP,0,hIns,0);
 ShowWindow(hWnd,iShow);
 while(GetMessage(&messages,NULL,0,0))
 {
  TranslateMessage(&messages);
  DispatchMessage(&messages);
 }

 return messages.wParam;
}


Finally, my original program above that creates the four Windows and displays mouse movements, typed text, etc., that was originally a PowerBASIC program I converted to C++. I code in C++ and PowerBASIC exactly the same. About the only difference is I don’t have to put a semicolon at the end of my PowerBASIC programs! Only kidding there! There are some slight differences, but not much. In PowerBASIC I didn’t have to create a String Class, of course, because all that and much more is handled natively by the language. I use C++ only for my Windows CE work because PowerBASIC doesn’t produce a Windows CE compiler. Also, I experiment with COM a lot, and I use C++ for that frequently because the doumentation is in C++. Here is what that Typer program looks like in PowerBASIC using my message cracker routines with function pointers like you saw above in the class framework comparison. It compiles a good bit smaller than the C++ versions too…
closed account (3pj6b7Xj)
That is just awesome! But I see the code is massive!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#Compile Exe   "Form6"               '19968 bytes
#Include       "Win32api.inc"

Type MessageHandler                  'This Type Associates a message with the
  wMessage As Long                   'address of the message handler which
  fnPtr    As Dword                  'handles it.  Messages are usually fairly
End Type                             'low numbers, e.g., WM_PAINT is 15.

Global MsgHdlr() As MessageHandler

Type WndEventArgs               'Type for passing window procedure parameters.  Allows me
  wParam           As Long      'to shorten parameter list.  .NET does this all the time.
  lParam           As Long      'See, for example pea for PaintEventArgs in the OnPaint()
  hWnd             As Dword     'Message Handler.
End Type

Type tagInstanceData   'User Defined Type for persisting or storing message
  wWidth   As Word     'information across invocations of fnWndProc.  It is
  wHeight  As Word     'necessary to persist this data because such items of
  wX       As Word     'information as, for example, the window height and
  wY       As Word     'width would be lost after they were obtained in a
  xPos     As Word     'WM_SIZE message after that message handler was exited.
  yPos     As Word     'Then, when a WM_PAINT message was received and it was
  wCharHt  As Word     'desired to update the window with this newly acquired
  szText   As Asciiz*128  'information, it would be gone.
End Type

'Model procedure showing function signature of all message handlers.
Declare Function fnPtr(wea As WndEventArgs) As Long


Function fnWndProc_OnCreate(wea As WndEventArgs) As Long
  Local ptrInstanceData As tagInstanceData Ptr
  Local tm As TEXTMETRIC      'When the main program window is created
  Local hHeap As Dword        'HeapAlloc() is called and a memory request is
  Local hDC As Dword          'made for enough bytes to store a tagInstanceData
                              'Type.  If HeapAlloc()returns a non-zero number
  hHeap=GetProcessHeap()      '(a valid address), a call to GetTextMetrics() is
  ptrInstanceData= _          'made to obtain the height of a character for text
  HeapAlloc _                 'spacing purposes.  Then a Call to SetWindowLong()
  ( _                         'stores the address of the memory acquired by
    hHeap, _                  'HeapAlloc() into the four cbWndExtra bytes of
    %HEAP_ZERO_MEMORY, _      'extra window storage in the Window Class
    SizeOf(tagInstanceData) _ 'structure requested when the "Form2B" Class was
  )                           'Registered in WinMain().
  If ptrInstanceData Then
     hDC=GetDC(wea.hWnd)
     Call GetTextMetrics(hDC,tm)
     Call ReleaseDC(wea.hWnd,hDC)
     @ptrInstanceData.wCharHt=tm.tmHeight
     Call SetWindowLong(wea.hWnd,0,ptrInstanceData)
     Local iCntr As Long
     iCntr=GetClassLong(wea.hWnd,0)
     Incr iCntr
     Call SetClassLong(wea.hWnd,0,iCntr)
  Else
     fnWndProc_OnCreate=-1
  End If
  
  fnWndProc_OnCreate=0
End Function


Function fnWndProc_OnMouseMove(wea As WndEventArgs) As Long  'If a WM_MOUSEMOVE
  Local ptrInstanceData As tagInstanceData Ptr   'message is received a pointer
                                                 'to a tagInstanceData Type is
  ptrInstanceData= _                             'declared.  This variable is
  GetWindowLong _                                'initialized with the address
  ( _                                            'stored in the cbWndExtra bytes.
    wea.hWnd, _                                  'The mouse coordinates are
    0 _                                          'extracted from the lParam
  )                                              'parameter and 'persisted'
  @ptrInstanceData.wX=LoWrd(wea.lParam)          'in the allocated memory using
  @ptrInstanceData.wY=HiWrd(wea.lParam)          'pointer indirection techniques.
  Call InvalidateRect _                          'Finally, an InvalidateRect()
  ( _                                            'Call is made.  This will force
    wea.hWnd, _                                  'a WM_PAINT message to be sent
    ByVal %NULL, _                               'to this Window Procedure in a
    %TRUE _                                      'completely separate WndProc()
  )                                              'Call or invocation.  At that
                                                 'point in the WM_PAINT handler
  fnWndProc_OnMouseMove=0                        'the mouse coordinates will be
End Function                                     'extracted and displayed.


Function fnWndProc_OnSize(wea As WndEventArgs) As Long  'The same pattern
  Local ptrInstanceData As tagInstanceData Ptr   'described just above for the
                                                 'WM_MOUSEMOVE message will be
  ptrInstanceData= _                             'repeated here in WM_SIZE logic
  GetWindowLong _                                'and, in all the other message
  ( _                                            'handlers.  The only thing that
    wea.hWnd, _                                  'will be different is the data
    0 _                                          'transferred in the wParam and
  )                                              'lParam variables, as that is
  @ptrInstanceData.wWidth=LoWrd(wea.lParam)      'dependent on the message
  @ptrInstanceData.wHeight=HiWrd(wea.lParam)     'itself.  The repeating pattern
  Call InvalidateRect(wea.hWnd,ByVal %NULL,%TRUE)'will be to obtain the pointer
                                                 'from the cbWndExtra bytes,
  fnWndProc_OnSize=0                             'store data there, then force
End Function                                     'a paint msg. to display data.


Function fnWndProc_OnChar(wea As WndEventArgs) As Long
  Local ptrInstanceData As tagInstanceData Ptr

  ptrInstanceData=GetWindowLong(wea.hWnd,0)
  @ptrInstanceData.szText=@ptrInstanceData.szText+Chr$(wea.wParam)
  Call InvalidateRect(wea.hWnd,ByVal %NULL,%TRUE)

  fnWndProc_OnChar=0
End Function


Function fnWndProc_OnLButtonDown(wea As WndEventArgs) As Long
  Local ptrInstanceData As tagInstanceData Ptr

  ptrInstanceData=GetWindowLong(wea.hWnd,0)
  If wea.wParam=%MK_LBUTTON Then
     @ptrInstanceData.xPos=LoWrd(wea.lParam)
     @ptrInstanceData.yPos=HiWrd(wea.lParam)
     Call InvalidateRect(wea.hWnd,ByVal 0,%TRUE)
  End If

  fnWndProc_OnLButtonDown=0
End Function 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
Function fnWndProc_OnPaint(wea As WndEventArgs) As Long
  Local ptrInstanceData As tagInstanceData Ptr
  Local szLine As Asciiz*64
  Local ps As PAINTSTRUCT     'This procedure will be called right after
  Local hDC As Long           'window creation, and every time an
                              'InvalidateRect() call is made when the mouse is
  hDC=BeginPaint(wea.hWnd,ps) 'moved over the window, the window is being
  ptrInstanceData=GetWindowLong(wea.hWnd,0)       'resized, a char key is
  szLine= _                                       'pressed, or a left mouse down
  "MouseX="+Trim$(Str$(@ptrInstanceData.wX)) & _  'is received.  Before the
  "  MouseY="+Trim$(Str$(@ptrInstanceData.wY))    'InvalidateRect() call is made
  TextOut(hDC,0,0,szLine,Len(szLine))             'the data from the specific
  szLine= _                                       'handler that is to be
  "@ptrInstanceData.wWidth="+ _                   'persisted across WndProc()
  Trim$(Str$(@ptrInstanceData.wWidth)) & _        'invocations will be stored in
  " @ptrInstanceData.wHeight=" + _                'the memory pointed to by the
  Trim$(Str$(@ptrInstanceData.wHeight))           'cbWndExtra bytes pointer.
  TextOut _                                       'That pointer will then be
  ( _                                             'retrieved here in WM_PAINT,
    hDC, _                                        'and the data in its totality
    0, _                                          'displayed to the window.  It
    16, _                                         'is one awesome machine.
    szLine, _
    Len(szLine) _
  )
  TextOut(hDC,0,32,@ptrInstanceData.szText,Len(@ptrInstanceData.szText))
  If @ptrInstanceData.xPos<>0 And @ptrInstanceData.yPos<>0 Then
     szLine= _
     "WM_LBUTTONDOWN At (" & Trim$(Str$(@ptrInstanceData.xPos)) & _
     "," & Trim$(Str$(@ptrInstanceData.yPos)) & ")"
     Call TextOut _
     ( _
       hDC, _
       @ptrInstanceData.xPos, _
       @ptrInstanceData.yPos, _
       szLine, _
       Len(szLine) _
     )
     @ptrInstanceData.xPos=0 : @ptrInstanceData.yPos=0
  End If
  Call EndPaint(wea.hWnd,ps)

  fnWndProc_OnPaint=0
End Function


Function fnWndProc_OnDestroy(Wea As WndEventArgs) As Long  'The most noteworthy
  Local ptrInstanceData As tagInstanceData Ptr   'event that must occur here is
  Local hHeap As Dword,blnFree As Dword          'that the memory allocated in
  Local iCtr As Long                             'WM_CREATE, a pointer to which
                                                 'is stored in the cbWndExtra
  hHeap=GetProcessHeap()                         'bytes, must be released or
  ptrInstanceData=GetWindowLong(wea.hWnd,0)      'freed.  HeapFree() is used for
  If ptrInstanceData Then                        'that purpose.  Note that since
     blnFree=HeapFree(hHeap,0,ptrInstanceData)   'four windows of class Form6
     MsgBox _                                    'were created in WinMain(), and
     ( _                                         'four separate memory allocations
       "blnFree=" & _                            'were made for the four WM_CREATE
       Trim$(Str$(IsTrue(blnFree))) _            'messages, four deallocations
     )                                           'will need to be done - one for
     MsgBox _                                    'each window's data.  A counter
     ( _                                         'variable was stored in the class
       "Always Make Sure Memory Deallocates!  Memory Leaks Are Bad!" _
     )                                           'extra bytes to keep track of how
  End If                                         'many windows of the class were
  iCtr=GetClassLong(Wea.hWnd,0)                  'created, and when that counter
  Decr iCtr                                      'decrements to zero through
  If iCtr=0 Then                                 'WM_DESTROY messages, a
     Call PostQuitMessage(0)                     'PostQuitMessage() needs to be
  Else                                           'stuck in the program's message
     Call SetClassLong(Wea.hWnd,0,iCtr)          'quene so the thing won't hang
  End If                                         'in memory in a loop with no
                                                 'live windows.
  fnWndProc_OnDestroy=0
End Function


Sub AttachMessageHandlers() 'Here is where the numeric value of a specific message that
  ReDim MsgHdlr(6) As MessageHandler  'is going to be handled in this program is associated
                                      'with the actual address of the message handler.
  MsgHdlr(0).wMessage=%WM_CREATE      : MsgHdlr(0).fnPtr=CodePtr(fnWndProc_OnCreate)
  MsgHdlr(1).wMessage=%WM_MOUSEMOVE   : MsgHdlr(1).fnPtr=CodePtr(fnWndProc_OnMouseMove)
  MsgHdlr(2).wMessage=%WM_SIZE        : MsgHdlr(2).fnPtr=CodePtr(fnWndProc_OnSize)
  MsgHdlr(3).wMessage=%WM_CHAR        : MsgHdlr(3).fnPtr=CodePtr(fnWndProc_OnChar)
  MsgHdlr(4).wMessage=%WM_LBUTTONDOWN : MsgHdlr(4).fnPtr=CodePtr(fnWndProc_OnLButtonDown)
  MsgHdlr(5).wMessage=%WM_PAINT       : MsgHdlr(5).fnPtr=CodePtr(fnWndProc_OnPaint)
  MsgHdlr(6).wMessage=%WM_DESTROY     : MsgHdlr(6).fnPtr=CodePtr(fnWndProc_OnDestroy)
End Sub


Function WndProc(ByVal hWnd As Long,ByVal wMsg As Long,ByVal wParam As Long,ByVal lParam As Long) As Long
  Local wea As WndEventArgs  'Every time a message is retrieved from the message
  Register iReturn As Long   'loop in WinMain() and ends up here, this For loop
  Register i As Long         'will cycle through all the messages it wants to
                             'handle to see if it can make a match.  If it makes
  For i=0 To 6               'a match the message handler will be called through
    If wMsg=MsgHdlr(i).wMessage Then     'a UDT array of function pointers.
       wea.hWnd=hWnd: wea.wParam=wParam: wea.lParam=lParam
       Call Dword MsgHdlr(i).fnPtr Using fnPtr(wea) To iReturn
       WndProc=iReturn
       Exit Function
    End If
  Next i

  WndProc=DefWindowProc(hWnd,wMsg,wParam,lParam)
End Function


Function WinMain(ByVal hIns As Long,ByVal hPrevIns As Long,ByVal lpCmdLine As Asciiz Ptr,ByVal iShow As Long) As Long
  Local szClassName As Asciiz*16
  Local wc As WndClassEx
  Local hWnd,i As Dword
  Local Msg As tagMsg

  Call AttachMessageHandlers()  'Attach User Defined Type array of messages and function pointers.
  szClassName="Form6"           'Note that in the WndClassEx Type the cbWndExtra bytes were set to four.
  wc.cbSize=SizeOf(wc)                               : wc.style=0
  wc.lpfnWndProc=CodePtr(WndProc)                    : wc.cbClsExtra=4
  wc.cbWndExtra=4                                    : wc.hInstance=hIns
  wc.hIcon=LoadIcon(%NULL,ByVal %IDI_APPLICATION)    : wc.hCursor=LoadCursor(%NULL,ByVal %IDC_ARROW)
  wc.hbrBackground=GetStockObject(%WHITE_BRUSH)      : wc.lpszMenuName=%NULL
  wc.lpszClassName=VarPtr(szClassName)               : wc.hIconSm=LoadIcon(%NULL,ByVal %IDI_APPLICATION)
  Call RegisterClassEx(wc)
  For i=1 To 4  'create four main program windows
    hWnd=CreateWindowEx(0,szClassName,"Form6",%WS_OVERLAPPEDWINDOW,%CW_USEDEFAULT,%CW_USEDEFAULT,325,300,%HWND_DESKTOP,0,hIns,ByVal 0)
    Call ShowWindow(hWnd,iShow)
  Next i
  While GetMessage(Msg,%NULL,0,0)
    Call TranslateMessage(Msg)
    Call DispatchMessage(Msg)
  Wend
  
  WinMain=msg.wParam
End Function


closed account (3pj6b7Xj)
Oh wow, I understand your powerBASIC code, it looks well like BASIC! My first programming language was BASIC, I also know Visual BASIC which is probably the same thing but I sort of don't use visual basic because I feel its too easy, C++ seemed more challenging in that I had total control of how I wanted my application to function.

Thanks freddie, very interesting to see the powerBASIC code, your right, its not much different than C++ code, perhaps its because the way the API was designed...I believe it would look similar in any other programming language with some slight differences.
Well, that's why I have a MINIMAL, a SMALL, and a FAST version. The FAST version has a sizeof(String) = 8 because it has an additional iAllowableStringSize member to keep track of its allocation, and most of the members have to then do a compare against it to determine if the existing allocation is sufficient to handle whatever is being done to the string, or if the existing string has to be released, a new larger allocation made, and the original string copied to it. I havn't gotten around to benchmarking or testing it, but some time I'd like to to see if all the extra work I went to matters.

Over in the PowerBASIC forums there are piles of asm speed freaks there and I saw a particularly vicious test sample I'd like to compare my string classes against to see how it measures up to PowerBASIC's string handling. There were a lot of string re-sizings involved. Memory allocations/de-allocations are the bottleneck in anything of this sort.
closed account (3pj6b7Xj)
I see, you've got me interested in this stuff. A good string class is key to being successful in making ANY program, as most programs use strings almost 80% of the time! I know I could just use someone else's string class but if I make my own and not cheat myself, there is a lot I can learn from it. This will make a very nice project. :) I've been married to the string class in the STL for quite some time, maybe its time to do one of my own.
In any given year I probably spend about half my time doing C++ and the other half PowerBASIC. The way it works out though if I'm doing a PowerBASIC project I might be working on it months at a time and when I switch over to C++ I keep forgetting to put ';' at the end of each line! There are pros/cons of each language. Most folks don't use the WinApi style like I do though. PowerBASIC has a proprietary implementation known as DDT - Dynamic Dialog Tools, that produces GUIs real easy. It uses the Windows Dialog Engine just like a lot of C/C++ coders do to make quick and dirty apps. Basically, I dislike the Windows Dialog engine so I do everything pretty much hard core Api CreateWindowEx style.

All us hard core Api and COM types pretty much hang out here....

http://www.jose.it-berater.org/smfforum/index.php


I've a board there with a few tutorials on it some have found useful. I believe the derivatiuon of that String Class is there somewhere. The man who runs the board is widely considered to be a genius. I doubt very much anyone at Microsoft has a deeper knowledge of the Component Object Model ( COM ) than he.

closed account (3pj6b7Xj)
Thanks freddie1, I some how share the same views with all the people over there, mostly everyone seems tired that Microsoft has bloated their software with too much crap. I personally hate Visual C++ 2010 because it is overloaded with too much crap that isn't needed...I prefer something simple like Code::Blocks or wxDev-C++ that doesn't deal with all the bloat of .NET and probably the worst is how Microsoft exploits the C++ language and changes it to behave a different way than from what I know.

I could write C++ code that WILL NOT compile under VC++ 2010 but under any other compiler like Dev-C++ or Code::Blocks it works!

I like programming because its fun, entertaining, challenging, great hobby..did I say its already fun? I really don't see myself working for some big software company, I'm what you call, the "independent" programmer who is in it for the fun and if I can make some money from something, sure why not, i'll give it a try but I will never give in to the bloat M$ has brought with .NEt and other stuff.....I learn to live with it....its funny how they try to fix/change what isn't broken.

Perhaps the best thing they ever did was the Win32 API which is very simple.....speaking of which, is there such thing as a win64 API? Not to deviate too much from the conversation.

COM sounds interesting but all this stuff is new to me, I was a DOS programmer, all this is new to me, so you just got me interested in COM and precisely what I can do with it. I'll be messing with the API and all this good stuff I believe for the next year and a half, before I even reach the level of profiency most guys have here, possibly even longer but i'll always be a learner, you learn something new even when you think you have it all down, I like to think that way. :)
Last edited on
closed account (3pj6b7Xj)
I started working on that application that uses the two buttons to display a message when I realize and completely forgot that you create buttons with CreateWindowEx as well, so now I must sit and lol right a class that makes buttons for me....lol lets just see how large this project is going to get, when I have it all completed i'll post the whole thing...
If there's any part of that Class FrameWork code I posted that you are having trouble understanding just give a holler. Some of it worked me pretty hard, and I've been working with this stuff over ten years. I was meaning to post some comments on it but got sidetracked with the String thing.

I was a Microsoft lover up until .NET hit. Their dumping VB6 really hurt. I loved coding VB6. I mean it had a big runtime and all that, but at least versions 5 and 6 were really pretty fast, and database access (I do a lot of DB programming) with DAO and ADO were great. Well, that's all spilt milk and ancient history at this point.

I had taught myself C in the nineties, but I mostly used VB. When the .NET thing hit I did nothing for about 6 months but try to teach myself C# and VB.NET. The nightmareish scenerio that kept haunting me though as I toiled through gigabytes of documentation was after I spent a huge chunk of my life figuring it all out, how long would it be before Microsoft announced the next BIG paradigm shift that would usher in the programming nirvirna and .NET would be 'deprecated'? And I didn't like it either. After I graduated from interpreted basica to compiled QuickBasic, I wasn't about to go back to interpreters.

Soon after that I discovered PowerBASIC (there are a lot of us 'VB Refugees' there) and I havn't looked back. When I first started with PowerBASIC I was tremendously preoccupied with trying to learn how to use all the fancy ActiveX stuff VB had. Its all very possible, but PowerBASIC doesn't hold your hand like VB did. Its just like C++. You've gotta suffer. But what doesn't kill ya makes ya stronger!

Anyway, regarding your comment about my String class being a lot of code, it still seems to compile a lot smaller than the C++ Standard String Class. If we're talking about the same one that is. Here is a test I just ran using my XP SP3 with Code::Blocks 10.05. The first three results are with my three versions of my String Class, and the last with the one you get this way...

#include <string>

std::string strWhatever;

All Tests Done With Code::Blocks Version 10.05 Windows XP SP3
=============================================================

MINIMAL - My String Class //22528 bytes

1
2
3
4
5
6
7
8
9
10
11
12
#include <Windows.h>         //22528 bytes
#include "Strings.h"

int main()
{
 String s1;

 s1="Hello, World!";
 s1.Print(true);

 return 0;
}




SMALL - My String Class //24064 bytes

1
2
3
4
5
6
7
8
9
#include <Windows.h>         //24064 bytes
#include "Strings.h"

int main()
{
 String s1("Hello, World!");
 s1.Print(true);
 return 0;
}



FAST - My String Class //27136 bytes

1
2
3
4
5
6
7
8
9
#include <Windows.h>         //27136 bytes
#include "Strings.h"

int main()
{
 String s1("Hello, World!");
 s1.Print(true);
 return 0;
}




C++ Standard String Class //43520 bytes

1
2
3
4
5
6
7
8
9
#include <cstdio>            //43520 bytes
#include <string>

int main()
{
 std::string s1("Hello, World!");
 printf("%s\n",s1.c_str());
 return 0;
}


I double checked that I had the debug symbols removed for everything, and optimized for small size.

By the way, the thing with VC9 is really strange. It produces much smaller code, but there is a hidden runtime of some sort involved. If I take a VC9 *.exe and try to run it on a Win 2000 machine I get missing dll errors. In the case of a GUI its MSVCP90.dll, and for console programs a different one. I had suspected that for some time, but today I checked it out close and its a definite fact. So that makes it tricky to compare exe sizes.

I don't have VC10. I bought VS 2008 Pro for myself summer before last and it cost me about US $550. Its gonna have to hold up a while for me yet.
Oh, the COM thing. Its just a challenge for me, and there probably isn't anything to be gained through it, but my ultimate goal (and I can retire and be put out to pasture after that if it doesn't kill me first), is to try to figure out how to write all the low level code to create an OLE COM ActiveX control from scratch without any of Microsoft's tools such as MFC or Atl to do it. Its a massively complex undertaking, and I'm not there yet, but I'm getting closer. What I've been able to figure out so far is how to create the visual COM object where I can reference it in either the 'References' dialog in VB6 or the COM tab in .NET, and have those environments provide me with fully working event procedures and so forth, and run the control perfectly well, but I havn't got the thing where I can do drag and drop and have it show up in the toolbox, so on and so forth. Been working on this on and off for years. Over in Jose's Forum I have posted versions of what I've accomplished so far with that. I believe the one version is in C and the other in PowerBASIC.

The fact of the matter though is I ought to be not so stubborn and just give it up. The version I have is quite lean and amounts to a very suitable architecture for putting custom GUI controls in COM objects which can be housed in Dlls and easily used in any language. All the stuff I havn't figurred out is just to support Visual Designers which I don't use anyway.
Last edited on
closed account (3pj6b7Xj)
VC 2008 was pretty good, I only messed with the express version .... VC 2010 I downloaded the express version and tried it out, I do understand its supposed to be very powerful but eats up a lot of system resources drawing its fancy looking user interface.
I'm a lot more into software than hardware, and am usually running old junk. Visual Studio really runs poor for me for that reason. I'm not really into fancy graphics and all that stuff. Folks that are usually like real up to date hardware & software it seems.
Topic archived. No new replies allowed.
Pages: 12