13 Replies - 660 Views - Last Post: 01 September 2019 - 03:07 AM Rate Topic: -----

#1 Dave89   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 20
  • Joined: 25-July 19

Win32 WndProc Message Question

Posted 23 August 2019 - 03:51 PM

Hello,

I'm working on a Win32 Framework for my projects and been experimenting with additional functionality such as adding additional windows (children). At present they share the same WndClass name as the main application window and as such share the same WndProc. The question I have is related to the WM_DESTROY message.

What happens to a child window when you close it? I know it sends the WM_DESTROY message and my code is as follows in the message so that the application does not terminate when we only close a child window:

case WM_DESTROY: {
		if (hWnd == m_mainWindow)
		{
			ShutDown();
			PostQuitMessage(0);
		}
		
		return 0;
	}



What is confusing me is that on closing the child window, it closes but I have not called DestroyWindow(m_child)? So does this window still exist? or what happens to it? I have tried debugging the code and the HWND variable m_child still shows the same address but if i try to show the window again does nothing, trying to fetch the title through GetWindowText gets "", which would seem like It has destroyed the child window automatically? but left m_child pointing to the same address in memory to where it was (dangling pointer?).

I know that in the case of a dangling pointer i could resolve it with:

case WM_DESTROY: {
		.....Code to check if main window ^
		// Check if child window
		if (hWnd == m_child)
		{
			DestroyWindow(hWnd);
			m_child = nullptr;
		}
		
		return 0;
	}


but if it has not actually destroyed the window (as I've not called DestroyWindow, what has happened to this child? can I somehow retrieve it again or am I missing something here completely? I've been searching google for answers but not come across anything helpful yet, so hoping someone here may be able to assist with my query.

Kind Regards
Dave

Is This A Good Question/Topic? 0
  • +

Replies To: Win32 WndProc Message Question

#2 Dave89   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 20
  • Joined: 25-July 19

Re: Win32 WndProc Message Question

Posted 23 August 2019 - 04:03 PM

From looking at the Microsoft docs into the WM_CLOSE message which first gets called on the user closing a window before WM_DESTROY:

Quote

Remarks
An application can prompt the user for confirmation, prior to destroying a window, by processing the WM_CLOSE message and calling the DestroyWindow function only if the user confirms the choice.

By default, the DefWindowProc function calls the DestroyWindow function to destroy the window.


So that answer's my question, If I handle the WM_CLOSE message I can decide if the m_child window gets destroyed or just hidden.

not a fan of the dangling pointer that occurs when we don't handle WM_CLOSE but as in my original post that is easily enough solved.

Sorry For Asking a question that was simpler than I first though.

Regards

Dave
Was This Post Helpful? 0
  • +
  • -

#3 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 7024
  • View blog
  • Posts: 23,849
  • Joined: 05-May 12

Re: Win32 WndProc Message Question

Posted 24 August 2019 - 10:43 AM

Glad that you figured out a solution to your problem.

In general, if you have two different kinds of windows, in this case a top level window vs. a child window, you'll want to have two different window message handlers (aka WndProc's). There's nothing to prevent you from subclassing windows, that is, chaining WndProc's, so that you can have shared common code, but keep specialized behavior specific to a particular kind of window separate. In fact, this is the way you are supposed to use Windows.

As an aside, so that you don't have any dangling pointers, stash your pointer in the window itself. That way, when the window is destroyed you know that it's time to also destroy your pointer. See my ancient tutorial: Better Windows Code Organization Using C++ classes
Was This Post Helpful? 1
  • +
  • -

#4 Dave89   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 20
  • Joined: 25-July 19

Re: Win32 WndProc Message Question

Posted 25 August 2019 - 03:17 AM

Thanks SkyDiver, Interesting read, will definitely look into implementing some of the points discussed in your tutorials (as well as trying to keep it at the latest standard of c++). Currently my framework is using a namespace for the main application and window rather than a singleton class with static variables. then pass in a class for the current project we wish to execute (which is derived from an pure abstract class IProject to define a set list of function that the project must implement). I will look into upgrading some of the code to take the concepts pointed at in your tutorials into action. Currently the code look as follows (Put it up in whole as don't mind sharing for educational purpose or for people to make their own implementation from it):

// File: Win32Application.h
#ifndef __WIN32APPLICATION_H__
#define __WIN32APPLICATION_H__

// Forward declarations
//class PROJECT_TEMPLATE;
class IPROJECT;

// Class: Win32Application
namespace Win32Application
{
	LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam);

	//int Run(PROJECT_TEMPLATE* pGraphicApp, HINSTANCE hInstance, int nCmdShow);
	int Run(IPROJECT* pGraphicApp, HINSTANCE hInstance, int nCmdShow);

	HWND Hwnd();
	HINSTANCE Instance();
	std::wstring ClassName();

	// For Debug
	void OutputStatus();
};

#endif


// File: Win32Application.cpp

// Includes
#include "PCH.h"
#include "Win32Application.h"
//#include "ProjectTemplate.h"
#include "IProject.h"

namespace Win32Application
{
	HWND m_hWnd{ nullptr };
	HINSTANCE m_hInstance{ nullptr };
	IPROJECT* m_pProject{ nullptr };// (nullptr);

	//int Run(PROJECT_TEMPLATE* pProject, HINSTANCE hInstance, int nCmdShow)
	int Run(IPROJECT* pProject, HINSTANCE hInstance, int nCmdShow)
	{
		m_pProject = pProject;
		m_hInstance = hInstance;

		// Initialize the window class
		WNDCLASSEX WCEX = { 0 };
		WCEX.cbSize = sizeof(WNDCLASSEX);
		WCEX.style = CS_HREDRAW | CS_VREDRAW;
		WCEX.hbrBackground = (HBRUSH)(CreateSolidBrush(RGB(128, 128, 255)));
		WCEX.lpfnWndProc = WndProc;
		WCEX.hInstance = hInstance;
		WCEX.hCursor = LoadCursor(NULL, IDC_ARROW);
		WCEX.lpszClassName = L"Win32AppClass";
		RegisterClassEx(&WCEX);

		RECT windowRect = { 0, 0, static_cast<LONG>(pProject->Size().cx), static_cast<LONG>(pProject->Size().cy) };
		AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE);

		long x = (GetSystemMetrics(SM_CXSCREEN) - pProject->Size().cx) / 2;
		long y = (GetSystemMetrics(SM_CYSCREEN) - pProject->Size().cy) / 2;

		// Create the window and store a handle to it
		m_hWnd = CreateWindow(WCEX.lpszClassName, m_pProject->Title(), WS_OVERLAPPEDWINDOW, x, y, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, nullptr, nullptr, hInstance, nullptr/*pSample*/);

		// Initialize the attached Project App
		m_pProject->StartUp();

		// Show the window
		ShowWindow(m_hWnd, nCmdShow);
		UpdateWindow(m_hWnd);

		// Main Win32 Loop
		MSG msg = {};
		while (msg.message != WM_QUIT)
		{
			// Process any messages in the queue.
			if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}

			if (m_pProject && m_pProject->Status() == RUNNING)
				m_pProject->Execute();
		}

		// ============================================
		// No need to call the shutdown function here 
		// as it is called on the WM_DESTROY message.
		// ============================================

		// Return this part of the WM_QUIT message to Windows.
		return static_cast<char>(msg.wParam);
	}

	HWND Hwnd()
	{
		return m_hWnd;
	}

	HINSTANCE Instance()
	{
		return m_hInstance;
	}

	std::wstring ClassName()
	{
		return std::wstring(L"Win32AppClass");
	}

	void OutputStatus()
	{
		switch (m_pProject->Status())
		{
		case RUNNING:
			OutputDebugString(L"RUNNING\n");
			break;
		case PAUSED:
			OutputDebugString(L"PAUSED\n");
			break;
		case RESIZE:
			OutputDebugString(L"RESIZING\n");
			break;
		case MINIMIZE:
			OutputDebugString(L"MINMIZED\n");
			break;
		case MAXIMIZE:
			OutputDebugString(L"MAXIMIZED\n");
			break;
		case QUIT:
			OutputDebugString(L"QUTTING, Good Bye.\n");
			break;
		default: break;
		}
	}

	// Main message handler for the sample.
	LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
	{
		// If the message is WM_DESTROY we want to end the application
		// We will check for this message first before handing the
		// control over to the Project supplied to the application.
		if (m_pProject) {
			return m_pProject->WndProc(window, message, wParam, lParam);
		}
		else {
			if (message == WM_DESTROY)
			{
				if (m_pProject)
				{
					m_pProject->ShutDown();
					delete m_pProject;
					m_pProject = nullptr;
				}
				PostQuitMessage(0);
			}

			return DefWindowProc(window, message, wParam, lParam);
		}

	}
}


// File: IProject.h
#ifndef __IPROJECT_H__
#define __IPROJECT_H__

#include <Windows.h>

enum PROJECT_STATUS
{
	QUIT = -4,			// App is quitting
	MAXIMIZE = -3,		// App is being minimised
	MINIMIZE = -2,		// App is being minimised
	RESIZE = -1,			// App is being resized
	PAUSED = 0,		// User has paused the applucation
	RUNNING = 1,		// Application is running as normal
};

// Define the IPRoject interface
class IPROJECT
{
public:
	//IPROJECT() = default;
	//IPROJECT(std::wstring title, long width, long height);
	virtual ~IPROJECT() = default;

	virtual void Initialize(LPCWSTR Title, long width, long height) = 0;
	virtual void StartUp() = 0; // Initializations etc
	virtual void Execute() = 0; // Run the Projects Update methods etc
	virtual void ShutDown() = 0; // Clean up this mess

	virtual SIZE	Size() const = 0;
	virtual LPCWSTR Title() const = 0;
	virtual PROJECT_STATUS	Status() const = 0;

	virtual LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) = 0;
};
#endif


// File: Main.cpp
// Includes
#include "PCH.h"
#include "Win32Application.h"
#include "IProject.h"

class TestProject : public IPROJECT
{
public:
	TestProject(std::wstring title, long width, long height) : IPROJECT() { Initialize(title.c_str(), width, height); };
	virtual ~TestProject() = default;

	LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam);

	void Initialize(LPCWSTR title, long width, long height);
	void StartUp() { };
	void Execute() { OutputDebugString(L"Running smoothly\n\n"); };
	void onresize() { };
	bool OnUpdate() { return true; }; // Update project logic
	void ShutDown() { };

	SIZE	Size() const { return m_size; }
	LPCWSTR Title() const {	return m_title.c_str();}
	PROJECT_STATUS	Status() const {return m_status;}

private:
	std::wstring m_title{};
	SIZE m_size{};
	PROJECT_STATUS m_status{ RUNNING };
};

_Use_decl_annotations_
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);
	UNREFERENCED_PARAMETER(nCmdShow);

	// Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif

	TestProject ProjectZero(L"Project Zero", 1024, 768);

	return Win32Application::Run(&ProjectZero, hInstance, nCmdShow);
}

LRESULT TestProject::WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_ACTIVATE:
		if (LOWORD(wParam) == WA_INACTIVE)
		{
			m_status = (PAUSED);
			OutputDebugString(L"Application Paused\n");
		}
		else
		{
			m_status = (RUNNING);
			OutputDebugString(L"Application UnPaused\n");
		}
		return 0;
	/*case WM_CLOSE:
		return 0;*/
	case WM_DESTROY:
		ShutDown();
		PostQuitMessage(0);
		return 0;
	default: return DefWindowProc(window, message, wParam, lParam);
	}
}

void TestProject::Initialize(LPCWSTR title, long width, long height)
{
	m_title = title;
	m_size = { width, height };
}



// File: PCH.h
#ifndef __PCH_H__
#define __PCH_H__

// Pre-Processor defines
#ifndef UNICODE
#define UNICODE
#endif

#ifndef _UNICODE
#define _UNICODE
#endif

#define WIN32_LEAN_AND_MEAN
#define WIN32_EXTRALEAN
#define VC_EXTRALEAN

// Libraries
#pragma comment(lib, "windowsapp.lib")

//Includes WinRT stuffs
#include <Unknwn.h>
#include <winrt/base.h>

#if defined(DEBUG) || defined(_DEBUG)
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif

// Includes
#include <windows.h>
#include <string>
#include <vector>
#include <algorithm>

#endif



I'm hoping the framework looks good so far and upgrading it I'll hoping to do the following:

1: Create a window class to contain the: Window handle, and allow for registering of a separate class (and hence wndproc). this will add re-usability for when I want to add child window to the application too through the project.

2: I an wandering if it may be worth me maing IProject and proper interface derived from IUnknown, as I am thing of having different project types (a standard window project, DirectX project, etc...) so sort of similar to the idea of DirectX's Device, Device1, device2... etc but for project types that add additional functionality for that specific project.

I wouldn't mind some feedback from this framework so far, if anyone would be so kind and I'll try keep it updated.

Kind Regards
Dave
Was This Post Helpful? 1
  • +
  • -

#5 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 7024
  • View blog
  • Posts: 23,849
  • Joined: 05-May 12

Re: Win32 WndProc Message Question

Posted 25 August 2019 - 05:49 AM

If you know with certainty that you'll be going down that path where you'll have different project types, then deriving from IUnknown would be the correct approach. You'll need to think hard though whether that IProject is really providing different kinds of projects like IDirectXProject or IWebBrowserProject, or if it is actually providing a service like IDebugCallbacks. If you are using the IUnknown as a service locator, then I recommend letting the base object implement IServiceProvider and then query for services like IDebugCallbacks from that service provider interfaces.
Was This Post Helpful? 1
  • +
  • -

#6 Dave89   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 20
  • Joined: 25-July 19

Re: Win32 WndProc Message Question

Posted 30 August 2019 - 02:34 AM

Thanks SkyDiver, Sorry for the late reply was away learning about sub classing and doing some code cleanup from my sample above. The IUknown approach I will definitely be looking into, the idea I have is to hide away most the Initial setup code so that the user may focus on the actual logic or features they are wanting (IDirectXProject would hide the initialization of the DirectX device and swapchains etc for the user to focus on actually making games or graphic simulations), Still looking for a few good tutorials on implementing IUknown, found some from Kenny Kerr which look hopeful but unsure about the guid stuff, but it's partially on the back burner as it's not an important implementation just now till I have a more complete project.

Updates to my above from above include:

Removing the need for a WndProc in IProject, instead using the std::function in an std::map (in the WIN32_BASE namespace) to allow the user to set events in the setup of the project and then in the main WndProc on the call to the default case we process to see if these messages have occurred and if so execute the function that they have chosen.

Using gsl::not_null for the project paramter in WIN32_BASE::Run to make sure that we have a valid object passed in.

The Code Update is as follows:

// File: PCH.h
#ifndef __PCH_H__
#define __PCH_H__

// Pre-Processor defines
#ifndef UNICODE
#define UNICODE
#endif

#ifndef _UNICODE
#define _UNICODE
#endif

#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#define WIN32_EXTRALEAN
#define VC_EXTRALEAN

// Libraries
#pragma comment(lib, "windowsapp.lib")
#pragma comment(lib, "Comctl32.lib")

//Includes WinRT stuffs
#include <Unknwn.h>
#include <winrt/base.h>
#include <gsl/gsl>

#if defined(DEBUG) || defined(_DEBUG)
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif

// Includes
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>

using namespace gsl;

#endif // ##EOF##



// ==================================================== //
//					File: Win32Base.h					//
// ==================================================== //
// Description:- Base namespace for all Win32 Projects	//
// which will create a base window and handle the base  //
// WNDPROC and deal with the essentials of any projects //
// passed though for running.							//
// ==================================================== //
#ifndef __WIN32BASE_H__
#define __WIN32BASE_H__

#include "IProject.h"
// Forward Declarations
//class IPROJECT;

namespace WIN32_BASE
{
	LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam);

	int Run(not_null<IPROJECT*> pProject, HINSTANCE hInstance, int nCmdShow);

	HWND Hwnd();
	HINSTANCE Instance();
	std::wstring ClassName();
	void SetupEvent(std::size_t Index, std::function<void(HWND, UINT, WPARAM, LPARAM)> Func);
	void ProcessEvents(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam);
}

#endif // ##EOF##



// File: Win32Base.cpp
// Description: Implementations of the header file: Win32Base.h

// Includes
#include "PCH.h"
#include "Win32Base.h"

namespace WIN32_BASE
{
	HWND m_hWnd{ nullptr };
	HINSTANCE m_hInstance{ nullptr };
	IPROJECT* m_pProject{ nullptr };
	std::map<std::int32_t, std::function<void(HWND, UINT, WPARAM, LPARAM)>> Functions;

	int Run(not_null<IPROJECT*> pProject, HINSTANCE hInstance, int nCmdShow)
	{
		m_pProject = pProject;
		m_hInstance = hInstance;

		// Initialize the window class
		WNDCLASSEX WCEX = { 0 };
		WCEX.cbSize = sizeof(WNDCLASSEX);
		WCEX.style = CS_HREDRAW | CS_VREDRAW;
		WCEX.hbrBackground = (HBRUSH)(CreateSolidBrush(RGB(128, 128, 255)));
		WCEX.lpfnWndProc = WndProc;
		WCEX.hInstance = hInstance;
		WCEX.hCursor = LoadCursor(NULL, IDC_ARROW);
		WCEX.lpszClassName = L"Win32AppClass";
		RegisterClassEx(&WCEX);

		RECT windowRect = { 0, 0, static_cast<LONG>(pProject->Size().cx), static_cast<LONG>(pProject->Size().cy) };
		AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE);

		long x = (GetSystemMetrics(SM_CXSCREEN) - pProject->Size().cx) / 2;
		long y = (GetSystemMetrics(SM_CYSCREEN) - pProject->Size().cy) / 2;

		// Create the window and store a handle to it
		m_hWnd = CreateWindow(WCEX.lpszClassName, m_pProject->Title(), WS_OVERLAPPEDWINDOW, x, y, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, nullptr, nullptr, hInstance, nullptr/*pSample*/);

		DWORD error{};
		error = GetLastError();

		// Initialize the attached Project App
		m_pProject->StartUp();

		// Show the window
		ShowWindow(m_hWnd, nCmdShow);
		UpdateWindow(m_hWnd);

		// Main Win32 Loop
		MSG msg = {};
		while (msg.message != WM_QUIT)
		{
			// Process any messages in the queue.
			if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}

			if (m_pProject && m_pProject->Status() == RUNNING)
				m_pProject->Execute();
		}

		m_pProject->ShutDown();
		m_pProject = nullptr;

		// Clear any remaining function calls
		for (int I = 0; I < 6; ++I)
		{
			Functions[I] = nullptr;
		}

		// ============================================
		// No need to call the shutdown function here 
		// as it is called on the WM_DESTROY message.
		// ============================================

		// Return this part of the WM_QUIT message to Windows.
		return static_cast<char>(msg.wParam);
	}

	HWND Hwnd()
	{
		return m_hWnd;
	}

	HINSTANCE Instance()
	{
		return m_hInstance;
	}

	std::wstring ClassName()
	{
		return std::wstring(L"Win32AppClass");
	}

	void SetupEvent(std::size_t Index, std::function<void(HWND, UINT, WPARAM, LPARAM)> Func)
	{
		if (Func != nullptr)
		{
			Functions.insert(std::make_pair(Index, Func));
		}
	}

	void ProcessEvents(HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
	{
		if (Functions[Msg] != nullptr)
		{
			Functions[Msg](Hwnd, Msg, wParam, lParam);
		}
	}

	void OutputStatus()
	{
		switch (m_pProject->Status())
		{
		case RUNNING:
			OutputDebugString(L"RUNNING\n");
			break;
		case PAUSED:
			OutputDebugString(L"PAUSED\n");
			break;
		case RESIZE:
			OutputDebugString(L"RESIZING\n");
			break;
		case MINIMIZE:
			OutputDebugString(L"MINMIZED\n");
			break;
		case MAXIMIZE:
			OutputDebugString(L"MAXIMIZED\n");
			break;
		case QUIT:
			OutputDebugString(L"QUTTING, Good Bye.\n");
			break;
		default: break;
		}
	}

	// Main message handler for the sample.
	LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
	{
		switch (message)
		{
		case WM_CLOSE:
		{
			if (MessageBox(nullptr, L"Do You want to Quit?", L"Quit", MB_YESNO) == IDYES)
				PostQuitMessage(0);
			else
				return 0;
		}
		case WM_DESTROY:
		{
			PostQuitMessage(0);
		}
		default:
		{
			ProcessEvents(window, message, wParam, lParam);
			return DefWindowProc(window, message, wParam, lParam);
		}
		}
	}
}


// File: IProject.h
#ifndef __IPROJECT_H__
#define __IPROJECT_H__

#include <Windows.h>

enum PROJECT_STATUS
{
	QUIT = -4,			// App is quitting
	MAXIMIZE = -3,		// App is being minimised
	MINIMIZE = -2,		// App is being minimised
	RESIZE = -1,			// App is being resized
	PAUSED = 0,		// User has paused the applucation
	RUNNING = 1,		// Application is running as normal
};

// Define the IPRoject interface
class IPROJECT
{
public:
	//IPROJECT() = default;
	//IPROJECT(std::wstring title, long width, long height);
	virtual ~IPROJECT() = default;

	virtual void Initialize(LPCWSTR Title, long width, long height) = 0;
	virtual void StartUp() = 0; // Initializations etc
	virtual void Execute() = 0; // Run the Projects Update methods etc
	virtual void ShutDown() = 0; // Clean up this mess

	virtual SIZE	Size() const = 0;
	virtual LPCWSTR Title() const = 0;
	virtual PROJECT_STATUS	Status() const = 0;

	//virtual LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) = 0;
};
#endif



// File: Main.cpp
// Includes
#include "PCH.h"
#include "Win32Base.h"
#include "IProject.h"

class TestProject : public IPROJECT
{
public:
	TestProject(std::wstring title, long width, long height) : IPROJECT() { Initialize(title.c_str(), width, height); };
	virtual ~TestProject() = default;

	void Initialize(LPCWSTR title, long width, long height);
	void StartUp() { };
	void Execute() { };
	void ShutDown() { };

	void onactivate(HWND, UINT, WPARAM, LPARAM);
	void onkeydown(HWND, UINT, WPARAM, LPARAM);
	void onresize(LPARAM);
	bool OnUpdate() { return true; }; // Update project logic

	SIZE	Size() const { return m_size; }
	LPCWSTR Title() const { return m_title.c_str(); }
	PROJECT_STATUS	Status() const { return m_status; }
	void			Status(PROJECT_STATUS newStatus) {	m_status = newStatus; }

private:
	std::wstring m_title{};
	SIZE m_size{};
	PROJECT_STATUS m_status{ RUNNING };
};

_Use_decl_annotations_
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE , LPWSTR , int nCmdShow)
{
	// Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif

	TestProject ProjectZero(L"Project Zero", 1024, 768);

	return WIN32_BASE::Run(&ProjectZero, hInstance, nCmdShow);
}

void TestProject::Initialize(LPCWSTR title, long width, long height)
{
	using namespace std::placeholders;

	m_title = title;
	m_size = { width, height };

	// Set event takes functions that can take up too 4 parameters based on the WNDPROC
	// Params, when we Bind the function we want to use we can Specify no placeholders
	// if the function takes no parameters or specify the specific placeholder we need
	// in our case the placeholder values are associated as follows:
	// _1 = HWND, _2 = UINT, _3 = WPARAM, _4 = LPARAM
	WIN32_BASE::SetupEvent(WM_SIZE, std::bind(&TestProject::onresize, this, _4));
	WIN32_BASE::SetupEvent(WM_ACTIVATE, std::bind(&TestProject::onactivate, this, _1, _2, _3, _4));
	WIN32_BASE::SetupEvent(WM_KEYDOWN, std::bind(&TestProject::onkeydown, this, _1, _2, _3, _4));
}

void TestProject::onresize(LPARAM lParam)
{
	SIZE p = { LOWORD(lParam),HIWORD(lParam) };
	std::wstring st{};
	st = L"Width: " + std::to_wstring(p.cx) + L" Height: " + std::to_wstring(p.cy) + L"\n"; 
	OutputDebugString(st.c_str());
};

void TestProject::onactivate(HWND, UINT, WPARAM wParam, LPARAM)
{
	if (LOWORD(wParam) == WA_INACTIVE)
	{
		this->Status(PAUSED);
		OutputDebugString(L"Application Paused\n");
	}
	else
	{
		this->Status(RUNNING);
		OutputDebugString(L"Application UnPaused\n");
	}
	return;
}

void TestProject::onkeydown(HWND, UINT, WPARAM wParam, LPARAM)
{
	if (wParam == VK_ESCAPE)
		PostQuitMessage(0);
}



I'm away to make a window class and control classes that can use sub classing if the user so desires (Think it should be left to the user to decide if they want to have SUBCLASSPROC or just use the standard WNDPROC in the namespace with the Event Map of functions.

Regards
Dave
Was This Post Helpful? 0
  • +
  • -

#7 Dave89   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 20
  • Joined: 25-July 19

Re: Win32 WndProc Message Question

Posted 31 August 2019 - 03:32 PM

I'm not having another WNDPROC message problem, I upgraded my code in have a window class as follows:

// File: Win32_window.h
#ifndef __WIN32WINDOW_H__
#define __WIN32WINDOW_H__

void DisplayError(LPCWSTR errCaption);

// Class: Win32Window
class Win32Window
{
public:
	Win32Window() = default;
	Win32Window(const Win32Window& cw) = delete;
	Win32Window(Win32Window&& cw) = delete;
	~Win32Window() {};

	void Create(HWND parent, std::wstring title, std::wstring className, WNDPROC windowProc, POINT position = { CW_USEDEFAULT, CW_USEDEFAULT }, SIZE size = { CW_USEDEFAULT, CW_USEDEFAULT }, DWORD style = { WS_OVERLAPPEDWINDOW }, DWORD styleEx = { 0 }, HMENU menu = nullptr);
	void Create(HWND parent, std::wstring title, WNDCLASSEX& wndClass, POINT position = { CW_USEDEFAULT, CW_USEDEFAULT }, SIZE size = { CW_USEDEFAULT, CW_USEDEFAULT }, DWORD style = { WS_OVERLAPPEDWINDOW }, DWORD styleEx = { 0 }, HMENU menu = nullptr);

	int Show(int nCmdShow) { return ShowWindow(m_Window, nCmdShow); }

	HWND operator &() { return m_Window; }
private:
	bool m_Message = false;
	HWND m_Window{ nullptr };

	Win32Window& operator = (const Win32Window& cw) = delete;
	Win32Window& operator = (Win32Window&& cw) = delete;

protected:
	MSG Messages = { nullptr };
};

#endif // ##EOF ##


// File: Win32_window.cpp

// Includes
#include "PCH.h"
#include "Win32_window.h"

void DisplayError(LPCWSTR errCaption)
{
	DWORD err = GetLastError();
	LPTSTR error = 0;
	if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr, err, 0, (LPTSTR)& error, 0, nullptr) != 0)
	{
		MessageBox(nullptr, error, errCaption, MB_OK | MB_IConerror);
	}

	if (error)
	{
		LocalFree(error);
		error = 0;
	}
}


void Win32Window::Create(HWND parent, std::wstring title, std::wstring className, WNDPROC windowProc, POINT position, SIZE size, DWORD style, DWORD styleEx, HMENU menu)
{
	WNDCLASSEX WndClass{ 0 };

	//parent == nullptr ? parent = HWND_DESKTOP : parent = parent;

	// Check if the class already exists
	if (!::GetClassInfoEx(GetModuleHandle(0), className.c_str(), &WndClass))
	{
		WndClass.cbSize = sizeof(WNDCLASSEX);
		WndClass.style = CS_HREDRAW | CS_VREDRAW;
		WndClass.hbrBackground = (HBRUSH)(CreateSolidBrush(RGB(128, 128, 255)));
		WndClass.lpfnWndProc = windowProc;
		WndClass.hInstance = GetModuleHandle(0);
		WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
		WndClass.lpszClassName = className.c_str();

		if (!RegisterClassEx(&WndClass))
			DisplayError(L"RegisterClass Failed");
	}

	RECT windowRect = { 0, 0, static_cast<LONG>(size.cx), static_cast<LONG>(size.cy) };
	AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE);

	long width = windowRect.right - windowRect.left;
	long height = windowRect.bottom - windowRect.top;

	m_Window = CreateWindowEx(styleEx, className.c_str(), title.c_str(), style, position.x, position.y, width, height, parent, menu, GetModuleHandle(nullptr), this);

	if (!m_Window)
		DisplayError(L"CreateWindow Failed");
	else
		ShowWindow(m_Window, SW_SHOW);
}

void Win32Window::Create(HWND parent, std::wstring title, WNDCLASSEX& wndClass, POINT position, SIZE size, DWORD style, DWORD styleEx, HMENU menu)
{
	// Check a valid WNDCLASS has been passed into the function
	if (wndClass.cbSize) // check for non-zero
	{
		// Check if the class name used already exists or not
		if (!::GetClassInfoEx(GetModuleHandle(0), wndClass.lpszClassName, &wndClass))
		{
			RegisterClassEx(&wndClass);
		}

		m_Window = CreateWindowEx(styleEx, wndClass.lpszClassName, title.c_str(), style, position.x, position.y, size.cx, size.cy, parent, menu, GetModuleHandle(nullptr), this);
	}
	else
	{
		DisplayError(L"Window Creation Failed");
		return;
	}
}



I then decided that instead of exclusively creating the window for the main application in the WIN32_BASE namespace I would use this class to create it. All was well and good, program ran and the window/s showed as they should but I noticed a few aspects that had stopped working...

WIN32_BASE::SetupEvent(WM_SIZE, std::bind(&TestProject::onresize, this, _4));
WIN32_BASE::SetupEvent(WM_ACTIVATE, std::bind(&TestProject::onactivate, this, _1, _2, _3, _4));


The other messages work fine as they should when called but it never seems to pick up the WM_SIZE or WM_ACTIVE message as it does in my previous sample posted. I've debugs it for a while but am totally stumped on why this is happening... the WM_SIZE function still gets called if I put the case statement into the WNDPROC directly instead of through the Process events the same with WM_ACTIVE, what is even strainger is that if I change they functions to a different message like:

WIN32_BASE::SetupEvent(WM_EXITSIZEMOVE, std::bind(&TestProject::onresize, this, _4));
WIN32_BASE::SetupEvent(WM_KEYUP, std::bind(&TestProject::onactivate, this, _1, _2, _3, _4));


it calls the functions when the exitsizemove message occurs and if unpressed a key? Any idea's of what might be causing it to miss the WM_ACTIVE, WM_SIZE? the code is pretty much the same apart from the window is now enclosed in a class instead of physically in the namespace.

One thing that had crossed my mind was the change of passing the HINSTANCE through the function to the call to GetModuleHandle(nullptr) but even changing the function to take the application instance in did not change anything.

Any ideas or suggestions would be much appreciated.

Kind Regards
Dave

Sorry I made a typo in the last post, it should of said "I'm having another WNDPROC message problem, I upgraded my code in have a window class as follows" (I'm not able to edit my posts yet to have fixed it manually)
Was This Post Helpful? 0
  • +
  • -

#8 Dave89   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 20
  • Joined: 25-July 19

Re: Win32 WndProc Message Question

Posted 31 August 2019 - 05:07 PM

** Having been debugging it further I've found that it is still sending the messages through changing the ProcessEvents functions to the following:

std::wstring st{};

		if(Msg == WM_SIZE)
			st = L"Message Sent: " +  std::to_wstring(Msg) + L"\n";
		OutputDebugString(st.c_str());
		if (Functions[Msg] != nullptr)
		{
			st = L"Internal Message Processed : " + std::to_wstring(Msg) + L"\n";
			OutputDebugString(st.c_str());
			Functions[Msg](Hwnd, Msg, wParam, lParam);
		}


On the Startup of the project where I set the event for the WM_SIZE it seems like it worked find but later in the execution it seems that the function at 5 (index of WM_SIZE) is then null and so it has nothing to execute. Still rattling my brain on this one since as I said before its working with the old code so, I upgrades the SetEvent Function to use try_emplace instead of insert but that didn't solve anything.

From what I can see it I can see how using a separate class for window creation would affect the code to such a degree (I've compared the current code to the old to look for any discrepancies but can't find any so far besizes the use of the Win32_Window class.
Was This Post Helpful? 0
  • +
  • -

#9 Dave89   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 20
  • Joined: 25-July 19

Re: Win32 WndProc Message Question

Posted 31 August 2019 - 05:14 PM

Having adjusted the code slightly (moved the setEvent function to the WinMain function between the two calls there with the following:

WIN32_BASE::SetupEvent(WM_SIZE, [&ProjectZero](HWND h, UINT m, WPARAM w, LPARAM lParam) {ProjectZero.onresize(h, m, w,lParam); });


This code works fine, so i'm starting to find it is how i'm calling the setEvent function in my class (most likely the TestProject::onresize) as it can't find the implementation of the function and as such passes null to std::function. I'm getting closer to fixing it I hope.
Was This Post Helpful? 0
  • +
  • -

#10 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 7024
  • View blog
  • Posts: 23,849
  • Joined: 05-May 12

Re: Win32 WndProc Message Question

Posted 31 August 2019 - 07:25 PM

View PostDave89, on 31 August 2019 - 08:07 PM, said:

On the Startup of the project where I set the event for the WM_SIZE it seems like it worked find but later in the execution it seems that the function at 5 (index of WM_SIZE) is then null and so it has nothing to execute.

Sounds like a buffer overrun or an invalid memory free operation happening somewhere. You could have set a memory breakpoint on it to see what was causing the memory block to change.
Was This Post Helpful? 1
  • +
  • -

#11 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 7024
  • View blog
  • Posts: 23,849
  • Joined: 05-May 12

Re: Win32 WndProc Message Question

Posted 31 August 2019 - 07:37 PM

Also, probably not directly related to your problem, but it did catch my eye: it looks like you are using 2 different types for the Windows message I'd: UINT and std::int32_t. One is signed, while the other one is unsigned. Things usually work out despite the compiler warnings of comparing signed vs. unsigned as long as the number of bits match up, but I don't know if using the C++ implementation of std::map() may have some impact on things. I don't recall right now if the C++ standard requires that std::map() has to be implemented as a red-black tree, or if the standard gives some leeway as long as the performance characteristics requirements are attained.
Was This Post Helpful? 1
  • +
  • -

#12 Dave89   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 20
  • Joined: 25-July 19

Re: Win32 WndProc Message Question

Posted 01 September 2019 - 02:38 AM

Hi SkyDiver, Thanks for the reply, I corrected the type miss match as you described. I'm trying to run memory break points (It's a new thing for me, never knew it existed tbh), I set it up for the td::map<std::int32_t, std::function<void(HWND, UINT, WPARAM, LPARAM)>> Functions; variables, The first condition I set was when the Index in SetEvent == 5 and it's output in the debug window was as follows:

&FUNCTION: Index = 5, Function = bind(0x00c21834 {Final Template 01.exe!TestProject::OnRResize(struct HWND__ *,unsigned int,unsigned int,long)}, 0x012ff89c {m_title=L"Project Zero" m_size={cx=1024 cy=768 } m_status=RUNNING (1) }, _1, _2, _3, _4);



That looked promising but after that I set another in the ProcessEvents function checking for the message WM_SIZE or 5 and then checking the contents of Functions and it returns the following:

WIN32_BASE::ProcessEvents(unsigned int, HWND__ *, unsigned int, unsigned int, long): Msg: 5 in Functions = Function std::map<unsigned int,std::function<void __cdecl(HWND__ *,unsigned int,unsigned int,long)>,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::function<void __cdecl(HWND__ *,unsigned int,unsigned int,long)> > > >::at has no address, possibly due to compiler optimizations.



I honestly don't fully Understand what that means, other messages like WM_KEYDOWN, WM_LBUTTONDOWN are showing in the Functions variables as they should with valid pointers to the respective function just not WM_ACTIVATE, WM_SIZE and WM_CREATE (not tried all the messages), so it can't be compiler optimizations surely or logically none of them should work. The only time that the Functions variables gets cleared is after the message loop before terminating the application.

I was thinking maybe the pointer to onresize and onactivate is somehow going out of scope after setting the event but that wouldn't make sense since the others are there and working, I have tried commenting all the other events out and just leaving the WM_SIZE event to see if it worked then but nope, change the message to WM_EXITSIZEMOVE and it works fine. Just looked into compiler optimizations and it is turned off. I'm not sure what other options I have here to check.

Kind Regards
Dave
Was This Post Helpful? 0
  • +
  • -

#13 Dave89   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 20
  • Joined: 25-July 19

Re: Win32 WndProc Message Question

Posted 01 September 2019 - 02:47 AM

Running the code in the WinMain in my second last post where it works returns the same output in the debug window:

WIN32_BASE::ProcessEvents(unsigned int, HWND__ *, unsigned int, unsigned int, long): Msg: 5 in Functions = Function std::map<unsigned int,std::function<void __cdecl(HWND__ *,unsigned int,unsigned int,long)>,std::less<unsigned int>,std::allocator<std::pair<unsigned int const ,std::function<void __cdecl(HWND__ *,unsigned int,unsigned int,long)> > > >::at has no address, possibly due to compiler optimizations.


but the code executes as it should so I must of don't something wrong with the memory break point, I'll keep looking into it further.
Was This Post Helpful? 0
  • +
  • -

#14 Dave89   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 20
  • Joined: 25-July 19

Re: Win32 WndProc Message Question

Posted 01 September 2019 - 03:07 AM

I Might of fixed it, I changed the ProcessEvents function slightly for debugging from:

if (Functions[Msg] != nullptr)
		{
			Functions[Msg](Hwnd, Msg, wParam, lParam);
		}


to

void ProcessEvents(std::size_t , HWND Hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
	{
		if (Functions.count(Msg)) {
			if (Functions.at(Msg) != nullptr)
			{
				Functions.at(Msg)(Hwnd, Msg, wParam, lParam);
			}
		}



It's a lot safer as we now are no longer trying to access array elements that may not exist through the [] operator and it checks to see if the message is actually present first before proceeding. And as you suggested in your reply Skydiver it looks like it might of been a buffer over run as before it was listing loads of messages in debugging and now it only shows the ones that we setup and event for.

Thank you for your input SkyDiver, make me click that using the [] operator wasn't bound safe for an array which in turn allowed me to fix the problem :D.

Kind Ragards
Dave
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1