Thread: Control handle declaration conventions in large WinAPI projects

  1. #1
    Registered User
    Join Date
    Jul 2015
    Posts
    64

    Control handle declaration conventions in large WinAPI projects

    Hey all.

    So I'm currently developing a relatively large raw Win32/WinAPI (what's the correct terminology these days?) application in C++. I have tried to abide by the typical C++ conventions as much as possible, but this seemingly complicates things under the umbrella of the Windows API. As an example:

    Globals are bad; don't use globals! - Pretty much every C++ programmer I've ever met
    It really feels like global variable declarations are necessary to keeps things legible. As it stands at the moment, I'm creating a window that contains about 40 child controls. Wanting to avoid globals as much as possible as per my C++/Jedi training, I decided to declare every HWND as static at the beginning of the window procedure (before switch casing the MSG parameter) and creating the controls in my WM_CREATE handling. As you can imagine, this got pretty ugly pretty fast.

    So I'm wanting to contain all of the control-creation code in a single function (in an external .cpp file). Obviously, I could declare all of the handles in the global namespace, but I've pretty much had the "avoid-globals" mentality hammered into my head since birth. Alternatively, I could encapsulate the handles into a class or structure, declare a static instance of that object at the beginning of the WNDPROC callback and, wherever necessary, pass this object by reference to external functions.

    Is there a typical convention regarding these architectural decisions? What are the pros and cons of each method and are there any other options that I haven't considered yet? I'm wondering if perhaps globals are simply more acceptable when GUI programming.

    While I'm here, something odd is happening with my copy of Visual Studio (Pro, 2015). To enable visual styles in my program, I used the following directive:

    Code:
    #pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
    Sometimes it compiles with visual styles enabled, sometimes it doesn't. The annoying thing is is that there is no real discernible pattern to it. Sometimes my compiler will throw:

    Code:
    Failed to write the updated manifest to the resource of file ".exe file". Not enough storage is available to process this command.
    I thought the above error might be related; following a second attempt at compilation, however, the exe is generated fine but, again, sometimes visual styles are enabled and sometimes they are not.

    Has anyone else experienced this?
    Many thanks for your time guys; it is always appreciated!

  2. #2
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    I think of function statics as just globals, but with reduced scope of access. To map a C API into a C++ organization, you typically want to associate a "this" pointer with the C construct (callback or handle object). For most C objects, I use aggregation:
    Code:
    /*------------------------------------------------------------------------------
    no_copy - Inherit from this to disallow copy semantics.
    ------------------------------------------------------------------------------*/
    class no_copy
    {
    protected:
        no_copy() {}
        ~no_copy() {}
    private:
        no_copy(const no_copy&);
        const no_copy& operator=(const no_copy&);
    };//no_copy
    
    /*------------------------------------------------------------------------------
    CriticalSection - C++ version of CRITICAL_SECTION
    ------------------------------------------------------------------------------*/
    class CriticalSection : public no_copy
    {
        CRITICAL_SECTION m_cs;
    public:
    
        CriticalSection()
        {
            if (!::InitializeCriticalSectionAndSpinCount(&m_cs, 4000))
                ::InitializeCriticalSection(&m_cs);
        }
        ~CriticalSection() {::DeleteCriticalSection(&m_cs);}
        void Acquire() {::EnterCriticalSection(&m_cs);}
        void Release() {::LeaveCriticalSection(&m_cs);}
    };//CriticalSection
    
    /*------------------------------------------------------------------------------
    Event - C++ version of a Win32 Event
    ------------------------------------------------------------------------------*/
    class Event : public no_copy
    {
        HANDLE m_handle;
    public:
        Event() : m_handle(0) {}
        ~Event() {Close();}
    
        operator HANDLE() {return m_handle;}
    
        void Close() {if (m_handle) ::CloseHandle(m_handle);}
    
        bool Create(bool manualReset = false, bool initialOwner = false,
                    const wchar_t *name = 0, LPSECURITY_ATTRIBUTES lpMuxAttrib = 0)
        {
            m_handle = ::CreateEventW(lpMuxAttrib, manualReset, initialOwner, name);
            return m_handle != 0;
        }//Create
    
        bool Open(const wchar_t *name, DWORD access = EVENT_MODIFY_STATE,
                  bool bInheritable = false)
        {
            m_handle = ::OpenEventW(access, bInheritable ? TRUE : FALSE, name);
            return m_handle != 0;
        }//Open
    
        bool IsValid() {return m_handle != 0;}
        bool Signal() {return ::SetEvent(m_handle) != FALSE;}
        bool Reset() {return ::ResetEvent(m_handle) != FALSE;}
        bool IsSignaled(DWORD to = 0)
            {return ::WaitForSingleObject(m_handle, to) == WAIT_OBJECT_0;}
    };//Event
    For an HWND object, there are a couple of design decisions and paths you could take, but it can get tricky. Below is a post with lots of links to explore. I like the thunk method myself.

    Member functions as child window procedures

    gg

  3. #3
    Registered User
    Join Date
    Dec 2010
    Location
    Trinidad, CO (log cabin in middle of nowhere)
    Posts
    148
    I create massive Win32 SDK (that's the correct terminology, I believe) style apps for use in large enterprise settings that involve tens of thousands of lines of code and dozens of Forms/Windows/Dialogs. And there are no global variables in any of them - nor statiics which I view as just cheap globals.

    Realize that when you create a Window in Windows - based of course on a Window Class, Windows the operating system knows (it has cached internally the info) everything there is to know about that window including the HWNDs of every child window control you have placed on whatever Form/Window/Dialog you have created. And its perfectly happy to return that information to you whenever and wherever you want it within your application! Those HWNDs are just a function call away. The magic function is GetDlgItem(). It has two parameters and returns the HWND of the requested window/control/whatever. The first parameter is the HWND of the parent window containing the child window control and the second parameter is the control ID of the child window control which you have set yourself in the HMENU parameter of the CreateWindow() or CreateWindowEx() call you used to create the control.

    That being the case, its hardly necessary to even do anything with the HWND returned by CreateWindow() calls for child window controls. In my WM_CREATE code I usually just define one ...

    Code:
    HWND hCtrl=NULL;
    ...local variable, and use that same variable for the return for every child window control I create. So essentially I'm throwing every one away with each CreateWindow() call! The reason I can treat them so caviliery is that I can get them back whenever I want them with GetDlgItem().
    Last edited by freddie; 06-16-2016 at 05:53 AM.

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

    For an HWND object, there are a couple of design decisions and paths you could take, but it can get tricky.
    There's nothing tricky about it. Its as simple as dirt. Like I said, Windows has already internally cached everything there is to know about every window you have created and any of it is just one function call away. And even better, you don't have to create the function yourself as its provided by the OS. That function is GetDlgItem(), and it works with either Windows Dialogs created by the Windows Dialog Engine or SDK Windows created by CreateWindowEx() calls.

    Now, if your goal is to just produce volumes of tricky and buggy code that accomplishes little besides add kilobytes to your application you can do all sorts of wierd science with C++.

  5. #5
    Registered User
    Join Date
    Dec 2010
    Location
    Trinidad, CO (log cabin in middle of nowhere)
    Posts
    148
    Typically someone with a C++ background will look at the Windows Api and say to him/herself "I need to somehow make classes out of all this stuff". Looking at the world through those 'C++ Colored Classes' tends to send one in the direction of creating C++ Classes which contain HWNDs of child window controls as member variables. I think it seldom if ever occurs to such folk that the Windows Api is C based OOP and those HWNDs of child window controls are already part of objects internal to the Windows Operating System which you can retrieve with just one function call. So when you create a class to encapsulate a Windows Api object you are just duplicating code and adding absolutely NOTHING to base functionality but you are adding complexity and unnecessary kilobytes to your executable.

  6. #6
    Registered User
    Join Date
    Dec 2010
    Location
    Trinidad, CO (log cabin in middle of nowhere)
    Posts
    148
    Here is an example of what I mean. There are no globals or statics, and I retrieve any HWND I want whenever I want it with GetDlgItem()...

    Code:
    //Main.cpp           //  8192 bytes : No String Class, i.e., using C style null terminated char buffers (MinGW 4.4.1)
    #define UNICODE      // 60928 bytes : C++ String Class, i.e., when using std::string or std::wstring    (MinGW 4.4.1)
    #define _UNICODE
    #include <windows.h>
    #include <tchar.h>
    #include <string>
    #ifdef UNICODE
       #define String std::wstring
    #else
       #define String std::string
    #endif
    #define  IDC_TEXT     1500
    #define  IDC_BUTTON   1505
    
    LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
    {
     switch(msg)
     {
       case WM_CREATE:
         {
            HWND hCtl;
            HINSTANCE hIns=((LPCREATESTRUCT)lParam)->hInstance;
            hCtl=CreateWindowEx(WS_EX_CLIENTEDGE,_T("edit"),_T(""),WS_CHILD|WS_VISIBLE,15,15,285,22,hwnd,(HMENU)IDC_TEXT,hIns,0);
            hCtl=CreateWindowEx(0,_T("button"),_T("Get Text"),WS_CHILD|WS_VISIBLE,115,55,80,30,hwnd,(HMENU)IDC_BUTTON,hIns,0);
            return 0L;
         }
       case WM_COMMAND:
         {
            if(LOWORD(wParam)==IDC_BUTTON)
            {
               TCHAR szBuffer[256];
               GetWindowText(GetDlgItem(hwnd,IDC_TEXT),szBuffer,256);
               String strText=szBuffer;                                                 //use if using std::string or std::wstring
               MessageBox(hwnd,strText.c_str(),_T("Here Is What You Typed..."),MB_OK);  //for text box output
               //MessageBox(hwnd,szBuffer,_T("Here Is What You Typed..."),MB_OK);       //use instead if using null terminated buffer
            }
            return 0L;
         }
       case WM_DESTROY:
         {
            PostQuitMessage(0);
            return 0;
         }
     }
    
     return (DefWindowProc(hwnd, msg, wParam, lParam));
    }
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int iShow)
    {
     TCHAR szClassName[]=_T("Form4");
     WNDCLASSEX wc;
     MSG messages;
     HWND hWnd;
    
     wc.lpszClassName  =  szClassName;
     wc.lpfnWndProc    =  fnWndProc;
     wc.cbSize         =  sizeof (WNDCLASSEX);
     wc.style          =  0;
     wc.hIcon          =  LoadIcon(NULL,IDI_APPLICATION);
     wc.hInstance      =  hInstance;
     wc.hIconSm        =  0;
     wc.hCursor        =  LoadCursor(NULL,IDC_ARROW);
     wc.hbrBackground  =  (HBRUSH)COLOR_BTNSHADOW;
     wc.cbWndExtra     =  0;
     wc.cbClsExtra     =  0;
     wc.lpszMenuName   =  NULL;
     RegisterClassEx(&wc),
     hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,200,200,330,135,HWND_DESKTOP,0,hInstance,0);
     ShowWindow(hWnd,iShow);
     while(GetMessage(&messages,NULL,0,0))
     {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
     }
    
     return messages.wParam;
    }
    That program above is from my board here where I compare Pure SDK techniques with Class Framework code...

    ProgEx41: Class Frameworks

  7. #7
    Registered User
    Join Date
    Jul 2015
    Posts
    64
    Thank you both very much for such detailed responses. I'm genuinely glad to hear that my approach thus far has been wrong; there's no way that any object-oriented code should be as messy as mine currently is. I'll pour through the material that you guys have provided and make a decision; as it stands at the moment, I'm leaning more towards Freddie's GetDlgItem() method because it certainly does seem like the cleaner and simpler way to go about things. I had actually briefly considered obtaining control handles from the system when needed quite early on in development, but I was unsure about what sort of performance impact that repeated calls to handle-returning functions would have; I gather now though that this overhead is negligible?

    With regards to wrapping C-style system objects in C++ classes, could it not be argued that one might as well get acquainted with MFC and have done with it? Don't get me wrong, I appreciate the code samples hugely because it gives me some sort of idea about how libraries like MFC might be constructed, but it does seem (at least to my inexperienced eyes,) a bit redundant to wrap Windows API functionality in custom C++ wrappers when that proverbial "wheel" has already been invented. Am I thinking about this in the wrong way?

  8. #8
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> ... but it can get tricky.
    The tricky part is in using a non-static member function (typically virtual) as your windows procedure - for both your own registered windows classes and for the built-in classes (like STATIC, EDIT, etc...). There are two ways to do this "purely" - the way MFC does it (with hooks) and the way ATL does it (with thunks).

    The alternative is to create the window and use the resulting HWND to stash a this pointer (using SetWindowLong() or SetProp()) which can then be used to forward a message from a C windows procedure to "this->WinProc()". The main disadvantage here is that there are several messages dispatched during the CreateWindow() call, before the HWND is returned. This is another problem that can be solved several different ways.

    >>... could it not be argued that one might as well get acquainted with MFC and have done with it?
    It could. There's also WTL, and other C++ frameworks on the web.

    gg

  9. #9
    Registered User
    Join Date
    Dec 2010
    Location
    Trinidad, CO (log cabin in middle of nowhere)
    Posts
    148
    I'm genuinely glad to hear that my approach thus far has been wrong; there's no way that any object-oriented code should be as messy as mine currently is.
    Whenever you can produce clean and clear code that helps a real real lot in my opinion. Since you mentioned you are doing what I term straight SDK code, one area I might mention that can be improved a lot is the Window Procedure and the way messages are associated with the code that handles them. Typically and I might add almost universally a switch logic construct is used to map an incoming message with the code block within the switch construct that handles it. So in other words, message handling code for WM_CREATE, WM_SIZE, WM_PAINT, WM_COMMAND, WM_DESTROY, etc., ad infinitum, is located within this one procedure. So carried to the extreme, you could conceivably end up with a 50,000 line program with two procedures, i.e., a WinMain() and a 49,950 line Window Procedure (the 1st couple years of my Windows Api programming ended up just like that!).

    That’s not good. The first level of modularization should be to break up each message into a separate procedure. All the Class Frameworks in whatever language you choose to use have done this for years, going all the way back to VBDOS in 1991. The techniques for doing this can range from fairly simple to somewhat sophisticated. I’ve written a tutorial on it located here…

    ProgEx39 -- Function Pointers For Modularizing Our Window Procedures

    With that technique I iterate through a structure with a for loop that associates the integral value of each message received in the Window Procedure with the integral value of the address of a separate event/message handling procedure that handles that message. When a match is made I call the message handling function through a function pointer. Using that technique my Window Procedures are the same size (approx. 15 lines) no matter how large the application becomes or how many messages are handled.

    Getting back to the issue of retrieving HWNDs from the system…

    I had actually briefly considered obtaining control handles from the system when needed quite early on in development, but I was unsure about what sort of performance impact that repeated calls to handle-returning functions would have; I gather now though that this overhead is negligible?
    The performance impact wouldn’t be much different than retrieving the value of any member variable in any class within your app that you would create yourself.

    With regards to wrapping C-style system objects in C++ classes, could it not be argued that one might as well get acquainted with MFC and have done with it? Don't get me wrong, I appreciate the code samples hugely because it gives me some sort of idea about how libraries like MFC might be constructed, but it does seem (at least to my inexperienced eyes,) a bit redundant to wrap Windows API functionality in custom C++ wrappers when that proverbial "wheel" has already been invented. Am I thinking about this in the wrong way?
    There’s certainly a whole lot could be said about that. And I’ll only say a little bit. I think it depends a lot on your goals and interests and coding background. I go all the way back to mainframes and keypunched cards of the 1970s. When PCs came out in the 80s hard drives and memory was tight. So I learned coding in environments where small program sizes and low memory/resource usages were critical. I’d say I pretty thoroughly assimilated those values.

    But nowadays those values might be close to useless as memory and storage space seems to be approaching infinity. So in that environment today’s coders have adopted the coding philosophy of wrap everything and encapsulate everything. The more layers of classes one can wrap around the ‘horrible grunge’ of C style procedural Apis the better. In that way one won’t be ‘profaned’ by touching the unholy mess.

    But my values which were assimilated in that earlier era can’t be changed. I like small, tight code. So I look hard at every encapsulation I make, and severely weight the cost in k against the benefits to me of the encapsulation. If it’s really nasty code and it doesn’t cost too much, I’ll wrap (classes, whatever).

    The Class Frameworks cost too much for my liking, so I don’t use them. Last I looked MFC was something like 1.5 MB. God only knows what it is now. I believe the cross platform toolkits are all much larger.

    Anyway, that’s why I code straight SDK style. As a matter of fact, I consider the Windows Api as a work of near genius. To me, its intellectually beautiful. I like it just the way it is, and my goal has never been to hide it in C++ classes. When it was created in the early 80s the ideas and core concepts of OOP were known, and those concepts were used in creating an OOP C based Api. The Windows Api is what Objected Oriented Programming looks like in C. Take for example the GetDlgItem() function we’ve been discussing. The 1st parameter of that function is the HWND of the window for which you wish to retrieve a child window control HWND. A HWND is what’s known in computer science as an ‘opaque pointer’ . Its defined in the Windows’ headers in a rather weird manner, but it boils down to being a void*. What it actually is, is a ‘this’ pointer to a virtual memory block in Windows where all the data about the parent is located. That is no different whatsoever from a C++ Class. A C++ class will in memory consist of a block of memory created by the compiler where are located all the member variables contained within the class, and function pointers to all the member functions. So that ‘this’ pointer in C based OOP will always be the first parameter of a member function call into the memory area of some particular object.
    So I’m guessing most folks who do SDK style apps for Windows like me are mostly interested in small code. Another issue might be rapid application development. If that’s necessary then a RAD environment might be chosen over small code size. In my case, my work situation is such that I can plow as much time into getting my apps right as I need. I only have a couple rather large mission critical apps that I’ve developed and I maintain, and my work on them is something of a labor of love. And I don’t have to convert them to Linux, OSX, or anything else, so cross platform isn’t an issue.

    In terms of a person’s abilities and inclinations, I personally found Windows SDK Api coding to be easier than MFC. Its supposed to be the other way around I guess, but for me MFC was hard. I never completely mastered it. Perhaps at the time I was working on it, which was a long time ago, my C++ skills weren’t up to snuff.

    Enough said for now with my ramblings!

  10. #10
    Registered User
    Join Date
    Jul 2015
    Posts
    64
    Freddie, that function pointers technique is exactly the sort of thing I was looking for; thank you! I shall implement that today.

    It seems that were share some similar values with respect to code organisation and minimal resource usage. I also am a huge fan of the way the Windows API was constructed and, in fact, it made me wonder whether to switch to C instead of C++. (Having said that though, I'd be lost without the std::string and std:vector vector classes, so maybe not.) I think part of the reason behind my gravitation to raw Win32 programming is that it has helped me further my understanding of operating system concepts immensely. (Well, it has helped me further my understanding of Windows internals, specifically. Windows is always quoted to be an event-driven operating system. This makes me wonder: what other operating system paradigms exist?)

    If you don't mind me asking for further advice, if one does request handles from the system on the fly, what would be the best way to manage device contexts? Should I create and destroy them (or get and release them) as and when needed? i.e. If I outsourced my message handling to external functions, should I cleanup at the end of the functions as opposed to freeing GDI resources when handling WM_CLOSE? Also, I have an instance of a class that will be using throughout the uptime of the program to interface with a Windows service; is there a better way to reference this class in my event handling functions other than passing them pointers to a statically declared instance of the object? (Again, I'm trying to follow your advice and avoid statically declaring an instance of the class in the Windows procedure before the switch.

    Many thanks, I am learning a lot!

    P.S. If I ever need to write portable code, I shall then consider writing wrapper classes. It's nice to know that the option is always there; thanks for the pointers.

  11. #11
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    For very simple and light-weight wrappers of objects (like GDI objects) I recommend you take a look at the WTL headers and mimic what they try to achieve. I did something very similar in my own thunk based wrapper's, which are used on ARM, SHx, MIPS, and x86 devices - and required an "embedded" mentality when writing Win API code for those guys.

    >>... what would be the best way to manage device contexts?
    Take a look at atlgdi.h in WTL. You'll see several simple wrappers for the various types of DC's - CDC (one you create), CDCHandle (one you didn't create), CPaintDC, CClientDC, CWindowDC, and CMemoryDC.

    In my mind, one of the best things the C++ brings to the table will these types of wrappers are support for RAII. In simple terms, releasing resources in the destructor. For example:
    Code:
    ~CWindowDC() {::ReleaseDC( ... );}
    
    ~CPaintDC() {::EndPaint( ... );}
    This allows you to write code without having to worry about cleanup - allowing you to return at any point in your code if need be. And if you use exceptions, it will be exception safe code as well.

    >> ... other than passing them pointers to a statically declared instance of the object?
    If you are passing the object around (as opposed to referencing a global), then you're already doing the right thing. Sounds like this object can simply be declared within WinMain - so that when main exits, the object's destructor is invoked.

    This is much more important when dealing with Dll's, since the global object constructors and destructors are run within the context of DllMain.

    gg

  12. #12
    Registered User
    Join Date
    Jul 2015
    Posts
    64
    Cheers Codeplug, I'll take a look at the WTL headers.

    When you say the object can probably be declared in WinMain, it certainly does make more sense, semantically, to do it that way. To access the object in the windows procedure, would it be sufficient and good practice to allocate extra memory in the window class via SetWindowLongPtr and storing a pointer to the object in this memory for retrieval via GetWindowLong?

  13. #13
    Registered User
    Join Date
    Dec 2010
    Location
    Trinidad, CO (log cabin in middle of nowhere)
    Posts
    148
    Real glad you found that useful Abyssion. After I posted a link to that it occurred to me that a couple years back I made a slight but I think beneficial modification to that procedure that's not included in that link. Instead of defining the WndEventArgs parameter as a pointer to one of those as I have in that tutorial, i.e. lpWndEventArgs, I changed it to a reference, which is more of a C++ idiom I think. So here is that GetDlgItem() program with the text box and button I posted a couple posts ago redone in the style I now use with the reference parameter...

    Code:
    //Main.h
    #ifndef Main_h
    #define Main_h
    
    #define dim(x) (sizeof(x) / sizeof(x[0]))
    
    #define IDC_EDIT              2000
    #define IDC_BUTTON            2005
    
    struct WndEventArgs
    {
     HWND                         hWnd;
     WPARAM                       wParam;
     LPARAM                       lParam;
     HINSTANCE                    hIns;
    };
    
    LRESULT fnWndProc_OnCreate    (WndEventArgs& Wea);
    LRESULT fnWndProc_OnCommand   (WndEventArgs& Wea);
    LRESULT fnWndProc_OnDestroy   (WndEventArgs& Wea);
    
    struct EVENTHANDLER
    {
     unsigned int                 iMsg;
     LRESULT                      (*fnPtr)(WndEventArgs&);
    };
    
    const EVENTHANDLER EventHandler[]=
    {
     {WM_CREATE,                  fnWndProc_OnCreate},
     {WM_COMMAND,                 fnWndProc_OnCommand},
     {WM_DESTROY,                 fnWndProc_OnDestroy}
    };
    #endif
    Code:
    //Main.cpp
    #include <windows.h>
    #include <tchar.h>
    #include "Form1.h"
    
    
    LRESULT fnWndProc_OnCreate(WndEventArgs& Wea)
    {
     Wea.hIns=((LPCREATESTRUCT)Wea.lParam)->hInstance;
     CreateWindowEx(WS_EX_CLIENTEDGE,_T("edit"),_T(""),WS_CHILD|WS_VISIBLE,25,35,255,25,Wea.hWnd,(HMENU)IDC_EDIT,Wea.hIns,0);
     CreateWindowEx(0,_T("button"),_T("Press To Extract Text"),WS_CHILD|WS_VISIBLE,55,85,200,25,Wea.hWnd,(HMENU)IDC_BUTTON,Wea.hIns,0);
    
     return 0;
    }
    
    
    LRESULT fnWndProc_OnCommand(WndEventArgs& Wea)
    {
     switch(LOWORD(Wea.wParam))
     {
       case IDC_BUTTON:
         {
            if(HIWORD(Wea.wParam)==BN_CLICKED)
            {
               TCHAR szBuffer[256];
               GetWindowText(GetDlgItem(Wea.hWnd,IDC_EDIT),szBuffer,255);
               MessageBox(Wea.hWnd,szBuffer,_T("Clicked Button!"),MB_OK);
            }
         }
         break;
     }
    
     return 0;
    }
    
    
    LRESULT fnWndProc_OnDestroy(WndEventArgs& Wea)
    {
     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].iMsg==msg)
         {
            Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
            return (*EventHandler[i].fnPtr)(Wea);
         }
     }
    
     return (DefWindowProc(hwnd, msg, wParam, lParam));
    }
    
    
    int WINAPI WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
    {
     TCHAR szClassName[]=_T("Form1");
     WNDCLASSEX wc;
     MSG messages;
     HWND hWnd;
    
     wc.lpszClassName=szClassName;                wc.lpfnWndProc=fnWndProc;
     wc.cbSize=sizeof (WNDCLASSEX);               wc.style=0;
     wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);     wc.hInstance=hIns;
     wc.hIconSm=NULL;                             wc.hCursor=LoadCursor(NULL,IDC_ARROW);
     wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;    wc.cbWndExtra=0;
     wc.lpszMenuName=NULL;                        wc.cbClsExtra=0;
     RegisterClassEx(&wc);
     hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,75,75,320,200,HWND_DESKTOP,0,hIns,0);
     ShowWindow(hWnd,iShow);
     while(GetMessage(&messages,NULL,0,0))
     {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
     }
    
     return messages.wParam;
    }
    If you compare it close you'll see what I mean. Not a major change but worth noting I believe. Also note above I used CreateWindow() calls for the child window controls without even assigning the returned HWND to a local HWND variable. Just throw it away. That's fine and doesn't interfere with the functionality of the program in any way.

    About being sympathetic to me about my passion for minimizing the sizes of my programs, you shouldn't get me going on that! Its one of my pet topics! Just to give you an idea of what's possible with C++, I can build the above program to a 3,584 byte stand alone executable in 64 bit with MSVC using my own version of the C Standard Library which I've developed and named TCLib.lib. The command line compilation string would be this...

    cl Main.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib user32.lib

    In contrast, with standard linkages to the C runtime (LIBCMT.LIB) which is the default with Visual C++ the executable is 39,936 with VC15 from Visual Studio 2008 in 64 bit...

    cl Main.cpp /O1 /Os /MT kernel32.lib user32.lib

    The main thing I've worked on this year is the development of replacements for the C and C++ Standard Libraries so that I can avoid the bloat of modern software development.

    Now, with regard to your question of how to persist objects not created at global/static scope across function calls, i.e., have them accessable throughout program run duration, any such needed object(s) I instantiate on the heap with new (or HeapAlloc()) during construction of my main program window in its WM_CREATE handler. Then, as with any data logically associated with the main program/window object, I store the pointer to that object as member data within the instantiated instance. The C ism of achieving this sort of OOP is not as elegant as with C++. What you have is the instantiated Class instance represented by the WNDCLASSEX object. One of the members of that object is...

    WNDCLASSEX::cbWndExtra

    ...and here one can specify howsoever many extra bytes one needs to store whatever. The SetWindowLongPtr() and GetWindowLongPtr() functions are the getters/setters (accessors in OOP speak). A ridiculously simple example should suffice. The purpose of this program is to calculate and display in a GUI window the dimensions of a box (I should be able to retire on the proceeds geneated by this 'must have' killer app!). (And I see by your later reply to CodePlug that you already understand this stuff). So CBox.h and CBox.cpp are a Class modeling a box...

    Code:
    #ifndef CBox_h
    #define CBox_h
    
    class CBox
    {
     public:
     CBox(double,double,double);  //Constructor
     ~CBox();                     //Destructor
     double GetLength();          //m_Length accessor
     double GetWidth();           //m_Width accessor
     double GetHeight();          //m_Height accessor
     double Volume();             //Returns Volume() of Box
    
     private:
     double m_Length;
     double m_Width;
     double m_Height;
    };
    #endif
    Code:
    //CBox.cpp
    #include "CBox.h"
    
    CBox::CBox(double dblLength, double dblWidth, double dblHeight)
    {
     this->m_Length=dblLength;
     this->m_Width=dblWidth;
     this->m_Height=dblHeight;
    }
    
    CBox::~CBox()
    {
     //destructor
    }
    
    double CBox::GetLength()
    {
     return this->m_Length;
    }
    
    double CBox::GetWidth()
    {
     return this->m_Width;
    }
    
    double CBox::GetHeight()
    {
     return this->m_Height;
    }
    
    double CBox::Volume()
    {
     return m_Length*m_Width*m_Height;
    }
    Our GUI main window represented by Main.cpp and Main.h instantiate one of these CBoxes in the WM_CREATE handler - fnWndProc_OnCreate(), which is the Constructor for the CBox_Window_Class RegisterClassEx()'ed down in WinMain(). And note it is indeed a C based Constructor as with C based OOP you'll inevitably have a constructor function such As CreateWindowEx() which returns a pointer to the newly created object. Further evidence that it is a Constructor can be seen by the fact that the code in the WM_CREATE handler is executing 'within' the CreateWindowEx() call in WinMain(), i.e., CreateWindowEx() down in WinMain() won't return and assign a HWND to hWnd in WinMain() until the WM_CREATE handler code returns with a 0. If a -1 is returned in fnWndProc_OnCreate() the CreateWindowEx() call in WinMain() will fail and hWnd will remain a NULL pointer.

    But in fnWndProc_OnCreate() we declare a local stack based CBox* pointer pBox and create an instance of a CBox object with new that has sides 2.0, 3.0, and 4.0. We then assign this pointer virtual address to our main window instance's WNDCLASSEX::cbWndExtra bytes at offset zero. Then the local CBox* object goes out of scope and the pointer is destroyed but the CBox object lives on within the program's heap, simply because delete was never called on it....

    Code:
    // Main.cpp                                       //C++ Object Oriented Program using native
    #include <windows.h>                              //Windows C Api to display the dimensions
    #include <stdio.h>                                //and volume of a class CBox object. Note
    #include "Main.h"                                 //that this program contains no global
    #include "CBox.h"                                 //variables.
    
    
    LRESULT fnWndProc_OnCreate(WndEventArgs& Wea)     //When the window object is created, create
    {                                                 //a CBox object and store/set a pointer to
     CBox* pBox=NULL;                                 //in the Window Class struct.  If the
                                                      //memory allocation (new) fails, return -1
     pBox=new CBox(2.0,3.0,4.0);                      //and program initialization will halt and
     if(pBox)                                         //WinMain() will exit.  A WM_CREATE message
        SetWindowLongPtr(Wea.hWnd,0,(LONG_PTR)pBox);  //is received one time on Window creation.
     else                                             //The CreateWindow() call in WinMain() won't
        return -1;                                    //return until this procedure returns.
    
     return 0;
    }
    
    
    LRESULT fnWndProc_OnPaint(WndEventArgs& Wea)      //Windows will send a WM_PAINT message to
    {                                                 //a window when any part of the window
     CBox* pBox=NULL;                                 //becomes 'invalid' and needs to be
     HFONT hFont,hTmp;                                //repainted.  This will occur at program
     PAINTSTRUCT ps;                                  //start up and any time the window is
     HDC hDC;                                         //uncovered by another window.  A pointer
                                                      //to the window's CBox object is stored
     char szBuffer[128];                              //in the window's Class Extra Bytes
     hDC=BeginPaint(Wea.hWnd,&ps);                    //( .cbWndExtra ), and is here accessed
     SetBkMode(hDC,TRANSPARENT);                      //using the Api function GetWindowLong().
     hFont=CreateFont                                 //To draw to a window in a WM_PAINT...
     (
      28,0,0,0,FW_HEAVY,0,0,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,      //handler one may use
      CLIP_DEFAULT_PRECIS,PROOF_QUALITY,DEFAULT_PITCH,"Courier New" //TextOut(), but that
     );                                                             //function requires a
     hTmp=(HFONT)SelectObject(hDC,hFont);                           //handle to a device
     pBox=(CBox*)GetWindowLongPtr(Wea.hWnd,0);                      //context obtainable
     sprintf(szBuffer,"Box.GetLength() = %6.2f",pBox->GetLength()); //with BeginPaint().
     TextOut(hDC,25,30,szBuffer,strlen(szBuffer));                  //The C Runtime func
     sprintf(szBuffer,"Box.GetWidth()  = %6.2f",pBox->GetWidth());  //sprintf can be used
     TextOut(hDC,25,60,szBuffer,strlen(szBuffer));                  //to output a text string
     sprintf(szBuffer,"Box.GetHeight() = %6.2f",pBox->GetHeight()); //to an output buffer,
     TextOut(hDC,25,90,szBuffer,strlen(szBuffer));                  //and then TextOut()
     sprintf(szBuffer,"Box.Volume()    = %6.2f",pBox->Volume());    //will draw the text.
     TextOut(hDC,25,120,szBuffer,strlen(szBuffer));                 //A FONT is a GDI object
     SelectObject(hDC,hTmp);                                        //and needs to be
     DeleteObject(hFont);                                           //released to prevent
     EndPaint(Wea.hWnd,&ps);                                        //memory leaks.  Finally
                                                                    //EndPaint() needs to be
     return 0;                                                      //called to terminate use
    }                                                               //of the device context.
    
    
    LRESULT fnWndProc_OnDestroy(WndEventArgs& Wea) //When you click the 'x' button in the
    {                                              //window's title bar Windows will send
     CBox* pBox=NULL;                              //WM_CLOSE and WM_DESTROY messages (among
                                                   //others) to the window's Window Procedure.
     pBox=(CBox*)GetWindowLongPtr(Wea.hWnd,0);     //This program needs to delete the CBox
     if(pBox)                                      //object a pointer to which is stored in
        delete pBox;                               //the program's .cbWndExtra bytes.  Then
     PostQuitMessage(0);                           //PostQuitMessage() needs to be called so
                                                   //the message pump in WinMain() will exit
     return 0;                                     //and the program will end.
    }
    
    
    LRESULT CALLBACK 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<dim(EventHandler); i++) //of the WndProc.  If a match is made the event handling
     {                                               //procedure is called through a function pointer -
         if(EventHandler[i].iMsg==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 (*EventHandler[i].fnPtr)(Wea);
         }
     }
    
     return (DefWindowProc(hwnd, msg, wParam, lParam));
    }
    
    
    int WINAPI WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
    {
     char szClassName[]="CBox_Window_Class";
     WNDCLASSEX wc;
     HWND hWnd=NULL;
     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)COLOR_BTNSHADOW;    wc.cbWndExtra=sizeof(void*);
     wc.lpszMenuName=NULL;                        wc.cbClsExtra=0;
     RegisterClassEx(&wc);
     hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,100,100,400,300,HWND_DESKTOP,0,hIns,0);
     ShowWindow(hWnd,iShow);
     while(GetMessage(&messages,NULL,0,0))
     {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
     }
    
     return messages.wParam;
    }
    Code:
    //Main.h
    #ifndef Main_h
    #define Main_h
    
    #define dim(x) (sizeof(x) / sizeof(x[0]))
    
    struct                       WndEventArgs
    {
     HWND                        hWnd;
     WPARAM                      wParam;
     LPARAM                      lParam;
     HINSTANCE                   hIns;
    };
    
    LRESULT fnWndProc_OnCreate   (WndEventArgs& Wea);
    LRESULT fnWndProc_OnPaint    (WndEventArgs& Wea);
    LRESULT fnWndProc_OnDestroy  (WndEventArgs& Wea);
    
    struct EVENTHANDLER
    {
     unsigned int                iMsg;
     LRESULT                     (*fnPtr)(WndEventArgs&);
    };
    
    const EVENTHANDLER           EventHandler[]=
    {
     {WM_CREATE,                 fnWndProc_OnCreate},
     {WM_PAINT,                  fnWndProc_OnPaint},
     {WM_DESTROY,                fnWndProc_OnDestroy}
    };
    
    #endif
    But to continue the story, when fnWndProc_OnCreate() exits the main program window is still not visible, although a lot of construction activity has taken place. When the Window Procedure finally retrieves a WM_PAINT message program execution will proceed to fnWndProc_OnPaint(), where another local CBox* object is created. But a CBox* isn't a CBox. The real instantiated CBox object created in WM_CREATE handler code is in the heap, but a pointer to it (its address) is stored at offset zero in the WNDCLASSEX::cbWndExtra bytes. We retrieve it like so...

    Code:
    pBox=(CBox*)GetWindowLongPtr(Wea.hWnd,0);
    ...and have a valid CBox* again in local pBox (local to WM_PAINT code). We can then TextOut() any of the information about our CBox such as the length, width, height and volume. When WM_PAINT handler code goes out of scope so does pBox again, but the actual CBox object in the heap remains valid.

    When you [x] out in fnWndProc_OnDestroy the pointer to the CBox is reset and we can call delete on it to reclaim the memory and prevent memory leaks. Of course it would have been reclaimed anyway when the program terminated, but we always want to do right. So I believe that answers your question about object lifetime and scope, OOP issues, and eliminating globals/statics.

    By the way, the above program can be compiled and linked against LIBCMT.LIB (the C Runtime) with this command line...

    Code:
    cl Main.cpp CBox.cpp /O1 /Os /MT kernel32.lib user32.lib gdi32.lib
    ...and in VC15 from Visual Studio 2008 I'm seeing 74,240 bytes for x64. For MinGW with the 4.8 compiler I'm seeing a whooping 188,928 bytes. That's a crying shame. The MinGW 32 bit compilers used to produce small executables because they link against msvcrt.dll (VC6 Runtime) which is a Windows system component, but for quite some time now they are linking against that winpthreads lib and their executable sizes have exploded. But the situation with Microsoft compilers is steadily worsening too as VC19 (Visual Studio 2015 - the latest) builds of the above have crept up to about 90,000 bytes. So the situation keeps getting worse. That's one of the reasons I've sunk most of this year into developing my replacements for the C and C++ Standard Libraries. The above program built with my 'Tiny C Lib' (TCLib.lib) comes in at a mere 6,144 bytes in x64. Here's the command line string for that...

    Code:
    cl Main.cpp CBox.cpp /O1 /Os /GS- /link TCLib.lib kernel32.lib user32.lib gdi32.lib
    Abyssion said...

    It seems that we share some similar values with respect to code organisation and minimal resource usage. I also am a huge fan of the way the Windows API was constructed and, in fact, it made me wonder whether to switch to C instead of C++. (Having said that though, I'd be lost without the std::string and std:vector vector classes, so maybe not.)
    I agree with you there too but the situation is somewhat different with me. I kind of got started with C++ before the present String Class in the C++ Standard Library was adopted. In those early years everybody was building their own String Classes and that's what happened with me. I just kept the one I created for myself and kept working with it, rewriting it, learning from it, and improving it over the years. And the one I had added a lot less to program size than the one incorporated into the C++ Standard Library, and it had more functionality, so I stuck with it.

    In terms of container objects such as vectors, I just simply never used them. I always preferred the models from BASIC family languages of dynamic multidimensional arrays. For example, the PowerBASIC language allows up to 8 dimensions all of which are dynamic, i.e., the arrays can be dimensioned (memory allocated) at runtime and also resized. The way I incorporated this into my C++ work was through a templated multi-dimension Array Class where I've overloaded the '(' and ')' operators so that I can use BASIC like syntax instead of C/C++ type syntax, e.g., var[][]. So like for a four dimensional array it might look like this...

    Code:
    var(1,2,3,4)
    ...instead of this C ism...

    Code:
    var[1][2][3][4];
    So I've somewhat mis-stated the situation with the C++ Standard Library and my alternate library development work. I didn't develop my own version of it like I did with the C Standard Library; I simply wrote it off as a total loss and used my own String Class which I've always had and developed my templated multidimensional array class for containers. Things like linked lists I already had from my C coding days, and one thing about using one's own library code that one has written oneself is that one doesn't have to study tutorials on how to use it! Let me provide a last simple example of what I'm talking about in terms of how easy it is to free oneself of the bloat introduced by the C++ Standard Library. Let's take the case of parsing a simple comma delimited string such as this...

    Code:
    std::string strLine = "Zero, One, Two, Three, Four, Five, Six";
    I believe the cannonical way of doing it in C++ would be something like this with the output following...

    Code:
    // cl StdLibParse1.cpp /O1 /Os /MT /EHsc
    // 200,192 Bytes
    #include <iostream>
    #include <sstream>
    
    int main()
    {
     std::string input = "Zero, One, Two, Three, Four, Five, Six";
     std::istringstream ss(input);
     std::string token;
     while(std::getline(ss, token, ','))
     {
        std::cout << token << '\n';
     }
     
     return 0;
    }
    
    
    #if 0
    
    Output:
    =======
    Zero
     One
     Two
     Three
     Four
     Five
     Six
    
    #endif
    So we've got 200,000 bytes to parse a string. Using my own library code I have this (My String Class not shown - but represented by #include Strings.h)...

    Code:
    // cl Demo5.cpp Strings.cpp /O1 /Os /GS- /Zc:sizedDealloc- /link TCLib.lib kernel32.lib
    // 4,608 Bytes With INTEGRAL_CONVERSIONS, FLOATING_POINT_CONVERSIONS, And FORMATTING Remmed Out
    // 5,120 Bytes With Full String Class
    #define UNICODE
    #define _UNICODE
    #include <windows.h>
    #include "stdlib.h"
    #include "stdio.h"
    #include "tchar.h"
    #include "Strings.h"
    extern "C" int _fltused=1;
    
    int main()
    {
     int iParseCount=0;
     String* pStrs=NULL;
     String s1;
     
     s1=_T("Zero, One, Two, Three, Four, Five");
     s1.Print(_T("s1 = "),true);
     iParseCount=s1.ParseCount(_T(','));
     _tprintf(_T("iParseCount = %d\n\n"),iParseCount);
     pStrs=new String[iParseCount];
     s1.Parse(pStrs, _T(','), iParseCount);
     for(int i=0; i<iParseCount; i++)
     {
         pStrs[i].LTrim();
         pStrs[i].Print(true);
     }
     delete [] pStrs;
     getchar();
     
     return 0;
    }
    
    // Output:
    // ==========================
    // s1 = Zero, One, Two, Three, Four, Five
    // iParseCount = 6
    
    // Zero
    // One
    // Two
    // Three
    // Four
    // Five
    Its close to the same program but I see I have added a few things in the above version not in the one using std::string, but in any case its 40 times smaller with a saving of about 195,000 bytes. Both programs are x64 compiled with VC19 from Visual Studio 2015. Actually, the magic of saving all those bytes isn't hard to locate. Here is the entirety of my String::Parse() method excerpted from my String Class...

    Code:
    void String::Parse(String* pStr, TCHAR delimiter, size_t iParseCount)
    {
     TCHAR* pBuffer=new TCHAR[this->iLen+1];
     if(pBuffer)
     {
        TCHAR* p=pBuffer;
        TCHAR* c=this->lpBuffer;
        while(*c)
        {
           if(*c==delimiter)
              *p=0;
           else
              *p=*c;
           p++, c++;
        }
        *p=0, p=pBuffer;
        for(size_t i=0; i<iParseCount; i++)
        {
            pStr[i]=p;
            p=p+pStr[i].iLen+1;
        }
        delete [] pBuffer;
     }
    }
    Not much magic there. The top while loop copies to another buffer all the characters of the one pointed to by this substituting NULLs when it encounters a delimiter. The end result of that is a buffer containing an array of null terminated strings. The second loop simply collects them, delete is called on the 'work' buffer, and the method returns. I think any decent C coder could come up with that. So I've got a 4 or 5 k 64 bit stand alone executable. Is there any RAII or structured exception handling? Nope. None. There are no free lunches in this world. If you've got to have that sort of thing then you are going to have to pay for it in code bloat. Or use C#. Or use VB.NET. Whatever. So its a difficult personal choice that has to be made.

    Getting back to CodePlugs point about tricky. I finally got what you were saying. Yep! I agree with you. A good many years ago I spent like a month or two working on writing my own C++ Class Framework. I've got a ClassFrameWorks directory under my C:\Code\CodeBlks directory with about 45 subfolders under it to prove it! And I remember getting stuck bad just on what you were talking about for several hours until I finally got something working that I was satisfied with. I forget the details but somehow or other I managed to set up the connection between the this pointer and HWNDs without using hooks or anything like that. So when I made that comment about 'simple as dirt' I meant obtaining a HWND from Windows using GetDlgItem() - not constructing a Class Framework and getting all the internal wiring worked out. Real sorry if I came off as insulting.

  14. #14
    Registered User
    Join Date
    Dec 2010
    Location
    Trinidad, CO (log cabin in middle of nowhere)
    Posts
    148
    When you say the object can probably be declared in WinMain, it certainly does make more sense, semantically, to do it that way. To access the object in the windows procedure, would it be sufficient and good practice to allocate extra memory in the window class via SetWindowLongPtr and storing a pointer to the object in this memory for retrieval via GetWindowLong?
    It would be possible to do that, but its against my personal style to do so. I prefer to do the barest minimal amount of coding in WinMain() and restrict myself severely to simply filling out the members of a WNDCLASSEX struct, registering that main window class, calling CreateWindowEx(), on it, and finally dropping into the message pump. Certainly WinMain is running during the entirety of program duration, and objects of any type of class or classes could be instantiated there. And they could be even local stack based objects, i.e., not created with new, simply because their duration would be the entirety of the program run. Further, the addresses of those local objects could be passed on through to the Window Procedure through the lpCreateParams (the last parameter of the CreateWindow() call) parameter, and retrieved in WM_CREATE code, as for that message the lParam is a pointer to a CREATESTRUCT object, which contains all the parameters of the Createindow() call in WinMain(). This is a 'trick' I do just about all the time. When you make a hard and fast rule for oneself such as 'no globals ever', and you live by it, you become very creative in the ways you move data around within your app.

  15. #15
    Registered User
    Join Date
    Jul 2015
    Posts
    64
    Hey Freddie. Sorry I haven't been about in a while, life has been hectic.

    Just wanted to say many thanks for all the effort you've put into your replies! The complete code samples are hugely appreciated, particularly from someone so experienced in the field. I'm currently about to embark on a complete overhaul of my code and the structured event handling that you suggest will certainly make for a more comprehensible read. Thank you! Also, cheers for your advice on long-life object instantiation; I shall definitely make use of your heap-based approach, initialising class instances in the WM_CREATE handler. Many thanks for the pointer (pun intended)! I also note that there seems to be personal preferences across different developers pertaining to code structure, so I'm trying to bare in mind that this is just one approach. It does, however, seem to be as structured as it gets when it comes to Win32 SDK programming.

    I've actually had a quick read through your board tutorials from start to finish; they're an excellent resource for information on good coding practices. With regards to developing your own tiny C and C++ standard library replacements, are you minimising executable sizes by eliminating functionality that you, personally, do not use? Or does the lack of bloat stem from optimised internal algorithms and the lack of SEH? Either way, the output sizes that you have achieved are incredible! Do you plan on publishing your libraries at any point or are they only for personal use? How's your character encoding support?

    This is on a completely separate note, sorry: is there any harm in trying to free "uninitialised" GDI object handles with DestroyIcon, DeleteObject, ReleaseDC, DeleteDC etc? I'd assume that the functions themselves would verify that the handles passed to them point to valid GDI objects before trying to free resources associated with them, but of course, knowing how easily GDI leaks can occur, I'd rather not make such assumptions and dance with undefined behaviour. Does anyone have any info on the inner workings of these functions? Their documentation on MSDN does not address what happens when invalid (NULL) handles are passed.

    I also have never heard of these lpCreateParams and CREATESTRUCT constructs; it sounds as though they're designed to pass additional information to the window procedure from WinMain()? This definitely sounds like something I should become familiar with. Just to clarify, is lpCreateParams a function that populates a CREATESTRUCT structure? There doesn't seem to be an MSDN page dedicated to lpCreateParams.

    Many thanks again!
    As I say, I am learning a lot

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Program design and outlay in large projects
    By wiqxlzxsxj in forum C Programming
    Replies: 3
    Last Post: 01-19-2016, 08:07 PM
  2. How to handle very large number in C
    By suryak in forum C Programming
    Replies: 25
    Last Post: 12-17-2013, 06:35 PM
  3. Proper coding style on large projects
    By Verneg in forum C Programming
    Replies: 0
    Last Post: 03-27-2010, 06:13 PM
  4. Design of Large C Projects/Program
    By amrishpurohit in forum Tech Board
    Replies: 5
    Last Post: 09-03-2008, 01:29 AM
  5. Replies: 2
    Last Post: 09-16-2005, 11:44 PM

Tags for this Thread