是的,你可以訪問一個子程序,這是嵌套在其他(父)子例程,從外面的世界。雖然有點棘手。我在網上找到了這個howto。
如何通過嵌套例程作爲程序參數(32位)
的Delphi通常不支持將嵌套例程作爲程序參數:
// This code does not compile:
procedure testpass(p: tprocedure);
begin
p;
end;
procedure calltestpass;
procedure inner;
begin
showmessage('hello');
end;
begin
testpass(inner);
end;
顯而易見的解決方法是通過程序地址並在testpass內部進行類型轉換:
// This code compiles and runs OK
procedure testpass(p: pointer);
begin
tProcedure(p);
end;
procedure calltestpass;
procedure inner;
begin
showmessage('hello');
end;
begin
testpass(@inner);
end;
但是, e上面的例子 - 如果「內部」例程引用了從testpass調用「內部」過程(calltestpass參數 - 如果存在任何或calltestpass中的本地變量 - 如果有)的任何變量被推入棧中的任何變量,你的系統最有可能崩潰:
// This code compiles OK but generates runtime exception (could even be
// EMachineHangs :-))
procedure testpass(p: pointer);
begin
tProcedure(p);
end;
procedure calltestpass;
var msg: string;
procedure inner;
begin
msg := 'hello';
showmessage(msg);
end;
begin
testpass(@inner);
end;
的原因是,在簡單的話,該堆棧幀安排 是「破」通過調用testpass常規和「內部」的過程 正確計算參數和局部變量的位置 (請不要怪Delphi)。 解決方法是在「testpass」內調用「inner」之前設置正確的堆棧上下文。
// This code compiles and runs OK
{$O-}
procedure testpass(p: pointer);
var callersBP: longint;
begin
asm // get caller's base pointer value at the very beginning
push dword ptr [ebp]
pop callersBP
end;
// here we can have some other OP code
asm // pushes caller's base pointer value onto stack and calls tProcedure(p)
push CallersBP
Call p
Pop CallersBP
end;
// here we can have some other OP code
end;
{$O+}
procedure calltestpass;
var msg: string;
procedure inner;
begin
msg := 'hello';
showmessage(msg);
end;
begin
testpass(@inner);
end;
請注意,對於testpass例程,優化被關閉 - 優化通常不能很好地處理混合的OP /彙編代碼。
私人私人:)你是否正在尋找一種方式使其公開而不更改代碼? – mjn 2010-05-19 14:39:18
它甚至不是私人的,它是內部的。它不存在於嵌入的「父」例程之外。 – 2010-05-19 17:25:25