How to properly close/terminate a simple Windows app - c++

Having such a simple DirectX application:
#include <d3dx9.h>
#include <tchar.h>
#include <chrono>
#include "Utils.h"
//#pragma comment (lib, "d3d9.lib")
//#pragma comment (lib, "d3dx9.lib")
using namespace std::chrono_literals;
using namespace std::chrono;
const wchar_t g_szClassName[] = _T("myWindowClass");
// we use a fixed timestep of 1 / (60 fps) = 16 milliseconds
//constexpr std::chrono::nanoseconds timestep(16ms);
constexpr std::chrono::nanoseconds timestep(1000ms);
// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
wchar_t msgbuf[100];
int loop_cnt = 0;
bool handle_events() {
// poll for events
return false; // true if the user wants to quit the game
}
void update(long long lag) {
// update game logic here
swprintf_s(msgbuf, _T("update - loop_cnt: %d\n"), loop_cnt++);
OutputDebugString(msgbuf);
}
void render() {
// render stuff here
}
VOID OnPaint(HDC hdc) {
}
// ------------ D3D ------------
IDirect3D9* pD3D;
IDirect3DDevice9* pDev;
IDirect3DVertexBuffer9* pVB;
const DWORD OURVERT_FVF = D3DFVF_XYZRHW | D3DFVF_DIFFUSE;
struct OurVertex {
float x, y, z; // pozycja
float rhw; // komponent rhw
D3DCOLOR color; // kolor
};
// -----------------------------
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) {
using clock = std::chrono::high_resolution_clock;
WNDCLASSEX wc;
HWND hWnd;
MSG Msg = {0};
std::chrono::nanoseconds lag(0ns);
auto start_time = clock::now();
bool quit_game = false;
//Step 1: Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc)) {
MessageBox(NULL, _T("Window Registration Failed!"), _T("Error!"), MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Step 2: Creating the Window
hWnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
_T("The title of my window"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
480, // initial x size
640, // initial y size
NULL,
NULL,
hInstance,
NULL
);
OutputDebugString(_T("--> WinMain <-- \n"));
if (hWnd == NULL) {
MessageBox(NULL, _T("Window Creation Failed!"), _T("Error!"), MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
// ------------ D3D ------------
pD3D = Direct3DCreate9(D3D_SDK_VERSION);
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.Windowed = true;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
OurVertex verts[] = {
{ 20.0f, 20.0f, 0.5f, 1.0f, 0xffff0000, },
{ 100.0f, 20.0f, 0.5f, 1.0f, 0xff00ff00, },
{ 20.0f, 100.0f, 0.5f, 1.0f, 0xff00ff55, },
{ 100.0f, 100.0f, 0.5f, 1.0f, 0xff0000ff},
};
pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &pDev);
HRESULT hr = pDev->CreateVertexBuffer(4*sizeof(OurVertex), D3DUSAGE_DYNAMIC, OURVERT_FVF, D3DPOOL_DEFAULT, &pVB, 0);
check_HRESULT(_T("CreateVertexBuffer"), hr);
void* data;
pVB->Lock(0, 4*sizeof(OurVertex), &data, D3DLOCK_DISCARD);
memcpy(data, (void*)verts, sizeof(verts));
pVB->Unlock();
// -----------------------------
// Step 3: The Message Loop
while (TRUE) {
while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE) > 0) {
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
if (Msg.message == WM_QUIT)
break;
// Run game logic
auto current_time = clock::now();
auto frame_time = current_time - start_time;
start_time = current_time;
lag += duration_cast<nanoseconds>(frame_time);
// update game logic as lag permits
while (lag >= timestep) {
lag -= timestep;
pDev->Clear(0, 0, D3DCLEAR_TARGET, 0xff000000, 1, 0);
pDev->BeginScene();
pDev->SetFVF(OURVERT_FVF);
pDev->SetStreamSource(0, pVB, 0, sizeof(OurVertex));
pDev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
pDev->EndScene();
pDev->Present(0, 0, 0, 0);
update(lag.count()); // update at a fixed rate each time
}
}
OutputDebugString(_T("--> AFTER MSG LOOP END <-- \n"));
return Msg.wParam;
}
// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
When i run debug (without any brakpoints defined) i get the output:
--> WinMain <--
update - loop_cnt: 0
update - loop_cnt: 1
update - loop_cnt: 2
update - loop_cnt: 3
update - loop_cnt: 4
update - loop_cnt: 5
...
The program successfully displays a colored rectangle as expected. But, something is going wrong here, because when I click the standard "X" close button in the right upper corner of the app window, the window closes but the program seems not to end properly. I still receive update - loop_cnt: # messages in the Output window in my Visual Studio, and there is no --> AFTER MSG LOOP END <-- message suggesting proper program termination.
What's more, when I try to run debug again, I get an error:
LINK : fatal error LNK1168: cannot open ...Game_Loop_Windows_1.exe for writing
...
MSB6006: "link.exe" exited with code 1168.
1>Done building project "Game_Loop_Windows_1.vcxproj" -- FAILED
What can be wrong with the code?
PS. I'm using Microsoft Visual Studio Community 2019 on Windows 10.

The message loop likely never terminates.
while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE) > 0) {
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
if (Msg.message == WM_QUIT)
break;
You are handling all messages, but only check that last one whether it was a WM_QUIT message. Instead, you would need to check all messages and properly exit the loop, something like this:
bool running{true};
while (running) {
while (running && PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE) > 0) {
TranslateMessage(&Msg);
DispatchMessage(&Msg);
running = Msg.message != WM_QUIT;
}
// Not strictly required; you can run another render loop and have
// the outermost loop exit when running is false
if (!running)
break;
The link error simply means that the process is still running. An executable image that's mapped into a process cannot be written to. That's what the linker is complaining about.

Related

Getting an error, namely "unresolved external symbol"

I have just started learning DirectX, and I am trying to implement it with the use of the C++ language. I have started reading a book about this problem, named Beginning DirectX 10 Game Programming. Nevermind, I am running in to a problem. I have added the needed libraries and have included the necessary files with Visual Studio. The error is as follow:
Error 26 error LNK2019: unresolved external symbol "bool __cdecl InitDirect3D(struct HWND__ *,int,int)" (?InitDirect3D##YA_NPAUHWND__##HH#Z) referenced in function _WinMain#16 C:\Users\Robert\documents\visual studio 2013\Projects\DirectXBook\exampleWithDirectX1\SourceCode.obj exampleWithDirectX1
Error 27 error LNK1120: 1 unresolved externals C:\Users\Robert\documents\visual studio 2013\Projects\DirectXBook\Debug\exampleWithDirectX1.exe 1 1 exampleWithDirectX1
#include <Windows.h>
#include <tchar.h>
#include <d3d10.h>
#include <d3dx10.h>
#pragma comment (lib, "d3dx10.lib")
#pragma comment (lib, "d3d10.lib")
HINSTANCE hInstance; //Instanszkezelo
HWND hWnd; //Ablakkezelo
ID3D10Device *pD3DDevice = NULL;
IDXGISwapChain *pSwapChain = NULL;
ID3D10RenderTargetView *pRenderTargetView = NULL;
int width = 640;
int height = 480;
//fuggveny prototipusok definialasa
bool InitWindow(HINSTANCE hInstance, int width, int height);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
bool InitDirect3D(HWND hWnd, int width, int height);
void Render();
void shutDownDirect3D();
//WinMain, belepopont a windows applikacioba
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdline, int nCmdShow){
//ablakinicalizalas
if (!InitWindow(hInstance, width, height)){
return false;
}
if (!InitDirect3D(hWnd, width, height)){
return 0;
}
//main message loop
MSG msg = { 0 };
while (WM_QUIT != msg.message){
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == TRUE){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//renderelest kezelo fuggveny meghivasa
Render();
}
shutDownDirect3D();
return msg.wParam;
}
bool InitWindow(HINSTANCE hInstance, int width, int height){
WNDCLASSEX wcex;
// WNDCLASSEX wcex objektum kitoltese
// meghatarozza, hogyan fog kinezni az ablakunk
wcex.cbSize = sizeof(WNDCLASSEX); // struktura merete
wcex.style = CS_HREDRAW | CS_VREDRAW; // az osztaly tipusa
wcex.lpfnWndProc = (WNDPROC)WndProc; // az ablak procedure visszahivas (callback)
wcex.cbClsExtra = 0; // extra byteok lefoglasasa az osztalynak
wcex.cbWndExtra = 0; // extra bytes to allocate for this instance
wcex.hInstance = hInstance; // kezelo az applikacios esemenynek
wcex.hIcon = 0; // applikaciohoz hozzarendelendo ikon
wcex.hCursor = LoadCursor(NULL, IDC_ARROW); // kurzor hasznalatanak definialasa
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // hatterszin
wcex.lpszMenuName = NULL; // eroforrsanev a menunek
wcex.lpszClassName = TEXT("DirectXExample"); // az osztalynev letrehozva
wcex.hIconSm = 0; // kezelo a kisikonhoz
RegisterClassEx(&wcex); //wcex objektum regisztralasa
RECT rect = { 0, 0, width, height }; // ablakatmeretezes
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
// ablak letrehozasa a felso osztalybol
hWnd = CreateWindow(TEXT("DirectXExample"),
TEXT("DirectXExample"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
rect.right - rect.left,
rect.bottom - rect.top,
NULL,
NULL,
hInstance,
NULL);
if (!hWnd){
return false;
}
// ablak kirajzoalsa a kijelzore
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
return true;
}
//WPARAM == typedef UINT_PTR WPARAM passing and returning polymorphic values
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
//megnézi, hogy van - e üzenet a sorban
switch (message){
case WM_KEYDOWN:
switch (wParam){
//ha a felhasznalo az escape billentyu megnyomja kilepes
case VK_ESCAPE:
PostQuitMessage(0);
break;
}
//ha a felhasznalo az X gombra kattint kilepes
case WM_DESTROY:
PostQuitMessage(0);
break;
}
//uzenet elkuldese az alapveto ablak proceduranak, hogy az tovabb elemezze
return DefWindowProc(hWnd, message, wParam, lParam);
}
bool InitDirect3d(HWND hWnd, int width, int height){
// Create and clear the DXGI_SWAP_CHAIN_DESC structure
DXGI_SWAP_CHAIN_DESC swapChainDesc;
ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));
// Fill in the needed values
swapChainDesc.BufferCount = 1;
swapChainDesc.BufferDesc.Width = width;
swapChainDesc.BufferDesc.Height = height;
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.OutputWindow = hWnd;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.Windowed = TRUE;
// Create the D3D device and the swap chain
HRESULT hr = D3D10CreateDeviceAndSwapChain(NULL,
D3D10_DRIVER_TYPE_REFERENCE,
NULL,
0,
D3D10_SDK_VERSION,
&swapChainDesc,
&pSwapChain,
&pD3DDevice);
// Error checking. Make sure the device was created
if (hr != S_OK){
return false;
}
// Get the back buffer from the swapchain
ID3D10Texture2D *pBackBuffer;
hr = pSwapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), (LPVOID*)
&pBackBuffer);
if (hr != S_OK){
return false;
}
// create the render target view
hr = pD3DDevice->CreateRenderTargetView(pBackBuffer, NULL, &pRenderTargetView);
// release the back buffer
pBackBuffer->Release();
// Make sure the render target view was created successfully
if (hr != S_OK){
return false;
}
// set the render target
pD3DDevice->OMSetRenderTargets(1, &pRenderTargetView, NULL);
// create and set the viewport
D3D10_VIEWPORT viewPort;
viewPort.Width = width;
viewPort.Height = height;
viewPort.MinDepth = 0.0f;
viewPort.MaxDepth = 1.0f;
viewPort.TopLeftX = 0;
viewPort.TopLeftY = 0;
pD3DDevice->RSSetViewports(1, &viewPort);
return true;
}
void Render(){
if (pD3DDevice != NULL){
//a celpuffer tisztitasa
pD3DDevice->ClearRenderTargetView(pRenderTargetView, D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f));
//IDE JON A RAJZOLAS
//a kovetkezo elem kijelzese a SwapChain - bol
pSwapChain->Present(0, 0);
}
}
void shutDownDirect3D(){
//release the rendertarget
if (pRenderTargetView){
pRenderTargetView->Release();
}
if (pSwapChain){
pSwapChain->Release();
}
if (pD3DDevice){
pD3DDevice->Release();
}
}
Thanks in advance!
And where is this function
bool InitDirect3D(HWND hWnd, int width, int height);
defined?
You defined it as InitDirect3d instead of InitDirect3D that is instead of 'd' you should write 'D'

Getting Rid of Black Console Window When Running C++ Application

I am using Netbeans 7.1 to toy around with the AI tutorial I found here.
edit: I am using the GCC compiler.
I've gotten everything working, but I can't seem to get the application to compile and run with the Windows Subsystem... The application appears to be written properly for Windows API, and the executable that came with the source files from that website launches without producing the black console window that my own executable creates.
I've tried adding -mwindows as an option to the linker, and I've tried -Wl,-subsystem,windows. Neither of these have worked for me. I've provided the main.cpp below.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdlib.h>
#include <time.h>
#include "utils.h"
#include "CController.h"
#include "CTimer.h"
#include "resource.h"
#include "CParams.h"
// edited this out, still not working
// #pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
///////////////////////GLOBALS ////////////////////////////////////
char* szApplicationName = "Smart Sweepers v1.0";
char* szWindowClassName = "sweeper";
//The controller class for this simulation
CController* g_pController = NULL;
//create an instance of the parameter class.
CParams g_Params;
//---------------------------- Cleanup ----------------------------------
//
// simply cleans up any memory issues when the application exits
//-----------------------------------------------------------------------
void Cleanup()
{
if (g_pController)
delete g_pController;
}
//-----------------------------------WinProc-----------------------------
//
//-----------------------------------------------------------------------
LRESULT CALLBACK WindowProc(HWND hwnd,
UINT msg,
WPARAM wparam,
LPARAM lparam)
{
//these hold the dimensions of the client window area
static int cxClient, cyClient;
//used to create the back buffer
static HDC hdcBackBuffer;
static HBITMAP hBitmap;
static HBITMAP hOldBitmap;
switch(msg)
{
case WM_CREATE:
{
//seed the random number generator
srand((unsigned) time(NULL));
//get the size of the client window
RECT rect;
GetClientRect(hwnd, &rect);
cxClient = rect.right;
cyClient = rect.bottom;
//setup the controller
g_pController = new CController(hwnd);
//create a surface for us to render to(backbuffer)
hdcBackBuffer = CreateCompatibleDC(NULL);
HDC hdc = GetDC(hwnd);
hBitmap = CreateCompatibleBitmap(hdc,
cxClient,
cyClient);
ReleaseDC(hwnd, hdc);
hOldBitmap = (HBITMAP)SelectObject(hdcBackBuffer, hBitmap);
}
break;
//check key press messages
case WM_KEYUP:
{
switch(wparam)
{
case VK_ESCAPE:
{
PostQuitMessage(0);
}
break;
case 'F':
{
g_pController->FastRenderToggle();
}
break;
//reset the demo
case 'R':
{
if (g_pController)
{
delete g_pController;
}
//setup the new controller
g_pController = new CController(hwnd);
}
break;
}//end WM_KEYUP switch
}
break;
//has the user resized the client area?
case WM_SIZE:
{
cxClient = LOWORD(lparam);
cyClient = HIWORD(lparam);
//resize the backbuffer accordingly
SelectObject(hdcBackBuffer, hOldBitmap);
HDC hdc = GetDC(hwnd);
hBitmap = CreateCompatibleBitmap(hdc,
cxClient,
cyClient);
ReleaseDC(hwnd, hdc);
hOldBitmap = (HBITMAP)SelectObject(hdcBackBuffer, hBitmap);
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
//fill our backbuffer with white
BitBlt(hdcBackBuffer,
0,
0,
cxClient,
cyClient,
NULL,
NULL,
NULL,
WHITENESS);
//render the mines and sweepers
g_pController->Render(hdcBackBuffer);
//now blit backbuffer to front
BitBlt(ps.hdc, 0, 0, cxClient, cyClient, hdcBackBuffer, 0, 0, SRCCOPY);
EndPaint(hwnd, &ps);
}
break;
case WM_DESTROY:
{
SelectObject(hdcBackBuffer, hOldBitmap);
//clean up our backbuffer objects
DeleteDC(hdcBackBuffer);
DeleteObject(hBitmap);
// kill the application, this sends a WM_QUIT message
PostQuitMessage(0);
}
break;
default:break;
}//end switch
// default msg handler
return (DefWindowProc(hwnd, msg, wparam, lparam));
}//end WinProc
//-----------------------------------WinMain-----------------------------------------
// Entry point for our windows application
//-----------------------------------------------------------------------------------
int WINAPI WinMain( HINSTANCE hinstance,
HINSTANCE hprevinstance,
LPSTR lpcmdline,
int ncmdshow)
{
WNDCLASSEX winclass;
HWND hwnd;
MSG msg;
// first fill in the window class stucture
winclass.cbSize = sizeof(WNDCLASSEX);
winclass.style = CS_HREDRAW | CS_VREDRAW;
winclass.lpfnWndProc = WindowProc;
winclass.cbClsExtra = 0;
winclass.cbWndExtra = 0;
winclass.hInstance = hinstance;
winclass.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(IDI_ICON1));
winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
winclass.hbrBackground= NULL;
winclass.lpszMenuName = NULL;
winclass.lpszClassName= szWindowClassName;
winclass.hIconSm = LoadIcon(hinstance, MAKEINTRESOURCE(IDI_ICON1));
// register the window class
if (!RegisterClassEx(&winclass))
{
MessageBox(NULL, "Error Registering Class!", "Error", 0);
return 0;
}
// create the window (one that cannot be resized)
if (!(hwnd = CreateWindowEx(NULL,
szWindowClassName,
szApplicationName,
WS_OVERLAPPED | WS_VISIBLE | WS_CAPTION | WS_SYSMENU,
GetSystemMetrics(SM_CXSCREEN)/2 - CParams::WindowWidth/2,
GetSystemMetrics(SM_CYSCREEN)/2 - CParams::WindowHeight/2,
CParams::WindowWidth,
CParams::WindowHeight,
NULL,
NULL,
hinstance,
NULL)))
{
MessageBox(NULL, "Error Creating Window!", "Error", 0);
return 0;
}
//Show the window
ShowWindow(hwnd, SW_SHOWDEFAULT );
UpdateWindow(hwnd);
//create a timer
CTimer timer(CParams::iFramesPerSecond);
//start the timer
timer.Start();
// Enter the message loop
bool bDone = FALSE;
while(!bDone)
{
while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
if( msg.message == WM_QUIT )
{
//Stop loop if it's a quit message
bDone = TRUE;
}
else
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
if (timer.ReadyForNextFrame() || g_pController->FastRender())
{
if(!g_pController->Update())
{
//we have a problem, end app
bDone = TRUE;
}
//this will call WM_PAINT which will render our scene
InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
}
}//end while
// Clean up everything and exit the app
Cleanup();
UnregisterClass( szWindowClassName, winclass.hInstance );
return 0;
} // end WinMain
This seems a bit strange, but windows subsystem application uses WinMainCRTStartup as entry point. So the following line looks inconsistent:
#pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
It probably should be "/SUBSYSTEM:windows /ENTRY:WinMainCRTStartup" or "/SUBSYSTEM:console /ENTRY:mainCRTStartup"
On the other hand, I never trried to make a windows app with gcc. It may completely ignore this #pragma... Anyway, try to comment it out and see what happens. Generally compiler should be able to select the proper entry point without the compile time parameter anyway.

direct3d initialization failure / c++

this is the code that creates a simple window and initializes a simple direct3d device. but every time the program reaches the render() function application terminates. i have no idea why that happens. can somebody explain me this weird behavior? thank you!!
//====================================================================================================
#include <windows.h>
#include <d3d9.h>
//====================================================================================================
HINSTANCE hInst;
HWND wndHandle;
//====================================================================================================
LPDIRECT3D9 pD3D; // the Direct3D object
LPDIRECT3DDEVICE9 pd3dDevice; // the Direct3D device
//====================================================================================================
bool initWindow(HINSTANCE hInstance);
bool initDirect3D(void);
void cleanUp (void);
void render(void);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
//====================================================================================================
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
if (!initWindow(hInstance)) return false;
if (!initDirect3D()) return false;
MSG msg;
ZeroMemory(&msg, sizeof(msg));
if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
} else {
render(); // i think this is the problem ...
}
return static_cast<int>(msg.wParam);
}
bool initWindow(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(0, IDI_APPLICATION);
wcex.hCursor = LoadCursor(0, IDC_ARROW);
wcex.hbrBackground = static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
wcex.lpszMenuName = 0L;
wcex.lpszClassName = L"DirectXTemplate";
wcex.hIconSm = 0;
RegisterClassEx(&wcex);
wndHandle = CreateWindow(L"DirectXTemplate", L"DirectX Template", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, hInstance, NULL);
if (!wndHandle) return false;
ShowWindow(wndHandle, SW_SHOW);
UpdateWindow(wndHandle);
return true;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
bool initDirect3D(void)
{
pD3D = NULL;
pd3dDevice = NULL;
// create the DirectX object
if(NULL == (pD3D = Direct3DCreate9(D3D_SDK_VERSION))) return false;
// fill the presentation parameters structure
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.BackBufferCount = 1;
d3dpp.BackBufferHeight = 480;
d3dpp.BackBufferWidth = 640;
d3dpp.hDeviceWindow = wndHandle;
// create a default DirectX device
if (FAILED(pD3D -> CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, wndHandle, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &pd3dDevice))) return false;
return true;
}
void render(void)
{
// Check to make sure you have a valid Direct3D device
if(NULL == pd3dDevice) return; // clear the back buffer to a blue color
pd3dDevice -> Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 255), 1.0f, 0);
// Present the back buffer contents to the display
pd3dDevice -> Present(NULL, NULL, NULL, NULL);
}
void cleanUp (void)
{
// Release the device and the Direct3D object
if (pd3dDevice != NULL) pd3dDevice -> Release();
if (pD3D != NULL) pD3D -> Release();
}
#DuckMaestro is right. Your program is going through the msg/render process once and is then ending. It should only end if the msg is to exit the program. Try putting in a loop like this:
while(msg.message!=WM_QUIT){
if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
} else {
render(); // i think this is the problem ...
}
}
Your...
if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
} else {
render(); // i think this is the problem ...
}
...needs to be in a while loop, no? Step through your code with a debugger, statement by statement. Win32 applications need a while loop to stay alive, so-to-speak.

Multithreading OpenGL in a child window

I'm trying to build an OpenGL application that is responsive even while the main window is being resized or moved. The most logical solution that I have found is to create a child window and a Message Pump in separate thread which renders the OpenGL. It can resize itself in between frames as necessary. The primary message pump and window frame runs in the main process.
It works great to a point. The window can be moved, menus used and resized without affecting the frame rate of the child window. SwapBuffers() is where it all falls apart.
SwapBuffers() seems to be running in software mode when it is run in this manner. It no longer holds at 60 FPS to match my monitor's VSync, it jumps into the hundreds when the window is around 100x100 and drops to 20 FPS when maximized to 1920x1080. When running in a single thread, everything seems normal.
There were a few issues that I resolved. Like when messages need to pass between parent and child it stalls the entire application. Overriding WM_SETCURSOR and setting WS_EX_NOPARENTNOTIFY resolved those. It still occasionally stutters when I click.
I'm hoping that I'm just not doing something properly and that someone experienced with OpenGL can help me out. Something to do with my initialization or cleanup may be off since this interferes with other OpenGL applications running on my PC even after I close it.
Here is a simplified test case that exhibits the issues that I'm experiencing. It should compile in about any modern Visual Studio.
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <wchar.h>
#pragma comment(lib, "opengl32.lib")
typedef signed int s32;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef float f32;
typedef double f64;
bool run = true;
// Window procedure for the main application window
LRESULT CALLBACK AppWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_DESTROY && (GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_APPWINDOW))
PostQuitMessage(0);
return DefWindowProc(hWnd, msg, wParam, lParam);
}
// Window procedure for the OpenGL rendering window
LRESULT CALLBACK RenderWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_SETCURSOR)
{
SetCursor(LoadCursor(NULL, IDC_CROSS));
return TRUE;
}
if (msg == WM_SIZE)
glViewport(0, 0, LOWORD(lParam)-2, HIWORD(lParam)-2);
return AppWindowProc(hWnd, msg, wParam, lParam);
}
int WINAPI ThreadMain(HWND parent)
{
HINSTANCE hInstance = GetModuleHandle(0);
// Depending on if this is running as a child or a overlap window, set up the window styles
UINT ClassStyle, Style, ExStyle;
if (parent)
{
ClassStyle = 0;
Style = WS_CHILD;
ExStyle = WS_EX_NOPARENTNOTIFY;
} else {
ClassStyle = 0 | CS_VREDRAW | CS_HREDRAW;
Style = WS_OVERLAPPEDWINDOW;
ExStyle = WS_EX_APPWINDOW;
}
// Create the child window class
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = ClassStyle;
wc.hInstance = hInstance;
wc.lpfnWndProc = RenderWindowProc;
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.lpszClassName = L"OGLChild";
ATOM ClassAtom = RegisterClassEx(&wc);
// Create the child window
RECT r = {0, 0, 640, 480};
if (parent)
GetClientRect(parent, &r);
HWND WindowHandle = CreateWindowExW(ExStyle, (LPCTSTR)MAKELONG(ClassAtom, 0), 0, Style,
0, 0, r.right, r.bottom, parent, 0, hInstance, 0);
// Initialize OpenGL render context
PIXELFORMATDESCRIPTOR pfd = {0};
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 16;
pfd.iLayerType = PFD_MAIN_PLANE;
HDC DeviceContext = GetDC(WindowHandle);
int format = ChoosePixelFormat(DeviceContext, &pfd);
SetPixelFormat(DeviceContext, format, &pfd);
HGLRC RenderContext = wglCreateContext(DeviceContext);
wglMakeCurrent(DeviceContext, RenderContext);
ShowWindow(WindowHandle, SW_SHOW);
GetClientRect(WindowHandle, &r);
glViewport(0, 0, r.right, r.bottom);
// Set up an accurate clock
u64 start, now, last, frequency;
QueryPerformanceFrequency((LARGE_INTEGER*)&frequency);
QueryPerformanceCounter((LARGE_INTEGER*)&now);
start = last = now;
u32 frames = 0; // total frames this second
f64 nextFrameCount = 0; // next FPS update
f32 left = 0; // line position
MSG msg;
while (run)
{
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if (msg.message == WM_QUIT || msg.message == WM_DESTROY)
run = false;
}
// Update the clock
QueryPerformanceCounter((LARGE_INTEGER*)&now);
f64 clock = (f64)(now - start) / frequency;
f64 delta = (f64)(now - last) / frequency;
last = now;
// Render a line moving
glOrtho(0, 640, 480, 0, -1, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glColor3f(1.0f, 1.0f, 0.0f);
left += (f32)(delta * 320.0f);
if (left > 640.0f)
left = 0;
glBegin(GL_LINES);
glVertex2f(0.0f, 0.0f);
glVertex2f(left, 480.0f);
glEnd();
SwapBuffers(DeviceContext);
// Resize as necessary
if (parent)
{
RECT pr, cr;
GetClientRect(parent, &pr);
GetClientRect(WindowHandle, &cr);
if (pr.right != cr.right || pr.bottom != cr.bottom)
MoveWindow(WindowHandle, 0, 0, pr.right, pr.bottom, FALSE);
}
// Update FPS counter
frames++;
if (clock > nextFrameCount)
{
WCHAR title[16] = {0};
_snwprintf_s(title, 16, 16, L"FPS: %u", frames);
SetWindowText(parent ? parent : WindowHandle, title);
nextFrameCount = clock + 1;
frames = 0;
}
Sleep(1);
}
// Cleanup OpenGL context
wglDeleteContext(RenderContext);
wglMakeCurrent(0,0);
ReleaseDC(WindowHandle, DeviceContext);
DestroyWindow(WindowHandle);
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
int result = MessageBox(0, L"Would you like to run in threaded child mode?",
L"Threaded OpenGL Demo", MB_YESNOCANCEL | MB_ICONQUESTION);
if (result == IDNO)
return ThreadMain(0);
else if (result != IDYES)
return 0;
// Create the parent window class
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.hInstance = hInstance;
wc.lpfnWndProc = (WNDPROC)AppWindowProc;
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.lpszClassName = L"OGLFrame";
ATOM ClassAtom = RegisterClassEx(&wc);
// Create the parent window
HWND WindowHandle = CreateWindowExW(WS_EX_APPWINDOW, (LPCTSTR)MAKELONG(ClassAtom, 0),
0, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
0, 0, 640, 480, 0, 0, hInstance, 0);
ShowWindow(WindowHandle, SW_SHOW);
// Start the child thread
HANDLE thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadMain, (LPVOID)WindowHandle, 0, 0);
MSG msg;
while (run)
{
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if (msg.message == WM_QUIT)
run = false;
}
Sleep(100);
}
DestroyWindow(WindowHandle);
// Wait for the child thread to finish
WaitForSingleObject(thread, INFINITE);
ExitProcess(0);
return 0;
}
I've been running this on a NVIDIA GeForce 8800 GTX, but I'd hope that any solution will work equally on any modern card.
I have tried other methods, such as a thread rendering into a window in the main process but during resizing I've gotten artifacting and even a couple blue screens. My application will be cross platform so DirectX isn't an option.
The issue turned out to be that prolonged debugging of OpenGL applications in Visual Studio can cause OpenGL to start failing to create contexts. Since I didn't trap for any errors, I never realized that it was running without hardware acceleration. It required a full reboot to recover and now works fine.
Also, replacing the pump WinMain with this fixes any issues with resizing:
MSG msg;
while (GetMessage(&msg, 0, 0, 0) != 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
run = false;

Displaying mesh using DirectX 9

#include <windows.h>
#include <d3d9.h>
#include <D3DX9Mesh.h>
#define THROW_ERROR_AND_EXIT(x) { \
MessageBox(0,x,0,0); \
return -1; \
}
LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
// Handle close event
switch( msg )
{
case WM_DESTROY:
PostQuitMessage( 0 );
return 0;
}
return DefWindowProc( hWnd, msg, wParam, lParam );
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// Registering class
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style= CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc= (WNDPROC)WndProc;
wcex.cbClsExtra= 0;
wcex.cbWndExtra= 0;
wcex.hInstance= hInstance;
wcex.hIcon= 0;
wcex.hCursor= LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground= (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName= 0;
wcex.lpszClassName= "MyMeshViewer";
wcex.hIconSm= 0;
RegisterClassEx(&wcex);
// Creating Window
HWND hWnd = CreateWindow("MyMeshViewer", "MyMeshViewer", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
// Creating Direct3D object
LPDIRECT3D9 d3dObject=NULL;
LPDIRECT3DDEVICE9 d3dDevice=NULL;
d3dObject=Direct3DCreate9(D3D_SDK_VERSION);
// Creating Direct3D device
if(NULL == d3dObject)
THROW_ERROR_AND_EXIT("NULL == d3dObject");
D3DPRESENT_PARAMETERS presParams;
ZeroMemory(&presParams,sizeof(presParams));
presParams.Windowed=TRUE;
presParams.SwapEffect=D3DSWAPEFFECT_DISCARD;
presParams.BackBufferFormat=D3DFMT_UNKNOWN;
presParams.PresentationInterval=D3DPRESENT_INTERVAL_ONE;
HRESULT hr=d3dObject->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &presParams, &d3dDevice);
if(FAILED(hr))
THROW_ERROR_AND_EXIT("d3dObject->CreateDevice");
// Rendering
d3dDevice->Clear(0,NULL,D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,255,0),1.0f,0);
d3dDevice->BeginScene();
// Loading the mesh
LPD3DXBUFFER materialBuffer = NULL;
DWORD numMaterials = 0;
LPD3DXMESH mesh = NULL;
hr=D3DXLoadMeshFromX("tiger.x", D3DXMESH_SYSTEMMEM,
d3dDevice, NULL,
&materialBuffer,NULL, &numMaterials,
&mesh );
if(FAILED(hr))
THROW_ERROR_AND_EXIT("hr=D3DXLoadMeshFromX");
// Loading the material buffer
D3DXMATERIAL* d3dxMaterials = (D3DXMATERIAL*)materialBuffer->GetBufferPointer();
// Holding material and texture pointers
D3DMATERIAL9 *meshMaterials = new D3DMATERIAL9[numMaterials];
LPDIRECT3DTEXTURE9 *meshTextures = new LPDIRECT3DTEXTURE9[numMaterials];
// Filling material and texture arrays
for (DWORD i=0; i<numMaterials; i++)
{
// Copy the material
meshMaterials[i] = d3dxMaterials[i].MatD3D;
// Set the ambient color for the material (D3DX does not do this)
meshMaterials[i].Ambient = meshMaterials[i].Diffuse;
// Create the texture if it exists - it may not
meshTextures[i] = NULL;
if (d3dxMaterials[i].pTextureFilename)
D3DXCreateTextureFromFile(d3dDevice, d3dxMaterials[i].pTextureFilename, &meshTextures[i]);
}
materialBuffer->Release();
for (DWORD i=0; i<numMaterials; i++)
{
// Set the material and texture for this subset
d3dDevice->SetMaterial(&meshMaterials[i]);
d3dDevice->SetTexture(0,meshTextures[i]);
// Draw the mesh subset
mesh->DrawSubset( i );
}
d3dDevice->EndScene();
d3dDevice->Present(NULL, NULL, NULL, NULL);
// Show Window
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
// Handle messages
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
This is my program to load a mesh tiger.x and display it. But it is not getting displayed. The value of the variable numMaterials remains 1 all the time. I guess there is some issue with my program. Someone please help me to figure it out. Thanks.
The tiger.x mesh file below
http://pastebin.com/DuvpS4mh
The problem is that you are rendering to the window and then calling UpdateWindow which forces a re-paint, thus erasing the drawing.
I suggest you download the DirectX SDK and look at the samples to see how to build a 'correct' rendering loop.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
{
// Rendering
d3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 255, 0), 1.0f, 0);
d3dDevice->BeginScene();
for (DWORD i = 0; i < numMaterials; i++)
{
// Set the material and texture for this subset
d3dDevice->SetMaterial(&meshMaterials[i]);
d3dDevice->SetTexture(0, meshTextures[i]);
// Draw the mesh subset
mesh->DrawSubset(i);
}
d3dDevice->EndScene();
d3dDevice->Present(NULL, NULL, NULL, NULL);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
You have to handle the WM_PAINT event.