Windows Subclassing

Pages: 12
I was wondering if someone could explain the concept of "Windows Subclassing". Or, could someone point me to a good article or tutorial on the subject. Petzold mentions it in his book. But, he seems to just gloss-over the subject.
In object oriented programming, you can create a class the uses another on as a base class. You get all the stuff the base class does, but you tweak the behaviour in your class; add a little, remove a little. In any event, most of the work is done in the base class and inheritance allows you to reuse that code. Your class is said to be a derived class.

The Windows GUI is object oriented in the original pure sense of the term. The same applies. There's a basic Window class. That class is derived from to make specialized all the other Window like things you see on the screen.

A lot of the specialised classes are provided by Windows itself, some by 3rd party vendors. But you may also create your own. There are two ways of doing this.
1. define the class to be derived from some existing base class
2. hook message handlers for some existing class and add your behaviour by handling new messages.

Finally, what I mean by the Windows GUI being pure, is this. A Object exists and is a member of some Class. Each instance of a Class can only be communicated with by sending and receiving messages. That's basically the Actor Model. Object Orientation adds inheritance. And that's exactly what Windows uses; instances of object that you can only communicate with using messages.
I liked your reply kbw! I have always tried to explain to C++ coders learning C based Win32 coding, i.e., a la Petzold, that the Win32 low level API is very Object Oriented. I think that folks don't always recognize that when they see C based OOP. The significance.....if as a C++ coder you look at it and think it doesn't look like OOP or C++, and then decide to build a C++ class framework around it, all you are doing is creating wrappers that won't add one bit of additional functionality to the functionality that is already there. Might make some stuff a bit easier to do or improve some syntax, which I admit has some merit, but there will be more code to compile, might be a bit slower, and it'll definitely be more bloated and bigger. Just my opinions, of course.

In any case, on May 5 member Malibor started a post in which I posted a compilable program with a demonstration of subclassing where I subclassed a button control to alter its behavior. Might help a bit....

http://www.cplusplus.com/forum/windows/270231/
Thanks kbw and freddie1. Sorry that I was away for a couple days. I had other obligations.

I suppose that my specific question involves "hooking". I recognize the code, and I understand class inheritance. However, I was wondering if I could get a step-by-step explanation of how each bit of code works, in hooking. That way, I can have a good mental picture of what is happening. Right now, it's all just "black box".

Freddie1, I like your for-loop system in WndProc(). What caught my attention, was the way it returned function pointers to WinMain. You must be my age. In the old days of DOS, the return of function pointers was used in cases of GUI items---menus, buttons, etc. It helped to avoid problems with nesting and recursion (functions calling functions, calling functions, calling functions...). I always wondered why this wasn't done in Windows (or why programming wasn't taught that way). I assume that WinMain executes those functions, once the pointer is returned.

If you get the chance, I would like to ask you about a couple items of code, in that program.
Hi anachronon!

Yea, I'm retired now, but learned Windows Coding back in the 90's with Petzold's Windows 95 book. You're right, he didn't go into any more detail than necessary for that one Colors program. At the time I was just moving from DOS and I was trying to emulate typical DOS program behavior in Windows, which actually had a different user interface model in terms of moving focus between controls. Typically in DOS data entry programs one used the [ENTER] key to move to the next data entry field. In Windows the [TAB] key was more commonly used. Like many, once I learned subclassing from Petzold's limited presentation of it, I had the answer to my prayers, so to speak, in terms of how to make Windows programs act like DOS data entry programs. By subclassing the edit control, one could pick up presses of the [ENTER] key, and set focus to the next control. I later came to understand a lot of coders from that era were doing the same as me. Of course, later I learned it was somewhat misguided what we were doing, but in any case I spent a lot of time working with and learning subclassing, and its something I used in nearly every program I had occasion to write. Back in those days when we used to get Microsoft's compilers, IDEs and build tools on CDs, i.e., VStudio 98, etc., one always had a MSDN disk, and on it would be a lot of articles on subclassing, superclassing, hooking, etc, etc. I would think those articles would still be online at MSDN and elsewhere.

But basically the idea of subclassing is fairly straight-foreward, conceptually. When you Register a Window Class, for example registering your main window class in WinMain(), you then have a Window Procedure also in your program that will receive messages for every interaction of the user or operating system with your Main Window. But, if you put some control on your main Form, i.e., an edit control or whatever, then you are using a window of a Window Class not created by you, but one rather created by Microsoft and whose Window Procedure is not in your program but rather 'within' the operating system itself. And when you interact with that control in any way, i.e., typing text into it or using any functionality of it, the messages generated go to the Window Procedure internally within Windows - not the one in your program for your main window. In that internal Window Procedure which you do not have access to, some of the messages are indeed filtered and then sent to the Window Procedure in your program riding along with WM_COMMAND messages. But it might not be enough to satisfy the needs of your program - particularly if you are attempting to do something unusual, e.g., my desire to move focus to another control after the user hits the [ENTER] key.

That's where subclassing comes in. It allows one to 'hook' into the internals of the control. With subclassing, one can direct Windows to first send all messages targeted to the control in question to a procedure in one's program with the same function signature as a Window Procedure. With SetWindowLong() or SetWindowLongPtr() one can specify the name of one's subclass procedure where Windows the OS is being directed to send messages. And these messages will be received then in one's program before they are processed by the control's internal Window Procedure, i.e., the one you don't have in your program because Windows owns it. In that way one can modify the behavior of the control for one's own use - diabolical or otherwise.

Hope this helps. Feel free to ask any questions. I've always loved coding for Windows. Definitely my favorite OS. Unfortunately for me, since I've retired, keeping up with my property and my banjo picking seems to have pushed coding on the back burner. But I keep trying to get back into it!
Last edited on
Thanks freddie1. Let's see if I understand this bit of code correctly (from your other thread):

1
2
3
 hBtn=CreateWindowEx(0,_T("button"),_T("Button #1"),WS_CHILD|WS_VISIBLE,105,70,90,25,Wea.hWnd,(HMENU)BUTTON_1,Wea.hIns,0);
 btnWndProc=(WNDPROC)SetWindowLongPtr(hBtn,GWLP_WNDPROC,(LONG_PTR)fnBtnSubClass);                                           
 SetWindowLongPtr(Wea.hWnd,0,(LONG_PTR)btnWndProc);


The first line creates the child button window. The second line redirects the commands to the custom function "fnBtnSubClass()". The third line returns control to the child button window.

Is there a difference in using "SetWindowLong()" and "SetWindowLongPtr()"? Petzold uses "SetWindowLong()". Further, he uses "CallWindowProc()" for his third line. It took a bit to chase-down Petzold's three commands, as they are scattered throughout the program.

One other question, on your function declarations:

 
long fnWndProc_OnCreate (WndEventArgs& Wea);


"WndEventArgs" is a structure. But, what about the ampersand ("&")? Is than an "address of" operator? How does that differ from declaring a pointer with an asterisk ("*")?
Pretty close, but not exactly. That 3rd line...

 
SetWindowLongPtr(Wea.hWnd,0,(LONG_PTR)btnWndProc);


...places within the WNDCLASSEX::cbWndExtra bytes of my main window ( the one registered in WinMain() ) the address of the internal button Window Procedure - the one I mentioned above which you don't have access to unless you perform this subclass operation. Quite a bit going on there. Let me elaborate.

The reason I did that, i.e., use SetWindowLongPtr to store this value, i.e., the address of the internal button class Window Procedure, is because my coding philosophy is to avoid using global variables at all costs. In some situations it isn't possible, but in GUI applications in particular it almost always is possible. This issue of global variables is a philosophic design thing. Some very good coders use them sparingly and in the right places (Petzold would be in that category), and others, such as myself don't.

Before I get into how the variable btnWndProc is used with Subclassing, let me first allude back to some of the statements kbw and I made at the outset of this post. It involves Object Oriented Programming and the C based Win32 Api. In C++ one creates a Class and one then has the opportunity of specifying private, protected, or public member variables as member variables of the Class. With C, while one can use a struct to create objects, the syntax and use of member functions in particular is not nearly so neat as in C++. When Microsoft created the Windows Api with 16 bit Windows back in the 1980s, C++ was not really fully developed, and while the advantages of OOP were understood, OOP had to be implemented in C - the dominent systems programming language of the time. So, the WNDCLASS::cbWndExtra Bytes (see WNDCLASS or RegisterClass in the MSDN docs) were a way Microsoft provided to allow coders to add 'instance data', i.e., member variables, to instances of a class. That would be the main window of the program we are discussing. If you look in WinMain() of my program you'll see this line....

 
wc.cbWndExtra=sizeof(void*); 


With that line I'm specifying to Windows that I want 4 bytes of extra storage in my Window Class for my main window if the app is built as a 32 bit app, or 8 bytes of extra storage if the app is built x64. For x86 builds sizeof(void*) will return 4 bytes and 8 bytes in x64 builds. So in other words I am going to store the address of the internal "button" Class Window Procedure within the .cbWndExtra bytes of the main window. In still other words, it will be 'persisted' there. In yet still different words, it will become a C based 'instance' variable of the instantiated window. The variable will be set there at program initialization, and persisted there 'till program termination. Why?

Two reasons. First, as previously stated, I don't like global variables, and had I not done that, I would have had to have added this....

 
WNDPROC btnWndProc=NULL;


...at global scope in the program, instead of making it a local automatic variable in fnWndProc_OnCreate(). If you check out Charles' Colors program, I'm pretty sure you'll find he specified the return from the SetWindowLong() call which created the subclass in a variable at global scope within his code.

Second, the variable is needed to make subclassing work. Recall that we're discussing child window controls whose parent will receive WM_COMMAND messages from the child during program execution. And, our subclass procedure - a new Window Procedure in our program, will receive all the messages which would have originally gone to the child window control's original Window Procedure, the address of which we just stored in the main window's .cbWndExtra bytes. With subclassing, when one intercepts a message from the control's original Window Procedure, or, better said, a message that was to originally go to the controls Window Procedure but will now be coming into ours, we need to decide what to do with the message. We can write code to take some specific action on the message, but we need to decide what to do with the message after we're done with it. Specifically, we have the power to 'eat' the message, or pass it on to the original control Window Procedure. If we decide to pass it on to the control's original Window Procedure, we need the address of that procedure, because it is the first parameter of the CallWindowProc() function, which function is provided by Microsoft as part of the subclassing design to pass on the parameters of a WNDPROC function to another WNDPROC signature function. Here is my call at the end of my fnBtnSubClass() function in the return statement....

 
return CallWindowProc((WNDPROC)GetWindowLongPtr(hParent,0), hwnd, msg, wParam, lParam);


Note that the 1st parameter to CallWindowProc() is a WNDPROC typed variable, and I have the address of the control's original Window Procedure stored at offset #1 (offset #0 if using zero based counting) in the main Window's WNDCLASS::cbWndExtra bytes. So that's how that convoluted function call works. Its kind of ugly and confusing, I admit, but as Petzold mentioned in a similar use of such syntax- it works.

Note that, in that program for Malibor I wanted to show how the behavior of a control can be modified by subclassing, and in that program I stopped a specific button from behaving the way it was designed to behave by Microsoft. I 'ate' several messages in the button subclass for I believe Button #2. So for Button #2 that return statement containing a call to CallWindowProc() wouldn't execute. So Button #2 was essentially 'broken'. But if Button #2 wasn't the one clicked, the return executed, and we needed that persisted 'btnWndProc' variable stored in the WNDCLASS::cbWndExtra bytes.

None of Petziold's Win32 books were written when 64 bit Windows existed. Luckily for us, Microsoft's x64 modifications to the Api were such that most 32 bit source code was largely 64 bit compatible. Most. But not all. In 64 bit builds one needs to use SetWindowLongPtr() - not SetWindowLong(). However, if one uses SetWindowLongPtr() one can still build the source as x86. That app I posted for Malibor can be built and it correctly functions if built as x86 or x64. That's why I used sizeof(void*) as the number of bytes I wanted to specify as 'extra' within the WNDCLASS::cbWndExtra bytes. In x64 pointers need 8 bytes, and in x86 4 bytes. In most cases the values I store within the WNDCLASS::cbWndExtra bytes are pointers to various application objects.

Finally, passing addresses of objects instead of objects themselves is a better and more efficient methodology in terms of function parameters. That program of mine couldn't be built as a C program because reference parameters were an innovation of C++ over C's pointer parameters. At one time in my coding history I used the same constructs but used that WndEventArgs as a pointer parameter. When I taught my self C++, such as I understood it, I started using references more than pointer parameters. I think it better and clearer. Hope this helps.

From Microsoft's SetWindowLongPtr() docs....

Calling SetWindowLongPtr with the GWLP_WNDPROC index creates a subclass of the window class used to create the window. An application can subclass a system class, but should not subclass a window class created by another process. The SetWindowLongPtr function creates the window subclass by changing the window procedure associated with a particular window class, causing the system to call the new window procedure instead of the previous one. An application must pass any messages not processed by the new window procedure to the previous window procedure by calling CallWindowProc. This allows the application to create a chain of window procedures.

Reserve extra window memory by specifying a nonzero value in the cbWndExtra member of the WNDCLASSEX structure used with the RegisterClassEx function.
Last edited on
Thanks so much Freddie1, for all of your time. I think that I have some handle on the use of subclassing. Windows programming is definitely messy.

I have a couple questions about when subclassing might be necessary. Or, if you have any other suggestions, I'd be interested in hearing them. The specific project that I am working-on, involves sculpting a 3D shape. This is not for CGI or 3D printing. Rather, it is a proprietary application for a specific purpose. The original program was written by me, for the DOS system, back in the early 90's. I am now trying to create a Windows version, as the new tech has finally caught-up with my idea.

The first stage of the process involves creating the curves for the outlines. Key points are defined along the outline, and there is a button for each of these points. When one of those buttons is selected, three more should buttons appear, for adjusting the X, Y, and Z coordinates. Functions compute the predefined curves between these points. Orthographic displays show these changes in real time. A readout display shows the numerical coordinates (also in real time).

Now, to some of the subclassing questions. The original DOS version had an entire array of buttons--the rows being the list of points, and the columns representing X, Y, and Z. Changes were made using the arrow keys. Most of this was accomplished by passing function pointers. I would like to simplify this button arrangement, if possible.

There is also a question of whether I could use a child scrollbar, to scroll two child windows at the same time, so that they stay in sync. Is this a case for subclassing?

I also have a question about pointers and references. Surprisingly, this is the first that I have heard of references. But then, I haven't messed with coding in a lot of years. The question involves the best way to pass a large array of structures to a function. Originally, I used pointers, and the pointer math tended to get pretty ugly. Is there now a better way?

Thanks again, for taking so much time.
Sounds like a good challenge to me! Hope you are successful. One of my many deficiencies as a programmer is that I haven't worked with fancy graphics a lot, so I can't help you there. All I can do is to add a little encouragement by saying that if you largely accomplished the task in DOS there is no reason it can't be accomplished in Windows GUI even better. Just an idle thought.... When I first moved to Windows from DOS in the 90s I became fascinated with the Windows Console Mode Subsystem. I suppose the reason it fascinated me was that it seemed to offer something of a 'stepping stone' or bridge between low level non-GUI DOS and a full graphical environment. I really spent a lot of time fooling around with that, and had a lot of fun doing it, but in the end I do believe I'd have to say that it's better to just make the full jump to standard GUI Windows a la Petzold.

By the way, were you using some kind of C based GUI Application Framework for generating graphics in C? I'm curious because during those years, i.e., late 80s, early 90s, there were some of those around. In particular, I experimented a lot with VB DOS during that time. Later, a little bit with a BASIC Application framework provided by Bob Zale in his PowerBASIC (formerly Borland's Turbo-Basic (written by Bob Zale).

Anyway, I don't think subclassing is some kind of 'magic bullet' which will automatically solve all your issues. On a more positive note it is sometimes very useful. You just need to understand how it works and what it does and when you run into a situation in your code where it will help you need to be able to recognize that. I haven't looked at that Petzold Colors program in decades, but if I recall he wasn't fully satisfied with tabbing between his child scroll bar controls, or something like that, and he used subclassing to make it work the way he wanted it.

In my case, like I said, I wanted to capture [ENTER] key presses so as to be able to move focus from one text box to another. In other applications, I captured cursor motion key presses to do the same, e.g., a user could move up or down, left or right, between text boxes on a data entry form. I believe the same could be done with buttons, and likely any other control. I guess the underlying idea is that, if some control does something that is useful to you as the application writer, but some aspect of it isn't quite right, it might be possible to modify the behavior of the control by accessing its internal Window Procedure. That's what subclassing is all about.

As an aside, the other day, in response to your mentioning a need for further documentation about subclassing, I did a Google search on subclassing, and was astounded to learn Microsoft had added a bunch of new Api functions or wrappers to their Api related to subclassing! Another one of my coding deficiencies is that I tend not to 'keep up' with many changes. I learn something one way and figure that's the end of it. So sometime between the 90s when I learned subclassing and now Microsoft added all these new functions.

I'll address your other questions in a bit.
Your asking about references indicates to me that you are more of a C coder than a C++ coder. I don't mean that in any disparaging sense at all. Same here. Reference function parameters were incorporated into the C++ language, I believe, at the outset. If that is false maybe someone can correct me. Its something of a syntactic improvement over C's 'pass by value' protocol. And, its a more efficient way of passing C++'s sometimes large objects. Here's an example....

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
// cl Test001.cpp /EHsc
// cl Test001.cpp  /O1 /Os /GS- TCLib.lib kernel32.lib user32.lib
//  3,584 Bytes With My TCLib
// 56,320 Bytes MS C Runtime (LIBC)
//#define TCLib
#ifdef    TCLib
   #include  <windows.h>
   #include  "stdio.h"
   #include  "string.h"
   #include  "tchar.h"
   #include  "memory.h"
#else
   #include  <cstdio>
   #include  <string>
   #include  <tchar.h>
#endif

struct SomeStruct
{
 int   iNumber;
 TCHAR szSomeText[64];
}; 

void Output(SomeStruct& Test)
{
 _tprintf(_T("  Entering Output()\n"));
 _tprintf(_T("    &Test           = %u\n"),&Test);
 _tprintf(_T("    Test.iNumber    = %d\n"),Test.iNumber);
 _tprintf(_T("    Test.szSomeText = %s\n"),Test.szSomeText);
 _tprintf(_T("  Leaving Output()\n"));
} 

int main()
{
 SomeStruct ss;
 
 _tprintf(_T("\nEntering main()\n"));
 ss.iNumber = 25;
 _tcscpy(ss.szSomeText,"Some Text!");
 _tprintf(_T("  &ss = %u\n"),&ss);
 Output(ss);
 _tprintf(_T("Leaving main()\n"));
 getchar();
 
 return 0;
}

#if 0

C:\Code\VStudio\VC++9\LibCTiny\x64\Test8>cl Test001.cpp /EHsc
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.21022.08 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

Test001.cpp
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Test001.exe
Test001.obj

C:\Code\VStudio\VC++9\LibCTiny\x64\Test8>Test001

Entering main()
  &ss = 1244848
  Entering Output()
    &Test           = 1244848
    Test.iNumber    = 25
    Test.szSomeText = Some Text!
  Leaving Output()
Leaving main()


C:\Code\VStudio\VC++9\LibCTiny\x64\Test8>

#endif 


In Test001.cpp I create a simple struct and set some values in it. Then I pass it By Reference to a function Output() where the vales get output to the console. I output to the console the address of my instantiated SomeStruct object in main(), then I output to console the address of the object ib Output(). If you look at the addresses - 1244848 - in both places you will see they are the same. The SomeStruct object in Output is the same SomeStruct object in main(). Its not a global object either.

Now let's look at Test002.cpp where we remove the '&' after the function parameter type....

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
// cl Test002.cpp /EHsc
// cl Test002.cpp  /O1 /Os /GS- TCLib.lib kernel32.lib user32.lib
#ifdef    TCLib
   #include  "stdio.h"
   #include  "string.h"
   #include  "tchar.h"
   #include  "memory.h"
#else
   #include  <cstdio>
  #include   <string>
   #include  <tchar.h>
#endif

struct SomeStruct
{
 int   iNumber;
 TCHAR szSomeText[64];
}; 

void Output(SomeStruct Test)
{
 _tprintf(_T("  Entering Output()\n"));
 _tprintf(_T("    &Test           = %u\n"),&Test);
 _tprintf(_T("    Test.iNumber    = %d\n"),Test.iNumber);
 _tprintf(_T("    Test.szSomeText = %s\n"),Test.szSomeText);
 _tprintf(_T("  Leaving Output()\n"));
} 

int main()
{
 SomeStruct ss;
 
 _tprintf(_T("\nEntering main()\n"));
 ss.iNumber = 25;
 _tcscpy(ss.szSomeText,"Some Text!");
 _tprintf(_T("  &ss = %u\n"),&ss);
 Output(ss);
 _tprintf(_T("Leaving main()\n"));
 getchar();
 
 return 0;
}

#if 0

C:\Code\VStudio\VC++9\LibCTiny\x64\Test8>cl Test002.cpp /EHsc
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.21022.08 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

Test002.cpp
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Test002.exe
Test002.obj

C:\Code\VStudio\VC++9\LibCTiny\x64\Test8>Test002

Entering main()
  &ss = 1244752
  Entering Output()
    &Test           = 1244832
    Test.iNumber    = 25
    Test.szSomeText = Some Text!
  Leaving Output()
Leaving main()


C:\Code\VStudio\VC++9\LibCTiny\x64\Test8>

#endif 


In this case the compiler is forced to make a copy of the object and pass that to the function Output(). You can see in my console output that two instantiations of the object are made as seen by two different addresses. This can become quite inefficient - especially with 'heavy' objects.

In C, forcing the compiler to re-use an object in a caller function when passing it to the 'callee' function - one would use pointers, as in Test003.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
// cl Test003.cpp /EHsc
// cl Test003.cpp  /O1 /Os /GS- TCLib.lib kernel32.lib user32.lib
//  3,584 Bytes With My TCLib
// 56,320 Bytes MS C Runtime (LIBC)
//#define TCLib
#ifdef    TCLib
   #include  <windows.h>
   #include  "stdio.h"
   #include  "string.h"
   #include  "tchar.h"
   #include  "memory.h"
#else
   #include  <cstdio>
  #include   <string>
   #include  <tchar.h>
#endif

struct SomeStruct
{
 int   iNumber;
 TCHAR szSomeText[64];
}; 

void Output(SomeStruct* pTest)
{
 _tprintf(_T("  Entering Output()\n"));
 _tprintf(_T("    pTest             = %u\n"),pTest);
 _tprintf(_T("    pTest->iNumber    = %d\n"),pTest->iNumber);
 _tprintf(_T("    pTest->szSomeText = %s\n"),pTest->szSomeText);
 _tprintf(_T("  Leaving Output()\n"));
} 

int main()
{
 SomeStruct ss;
 
 _tprintf(_T("\nEntering main()\n"));
 ss.iNumber = 25;
 _tcscpy(ss.szSomeText,"Some Text!");
 _tprintf(_T("  &ss = %u\n"),&ss);
 Output(&ss);
 _tprintf(_T("Leaving main()\n"));
 getchar();
 
 return 0;
}

#if 0
C:\Code\VStudio\VC++9\LibCTiny\x64\Test8>cl Test003.cpp /EHsc
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.21022.08 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

Test003.cpp
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Test003.exe
Test003.obj

C:\Code\VStudio\VC++9\LibCTiny\x64\Test8>Test003

Entering main()
  &ss = 1244848
  Entering Output()
    pTest             = 1244848
    pTest->iNumber    = 25
    pTest->szSomeText = Some Text!
  Leaving Output()
Leaving main()
#endif 


In the above program you can see both the caller and the callee are working with the same object.

Using reference parameters in C++ alleviates the need for pointers and pointer '->' notation. I believe the syntax and readability are a bit better. I might add that passing parameters by reference is very common in other programming languages. Next post I'll discuss that.
The programming language I used most for my desktop applications (I spent a large part of my career writing C and C++ code for handheld data collectors with Embedded Visual C++ 3.0 and 4.0) was PowerBASIC. Here is the same program as above in PowerBASIC....

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
#Compile Exe
#Dim All

Type SomeStruct
  iNumber As Long
  szSomeText  As Asciiz*64
End Type

Sub MyOutput(ByRef Test As SomeStruct)
  Print "  Entering Output()"
  Print "    Varptr(Test)    = " Varptr(Test)
  Print "    Test.iNumber    = " Test.iNumber
  Print "    Test.szSomeText = " Test.szSomeText
  Print "  Leaving Output()"
End Sub

Function PBMain() As Long
  Local ss As SomeStruct

  Print "Entering PBMain()"
  ss.iNumber  = 25
  ss.szSomeText = "Some Text!"
  Print "  Varptr(ss) = " Varptr(ss)
  MyOutput(ss)
  Print "Leaving PBMain()"
  Waitkey$

  PBMain=0
End Function

#if 0

Entering PBMain()
  Varptr(ss) =  1637868
  Entering Output()
    Varptr(Test)    =  1637868
    Test.iNumber    =  25
    Test.szSomeText = Some Text!
  Leaving Output()
Leaving PBMain()

#endif 


Note above I have the parameter to MyOutput() specified like so....

ByRef Test As SomeStruct

I could have eliminated the ByRef keyword because 'pass by reference' is default behavior in PowerBASIC and I believe most versions of basic. As you can see, its like the Test001.cpp program in that only one object exists as proven by the SomeStruct instantiation address. In PowerBASIC one can over - ride that behavior and emulate C's 'pass by value' protocol by using what's termed in that language an 'override'. Its done like so....

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
#Compile Exe
#Dim     All

Type SomeStruct
  iNumber As Long
  szSomeText  As Asciiz*64
End Type

Sub MyOutput(ByVal Test As SomeStruct)
  Print "  Entering Output()"
  Print "    Varptr(Test)    = " Varptr(Test)
  Print "    Test.iNumber    = " Test.iNumber
  Print "    Test.szSomeText = " Test.szSomeText
  Print "  Leaving Output()"
End Sub

Function PBMain() As Long
  Local ss As SomeStruct

  Print "Entering PBMain()"
  ss.iNumber  = 25
  ss.szSomeText = "Some Text!"
  Print "  Varptr(ss) = " Varptr(ss)
  MyOutput(ss)
  Print "Leaving PBMain()"
  Waitkey$

  PBMain=0
End Function

#if 0

Entering PBMain()
  Varptr(ss) =  1637868
  Entering Output()
    Varptr(Test)    =  1637800
    Test.iNumber    =  25
    Test.szSomeText = Some Text!
  Leaving Output()
Leaving PBMain()


#endif 


You'll note the subroutine parameter is typed like so, and the output shows two different addresses - the one in PBMain() and the copy in MyOutput()....

ByVal Test As SomeStruct

One thing perhaps worthwhile to note is that when using references it doesn't change the syntax of the function call. Look at the way Output() was called in both Test001.cpp and Test002.cpp. They asre both like so...

Output(ss);

The little '&' character (address of operator) made all the difference in the function prototype.

Hope this was a good introduction to references!

Last edited on
Hey freddie1. Thanks again, for all of the time you have taken with me. To answer your first question, I did not use any kind of DOS application framework, to create a GUI. I did not know that such things existed. So, I just read-up on the subject, and created my own, from scratch. The real pain though, was having to write all of the graphics drivers myself. DOS had very limited graphics capability. And, the graphics-card manufacturers did not supply drivers with their products.

On "subclassing", I suppose that I was confused by the name. From what I gather, "subclassing" is a bit of a misnomer. It seems to be more of a "jump" function--a way to switch the focus from one child window to another, without cycling through WinMain() or WndProc(). It seems to especially apply to keyboard entries.

I understand the difference between references, pointers, and passing copies. However, how would one pass a reference to and array of structs? In your very first C++ example, where you pass a reference to the Output() function, how would you handle it, if the definition in your main function was "SomeStruct ss[3];"? You couldn't use, "Output(ss);", as that would pass a pointer to the array.
Right. A reference is something termed an 'LVALUE'. Has to refer to a single value. Can't seem to have arrays of them. You need to use pointers for arrays of structs like so....

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
// cl Test004.cpp /EHsc
// cl Test004.cpp  /O1 /Os /GS- TCLib.lib kernel32.lib user32.lib
//  3,584 Bytes With My TCLib
// 56,320 Bytes MS C Runtime (LIBC)
#define   TCLib
#ifdef    TCLib
   #include  <windows.h>
   #include  "stdio.h"
   #include  "string.h"
   #include  "tchar.h"
   #include  "memory.h"
#else
   #include  <cstdio>
  #include   <string>
   #include  <tchar.h>
#endif

struct SomeStruct
{
 int   iNumber;
 TCHAR szSomeText[64];
}; 

void Output(SomeStruct* pTest)
{
 _tprintf(_T("  Entering Output()\n"));
 _tprintf(_T("    pTest               = %u\n"),pTest);
 
 _tprintf(_T("    pTest[0].iNumber    = %d\n"),pTest[0].iNumber);    // Output Zeroeth Element
 _tprintf(_T("    pTest[0].szSomeText = %s\n"),pTest[0].szSomeText);
 
 _tprintf(_T("    pTest[1].iNumber    = %d\n"),pTest[1].iNumber);    // Output First Element
 _tprintf(_T("    pTest[1].szSomeText = %s\n"),pTest[1].szSomeText);
  
 _tprintf(_T("  Leaving Output()\n"));
} 

int main()
{
 SomeStruct ss[2];  // Array of SomeStruct Containing Two Elements
 
 _tprintf(_T("\nEntering main()\n"));
 
 ss[0].iNumber = 25;                      // Zeroeth Element
 _tcscpy(ss[0].szSomeText,"Some Text!");
 
 ss[1].iNumber = 30;                      // First Element
 _tcscpy(ss[1].szSomeText,"Some More Text!");
 
  
 _tprintf(_T("  &ss = %u\n"),&ss);
 Output(&ss[0]);
 _tprintf(_T("Leaving main()\n"));
 getchar();
 
 return 0;
}

#if 0

C:\Code\VStudio\VC++9\LibCTiny\x64\Test8>cl Test004.cpp  /O1 /Os /GS- TCLib.lib kernel32.lib user32.lib
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.21022.08 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

Test004.cpp
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Test004.exe
Test004.obj
TCLib.lib
kernel32.lib
user32.lib

C:\Code\VStudio\VC++9\LibCTiny\x64\Test8>Test004.exe

Entering main()
  &ss = 1244816
  Entering Output()
    pTest               = 1244816
    pTest[0].iNumber    = 25
    pTest[0].szSomeText = Some Text!
    pTest[1].iNumber    = 30
    pTest[1].szSomeText = Some More Text!
  Leaving Output()
Leaving main()

#endif 
About your DOS graphics....

I actually did do something like that once. Never had to fiddle with drivers though. I'm a forester, and at work (in forestry research), we needed to determine the percentage volume in four foot sections of a tree, where we used a laser to take three diameter measurements of a tree...

1) At 4.5 feet above ground;
2) At 17.3 feet above ground;
3) Height where tree diameter reduced to 8.5" inches for hardwoods and 6.5 inches for sioftwoods;

The way I did it was with Microsoft Quickbasic 4.5. There is some rule or other in mathematics - you might know something about that, that the order of the equation to run a line through points is one less or one more or something - it was 25 years ago - my memory is dim, so that's what I did with calculus, or a least squared analysis, or something to determine the unique curve for that tree as per its measurements. After having the equation I rotated the curve about the x axis (had to lay tree down :) ). I think I used systems of matrices to solve it. In any case, I used Quickbasic to provide a really cool graphic of the tree.

Last edited on
The Array capabilities of C and C++ leave something to be desired. I'm told the C++ Standard Library now has dynamic array capabilities, and there are other locations where folks get code to deal with them. For myself, I don't use anything from the C++ Standard Library (I do make big use of C Standard Library however, which is kind of a subset of it) so I made my own dynamic multi-dimensional templated array class. I needed dynamic four dimensional arrays for the work I did. Its really not a lot of code. I could easily post it if its something you would want to use for your project. It obviously needs C++ though.
Freddie1, thanks again for your time. Have they improved the way that pointers are passed to structures? This was the the function code that you posted:

1
2
3
4
5
6
7
8
9
10
11
12
13
void Output(SomeStruct* pTest)
{
 _tprintf(_T("  Entering Output()\n"));
 _tprintf(_T("    pTest               = %u\n"),pTest);
 
 _tprintf(_T("    pTest[0].iNumber    = %d\n"),pTest[0].iNumber);    // Output Zeroeth Element
 _tprintf(_T("    pTest[0].szSomeText = %s\n"),pTest[0].szSomeText);
 
 _tprintf(_T("    pTest[1].iNumber    = %d\n"),pTest[1].iNumber);    // Output First Element
 _tprintf(_T("    pTest[1].szSomeText = %s\n"),pTest[1].szSomeText);
  
 _tprintf(_T("  Leaving Output()\n"));
} 


Back in the day, I had to use something more like this (forgive me if I don't quite recall the syntax correctly; I'm too lazy at the moment, to dig-out my notes):

1
2
3
4
5
6
7
8
9
10
11
12
void Output(SomeStruct* pTest)
 {
     int i;
      _tprintf(_T("  Entering Output()\n"));
      _tprintf(_T("    pTest               = %u\n"),pTest);

     for(i=0; i<2; i++){
           _tprintf(_T("    pTest[0].iNumber    = %d\n"),(pTest+i*sizeof(SomeStruct))->iNumber);
           _tprintf(_T("    pTest[0].szSomeText = %s\n"),(pTest+i*sizeof(SomeStruct))->szSomeText);
      }
      _tprintf(_T("  Leaving Output()\n"));
 } 


You can see how that could become quite messy and buggy, when dealing with multidimensional arrays.

The equations with the trees that you mentioned, are "polynomial splines". The order of the equation is one less than the number of data points. The most common and easiest to use is the "cubic spline", which uses four points. I used that one quite a bit in my project. There is also a parabolic spline, for cases of only three data points. But, you know, I can't even find the formula for that one, and I forgot how to do it years ago. I do have an old textbook on "Numerical Analysis" somewhere around here. It may be in there.
One other question. When passing the structure pointer to the array, you used:

 
Output(&ss[0]);


Isn't that the same as:

 
Output(ss);


Or, is that method no obsolete?
If ss is an array or pointer, yes, it's the same thing.
I'd argue that it's cleaner to just pass it as ss, since it's being used as an array, which is what ss is.
Last edited on
 
pTest+i*sizeof(SomeStruct))->iNumber


I used to do that when I first learned C. Like you, I thought it was a terribly awkward construct. Somewhere along the line - not sure where, I learned the 'array' notation which in my mind is clearly better.

Ah! Numerical Analysis! I remember back in my college days seeing courses on that. Don't remember if they were in the mathematics or statistics departments. Had a lot of math and statistics courses, but never took that. I figured I was missing a body of information somewhere because I struggled mightly with that problem. I really needed a higher order equation to get my tree exactly right, and none of my statistics books or linear algebra books showed how to solve matrices as large as I needed. This was around 1995. It was around that time I discovered the PowerBASIC programming language, which had a matrix package which solved these high order matrices. That's where I got into that language big time, but I guess my priorities changed, and I never upgraded my project. What I had was good enough, I guess. I had three points, which you said is a parabolic spline. I can add this to my list of potential coding projects in my retirement!
Pages: 12