函数调用 ( 积分: 200 )

  • 主题发起人 主题发起人 amwfhv
  • 开始时间 开始时间
A

amwfhv

Unregistered / Unconfirmed
GUEST, unregistred user!
我有一程序建立了一树型,需要根据用户选择树型的分支值来确定调用不同的函数,
现在不想使用case,if等,希望直接从数据库中根据树型选择的值读入函数名来执行,应该怎么做?

分不够再加
 
var
P: Pointer;
Proc: TNotifyEvent;
begin
P := MethodAddress(ProcName); //根据函数名获取地址函数地址,ProcName从数据库读入
if P <> nil then //如果找到相应的函数则执行
begin
TMethod(Proc).Code := P;
TMethod(Proc).Data := Self;
Proc(Self);
end;
end;

函数要声明在Published中才行
 
你用的控件是Tfctreeview吗,如果是的话在这个控件中指定相应节点的stringGrid值就可以了
 
转贴,字符串执行函数:

来自: ghc_x, 时间: 2004-07-22 17:26:12, ID: 2725092
昨天晚上回家走在路上的时候,觉得这个问题比较有意思,就试着实现了一下,还是比较有意思的。为了简单起见,我在这里把要转换的代码改成
showmessage('c');
而不是楼主的
str:=inttostr(12345);
但原理都是一样的。
在这里说明一下,showmessage('c');为运行时要转换的代码,将该代码写在一个名为a.txt的文本文件的第一行,并将该文件和应用程序放在同一目录下。’c’为要显示的字符串,可以随时更改该字符串的内容(比如说可以改成 ’a’ 或 ‘z’ 什么的,如果改成大于1个字符的字符串那么只能显示该字符串的第一个字符,这个限制只是因为我比较懒:)而且代码看上去会简单一些,所以没有实现整个字符串的拷贝,和动态编译没有任何关系)以显示动态编译的效果。现在我们就可以把这个文本文件看作是一个procedure的实现代码,只不过该procedure只有一行代码showmessage('c');而已。新建一个工程,在form1上放一个按钮,点击该按钮则动态编译a.txt中的代码。好了,代码伺候:

//在单元的interface部分声明一个过程类型,该过程类型的变量就代表整个a.txt所代表
//的那个procedure,该变量其实是一个指针,指向procedure的代码存放地址。然后执行该
//变量程序流程就转到该地址了。其实还有一种实现方法就是在delphi中写嵌入式汇编,直
//接写call指令也能实现代码的跳转,这里用的是声明过程类型的方法。
TPro = procedure();


procedure TForm1.Button1Click(Sender: TObject);
var
F: TextFile;
S: string;
a: TPro;
b: integer;
c: string;
d: pointer;
e: pointer;
begin
AssignFile(F, 'a.txt'); //打开存有代码的文本文件
Reset(F);
Readln(F, S); //读取该文件第一行
CloseFile(F);
b := pos('showmessage', s); //定位代码的在该行中的起始位置
if b <> 0 then
begin
c := copy(s, b + 13, 1); //只拷贝字符串的第一个字符,偷了一点懒:)
d := @showmessage; //获得要showmessage的地址
e := AllocMem(11); //在堆上分配一个11个字节的内存块,
//我们动态编译后
//的代码就放在这里
pointer(@a) := e; //这里就是将过程类型的变量赋值为动态编译代码的
//地址
pbyte(integer(e))^ := $B8; //从这里就开始用procedure的机器码填充内存块
ppointer(integer(e) + 1)^ := ppointer(c); //获得要显示字符串的地址
pbyte(integer(e) + 5)^ := $E8;
pinteger(integer(e) + 6)^ := integer(d) - (integer(e) + 10);
//计算出showmessage函数的代码地址与当前地址之间的偏移量
pbyte(integer(e) + 10)^ := $C3;

//填充完内存块后,该11个字节内存块的布局应该是这样的:
//B8 XX XX XX XX E8 YY YY YY YY C3
//其中XX XX XX XX 表示要显示字符串的地址,YY YY YY YY 表示showmessage函数
//的代码地址与当前地址之间的偏移量。接下来看看这段代码的意思:
//第一句B8 XX XX XX XX 表示将eax寄存器赋值为XX XX XX XX
//第二句E8 YY YY YY YY 表示调用一个子程序也就是showmessage,子程序的地址为
//当前地址加上YY YY YY YY 。第三句C3表示返回调用该代码(这11个字节)的地方。
//再回头看第一句,就表示将showmessage的参数放入eax(delphi中过程的调用规则是将第
//一个参数传入eax)第二句就开始调用showmessage。
a; //就从这里,程序的流程就跑到刚才建立的那11个字节的代码了,最后的C3
//执行完又返回到这里
freemem(e); //释放分配的内存块
end;
end;
这就是一个最简单的动态编译的例子,总结一下,就是要分配一块地址空间,将动态编译后的机器码放入该地址空间,再使程序流程进入到这里。微软的.net框架的JIT就是这么实现的,.net框架在你调用某个过程的时候才将该过程的IL中间码(以文本形式存在)编译为机器码然后跳到刚才编译完的机器码去执行。
 
var
P: Pointer;
Proc: TNotifyEvent;
begin
P := MethodAddress(ProcName); //根据函数名获取地址函数地址,ProcName从数据库读入
if P <> nil then //如果找到相应的函数则执行
begin
TMethod(Proc).Code := P;
TMethod(Proc).Data := Self;
Proc(Self);
end;
end;

函数要声明在Published中才行

确实可以执行声明在published中的函数
引伸一下
如果我的函数是在另外一个单元,并没有在类里面,我是利用这个函数来将该窗体创建出来

function fun_searchcenter():Boolean;

implementation
function fun_searchcenter():Boolean;
begin
Result:=True;
try
with TFrmSCenter.Create(application) do
begin
//------------这里处理一些东西
Show;
end;
finally
end;
end;
像这种情况又该如何动态的调用他
 
希望解决上面的问题
分不够再帖
 
执行其他单元的函数,用第一种方法,你可以增加一个类函数,然后在类函数内调用目标函数。
用我转贴的方法,则只需引用单元然后用@符号取得函数地址就行了啊。
至于参数问题,楼主自行琢磨一下了。
 
UFO!你转的方法我试了一下,也不行取不到函数的地址
 
如果引用了单元,没有取不到地址的道理,你有跟踪看过吗
类似d := @showmessage;这样的代码,返回值是多少?
 
后退
顶部