Thread: My Cardiograph

  1. #1
    System Novice siavoshkc's Avatar
    Join Date
    Jan 2006
    Location
    Tehran
    Posts
    1,246

    My Cardiograph

    I have a PC that freezes often with no (known) reason. So I decided to write a program which shows something so I can see its working from a distance. I am new to Win32 and GDI+. So I need your comments. My known problem is that rectangle flickers. The good thing is that GDI+ is implemented in C++.
    Code:
    // Cardiograph.cpp : Defines the entry point for the application.
    //
    
    #include "stdafx.h"
    #include "Cardiograph.h"
    
    #define MAX_LOADSTRING 100
    
    // Global Variables:
    HINSTANCE hInst;        // current instance
    HWND hWnd;               // I added this to keep track of window
    TCHAR szTitle[MAX_LOADSTRING];      // The title bar text
    TCHAR szWindowClass[MAX_LOADSTRING];        // the main window class name
    
    // Forward declarations of functions included in this code module:
    ATOM   MyRegisterClass(HINSTANCE hInstance);
    BOOL      InitInstance(HINSTANCE, int);
    LRESULT CALLBACK  WndProc(HWND, UINT, WPARAM, LPARAM);
    
    
    int APIENTRY _tWinMain(HINSTANCE hInstance,
                                     HINSTANCE hPrevInstance,
                                     LPTSTR    lpCmdLine,
                                     int       nCmdShow)
    {
          UNREFERENCED_PARAMETER(hPrevInstance);
          UNREFERENCED_PARAMETER(lpCmdLine);
    
          MSG msg;
          Gdiplus::GdiplusStartupInput gdiplusStartupInput;
          ULONG_PTR gdiplusToken;
    
          // Initialize GDI+.
          GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    
          // Initialize global strings
          LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
          LoadString(hInstance, IDC_KARDIOGRAPH, szWindowClass, MAX_LOADSTRING);
          MyRegisterClass(hInstance);
    
          // Perform application initialization:
          if (!InitInstance (hInstance, nCmdShow)) return FALSE;
    
          // Main message loop that is reorginized:
         do
          {
                PeekMessage(&msg, NULL,  0, 0, PM_REMOVE);
                DispatchMessage(&msg);
                Sleep(250);
                //I made hWnd global so I can use it here
                //I am not so sure about RDW flags
                RedrawWindow(hWnd, NULL, NULL, RDW_UPDATENOW|RDW_ERASE|RDW_INVALIDATE);
          }while (msg.message != WM_QUIT);
    
          Gdiplus::GdiplusShutdown(gdiplusToken);
    
          return (int) msg.wParam;
    }
    
    //
    //  FUNCTION: MyRegisterClass()
    //
    //  PURPOSE: Registers the window class.
    //
    //  COMMENTS:
    //
    //    This function and its usage are only necessary if you want this code
    //    to be compatible with Win32 systems prior to the 'RegisterClassEx'
    //    function that was added to Windows 95. It is important to call this function
    //    so that the application will get 'well formed' small icons associated
    //    with it.
    //
    ATOM MyRegisterClass(HINSTANCE hInstance)
    {
          WNDCLASSEX wcex;
    
          wcex.cbSize = sizeof(WNDCLASSEX);
    
          wcex.style                  = CS_HREDRAW | CS_VREDRAW;
          wcex.lpfnWndProc      = WndProc;
          wcex.cbClsExtra         = 0;
          wcex.cbWndExtra       = 0;
          wcex.hInstance          = hInstance;
          wcex.hIcon                 = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_KARDIOGRAPH));
          wcex.hCursor             = LoadCursor(NULL, IDC_ARROW);
          wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
          wcex.lpszMenuName  = NULL;
          wcex.lpszClassName  = szWindowClass;
          wcex.hIconSm            = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    
          return RegisterClassEx(&wcex);
    }
    
    //
    //   FUNCTION: InitInstance(HINSTANCE, int)
    //
    //   PURPOSE: Saves instance handle and creates main window
    //
    //   COMMENTS:
    //
    //        In this function, we save the instance handle in a global variable and
    //        create and display the main program window.
    //
    BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
    {
          // HWND hWnd; I made it global but not sure if its a good practice
    
          hInst = hInstance; // Store instance handle in our global variable
          POINT pt = {0,0};
          HMONITOR hMonitor = MonitorFromPoint(pt,MONITOR_DEFAULTTOPRIMARY);
          MONITORINFO mi;
          mi.cbSize = sizeof(MONITORINFO);
          GetMonitorInfo(hMonitor, &mi);
    
    
          hWnd = CreateWindow(szWindowClass, szTitle, WS_BORDER|WS_SYSMENU ,
                mi.rcMonitor.right - 270, 0, 270, 80, NULL, NULL, hInstance, NULL);
    
          if (!hWnd) return FALSE;
    
          ShowWindow(hWnd, nCmdShow);
          return TRUE;
    }
    
    VOID OnPaint(HDC hdc,int a)
    {
          Gdiplus::Graphics graphics(hdc);
          Gdiplus::SolidBrush sBrush(Gdiplus::Color(a,255,0,0));
          graphics.FillRectangle(&sBrush, Gdiplus::Rect(0,0,a+15,80));
    }
    
    
    //
    //  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
    //
    //  PURPOSE:  Processes messages for the main window.
    //
    //  WM_COMMAND	- process the application menu - No menu so code deleted!
    //  WM_PAINT	- Paint the main window
    //  WM_DESTROY	- post a quit message and return
    //
    //
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
          PAINTSTRUCT ps;
          HDC hdc;
    
          switch (message)
          {
          case WM_PAINT:
                hdc = BeginPaint(hWnd, &ps);
                OnPaint(hdc,(GetTickCount()/10)%256); //DrawRect
                EndPaint(hWnd, &ps);
                break;
          case WM_DESTROY:
                PostQuitMessage(0);
                break;
          default:
                return DefWindowProc(hWnd, message, wParam, lParam);
          }
          return 0;
    }
    Last edited by siavoshkc; 03-04-2009 at 09:43 AM.
    Learn C++ (C++ Books, C Books, FAQ, Forum Search)
    Code painter latest version on sourceforge DOWNLOAD NOW!
    Download FSB Data Integrity Tester.
    Siavosh K C

  2. #2
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    IMO your msg loop/pump is the issue.

    Sleep'ing for 1/4 of a sec between each msg is not a good idea (very bad idea). Adding another msg to the queue each time makes this worse.

    I would use a WM_TIMER msg to send an InvalidateRect() and UpdateWindow() periodically and use a standard msg loop/pump.

    but.....

    Handling the WM_ERASEBKGND (just return true) may help (or removing the RDW_ERASE).
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  3. #3
    System Novice siavoshkc's Avatar
    Join Date
    Jan 2006
    Location
    Tehran
    Posts
    1,246
    Code:
    // Cardiograph.cpp : Defines the entry point for the application.
    //
    
    #include "stdafx.h"
    #include "Cardiograph.h"
    
    #define MAX_LOADSTRING 100
    
    // Global Variables:
    HINSTANCE hInst;                                                // current instance
    HWND hWnd;                                                            // I added this to keep track of window
    TCHAR szTitle[MAX_LOADSTRING];                              // The title bar text
    TCHAR szWindowClass[MAX_LOADSTRING];                  // the main window class name
    
    // Forward declarations of functions included in this code module:
    ATOM                        MyRegisterClass(HINSTANCE hInstance);
    BOOL                        InitInstance(HINSTANCE, int);
    LRESULT CALLBACK      WndProc(HWND, UINT, WPARAM, LPARAM);
    
    
    int APIENTRY _tWinMain(HINSTANCE hInstance,
                                     HINSTANCE hPrevInstance,
                                     LPTSTR    lpCmdLine,
                                     int       nCmdShow)
    {
          UNREFERENCED_PARAMETER(hPrevInstance);
          UNREFERENCED_PARAMETER(lpCmdLine);
    
          MSG msg;
          Gdiplus::GdiplusStartupInput gdiplusStartupInput;
          ULONG_PTR gdiplusToken;
    
          // Initialize GDI+.
          GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    
          // Initialize global strings 
          LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
          LoadString(hInstance, IDC_KARDIOGRAPH, szWindowClass, MAX_LOADSTRING);
          MyRegisterClass(hInstance);
    
          // Perform application initialization:
          if (!InitInstance (hInstance, nCmdShow)) return FALSE;
    
          //Creating the timer
          SetTimer(hWnd,0, 800, NULL);
          // Main message loop that is reorginized:
          while(GetMessage(&msg, NULL, 0, 0))
          {
                DispatchMessage(&msg); 
          }
          
          //Destructions
          Gdiplus::GdiplusShutdown(gdiplusToken);
          KillTimer(hWnd, 0);
          return (int) msg.wParam;
    }
    
    //
    //  FUNCTION: MyRegisterClass()
    //
    //  PURPOSE: Registers the window class.
    //
    //  COMMENTS:
    //
    //    This function and its usage are only necessary if you want this code
    //    to be compatible with Win32 systems prior to the 'RegisterClassEx'
    //    function that was added to Windows 95. It is important to call this function
    //    so that the application will get 'well formed' small icons associated
    //    with it.
    //
    ATOM MyRegisterClass(HINSTANCE hInstance)
    {
          WNDCLASSEX wcex;
    
          wcex.cbSize = sizeof(WNDCLASSEX);
    
          wcex.style                  = CS_HREDRAW | CS_VREDRAW;
          wcex.lpfnWndProc      = WndProc;
          wcex.cbClsExtra            = 0;
          wcex.cbWndExtra            = 0;
          wcex.hInstance            = hInstance;
          wcex.hIcon                  = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_KARDIOGRAPH));
          wcex.hCursor            = LoadCursor(NULL, IDC_ARROW);
          wcex.hbrBackground      = (HBRUSH)(COLOR_WINDOW+1);
          wcex.lpszMenuName      = NULL;
          wcex.lpszClassName      = szWindowClass;
          wcex.hIconSm            = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    
          return RegisterClassEx(&wcex);
    }
    
    //
    //   FUNCTION: InitInstance(HINSTANCE, int)
    //
    //   PURPOSE: Saves instance handle and creates main window
    //
    //   COMMENTS:
    //
    //        In this function, we save the instance handle in a global variable and
    //        create and display the main program window.
    //
    BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
    {
          // HWND hWnd; I made it global but not sure if its a good practice
    
          hInst = hInstance; // Store instance handle in our global variable
          POINT pt = {0,0};
          HMONITOR hMonitor = MonitorFromPoint(pt,MONITOR_DEFAULTTOPRIMARY);
          MONITORINFO mi;
          mi.cbSize = sizeof(MONITORINFO);
          GetMonitorInfo(hMonitor, &mi);
    
    
          hWnd = CreateWindowEx(WS_EX_TOOLWINDOW,szWindowClass, szTitle, WS_BORDER|WS_SYSMENU ,
                mi.rcMonitor.right - 270, 0, 270, 80, NULL, NULL, hInstance, NULL);
    
          if (!hWnd) return FALSE;
    
          ShowWindow(hWnd, nCmdShow);
          return TRUE;
    }
    
    VOID OnPaint(HDC hdc,int a)
    {
          Gdiplus::Graphics graphics(hdc);
          Gdiplus::SolidBrush sBrush(Gdiplus::Color(a,255,0,0));
          graphics.FillRectangle(&sBrush, Gdiplus::Rect(0,0,a+15,80));
    }
    
    
    //
    //  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
    //
    //  PURPOSE:  Processes messages for the main window.
    //
    //  WM_COMMAND	- process the application menu - No menu so code deleted!
    //  WM_PAINT	- Paint the main window
    //  WM_DESTROY	- post a quit message and return
    //
    //
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
          PAINTSTRUCT ps;
          HDC hdc;
    
          switch (message)
          {
          case WM_TIMER:
          
                RedrawWindow(hWnd, NULL, NULL, RDW_UPDATENOW|RDW_ERASE|RDW_INVALIDATE);
                
    
          case WM_PAINT:
                hdc = BeginPaint(hWnd, &ps);
                OnPaint(hdc,(GetTickCount()/10)%256); //DrawRect
                EndPaint(hWnd, &ps);
                break;
          case WM_DESTROY:
                PostQuitMessage(0);
                break;
          default:
                return DefWindowProc(hWnd, message, wParam, lParam);
          }
          return 0;
    }
    Learn C++ (C++ Books, C Books, FAQ, Forum Search)
    Code painter latest version on sourceforge DOWNLOAD NOW!
    Download FSB Data Integrity Tester.
    Siavosh K C

  4. #4
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    Why are you using GDI+ for this simple drawing?

    You have missed the break in the timer handler.

    Use the WM_CREATE or WM_INITDIALOG to call SetTimer()


    I would use
    Code:
    WM_TIMER:
    InvalidateRect(NULL,false);//draw whole client area but do not erase dlg backgnd
    UpdateWindow(hWnd);//post directly to this callback, not to the OS msg queue
    break;
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  5. #5
    System Novice siavoshkc's Avatar
    Join Date
    Jan 2006
    Location
    Tehran
    Posts
    1,246
    I made hWnd private as it was. I also used GDI instead of GDI+ to reduce overhead. Note the WndProc WM_TIMER with no break. There is no need to call WindowUpdate (It may be dangures I think). I have two goals in this program low memory consumption and low CPU usage. Now it shows 2000 KB in Task Manager. I want to reduce it as much as possible.
    Code:
    // Cardiograph.cpp : Defines the entry point for the application.
    //
    
    #include "stdafx.h"
    #include "Cardiograph.h"
    
    #define MAX_LOADSTRING 100
    
    // Global Variables:
    HINSTANCE hInst;                                          // current instance
    TCHAR szTitle[MAX_LOADSTRING];                        // The title bar text
    TCHAR szWindowClass[MAX_LOADSTRING];                  // the main window class name
    
    // Forward declarations of functions included in this code module:
    ATOM                        MyRegisterClass(HINSTANCE hInstance);
    BOOL                        InitInstance(HINSTANCE, int);
    LRESULT CALLBACK      WndProc(HWND, UINT, WPARAM, LPARAM);
    
    
    int APIENTRY _tWinMain(HINSTANCE hInstance,
                                     HINSTANCE hPrevInstance,
                                     LPTSTR    lpCmdLine,
                                     int       nCmdShow)
    {
          UNREFERENCED_PARAMETER(hPrevInstance);
          UNREFERENCED_PARAMETER(lpCmdLine);
    
          MSG msg;
    
          // Initialize global strings 
          LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
          LoadString(hInstance, IDC_KARDIOGRAPH, szWindowClass, MAX_LOADSTRING);
          MyRegisterClass(hInstance);
    
          // Perform application initialization:
          if (!InitInstance (hInstance, nCmdShow)) return FALSE;
    
          // Main message loop:
          while(GetMessage(&msg, NULL, 0, 0))
          {
                DispatchMessage(&msg); 
          }
    
          return (int) msg.wParam;
    }
    
    //
    //  FUNCTION: MyRegisterClass()
    //
    //  PURPOSE: Registers the window class.
    //
    //  COMMENTS:
    //
    //    This function and its usage are only necessary if you want this code
    //    to be compatible with Win32 systems prior to the 'RegisterClassEx'
    //    function that was added to Windows 95. It is important to call this function
    //    so that the application will get 'well formed' small icons associated
    //    with it.
    //
    ATOM MyRegisterClass(HINSTANCE hInstance)
    {
          WNDCLASSEX wcex;
    
          wcex.cbSize = sizeof(WNDCLASSEX);
    
          wcex.style                  = CS_HREDRAW | CS_VREDRAW;
          wcex.lpfnWndProc      = WndProc;
          wcex.cbClsExtra            = 0;
          wcex.cbWndExtra            = 0;
          wcex.hInstance            = hInstance;
          wcex.hIcon                  = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_KARDIOGRAPH));
          wcex.hCursor            = LoadCursor(NULL, IDC_ARROW);
          wcex.hbrBackground      = (HBRUSH)(COLOR_WINDOW+1);
          wcex.lpszMenuName      = NULL;
          wcex.lpszClassName      = szWindowClass;
          wcex.hIconSm            = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    
          return RegisterClassEx(&wcex);
    }
    
    //
    //   FUNCTION: InitInstance(HINSTANCE, int)
    //
    //   PURPOSE: Saves instance handle and creates main window
    //
    //   COMMENTS:
    //
    //        In this function, we save the instance handle in a global variable and
    //        create and display the main program window.
    //
    BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
    {
           HWND hWnd;
    
          hInst = hInstance; // Store instance handle in our global variable
          POINT pt = {0,0};
          HMONITOR hMonitor = MonitorFromPoint(pt,MONITOR_DEFAULTTOPRIMARY);
          MONITORINFO mi;
          mi.cbSize = sizeof(MONITORINFO);
          GetMonitorInfo(hMonitor, &mi);
    
    
          hWnd = CreateWindowEx(WS_EX_TOOLWINDOW,szWindowClass, szTitle, WS_BORDER|WS_SYSMENU ,
                mi.rcMonitor.right - 270, 0, 270, 80, NULL, NULL, hInstance, NULL);
    
          if (!hWnd) return FALSE;
    
          ShowWindow(hWnd, nCmdShow);
          return TRUE;
    }
    
    //
    //  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
    //
    //  PURPOSE:  Processes messages for the main window.
    //
    //  WM_COMMAND	- process the application menu - No menu so code deleted!
    //  WM_PAINT	- Paint the main window
    //  WM_DESTROY	- post a quit message and return
    //
    //
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
          PAINTSTRUCT ps;
          HDC hdc;
    
          switch (message)
          {
    
          case WM_CREATE:
                //Creating the timer
                SetTimer(hWnd,0, 800, NULL);
                break;
                
          case WM_TIMER:
                InvalidateRect(hWnd, NULL, 1);
    
          case WM_PAINT:
                hdc = BeginPaint(hWnd, &ps);
                SetDCBrushColor(hdc,RGB(255,0,0));
                SelectObject(ps.hdc, GetStockObject(DC_BRUSH)); 
                Rectangle(hdc,0,0,((GetTickCount()/10)%256)+15,80);
                EndPaint(hWnd, &ps);
                break;
          case WM_DESTROY:
                KillTimer(hWnd, 0);
                PostQuitMessage(0);
                break;
          default:
                return DefWindowProc(hWnd, message, wParam, lParam);
          }
          return 0;
    }
    Learn C++ (C++ Books, C Books, FAQ, Forum Search)
    Code painter latest version on sourceforge DOWNLOAD NOW!
    Download FSB Data Integrity Tester.
    Siavosh K C

  6. #6
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,868
    UpdateWindow() is required.
    It bypasses all the msg queues and posts directly to the callback for fast drawing.

    The missing break in the timer will cause problems.
    BeginPaint() only allows painting of the Invalidated area (the HDC you are using will have the invalidate rect area ONLY). Which because you have not used UpdateWindow() or allowed the msg to be processed is (probably) still empty. In a debugger you will probably not see this issue, due to the concatination of WM_PAINT msgs while they are in teh OS msg queue.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  7. #7
    System Novice siavoshkc's Avatar
    Join Date
    Jan 2006
    Location
    Tehran
    Posts
    1,246
    Sorry for delay.
    OK. I made it right. But this small code seems to use 3 MBs of memory. Isn't it a little high? I tried anything I could to make it use less memory:
    - All optimizations are on.
    - I tried changing heap default value to a less value but nothing changed (in good direction).
    - I removed any useless thing from my code. I even changed all TCHARs to CHAR. and made it use non-unicode character set.
    Learn C++ (C++ Books, C Books, FAQ, Forum Search)
    Code painter latest version on sourceforge DOWNLOAD NOW!
    Download FSB Data Integrity Tester.
    Siavosh K C

Popular pages Recent additions subscribe to a feed