Да, вы можете получить доступ к подпрограмме, которая вложена в другую (родительскую) подпрограмму из внешнего мира. Хотя это несколько сложно. Я нашел это руководство в Интернете.
Как передать вложенную подпрограмму в качестве процедурного параметра (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;
Очевидный обходной путь - передать адрес процедуры и типизировать его в пределах теста:
// 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;
Однако в приведенном выше примере есть ловушка - если «внутренняя» процедура ссылается на любую переменную, которая была помещена в стек до того, как «внутренняя» процедура была вызвана из 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 и "внутренней" процедуры
неправильно рассчитывает параметры и расположение локальных переменных
(не вините Дельфи, пожалуйста).
Обходной путь должен установить правильный контекст стека перед
«внутренний» вызывается изнутри «тестпасс».
// 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;
Обратите внимание, что оптимизация выключена для процедуры тестового прохождения - оптимизация обычно не очень хорошо обрабатывает смешанный код OP / ассемблера.