Thread: Modeless MessageBox() Internals

  1. #16
    Registered User
    Join Date
    Jul 2015
    Posts
    64
    Quote Originally Posted by freddie View Post
    Quite possibly Abyssion. To be perfectly honest, I was never that interested in it to give it much thought. I guess you could say I take it on faith that it works. There is always an alternative to using various Windows functions or functionality such as that to accomplish keyboard navigation among child window controls, but it always involves writing a bunch of nasty code oneself - like dozens and dozens of lines of it, involving subclassing and whatnot. Its just easier to play by the rules is all.
    Excellent, I'm glad I haven't demonstrated some more fundamental misunderstandings that you can point out in a second; cheers Freddie! Aye, I would definitely agree about not wanting to write the nasty code oneself; in a real project, I would take provided functions over my own any day! (For obvious reasons, I'm still not that confident in my ability to write good code, but I'm learning slowly.) Anyways, I just wanted to make sure that I had as great an understanding of IsDialogMessage() as possible before actually using it - there's nothing worse than trying to debug a block of code that contains a function which you don't really know what it does.

    Thank you both for your help!

  2. #17
    Registered User
    Join Date
    Dec 2010
    Location
    Trinidad, CO (log cabin in middle of nowhere)
    Posts
    148
    It is the size of the control in the dialog that is too large. And I have tried configuring them as small as possible.
    I've already been faced with the problem of needing a lot of controls within a window/form/dialog and not having room to put them all. I face this in a major Windows CE project of mine. We use Windows CE data recorders where I work and I write the data collection apps for them. The screen is like three inches by two and a half inches!

    What I did was create a 'virtual' screen 'behind' the real app screen which is much larger than the main program screen which as I said is rather small. And I scroll the virtual screen. The user can use scroll bars to bring those portions of the virtual screen into view. Another way of handling it is with tab controls of course. Not sure if this will help, but just thought I'd let you know how I've dealt with that problem successfully.

  3. #18
    Registered User
    Join Date
    Dec 2010
    Location
    Trinidad, CO (log cabin in middle of nowhere)
    Posts
    148
    Here’s another demo type program illustrating multiple windows/dialogs/forms based on the same Window Class. It provides high order entertainment value for every bit of three or four minutes! This is an update of a very old program from my Windows programming learning days of many years ago.

    It starts out of course in WinMain() where a Window Class is Registered in the typical manner. Where things become unusual is that instead of a single CreateWindow() call to create a main program window, the CreateWindow() call is wrapped in a For loop which creates four main program windows!

    The single Window Procedure processes WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_SIZE, and WM_CHAR messages, in addition to WM_CREATE, WM_PAINT, and WM_DESTROY.

    When you move the mouse over any of the windows that particular window instance displays a continuously running report of the mouse position as reported to the window as part of the WM_MOUSEMOVE Window Procedure call.

    When you click anywhere within the client area of a window instance that window will display the pixel coordinates of your click within that window.

    If you size a window instance by dragging any of the window borders that instance will display the present size of the window as provided by the WM_SIZE message.

    If you type characters while any specific window has the focus the window instance will display what you are typing as would be the case with a simple word processor.

    There are no global or static variables in the program, so data needing to be persisted across Window Procedure calls, i.e., any such data as described above, is stored in dynamically allocated memory accessed through a pointer stored within each window instance’s window memory (WNDCLASSEX::cbWndExtra Bytes). A ProgramData object is defined for that purpose. The data is accessed within the WM_PAINT handler and TextOut() is used to display all this data within each Window instance.

    Another interesting aspect of the program is its use of the WNDCLASSEX::cbClsExtra bytes. Typically, WM_DESTROY handlers call PostQuitMessage() to terminate the message pump in WinMain(). That wouldn’t do here as clicking the x in the title bar of any one window instance wouldn’t destroy just that one window instance but would destroy all of them and terminate the program. So the count of window instances is maintained within the Window Class structure through the WNDCLASSEX::cbClsExtra bytes. The count is incremented for every call of WM_CREATE and decremented for every call of WM_DESTROY. Only when the count reaches zero as determined by WM_DESTROY processing is PostQuitMessage() called to terminate the message loop.

    I see one can attach zips and what not at this site. So I’ll try to attach my TCLib.lib (Tiny C Lib based on Microsoft Systems Journal author Matt Pietrek’s LibCTiny.lib) and my String Class in source code form. The code I’m posting uses my String Class which I wrote myself in lieu of the C++ Standard Library’s String Class. It is represented by Strings.h and Strings.cpp. Using that and my TCLib.lib results in an 8,704 byte x64 UNICODE stand alone executable as built by VC 19 of VS 2015. Building with Visual Studio or GCC using standard linkages results in executables orders of magnitude larger. The command line compilation strings are at top in the source code of Main.cpp. The top one is for building with my TCLib.lib and the bottom one for building with default C Runtime linkages. To build from the command line go to your Start Menu and locate the Visual Studio x64 Native Tools Command Prompt. Put Main.cpp, Form3.h, Strings.cpp, Strings.h, stdio.h, string.h, stdlib.h, tchar.h and TCLib.lib from the attached zip in some folder on your computer. Then using the Change Directory command ( CD ) within the command prompt window navigate to that directory where the files are located. Then paste the command line compilation strings in there and hit [ENTER]. Note there is a …

    Code:
    #define TCLib
    …equate near the top of Main.cpp and Strings.cpp. If that isn’t commented out in both places the build will be with my TCLib.lib linkage (No static linkage with C Runtime). That will result in the 8704 byte executable. If you comment that out you’ll get static linkage with the C Runtime system. In case this is all to much, I put the compiled Form3a.exe (8704 bytes) in the zip.

    continued...

  4. #19
    Registered User
    Join Date
    Dec 2010
    Location
    Trinidad, CO (log cabin in middle of nowhere)
    Posts
    148
    Main.cpp

    Code:
    // Main.cpp
    // cl Main.cpp Strings.cpp /O1 /Os /GS- /FeForm3a.exe /Zc:sizedDealloc- TCLib.lib kernel32.lib user32.lib gdi32.lib
    // cl Main.cpp Strings.cpp /O1 /Os /GS- /MT /FeForm3b.exe kernel32.lib user32.lib gdi32.lib
    // 215,040 Bytes GCC 4.8 x64 UNICODE
    // 117,248 Bytes VC19 (VS 2015) x64 UNICODE /MT Linked
    //   8,704 Bytes VC19 (VS 2015) x64 UNICODE TCLib Linked
    #ifndef UNICODE
       #define UNICODE
    #endif
    #ifndef _UNICODE
       #define _UNICODE
    #endif
    #include <windows.h>
    #define TCLib
    #ifdef TCLib
       #include "tchar.h"
       #include "stdio.h"
    #else
       #include <tchar.h>
       #include <stdio.h>
    #endif
    #include "Form3.h"
    #include "Strings.h"
    
    
    LRESULT fnWndProc_OnCreate(WndEventArgs& Wea)
    {
     ProgramData* pProgramData=NULL;
     String* pStr;
     size_t iCtr;
    
     pStr=new String;
     SetWindowLongPtr(Wea.hWnd,0,(LONG_PTR)pStr);
     pProgramData=(ProgramData*)GlobalAlloc(GPTR,sizeof(ProgramData));
     if(pProgramData)
     {
        SetWindowLongPtr(Wea.hWnd,1*sizeof(void*),(LONG_PTR)pProgramData);
        iCtr=GetClassLongPtr(Wea.hWnd,0);
        iCtr++;
        SetClassLongPtr(Wea.hWnd,0,(LONG_PTR)iCtr);
        return 0;
     }
     else
        return -1;
    }
    
    
    LRESULT fnWndProc_OnSize(WndEventArgs& Wea)
    {
     ProgramData* pProgramData=NULL;
    
     pProgramData=(ProgramData*)GetWindowLongPtr(Wea.hWnd,1*sizeof(void*));
     if(pProgramData)
     {
        pProgramData->xSize=LOWORD(Wea.lParam);
        pProgramData->ySize=HIWORD(Wea.lParam);
        InvalidateRect(Wea.hWnd,NULL,TRUE);
     }
    
     return 0;
    }
    
    
    LRESULT fnWndProc_OnChar(WndEventArgs& Wea)
    {
     String* pStr=NULL;
    
     pStr=(String*)GetWindowLongPtr(Wea.hWnd,0);
     if(pStr)
     {
        *pStr=*pStr+Wea.wParam;
        InvalidateRect(Wea.hWnd,NULL,FALSE);
     }
    
     return 0;
    }
    
    
    LRESULT fnWndProc_OnMouseMove(WndEventArgs& Wea)
    {
     ProgramData* pProgramData=NULL;
    
     pProgramData=(ProgramData*)GetWindowLongPtr(Wea.hWnd,1*sizeof(void*));
     if(pProgramData)
     {
        pProgramData->xMouse=LOWORD(Wea.lParam);
        pProgramData->yMouse=HIWORD(Wea.lParam);
        InvalidateRect(Wea.hWnd,NULL,TRUE);
     }
    
     return 0;
    }
    
    
    LRESULT fnWndProc_OnLButtonDown(WndEventArgs& Wea)
    {
     ProgramData* pProgramData=NULL;
    
     pProgramData=(ProgramData*)GetWindowLongPtr(Wea.hWnd,1*sizeof(void*));
     if(pProgramData)
     {
        pProgramData->xButton=LOWORD(Wea.lParam);
        pProgramData->yButton=HIWORD(Wea.lParam);
        InvalidateRect(Wea.hWnd,NULL,FALSE);
     }
    
     return 0;
    }
    
    
    LRESULT fnWndProc_OnPaint(WndEventArgs& Wea)
    {
     ProgramData* pProgramData=NULL;
     String* pStr=NULL;
     String s1,s2,s3;
     PAINTSTRUCT ps;
     HDC hDC;
    
     hDC=BeginPaint(Wea.hWnd,&ps);
     pProgramData=(ProgramData*)GetWindowLongPtr(Wea.hWnd,1*sizeof(void*));
     if(pProgramData)
     {
        s1=pProgramData->xMouse;
        s2=pProgramData->yMouse;
        s3=_T("xMouse=");
        s3=s3+s1+_T("  yMouse=")+s2+_T("  ");
        TextOut(hDC,0,0,s3.lpStr(),s3.Len());
        if(pProgramData->xButton||pProgramData->yButton)
        {
           s1=pProgramData->xButton;
           s2=pProgramData->yButton;
           s3=_T("xButton=");
           s3=s3+s1+_T("  yButton=")+s2+_T("  ");
           TextOut(hDC,pProgramData->xButton+12,pProgramData->yButton,s3.lpStr(),s3.Len());
           pProgramData->xButton=0, pProgramData->yButton=0;
        }
        s1=pProgramData->xSize;
        s2=pProgramData->ySize;
        s3=_T("Width=");
        s3=s3+s1+_T("  Height=")+s2+_T("  ");
        TextOut(hDC,0,20,s3.lpStr(),s3.Len());
        pStr=(String*)GetWindowLongPtr(Wea.hWnd,0);
        if(pStr)
           TextOut(hDC,0,40,pStr->lpStr(),pStr->Len());
     }
     EndPaint(Wea.hWnd,&ps);
    
     return 0;
    }
    
    
    LRESULT fnWndProc_OnDestroy(WndEventArgs& Wea)
    {
     ProgramData* pProgramData=NULL;
     String* pStr=NULL;
     size_t iCtr;
    
     pStr=(String*)GetWindowLongPtr(Wea.hWnd,0);
     if(pStr)
        delete pStr;
     pProgramData=(ProgramData*)GetWindowLongPtr(Wea.hWnd,1*sizeof(void*));
     if(pProgramData)
        GlobalFree(pProgramData);
     iCtr=GetClassLongPtr(Wea.hWnd,0);
     iCtr--;
     if(iCtr==0)
     {
        MessageBox(Wea.hWnd,_T("No More Windows Left!  Will Call PostQuitMessage()!"),_T("Time To Go!"),MB_OK);
        PostQuitMessage(0);
     }
     else
     {
        MessageBox(Wea.hWnd,_T("Will Be Just Destroying This Window!"),_T("Another Dead Window!"),MB_OK);
        SetClassLongPtr(Wea.hWnd,0,(LONG_PTR)iCtr);
     }
    
     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 __stdcall WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
    {
     TCHAR szClassName[]=_T("Form3");
     TCHAR szCaption[80];
     WNDCLASSEX wc;
     MSG messages;
    
     wc.lpszClassName=szClassName,                          wc.lpfnWndProc=fnWndProc;
     wc.cbSize=sizeof(WNDCLASSEX),                          wc.style=0;
     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=3*sizeof(void*);
     wc.lpszMenuName=NULL,                                  wc.cbClsExtra=8;
     RegisterClassEx(&wc);
     _tcscpy(szCaption,_T("Try Typing Text, Moving Mouse, Clicking Left Mouse Button, Or Sizing Window!"));
     for(size_t i=0; i<4; i++)
         CreateWindowEx(0,szClassName,szCaption,WS_OVERLAPPEDWINDOW|WS_VISIBLE,CW_USEDEFAULT,CW_USEDEFAULT,625,400,HWND_DESKTOP,0,hIns,0);
     while(GetMessage(&messages,NULL,0,0))
     {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
     }
    
     return messages.wParam;
    }
    Form3.h
    Code:
    //Form3.h
    #ifndef Form3_h
    #define Form3_h
    
    #define dim(x)                  (sizeof(x) / sizeof(x[0]))
    #ifdef TCLib
    extern "C" int                  _fltused = 1;
    #endif
    
    struct                          WndEventArgs
    {
     HWND                           hWnd;
     WPARAM                         wParam;
     LPARAM                         lParam;
     HINSTANCE                      hIns;
    };
    
    struct                          EVENTHANDLER
    {
     unsigned int                   Code;
     LRESULT                        (*fnPtr)(WndEventArgs&);
    };
    
    LRESULT fnWndProc_OnCreate      (WndEventArgs& Wea);
    LRESULT fnWndProc_OnSize        (WndEventArgs& Wea);
    LRESULT fnWndProc_OnChar        (WndEventArgs& Wea);
    LRESULT fnWndProc_OnMouseMove   (WndEventArgs& Wea);
    LRESULT fnWndProc_OnLButtonDown (WndEventArgs& Wea);
    LRESULT fnWndProc_OnPaint       (WndEventArgs& Wea);
    LRESULT fnWndProc_OnDestroy     (WndEventArgs& Wea);
    
    const EVENTHANDLER              EventHandler[] =
    {
     {WM_CREATE,                    fnWndProc_OnCreate},
     {WM_SIZE,                      fnWndProc_OnSize},
     {WM_CHAR,                      fnWndProc_OnChar},
     {WM_MOUSEMOVE,                 fnWndProc_OnMouseMove},
     {WM_LBUTTONDOWN,               fnWndProc_OnLButtonDown},
     {WM_PAINT,                     fnWndProc_OnPaint},
     {WM_DESTROY,                   fnWndProc_OnDestroy}
    };
    
    struct ProgramData
    {
     short int                      xMouse;
     short int                      yMouse;
     short int                      xSize;
     short int                      ySize;
     short int                      xButton;
     short int                      yButton;
    };
    
    #endif

  5. #20
    Registered User
    Join Date
    Dec 2010
    Location
    Trinidad, CO (log cabin in middle of nowhere)
    Posts
    148
    Will try to attach zip of everything required to build above app...
    Attached Files Attached Files

  6. #21
    Registered User
    Join Date
    Mar 2011
    Posts
    596
    Quote Originally Posted by freddie View Post
    I've already been faced with the problem of needing a lot of controls within a window/form/dialog and not having room to put them all. I face this in a major Windows CE project of mine. We use Windows CE data recorders where I work and I write the data collection apps for them. The screen is like three inches by two and a half inches!

    What I did was create a 'virtual' screen 'behind' the real app screen which is much larger than the main program screen which as I said is rather small. And I scroll the virtual screen. The user can use scroll bars to bring those portions of the virtual screen into view. Another way of handling it is with tab controls of course. Not sure if this will help, but just thought I'd let you know how I've dealt with that problem successfully.
    Tiny screens do present a challenge. Sounds like a good solution.

    I think I will use tab controls if I run out of space. My "custom combobox" seems be working, so I may be able to fit everything, at least on this project.

    -

  7. #22
    Registered User
    Join Date
    Jul 2015
    Posts
    64
    Sorry, I haven't been about in a while.

    That's awesome Freddie, thank you! It's given me some insight into the difference between WNDCLASSEX::cbClsExtra and ::cbWndExtra and where each one might be appropriate to use; that was actually something I was thinking about today. I note that it would be possible to have one windows procedure servicing an entire application, even if it had 5 or 6 windows/toolbars/forms or whatever, all with different functionalities. (Well, the base functionality of the window would be the same, but we could switch the w/lParams in the WM_COMMAND message handler so each control has different functionality.) I assume to keep things straightforward and logically encapsulated (not to mention create different controls per window in the WM_CREATE handler,) that this is not a typical architecture and that one should usually have one windows procedure for each window in the application?

    Are there any situations when sharing a windows procedure might come in handy?

    It was very good of you to include a copy of your TCLib Freddie, cheers for that. In terms of actually using it, I remember from the other thread that you've overloaded the '(' and ')' operators to allow you to create multidimensional arrays like so:

    Code:
    array(1, 2)
    Just wondering if there are any other differences compared to the standard?
    So I know for the future, do you want to place any restrictions on it (usage/distribution etc)? I can't image a situation where I'd be asked to distribute it, and even if I did happen across such a situation, I would never pass on someone else's work without explicit permission first. I'm thinking more about usage; do you want to prohibit linking to it except for educational purposes? Or perhaps credit should be given on the 'about' section of any apps built with it?

    Cheers!

  8. #23
    Registered User
    Join Date
    Dec 2010
    Location
    Trinidad, CO (log cabin in middle of nowhere)
    Posts
    148
    I note that it would be possible to have one windows procedure servicing an entire application, even if it had 5 or 6 windows/toolbars/forms or whatever, all with different functionalities. (Well, the base functionality of the window would be the same, but we could switch the w/lParams in the WM_COMMAND message handler so each control has different functionality.) I assume to keep things straightforward and logically encapsulated (not to mention create different controls per window in the WM_CREATE handler,) that this is not a typical architecture and that one should usually have one windows procedure for each window in the application?
    I personally don't use an architecture such as you describe for the following reason - its simpler to to have a seperate Window Procedure for every top level application window. As you've no doubt gleaned by now complexity is the killer in application development. One must in every good application one writes come up with ways of handling complexity so that one can use the 'divide and conquer' approach to allow one to isolate functionality into smaller and smaller manageable piecies, so that one can fully grasp each particular small part and 'get it right' before moving on to another part of a program. That's basically what the RegisterClass/CreateWindow Apis allows one to do; each specific window in a large application has some particular purpose apart from all others. By Registering a Window Class for each top level window/form/dialog in a program that necessitates providing an address to WNDCLASSEX::lpfnWndProc for that window. In that manner, the functionality and code specific to that window type is isolated from all other windows in an application by the Window Procedure specific to that Window Class. And if each Window Procedure along with the message handlers pertaining to that Window Procedure are put in a seperate code file (*.cpp, *.h), then one has an important modularization technique available for conquering complexity in large application development scenarios. The situation is really no different from the design philosophy underlying C++ Classes, i.e., create a seperate class for each functionality you need, and don't conflate/conflict things together. So when I provided that previous example of mine with a Dialog for Form1, and then that Form2 and Form3, with seperate Window Procedures for all three, I was basically showing a simplified 'model' of how I design very large 'enterprise' scale applications. There are no doubt other way to go about it, but what I have shown is what I have personally found effective.

    The phone rings in my organization and somebody tells me they are having some kind of trouble with one of my applications. First question I ask is which program. Next question I ask is what window of that specific program. If the application has 75,000 lines of code but the window where the user was working when the problem was encountered has only 5,000 lines of code associated with it (because a seperate Window Procedure was Registered for that Window), then I've just eliminated 70,000 lines of code which must be examined to solve the problem. If upon further inquiry I found out the problem was encountered when the user clicked a button, then within that 5,000 lines of code for that window all I need to look at is a couple hundred lines maybe pertaining to the code that runs when the user clicked that button. So you can 'zero in' quickly on the source of problems the more effectively you use the modularization techniques such as a seperate Window Procedures for each Window Class.

  9. #24
    Registered User
    Join Date
    Dec 2010
    Location
    Trinidad, CO (log cabin in middle of nowhere)
    Posts
    148
    It was very good of you to include a copy of your TCLib Freddie, cheers for that. In terms of actually using it, I remember from the other thread that you've overloaded the '(' and ')' operators to allow you to create multidimensional arrays like so:
    I don't recall posting any code here about my multi-dimensional array class, but I do have an early version of it posted over in the Jose Roca forum. That particular piece of code was actually the final piece of the puzzle I needed to be able to fully integrate C++ into my work. In the technical area in which I work, which is forestry, I had developed what I'd consider to be some fairly sophisticated algorithms for processing the type of data I routinely work with. I had first started developing these algorithms back in the mid 1980s using early versions of basic, which provided strong support for dynamic multi-dimensional arrays, i.e., the array subscripts not being known until runtime.

    When I taught myself C in the 1990s it didn't take me too long to realize that there was little to no support for the sorts of things I was doing with arrays in either the base C language or in the C Standard Library.

    Some years later when I attempted to teach myself C++ I was surprised to see that the situation there hadn't changed much from C - at least regarding dynamic multi-dimensional arrays. To this day I expect the various container objects (vectors, etc.) within the C++ Standard Library must have some kind of support for this functionality, but as I've said before, I don't use the C++ specific features of the Standard Library so I don't know how to do it. Personal problem and lack of knowledge on my part. The issue does interest me though so I'll post a simple example of it; it doesn't take much code to do it. Perhaps you or someone else that's good with the various container objects in the C++ Standard Library can show how the example can be done using that.

  10. #25
    Registered User
    Join Date
    Dec 2010
    Location
    Trinidad, CO (log cabin in middle of nowhere)
    Posts
    148
    I pretty much had this multi-dimensional array code from a couple years ago Abyssion, but I reworked things to see if it would work with my TCLib.lib and it does fantastically, I’m really glad to see! If you’ve downloaded and used my TCLib.lib from earlier zip you’re good to go as nothing has changed there. However, as a result of some comments on one of my String Class methods I posted a couple days ago (a copy constructor) by Laserlight and Elysia in the C++ Forum, I decided to go through the whole String Class and modify methods to test for memory allocation failures and deal with them. I actually feel pretty ‘sheepish’ about that that I allowed that situation to go on as long as it has. For you see, I NEVER, EVER, do memory allocations without testing the return values for success. At least not in my Windows Api work where I routinely use HeapAlloc() to allocate memory. But for whatever reason my String Class has all totally unprotected calls to C++ new to allocate char/wchar_t arrays. Its been like that since I first started coding it about 10 years ago. And I’ve never had any failures regarding it. But that doesn’t make it right. In my personal use of it in my work I almost never work with really large strings or so many of them that Windows can’t handle the memory allocations. I guess that’s why it has never failed for me. In thinking about why I’ve left this untenable situation go on for so long all I can come up with is that testing the return from new for a non NULL pointer doesn’t do any good as rather than returning NULL for a memory allocation failure as is typical of all Win32 memory allocation functions, C++ raises an exception instead. And if the exception isn’t handled in a try/catch block then the app GPFs. So testing for NULL does no good. And since I don’t use C++ Exception Handling I’m out of luck. The only other issue here is that if the std::nothrow option is used with new instead new returns NULL on memory allocation failures rather than throwing an exception. So I decided to ‘run’ with that.

    So what I did was go through my whole String Class the past two days changing all the calls to new to work like that, i.e., use std::nothrow, and test for memory allocation failures. The other part of the puzzle is I added another member variable to the private member variables section of my Class named this->blnSucceeded. If a memory allocation fails I set this to FALSE obviously, so I think this more or less ‘hack’ of mine satisfactorily addresses the problem, at least to my way of thinking. And it didn’t seem to increase the size of the binaries anything noticeable. So attached is DynArrays.zip which contains my revised String Class (Strings.cpp and Strings.h), as well as Demo26.cpp which uses my templated CArray Class. Far as I know, you can create dynamic arrays of any type of object out to four dimensions. What Demo26.cpp does is create a three dimensional array of Strings whose output looks like this…

    Code:
    (0,0,0) (0,1,0) (0,2,0) (0,3,0) (0,4,0)
    (1,0,0) (1,1,0) (1,2,0) (1,3,0) (1,4,0)
    (2,0,0) (2,1,0) (2,2,0) (2,3,0) (2,4,0)
    (3,0,0) (3,1,0) (3,2,0) (3,3,0) (3,4,0)
    
    (0,0,1) (0,1,1) (0,2,1) (0,3,1) (0,4,1)
    (1,0,1) (1,1,1) (1,2,1) (1,3,1) (1,4,1)
    (2,0,1) (2,1,1) (2,2,1) (2,3,1) (2,4,1)
    (3,0,1) (3,1,1) (3,2,1) (3,3,1) (3,4,1)
    
    (0,0,2) (0,1,2) (0,2,2) (0,3,2) (0,4,2)
    (1,0,2) (1,1,2) (1,2,2) (1,3,2) (1,4,2)
    (2,0,2) (2,1,2) (2,2,2) (2,3,2) (2,4,2)
    (3,0,2) (3,1,2) (3,2,2) (3,3,2) (3,4,2)
    
    (0,0,3) (0,1,3) (0,2,3) (0,3,3) (0,4,3)
    (1,0,3) (1,1,3) (1,2,3) (1,3,3) (1,4,3)
    (2,0,3) (2,1,3) (2,2,3) (2,3,3) (2,4,3)
    (3,0,3) (3,1,3) (3,2,3) (3,3,3) (3,4,3)
    
    (0,0,4) (0,1,4) (0,2,4) (0,3,4) (0,4,4)
    (1,0,4) (1,1,4) (1,2,4) (1,3,4) (1,4,4)
    (2,0,4) (2,1,4) (2,2,4) (2,3,4) (2,4,4)
    (3,0,4) (3,1,4) (3,2,4) (3,3,4) (3,4,4)
    
    (0,0,5) (0,1,5) (0,2,5) (0,3,5) (0,4,5)
    (1,0,5) (1,1,5) (1,2,5) (1,3,5) (1,4,5)
    (2,0,5) (2,1,5) (2,2,5) (2,3,5) (2,4,5)
    (3,0,5) (3,1,5) (3,2,5) (3,3,5) (3,4,5)
    For a mental image think of a big apartment building six stories high taking up a whole city block. You can think of each apartment as having an x/y or col/row two dimensional position relative to the city streets alongside it. And there are six floors. The floor or z displacement is the 3rd dimension. Or alternately, think of the little wooded square building blocks babies play with where you can stack them one upon one another. Anyway, the indexes work like so…

    Code:
    ar_3(row, column, height)
    I named the array ar_3 in main and set it up like so…

    Code:
    int rows=4, cols=5, levels=6, i, j, k;
    
    CArray<String> ar_3(rows, cols, levels);
    Using my TCLib.lib the program compiles x64 UNICODE 5,632 bytes and I’ve no complaint about that! Using static linkage to the C Runtime brings it up to 145,408 bytes. Also interestingly, remarked out at the bottom of the source code file is the same program using my CArray Class, but using the C++ Standard Library’s String Class. That actually came out to 143,872 bytes – about 1,500 bytes smaller than my String Class, and I have no explanation for that. I find that startling actually. It’s the first occasion I’ve ever seen of the C++ Standard Library’s String Class building smaller than mine. But as I’ve mentioned before you can’t use the C++ Standard Library’s String Class with my TCLib.lib.

    Kind of a ‘twist’ to things I’d better mention is that I use my String Class with both MS VC and GCC and with MS VC I sometimes use standard C Runtime linkage and at other times I use my TCLib.lib. This presents something of a problem because I do not have the std::nothrow option with new implemented within my TCLib.lib. The actual internal implementation is this…

    Code:
    //=====================================================================================
    //               Developed As An Addition To Matt Pietrek's LibCTiny.lib
    //
    //                          LIBCTINY -- Matt Pietrek 2001
    //                           MSDN Magazine, January 2001
    //
    //                              With Help From Mike_V
    //                       
    //                           By Fred Harris, January 2016
    //
    //                    cl newdel.cpp /GS- /c /W3 /DWIN32_LEAN_AND_MEAN 
    //=====================================================================================
    #include <windows.h>
    
    void* __cdecl operator new(size_t s)
    {
     return HeapAlloc(GetProcessHeap(), 0, s);
    }
    
    void  __cdecl operator delete(void* p)
    {
     HeapFree(GetProcessHeap(), 0, p);
    }
    
    void* operator new [] (size_t s)
    {
     return HeapAlloc(GetProcessHeap(), 0, s);
    }
    
    void operator delete [] (void* p)
    {
     HeapFree(GetProcessHeap(), 0, p);
    }
    So since these implementations are just simple wrappers around HeapAlloc() calls which return NULL upon failure I don’t need the std::nothrow option with my TCLib.lib; just using new returns NULL on failure. But if I’m using standard linkages with the C and C++ runtimes I need to use new(std::nothrow) and #include <new>. The way I set it up so as to be able to have one codebase and master String Class for any usage is I set this up with a #define macro…

    Code:
    #ifdef TCLib
       #include "stdio.h"            // Since Microsoft Recently 'Refactored' The C Runtime, I Can't Use Their Includes With TCLib Anymore
       #include "stdlib.h"           // They've Apparently Added Some New function descriptors that screw things up for me.
       #define NEW new               // My newdel.cpp file has alternate definitions (simpler) of new and new []
       extern "C" int _fltused=1;    // I don't want to get into this here; concerns floating point support
    #else
       #include <cstdio>
       #include <cstdlib>
       #include <new>
       #define NEW new(std::nothrow)
    #endif
    So you have to be careful about defining or commenting out #define TCLib in both Strings.cpp and Demo26.cpp for whatever usage you are running, or you’ll get particularly nasty, ugly error messages. Definitely nastier than average ones!

    Lot of stuff I’m throwing at you here. Ask if you have any questions!

    Code:
    // cl Demo26.cpp Strings.cpp /O1 /Os /GS- /Zc:sizedDealloc- TCLib.lib kernel32.lib user32.lib    // <<< For Use With TCLib.lib
    // cl Demo26.cpp Strings.cpp /O1 /Os /GS- /EHs kernel32.lib user32.lib                           // <<< For Standard C Runtime Linkage
    // 145,408 Bytes C Runtime Linkage;   x64; VC19 (VS 2015); UNICODE
    //   5,632 Bytes TCLib Linkage;       x64; VC19 (VS 2015); UNICODE
    #define TCLib
    #ifndef UNICODE
       #define UNICODE
    #endif
    #ifndef _UNICODE
       #define _UNICODE
    #endif
    #include <windows.h>
    #ifdef TCLib
       #include "stdio.h"            // Since Microsoft Recently 'Refactored' The C Runtime, I Can't Use Their Includes With TCLib Anymore
       #include "stdlib.h"           // They've Apparently Added Some New function descriptors that screw things up for me.
       #define NEW new               // My newdel.cpp file has alternate definitions (simpler) of new and new []
       extern "C" int _fltused=1;    // I don't want to get into this here; concerns floating point support
    #else
       #include <cstdio>
       #include <cstdlib>
       #include <new>
       #define NEW new(std::nothrow)
    #endif
    #include "Strings.h"             // My String Class
    
    
    template <class datatype> class CArray            // This entity is a templated class for creating dynamic
    {                                                 // multi-dimensional arrays of from one to four dimensions.
     public:                                          // It allows for a basic language type syntax for doing
     CArray() : pObjs(0)                              // the above.  GNU C++ compilers implement dynamic array
     {                                                // allocation but MS VC++ compilers don't.  Since I wanted
      this->iNumObjects=0;                            // to test compile with both I developed this class.
      d1=d2=d3=d4=0;
     }
    
     CArray(int i) : pObjs(0)                         // One Dimensional Array Constructor
     {
      this->iNumObjects=i;
      this->pObjs = NEW datatype[this->iNumObjects]();
      d1=i, d2=0, d3=0, d4=0;
     }
    
     CArray(int i, int j) : pObjs(0)                  // Two Dimensional Array Constructor
     {
      this->iNumObjects=i*j;
      this->pObjs = NEW datatype[this->iNumObjects]();
      d1=i, d2=j, d3=0, d4=0;
     }
    
     CArray(int i, int j, int k) : pObjs(0)           // Three Dimensional Array Constructor
     {
      this->iNumObjects=i*j*k;
      this->pObjs = NEW datatype[this->iNumObjects]();
      d1=i, d2=j, d3=k, d4=0;
     }
    
     CArray(int i, int j, int k, int l) : pObjs(0)    // Four Dimensional Array Constructor
     {
      this->iNumObjects=i*j*k*l;
      this->pObjs = NEW datatype[this->iNumObjects]();
      d1=i, d2=j, d3=k, d4=l;
     }
    
     datatype& operator()(int i)                      // One Dimensional Accessor
     {
      return pObjs[i];
     }
    
     datatype& operator()(int i, int j)               // Two Dimensional Accessor
     {
      return pObjs[i*d2 + j];
     }
    
     datatype& operator()(int i, int j, int k)        // Three Dimensional Accessor
     {
      return pObjs[i*d2 + j + k*d1*d2];
     }
    
     datatype& operator()(int i, int j, int k, int l) // Four Dimensional Accessor
     {
      return pObjs[i*d2 + j + k*d1*d2 + l*d1*d2*d3];
     }
    
     bool blnMemoryIsGood()
     {
      return !!pObjs;
     }
    
     int UBound(int iDim)
     {
      if(iDim==1)
         return d1-1;
      if(iDim==2)
         return d2-1;
      if(iDim==3)
         return d3-1;
      if(iDim==4)
         return d4-1;
      else
         return 0;
     }
    
     ~CArray()
     {
      if(this->pObjs)
         delete [] this->pObjs;
     }
    
     private:
     datatype* pObjs;       // pointer to the base memory allocation for array
     int       iNumObjects; // We'll need this to zero memory for non-class types
     int       d1;          // Dimension #1
     int       d2;          // Dimension #2
     int       d3;          // Dimension #3
     int       d4;          // Dimension #4
    };
    
    
    template<typename t1> void Output(CArray<t1>& Ar3)
    {
     for(int h=0; h<=Ar3.UBound(3); h++)
     {
         for(int i=0; i<=Ar3.UBound(1); i++)
         {
             for(int j=0; j<=Ar3.UBound(2); j++)
                 wprintf(L"%s\t",Ar3(i,j,h).lpStr());
             printf("\n");
         }
         printf("\n");
     }
    }
    
    
    int main()
    {
     int rows=4,cols=5,levels=6,i,j,k;
     wchar_t szBuffer[16];
    
     CArray<String> ar_3(rows,cols,levels);
     for(i=0; i<=ar_3.UBound(3); i++)
     {
         for(j=0; j<=ar_3.UBound(1); j++)
         {
             for(k=0; k<=ar_3.UBound(2); k++)
             {
                 ar_3(j,k,i)=L"(";
                 swprintf(szBuffer,L"%d",j);
                 ar_3(j,k,i)=ar_3(j,k,i)+szBuffer+L",";
                 swprintf(szBuffer,L"%d",k);
                 ar_3(j,k,i)=ar_3(j,k,i)+szBuffer+L",";
                 swprintf(szBuffer,L"%d",i);
                 ar_3(j,k,i)=ar_3(j,k,i)+szBuffer+L")";
             }
         }
     }
     Output(ar_3);
     getchar();
    
     return 0;
    }
    
    
    #if 0
    
    (0,0,0) (0,1,0) (0,2,0) (0,3,0) (0,4,0)
    (1,0,0) (1,1,0) (1,2,0) (1,3,0) (1,4,0)
    (2,0,0) (2,1,0) (2,2,0) (2,3,0) (2,4,0)
    (3,0,0) (3,1,0) (3,2,0) (3,3,0) (3,4,0)
    
    (0,0,1) (0,1,1) (0,2,1) (0,3,1) (0,4,1)
    (1,0,1) (1,1,1) (1,2,1) (1,3,1) (1,4,1)
    (2,0,1) (2,1,1) (2,2,1) (2,3,1) (2,4,1)
    (3,0,1) (3,1,1) (3,2,1) (3,3,1) (3,4,1)
    
    (0,0,2) (0,1,2) (0,2,2) (0,3,2) (0,4,2)
    (1,0,2) (1,1,2) (1,2,2) (1,3,2) (1,4,2)
    (2,0,2) (2,1,2) (2,2,2) (2,3,2) (2,4,2)
    (3,0,2) (3,1,2) (3,2,2) (3,3,2) (3,4,2)
    
    (0,0,3) (0,1,3) (0,2,3) (0,3,3) (0,4,3)
    (1,0,3) (1,1,3) (1,2,3) (1,3,3) (1,4,3)
    (2,0,3) (2,1,3) (2,2,3) (2,3,3) (2,4,3)
    (3,0,3) (3,1,3) (3,2,3) (3,3,3) (3,4,3)
    
    (0,0,4) (0,1,4) (0,2,4) (0,3,4) (0,4,4)
    (1,0,4) (1,1,4) (1,2,4) (1,3,4) (1,4,4)
    (2,0,4) (2,1,4) (2,2,4) (2,3,4) (2,4,4)
    (3,0,4) (3,1,4) (3,2,4) (3,3,4) (3,4,4)
    
    (0,0,5) (0,1,5) (0,2,5) (0,3,5) (0,4,5)
    (1,0,5) (1,1,5) (1,2,5) (1,3,5) (1,4,5)
    (2,0,5) (2,1,5) (2,2,5) (2,3,5) (2,4,5)
    (3,0,5) (3,1,5) (3,2,5) (3,3,5) (3,4,5)
    
    #endif
    Attached Files Attached Files

  11. #26
    Registered User
    Join Date
    Dec 2010
    Location
    Trinidad, CO (log cabin in middle of nowhere)
    Posts
    148
    Are there any situations when sharing a windows procedure might come in handy?
    Absolutely! That's why I posted that multiple windows per Window Procedure app. Certainly one can get creative with one's coding in solving problems, and there may be other examples than this one, but think back to that 'grid' app I posted awhile back, where I registered a 'grid' Window Class, and created an instance of it on a parent or main application window/form/dialog. Of course, I didn't flush out all the details of creating an actual "Grid" Control, but the basic idea was all there. Now think of the "cells" of the grid control, which I didn't implement at that time. These could be yet another Window Class and another Window Procedure. And in the WM_CREATE code of the "Grid" instance, which recall (now brace yourself for this one, its gonna hurt) is executing within the WM_CREATE code for the Main Application Window whose CreateWindow() call has not yet returned, you would make multiple CreateWindow() calls for as many "Cell" Window Class instances as you would need cells for the grid, i.e., if you needed 12 rows of cells and 6 columns that would be 72 "Cell" instances which you would get with a double for loop. And that means that 72 WM_CREATE messages will be executed within the "Cell" Classes' Window Procedure, which is executing within the "Grid" Classes' WM_CREATE handler of its Window Procedure, which is executing within the Main Applications WM_CREATE handler of its Window Procedure, and all of this activitry is occurring before a HWND is returned in WinMain for the Main Application Window!

    So now your grid is visible on screen and you can see all the cells. If you click in any of them the Window Procedure for the "Cell" Class will receive a WM_LBUTTONDOWN message, and the HWND 1st parameter of the Window Procedure will reveal which of the 72 cells the click occurred in, just as with the app with four top level windows I posted previously. Its one mean machine!

  12. #27
    Registered User
    Join Date
    Jul 2015
    Posts
    64
    Alright Freddie,

    Just wanted to say a big cheers for all the material and discussion you've provided! I've been away for a good week or so because I've had an important paper to write, but I shall work my way through the material once I'm back into some sort of routine and development mind set. Just wanted to let you know that I appreciate all your tuition thus far and, whilst your TCLib efforts are far beyond me at the moment, I look forward to diving into the depths of low level runtimes when I'm more primed to deal with that sort of thing in the
    future.

    Thanks for all your time and apologies, everyone, for the slight bump.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Windows Internals book
    By pc2-brazil in forum Windows Programming
    Replies: 1
    Last Post: 05-14-2013, 03:11 AM
  2. Internals of OPENMP?
    By chiku123 in forum C++ Programming
    Replies: 1
    Last Post: 10-23-2007, 10:19 AM
  3. modal and modeless
    By Bleech in forum Windows Programming
    Replies: 2
    Last Post: 12-02-2006, 06:07 PM
  4. The internet's internals revealed
    By valis in forum A Brief History of Cprogramming.com
    Replies: 22
    Last Post: 07-29-2006, 08:13 AM
  5. problem with A simple modeless messagebox
    By hanhao in forum C++ Programming
    Replies: 8
    Last Post: 07-05-2005, 11:18 PM

Tags for this Thread