Unicode in C++ und der WinAPI

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Fitim
Beiträge: 12
Registriert: 27.05.2012, 19:29

Unicode in C++ und der WinAPI

Beitrag 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
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: Unicode in C++ und der WinAPI

Beitrag 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.
Fitim
Beiträge: 12
Registriert: 27.05.2012, 19:29

Re: Unicode in C++ und der WinAPI

Beitrag von Fitim »

Ahhh Danke.
Antworten