Screen is flickering even when using doublebuffering - c

I am totally new in programming.
For my first programm I tried to make a small game in c with windows api.
the following code is a nearly working snake, (some bugs not fixed yet)
but i cant find a working solution to fix that flickering.
(I dont know double Buffering so I just took from here: [http://www.codeguru.com/forum/archive/index.php/t-272723.html][1]
// snake1.02.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "snake1.02.h"
#include "stdio.h"
#define ID_MYTIMER 1234567
#define ID_BERRY 1
#define SEGMENTSIZE 10
const WCHAR szClassName[] = L"Snake1.02";
HWND hWnd;
UINT myTimer;
struct point {
int x, y;
};
point snake[500];
int dx = SEGMENTSIZE; //Change direction l=l+1
int dy = 0;
int snakelength=3;
int highscore=0;
WCHAR nachricht[100];
int map[80][60]; //Wenn Bildschirmgröße angepasst wird, auch hier anpassen!!!
// Global Variables:
HINSTANCE hInst; // current instance
// 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_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
MSG msg;
// Initialize global strings
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
myTimer = SetTimer ( hWnd, ID_MYTIMER, 40, NULL );
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
KillTimer ( hWnd, myTimer );
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(NULL, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+2);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szClassName;
wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
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)
{
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szClassName, szClassName, WS_OVERLAPPED,
CW_USEDEFAULT, 0, 800, 600, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
for(int i=snakelength; i>=0; i--){
snake[i].x =100-(i*SEGMENTSIZE);
snake[i].y=20;}
for(int x=0; x<78;x++){
map[x][0]=1;
map[x][56]=1;
}
for(int y=0; y<57;y++){
map[0][y]=1;
map[78][y]=1;
}
map[50][70]=2;
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// 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)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
HDC memDC;
HBITMAP hMemBmp;
HBITMAP hOldBmp;
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
memDC = CreateCompatibleDC(hdc); //flimmerfrei
hMemBmp = CreateCompatibleBitmap(hdc, 800, 600);
hOldBmp = (HBITMAP)SelectObject(memDC, hMemBmp);
// TODO: Add any drawing code here...
for (int i = 0; i<=snakelength; i++) {
Rectangle(memDC, snake[i].x,snake[i].y,snake[i].x+10,snake[i].y+10);
}
for (int y=0; y<60;y++){
for (int x=0; x<80;x++){
if(map[x][y]==1||map[x][y]==2){
Rectangle(memDC,x*SEGMENTSIZE,y*SEGMENTSIZE,x*SEGMENTSIZE+SEGMENTSIZE,y*SEGMENTSIZE+SEGMENTSIZE);
}}
}
BitBlt(hdc, 0, 0, 800, 600, memDC, 0, 0, SRCCOPY);
SelectObject(memDC, hOldBmp);
DeleteObject(hMemBmp);
DeleteDC(memDC);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_TIMER:
switch (wParam)
{
case ID_MYTIMER: {
if (map[snake[0].x/10][snake[0].y/10]==1){ //crashcheck
KillTimer ( hWnd, myTimer );
wsprintf (nachricht,L"Game Over. Punktestand: %d",highscore);
MessageBox(hWnd, (LPCWSTR)nachricht,L"Ende",0);
}
if (map[snake[0].x/10][snake[0].y/10]==2) //grow
{
for (int i = snakelength; i>0; i--)
{
snake[i].x = snake[i-1].x;
snake[i].y = snake[i-1].y;
}
snake[0].x = snake[1].x + dx;
snake[0].y = snake[1].y + dy;
map[snake[0].x/10][snake[0].y/10]=0;
snakelength++;
highscore+=10;
}
for (int i = snakelength; i>0; i--) {
snake[i].x = snake[i-1].x;
snake[i].y = snake[i-1].y;
}
snake[0].x = snake[1].x + dx;
snake[0].y = snake[1].y + dy;
InvalidateRect(hWnd, 0,1);
return 0;
}
}
break;
case WM_KEYDOWN: {
switch (wParam) {
case VK_DOWN:{
if(dy!=-SEGMENTSIZE){
dy=SEGMENTSIZE;
dx=0;
}
}
break;
case VK_RIGHT:{
if (dx!=-SEGMENTSIZE){
dy=0;
dx=SEGMENTSIZE;
}
}
break;
case VK_UP:{
if (dy!=SEGMENTSIZE){
dy=-SEGMENTSIZE;
dx=0;
}
}
break;
case VK_LEFT:{
if(dx!=SEGMENTSIZE){
dy=0;
dx=-SEGMENTSIZE;
}
}
break;
case VK_ESCAPE:{
ExitProcess(0);
}
break;
}
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

Add one case to your main switch statement in WndProc:
case WM_ERASEBKGND:
return TRUE;

Related

Message queue for two cpp file with windows API

To send the message in the Queue from one source file and get the message in another source file.I read the docs from Microsoft and try to implement as below
test2.c - post the message
main.c - get the Message
Testing1: If i execute the same code in single file that get executed and i receive the data
Testing : Same code is written in two separate file "if (msg.message == WM_YOUR_MESSAGE)" these statement does not get satisfied.
test2.h
typedef struct
{
int SomeData1;
int SomeData2;
int SomeDataN;
} MessageData;
/* Unique IDs for Window messages to exchange between the worker and the
GUI thread. */
#define WM_YOUR_MESSAGE ( WM_USER + 3 )
void __cdecl ThreadProc(void* aArg);
test2.c
#include <windows.h>
#include <process.h>
#include "malloc.h"
#include <stdio.h>
#include <test2.h>
volatile DWORD ThreadID_GUI;
void __cdecl ThreadProc(void* aArg)
{
MessageData* data;
for (;; )
{
Sleep(500);
/* Allocate memory for a new message data structure */
data = (MessageData*)malloc(sizeof(*data));
/* Initialize the message data structure with some information to transfer
to the GUI thread. */
data->SomeData1 = 1234;
data->SomeData2 = 4567;
data->SomeDataN = 7894;
PostThreadMessage(ThreadID_GUI, WM_YOUR_MESSAGE, 0, (LPARAM)data);
}
}
main.c
#include <windows.h>
#include <tchar.h>
#include <process.h>
#include "malloc.h"
#include "stdio.h"
#include<test2.h>
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
ThreadID_GUI = GetCurrentThreadId();
/* Start some background thread */
_beginthread(ThreadProc, 0, 0);
// Initialize global strings
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_TESTMESSAGEQUEUE, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTMESSAGEQUEUE));
MSG msg;
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
/* STEP 3: React on the message sent from the foreign thread */
if (msg.message == WM_YOUR_MESSAGE)
{
MessageData* tmp = (MessageData*)msg.lParam;
if (tmp->SomeData1 == 1234) {
printf("someData\n");
}
/* Free the data structure associated to the message */
free(tmp);
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW 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_CLIENTMQ));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_CLIENTMQ);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance,
MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance,
nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd,
About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM
lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
After filling all the missing information in your source, and removing useless code, I have your code working.
main.c:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <process.h>
#include "test2.h"
HINSTANCE hInst;
LPCWSTR szWindowClass = L"Piu";
LPCWSTR szTitle = L"Title";
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
/*
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd,
About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
*/
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
ZeroMemory(&wcex, sizeof(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_CLIENTMQ));
wcex.hIconSm = LoadIcon(wcex.hInstance,
MAKEINTRESOURCE(IDI_SMALL));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_CLIENTMQ);
*/
wcex.lpszClassName = szWindowClass;
return RegisterClassExW(&wcex);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, hInstance,
NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
ThreadID_GUI = GetCurrentThreadId();
/* Start some background thread */
_beginthread(ThreadProc, 0, 0);
// Initialize global strings
// LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
// LoadStringW(hInstance, IDC_TESTMESSAGEQUEUE, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance(hInstance, nCmdShow))
{
return FALSE;
}
// HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTMESSAGEQUEUE));
MSG msg;
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
/* STEP 3: React on the message sent from the foreign thread */
if (msg.message == WM_YOUR_MESSAGE)
{
MessageData* tmp = (MessageData*)msg.lParam;
if (tmp->SomeData1 == 1234) {
printf("someData\n");
}
/* Free the data structure associated to the message */
free(tmp);
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
test2.c:
#include <Windows.h>
#include "test2.h"
volatile DWORD ThreadID_GUI;
void __cdecl ThreadProc(void* aArg)
{
MessageData* data;
for (;; )
{
Sleep(500);
/* Allocate memory for a new message data structure */
data = (MessageData*)malloc(sizeof(*data));
/* Initialize the message data structure with some information to transfer
to the GUI thread. */
data->SomeData1 = 1234;
data->SomeData2 = 4567;
data->SomeDataN = 7894;
PostThreadMessage(ThreadID_GUI, WM_YOUR_MESSAGE, 0, (LPARAM)data);
}
}
test2.h
typedef struct
{
int SomeData1;
int SomeData2;
int SomeDataN;
} MessageData;
/* Unique IDs for Window messages to exchange between the worker and the
GUI thread. */
#define WM_YOUR_MESSAGE ( WM_USER + 3 )
void __cdecl ThreadProc(void* aArg);
extern volatile DWORD ThreadID_GUI;
I removed menu and icons so there is no .rc file.

Great Performance Decreasement when Moving the GetDC code into a thread

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;

How to update the text inside a window?

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;
}

WinApi, Push button are not displayed

I'm trying to create simple windows app uisng WinAPi.
Here is the code:
#include "stdafx.h"
#include "APIup.h"
#define MAX_LOADSTRING 100
HWND hWnd,cw13;
// 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);
ATOM Register(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK WndProc_dla13(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
MSG msg;
HACCEL hAccelTable;
// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_APIUP, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
Register(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_APIUP));
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
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_APIUP));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_APIUP);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&wcex);
}
ATOM Register(HINSTANCE hInstance)
{
WNDCLASSEX cw13class;
cw13class.cbSize = sizeof(WNDCLASSEX);
cw13class.style = CS_HREDRAW | CS_VREDRAW;
cw13class.lpfnWndProc = WndProc_dla13;
cw13class.cbClsExtra = 0;
cw13class.cbWndExtra = 0;
cw13class.hInstance = hInstance;
cw13class.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APIUP));
cw13class.hCursor = LoadCursor(NULL, IDC_ARROW);
cw13class.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
cw13class.lpszMenuName = NULL;
cw13class.lpszClassName = L"13class";
cw13class.hIconSm = LoadIcon(cw13class.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&cw13class);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, 100, 100, NULL, NULL, hInstance, NULL);
cw13=CreateWindow(L"13class",L"Ćwiczenie 13",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,0,300,300,NULL,NULL,hInst,NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
case ID_32771:
ShowWindow(cw13, 1);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK WndProc_dla13 (HWND hWnd, UINT msg/*message*/, WPARAM wParam,LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
CreateWindowW(L"button", L"Beep",
WS_VISIBLE | WS_CHILD ,
20, 50, 80, 25,
cw13, (HMENU) 1, hInst, NULL);
CreateWindowW(L"button", L"Quit",
WS_VISIBLE | WS_CHILD ,
120, 50, 80, 25,
cw13, (HMENU) 2, NULL, NULL);
break;
case WM_COMMAND:
if (LOWORD(wParam) == 1) {
Beep(40, 50);
}
if (LOWORD(wParam) == 2) {
PostQuitMessage(0);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
And the problem is that: when i create window 'cw13' it is created and displayed, but push buttons are not.I created two separate WndProc functions. Other messages are executed correctly( like WM_QUIT), only CreateWindowW under WM_CREATE,witch should create and show Push buttons aren't. Where is the problem?
When processing the WM_CREATE message, your call to CreateWindow(L"13class") is still running. Your cw13 variable has not been assigned yet, so you are assigning an invalid HWND as the parent of your buttons. That is why they do not appear. Use the hwnd parameter of WndProc_dla13() instead as the parent. That will be the same HWND that gets assigned to the cw13 variable once CreateWindow(L"13class") exits.

ANSI C & WinAPI: how to get a handle to the window from hook procedure?

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.

Resources