delphi 类型转换无效:在64位平台上将记录转换为对象

7eumitmz  于 2023-10-18  发布在  其他
关注(0)|答案(4)|浏览(120)

它在32位平台上工作。但不是64位这里是exzample

TVerbInfo = packed record
    Verb: Smallint;
    Flags: Word;
  end;

var
  VerbInfo: TVerbInfo;
  strList : TStringList;
  verb : Smallint;
  flags : Word;
begin
  strList := TStringList.create();
  .....
  verbInfo.verb := verb;
  verbInfo.flags := flags;
  strList.addObject('verb1',TObject(VerbInfo));  //invalid typecast happened here
end;

有人能帮我吗?非常感谢

laximzn5

laximzn51#

你可以试试这样的方法:

function MakeVerbInfoObject(const AVerbInfo: TVerbInfo): TObject;
begin
  Result := nil;
  Move(AVerbInfo, Result, SizeOf(AVerbInfo));
end;

strList.addObject('verb1', MakeVerbInfoObject(VerbInfo));

要检索该值,可以使用相应的函数,如

function GetVerbInfoFromObject(AObject: TObject): TVerbInfo;
begin
  Move(AObject, Result, SizeOf(Result));
end;

VerbInfo := GetVerbInfoFromObject(strList.Objects[idx]);
5w9g7ksd

5w9g7ksd2#

你的强制转换TObject(VerbInfo)将编译,前提是SizeOf(TObject) = SizeOf(TVerbInfo)。但是TObject是一个指针,所以它的大小随体系结构而变化。另一方面,SizeOf(TVerbInfo)不随架构而变化。因此,演员阵容只能在一个架构上工作。
在泛型出现之前的 Delphi 中,就必须这样使用强制转换。但是现在,你应该使用通用容器。
例如,如果你有一个列表,并且字符串是唯一的,那么你可以使用字典:

TDictionary<string, TVerbInfo>

如果有可能存在重复的字符串,那么你需要一个新的记录声明:

type
  TVerbInfo = record
    Name: string
    Verb: Integer;
    Flags: Word;
  end;

然后将这些的列表存储在

TList<TVerbInfo>

最后一点是你应该避免使用打包记录。这会导致数据结构不对齐,进而导致性能低下。

dced5bon

dced5bon3#

我认为你必须在不同的平台上运行这个并比较结果

ShowMessage( IntToStr( SizeOf( Integer ) ) );
ShowMessage( IntToStr( SizeOf( Pointer ) ) );
ShowMessage( IntToStr( SizeOf( TVerbInfo ) ) );
ShowMessage( IntToStr( SizeOf( TObject ) ) );

我怀疑你不能做一个硬铸件,因为大小不同。
您可以尝试使用以下解决方法,

type TBoth = record
  case byte of
    0: ( rec: TVerbInfo);
    1: ( obj: TObject);
  end;

您也可以尝试使用TDictionary<String, TVerbInfo>类型而不是TStringList

pdkcd3nj

pdkcd3nj4#

我昨晚尝试了Uli的代码,它不会工作,因为MakeVerbInfoObject不会给予你一个有效的记录指针。相反,它将Verb和Flags字段的值复制到Result中(显然这不是我们想要的)。

type
  PVerbInfo= ^TVerbInfo;
  TVerbInfo = packed record
    Verb: Smallint;
    Flags: Word;
  end;

function MakeVerbInfoObject(const AVerbInfo: TVerbInfo): TObject;
begin
  Result := nil;
  Move(AVerbInfo, Result, SizeOf(AVerbInfo));
end;

procedure Test;
var
   VerbInfo: TVerbInfo;
   List: TObjectList;
begin
  VerbInfo.Verb:= 7;
  VerbInfo.Flags:= 8;

  List:= TObjectList.Create(false);
  List.Add(MakeVerbInfoObject(VerbInfo));

  // AV here!!!!!!!!
  var i:= PVerbInfo(List[0])^.Verb;

  FreeAndNil(List);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Test2;
end;

但是,这将工作(在32/64位上测试):

Type
 PVerbInfo2= ^TVerbInfo2;
 TVerbInfo2= packed record
   Verb: SmallInt;
   Flags: Word;
 end;

procedure Test;
var
   VerbInfo: TVerbInfo2;
   List: TObjectList;
begin
  VerbInfo.Flags:= 7;
  VerbInfo.Verb:= 8;

  List:= TObjectList.Create(false);

  //List.add(Pointer(VerbInfo));    // "E2089 Invalid typecast" when compiling on 64 bit

  List.Add(TObject(@VerbInfo));

  // This gives correct values:
  var i := PVerbInfo2(List[0])^.Verb; // Inspect "I"

  FreeAndNil(List);
end;

但是如果你有一个支持泛型的 Delphi 版本,你绝对应该去做。代码更好,更安全:

procedure TestMineBetter;
var
   VerbInfo: TVerbInfo2;
   List: TList<TVerbInfo2>;
begin
  VerbInfo.Flags:= 7;
  VerbInfo.Verb:= 8;

  List:= TList<TVerbInfo2>.Create;
  List.Add(VerbInfo);
  var i := List[0].Verb;

  FreeAndNil(List);
end;

相关问题