-2

我现在决定将我的 Delphi 7 项目转换为 XE4 ,但是在我的代码行之一中,我遇到了一个女巫问题,我试图修复它但没有希望,所以我希望有人可以帮助我修复它。

这是问题:

在服务器和客户端应用程序(酒店客房管理系统)之间使用的共享单元中,我有这个记录类型:

      Type
      THotelClientDetails = packed record
      LSize:  Integer;
      ClientName:  array[0..25] of char;
      ClientRoomN:  Integer;
      RWithInternet: Boolean;
      RoomStatus    :Integer;
    //... etc 
      end;
          PHotelClientDetails = ^THotelClientDetails;

在客户端应用程序中,我使用以下程序:

procedure TCForm.SendClientDetailsClick(Sender: TObject);
  var
  pClientDetails: PHotelClientDetails;
  iSize: Integer;
  begin

  iSize:= SizeOf(THotelClientDetails)+Length(ClientNameEd.Text)+1;
  GetMem(pClientDetails,iSize);
  ZeroMemory(pClientDetails,iSize);
  pClientDetails.LSize := iSize;
  StrCopy(pClientDetails.ClientName,PChar(ClientNameEd.Text));
  pClientDetails.ClientRoomN  :=StrToInt(ClientNEd.text);
  pClientDetails.RWithInternet:=ClientWInternet.Checked;
  pClientDetails.RoomStatus   :=ClientRoomStatus.ItemIndex;
  StrCopy(Pointer(Cardinal(pClientDetails)+SizeOf(THotelClientDetails)),
  PChar(ClientNameEd.Text));
  SendClientsBuffer(pClientDetails,iSize);// to the Server for Check
  FreeMem(pClientDetails);

  end;

在服务器应用程序中,我使用以下程序:

Procedure TSForm.GetClientDetails(pClientDetails:PHotelClientDetails; Cntx: Pointer);
  var
  ClientName: string;
  begin
  ClientName:=PChar(Cardinal(pClientDetails)+SizeOf(THotelClientDetails));
  //*** just a test to get the ClientName
  ShowMessage(ClientName);
  //***
  end;

所以我的问题是在使用 Delphi 7 时,我得到了客户端应用程序发送的全名:例如,如果我想将客户端“simon”或“matthew”发送到服务器

我得到了正确的名字:

ShowMessage(ClientName);//simon or matthew

但是当在 XE4 中使用相同的程序时,我总是得到

simsimonmattmatthew

这意味着服务器没有像 Delphi7 项目那样接收完整的客户端名称。

虽然在 Both Projects 中添加了单元“System.AnsiStrings;”。

那么请问我该如何解决这个问题?

非常感谢。

西蒙

4

2 回答 2

3

Delphi 7 使用 ANSI 字符串(单字节字符),而 Delphi 2009 及更高版本使用 Unicode 字符串(多字节字符)。

对您的代码最直接的修复是从Charto AnsiCharstringtoAnsiStringPCharto更改PAnsiChar

Type
  THotelClientDetails = packed record
    LSize:  Integer;
    ClientName:  array[0..25] of AnsiChar;
    ClientRoomN:  Integer;
    RWithInternet: Boolean;
    RoomStatus    :Integer;
    //... etc 
  end;

StrCopy(pClientDetails.ClientName, PAnsiChar(ClientNameEd.Text));
// and
StrCopy(Pointer(Cardinal(pClientDetails) + SizeOf(THotelClientDetails)),
        PAnsiChar(ClientNameEd.Text));

Procedure TSForm.GetClientDetails(pClientDetails:PHotelClientDetails; Cntx: Pointer);
var
  ClientName: string;
begin
  ClientName := PAnsiChar(Cardinal(pClientDetails)+SizeOf(THotelClientDetails));
  //*** just a test to get the ClientName
  ShowMessage(ClientName);
  //***
end;

实际上有几十个(如果不是数百个)与将 Delphi 代码从 D2007 及更早版本移植到 D2009 及以后版本有关的问题。您应该花一些时间在此处浏览标签。

于 2013-08-31T23:53:46.597 回答
1

我将其发布为答案,因为它比对问题的评论更容易格式化。

关于您可能想要做的事情的一些评论:

  1. 使用 Delphi 2009 及更高版本中的 Unicode 支持,不要以为这Length会得到字符串的实际字节数。
  2. 在 Delphi 7 和 XE4 之间,引入了关于记录的概念操作方法(我认为在 Delphi 2006 中)。将部分逻辑移动SendClientDetailsClickTHotelClientDetails
  3. 由于THotelClientDetails.ClientName限制为 26 个字节(25AnsiChar个字符字节加上一个空终止字节),因此不需要
    iSize:= SizeOf(THotelClientDetails)+Length(ClientNameEd.Text)+1;
    这意味着iSize:= SizeOf(THotelClientDetails).
  4. 你有一个指针的唯一原因pClientDetails: PHotelClientDetails是你调用 SendClientsBuffer(pClientDetails,iSize);// to the Server for Check
    它可以替换为
    SendClientsBuffer(ClientDetails,iSize);// to the Server for Check
  5. 没有长度保护来防止在
    StrCopy(pClientDetails.ClientName,PChar(ClientNameEd.Text));
    Use StrLCopy中复制超过 25 个字节,而不是StrCopy
  6. 为什么要执行ClientNameEd两次复制操作?
    StrCopy(pClientDetails.ClientName,PChar(ClientNameEd.Text));
    //...
    StrCopy(Pointer(Cardinal(pClientDetails)+SizeOf(THotelClientDetails)), PChar(ClientNameEd.Text));
  7. 如果您坚持使用指针,则FreeMem应该在一个finally块中。

像这样的东西更合适:

procedure TCForm.SendClientDetailsClick(Sender: TObject);
var
  ClientDetails: PHotelClientDetails;
  iSize: Integer;
begin
  iSize := SizeOf(THotelClientDetails);
  ZeroMemory(@ClientDetails, iSize);
  ClientDetails.LSize := iSize;
  StrLCopy(ClientDetails.ClientName, PAnsiChar(ClientNameEd.Text), SizeOf(ClientDetails.ClientName)-1);
  pClientDetails.ClientRoomN   := StrToInt(ClientNEd.text);
  pClientDetails.RWithInternet := ClientWInternet.Checked;
  pClientDetails.RoomStatus    := ClientRoomStatus.ItemIndex;
  SendClientsBuffer(@ClientDetails,iSize); // to the Server for Check
end;
于 2013-09-01T08:59:09.000 回答