我正在尝试将Delphi 5代码迁移到Delphi XE7-WIN64,并且在以下块中遇到了混合汇编代码的问题。我也是新手。
procedure IterateMenus(Func: Pointer; Menu1, Menu2: TElMenuItem);
var
I, J: Integer;
IIndex, JIndex: Byte;
Menu1Size, Menu2Size: Integer;
Done: Boolean;
function Iterate(var I: Integer; MenuItem: TElMenuItem; AFunc: Pointer): Boolean;
var
Item: TMenuItem;
begin
if MenuItem = nil then Exit;
Result := False;
while not Result and (I < MenuItem.Count) do
begin
Item := MenuItem[I];
if Item.GroupIndex > IIndex then Break;
asm
MOV EAX,Item
MOV EDX,[EBP+8]
PUSH DWORD PTR [EDX]
CALL DWORD PTR AFunc
ADD ESP,4
MOV Result,AL
end;
Inc(I);
end;
end;
begin
I := 0;
J := 0;
Menu1Size := 0;
Menu2Size := 0;
if Menu1 <> nil then
Menu1Size := Menu1.Count;
if Menu2 <> nil then
Menu2Size := Menu2.Count;
Done := False;
while not Done and ((I < Menu1Size) or (J < Menu2Size)) do
begin
IIndex := High(Byte);
JIndex := High(Byte);
if (I < Menu1Size) then
IIndex := Menu1[I].GroupIndex;
if (J < Menu2Size) then
JIndex := Menu2[J].GroupIndex;
if IIndex <= JIndex then
Done := Iterate(I, Menu1, Func)
else
begin
IIndex := JIndex;
Done := Iterate(J, Menu2, Func);
end;
while (I < Menu1Size) and (Menu1[I].GroupIndex <= IIndex) do
Inc(I);
while (J < Menu2Size) and (Menu2[J].GroupIndex <= IIndex) do
Inc(J);
end;
end;
我正在尝试将 asm 块转换为 purepascal,因为 delphi x64 不允许混合代码,并且 asm 对程序员不友好。
据我了解,项目的地址已移至EAX,然后我什么也得不到。EBP从何而来?什么是 ESP 和 AL?上面的代码片段来自ELPack的ELmenus.pas。
那么基本上什么是 asm 代码块的 PurePascal 版本?
对于函数,我发现这个
procedure TElMenuItem.UpdateItems;
function UpdateItem(MenuItem: TElMenuItem): Boolean;
begin
Result := False;
IterateMenus(@UpdateItem, MenuItem.FMerged, MenuItem);
MenuItem.SubItemChanged(MenuItem, MenuItem, True);
end;
begin
IterateMenus(@UpdateItem, FMerged, Self);
end;
procedure TElMenuItem.PopulateMenu;
var
MenuRightToLeft: Boolean;
function AddIn(MenuItem: TElMenuItem): Boolean;
begin
MenuItem.AppendTo(FHandle, MenuRightToLeft);
Result := False;
end;
begin // all menu items use BiDiMode of their root menu
{$ifdef VCL_4_USED}
MenuRightToLeft := (FMenu <> nil) and FMenu.IsRightToLeft;
{$else}
MenuRightToLeft := false;
{$endif}
IterateMenus(@AddIn, FMerged, Self);
end;
我猜原始程序员编写了奇怪的代码来绕过不支持本地函数指针的Delphi。(这是一个有时让我恼火的限制。
以下应该有效。(虽然我不确定我的 asm 那么敏锐。我将首先通过单步执行 D5 中的调试器来测试 asm 代码,以确认行CALL DWORD PTR AFunc
传递了我预期的值。
1) Delcare 一个函数指针类型并移动 UpdateItem 使其不再是本地的。
type
TMenuOperation = function (AMenuItem: TElMenuItem): Boolean;
//Or if you want to move the UpdateItem to a method of a class..
//TMenuOperation = function (AMenuItem: TElMenuItem): Boolean of object;
2) 进行以下更改:
procedure IterateMenus(Func: Pointer; Menu1, Menu2: TElMenuItem);
//becomes
procedure IterateMenus(Func: TMenuOperation; Menu1, Menu2: TElMenuItem);
//...
function Iterate(var I: Integer; MenuItem: TElMenuItem; AFunc: Pointer): Boolean;
var
Item: TMenuItem;
//becomes
function Iterate(var I: Integer; MenuItem: TElMenuItem; AFunc: TMenuOperation): Boolean;
var
Item: TElMenuItem;
//...
Item := MenuItem[I];
//becomes
Item := MenuItem[I] as TElMenuItem;
3)最后汇编块变成:
Result := AFunc(Item);
该 asm 代码正在调用 Item
上的方法。我想说的是,无论谁编写代码,都需要检查他们的头脑。就好像他们不知道方法指针一样。
我解决这个问题的方法是查找调用函数的代码,并查看作为Func
参数传递的内容。这就是asm
块所称呼的。将 Func
参数的类型更改为适当的过程类型,并将 asm
块替换为对该方法指针的调用。
好的,现在我看到原始代码在本地过程中快速而松散地播放。您应该做的是Func
成为匿名方法类型:
Func: TFunc<TElMenuItem, Boolean>;
然后将本地函数转换为匿名方法。
升级
德尔福版本的常用方法是同时升级第三方库。如果你这样做,那么我想你不需要移植 15 年前的图书馆代码。