[C++] Creating an edit box with my own procedure?

Pages: 12
Here's what I need: I have many edit boxes, enough to where I needed to use a scroll function so that my window was super long. However, this leads to the issue that I now need a way to scroll the window when I focus on an edit box that isn't currently in view. So I was thinking that if I could define my own procedure, then on focus I could get the position of the window, and if the window is in view, then do nothing, but if not, then I could scroll the scrollbox.

If there is a better way to do this, I'll gladly accept suggestions.

Thanks!
If the edit box isn't in view meesa, how are you focusing on it, programatically? After all, if the user can't see it, how can he, she, or it click on it?

To answer your question about creating your own edit control, the answer is yes, and I do that in custom control grids I create. It gives you finer grained control over what's going on, as, like you say, the Window Procedure is right in your own program code - not within Windows user32.dll. The down side is you have to write a lot of tricky code yourself - unless you can beg, borrow, or steal it somewhere. My home made edit controls are stippped down one liners. Its in my plans to build a more full featured one with copy - past capabilities, and auto-horizontal scrolling. However, I just haven't gotten around to it yet.
They can get to it with [Tab], so there's the issue.

So it sounds like I really don't want to do that then, as I want copy, past, etc., to be available. Do you have a suggestion on how I could go about this?
Its coming back to me now meesa. I believe we were discussing this several weeks ago, and what you are asking is pretty much where we left off then. True?

If I recall I kind of steered you in the direction of using IsDialogMessage() in your message pump in conjunction with TABSTOP styles in the edit controls to make tabbing possible? Maybe I didn't know which way you were headed with this, and I thought that would be the easiest route to get some sort of rough keyboard navagation. But considering what you are asking now, I'm suspecting that may not have been the route to take. In the past when I've taken the IsDialogMessage() / tabstop route I've noticed that the pattern of messages received in the various controls have been altered by Windows. That's actually the point of that architecture. For me to get a finer grain of control over messages received in various child window controls, i.e., to not have various keypress messages pre-processed/intercepted by Windowsd, I had to remove the IsDialogMessage/TABSTOP stuff, and go with pure Window Subclassing. Doing that you'll get every message the control is capable of receiving such as tab key presses, focus messages, any and all keypresses really, including cursor motion keys, etc. You then can act on them in whatever manner is appropriate to the logic of your application.

The way it would work would be as follows. In your subclass procedure which is really just a Window Procedure you would receive through the hWnd, wParam, and lParam parameters the info you need to know to identify the control which just received a message, the key pressed, etc. You would also be able to retrieve your scroll bar info through the SCROLLINFO struct, so you would know the positioning of everything, and your code would need to implement the logic to take action on all this information. I'm talking thousands of lines of code literally. I know because I've done it. You may not have really appreciated the complexity of all this before you began. It is absolutely doable. But its very intricate tedious coding. As I've mentioned, I've implemented this exact sort of thing in Windows CE applications. Its really neat to accomplish it, but quite difficult to do. So you have to ask yourself how bad you want to do it.
Yes, you are correct. I haven't had time to work on it lately, thus the long span.

And yes, your memory is not failing you. Apparently I did not appreciate the complexity either, I knew it would not be a simple task, but a thousand+ lines to make sure the window moves when something is focused was defenetly unexpected. In my mine, it's a few if-else statements and some formulas.

However, I do want to do this. The information I learn will not be in vain, and the result will be rewarding, thus I would appreciate it if you would point me in the right direction on where to start.

BTW, I really appreciate all the help that you've given me. :)
Do you understand Window Subclassing meesa? If you don't it sounds harder than it is.
Nope, I guess that's what I'll be Googling today. I'll report back if I have any issues.
I have a really good (at least I think its good) tutorial on that. Let me find the link...

http://www.jose.it-berater.org/smfforum/index.php?topic=3392.0

Its ProgEx40f. All the code should be there.

Subclassing is a tremendously powerful technique, and really shows off the power and elegance of the underlying architecture of Windows.

As you know, when you create your main program window, you Register a Window Class. The two most important fields of the WNDCLASSEX struct are the lpfnWndProc and szClassName fields. The lpfnWndProc field is a pointer to your Window Procedure. That's how Windows knows how to notify your application when any event occurs.

The standard Windows controls and the Common Controls also have a Window Class, i.e., "edit", "button", etc. You create instances of these controls the same way you create your program's main Window, i.e., a CreateWindow() call. However, unlike with your main program window, the Window Procedures for these pre-defined controls are in various system Dll, likely "user32.dll" or some dependency of that. The beautiful thing about it though is that using the SetWindowLong() Api function you can tell Windows that you want messages to the predefined control to be sent to a Window Procedure you define in your app, where you can take a first crack at the messages, then decide whether or not you want to pass the message on to the real Window Procedure for the control. If that sounds powerful, it indeed is.

Last edited on
It is in that manner that you'll be able to receive TAB keypresses from edit controls in your app. You'll have the keypress information in terms of which key was pressed, and you'll have the hWnd of the control where keypress activity occurred. You'll have only one subclass proc in your app, but it will serve for all your edit controls. What you'll do then is make SetFocus() calls yourself on the edit control you wish to set focus too, instead of having it happen automatically as it is now with the IsDialogMessage / TABSTOP functionality.

If you are only interested in tab key presses it might not take as miuch code as I mentioned. In my data collector program where I have this implemented I also implemented navigation between all the many edit controls through the cursor left - right - up & down keys, and this added a lot to code bulk.

After you think you understand subclassing, I think I can post some of the details of the actual code. It isn't hard - just tedious.
Okay, I have the basic idea of what is going on now. (Thanks for the link) However, there are a few aspects of the code that I don't understand.

The first is: Why does the guy go though that long line of code to get the hInstance rather than just using GetModuleHandle(0) (I think that's the right function)?

Second, what is the point of this message thing:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
long __stdcall fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam,LPARAM lParam)
{
 WndEventArgs Wea;                  //This procedure loops through the EVENTHANDER array
                                    //of structs to try to make a match with the msg parameter
 for(unsigned int i=0; i<3; i++)    //of the WndProc.  If a match is made the event handling
 {                                  //procedure is called through a function pointer -
     if(MainHandler[i].Code==msg)   //(EventHandler[i].fnPtr).  If no match is found the
     {                              //msg is passed onto DefWindowProc().
        Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*MainHandler[i].fnPtr)(&Wea);
     }
 }

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


I understand it's the default procedure, but why does it have to go though that process to send the corrent message? I thought that's what fnButtonWndProc=(WNDPROC)SetWindowLong(hCtl,GWL_WNDPROC,(LONG)fnButtonSubClass); was for?

And lastly, for now, is there a reason that on every SetWindowLong there is something to catch the return value? Wouldn't just doing it once for the first control of the type be enough? Or is it just for uniformity? I'm just curious if it's necessary, I'll probably do it anyways.

The first is: Why does the guy go though that long line of code to get the hInstance rather than just using GetModuleHandle(0) (I think that's the right function)?


There are three or four ways to get the hInstance variable. That guy (who is actually me ) probably figured that since Windows has already handed the hInstance to me as part of the LPCREATESTRUCT, a pointer to which was passed to the WM_CREATE handler in the lParam, it would be somewhat ignorant to ignore it and call another function to get it. I frequently get other info out of the CREATESTRUCT pointer, so its not unnatural for me to get it that way. But you can get it any way that floats your boat.

Second, what is the point of this message thing:
....
.....

That also is unnecessary; however, its important to me to do it that way, for you see, I write very large applications with tens of thousands of lines of code, and its critical to me that the code be very highly modularized so that it can be easily understood and maintained. The standard Win32 technique of using a switch construct to map messages to the code which handles the message becomes more and more unmaintainable as the app size grows beyond beginner type programs such as those in tutorials and books. The technique shown of using a for loop and function pointer setup to map messages to the code which hsandles the message is a technique I first learned from Douglas Boling in his Windows CE books from Microsoft Press. He credits the technique to Ray Duncan who used to write articles for PC Techniques magazine and others I believe.

In any case, all the class frameworks (MFC, OWL, .NET, etc) utilize techniques similiar to this through complicated macros that the users of such frameworks never see. For example, with MFC you'll have ::OnPaint(), OnChar(), etc., event handling procedures created for you through the class framework.

I don't use class frameworks because I like to write all my own code, and yet this technique of routing messages to the procedure which handles them is a crtically important concept. It is one of the reasons Visual Basic became such an immensely popular application generation framework back in the 90s. The programmer only had to fill out the code that would execute in these auto-generated event procedures. Again, a very powerful idea.

What I do only adds 2 or 3 K to an executables size, and once that 2 or 3 K hit is absorbed, you have a framework that is extensible to apps of unlimited size. The Window Procedure will never grow beyond the size of what you posted above, and all code will be added to event procedures.

If you wish to learn about and understand that technique, I explain it in great detail several tutorials before the one you read. The point is, its right in my code; not hidden deep within the class framework somewhere which is where it would be for most users of class frameworks who would just use and take for granted the code auto-generated for them.

Every edit control you wish to subclass has to have the SetWindowLong(GWL_WNDPROC) call made on it so as to reset your custom intercept procedure into the chain of Window Procedures Windows will call on the control. Since in that example program I had one intercept procedure (subclass) for the buttons and one for all the edit controls, I wouldn't have had to store the return value for any beyond the 1st of each type.

Realize though that its a critical step that within the subclass procs, the original Window Procedure within windows has to be called. If you 'eat' the message and don't pass it on, the control will completely stop working.
Last edited on
So would you say one is better over the other? I don't want to use a bad method, even if it works.

So basically, (And I realize this isn't what you code says, since you use a loop) you're using a switch statement and then calling a function for the proper code? Aside from each event being in a function, which you could do with a switch statement, how does this make it easier? After all, don't you have to edit code in three places and add code rather than editing code in one place and adding?

Yes, I understand that it would fail. But like you, I prefer to write my own code, and I don't like just copying. I prefer to know what's happening as much as possible.

BTW, you write good tutorials. Even just skimming looking for what I wanted, I was able to find it.
Last edited on
Actually, the whole derivation of my function pointer setup is described in detail in a separate tutorial here…

http://www.jose.it-berater.org/smfforum/index.php?topic=3391.0

I had forgotten I had it in a separate tutorial instead of buried within another one somewhere. This below is from Douglas Boling’s “Programming Microsoft Windows CE” series of books from Microsoft Press. Douglas Boling is more or less the Charles Petzold of Windows CE programming and he writes books, consults on Windows CE issues, and teaches seminars on it etc. Here is what he has to say about this technique…


One criticism of the typical SDK style of Windows programming has always been the huge switch statement in the window procedure. The switch statement parses the message to the window procedure so that each message can be handled independently. This standard structure has the one great advantage of enforcing a similar structure across almost all Windows applications, making it much easier for one programmer to understand the workings of another programmer’s code. The disadvantage is that all the variables for the entire window procedure typically appear jumbled at the top of the procedure.

Over the years, I’ve developed a different style for my Windows programs. The idea is to break up the WinMain and WinProc procedures into manageable units that can be easily understood and easily transferred to other Windows programs….

I break the window procedure into individual procedures, with each handling a specific message. What remains of the window procedure itself is a fragment of code that simply looks up the message that’s being passed to see whether a procedure has been written to handle that message. If so, that procedure is called. If not, the message is passed to the default window procedure.

This struct divides the handling of messages into individual blocks that can be more easily understood. Also, with greater isolation of one message handling code fragment from another, you can more easily transfer the code that handles a specific message from one program to the next. I first saw this structure described a number of years ago by Ray Duncan in one of his old “Power Programming” columns in ‘PC Magazine’. Ray is one of the ledgends in the field of MS-DOS and OS/2 programming. I’ve since modified the design a bit to fit my needs, but Ray should get the credit for this program structure.


Actually, I had bought that book around 2000 or so, not too long after I had just started to do Sdk style Windows programming, and being on information overload at the time, I just ignored it as being to wierd and complicated. Several years later though, after having written a few major Sdk style Windows programs, and having suffered through exactly the sorts of ills that program structure was designed to prevent, I decided to sink the time into it to understand it. It really didn’t take me that long to figure it out (you have to understand function pointers pretty well though), and when I did I thought, “Wow! This really IS slick!”. I immediately adopted the technique into both my PowerBASIC and C/C++ coding styles. At about that time too I spent a lot of time testing it in terms of speed to see if the seemingly extra code and complexity was slowing down the processing of messages. To my astonishment I found out that using the for loop to loop through the function pointer array was actually processing a hair faster than using a switch construct to map messages to the procedure that handles them. So I’ve done that ever since.

I didn’t exactly copy Douglas Boling’s code. Fact of the matter is, his setup is more preferable to mine. He does this at global scope in his apps…

1
2
3
4
5
6
//Message Dispatch Table For MainWindowProc
const struct decodeUINT MainMessages[]=
{
  WM_PAINT,   DoPaintMain,
  WM_DESTROY, DoDestroyMain
}


That setup would actually be a bit superior to mine in that for me to add a message handler to a program I have to remember to add one to my EventHandler[] array, and change the upper limit on my for loop in WndProc().

His Window Procedure then looks like this…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
 Int I;

 //Search message list to see if we need to handle this message.  If in list, call    
 //procedure…
 for(i=0; i<dim(MainMessages); i++)
 {
      if(wMsg==MainMessages[i].Code)
         return(*MainMessages[i].Fxn)(hWnd,wMsg,wParam,lParam);
 }

 return  DefWindowProc(hWnd, wMsg, wParam, lParam);
}


In the above code ‘dim’ is a macro I can’t seem to dig up right now, but I imagine it simply uses sizeof() to come up with the number of elements in the array of messages/function pointers.

I do believe though that I’m providing accurate information when I state that ALL Windows programming frameworks be it MFC, .NET, I imagine wxWidgets, likely QT – everything, provide this sort of setup buried deep within the framework somewhere that users just take for granted and never see. I know for a fact that its part of MFC and .NET as I’ve worked fairly extensively with those languages. The reason I like this setup is it completely modularizes my code, and I don’t end up with unwieldly switch constructs running on for thousands of lines.

While I’m harping on this topic, let me mention something else that will modularize and improve your code…

DON’T USE GLOBAL VARIABLES!

Not any at all? Nope. None. If you look at any of the code I’ve posted for you, or any in my tutorials, you will see that there are none. This, coupled with modularizing you Window Procedure code, will make your program architecture more extensible. In other words, your application can grow to any size without complexity finally killing it.

But yes, you have to add code in a couple places instead of just one place. But that's essentially the point of it not? If you keep adding it in one place, pretty soon you have an unmanageable pile.


Last edited on
As I read 39d...
This new type is called wchat_t (say wide character type).
Just thought you might want to know about that typo. (I don't even know how I caught that. :) )

Well, the method seems to work, and since it should keep me from issues down the road, I might as well learn it now. I'll get back to you on how all this works out.

DON’T USE GLOBAL VARIABLES!


Not to worry, I've have that drilled into my head with other books as well. However, he uses a struct there. I thought that structs (and classes) weren't global variables. And even you use a global struct in your definition:

1
2
3
4
5
6
/*Other guy*/
const struct decodeUINT MainMessages[]=

/*You*/
struct EVENTHANDLER
EVENTHANDLER  EventHandler[7];


So what is the difference between the two, except that you have to remember to change two numbers that you don't with his method?
Last edited on

...he uses a struct there. I thought that structs (and classes) weren't global variables. And even you use a global struct in your definition:


Anything defined outside of a function or class has module or translation unit scope. However, in Boling's case he is defining the struct as const, so its not really a variable. In my case I didn't do that and in fact assign values to the members of EVENTHANDLER (1st member a define from Windows.h, 2nd member an address of a procedure obtained at program start up in AttachEventHandlers(), so technically they are variables, although I'm initializing them with constants and never changing them afterwards. But you made me think about this (something I havn't done in awhile). I'll probably change over to Boling's technique in the future. In fact, I'll look at it right now...
In that case, I'd like to know what you determine. As I was planning on adapting both methods (Take your method, and put it in a struct so I could work like Boling did) until I realized that Boling was already using a struct.
I just went through one of my scrolling examples and changed it to Boling's method. I'm glad you questioned me on this. His method is better than mine, in that you only have to change things in one place to add a message handler. Try this. I used Code::Blocks 8.02. Release Build compiled to 8192 bytes...

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
//WinTypes.h
#ifndef WINTYPES_H
#define WINTYPES_H


#define dim(x) (sizeof(x) / sizeof(x[0]))

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

long fnWndProc_OnCreate(lpWndEventArgs Wea);
long fnWndProc_OnSize(lpWndEventArgs Wea);
long fnWndProc_OnVScroll(lpWndEventArgs Wea);
long fnWndProc_OnPaint(lpWndEventArgs Wea);
long fnWndProc_OnClose(lpWndEventArgs Wea);

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

const EVENTHANDLER EventHandler[]=
{
 {WM_CREATE,    fnWndProc_OnCreate},
 {WM_PAINT,     fnWndProc_OnPaint},
 {WM_SIZE,      fnWndProc_OnSize},
 {WM_VSCROLL,   fnWndProc_OnVScroll},
 {WM_CLOSE,     fnWndProc_OnClose}
};

#endif 


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
//Main.cpp
#include      <windows.h>
#include      <tchar.h>
#include      <stdio.h>
#include      <string.h>
#include      "WinTypes.h"


long fnWndProc_OnCreate(lpWndEventArgs Wea)     //Offset   What's Stored There
{                                               //================================
 unsigned int iLineCount=50,i;                  //0  -  3  iLineCount
 TCHAR szBuffer[80], szNum[16];                 //4  -  7  ptrPtrBuffer
 TCHAR** ptrPtrBuffer=NULL;                     //8  -  11 cyChar
 TEXTMETRIC tm;
 HDC hDC;

 Wea->hIns=((LPCREATESTRUCT)Wea->lParam)->hInstance;
 SetWindowLong(Wea->hWnd,0,iLineCount);
 ptrPtrBuffer=(TCHAR**)GlobalAlloc(GPTR,sizeof(TCHAR*)*iLineCount);
 SetWindowLong(Wea->hWnd,4,(long)ptrPtrBuffer);
 for(i=0;i<iLineCount;i++)
 {
     _tcscpy(szBuffer,_T("  "));
     _stprintf(szNum,_T("%u"),i);
     _tcscat(szBuffer,szNum);
     _tcscat(szBuffer,_T("  This Is Line #"));
     _tcscat(szBuffer,szNum);
     ptrPtrBuffer[i]=(TCHAR*)GlobalAlloc(GPTR,sizeof(TCHAR)*(_tcslen(szBuffer)+1));
     _tcscpy(ptrPtrBuffer[i],szBuffer);
 }
 hDC=GetDC(Wea->hWnd);
 GetTextMetrics(hDC,&tm);
 SetWindowLong(Wea->hWnd,8,(long)tm.tmHeight);
 ReleaseDC(Wea->hWnd,hDC);

 return 0;
}


long fnWndProc_OnSize(lpWndEventArgs Wea)
{
 int iLinesVisible,iLineCount;
 unsigned int cyChar;
 SCROLLINFO si;

 ZeroMemory(&si, sizeof(SCROLLINFO));
 iLineCount=(unsigned int)GetWindowLong(Wea->hWnd,0);
 cyChar=(unsigned int)GetWindowLong(Wea->hWnd,8);
 iLinesVisible=HIWORD(Wea->lParam)/cyChar;
 si.cbSize = sizeof(SCROLLINFO);
 si.fMask =   SIF_RANGE|SIF_PAGE;
 si.nMin = 0;
 si.nMax = iLineCount-1;
 si.nPage=iLinesVisible;
 if(si.nMax<0)
    si.nMax=0;
 SetScrollInfo(Wea->hWnd,SB_VERT,&si,TRUE);
 if(iLinesVisible<=iLineCount)
    InvalidateRect(Wea->hWnd,NULL,TRUE);

 return 0;
}


long fnWndProc_OnVScroll(lpWndEventArgs Wea)
{
 int iVScrollPos;
 SCROLLINFO si;

 ZeroMemory(&si, sizeof(SCROLLINFO));
 si.cbSize = sizeof(SCROLLINFO);
 si.fMask=SIF_ALL;
 GetScrollInfo(Wea->hWnd,SB_VERT,&si);
 iVScrollPos=si.nPos;
 switch(LOWORD(Wea->wParam))
 {
  case SB_LINEUP:
    if(si.nPos>si.nMin)
       si.nPos--;
    break;
  case SB_PAGEUP:
    si.nPos = si.nPos - si.nPage;
    break;
  case SB_LINEDOWN:
    if(si.nPos<si.nMax)
       si.nPos++;
    break;
  case SB_PAGEDOWN:
    si.nPos = si.nPos + si.nPage;
    break;
  case SB_THUMBTRACK:
    si.nPos=si.nTrackPos;
    break;
 }
 si.fMask = SIF_POS;                           // Set the position and then retrieve it.  Due to adjustments
 SetScrollInfo(Wea->hWnd, SB_VERT, &si, TRUE); // by Windows it may not be the same as the value set.
 GetScrollInfo (Wea->hWnd, SB_VERT, &si);
 if(si.nPos != iVScrollPos)                    // If the position has changed, scroll the window and update it
    ScrollWindow(Wea->hWnd, 0,GetWindowLong(Wea->hWnd,8)*(iVScrollPos-si.nPos), NULL, NULL);

 return 0;
}


long fnWndProc_OnPaint(lpWndEventArgs Wea)
{
 unsigned int iLineCount,i,iStart,iFinish,iLine,iPos;
 TCHAR** ptrPtrBuffer=NULL;
 PAINTSTRUCT ps;
 SCROLLINFO si;
 long cyChar;
 HDC hDC;

 hDC=BeginPaint(Wea->hWnd,&ps);
 iLineCount=(unsigned int)GetWindowLong(Wea->hWnd,0);
 ptrPtrBuffer=(TCHAR**)GetWindowLong(Wea->hWnd,4);
 cyChar=GetWindowLong(Wea->hWnd,8);
 si.cbSize = sizeof(SCROLLINFO);
 si.fMask  = SIF_POS;
 GetScrollInfo(Wea->hWnd, SB_VERT, &si);
 iPos=si.nPos;
 iStart=ps.rcPaint.top/cyChar;
 iFinish=ps.rcPaint.bottom/cyChar;
 for(i=iStart;i<=iFinish;i++)
 {
     iLine=iPos+i;
     if(iLine<iLineCount)
        TextOut(hDC,0,i*cyChar,ptrPtrBuffer[iLine],_tcslen(ptrPtrBuffer[iLine]));
 }
 EndPaint(Wea->hWnd,&ps);

 return 0;
}


long fnWndProc_OnClose(lpWndEventArgs Wea)
{
 unsigned int iLineCount;
 TCHAR** ptrPtrBuffer=NULL;
 unsigned int i;

 iLineCount=(unsigned int)GetWindowLong(Wea->hWnd,0);
 ptrPtrBuffer=(TCHAR**)GetWindowLong(Wea->hWnd,4);
 for(i=0;i<iLineCount;i++)
     GlobalFree(ptrPtrBuffer[i]);
 GlobalFree(ptrPtrBuffer);
 DestroyWindow(Wea->hWnd);
 PostQuitMessage(0);

 return 0;
}


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

 for(unsigned int i=0; i<dim(EventHandler); 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[]=TEXT("ScrollWindow");
 WNDCLASSEX wc;
 MSG messages;
 HWND hWnd;

 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)GetStockObject(WHITE_BRUSH);  wc.cbWndExtra=12;
 wc.lpszMenuName=NULL;                                  wc.cbClsExtra=0;
 RegisterClassEx(&wc);
 hWnd=CreateWindow(szClassName,szClassName,WS_OVERLAPPEDWINDOW|WS_VSCROLL,200,100,300,228,HWND_DESKTOP,0,hIns,0);
 ShowWindow(hWnd,iShow);
 UpdateWindow(hWnd);
 while(GetMessage(&messages,NULL,0,0))
 {
       TranslateMessage(&messages);
       DispatchMessage(&messages);
 }

 return messages.wParam;
}
To be specific, if you need, for example, to add a message handler to your code, you would only need to go to your *.h file and add the #define and address to the EventHandler[] array (which is const). The dim macro would take care of the proper loop limits in your for loop in the Window Procedure. Cool beans!
Okay, so I'm working on adapting this method to my program, and I have two questions..

The first is how does __stdcall work? I see you use a long __stdcall for your WndProc procedure, but rather than a LRESULT CALLBACK. Would you mind clarfying how the two are interchangeable?

The second is why don't you always use the for loop method? You use it on the first procedure, but if it's faster than a switch statement, why not use it everywhere? Such as in the WM_COMMAND sectiion and any other that would use a switch statement?

Also, thanks for meantioning that you compiled that in Code::Blocks. I like the C::B interface much better than MVC++, but I'd never figured out how to be able to get Windows programs to compile. But you inspired me to figure it out, and now I have an interface I like much better. *Happy* :)
I've got this strange 'thing' about obscurities in code (I guess I shouldn't talk, seeing as what I put you through with my bizarre 'message cracker' setup), and like most Windows GUI programmers, when I was starting out I saw all these strange appendages in Windows code such as 'LRESULT', 'CALLBACK', 'WINAPI', etc., ad infinitum, like you never see anywhere else. I started doing a search through the various Windows headers trying to figure out what these things were, and what I found was that most of them were simple type redefinitions of various bona fide C or C++ keywords such as LRESULT = long, 'WINAPI' = __stdcall, 'CALLBACK' = __stdcall, etc. So I just started substituting some of these in place of the #defines & typedefs, and wouldn't you know, everything works fine. In Microsoft's editors __stdcall outlines in blue, and I like pretty colors. If you are serious about Windows programming I'd recommend Charles Petzolds books - he discusses most of these strange defines..

You can only go so far with it though. If you try it in Win64 - it won't work. For example, the various function calling conventions related to stack layout are changed there.

Doug Boling does use the loop - lookup method in his WM_COMMAND parsing. I never bothered because my WM_COMMAND handlers never got too much out of hand in terms of size - maybe a few screens at most, but not thousands of lines.

Two summers ago I bought myself VS Pro 2008 - but I find CodeBlocks easier to use. I tend to not go for real heavyweight editors. I'm not even beyond using Notepad occasionally, and compiling from the command line. Glad you got CodeBlocks working. If you are interested, I can show you how to do command line compiling with it.
Pages: 12