3
\$\begingroup\$

I compiled this with wxDevC++, and it needs the GDI+ library and header includes for Windows, to compile.

On a larger program I have had memory fluctuating from plus minus 4MBs. Not a lot, but why is it doing that?

This is a short replication, which fluctuates by .1MB.

I'm wondering if there is a mistake, or is that normal? Other programs on my computer experience no fluctuation in its memory usage.

Also, I saved all the common used GDI+ tools so they did not need to be created every time. Is that good or bad?

// Includes
#define WIN32_LEAN_AND_MEAN
#define WINVER 0x0502
#include <windows.h>
#include <stdio.h> 
#include <windowsx.h>
#include <string>
#include <cstdlib>
#include <iostream>
#include <gdiplus.h>

// Global Variables
HWND hWnd;
HINSTANCE hInstance;
ULONG_PTR gdiplusToken;
int count;
Style lk;


// Functions
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
void LoadStyle();
void UnloadStyle();



int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil) {

    // GDI+
    GdiplusStartupInput gdiplusStartupInput;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    // Normal Vars
    HWND hwnd;              
    MSG messages;            
    WNDCLASSEX wincl;      

    // window structure to register
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      
    wincl.style = CS_DBLCLKS;            
    wincl.cbSize = sizeof (WNDCLASSEX);
    // defaults
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                
    wincl.cbClsExtra = 0;                      
    wincl.cbWndExtra = 0;                  
    // background color
    wincl.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH);

    // register window class
    if (!RegisterClassEx (&wincl))
        return 0;

    // Create window
    hwnd = CreateWindowEx(0, szClassName, L"Test Transparent Static Main Window", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_CLIPCHILDREN, 100, 100, WINDOW_WIDTH, WINDOW_HEIGHT, HWND_DESKTOP, NULL, hThisInstance, NULL);        
    ShowWindow (hwnd, nFunsterStil);

    // Set globals
    hWnd = hwnd;
    hInstance = hThisInstance;

    // Main window message loop
    while (GetMessage (&messages, NULL, 0, 0)) {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
    }
    return messages.wParam;
}


// Main Window Procedure
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {

    switch (message) {

        case WM_CREATE: {    
            LoadStyle();
            const int timerSpeed = 75;
            SetTimer(hwnd, ID_TIMER, timerSpeed, NULL);
        } break;

        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            EndPaint(hwnd, &ps);
        } break;

       case WM_DESTROY: {
            UnloadStyle();
            GdiplusShutdown(gdiplusToken);
            PostQuitMessage(0);  
        } break;

        case WM_TIMER: {

            // Start -----------------------------------------------------------------------------------------------------------------------------------
            HDC hdc = GetDC(hWnd);
            Graphics *g = new Graphics(hdc);
            g->FillRectangle(lk.blackBrush, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
            g->SetTextRenderingHint(TextRenderingHintAntiAlias);

            // Draw Strings
            g->DrawString(L"String1", -1, lk.font, PointF(0, 0), lk.textColor1);  
            g->DrawString(L"String2", -1, lk.font, PointF(0, 120), lk.textColor2);                

            // Done
            ReleaseDC(hWnd, hdc);
            delete hdc;
            g->Flush();
            delete g;
            // -------------------------------------------------------------------------------------------------------------------------------------

        } break;

        default: {
            return DefWindowProc (hwnd, message, wParam, lParam);
        }
    }
    return 0;
}



void LoadStyle() {
    lk.blackBrush = new SolidBrush(Color(25,10,10,10));
    lk.penBorder = new Pen(Color(100,255,255,255), 3);            
    lk.fontFamily = new FontFamily(L"Times New Roman");
    lk.font = new Font(lk.fontFamily, 32, FontStyleBold, UnitPixel);
    lk.textColor1 = new SolidBrush(Color(100,100,100,100));
    lk.textColor2 = new SolidBrush(Color(200,200,200,200));
}                

void UnloadStyle() {
    delete lk.blackBrush;
    delete lk.penBorder;
    delete lk.fontFamily;
    delete lk.font;
    delete lk.textColor1;
    delete lk.textColor2;    
}
\$\endgroup\$
  • 2
    \$\begingroup\$ Consider using a smart pointer for all those dynamic objects. \$\endgroup\$ – glampert Oct 3 '14 at 4:25
  • 3
    \$\begingroup\$ Is delete hdc after ReleaseDC really valid? It has been a while I was writing code like this (moved to .NET some time ago). I would not allocate (new/delete) what can be made part of stack or object. And the approach with drawing in timer, hmm, I would call Invalidate and handle it in WM_PAINT. Just few hints, because I am no longer expert in WinAPI. \$\endgroup\$ – user52292 Oct 5 '14 at 14:29
  • 1
    \$\begingroup\$ I later found out that delete hdc is invalid and will cause the program to immediately crash. That was my error in the copy and paste. \$\endgroup\$ – Evan Carslake Oct 5 '14 at 17:13
3
\$\begingroup\$

I don't know about the GDI Library but there a still some things in this code which could need a examination:

// Create window
hwnd = CreateWindowEx(0, szClassName, L"Test Transparent Static Main Window", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_CLIPCHILDREN, 100, 100, WINDOW_WIDTH, WINDOW_HEIGHT, HWND_DESKTOP, NULL, hThisInstance, NULL);
ShowWindow(hwnd, nFunsterStil);

That is a very, very long line. It is recommended to limit the line length, so you have the option two watch two source codes next to each other on the screen.

Isn't this a lot better to read to see what goes into making this Windows?

// Create window
hwnd = CreateWindowEx(
    0, szClassName, L"Test Transparent Static Main Window", 
    WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | 
    WS_CLIPCHILDREN, 100, 100, WINDOW_WIDTH, WINDOW_HEIGHT, HWND_DESKTOP, 
    NULL, hThisInstance, NULL);

I personally stick with 80 lines per row. It forces you to write more clean code and keep the complexity in youre code down. In the end you write mire readable code For a discussion. why see here: https://softwareengineering.stackexchange.com/questions/604/is-the-80-character-limit-still-relevant-in-times-of-widescreen-monitors

Then annother issue:

// Global Variables
HWND hWnd;
HINSTANCE hInstance;
ULONG_PTR gdiplusToken;
int count;
Style lk;

You should nearly never use global variables. They are simply a maintenance Hazard. If you feel that without them beeing global you have to pass them to too many functions then probaly it is a good reason to refactor youre code into using a class. See https://stackoverflow.com/questions/484635/are-global-variables-bad

Annother thing is the complexity of the code. Take youre WinMain function:

int WINAPI WinMain(
    HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, 
    int nFunsterStil) {

    // GDI+
    GdiplusStartupInput gdiplusStartupInput;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    // Normal Vars
    HWND hwnd;
    MSG messages;
    WNDCLASSEX wincl;

    // window structure to register
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;
    wincl.style = CS_DBLCLKS;
    wincl.cbSize = sizeof(WNDCLASSEX);
    // defaults
    wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;
    wincl.cbClsExtra = 0;
    wincl.cbWndExtra = 0;
    // background color
    wincl.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH);

    // register window class
    if (!RegisterClassEx(&wincl))
        return 0;

    // Create window
    hwnd = CreateWindowEx(
        0, szClassName, L"Test Transparent Static Main Window", 
        WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | 
        WS_CLIPCHILDREN, 100, 100, WINDOW_WIDTH, WINDOW_HEIGHT, HWND_DESKTOP, 
        NULL, hThisInstance, NULL);

    ShowWindow(hwnd, nFunsterStil);

    // Set globals
    hWnd = hwnd;
    hInstance = hThisInstance;

    // Main window message loop
    while (GetMessage(&messages, NULL, 0, 0)) {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
    }
    return messages.wParam;
}

There is a lot of stuff going on and it makes youre eyes bleed to focus on certain parts. You also realized that by commenting certain blocks what they are doing. A better approach is making the comments into functions and hiding the more low level parts.

Here the refactored method (still not perfect but to give you a hint):

WNDCLASSEX initWindowStructure(HINSTANCE hThisInstance)
{
    WNDCLASSEX wincl;

    // window structure to register
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;
    wincl.style = CS_DBLCLKS;
    wincl.cbSize = sizeof(WNDCLASSEX);
    // defaults
    wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;
    wincl.cbClsExtra = 0;
    wincl.cbWndExtra = 0;
    // background color
    wincl.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH);

    return wincl;
}

HWND createWindow(HINSTANCE hThisInstance)
{
    return CreateWindowEx(
        0, szClassName, L"Test Transparent Static Main Window",
        WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
        WS_CLIPCHILDREN, 100, 100, WINDOW_WIDTH, WINDOW_HEIGHT, HWND_DESKTOP,
        NULL, hThisInstance, NULL);
}


int WINAPI WinMain(
    HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, 
    int nFunsterStil) {

    // GDI+
    GdiplusStartupInput gdiplusStartupInput;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    // Normal Vars
    MSG messages;
    WNDCLASSEX wincl = initWindowStructure(hThisInstance);

    // register window class
    if (!RegisterClassEx(&wincl))
        return 0;

    HWND hwnd  = createWindow(hThisInstance);

    ShowWindow(hwnd, nFunsterStil);

    // Set globals
    hWnd = hwnd;
    hInstance = hThisInstance;

    // Main window message loop
    while (GetMessage(&messages, NULL, 0, 0)) {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
    }
    return messages.wParam;
}

Suddenly The Method is easier to follow don't you thing. Probaly you are not sad that the ugly details are hidden away. And unlike comments the methods we created can stay. Comments have the danger of disappear or get out date...

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.