Verbesserung der Umwandlung eines ANSI- in einen Wide-String gesucht


  • Mod

    Mein angebotenes Verfahren funktioniert solange Du eben nicht so einen Quatsch machst und Zeiger und Referenzen bedienst.



  • Martin Richter schrieb:

    Mein angebotenes Verfahren funktioniert solange Du eben nicht so einen Quatsch machst und Zeiger und Referenzen bedienst.

    O.k., gut. Dann sag mir doch bitte noch, wie die Konvertierungsfunktion letztendlich genau aussehen muß. Wenn ich es so mache, wie Du vorgeschlagen hast:

    #include <iostream>
    #include <windows.h>
    #include <atlbase.h>
    
    using namespace std;
    
    string Funktion()
    {
        string str = "Hallo";
    
        return str;
    }
    
    int main()
    {
        USES_CONVERSION;
        LPCWSTR winAPIString = A2W(Funktion().c_str());
    
        cout << winAPIString << '\n';
    }
    

    dann muß ich ja entweder Makros bemühen oder das, was sich hinter den Makros befindet, direkt hinschreiben. Zweitere Möglichkeit bedeutet allerdings, daß ich all die Sachen, die sich hinter den Makros befinden, jedesmal, bei jedem einzelnen String, manuell neu deklarieren muß (zum Beispiel die Variablen aus USES_CONVERSION).
    Wie würde das ganze also schlußendlich aussehen, wenn ich eine einzelne, für sich allein stehende Funktion haben will, die mir einen String in einen anderen konvertiert?
    Ich habe nun schon diverse Tips hier gelesen, aber ich habe noch nicht eine in sich geschlossene Funktion gesehen, die man von überall aufrufen kann. Wie müßte diese aussehen? Wie sieht eine ConvertString-Funktion aus, die intern vielleicht noch die Makros USES_CONVERSION und A2W/W2A benutzt (die Makros auflösen kann ich ja dann später selbst), mit der es aber nicht mehr nötig ist, diese Makros außerhalb (in der main oder einer anderen Funktion) noch irgendwo zu verwenden? Wie sieht eine ConvertString-Funktion aus, bei der ich in der main nur noch schreiben muß:

    LPCSTR str = NULL;
    
    /* ... */ ConvertString(/* ... */ L"Tadaaa! Es klappt!" /* ... */);
    
    MessageBoxA(str);
    // Ausgabe:
    // Tadaaa! Es klappt!
    

    Wenn Du mir sagen könntest, wie das geht, dann wäre ich Dir wirklich dankbar.



  • Wie würde das ganze also schlußendlich aussehen, wenn ich eine einzelne, für sich allein stehende Funktion haben will, die mir einen String in einen anderen konvertiert?

    Die Antwort wurde bereits mehrfach gegeben, nur du akzeptierst sie nicht.

    Es gibt in C++ keine Strings, also kannst du auch keine zurückgeben. Du kannst nur entweder einen Zeiger auf ein char Array zurückgeben, oder ein Objekt. Wenn du einen Zeiger auf ein char Array zurückgibst hast du ein Problem mit der Speicherverwaltung, und Objekte willst du nicht.
    Ergo: was du wünscht ist *unmöglich*.



  • O.k., ich hab es jetzt so geregelt, daß mir die Funktion einen String auf dem Heap erzeugt und zurückgibt. Dazu noch eine Delete-Funktion. Das ganze sieht so aus:

    LPSTR CreateANSIString(LPCWSTR str)
    {
    	if (str)
    	{
    		const UINT LENGTH = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
    		LPSTR newString = new CHAR[LENGTH];
    
    		WideCharToMultiByte(CP_ACP, 0, str, -1, newString, LENGTH, NULL, NULL);
    
    		return newString;
    	}
    	else
    		return NULL;
    }
    
    void DeleteANSIString(LPSTR str)
    {
    	if (str)
    		delete[] str;
    }
    

    Nur ist jetzt das Problem: Die Strings, die in Windows immer verwendet werden (z.B. WNDCLASSEXA::lpszClassName) sind vom Typ LPCSTR und die ließen sich nicht mit delete[] löschen. Was kann man da tun?


  • Mod

    Dann deklariere die zweite Funktion doch mit einem const Zeiger?

    PS: Ich Frage mich warum wir diese ganze Diskussion geführt haben.
    Nachträglich diese Funktion einzubauen ist mit Sicherheit genauso schwer, wie eine vernünftige Klasse zu verwenden!



  • Martin Richter schrieb:

    Dann deklariere die zweite Funktion doch mit einem const Zeiger?

    Hab ich. Dann hat er sich geweigert, das delete durchzuführen.

    Martin Richter schrieb:

    PS: Ich Frage mich warum wir diese ganze Diskussion geführt haben.
    Nachträglich diese Funktion einzubauen ist mit Sicherheit genauso schwer, wie eine vernünftige Klasse zu verwenden!

    Aber ich kann keine Klasse verwenden, weil die Strings, die ich benutze, nicht von mir selbst deklariert wurden. Es ging mir darum, sowas hier zu realisieren:

    WNDCLASSEXW wndClass;
    // ...
    wndClass.lpszClassName = L"Irgendein String";
    // ...
    
    if (/* OS unterstützt Unicode nicht*/)
    {
        WNDCLASSEXA wndClassA;
        // ...
        wndClassA.lpszClassName = Convert(wndClass.lpszClassName);
        // ...
        RegisterClassExA(&wndClassA);
    }
    else
        RegisterClassExW(&wndClass);
    

    In diesem Fall nützt es mir überhaupt nichts, irgendeine Wrapperklasse zu benutzen, da der umzuwandelnde String von Windows vorgegeben ist. Und ich kann ja nicht die WNDCLASSEX-Struktur umdefinieren.



  • CStringW strClassNameW = L"Irgendein String";
    CStringA strClassNameA = strClassNameW;
    
    WNDCLASSEXW wndClass;
    // ...
    wndClass.lpszClassName = strClassNameW;
    // ...
    
    if (/* OS unterstützt Unicode nicht*/)
    {
        WNDCLASSEXA wndClassA;
        // ...
        wndClassA.lpszClassName = strClassNameA;
        // ...
        RegisterClassExA(&wndClassA);
    }
    else
        RegisterClassExW(&wndClass);
    
        if (UnicodeIsSupported()) 
            return CreateWindowExW(/* ... */ strClassNameW /* ... */); 
        else 
            return CreateWindowExA(/* ... */ strClassNameA /* ... */);
    


  • Und was ist an der Idee, für jeden String noch einen weiteren lokalen String zu haben, jetzt besser?


  • Mod

    Deterministische Zerstörung! Keine Leaks!



  • Einfache Konvertierung und automatische Speicherfreigabe bei Scope-Ende.

    Alternativen:
    - Auf Unterstützung für Windows 9x/ME verzichten und nur noch Unicode benutzen
    - MSLU verwenden



  • sri schrieb:

    Alternativen:
    - Auf Unterstützung für Windows 9x/ME verzichten und nur noch Unicode benutzen

    Hm, klar. Vor allem, wenn man in 99% der Fälle überhaupt kein Unicode braucht. Ich finde es immer toll, wenn da steht, daß das Programm nicht funktioniert, nur weil darauf bestanden wird, daß der 08/15-String "Hauptfenster" als L"Hauptfenster" geschrieben wird.

    sri schrieb:

    - MSLU verwenden

    Ich halte es für inakzeptabel, bei einer 50 KB-Anwendung Abhängigkeiten zu erstellen, die nur aufgrund der Faulheit des Programmierers existieren.


  • Mod

    1. Mit korrekter T-Notation ist Unicode gar kein Problem.
    2. Arbeitet Windows sowieso nur in Unicode. Jeder Deiner MBCS Strings wird also extra für Dich umgewandelt.
    3. CreateWindow benötigt kein L"", CreateWindowW benötigt L"" Notation, CreateWindow "". Korrekte Schreibwweise ist also _T("") for CreateWindow
    4. Wenn Dich Unicode also nicht interessiert, warum wilst Du es dann bitte für die Windows API verwenden. Schalte es ab...
    Dann können wir uns in Zukunft solche überflüssigen Diskussionen sparen.



  • Ich weiß, daß ich den ganzen Ärger hätte vermeiden können, wenn ich einfach nur mit LPCTSTR gearbeitet hätte. Aber es geht mir um folgendes: Die A und die W-Versionen der Funktionen in der WinAPI sind ja vor dem Benutzer nicht versteckt. Das heißt, er kann wählen, ob er die A-, die W- oder die T-Version aufruft. Normalerweise benutzt man T. In dem Fall ist die Frage nach Unicode abhängig davon, ob man das UNICODE-Makro gesetzt hat. Aber es ist eben auch möglich, völlig aus diesem System auszubrechen und Funktionsaufrufe zu benutzen, die ANSI oder Unicode benutzen, ohne auf das Makro zu achten. Da man in der WinAPI also nicht gezwungen ist, seine Ausgabe vom UNICODE-Makro abhängig zu machen und auch solche Dinge möglich sind:

    void DreiMessageBoxen()
    {
        MessageBoxA(NULL, "ANSI", "ANSI", MB_OK);
        MessageBoxW(NULL, L"Unicode", L"Unicode", MB_OK);
        MessageBox(NULL, TEXT("Makroabhängig"), TEXT("???"), MB_OK);
    }
    

    wollte ich meinen Code (welcher ein paar allgemeingültige Wrapperklassen für Fenstererstellungen beinhaltet) eben nicht einfach nur mit T-Strings schreiben, sondern es dem Programmierer ermöglichen, selbst zu wählen, ob er abhängig vom UNICODE-Makro arbeitet oder den Datentyp hardcodiert. Deshalb biete ich Funktionen für LPCSTR und LPCWSTR an.



  • Ich arbeite bei aktuellen und neuen Projekten nur noch mit Variante 2. Die Zeiten von Windows 9x/ME sind glücklicherweise vorbei, zudem laufen mit VS 2008 erstellte Programme eh nur noch ab Windows 2000. Da kann man dann auch gleich Unicode verwenden.


  • Mod

    sri schrieb:

    Ich arbeite bei aktuellen und neuen Projekten nur noch mit Variante 2. Die Zeiten von Windows 9x/ME sind glücklicherweise vorbei, zudem laufen mit VS 2008 erstellte Programme eh nur noch ab Windows 2000. Da kann man dann auch gleich Unicode verwenden.

    Ich habe immer noch Teile des Codes, die bewusst auf MBCS basieren: Große Textdatenmengen sind eben doch nur halb so groß. Aber es spricht ja nichts dagegen, wchar_t zu verwenden, wo man es will und char da wo es eben mehr Sinn macht.



  • sri schrieb:

    Ich arbeite bei aktuellen und neuen Projekten nur noch mit Variante 2. Die Zeiten von Windows 9x/ME sind glücklicherweise vorbei

    Diese Einstellung hasse ich. Einerseits preist Ihr die Plattformunabhängigkeit, andererseits stellt Ihr Eure Projekte bewußt so ein, daß sie unter alten Betriebssystemen nicht mehr laufen. Wenn es noch wenigstens einen anständigen Grund gäbe, wäre es ja nicht so schlimm. Aber wenn man es nur aus Prinzip macht, ist das eine scheiß Einstellung. Denn ich frage Dich: Gibt es in Deinen Programmen wirklich Unicode-Zeichen? Arbeitest Du mit anderen Zeichen als dem ASCII-Zeichensatz? Wenn nicht, dann sehe ich auch keinen Grund, prinzipiell Unicode zu verwenden.

    sri schrieb:

    zudem laufen mit VS 2008 erstellte Programme eh nur noch ab Windows 2000.

    Ernsthaft? Auch simple Konsolenanwendungen?



  • NES-Spieler schrieb:

    Diese Einstellung hasse ich. Einerseits preist Ihr die Plattformunabhängigkeit, andererseits stellt Ihr Eure Projekte bewußt so ein, daß sie unter alten Betriebssystemen nicht mehr laufen. Wenn es noch wenigstens einen anständigen Grund gäbe, wäre es ja nicht so schlimm. Aber wenn man es nur aus Prinzip macht, ist das eine scheiß Einstellung. Denn ich frage Dich: Gibt es in Deinen Programmen wirklich Unicode-Zeichen? Arbeitest Du mit anderen Zeichen als dem ASCII-Zeichensatz? Wenn nicht, dann sehe ich auch keinen Grund, prinzipiell Unicode zu verwenden.

    Wer heute immer noch Windows 9x/ME einsetzt, der muss damit leben, dass aktuelle Programme hier nicht mehr laufen. Man kann nicht ein völlig verstaubtes Betriebssystem mit dem Anspruch einsetzen, bis in die Ewigkeit mit neuer Software versorgt zu werden. Die Windows-API wird mit jeder Version umfangreicher und es macht nicht wirklich Spaß, für alte Krücken jedesmal einen Würgaround programmieren zu müssen. Fast alle Anwender haben heute XP oder Vista, ein kleiner Teil verwendet noch Windows 2000. Irgendwann muss man halt mal einen Strich machen. Oder unterstützt Du noch Windows 3.1?

    Wenn Deine Programme außerhalb des deutsch-/englischsprachigen Raums verwendet werden, dann ist schon der erste Unicode-Dateiname Grund genug, eine Unicode-Version anzubieten.

    NES-Spieler schrieb:

    sri schrieb:

    zudem laufen mit VS 2008 erstellte Programme eh nur noch ab Windows 2000.

    Ernsthaft? Auch simple Konsolenanwendungen?

    Der Linker setzt die Mindestversion in der .EXE auf 5.00, was Windows 2000 voraussetzt. Frühere Windows-Versionen zeigen daher beim Start immer eine Fehlermeldung an.



  • Ausserdem ist das (2000, XP, etc.) Unicode basiert.
    D.h. bei WinAPI Aufrufen wird sowiso am Ende Unicode verwendet.



  • Denn ich frage Dich: Gibt es in Deinen Programmen wirklich Unicode-Zeichen?

    Jedes Programm kann mit Unicode Zeichen konfrontiert werden sobald es z.B. mit Dateinamen arbeitet, die bei allen NT basierten Windows Versionen ja schliesslich Unicode Zeichen enthalten können.
    Allein das ist für mich schon ein Grund bei den meisten Programmen nurmehr Unicode zu verwenden.



  • ich hasse programme, die nicht mit unicode klarkommen. denk mal über deinen kleinen horizont hinaus NES-Spieler, bevor du hier andere anmachst.


Anmelden zum Antworten