Ich bin noch relativ neu in der Welt von C++ habe aber schon ein kleines Spiel mittels XNA und C# progammiert.
Nun möchte ich das ganze auch mal in C++ versuchen und habe mich durch diverse Tutorials gekämpft.
Nun habe ich eine Fensterumgebung programmiert, die meinem Spiel als Hauptfenster dienen soll.
Hier einmal die Implementation:
WindowEnvoirement.hpp
Code: Alles auswählen
#ifndef MAIN_WINDOW_ENVOIREMENT_HPP
#define MAIN_WINDOW_ENVOIREMENT_HPP
#include <functional>
#include <memory>
#include <WinDef.h>
namespace Main
{
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Der Nachrichtenhandler wird aufgerufen wenn eine Nachricht verarbeitet
// werden muss.
//
// Parameter windowHandle:
// Ein Handle auf das betreffende Fenster.
//
// Parameter message:
// Die Nachricht.
//
// Parameter wordParameter:
// Der erste Nachrichtenparameter.
//
// Parameter longParameter:
// Der zweite Nachrichtenparameter:
//
// Rückgabewert:
// True wenn die Nachricht weiterverarbeitet werden soll.
//
//-----------------------------------------------------------------------------
typedef std::function<bool (HWND, UINT, WPARAM, LPARAM)> MessageHandler;
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Stellt eine Fensterumgebung zur Verfügung.
//
// Bemerkung:
// Eine Instanz kann weder kopiert noch zugewiesen werden.
//
//-----------------------------------------------------------------------------
class WindowEnvoirement
{
WindowEnvoirement(const WindowEnvoirement&);
WindowEnvoirement& operator=(const WindowEnvoirement&);
public:
WindowEnvoirement();
~WindowEnvoirement();
void Register(HINSTANCE instanceHandle, HICON iconHandle, HCURSOR cursorHandle, const WCHAR* windowClassName, const MessageHandler& messageHandler);
void Create(const WCHAR* windowName, DWORD windowStyle, DWORD extendedWindowStyle, UINT clientWidth, UINT clientHeight);
void CenterWindowAtNearestDesktop();
void Show(int showCommand);
void CheckForMessageHandlerError();
bool IsMinimized() const;
HWND GetWindowHandle() const;
void Destroy();
void Unregister();
private:
class Private;
std::unique_ptr<Private> mPrivate;
};
}
#endif // MAIN_WINDOW_ENVOIREMENT_HPP
Code: Alles auswählen
#define WIN32_LEAN_AND_MEAN
#include <cassert>
#include <exception>
#include <stdexcept>
#include <string>
#include <Windows.h>
#include "WindowEnvoirement.hpp"
namespace Main
{
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Die private Implementation der Fensterumgebung.
//
// Bemerkung:
// Eine Instanz kann weder kopiert noch zugewiesen werden.
//
//-----------------------------------------------------------------------------
class WindowEnvoirement::Private
{
Private(const Private&);
Private& operator=(const Private&);
public:
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Konstruktor.
//
//-----------------------------------------------------------------------------
Private() :
mWindowHandle(nullptr),
mInstanceHandle(nullptr),
mWindowClass(0),
mWindowClassName(L""),
mMessageHandlerError(std::exception_ptr()),
mMessageHandler([](HWND, UINT, WPARAM, LPARAM) { return true; }),
mMessageHandlerFailed(false),
mIgnoreMessageHandler(false)
{
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Destruktor.
//
//-----------------------------------------------------------------------------
~Private()
{
mIgnoreMessageHandler = true;
Destroy();
Unregister();
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Registriert die Fensterklasse.
//
// Ausnahme std::invalid_argument:
// Wird geworfen wenn ein untültiger Parameter übergeben worden ist.
//
// Ausnahme std::runtime_error:
// Wird geworfen wenn die Fensterklasse nicht registriert werden konnte.
//
// Parameter instanceHandle:
// Ein Handle auf die Programminstanz.
//
// Parameter iconHandle:
// Ein Handle auf das Programmsymbol.
//
// Parameter cursorHandle:
// Ein Handle auf den Mauszeiger.
//
// Parameter windowClassName:
// Der Fensterklassenname.
//
// Parameter messageHandler:
// Der Nachrichtenhandler.
//
//-----------------------------------------------------------------------------
void Register(HINSTANCE instanceHandle, HICON iconHandle, HCURSOR cursorHandle, const WCHAR* windowClassName, const MessageHandler& messageHandler)
{
assert(mWindowClass == 0);
if (instanceHandle == nullptr || iconHandle == nullptr || cursorHandle == nullptr)
{
throw std::invalid_argument("Invalid handle passed!");
}
if (windowClassName == nullptr)
{
throw std::invalid_argument("Invalid pointer passed!");
}
mInstanceHandle = instanceHandle;
mWindowClassName = windowClassName;
mMessageHandler = messageHandler;
WNDCLASSEXW extendedWindowClass;
extendedWindowClass.cbSize = sizeof(extendedWindowClass);
extendedWindowClass.cbClsExtra = 0;
extendedWindowClass.cbWndExtra = 0;
extendedWindowClass.hbrBackground = reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
extendedWindowClass.hIcon = iconHandle;
extendedWindowClass.hIconSm = iconHandle;
extendedWindowClass.hCursor = cursorHandle;
extendedWindowClass.hInstance = mInstanceHandle;
extendedWindowClass.lpfnWndProc = &Private::WindowProcedure;
extendedWindowClass.lpszClassName = mWindowClassName.c_str();
extendedWindowClass.lpszMenuName = nullptr;
extendedWindowClass.style = CS_VREDRAW | CS_HREDRAW;
mWindowClass = RegisterClassExW(&extendedWindowClass);
if (!mWindowClass)
{
throw std::runtime_error("Failed to register windowEnvoirement class!");
}
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Erstellt das Fenster in der Mitte des nächtsgelegenen Desktops.
//
// Bemerkung:
// Damit diese Methode ausgeführt werden kann, muss die Fensterklasse bereits
// registriert worden sein.
//
// Ausnahme std::invalid_argument:
// Wird geworfen wenn ein untültiger Parameter übergeben worden ist.
//
// Ausnahme std::runtime_error:
// Wird geworfen wenn das Fenster nicht erstellt werden konnte.
//
// Parameter windowName:
// Der Fenstername.
//
// Parameter windowStyle:
// Der Fensterstil.
//
// Parameter extendedWindowStyle:
// Der erweiterte Fensterstil.
//
// Parameter clientWidth:
// Die Fensterklientbreite.
//
// Parameter clientHeight:
// Die Fensterklienthöhe.
//
//-----------------------------------------------------------------------------
void Create(const WCHAR* windowName, DWORD windowStyle, DWORD extendedWindowStyle, UINT clientWidth, UINT clientHeight)
{
assert(mWindowHandle == nullptr);
assert(mInstanceHandle != nullptr);
if (windowName == nullptr)
{
throw std::invalid_argument("Invalid pointer passed!");
}
RECT windowRectangle;
windowRectangle.left = 0;
windowRectangle.top = 0;
windowRectangle.right = static_cast<LONG>(clientWidth);
windowRectangle.bottom = static_cast<LONG>(clientHeight);
AdjustWindowRectEx(&windowRectangle, windowStyle, FALSE, extendedWindowStyle);
mWindowHandle = CreateWindowExW(
extendedWindowStyle,
mWindowClassName.c_str(),
windowName,
windowStyle,
0,
0,
static_cast<int>(windowRectangle.right - windowRectangle.left),
static_cast<int>(windowRectangle.bottom - windowRectangle.top),
nullptr,
nullptr,
mInstanceHandle,
reinterpret_cast<void*>(this));
if (!mWindowHandle)
{
throw std::runtime_error("Failed to create windowEnvoirement!");
}
CenterWindowAtNearestDesktop();
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Platziert das Fenster in der Mitte des nächtsgelegenen Desktopts.
//
//-----------------------------------------------------------------------------
void CenterWindowAtNearestDesktop()
{
assert(mWindowHandle != nullptr);
RECT windowRectangle;
GetWindowRect(mWindowHandle, &windowRectangle);
MONITORINFO monitorInfo;
monitorInfo.cbSize = sizeof(monitorInfo);
GetMonitorInfoW(MonitorFromRect(&windowRectangle, MONITOR_DEFAULTTONEAREST), &monitorInfo);
int windowPositionX = static_cast<int>((
(monitorInfo.rcWork.right - monitorInfo.rcWork.left) -
(windowRectangle.right - windowRectangle.left)) / 2);
int windowPositionY = static_cast<int>((
(monitorInfo.rcWork.bottom - monitorInfo.rcWork.top) -
(windowRectangle.bottom - windowRectangle.top)) / 2);
SetWindowPos(
mWindowHandle,
nullptr,
static_cast<int>(monitorInfo.rcWork.left) + windowPositionX,
static_cast<int>(monitorInfo.rcWork.top) + windowPositionY,
0,
0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Setzt den Fensteranzeigestatus.
//
// Parameter showCommand:
// Gibt an wie das Fenster angezeigt werden soll.
//
//-----------------------------------------------------------------------------
void Show(int showCommand)
{
assert(mWindowHandle != nullptr);
ShowWindow(mWindowHandle, showCommand);
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Prüft, ob der Nachrichtenhandler eine Ausnhame geworfen hat. Sollte dies
// der Fall sein, so wird diese weitergeworfen.
//
// Ausnahme std::exception_ptr:
// Die vom Nachrichtenhandler geworfene Ausnahme.
//
// Bemerkung:
// Die Ausnahme kann von einem beliebigen Typ sein.
//
//-----------------------------------------------------------------------------
void CheckForMessageHandlerError()
{
assert(mWindowHandle != nullptr);
if (mMessageHandlerFailed)
{
mMessageHandlerFailed = false;
std::rethrow_exception(mMessageHandlerError);
}
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Gib an ob das Fesnter minimiert ist.
//
// Rückgabewert:
// True wenn das Fenster minimiert ist.
//
//-----------------------------------------------------------------------------
bool IsMinimized() const
{
assert(mWindowHandle != nullptr);
return IsIconic(mWindowHandle) == TRUE;
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Ermittelt das Fensterhandle.
//
// Rückgabewert:
// Ein Handle auf das Fenster.
//
//-----------------------------------------------------------------------------
HWND GetWindowHandle() const
{
return mWindowHandle;
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Zerstört das Fenster.
//
//-----------------------------------------------------------------------------
void Destroy()
{
if (mWindowHandle)
{
DestroyWindow(mWindowHandle);
mWindowHandle = nullptr;
}
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Meldet die Fensterklasse ab.
//
//-----------------------------------------------------------------------------
void Unregister()
{
if (mWindowClass)
{
UnregisterClassW(mWindowClassName.c_str(), mInstanceHandle);
mInstanceHandle = nullptr;
mWindowClass = 0;
mWindowClassName = L"";
mMessageHandlerError = std::exception_ptr();
mMessageHandler = [](HWND, UINT, WPARAM, LPARAM) { return true; };
mMessageHandlerFailed = false;
}
}
private:
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Verarbeitet die Fensternachrichten und leitet entsprechende Nachrichten
// an den registrierten Nachrichtenhandler weiter.
//
// Parameter windowHandle:
// Ein Handle auf das betreffende Fenster.
//
// Parameter message:
// Die Nachricht.
//
// Parameter wordParameter:
// Der erste Nachrichtenparameter.
//
// Parameter longParameter:
// Der zweite Nachrichtenparameter:
//
// Rückgabewert:
// Das Resultat der verarbeiteten Nachricht wobei ein Wert von Null bedeutet,
// das die Nachricht nicht weiterverarbeitet werden soll.
//
//-----------------------------------------------------------------------------
static LRESULT CALLBACK WindowProcedure(HWND windowHandle, UINT message, WPARAM wordParameter, LPARAM longParameter)
{
bool processMessage = true;
if (message == WM_NCCREATE)
{
SetWindowLongPtrW(
windowHandle,
GWL_USERDATA,
reinterpret_cast<LONG_PTR>(
reinterpret_cast<Private*>(
reinterpret_cast<LPCREATESTRUCTW>(longParameter)->lpCreateParams)));
}
Private* windowEnvoirement = reinterpret_cast<Private*>(GetWindowLongPtrW(windowHandle, GWL_USERDATA));
if (windowEnvoirement && !windowEnvoirement->mMessageHandlerFailed && !windowEnvoirement->mIgnoreMessageHandler)
{
try
{
processMessage = windowEnvoirement->mMessageHandler(windowHandle, message, wordParameter, longParameter);
}
catch (...)
{
windowEnvoirement->mMessageHandlerError = std::current_exception();
windowEnvoirement->mMessageHandlerFailed = true;
}
}
if (windowEnvoirement && message == WM_CLOSE && processMessage)
{
windowEnvoirement->Destroy();
processMessage = false;
}
if (processMessage)
{
return DefWindowProcW(windowHandle, message, wordParameter, longParameter);
}
else
{
return 0;
}
}
private:
//-----------------------------------------------------------------------------
//
// Attribut mWindowHandle:
// Ein Handle auf das Fenster.
//
// Attribut mInstanceHandle:
// Ein Handle auf die Programminstnanz.
//
// Attribut mWindowClass:
// Die ID der registrierten Fensterklasse.
//
// Attribut mWindowClassName:
// Der Fensterklassenname.
//
// Attribut mMessageHandlerError:
// Speichert eine vom Nachrichtenhandler geworfene Ausnahme damit diese
// später weiterverarbeitet werden kann.
//
// Attribut mMessageHandler:
// Der Nachrichtenhandler
//
// Attribut mMessageHandlerFailed:
// Gibt an ob der Nachrichtenhandler eine Ausnahme geworfen hat.
//
// Attribut mIgnoreMessageHandler:
// Wird vom Destruktor gesetzt und gibt an das keine Nachrichten mehr an den
// Nachrichtenhandler mehr weitergeleitet werden sollen.
//
//-----------------------------------------------------------------------------
HWND mWindowHandle;
HINSTANCE mInstanceHandle;
ATOM mWindowClass;
std::wstring mWindowClassName;
std::exception_ptr mMessageHandlerError;
MessageHandler mMessageHandler;
bool mMessageHandlerFailed;
bool mIgnoreMessageHandler;
};
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Konstruktor.
//
//-----------------------------------------------------------------------------
WindowEnvoirement::WindowEnvoirement() : mPrivate(new Private)
{
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Destruktor.
//
//-----------------------------------------------------------------------------
WindowEnvoirement::~WindowEnvoirement()
{
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Ruft die eigentliche Implementation auf.
//
//-----------------------------------------------------------------------------
void WindowEnvoirement::Register(HINSTANCE instanceHandle, HICON iconHandle, HCURSOR cursorHandle, const WCHAR* windowClassName, const MessageHandler& messageHandler)
{
mPrivate->Register(instanceHandle, iconHandle, cursorHandle, windowClassName, messageHandler);
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Ruft die eigentliche Implementation auf.
//
//-----------------------------------------------------------------------------
void WindowEnvoirement::Create(const WCHAR* windowName, DWORD windowStyle, DWORD extendedWindowStyle, UINT clientWidth, UINT clientHeight)
{
mPrivate->Create(windowName, windowStyle, extendedWindowStyle, clientWidth, clientHeight);
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Ruft die eigentliche Implementation auf.
//
//-----------------------------------------------------------------------------
void WindowEnvoirement::CenterWindowAtNearestDesktop()
{
mPrivate->CenterWindowAtNearestDesktop();
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Ruft die eigentliche Implementation auf.
//
//-----------------------------------------------------------------------------
void WindowEnvoirement::Show(int showCommand)
{
mPrivate->Show(showCommand);
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Ruft die eigentliche Implementation auf.
//
//-----------------------------------------------------------------------------
void WindowEnvoirement::CheckForMessageHandlerError()
{
mPrivate->CheckForMessageHandlerError();
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Ruft die eigentliche Implementation auf.
//
//-----------------------------------------------------------------------------
bool WindowEnvoirement::IsMinimized() const
{
return mPrivate->IsMinimized();
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Ruft die eigentliche Implementation auf.
//
//-----------------------------------------------------------------------------
HWND WindowEnvoirement::GetWindowHandle() const
{
return mPrivate->GetWindowHandle();
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Ruft die eigentliche Implementation auf.
//
//-----------------------------------------------------------------------------
void WindowEnvoirement::Destroy()
{
mPrivate->Destroy();
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Ruft die eigentliche Implementation auf.
//
//-----------------------------------------------------------------------------
void WindowEnvoirement::Unregister()
{
mPrivate->Unregister();
}
}
Code: Alles auswählen
#define WIN32_LEAN_AND_MEAN
#include <cassert>
#include <exception>
#include <Windows.h>
#include "Manifest.hpp"
#include "WindowEnvoirement.hpp"
namespace Main
{
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Die Anwendung.
//
// Bemerkung:
// Eine Instanz kann weder kopiert noch zugewiesen werden.
//
//-----------------------------------------------------------------------------
class Application
{
Application(const Application&);
Application& operator=(const Application&);
public:
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Konstruktor.
//
//-----------------------------------------------------------------------------
Application()
{
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Führt die Anwendung aus.
//
// Parameter instanceHandle:
// Ein Handle auf die Programminstanz.
//
//-----------------------------------------------------------------------------
void Execute(HINSTANCE instanceHandle)
{
assert(mWindowEnvoirement.GetWindowHandle() == nullptr);
const auto messageHandler = std::bind(
&Application::MainMessageHandler,
std::ref(*this),
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3,
std::placeholders::_4);
mWindowEnvoirement.Register(
instanceHandle,
LoadIconW(nullptr, IDI_APPLICATION),
LoadCursorW(nullptr, IDC_ARROW),
L"TestWindow",
messageHandler);
mWindowEnvoirement.Create(
L"Test",
WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION,
0,
1280,
720);
mWindowEnvoirement.Show(SW_SHOWNORMAL);
RunMessageLoop();
}
private:
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Startet die Nachrichtenschleife und verarbeitet alle auf dem Nachrichten-
// stapel liegenden Nachrichten.
//
//-----------------------------------------------------------------------------
void RunMessageLoop()
{
MSG message;
while (true)
{
if (PeekMessageW(&message, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&message);
DispatchMessageW(&message);
if (message.message == WM_QUIT)
{
break;
}
}
else
{
mWindowEnvoirement.CheckForMessageHandlerError();
//
// Todo ...
//
}
}
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Verarbeitet eine Fensternachricht.
//
// Parameter windowHandle:
// Ein Handle auf das betreffende Fenster.
//
// Parameter message:
// Die Nachricht.
//
// Parameter wordParameter:
// Der erste Nachrichtenparameter.
//
// Parameter longParameter:
// Der zweite Nachrichtenparameter:
//
// Rückgabewert:
// True wenn die Nachricht weiterverarbeitet werden soll.
//
//-----------------------------------------------------------------------------
bool MainMessageHandler(HWND windowHandle, UINT message, WPARAM wordParameter, LPARAM longParameter)
{
bool processMessage = true;
switch (message)
{
case WM_KEYDOWN:
HandleKeydownMessage(wordParameter);
break;
case WM_CLOSE:
processMessage = HandleCloseMessage();
break;
case WM_DESTROY:
PostQuitMessage(0);
processMessage = false;
break;
}
return processMessage;
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Verarbeitet die WM_KEYDOWN Nachricht.
//
// Parameter wordParameter:
// Der erste Nachrichtenparameter.
//
//-----------------------------------------------------------------------------
void HandleKeydownMessage(WPARAM wordParameter)
{
switch (wordParameter)
{
case VK_ESCAPE:
PostMessageW(mWindowEnvoirement.GetWindowHandle(), WM_CLOSE, 0, 0);
break;
case VK_SPACE:
mWindowEnvoirement.CenterWindowAtNearestDesktop();
break;
case VK_RETURN:
throw std::exception("Callback Exception Test");
}
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Verarbeitet die WM_CLOSE Nachricht.
//
// Rückgabewert:
// True wenn das Fenster geschlossen werden soll.
//
//-----------------------------------------------------------------------------
bool HandleCloseMessage()
{
int result = MessageBoxW(
mWindowEnvoirement.GetWindowHandle(),
L"Wollen Sie das Fenster wirklich schliessen?",
L"Demo",
MB_ICONQUESTION | MB_YESNOCANCEL | MB_TASKMODAL);
return result == IDYES;
}
private:
//-----------------------------------------------------------------------------
//
// Attribut mWindowEnvoirement:
// Die Fensterumgebung.
//
//-----------------------------------------------------------------------------
WindowEnvoirement mWindowEnvoirement;
};
}
//-----------------------------------------------------------------------------
//
// Beschreibung:
// Der Haupteinsteigspunkt der Anwendung.
//
// Parameter instanceHandle:
// Ein Handle auf die Programminstanz.
//
// Rückgabewert:
// Einen Statuscode wobei jeder Wert ungleich Null einem Fehler entspricht.
//
//-----------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE instanceHandle, HINSTANCE, char*, int)
{
try
{
Main::Application application;
application.Execute(instanceHandle);
}
catch (const std::exception& exception)
{
MessageBoxA(nullptr, exception.what(), "Exception", MB_ICONERROR);
return -1;
}
return 0;
}
Ein wenig blöd ist ja, dass eine Methode praktisch zwei mal Aufgerufen werden muss. Oder habe ich das PIMPL-Idom einfach nicht verstanden und es ist eigentlich für ganz andere Konzepte gedacht?
Vielen Dank schon mal im Voraus