Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Unterschied von PChar zwischen DelphiXE und Delphi2007


chrissi68 - Do 27.10.11 18:58
Titel: Unterschied von PChar zwischen DelphiXE und Delphi2007
Hallo,

unter Delphi2007 hatte ich ein Programm zur Kommunikation mit RFID-Antennen geschrieben. Die externe Dll besitzt folgenden Funktionsaufruf:

Delphi-Quelltext
1:
function FECOM_SetPortPara(iPortHnd: Integer; cPara: PChar; cValue: PChar): Integer; stdcallexternal 'Fecom.DLL';                    


Aufgerufen habe ich die Funktion mit:

Delphi-Quelltext
1:
2:
3:
4:
5:
var
  rBaudRate: PAnsiChar;
  .
  .
      cRet:=FECOM_SetPortPara(Reader.iPortHnd, 'Baud', rBaudRate);


Mittlerweile benutze ich Delphi XE und das Programm funzt nicht mehr :? weil PChar jetzt Unicode-fähig ist. Anscheinend soll PChar mit PWideChar ersetzt werden. Dies
habe ich auch gemacht.....


Delphi-Quelltext
1:
function FECOM_SetPortPara(iPortHnd: Integer; cPara: PWideChar; cValue: PWideChar): Integer; stdcallexternal 'Fecom.DLL';                    


und die Variable "var rBaudRate: PWideChar" entsprechend geändert.
Leider klappt auch das nicht :? .

Ersetze ich aber PWideChar durch PAnsiChar........

Delphi-Quelltext
1:
2:
3:
4:
function FECOM_SetPortPara(iPortHnd: Integer; cPara: PAnsiChar; cValue: PAnsiChar): Integer; stdcallexternal 'Fecom.DLL';
.
.
cRet:=FECOM_SetPortPara(Reader.iPortHnd, 'Baud''38400');


.... und rufe die Function mit dem statischen Wert auf, so klappt die Übergabe an die DLL!! :?: :?:

Vielleicht hat von Euch jemand eine Idee wie man die Parameterübergabe in Delphi XE hin bekommt, wäre klasse!

Viele Grüße

Chrissi


bummi - Do 27.10.11 19:28

der einzige Fehler dürfte sein dass Du rBaudRate: PAnsiChar; auf eine String nicht auf einen AnsiString zeigen lässt.
Wenn Du für Para und Value AnsiStrings nimmst und diese über para[1] oder PAnsiChar(para) übergibst sollte es durchlaufen.
Vielleicht kannst Du an dem Beispiel sehen was ich meine

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
Procedure ShowIt(p:PAnsiChar);
begin
  Showmessage(p);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  s:String;
  s2:AnsiString;
begin
  s := 'Test';
  s2 := 'Test';
  ShowIt(PAnsichar(s));
  ShowIt(PAnsichar(s2));
end;


Delete - Do 27.10.11 19:49

Wenn die DLL AnsiChars haben will, dann sollte man ihr auch welche geben, weil eventuell kommt sie mit WideChars nicht zu recht.


bummi - Do 27.10.11 20:11

Fecom.DLL und Ferwa.DLL sind definitiv steinnalt


jaenicke - Fr 28.10.11 06:19

user profile iconchrissi68 hat folgendes geschrieben Zum zitierten Posting springen:
Anscheinend soll PChar mit PWideChar ersetzt werden. Dies
habe ich auch gemacht.....
Um das einmal klarzustellen:
Nein, das ist falsch. Bis Delphi 2007 entsprach PChar = PAnsiChar. Seit Delphi 2009 entspricht PChar = PWideChar.

PChar in einer Schnittstellendefinition für eine DLL zu benutzen, war aber schon immer ein Programmierfehler, da es nicht dem konkreten Typ entspricht. Das gilt genauso für andere Metatypen wie Integer, Boolean usw., die dort auch nichts zu suchen haben. Stattdessen sollte immer ein konkreter Typ wie DWord, WordBool, ... benutzt werden um exakt zu definieren welche Größe die Datentypen haben.

Dies wurde auch schon viele Jahre vor der Umstellung auf Unicode gesagt, aber viele haben nicht darauf gehört.
(Wie auch auf die Warnungen vor dem Schreiben ins Exe-Verzeichnis z.B., beides mussten diejenigen mit der UAC in Vista bzw. Unicode in Delphi dann eben auf die harte Tour lernen.)

Wenn du jedenfalls in einer alten Schnittstelle PChar findest, ist dort immer PAnsiChar gemeint. An der Stelle musst du dann aber nach dem Ersetzen auch die Warnungen beachten, denn bei einem Cast von string (= UnicodeString ab Delphi 2009) auf PAnsiChar sollte eine Warnung vor einer bedenklichen Typumwandlung kommen.


chrissi68 - Fr 28.10.11 10:41

IHR SEID GROSSARTIG!!!!!!!!!!!!! :D :D :D :D :D :D :D :D :D :D

Vielen herzlichen Dank, jetzt funzt meine Function wieder!
Habe die PChar durch PAnsiChar ersetzt und übergebe jetzt AnsiString. Die Rückgabe erfolgt jetzt durch ein Array of AnsiChar, früher Array of Char

Hier der vollständige Code:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
function TFTestRFID.OpenRFIDPort(var Reader: TReader):boolean;
var
  cRet            : Integer;
  cBaud           : Array[0..127of AnsiChar;
  cTimeOut        : Array[0..127of AnsiChar;
  rBaudRate       : AnsiString;
  rFrame          : AnsiString;
  rTimeout        : AnsiString;
  rTxTimeControl  : AnsiString;
  rTxDelayTime    : AnsiString;
  rCharTimeoutMpy : AnsiString;

begin
  Result:=True;
  cRet:=0;
  cBaud:=#0;
  cTimeOut:=#0;
  rBaudRate:='';
  rFrame:='';
  rTimeOut:='';
  rTxTimeControl:='';
  rTxDelayTime:='';
  rCharTimeoutMpy:='';
  cRet:=FECOM_DetectPort(Reader.iPortNr);
  if cRet = 0 then
  begin
    Reader.iPortHnd:=FECOM_OpenPort(PAnsiChar(IntToStr(Reader.iPortNr)));
    if Reader.iPortHnd >= 0 then
    begin
      rBaudRate:=IntToStr(Reader.iBaudRate);
      rFrame:=Reader.sFrame;
      rTimeout:=IntToStr(Reader.iTimeout);
      rTxTimeControl:=IntToStr(1);
      rTxDelayTime:=IntToStr(10);
      rCharTimeoutMpy:=IntToStr(5);
      cRet:=FECOM_SetPortPara(Reader.iPortHnd, 'Baud', PAnsiChar(rBaudRate));
      cRet:=FECOM_SetPortPara(Reader.iPortHnd, 'Frame', PAnsiChar(rFrame));
      cRet:=FECOM_SetPortPara(Reader.iPortHnd, 'Timeout', PAnsiChar(rTimeout));
      cRet:=FECOM_SetPortPara(Reader.iPortHnd, 'TxTimeControl', PAnsiChar(
        rTxTimeControl));
      cRet:=FECOM_SetPortPara(Reader.iPortHnd, 'TxDelayTime', PAnsiChar(
        rTxDelayTime));
      cRet:=FECOM_SetPortPara(Reader.iPortHnd, 'CharTimeoutMpy', PAnsiChar(
        rCharTimeoutMpy));
      cRet:=FECOM_GetPortPara(Reader.iPortHnd, 'Baud', cBaud);
      cRet:=FECOM_GetPortPara(Reader.iPortHnd, 'Timeout', cTimeout);
      DisplayRFIDData('Baudrate            : ' + cBaud);
      DisplayRFIDData('Timeout             : ' + cTimeout);
      DisplayRFIDData('TxTimeControl       : ' + rTxTimeControl);
      DisplayRFIDData('TxDelayTime         : ' + rTxDelayTime);
      DisplayRFIDData('CharTimeoutMpy      : ' + rCharTimeoutMpy);
      DisplayRFIDData('Frame               : ' + rFrame);
    end
    else
      Result:=False;
  end
  else
    Result:=False;
end;


@bummi: Ja, die DLL's sind uralt, aber soweit ich weiss hat sich an der Funktionalität im Wesentlichen auch nichts großartiges geändert und
sie funktionieren noch immer, wenn nicht Embarcadero die Datentypen ändert........... :roll:

Viele Grüße

Chrissi


jaenicke - Fr 28.10.11 11:54

user profile iconchrissi68 hat folgendes geschrieben Zum zitierten Posting springen:
wenn nicht Embarcadero die Datentypen ändert........... :roll:
Embarcadero hat sicher schon einiges falsch gemacht, aber hier nicht. Es wurden nur die Typen geändert, die dafür vorgesehen waren, wie gesagt. Dass in der Definition der DLL-Schnittstelle die falschen Typen (eben die Metatypen statt der konkreten Typen) verwendet wurden, dafür kann Embarcadero ja nichts... :roll:

Es hat eben einen Grund weshalb schon immer (gut, bei den ganz alten Versionen wie Delphi 1 weiß ich es nicht) in Delphi einmal die Unicodevariante einer API-Funktion und einmal die Ansivariante eingebunden wurde und die ohne A oder W am Ende (die man in der Regel benutzt, z.B. ShellExecute) auf die entsprechende gemappt wird (eben auf A am Ende bis Delphi 2007, z.B. ShellExecuteA, und auf W am Ende ab Delphi 2009, z.B. ShellExecuteW).