Seite 1 von 1

Unicode in C++ und der WinAPI

Verfasst: 23.08.2012, 17:25
von Fitim
Hallo zusammen.

Haben wieder einmal ein wenig Zeit gefunden um an meinem kleinen simplen Spiel fortzufahren. In meinem Fenstercode verwende ich die IrgendeineWinApiFunktionW() ... Funktionen. Diese erwarten einen UTF-16 kodierten String. Ich habe aber einfach blos einen wchar_t String mittels L"" übergeben. Hier ein kurzer Überblick, wie ich mein Fenster erstelle.

Code: Alles auswählen

#include <exception>
#include <string>
#include <sstream>
#include <Windows.h>

namespace UnnamedProject
{
	//-----------------------------------------------------------------------------------------------------------
	// Bewirkt, dass eine Instanz weder kopiert noch zugweiesen werden kann.
	//-----------------------------------------------------------------------------------------------------------
	class INonCopyAssignable
	{
	protected:
		//-----------------------------------------------------------------------------------------------------------
		// Wirft unter keinen Umständen eine Ausnahme.
		//-----------------------------------------------------------------------------------------------------------
		INonCopyAssignable()
		{
			return;
		}

	private:
		//-----------------------------------------------------------------------------------------------------------
		// Nicht implementiert.
		//-----------------------------------------------------------------------------------------------------------
		INonCopyAssignable(INonCopyAssignable const &);
		
		//-----------------------------------------------------------------------------------------------------------
		// Nicht implementiert.
		//-----------------------------------------------------------------------------------------------------------
		INonCopyAssignable & operator=(INonCopyAssignable const &);
	};
}

namespace UnnamedProject
{
	//-----------------------------------------------------------------------------------------------------------
	// Eine unspezifische Ausnahme.
	//-----------------------------------------------------------------------------------------------------------
	class Exception : public std::exception
	{
	public:
		//-----------------------------------------------------------------------------------------------------------
		// Initialisiert die Ausnahme.
		//-----------------------------------------------------------------------------------------------------------
		explicit Exception(char const * description)
			: exception(description)
		{
			return;
		}

		//-----------------------------------------------------------------------------------------------------------
		// Gibt die Beschreibung zurück.
		// Wirft unter keinen Umständen eine Ausnahme.
		//-----------------------------------------------------------------------------------------------------------
		char const * GetDescription() const
		{
			return what();
		}
	};
}

namespace UnnamedProject
{
	namespace Windows
	{
		//-----------------------------------------------------------------------------------------------------------
		// Wirft eine Windows-spezifische Ausnahme.
		//-----------------------------------------------------------------------------------------------------------
		void ThrowException(char const * message, ::DWORD const error = ::GetLastError())
		{
			std::stringstream format;
			format 
				<< "Windows error [" 
				<< error 
				<< "] -> "
				<< message;
			
			throw Exception(format.str().c_str());
		}

		//-----------------------------------------------------------------------------------------------------------
		// Eine unspezifische Fensterklasse.
		//-----------------------------------------------------------------------------------------------------------
		class WindowClass : private INonCopyAssignable 
		{
		private:
			::HINSTANCE const myModuleInstance;
			std::wstring const myClassName;

		public:
			//-----------------------------------------------------------------------------------------------------------
			// Registriert die Fensterklasse.
			// Wirft eine Windows-spezifische Ausnahme bei einem Fehler.
			//-----------------------------------------------------------------------------------------------------------
			WindowClass(
				::HINSTANCE const & moduleInstance,
				::HICON const & icon,
				::HCURSOR const & cursor,
				::HBRUSH const & backgroundColor,
				::WNDPROC const & windowProcedure,
				::WCHAR const * className, // <---- UTF-16 String
				::WCHAR const * menuName, // <---- UTF-16 String
				::UINT const & style)
				
				: myModuleInstance(moduleInstance)
				, myClassName(className)
			{			
				::WNDCLASSEXW description;
				description.cbSize = sizeof(::WNDCLASSEXW);
				description.cbClsExtra = 0;
				description.cbWndExtra = 0;
				description.hInstance = moduleInstance;
				description.hIcon = icon;
				description.hIconSm = icon;
				description.hCursor = cursor;
				description.hbrBackground = backgroundColor;
				description.lpfnWndProc = windowProcedure;
				description.lpszClassName = className;
				description.lpszMenuName = menuName;
				description.style = style;

				if (::RegisterClassExW(&description) == 0)
					ThrowException("::RegisterClassExW() failed.");

				return;
			}

			//-----------------------------------------------------------------------------------------------------------
			// Meldet die Fensterklasse ab.
			// Wirft unter keinen Umständen eine Ausnahme.
			//-----------------------------------------------------------------------------------------------------------
			~WindowClass() 
			{			
				::UnregisterClassW(myClassName.c_str(), myModuleInstance);
				return;
			}
		};
	}
}

namespace UnnamedProject 
{
	namespace Windows
	{
		//-----------------------------------------------------------------------------------------------------------
		// Ein unspezifisches Fenster.
		//-----------------------------------------------------------------------------------------------------------
		class Window : private INonCopyAssignable 
		{
		private:
			::HWND myWindow;

		public:
			//-----------------------------------------------------------------------------------------------------------
			// Erstellt das Fenster.
			// Wirft eine Windows-spezifische Ausnahme bei einem Fehler.
			//-----------------------------------------------------------------------------------------------------------
			Window(
				::DWORD const style,
				::DWORD const extendedStyle,
				::WCHAR const * className, // <---- UTF-16 String
				::WCHAR const * windowName, // <---- UTF-16 String
				int const positionX,
				int const positionY,
				int const width,
				int const height,
				::HWND const & parentWindow,
				::HMENU const & menu,
				::HINSTANCE const & moduleInstance)

				: myWindow(::CreateWindowExW(
					extendedStyle, 
					className, 
					windowName, 
					style, 
					positionX, 
					positionY, 
					width, 
					height, 
					parentWindow,
					menu, 
					moduleInstance, 
					nullptr))
			{
				if (myWindow == nullptr)
					ThrowException("::CreateWindowExW() failed.");

				return;
			}

			//-----------------------------------------------------------------------------------------------------------
			// Zerstört das Fenster.
			// Wirft unter keinen Umständen eine Ausnahme.
			//-----------------------------------------------------------------------------------------------------------
			~Window() 
			{
				// Den Benutzerbereich des Fensters zurücksetzen. Dies, da in diesem möglicherweise eine Instanz
				// gespeichert ist welche einen Nachrichtenhandler aufruft.
				::SetWindowLongPtrW(myWindow, GWLP_USERDATA, reinterpret_cast<::LONG_PTR>(nullptr));
				::DestroyWindow(myWindow);
				return;
			}

			//-----------------------------------------------------------------------------------------------------------
			// Erlaubt das Manipulieren der Instanz als ::HWND Handle.
			// Wirft unter keinen Umständen eine Ausnahme.
			//-----------------------------------------------------------------------------------------------------------
			operator ::HWND & ()
			{
				return myWindow;
			}

			//-----------------------------------------------------------------------------------------------------------
			// Erlaubt das Verwenden der Instanz als ::HWND Handle.
			// Wirft unter keinen Umständen eine Ausnahme.
			//-----------------------------------------------------------------------------------------------------------
			operator ::HWND const & () const
			{
				return myWindow;
			}
		};
	}
}

namespace UnnamedProject 
{
	//-----------------------------------------------------------------------------------------------------------
	// Die Hauptanwendung.
	//-----------------------------------------------------------------------------------------------------------
	class Application : private INonCopyAssignable 
	{
	private:
		Windows::WindowClass const myWindowClass;
		Windows::Window myWindow;
		bool myRunningState;
		bool myValidState;

	public:
		//-----------------------------------------------------------------------------------------------------------
		// Initialisiert die Anwendung.
		//-----------------------------------------------------------------------------------------------------------
		explicit Application(::HINSTANCE const & moduleInstance)

			// Bevor ein Fenster erstellt werden kann, muss eine entsprechende Fensterklasse registriert werden.
			: myWindowClass(
				moduleInstance,
				::LoadIconW(nullptr, IDI_APPLICATION),
				::LoadCursorW(nullptr, IDC_ARROW),
				reinterpret_cast<::HBRUSH>(::GetStockObject(BLACK_BRUSH)),
				&Application::WindowProcedure,
				L"RenderWindow", // <---- UTF-16 ???
				nullptr,
				CS_DBLCLKS)

			, myWindow(
				WS_OVERLAPPEDWINDOW,
				WS_EX_APPWINDOW,
				L"RenderWindow", // <---- UTF-16 ???
				L"Unnamed Project", // <---- UTF-16 ???
				CW_USEDEFAULT,
				CW_USEDEFAULT,
				CW_USEDEFAULT,
				CW_USEDEFAULT,
				nullptr,
				nullptr,
				moduleInstance)

			, myRunningState(true)
			, myValidState(true)
		{
			// Dem Fenster die Anwendungsinstanz übergeben damit deren Nachrichtenhandler aufgerufen werden kann.
			::SetWindowLongPtrW(myWindow, GWLP_USERDATA, reinterpret_cast<::LONG_PTR>(this));
			::ShowWindow(myWindow, SW_SHOWNORMAL);
			::SetForegroundWindow(myWindow);

			// Nicht minimiert starten.
			if (::IsIconic(myWindow))
			{				
				::ShowWindow(myWindow, SW_RESTORE);
				::Sleep(100);
			}

			return;
		}

		//-----------------------------------------------------------------------------------------------------------
		// Führt die Anwendung aus.
		//-----------------------------------------------------------------------------------------------------------
		void Run() 
		{
			if (!myValidState)
				return;

			::MSG message = { 0 };

			while (myRunningState)
			{
				myValidState = false;

				// Alle verarbeiteten Nachrichten anschliessend entfernen.
				if (::PeekMessageW(&message, nullptr, 0, 0, PM_REMOVE))
				{
					::TranslateMessage(&message);
					::DispatchMessageW(&message);
				}
				else
					Update();

				// Keine Ausnahme aufgetreten.
				myValidState = true;
			}

			return;
		}

	private:
		//-----------------------------------------------------------------------------------------------------------
		// Die Nachrichtenprozedur des Fensters.
		// Wirft unter keinen Umständen eine Ausnahme.
		//-----------------------------------------------------------------------------------------------------------
		static ::LRESULT CALLBACK WindowProcedure(
			::HWND window,
			::UINT message,
			::WPARAM firstParameter,
			::LPARAM secondParameter)
		{
			// Die Anwendungsinstanz ermitteln damit deren Nachrichtenhandler aufgerufen werden kann.
			auto const application = reinterpret_cast<Application *>(::GetWindowLongPtrW(window, GWLP_USERDATA));

			// Wenn die Nachricht nicht weiterverarbeitet werden soll, '0' zurückgeben.
			if (application && !application->MessageHandler(message, firstParameter, secondParameter))
				return 0;

			// WM_CLOSE nicht weiterverarbeiten da ansonsten ::DestroyWindow() aufegrufen werden würde.
			// Dies aber erledigt bereits der Destruktor einer Windows::Window Instanz.
			if (message == WM_CLOSE)
				return 0;

			return ::DefWindowProcW(window, message, firstParameter, secondParameter);
		}

		//-----------------------------------------------------------------------------------------------------------
		// Verarbeitet die Fensternachrichten.
		// Wirft unter keinen Umständen eine Ausnahme.
		//-----------------------------------------------------------------------------------------------------------
		bool MessageHandler(::UINT const message, ::WPARAM const firstParameter, ::LPARAM const secondParameter)
		{
			switch (message) 
			{
			case WM_CLOSE:
				myRunningState = false;
				return false;
			}

			return true;
		}

		//-----------------------------------------------------------------------------------------------------------
		// Aktualisiert die Anwendung.
		//-----------------------------------------------------------------------------------------------------------
		void Update()
		{
			return;
		}
	};
}

//-----------------------------------------------------------------------------------------------------------
// Der Haupteinstiegspunkt.
// Wirft unter keinen Umständen eine Ausnahme.
//-----------------------------------------------------------------------------------------------------------
int WINAPI WinMain(::HINSTANCE moduleInstance, ::HINSTANCE, ::LPSTR, int)
{
	using namespace UnnamedProject;

	try 
	{
		Application application(moduleInstance);
		application.Run();
	}
	catch (std::exception const & exception) 
	{
		::MessageBoxA(
			HWND_DESKTOP,
			exception.what(),
			"Unnamed Project - Unhandled exception",
			MB_ICONERROR |
			MB_SETFOREGROUND);
	}
	catch (...) 
	{
		::MessageBoxA(
			HWND_DESKTOP,
			"System exception",
			"Unnamed Project - Unhandled exception",
			MB_ICONERROR |
			MB_SETFOREGROUND);
	}

	return 0;
}
Ich habe mich durch mehrere Unicode-Seiten geschlagen um mir einen Überblick zu verschaffen.
Soweit ich das verstanden habe, ist es durchaus möglich, einen UTF-8 kodierten String in einem std::string (const char *) abzuspeichern. Und ein std::wstring (const wchar_t *) ist einfach blos ein Widestring, welcher aber benutzt werden kann, um einen UTF-16 String zu speichern.

Nun bin ich auf zwei WinApi Funktionen gestossen (::WideCharToMultiByte() und ::MultiByteToWideChar()), welche UTF-8 sowie UTF-16 String erstellen können. Muss ich nun bei jedem Aufruf einer WinAPIW() Funktion einen std::string erstellen und diesen mit ::MultiByteToWideChar() in einen UTF-16 std::wstring umwandeln? Denn irgendwie scheint bei mir (Visual Studio 2012 RC) das C++ 11 Feature u"bla" nicht unterstützt zu werden.

Besten Dank

Re: Unicode in C++ und der WinAPI

Verfasst: 23.08.2012, 17:38
von eXile
Fitim hat geschrieben:Ich habe mich durch mehrere Unicode-Seiten geschlagen um mir einen Überblick zu verschaffen.
Soweit ich das verstanden habe, ist es durchaus möglich, einen UTF-8 kodierten String in einem std::string (const char *) abzuspeichern. Und ein std::wstring (const wchar_t *) ist einfach blos ein Widestring, welcher aber benutzt werden kann, um einen UTF-16 String zu speichern.
Ja, diese Sichtweise ist richtig. Ich habe einfach std::string<char> als UTF8String und std::string<wchar_t> als UTF16String getypedeft.
Fitim hat geschrieben:Nun bin ich auf zwei WinApi Funktionen gestossen (::WideCharToMultiByte() und ::MultiByteToWideChar()), welche UTF-8 sowie UTF-16 String erstellen können. Muss ich nun bei jedem Aufruf einer WinAPIW() Funktion einen std::string erstellen und diesen mit ::MultiByteToWideChar() in einen UTF-16 std::wstring umwandeln?
Wenn eine Funktion einen UTF8-String erwartet, muss du natürlich einen UTF8-String übergeben, und bei UTF16-Strings ebenso. Gegebenenfalls musst du Konvertierungen vornehmen. Ich würde vorschlagen, dass du dir mal UTF8-CPP-Library anschaust; ich habe damit sehr gute Erfahrungen gemacht.
Fitim hat geschrieben:Denn irgendwie scheint bei mir (Visual Studio 2012 RC) das C++ 11 Feature u"bla" nicht unterstützt zu werden.
Ja, mit der Einschätzung liegst du richtig.

Re: Unicode in C++ und der WinAPI

Verfasst: 23.08.2012, 18:12
von Fitim
Ahhh Danke.