是的,你可以访问一个子程序,这是嵌套在其他(父)子例程,从外面的世界。虽然有点棘手。我在网上找到了这个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