Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Wie C-Funktion korrekt nach XE4 umsetzen ?


OlafSt - Mi 21.08.13 14:22
Titel: Wie C-Funktion korrekt nach XE4 umsetzen ?
Hallo Freunde,

heute mal ein triviales Problem. Ich habe hier ein SDK, natürlich in C geschrieben. Nun müssen an verschiedene Routinen des SDK Zeichenketten übergeben werden. Natürlich sind diese allesamt als "char *" deklariert.

Greifen wir uns einfach eine solche Routine heraus:

Quelltext
1:
2:
void UserLoggingMessageEvent(char *logmessage, int Loglevel, char* logChannel, uint64 logID, 
                             char* logTime, char* completeLogString);


In den neueren Delphi-Versionen sind nun alle Strings Unicode-Strings. Diese Strings dem SDK zu fressen zu geben geht erwartungsgemäß schwer in die Hose, ebenso sind die Rückgabewerte vom Typ char* ebenso Schrott. Immerhin haben wir damit schon mal herausgefunden, das das SDK kein Unicode unterstützt ;)

Nun wird es spannend. Ich übersetze obige Deklaration wie folgt:

Delphi-Quelltext
1:
2:
procedure UserLoggingMessageEvent(const logmessage: PAnsiChar; logLevel: integer; const logChannel: PAnsiChar; 
                                  logID: uint64; const logTime: PAnsiChar; const completeLogString: PAnsiChar); cdecl;


Kein Problem. Aber sämtliche Zeichenketten müssen nun so übergeben werden:

Delphi-Quelltext
1:
UserLoggingMessageEvent(PAnsiChar(myMessage[1]),1,PAnsiChar(myChannel[1]),1234,'','');                    


Benutze ich nicht die Indizierung [1], wirft der Compiler massenhaft Warnungen aus: W1044 Bedenkliche Typumwandlung von string in PAnsiChar. Hat er ja auch recht, das wahre ist das Konstrukt nicht.

Ich habe nun überlegt, statt PAnsiChar einfach PAnsiString zu verwenden. Damit verschwinden die Warnungen. Aber nun sind plötzlich folgende Aufrufe unmöglich:

Delphi-Quelltext
1:
UserLoggingMessageEvent('Debug Message',1,'Schwarzer Kanal',1234,'','');                    

E2010 Inkompatible Typen 'PAnsiString' und 'string'. Typecasten nach PAnsiString hilft auch nix, ergibt einen E2089 Ungültige Typumwandlung. Auch doppeltes Typecasten PAnsiString(AnsiString('Schwarzer Kanal')) bringt das gleiche Resultat.

Gibt es eine Übersetzung nach Delphi XE4, ohne Myriaden an Typecasts zu machen oder die Warnung abschalten zu müssen ?


WasWeißDennIch - Mi 21.08.13 17:43

Und wenn Du myMessage, myChannel etc. als AnsiString deklarierst? Dann müsste es doch auch ohne die Indizierung funktionieren.


jaenicke - Mi 21.08.13 19:51

Das sollte auch ohne Warnungen gehen:

Delphi-Quelltext
1:
PAnsiChar(AnsiString(MeinString));                    


Martok - Do 22.08.13 00:02

PAnsiString ist schonmal ganz falsch, das ist nämlich ein Zeiger auf einen Delphi-String.

Diese Warnung W1044 gibt es analog auch bei FreePascal, dort lasse ich die tatsächlich meistens unterdrücken. Nutzwert ist 0, wenn man anfängt wild in der Gegend rumzucasten sollte man eh vorher drüber nachdenken. Da brauch ich keine Compilermeldung zu ;)


jaenicke - Do 22.08.13 07:14

user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
Nutzwert ist 0, wenn man anfängt wild in der Gegend rumzucasten sollte man eh vorher drüber nachdenken.
Das ist nicht richtig, denn da viele Quelltexte aus der Zeit vor Delphi 2009 stammen, ist diese Meldung sehr wichtig, wenn man solchen Quelltext dann unter 2009+ kompiliert. Sonst müsste man die Casts manuell prüfen... denn es gibt leider viele Casts in nicht so guten Quelltexten auf PAnsiChar beim Aufruf von APIs obwohl nicht explizit die ...A Variante benutzt wurde...


OlafSt - Do 22.08.13 10:20

user profile iconWasWeißDennIch hat folgendes geschrieben Zum zitierten Posting springen:
Und wenn Du myMessage, myChannel etc. als AnsiString deklarierst? Dann müsste es doch auch ohne die Indizierung funktionieren.


Die Idee ist nicht schlecht und habe ich auch gleich ausprobiert. Bei Routinen wie der genannten funktioniert das ganze auch prima. Aber leider nicht bei allen. Da gibt es dann auch noch solche bösen Dinger:


Quelltext
1:
unsigned int getDefaultPlayBackMode(char **result);                    


Hier gibt es einen Fehlercode als Ergebniswert (oder die übliche 0, wenn alles okay ist). Der DefaultPlaybackMode wird aber als Zeichenkette in result zurückgegeben. Diese Zeichenkette wird vom SDK alloziert und muß später mit einer eigenen SDK-Funktion wieder freigegeben werden. AnsiString ist hier also nicht möglich. Damit schließt sich auch ein var result: AnsiString aus.

In diesem SDK gibt es noch wesentlich beklopptere Konstrukte mit char ****result. Kein Witz - und spätestens hier ist klar, welches SDK ich hier gerade beackere ;)

Zitat:
PAnsiString ist schonmal ganz falsch, das ist nämlich ein Zeiger auf einen Delphi-String.


PAnsiString ist tatsächlich ein Zeiger auf einen Delphi-String, nämlich einen AnsiString. Und der läßt sich prima als nullterminierter String behandeln. Vermutlich war hier ein ShortString gemeint (die Dinger, die nur 255 Zeichen umfassen). Die sind hier wirklich unbrauchbar.

Weitere Ideen ?


jaenicke - Do 22.08.13 13:12

user profile iconOlafSt hat folgendes geschrieben Zum zitierten Posting springen:
Die Idee ist nicht schlecht und habe ich auch gleich ausprobiert. Bei Routinen wie der genannten funktioniert das ganze auch prima. Aber leider nicht bei allen. Da gibt es dann auch noch solche bösen Dinger:


Quelltext
1:
unsigned int getDefaultPlayBackMode(char **result);                    
Da musst du einen echten PAnsiChar nehmen. Da du den Speicher dazu auch nicht allozieren musst, reicht das auch schon. Übergeben tust du das dann einfach als variablen Parameter.