For the next question of Strange Efficiency Decrease when Localizing a Global Variable into a Subthread, resently I found the question of the performance decreasement is actually caused by moving the GetDC code into a thread, where it was originally placed in a WindowProc/WndProc function, but I have no idea with the detail.
Could anyone help me?
After all, here is my code:
Quick one:
#include <stdio.h>
#include <math.h>
#include <windows.h>
#define num 4
#define width 1
double position[num][2] = {{733, 434}, {633, 384}, {733, 284}, {733, 684}};
double velocity[num][2] = {{-5, 2.5}, {5, -2.5}, {0, 2.5}, {10, -2.5}};
COLORREF color[num] = {0xffffff, 0x00ffff, 0x0000ff, 0xffff00};
COLORREF background_color = 0x000000;
double distance;
int count, i, j;
HDC hdc[num];
HPEN hpen[num];
inline double sqr(double x) {return x * x;}
DWORD WINAPI threadProc(LPVOID lpParamter) {
Sleep(1000);
while(1) {
for(i=0; i<num; ++i) {
LineTo(hdc[i], position[i][0], position[i][1]);
position[i][0] += velocity[i][0];
position[i][1] += velocity[i][1];
}
}
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPSTR pCmdLine, int nCmdShow) {
const char *CLASS_NAME = "SUGEWND";
void *pHWND = &hInstance;
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(0, CLASS_NAME, "SUGE",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
if(hwnd == NULL) return 0;
ShowWindow(hwnd, SW_SHOWMAXIMIZED);
MSG msg={};
while(GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch(uMsg) {
case WM_DESTROY: {
PostQuitMessage(0);
return 0;
}
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc2=BeginPaint(hwnd, &ps);
FillRect(hdc2, &ps.rcPaint, CreateSolidBrush(background_color));
EndPaint(hwnd, &ps);
return 0;
}
case WM_CREATE: {
for(i=0; i<num; ++i) {
hdc[i] = GetDC(hwnd);
hpen[i] = CreatePen(PS_SOLID, width, color[i]);
SelectObject(hdc[i], hpen[i]);
MoveToEx(hdc[i], position[i][0], position[i][1], 0);
}
HANDLE hThread = CreateThread(NULL, 0, threadProc, NULL, 0, NULL);
CloseHandle(hThread);
return 0;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Slow one:
#include <stdio.h>
#include <math.h>
#include <windows.h>
#define num 4
#define width 1
double position[num][2] = {{733, 434}, {633, 384}, {733, 284}, {733, 684}};
double velocity[num][2] = {{-5, 2.5}, {5, -2.5}, {0, 2.5}, {10, -2.5}};
COLORREF color[num] = {0xffffff, 0x00ffff, 0x0000ff, 0xffff00};
COLORREF background_color = 0x000000;
double simulate_acc = 0.00001;
double distance;
int count, i, j;
HDC hdc[num];
HPEN hpen[num];
inline double sqr(double x) {return x * x;}
DWORD WINAPI threadProc(LPVOID lpParamter) {
HWND hwnd = *(HWND*)lpParamter;
for(i=0; i<num; ++i) {
hdc[i] = GetDC(hwnd);
hpen[i] = CreatePen(PS_SOLID, width, color[i]);
SelectObject(hdc[i], hpen[i]);
MoveToEx(hdc[i], position[i][0], position[i][1], 0);
}
Sleep(1000);
while(1) {
for(i=0; i<num; ++i) {
LineTo(hdc[i], position[i][0], position[i][1]);
position[i][0] += velocity[i][0];
position[i][1] += velocity[i][1];
}
}
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPSTR pCmdLine, int nCmdShow) {
const char *CLASS_NAME = "SUGEWND";
void *pHWND = &hInstance;
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(0, CLASS_NAME, "SUGE",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
if(hwnd == NULL) return 0;
ShowWindow(hwnd, SW_SHOWMAXIMIZED);
MSG msg={};
while(GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch(uMsg) {
case WM_DESTROY: {
PostQuitMessage(0);
return 0;
}
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc2=BeginPaint(hwnd, &ps);
FillRect(hdc2, &ps.rcPaint, CreateSolidBrush(background_color));
EndPaint(hwnd, &ps);
return 0;
}
case WM_CREATE: {
HANDLE hThread = CreateThread(NULL, 0, threadProc, &hwnd, 0, NULL);
CloseHandle(hThread);
return 0;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
You are passing a pointer to a local variable to your threadProc. After exiting from WindowProc it's local variables are no more valid.
You can pass HWND by value:
case WM_CREATE: {
HANDLE hThread = CreateThread(NULL, 0, threadProc, (LPVOID)hwnd, 0, NULL);
CloseHandle(hThread);
return 0;
}
and read it's value so:
DWORD WINAPI threadProc(LPVOID lpParamter) {
HWND hwnd = (HWND)lpParamter;
Related
Below I have a code snippet showing what I have tried.
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <windowsx.h>
#define WC_MAIN "MainClass"
#define WC_NUMBER "NumberClass"
LRESULT CALLBACK NumberProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void InitClasses(void)
{
WNDCLASS wc;
memset(&wc, 0, sizeof(wc));
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpfnWndProc = MainWndProc;
wc.lpszClassName = WC_MAIN;
RegisterClass(&wc);
wc.lpfnWndProc = NumberProc;
wc.lpszClassName = WC_NUMBER;
RegisterClass(&wc);
}
#define NUMBER_SPEED 2
#define NUMBER_TICK_SPEED 25
#define NUMBER_TICKS 55
typedef struct {
UINT ticks;
HBITMAP buffer;
} NUMBERINFO;
HWND CreateNumber(HWND parent, const char *text, int x, int y, COLORREF color)
{
NUMBERINFO *ni = malloc(sizeof(*ni));
HDC hdc = GetDC(NULL);
HFONT oldFont = NULL;
SIZE s;
GetTextExtentPoint32(hdc, text, strlen(text), &s);
SelectObject(hdc, oldFont);
BITMAPINFO bmi;
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = s.cx;
bmi.bmiHeader.biHeight = s.cy;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32; // four 8-bit components
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = s.cx * s.cy * 4;
COLORREF *pvBits;
HBITMAP buffer = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**) &pvBits, NULL, 0);
HDC bufferDc = CreateCompatibleDC(hdc);
HBITMAP oldBmp = SelectObject(bufferDc, buffer);
oldFont = NULL;
SetTextAlign(bufferDc, TA_TOP | TA_LEFT);
SetBkMode(bufferDc, TRANSPARENT);
SetTextColor(bufferDc, color);
TextOut(bufferDc, 0, 0, text, strlen(text));
SelectObject(bufferDc, oldFont);
SelectObject(bufferDc, oldBmp);
DeleteDC(bufferDc);
ReleaseDC(NULL, hdc);
ni->buffer = buffer;
return CreateWindow(WC_NUMBER, text, WS_VISIBLE | WS_CHILD, x - (s.cx >> 1), y - (s.cy >> 1), s.cx, s.cy, parent, NULL, NULL, ni);
}
LRESULT CALLBACK NumberProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
NUMBERINFO *info = (NUMBERINFO*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
switch(msg)
{
case WM_CREATE:
info = ((CREATESTRUCT*) lParam)->lpCreateParams;
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) info);
SetTimer(hWnd, 0, NUMBER_TICK_SPEED, NULL);
info->ticks = NUMBER_TICKS;
return 0;
case WM_DESTROY: return 0;
case WM_TIMER:
{
RECT rc;
GetWindowRect(hWnd, &rc);
HWND parent = GetParent(hWnd);
MapWindowPoints(HWND_DESKTOP, parent, (POINT*) &rc, 2);
rc.top -= NUMBER_SPEED;
if(!--info->ticks)
{
DestroyWindow(hWnd);
return 0;
}
SetWindowPos(hWnd, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top - NUMBER_SPEED, SWP_NOREDRAW | SWP_NOCOPYBITS);
// redraw parent call erases last shown number
RedrawWindow(GetParent(hWnd), &rc, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
return 0;
}
case WM_ERASEBKGND: return 1;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
RECT r;
GetClientRect(hWnd, &r);
HDC bufferDc = CreateCompatibleDC(hdc);
HBITMAP oldBmp = SelectObject(bufferDc, info->buffer);
BLENDFUNCTION bfn;
bfn.BlendOp = AC_SRC_OVER;
bfn.BlendFlags = 0;
bfn.SourceConstantAlpha = info->ticks * 0xFF / NUMBER_TICKS;
bfn.AlphaFormat = 0;
//TransparentBlt(hdc, 0, 0, r.right, r.bottom, bufferDc, 0, 0, r.right, r.bottom, 0);
AlphaBlend(hdc, 0, 0, r.right, r.bottom, bufferDc, 0, 0, r.right, r.bottom, bfn);
SelectObject(bufferDc, oldBmp);
DeleteDC(bufferDc);
EndPaint(hWnd, &ps);
return 0;
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
LRESULT CALLBACK MainWndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY: PostQuitMessage(0); return 0;
case WM_ERASEBKGND: return 1;
case WM_PAINT:
{
InvalidateRect(hWnd, NULL, FALSE);
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// draw gradient (red to green)
RECT r;
GetClientRect(hWnd, &r);
GRADIENT_RECT gr;
gr.UpperLeft = 0;
gr.LowerRight = 1;
TRIVERTEX pVertex[2] = {
{ 0, 0, 0xFF00, 0x0000, 0x0000, 0xFF00 },
{ r.right, r.bottom, 0x0000, 0xFF00, 0x0000, 0xFF00 }
};
GradientFill(hdc, pVertex, 2, &gr, 2, GRADIENT_FILL_RECT_H);
EndPaint(hWnd, &ps);
return 0;
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int main()
{
InitClasses();
HWND mainWindow = CreateWindow(WC_MAIN, "Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), NULL);
ShowWindow(mainWindow, 1);
UpdateWindow(mainWindow);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
// adding a number at mouse position if pressed (testing)
if(msg.message == WM_LBUTTONDOWN)
{
RECT r;
GetClientRect(mainWindow, &r);
int x = GET_X_LPARAM(msg.lParam);
int y = GET_Y_LPARAM(msg.lParam);
CreateNumber(mainWindow, "123", x, y, 0xFFFF00);
}
}
return 0;
}
This draws some text with any color and every time a timer ticks, the text becomes more faded (more transparent), but the black background of the bitmap the text was drawn on, is drawn, but I want no background to be present, just the text.
I would assume I need a combination of TransparentBlt and AlphaBlend.
How would I go on to solve this?
The solution was quite simple. It was to use AC_SRC_ALPHA for the AlphaFormat member of BLENDFUNCTION and in order for that to work, set the alpha values of the HBITMAP, the buffer.
After the buffer (the one inside of NUMBERINFO) is created (in CreateNumber; check question code for reference), one must loop over all the colors and set the alpha value to 255 where needed.
for(int i = s.cx * s.cy; i--; pvBits++)
{
if(*pvBits)
*pvBits |= 0xFF000000; // make non black pixels opaque
}
And setting the flag in WM_PAINT: bfn.AlphaFormat = AC_SRC_ALPHA;
Ok, so I literally just started using the WinAPI for drawing bitmap images. So if my code is absolute trash, I apologize. Anyways, how the heck am I supposed to do this? Essentially, I want to create an HBITMAP object from a simple unsigned char pixel buffer using CreateBitmap() like this...
HBITMAP hbm = CreateBitmap(width, height, 1, 24, buffer);
(obviously this is 3-bytes per pixel)
That appears to work fine until I try rendering it. For now, I just wanted to keep it simple and draw a 100 by 100 black pixel square on the screen but nothing shows up. I would appreciate it if anyone could point out the mistakes in my atrocious code.
This is my setup (I am aware a lot of things could be improved I just wanted to go as minimalistic as possible for now and actually have something appear on the screen):
HBITMAP hbm = NULL;
void window_init(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
static unsigned char buffer[30000] = {0};
hbm = CreateBitmap(100, 100, 1, 24, buffer);
}
void window_draw(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmOld = SelectObject(hdcMem, hbm);
BitBlt(hdc, 100, 100, 100, 100, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE: window_init(hwnd, msg, wParam, lParam); break;
case WM_PAINT: window_draw(hwnd, msg, wParam, lParam); break;
case WM_CLOSE: DestroyWindow(hwnd); break;
case WM_DESTROY: PostQuitMessage(0); break;
default: return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Here's my entire code in case you want to copy it...
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
typedef struct {
const char *wc_name;
const char *title;
unsigned int width;
unsigned int height;
} Window;
static HBITMAP hbmp = NULL;
static PAINTSTRUCT ps;
void window_create(Window *window, const char *wc_name, const char *title, unsigned int width, unsigned int height) {
window->wc_name = wc_name;
window->title = title;
window->width = width;
window->height = height;
}
void window_init(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
static unsigned char buffer[30000] = {0};
hbmp = CreateBitmap(100, 100, 1, 24, buffer);
}
void window_draw(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmOld = SelectObject(hdcMem, hbmp);
BitBlt(hdc, 100, 100, 100, 100, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);;
EndPaint(hwnd, &ps);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE: window_init(hwnd, msg, wParam, lParam); break;
case WM_PAINT: window_draw(hwnd, msg, wParam, lParam); break;
case WM_CLOSE: DestroyWindow(hwnd); break;
case WM_DESTROY: PostQuitMessage(0); break;
default: return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
Window window;
window_create(&window, "Parent Window", "My Window", 1000, 1000 / 16 * 9);
WNDCLASSEX wc = {0};
MSG msg = {0};
HWND hwnd = NULL;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
wc.lpszClassName = window.wc_name;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc)) {
MessageBox(NULL, "Failed to register application window!", "WC Registration Error!", MB_ICONEXCLAMATION | MB_OK);
return -1;
}
hwnd = CreateWindowEx(
0, window.wc_name, window.title,
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
window.width, window.height, NULL, NULL, hInstance, NULL
);
if (!hwnd) {
MessageBox(NULL, "Failed to launch application window!", "HWND Creation Error!", MB_ICONEXCLAMATION | MB_OK);
return -1;
}
ShowWindow(hwnd, SW_MAXIMIZE);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
CreateBitmap produces a handle to a device dependent bitmap unlike CreateDIBSection which is guaranteed to create a device-independent bitmap with the device context if it succeeds, and allows you access to the actual bits in the bitmap, unlike CreateCompatibleBitmap.
How to use CreateDIBSection:
HDC hdc = GetDC(hwnd);
BITMAPINFO bi = { 0 };
static BYTE *bits = NULL;
static unsigned char buffer[30000] = { 0 };
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = 100;
bi.bmiHeader.biHeight = -100;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 24;
bi.bmiHeader.biCompression = BI_RGB;
hbmp = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
CopyMemory(bits, buffer, 30000);
ReleaseDC(hwnd, hdc);
When I use DrawThemeBackground() with part TABP_TABITEMRIGHTEDGE and state TIRES_NORMAL it leaves a rect 2 pixels wide on the right unpainted. It doesn't happen with part TABP_TABITEM, TABP_TABITEMLEFTEDGE or TABP_TABITEMBOTHEDGE. What can I do to fix this?
Edit: This only happens in windows 10 and not in widnows xp. I could erase the whole background, then paint the tab and this would solve my problem, and I wouldn't see any flicker in windows 10, but it will flicker in older versions of windows and I don't want that, so I erase the background excluding the area occupied by the tab. In windows 10 it leaves a black rect 2 pixels wide to the right of the tab. Is there a theme function that tells you how much of the rect specified to DrawThemeBackground() will be painted by DrawThemeBackground()?
#include <windows.h>
#include <uxtheme.h>
#include <vsstyle.h>
#include <vssym32.h>
#pragma comment(lib, "uxtheme.lib")
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HTHEME hTheme;
static HBRUSH hbrBkg;
switch(msg)
{
case WM_CREATE:
hTheme = OpenThemeData(0, L"Tab");
hbrBkg = CreateSolidBrush(RGB(0, 120, 120));
break;
case WM_PAINT:
{
HDC hdc;
PAINTSTRUCT ps;
hdc = BeginPaint(hwnd, &ps);
if(hdc)
{
HBRUSH hbr = CreateSolidBrush(RGB(255, 0, 0));
if(hbrBkg) FillRect(hdc, &ps.rcPaint, hbrBkg);
if(hbr)
{
RECT rc = { 4, 4, 104, 44 };
FillRect(hdc, &rc, hbr);
if(hTheme) DrawThemeBackground(hTheme, hdc, TABP_TABITEMRIGHTEDGE, TIRES_NORMAL, &rc, 0);
DeleteObject(hbr);
}
EndPaint(hwnd, &ps);
}
}
break;
case WM_DESTROY:
if(hTheme) CloseThemeData(hTheme);
if(hbrBkg) DeleteObject(hbrBkg);
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
const WCHAR szClassName[] = L"Themes";
WNDCLASSEXW wc;
HWND hwnd;
MSG msg;
SecureZeroMemory(&wc, sizeof(WNDCLASSEXW));
wc.cbSize = sizeof(WNDCLASSEXW);
wc.hCursor = LoadCursorW(0, IDC_ARROW);
wc.hIcon = LoadIconW(0, IDI_APPLICATION);;
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = szClassName;
if(!RegisterClassExW(&wc)) return 0;
hwnd = CreateWindowExW(0, szClassName, L"Test", WS_OVERLAPPEDWINDOW, 140, 140, 440, 240, 0, 0, hInstance, 0);
if(!hwnd) return 0;
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while(GetMessageW(&msg, 0, 0, 0) > 0)
{
if(!IsDialogMessageW(hwnd, &msg))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
return (int)msg.wParam;
}
I am trying to create my first GUI Application. I just want to display a text(could be a number maybe), and then in a loop change/update it. I found some basic examples to create and display a window, with some text, but how do i update the text?
Could someone please show me a simple example? A good example would be displaying the time.
Thanks in advance!
Update:
Here is my code. It is nothing special, i just took an example from MSDN.
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
static TCHAR szWindowClass[] = _T( "win32app" );
static TCHAR szTitle[] = _T( "Win32 Guided Tour Application" );
HINSTANCE hInst;
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
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_APPLICATION ) );
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_APPLICATION ) );
if ( !RegisterClassEx( &wcex ) )
{
MessageBox( NULL, _T( "Call to RegisterClassEx failed!" ), _T( "Win32 Guided Tour" ), NULL );
return 1;
}
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindow( szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 100, NULL, NULL, hInstance, NULL );
if ( !hWnd )
{
MessageBox( NULL, _T( "Call to CreateWindow failed!" ), _T( "Win32 Guided Tour" ), NULL );
return 1;
}
ShowWindow( hWnd, nCmdShow );
UpdateWindow( hWnd );
HDC hdc;
PAINTSTRUCT ps;
// Main message loop:
MSG msg;
char test[ 100 ] = { 0 };
int i = 0;
while ( GetMessage( &msg, NULL, 0, 0 ) )
{
i++;
hdc = BeginPaint( hWnd, &ps );
sprintf(test, "%d", i);
TextOutA( hdc, 5, 5, test, strlen( test ) );
EndPaint( hWnd, &ps );
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return ( int )msg.wParam;
}
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
PAINTSTRUCT ps;
HDC hdc;
TCHAR greeting[] = _T( "Hello, World!" );
switch ( message )
{
case WM_PAINT:
hdc = BeginPaint( hWnd, &ps );
TextOut( hdc, 5, 5, greeting, _tcslen( greeting ) );
EndPaint( hWnd, &ps );
break;
case WM_DESTROY:
PostQuitMessage( 0 );
break;
default:
return DefWindowProc( hWnd, message, wParam, lParam );
break;
}
return 0;
}
1) Do not do the drawing in your loop.
2) Only draw in WM_PAINT
3) Create a variable that contains what you want to draw
4) If you want to redraw your window, call InvalidateRect(hWnd, NULL, NULL) and it will post a WM_PAINT message to your window proc.
5) I'd suggest creating a timer that redraws maybe once every 5 seconds. Ideally, you would redraw when something changes the state of your data. If you redraw every time through your message loop, it's going to continuously redraw and be very unresponsive.
This example shows you how a number is printed in the center of the window and incremented and updated whenever your press with the left mouse button anywhere on the window's client area.
#include <windows.h>
#include <cstdio>
LRESULT __stdcall wndProc(HWND, UINT, WPARAM, LPARAM);
void register_window_class(HINSTANCE hInstance)
{
WNDCLASSEX wndclass;
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = wndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(COLOR_BTNFACE + 1);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = "wndclass";
wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&wndclass);
}
HWND create_window(HINSTANCE hInstance)
{
HWND hwnd = CreateWindowEx(
WS_EX_OVERLAPPEDWINDOW,
"wndclass",
"My first window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
800,
600,
NULL,
NULL,
hInstance,
NULL);
return hwnd;
}
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, char* szCmdLine, int iCmdShow)
{
try{
register_window_class(hInstance);
HWND hwnd = create_window(hInstance);
ShowWindow(hwnd, SW_SHOWNORMAL);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return EXIT_SUCCESS;
}
catch(...){
return EXIT_FAILURE;
}
}
LRESULT __stdcall wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
RECT rect;
PAINTSTRUCT ps;
static int iCount = 0;
static char buffer[256];
switch(msg){
case WM_LBUTTONDOWN:
++iCount;
snprintf(buffer, 256, "%d", iCount);
InvalidateRect(hwnd, NULL, true);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
SetTextAlign(hdc, TA_CENTER);
TextOut(hdc, rect.right / 2, rect.bottom / 2, buffer, strlen(buffer));
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
So when you click with the left mouse button, the message
WM_LBUTTONDOWN
is sent to your window procedure by Windows. Whenever that occurs, a static int is incremented, written to a char buffer. Finally, the call
InvalidateRect(hwnd, NULL, true);
invalidates the window's entire client area. This means that WM_PAINT will be called because there is a portion of the client area that is invalid. Also, the last argument which is set to true makes sure that when
hdc = BeginPaint(hwnd, &ps);
is executed, the invalid section of the client area is painted over with the background brush specified in the window class. This effectively erases any previous window contents so that
TextOut(hdc, rect.right / 2, rect.right / 2, buffer);
has a clean area to write on.
It is a good habit to structure your program so that all information is accumulated so a complete re-paint can be done in WM_PAINT (basically quoting the Win32 bible "Programming Windows" by Charles Petzold).
You need to do all your drawing in the WM_PAINT handler in your message loop between the BeginPaint/EndPaint calls, otherwise it'll get overwritten.
Here's an example that displays the time. I use a timer to invalidate the window which generates a WM_PAINT message but you can do it a different way if you like.
#include <SDKDDKVer.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
HINSTANCE hInst;
HWND hWnd;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch(message)
{
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
SYSTEMTIME time;
GetLocalTime(&time);
wchar_t timeString[30] = {};
GetTimeFormatEx(nullptr, 0, &time, nullptr, timeString, 30);
RECT clientRect;
GetClientRect(hWnd, &clientRect);
DrawText(hdc, timeString, -1, &clientRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
EndPaint(hWnd, &ps);
}
break;
case WM_TIMER:
InvalidateRect(hWnd, nullptr, false);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
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(NULL, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"TestClass";
wcex.hIconSm = NULL;
return RegisterClassEx(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance;
RECT sz = {0, 0, 128, 64};
AdjustWindowRect(&sz, WS_OVERLAPPEDWINDOW, TRUE);
hWnd = CreateWindow(L"TestClass", L"Test Window", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, sz.right - sz.left, sz.bottom - sz.top,
NULL, NULL, hInstance, NULL);
if(!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
MyRegisterClass(hInstance);
if(!InitInstance(hInstance, nCmdShow))
{
return FALSE;
}
SetTimer(hWnd, 1, 1000, nullptr);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
I'm writing a very simple program, which prints color of selected pixel.
Here is my code:
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define UNICODE
LRESULT CALLBACK mouse_hook_low_level(int nCode, WPARAM wParam, LPARAM lParam)
{
if(wParam == WM_MOUSEMOVE) {
//Need to get a handle to the window!
InvalidateRect(window, NULL, FALSE);
UpdateWindow(window);
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
LRESULT CALLBACK window_process(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC dc = GetDC(NULL);
HDC hdc;
HFONT font;
PAINTSTRUCT ps;
int r, g, b;
POINT p;
RECT background_rect, color_rect;
wchar_t buffer[256];
switch(message) {
case WM_PAINT:
GetCursorPos(&p);
r = GetRValue(GetPixel(dc, p.x, p.y));
g = GetGValue(GetPixel(dc, p.x, p.y));
b = GetBValue(GetPixel(dc, p.x, p.y));
hdc = BeginPaint(window, &ps);
background_rect.left = 0;
background_rect.right = 199;
background_rect.top = 0;
background_rect.bottom = 99;
FillRect(hdc, &background_rect, (HBRUSH)(COLOR_WINDOW+1));
font = CreateFont(
14,
0,
0,
0,
FW_DONTCARE,
FALSE,
FALSE,
FALSE,
DEFAULT_CHARSET,
OUT_OUTLINE_PRECIS,
CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY,
VARIABLE_PITCH,
TEXT("Times New Roman")
);
SelectObject(hdc, font);
swprintf_s(buffer, 256, L"Coordinates: (%d, %d)", p.x, p.y);
TextOut(hdc, 70, 10, buffer, wcslen(buffer));
swprintf_s(buffer, 256, L"RGB: (%d, %d, %d)", r, g, b);
TextOut(hdc, 70, 40, buffer, wcslen(buffer));
color_rect.left = 10;
color_rect.right = 60;
color_rect.top = 10;
color_rect.bottom = 60;
FillRect(hdc, &color_rect, (HBRUSH)CreateSolidBrush(RGB(r, g, b)));
EndPaint(window, &ps);
break;
case WM_CLOSE:
DestroyWindow(window);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
ReleaseDC(window, hdc);
return DefWindowProc(window, message, wParam, lParam);
}
ReleaseDC(window, hdc);
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
LPCWSTR class_name = L"name";
MSG message;
WNDCLASSEX window_class;
HWND window;
HHOOK MouseHook;
window_class.cbSize = sizeof(WNDCLASSEX);
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.lpfnWndProc = window_process;
window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0;
window_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
window_class.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
window_class.lpszMenuName = NULL;
window_class.lpszClassName = class_name;
window_class.hInstance = hInstance;
window_class.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&window_class);
window = CreateWindow(
class_name,
L"Pixel color",
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU,
CW_USEDEFAULT,
CW_USEDEFAULT,
200,
100,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow(window, SW_SHOWNORMAL);
MouseHook = SetWindowsHookEx(WH_MOUSE_LL, mouse_hook_low_level, hInstance, 0);
while(GetMessage(&message, NULL, 0, 0)) {
TranslateMessage(&message);
DispatchMessage(&message);
}
UnhookWindowsHookEx(MouseHook);
return message.wParam;
}
The question is: how can I get a handle to the window for further window update in hook procedure at line 9? And what can you say generally about my code? I'm a student and have a little C-programming experience, could you point out my errors?
Thanks in advance.
First, try WindowFromPoint. If it fails to find a window, i.e. it's not your process window, then enumerate all top-level windows and all its child windows to find a topmost window under the mouse pointer.