I am extending an old console application. Trying to add a window( for an animation ). Are able to display first image, but unable to update the window with later images. Have created a minimum application to demonstrate the problem. In this app I have a red window that should change at each update of the window( it is not ). What am I doing wrong ?
#include "pch.h"
#include <iostream>
#include <Windows.h>
using namespace std;
HWND hWindow;
LRESULT __stdcall MyWindowProcedure(HWND hWnd, unsigned int msg, WPARAM wp, LPARAM lp)
{
PAINTSTRUCT ps;
HDC hdc;
HBITMAP hBitmap;
VOID * pvBits;
RGBQUAD *bmiColors;
static int j = 128;
HBITMAP hOldBmp;
BITMAPINFO MyBitmap;
BOOL qRetBlit;
switch (msg)
{
case WM_DESTROY:
std::cout << "\ndestroying window\n";
PostQuitMessage(0);
return 0L;
case WM_LBUTTONDOWN:
std::cout << "\nmouse left button down at (" << LOWORD(lp)
<< ',' << HIWORD(lp) << ")\n";
// fall thru
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
#if 1
// Create a device context that is compatible with the window
HDC hLocalDC;
hLocalDC = ::CreateCompatibleDC(hdc);
// Verify that the device context was created
if (hLocalDC == NULL) {
printf("CreateCompatibleDC Failed\n");
// return false;
break;
}
MyBitmap.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
MyBitmap.bmiHeader.biWidth = 256;
MyBitmap.bmiHeader.biHeight = -256;
MyBitmap.bmiHeader.biPlanes = 1;
MyBitmap.bmiHeader.biBitCount = 32;
MyBitmap.bmiHeader.biCompression = BI_RGB;
MyBitmap.bmiHeader.biSizeImage = 256 * 256 * 4;
MyBitmap.bmiHeader.biYPelsPerMeter = 1000;
MyBitmap.bmiHeader.biXPelsPerMeter = 1000;
MyBitmap.bmiHeader.biClrUsed = 0;
MyBitmap.bmiHeader.biClrImportant = 0;
hBitmap = CreateDIBSection(NULL, &MyBitmap, DIB_RGB_COLORS, &pvBits, NULL, NULL);
bmiColors = (RGBQUAD*)pvBits;
// note: BGR
for (int i = 0; i < 256 * 256; i++)
{
bmiColors[i].rgbBlue = 0;
bmiColors[i].rgbGreen = 0;
bmiColors[i].rgbRed = j % 256;
bmiColors[i].rgbReserved = 255;
}
j += 10;
bmiColors[1000].rgbRed;
// Select the bitmap into the device context
hOldBmp = (HBITMAP)::SelectObject(hLocalDC, hBitmap);
if (hOldBmp == NULL) {
printf("SelectObject Failed");
// return false;
break;
}
// Blit the dc which holds the bitmap onto the window's dc
qRetBlit = ::BitBlt(hdc, 0, 0, 256, 256, hLocalDC, 0, 0, SRCCOPY);
if (!qRetBlit) {
printf("Blit Failed");
// return false;
break;
}
// Unitialize and deallocate resources
SelectObject(hLocalDC, hOldBmp);
DeleteDC(hLocalDC);
DeleteObject(hBitmap);
#endif
EndPaint(hWnd, &ps);
break;
default:
std::cout << '.';
break;
}
return DefWindowProc(hWnd, msg, wp, lp);
}
int main()
{
WNDCLASSEX wndclass = { sizeof(WNDCLASSEX), CS_DBLCLKS | CS_HREDRAW, MyWindowProcedure,
0, 0, GetModuleHandle(0), LoadIcon(0,IDI_APPLICATION),
LoadCursor(0,IDC_ARROW), HBRUSH(COLOR_WINDOW + 1),
0, L"myclass", LoadIcon(0,IDI_APPLICATION) };
if (RegisterClassEx(&wndclass))
{
hWindow = CreateWindowEx(0, L"myclass", L"myclass", WS_OVERLAPPEDWINDOW, 1200, 600, 256 + 15, 256 + 38, 0, 0, GetModuleHandle(0), 0);
ShowWindow(hWindow, SW_SHOWDEFAULT);
UpdateWindow(hWindow);
MSG msg;
byte bRet;
while ((bRet = GetMessage(&msg, hWindow, 0, 0)) != 0)
{
if (bRet == -1)
{
return 1;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
}
As pointed out. You do not want to call WM_PAINT your self. Let the system handle it. So in WM_LBUTTONDOWN do:
case WM_LBUTTONDOWN:
std::cout << "\nmouse left button down at (" << LOWORD(lp)
<< ',' << HIWORD(lp) << ")\n";
InvalidateRect(hWnd, nullptr, FALSE);
return 0;
Do not fall through. Wnen you call RedrawWindow the window is invalidated and then needs to be repainted. I just tested this, the window lightens up on each click.
UPDATE:
Thanks to Remy Lebeau
Difference between InvalidateRect and RedrawWindow
And yes, it is better to let windows use its own schedule for this kind of stuff.
Related
I'm working on a simple GUI, though my GUI window is unaligned and I cant see any issues in the code. I'm throwing this out there in hopes someone has seen this before because im pretty new to ImGui and have been looking at this code for days lol.
this GUI is using direct x 9.
here's the main .cpp file.
#pragma once
#include "gui.h"
#include "imgui.h"
#include "imgui_impl_dx9.h"
#include "imgui_impl_win32.h"
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(
HWND window,
UINT message,
WPARAM wideParameter,
LPARAM longParameter
);
long __stdcall WindowProcess(
HWND window,
UINT message,
WPARAM wideParameter,
LPARAM longParameter
)
{
if (ImGui_ImplWin32_WndProcHandler(window, message, wideParameter, longParameter))
return true;
switch (message)
{
case WM_SIZE: {
if (gui::device && wideParameter != SIZE_MINIMIZED)
{
gui::presentParams.BackBufferWidth = LOWORD(longParameter);
gui::presentParams.BackBufferHeight = LOWORD(longParameter);
gui::ResetDevice();
}
}return 0;
case WM_SYSCOMMAND: {
if ((wideParameter & 0xfff0) == SC_KEYMENU)
return 0;
}break;
case WM_DESTROY: {
PostQuitMessage(0);
}return 0;
case WM_LBUTTONDOWN: {
gui::pos = MAKEPOINTS(longParameter);
}return 0;
case WM_MOUSEMOVE: {
if (wideParameter == MK_LBUTTON)
{
const auto points = MAKEPOINTS(longParameter);
auto rect = ::RECT{};
GetWindowRect(gui::window, &rect);
rect.left += points.x - gui::pos.x;
rect.top += points.y - gui::pos.y;
if (gui::pos.x >= 0 &&
gui::pos.x <= gui::w &&
gui::pos.y >= 0 && gui::pos.y <= 19)
SetWindowPos(
gui::window,
HWND_TOPMOST,
rect.left,
rect.top,
0, 0,
SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOZORDER
);
}
}
}
return DefWindowProcW(window, message, wideParameter, longParameter);
}
void gui::CreateHWindow(const char* windowName, const char* className) noexcept
{
windowClass.cbSize = sizeof(WNDCLASSEXA);
windowClass.style = CS_CLASSDC;
windowClass.lpfnWndProc = WindowProcess;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = GetModuleHandleA(0);
windowClass.hIcon = 0;
windowClass.hCursor = 0;
windowClass.hbrBackground = 0;
windowClass.lpszMenuName = 0;
windowClass.lpszClassName = className;
windowClass.hIconSm = 0;
RegisterClassExA(&windowClass);
window = CreateWindowA(
className,
windowName,
WS_POPUP,
0,
0,
w,
h,
0,
0,
windowClass.hInstance,
0
);
ShowWindow(window, SW_SHOWDEFAULT);
UpdateWindow(window);
}
void gui::DestroyHWindow() noexcept
{
DestroyWindow(window);
UnregisterClass(windowClass.lpszClassName, windowClass.hInstance);
}
bool gui::CreateDevice() noexcept
{
d3d = Direct3DCreate9(D3D_SDK_VERSION);
if (!d3d)
return false;
ZeroMemory(&presentParams, sizeof(presentParams));
presentParams.Windowed = TRUE;
presentParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
presentParams.BackBufferFormat = D3DFMT_UNKNOWN;
presentParams.EnableAutoDepthStencil = TRUE;
presentParams.AutoDepthStencilFormat = D3DFMT_D16;
presentParams.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
if (d3d->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
window,
D3DCREATE_HARDWARE_VERTEXPROCESSING,
&presentParams,
&device) < 0)
return false;
return true;
}
void gui::ResetDevice() noexcept
{
ImGui_ImplDX9_InvalidateDeviceObjects();
const auto result = device->Reset(&presentParams);
if (result == D3DERR_INVALIDCALL)
IM_ASSERT(0);
ImGui_ImplDX9_CreateDeviceObjects();
}
void gui::DestroyDevice() noexcept
{
if (device)
{
device->Release();
device = nullptr;
}
if (d3d)
{
d3d->Release();
d3d = nullptr;
}
}
void gui::CreateGUI() noexcept
{
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ::ImGui::GetIO();
io.IniFilename = NULL;
// GUI style colors
ImGui::StyleColorsDark();
ImGui_ImplWin32_Init(window);
ImGui_ImplDX9_Init(device);
}
void gui::DestroyGUI() noexcept
{
ImGui_ImplDX9_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
}
void gui::BeginRender() noexcept
{
MSG message;
while (PeekMessage(&message, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
ImGui_ImplDX9_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
}
void gui::EndRender() noexcept
{
ImGui::EndFrame();
device->SetRenderState(D3DRS_ZENABLE, FALSE);
device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
device->Clear(0,0,D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA(100, 0, 230, 255), 1.0f, 0);
if (device->BeginScene() >= 0)
{
ImGui::Render();
ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
device->EndScene();
}
const auto result = device->Present(0, 0, 0, 0);
if (result == D3DERR_DEVICELOST && device->TestCooperativeLevel() == D3DERR_DEVICENOTRESET)
ResetDevice();
}
void gui::Render() noexcept
{
ImGui::SetWindowPos({0,0 });
ImGui::SetNextWindowSize({ w - 100,h - 100});
ImGui::Begin(
"Window",
&exit,
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoSavedSettings |
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove
);
}
[![GUI Image][1]][1]
[1]: https://i.stack.imgur.com/YBGdn.png
I am receiving those screenshots in WM_PAINT, and then save it to memory. The screenshots have around 8-10kB size. And I am trying to display those screenshots in a 15 FPS, but the problem is that its displaying sometimes glitched screenshots like this:
[the gray square with dots inside it, that should not be there, its covering the half of the screenshot :c ]
Image displayed like this, is like every second or third screenshot. I have no idea how to fix this im stuck a couple days at this.
Here is the server code (window procedure) where i am trying to display these screenshots
LRESULT CALLBACK ClientWndProc(HWND Chwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
std::vector<char> buffer(50000);
switch (uMsg)
{
case WM_CREATE:
{
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
SetTimer(Chwnd, SCREEN_TIMER, 1000/15, NULL);
break;
}
case WM_TIMER:
{
if (wParam == SCREEN_TIMER)
{
SOCKET selectedSocket = clientSockets[itemIndex];
auto it = std::find(clientSockets.begin(), clientSockets.end(), selectedSocket);
send(selectedSocket, "capscr", strlen("capscr"), 0);
InvalidateRect(Chwnd, NULL, TRUE);
}
break;
}
case WM_PAINT:
{
SOCKET selectedSocket = clientSockets[itemIndex];
auto it = std::find(clientSockets.begin(), clientSockets.end(), selectedSocket);
if (it != clientSockets.end())
{
int bytesReceived = 0;
int totalBytesReceived = 0;
int expectedBytes = buffer.size();
Sleep(1000/15);
do
{
bytesReceived = recv(selectedSocket, buffer.data() + totalBytesReceived, buffer.size() - totalBytesReceived, 0);
totalBytesReceived += bytesReceived;
} while (totalBytesReceived < expectedBytes && bytesReceived > 0);
//MessageBoxA(NULL, buffer.data(), "s", MB_OK);
HGLOBAL hGlobal = GlobalAlloc(GHND, expectedBytes);
void* pData = GlobalLock(hGlobal);
memcpy(pData, buffer.data(), buffer.size());
GlobalUnlock(hGlobal);
IStream* pStream = NULL;
CreateStreamOnHGlobal(hGlobal, TRUE, &pStream);
Gdiplus::Bitmap bitmap(pStream);
Gdiplus::Status status = bitmap.GetLastStatus();
PAINTSTRUCT ps;
HDC hdc = BeginPaint(Chwnd, &ps);
int imgWidth = bitmap.GetWidth();
int imgHeight = bitmap.GetHeight();
Gdiplus::Graphics graphics(hdc);
RECT clientRect;
GetClientRect(Chwnd, &clientRect);
graphics.DrawImage(&bitmap, 0, 0, imgWidth, imgHeight);
EndPaint(Chwnd, &ps);
GlobalFree(hGlobal);
}
break;
}
case WM_ERASEBKGND:
return TRUE;
case WM_CLOSE:
{
DestroyWindow(hwndClient);
break;
}
case WM_DESTROY:
DestroyWindow(hwndClient);
return 0;
default:
return DefWindowProc(Chwnd, uMsg, wParam, lParam);
}
return 0;
}
client code sending screenshots:
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if (size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return 0;
}
void shutdownGdiPlus()
{
Gdiplus::GdiplusShutdown(gdiPlusToken);
gdiPlusToken = NULL;
}
bool initGdiPlusIfNeeded()
{
// If already initialized then return true
if (gdiPlusToken != NULL)
return true;
static Gdiplus::GdiplusStartupInput gdiPlusStartupInput;
return (success = GdiplusStartup(&gdiPlusToken, &gdiPlusStartupInput, NULL)) == Gdiplus::Status::Ok;
}
std::pair<ULONGLONG, char*> getScreenShotAsByteArray()
{
if (!initGdiPlusIfNeeded())
return {};
IStream* iStream;
HRESULT res = CreateStreamOnHGlobal(NULL, true, &iStream);
const HDC srcDC = ::GetDC(NULL);
const int screenHeight = GetSystemMetrics(SM_CYSCREEN);
const int screenWidth = GetSystemMetrics(SM_CXSCREEN);
const HDC memDC = CreateCompatibleDC(srcDC);
const HBITMAP membit = CreateCompatibleBitmap(srcDC, screenWidth, screenHeight);
HBITMAP hOldBitmap = (HBITMAP)SelectObject(memDC, membit);
BitBlt(memDC, 0, 0, screenWidth, screenHeight, srcDC, 0, 0, SRCCOPY);
// Create a bitmap to store the previous screenshot
HBITMAP prevBitmap = CreateCompatibleBitmap(srcDC, screenWidth, screenHeight);
HDC prevDC = CreateCompatibleDC(srcDC);
SelectObject(prevDC, prevBitmap);
// Get the size of the bitmaps in bytes
BITMAP bmp;
GetObject(prevBitmap, sizeof(BITMAP), &bmp);
int prevBmpSize = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8);
GetObject(membit, sizeof(BITMAP), &bmp);
int currBmpSize = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8);
// Allocate memory for the bitmap data
char* prevBmpData = new char[prevBmpSize];
char* currBmpData = new char[currBmpSize];
// Get the raw pixel data of the bitmaps
GetBitmapBits(prevBitmap, prevBmpSize, prevBmpData);
GetBitmapBits(membit, currBmpSize, currBmpData);
// Compare the bitmap data
bool isDifferent = memcmp(prevBmpData, currBmpData, currBmpSize) != 0;
// Free the allocated memory
delete[] prevBmpData;
delete[] currBmpData;
// Check if the current screenshot is different from the previous screenshot
if (isDifferent)
{
// Screenshot is different, take a new screenshot
int newScreenWidth = 700;
int newScreenHeight = 500;
Gdiplus::Bitmap fullScreenBitmap(membit, NULL);
Gdiplus::Bitmap newBitmap(newScreenWidth, newScreenHeight);
Gdiplus::Graphics graphics(&newBitmap);
graphics.DrawImage(&fullScreenBitmap, 0, 0, newScreenWidth, newScreenHeight);
CLSID clsid;
GetEncoderClsid(L"image/jpeg", &clsid);
ULONG quality = 0;
EncoderParameters encoderParams;
encoderParams.Count = 1;
encoderParams.Parameter[0].Guid = EncoderQuality;
encoderParams.Parameter[0].Type = EncoderParameterValueTypeLong;
encoderParams.Parameter[0].NumberOfValues = 1;
encoderParams.Parameter[0].Value = &quality;
newBitmap.Save(iStream, &clsid, &encoderParams);
ULARGE_INTEGER pos{ 0, 0 };
const LARGE_INTEGER pos2{ 0, 0 };
iStream->Seek(pos2, STREAM_SEEK_SET, &pos);
ULONG bytesWritten = 0;
STATSTG statstg;
iStream->Stat(&statstg, STATFLAG_DEFAULT);
const ULONGLONG bufferLen = statstg.cbSize.QuadPart;
char* imageBuffer = new char[bufferLen];
iStream->Read(imageBuffer, bufferLen, &bytesWritten);
iStream->Release();
DeleteObject(memDC);
DeleteObject(membit);
::ReleaseDC(NULL, srcDC);
std::pair<ULONGLONG, char*> result(bufferLen, imageBuffer);
return result;
}
else
{
return {};
}
}
void sendScreens() {
if (clientsocket == INVALID_SOCKET) {
// handle error, the socket is not valid or not connected
return;
}
std::pair<ULONGLONG, char*> image = getScreenShotAsByteArray();
ULONGLONG bufferLen = image.first;
char* imageBuffer = image.second;
int bytesSent = send(clientsocket, imageBuffer, bufferLen, 0);
if(bytesSent < 0)
{
return;
}
std::cout << "Sent Bytes: " << bytesSent << "\n";
// wait for the desired interval before sending the next screenshot
std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
std::chrono::high_resolution_clock::time_point end = start + std::chrono::milliseconds(FPS);
std::this_thread::sleep_until(end);
}
I have successfully created a window but it wont update the window to change its shape and color. I have tried to solve this for days an appreciate any help.
The program is meant to show an orange window that can be reshaped. It would also help, if you could check the code by compiling it in Visual Studios.
Here is the code:
#include <Windows.h>
#include "Header.h"
bool running = true;
void *buffer_memory;
int buffer_width;
int buffer_hight;
BITMAPINFO buffer_bitmap_info;
LRESULT CALLBACK windows_callback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
switch (uMsg)
{
case WM_CLOSE:
case WM_DESTROY:
{
running = false;
}
break;
case WM_SIZE:
{
RECT rect;
GetClientRect(hwnd, &rect);
buffer_width = rect.left - rect.right;
buffer_hight = rect.bottom - rect.top;
int buffer_size = buffer_width *buffer_hight* sizeof(unsigned int);
if (buffer_memory) VirtualFree(buffer_memory, 0, MEM_RELEASE);
buffer_memory = VirtualAlloc(0, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
buffer_bitmap_info.bmiHeader.biSize = sizeof(buffer_bitmap_info.bmiHeader);
buffer_bitmap_info.bmiHeader.biWidth = buffer_width;
buffer_bitmap_info.bmiHeader.biHeight = buffer_hight;
buffer_bitmap_info.bmiHeader.biPlanes = 1;
buffer_bitmap_info.bmiHeader.biBitCount = 32;
buffer_bitmap_info.bmiHeader.biCompression = BI_RGB;
}
break;
default:
{
result = DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
return result;
}
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
//compile window
CHAR clsName[] = "test";
WNDCLASSA window_class = {};
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.lpszClassName = clsName;
window_class.lpfnWndProc = windows_callback;
//register clases
ATOM atom = RegisterClassA(&window_class);
if (0 == atom)
{
DWORD err = GetLastError();
return 1;
}
// create window
HWND window = CreateWindow(clsName, "game", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT,
CW_USEDEFAULT, 720, 360, 0, 0, hInstance, 0);
if (NULL == window)
{
DWORD err = GetLastError();
return 1;
}
while (running)
{
HDC hdc = GetDC(window);
// input
// simulate
MSG mesage;
while (PeekMessage(&mesage, window, 0, 0, PM_REMOVE))
{
TranslateMessage(&mesage);
DispatchMessage(&mesage);
}
unsigned int *pixel = (unsigned int *) buffer_memory;
for (int y = 0; y < buffer_hight; y++)
{
for (int x = 0; x < buffer_width; x++)
{
*pixel++ = 0xff5500;
}
}
// render
StretchDIBits(hdc, 0, 0, buffer_width, buffer_hight, 0, 0, buffer_width, buffer_hight,
buffer_memory, &buffer_bitmap_info, DIB_RGB_COLORS, SRCCOPY); {}
}
};
The line: buffer_width = rect.left - rect.right;
=> buffer_width = rect.right- rect.left ;
You should draw in the WM_PAINT message,
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
unsigned int* pixel = (unsigned int*)buffer_memory;
for (int y = 0; y < buffer_hight; y++)
{
for (int x = 0; x < buffer_width; x++)
{
*pixel++ = 0xff5500;
}
}
// render
StretchDIBits(hdc, 0, 0, buffer_width, buffer_hight, 0, 0, buffer_width, buffer_hight,
buffer_memory, &buffer_bitmap_info, DIB_RGB_COLORS, SRCCOPY); {}
EndPaint(hwnd, &ps);
}
break;
Updated:
#include <Windows.h>
//#include "Header.h"
bool running = true;
void* buffer_memory;
int buffer_width;
int buffer_hight;
BITMAPINFO buffer_bitmap_info;
LRESULT CALLBACK windows_callback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
switch (uMsg)
{
case WM_CLOSE:
case WM_DESTROY:
{
PostQuitMessage(0);
}
break;
case WM_SIZE:
{
RECT rect;
GetClientRect(hwnd, &rect);
buffer_width = rect.right - rect.left;
buffer_hight = rect.bottom - rect.top;
int buffer_size = buffer_width * buffer_hight * sizeof(unsigned int);
if (buffer_memory) VirtualFree(buffer_memory, 0, MEM_RELEASE);
buffer_memory = VirtualAlloc(0, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
buffer_bitmap_info.bmiHeader.biSize = sizeof(buffer_bitmap_info.bmiHeader);
buffer_bitmap_info.bmiHeader.biWidth = buffer_width;
buffer_bitmap_info.bmiHeader.biHeight = buffer_hight;
buffer_bitmap_info.bmiHeader.biPlanes = 1;
buffer_bitmap_info.bmiHeader.biBitCount = 32;
buffer_bitmap_info.bmiHeader.biCompression = BI_RGB;
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
unsigned int* pixel = (unsigned int*)buffer_memory;
for (int y = 0; y < buffer_hight; y++)
{
for (int x = 0; x < buffer_width; x++)
{
*pixel++ = 0xff5500;
}
}
// render
StretchDIBits(hdc, 0, 0, buffer_width, buffer_hight, 0, 0, buffer_width, buffer_hight,
buffer_memory, &buffer_bitmap_info, DIB_RGB_COLORS, SRCCOPY); {}
EndPaint(hwnd, &ps);
}
break;
default:
{
result = DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
return result;
}
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
//compile window
CHAR clsName[] = "test";
WNDCLASSA window_class = {};
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.lpszClassName = clsName;
window_class.lpfnWndProc = windows_callback;
//register clases
ATOM atom = RegisterClassA(&window_class);
if (0 == atom)
{
DWORD err = GetLastError();
return 1;
}
// create window
HWND window = CreateWindow(clsName, "game", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT,
CW_USEDEFAULT, 720, 360, 0, 0, hInstance, 0);
if (NULL == window)
{
DWORD err = GetLastError();
return 1;
}
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Here's my simple program:
#include "stdafx.h"
#include<Windows.h>
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void initBackBuffer(HWND hwnd);
HDC hBackDC = NULL;
HBITMAP hBackBitmap = NULL;
const int WIDTH = 512;
const int HEIGHT = 512;
DWORD screenBuffer[WIDTH * HEIGHT];
void draw(HWND hwnd) {
HDC hWinDC = GetDC(hwnd);
SetBitmapBits(hBackBitmap, HEIGHT * WIDTH * sizeof(DWORD), (const void*)(screenBuffer));
BitBlt(hWinDC, 0, 0, WIDTH, HEIGHT, hBackDC, 0, 0, SRCCOPY);
ReleaseDC(hwnd, hWinDC);
}
int WINAPI wWinMain(HINSTANCE hInstace, HINSTANCE hPrevInstace, LPWSTR lpCmdLine, int nCmdShow) {
memset(screenBuffer, 0, sizeof(screenBuffer));
MSG msg = { 0 };
WNDCLASS wnd = { 0 };
wnd.lpfnWndProc = WndProc;
wnd.hInstance = hInstace;
wnd.lpszClassName = L"Window";
if (!RegisterClass(&wnd)) {
return 0;
}
HWND hwnd = CreateWindowEx(NULL, wnd.lpszClassName, L"Window",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WIDTH, HEIGHT, NULL, NULL, hInstace, NULL);
if (!hwnd) {
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
for (int i = 0; i <= 512; i++) {
screenBuffer[i * WIDTH + 0] = 0x00FF0000;
}
while (true) {
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
draw(hwnd);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
switch (msg){
case WM_CREATE:
initBackBuffer(hwnd);
break;
case WM_DESTROY:
DeleteDC(hBackDC);
DeleteObject(hBackBitmap);
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void initBackBuffer(HWND hwnd) {
HDC hWinDC = GetDC(hwnd);
hBackDC = CreateCompatibleDC(hWinDC);
hBackBitmap = CreateCompatibleBitmap(hWinDC, WIDTH, HEIGHT);
SetBitmapBits(hBackBitmap, HEIGHT * WIDTH * sizeof(DWORD), (const void*)(screenBuffer));
SelectObject(hBackDC, hBackBitmap);
ReleaseDC(hwnd, hWinDC);
}
The output is as expected.
I moved
const int WIDTH = 512;
const int HEIGHT = 512;
DWORD screenBuffer[WIDTH * HEIGHT];
into Global.h and I added #include "Global.h" in my main file.
Main File :
#include "stdafx.h"
#include<Windows.h>
#include "Global.h"
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void initBackBuffer(HWND hwnd);
HDC hBackDC = NULL;
HBITMAP hBackBitmap = NULL;
void draw(HWND hwnd) {
HDC hWinDC = GetDC(hwnd);
SetBitmapBits(hBackBitmap, HEIGHT * WIDTH * sizeof(DWORD), (const void*)(screenBuffer));
BitBlt(hWinDC, 0, 0, WIDTH, HEIGHT, hBackDC, 0, 0, SRCCOPY);
ReleaseDC(hwnd, hWinDC);
}
int WINAPI wWinMain(HINSTANCE hInstace, HINSTANCE hPrevInstace, LPWSTR lpCmdLine, int nCmdShow) {
memset(screenBuffer, 0, sizeof(screenBuffer));
MSG msg = { 0 };
WNDCLASS wnd = { 0 };
wnd.lpfnWndProc = WndProc;
wnd.hInstance = hInstace;
wnd.lpszClassName = L"Window";
if (!RegisterClass(&wnd)) {
return 0;
}
HWND hwnd = CreateWindowEx(NULL, wnd.lpszClassName, L"Window",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WIDTH, HEIGHT, NULL, NULL, hInstace, NULL);
if (!hwnd) {
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
for (int i = 0; i <= 512; i++) {
screenBuffer[i * WIDTH + 0] = 0x00FF0000;
}
while (true) {
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
draw(hwnd);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
switch (msg){
case WM_CREATE:
initBackBuffer(hwnd);
break;
case WM_DESTROY:
DeleteDC(hBackDC);
DeleteObject(hBackBitmap);
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void initBackBuffer(HWND hwnd) {
HDC hWinDC = GetDC(hwnd);
hBackDC = CreateCompatibleDC(hWinDC);
hBackBitmap = CreateCompatibleBitmap(hWinDC, WIDTH, HEIGHT);
SetBitmapBits(hBackBitmap, HEIGHT * WIDTH * sizeof(DWORD), (const void*)(screenBuffer));
SelectObject(hBackDC, hBackBitmap);
ReleaseDC(hwnd, hWinDC);
}
Global.h
#pragma once
const int WIDTH = 512;
const int HEIGHT = 512;
DWORD screenBuffer[WIDTH * HEIGHT];
I get a erroneous white window.
I don't understand why this is happening because the compiler will anyway copy the contents of Global.h in to main file, so both variants should produce same results.
What is the cause of this problem ?
There is a bug here:
const int WIDTH = 512;
const int HEIGHT = 512;
DWORD screenBuffer[WIDTH * HEIGHT];
void foo()
{
for (int i = 0; i <= 512; i++) {
screenBuffer[i * WIDTH + 0] = 0x00FF0000;
}
}
This should be should be i < 512. Otherwise it overwrites a random memory location, this can result in an error in a different location, or no error if you are lucky. Debugger may report a nonsensical error, or no error at all. If screenBuffer was created on stack, debugger may give "heap corruption" error.
Consider using std::vector to avoid this problem in future.
vector<int> vec;
vec[vec.size()] = 0;//<- debug error
Side note: SetDIBitsToDevice or StretchDIBits will set bits directly:
void draw(HWND hwnd)
{
BITMAPINFO bi;
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biWidth = WIDTH;
bi.bmiHeader.biHeight = HEIGHT;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biCompression = BI_RGB;
HDC hdc = GetDC(hwnd);
SetDIBitsToDevice(hdc, 0, 0, WIDTH, HEIGHT, 0, 0, 0, HEIGHT, screenBuffer,
&bi, DIB_RGB_COLORS);
ReleaseDC(hwnd, hdc);
}
ATOM MyRegisterChildClass(void)
{
WNDCLASSEX wcex = {0};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpfnWndProc = ChildProc;
wcex.hInstance = hInst;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 8);
wcex.lpszClassName = ChildClassName;
return RegisterClassEx(&wcex);
}
static HFONT newFont;
static HWND hChild[9];
unsigned char k[9] = {0};
char text[] = {' ', 'X', '0'};
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int i;
static int sx, sy;
switch (message)
{
case WM_CREATE:
MyRegisterChildClass();
for(i = 0; i < 9; i++)
hChild[i] = CreateWindow(ChildClassName, NULL, WS_CHILD |
WS_DLGFRAME | WS_VISIBLE, 0, 0, 0, 0, hWnd, NULL, hInst, NULL);
break;
case WM_SIZE:
if(wParam == SIZE_MINIMIZED)
break;
sx = LOWORD(lParam)/3;
sy = HIWORD(lParam)/3;
if(newFont)
DeleteObject(newFont);
newFont = CreateFont(min(sx, sy), 0, 0, 0, FW_NORMAL, 0, 0, 0,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Arial");
for(i = 0; i < 9; i++)
{
MoveWindow(hChild[i], (i%3)*sx, (i/3)*sy, sx, sy, TRUE);
UpdateWindow(hChild[i]);
}
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_NEW:
for(i = 0; i < 9; i++)
{
k[i] = 0;
InvalidateRect(hChild[i], NULL, TRUE);
UpdateWindow(hChild[i]);
}
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK ChildProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hDC;
RECT rt;
int s, i;
char *ch;
switch(message)
{
case WM_LBUTTONDOWN:
for(i = 0; hWnd != hChild[i]; i++)
;
if(k[i])
break;
else
k[i] = 1;
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
srand(lParam);
for(i = s = 0; i < 9; i++)
if(k[i])
s++;
if(s == 9)
MessageBox(hWnd, L"...",L"...", MB_OK|MB_ICONQUESTION);
else
{
while(true)
{
s = rand()*9/(RAND_MAX+1);
if(k[s])
continue;
k[s] = 2;
InvalidateRect(hChild[s], NULL, TRUE);
UpdateWindow(hChild[s]);
break;
}
}
break;
case WM_PAINT:
for(i = 0; hWnd != hChild[i]; i++);
if(k[i])
{
ch = text+k[i];
hDC = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rt);
SelectObject(hDC, newFont);
DrawTextA(hDC, ch, 1, &rt, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hWnd, &ps);
}
else
DefWindowProc(hWnd, message, wParam, lParam);
break;
default:
DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
I try to create a Tic-Tac-Toe game. Here is the code that I've written by hand (no standard code generated by Visual Studio included). I made 9 child windows. This code runs, but it doesn't show child windows and doesn't react when I push left mouse button. With the help of debugger I saw that messages WM_LBUTTONDOWN and WM_PAINT are never sent to ChildProc function.
What's wrong?
for(i = 0; i < 9; i++)
hChild[i] = CreateWindow(ChildClassName, NULL, WS_CHILD |
WS_DLGFRAME | WS_VISIBLE, 0, 0, 0, 0, hWnd, NULL, hInst, NULL);
You are making a traditional mistake, you are blindly hoping that winapi functions will succeed. That's idle hope, CreateWindow() fails and returns NULL. Something you could also see with the debugger, you'll see the hChild array contain nothing but nulls. Always write defensive code, at the absolute minimum use assert() to backup your assumptions:
for(i = 0; i < 9; i++) {
hChild[i] = CreateWindow(ChildClassName, ...);
assert(hChild[i]);
}
Lots of other places in your code where you should do this. You'll now have to trouble diagnosing failure.
The actual bug is located in ChildProc():
default:
DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
Or in other words, it always return 0. That's not okay, you should only return that value when you processed the message. You didn't process every message. Again use the debugger and set a breakpoint on the switch() statement in ChildProc. You'll see the first message being improperly handled, WM_NCCREATE. Which requires you to return TRUE to continue creation of the window. You don't, you return FALSE. So that's a quick end to the window getting created. You must return the value of DefWindowProc(). Fix:
default:
return DefWindowProc(hWnd, message, wParam, lParam);