在已知函数名称和参数的情况下,静态调用和动态调用一个函数都是很简单的,但是如果我不知道这个函数的参数(编译的时候),但是在运行的时候,可以给出一个函数的声明到一个文件里面,那么这个时候如何去调用这个函数呢?
///Kingron测试的时候,发现用下面的方法可以做到,但是其中有一个PtrRec没有定义,而且会出现非法操作~~~~~~:(,不过你可以用简单的语句来测试:
asm
push 100; ///Push进去的参数值和个数可以动态改变的
push 200;
call windows.beep; /// Call可以调用一个指针,因此也是可以动态改变的
end;
就是这个动态的改变需要用汇编来做,很麻烦~~~~~~~~~:(,因为必须判断参数个数和类型,之于调用那个函数倒是很简单
一般来说,凡是编译型的语言很难做到这一点,不过可以采取特殊的方法来做到它。用Delphi也可以做到,但是需要用到汇编代码。
当然,主要的问题是如何调用那些参数,至于函数名称和从DLL中读取都是很简单的事情,用LoadLibrary和GetProcAddress就可以做到了,在此不予讨论。只要解决了参数的个数和传递的问题,那么我们的问题就解决了,我们知道,函数的参数实际上都是一个堆栈来管理的,在调用函数之前,把参数压栈,然后Call那个函数即可,因此,明白了这个道理,那么我们就可以用一个自己创建的堆栈来进行模拟~~~~~~~~~
首先我们需要一个堆栈类,用于管理函数的参数:
Type
TStack= Class
private
FPtr: Pointer;
FSize: Word;
FStackTop: Pointer;
FUsed: Word;
Function GetFree: Word;
public
Constructor Create( size: Word );
Destructor Destroy; override;
Procedure Push( Var data; datasize: Word );
Property StackTop: Pointer read FStackTop;
Property Free: Word read GetFree;
Property Used: Word read FUsed
end;
Function TStack.GetFree: Word;
Begin
Result := FSize - FUsed;
End;
Constructor TStack.Create( size: Word );
Begin
inherited Create;
FSize := size;
GetMem( FPtr, size );
FStackTop := FPtr;
Inc( PtrRec( FStackTop ).ofs, size );
End;
Destructor TStack.Destroy;
Begin
FreeMem( FPtr, size );
End;
Procedure TStack.Push( Var data; datasize: Word );
Begin
If Free >= datasize Then Begin
Dec( PtrRec( FStackTop ).ofs, datasize );
Move( data, FStackTop^, datasize );
Inc( FUsed, datasize );
End
Else
raise Exception.Create( 'Stack full!' );
End;
接下来我们必须创建一个堆栈类的实例,然后把那些参数压栈,最后调用函数即可,那么调用的代码可能类似下面的:
Var
Stack: TStack;
size : Word;
Top : Pointer;
Begin
..create stack and fill it
size := Stack.Used;
Top := Stack.StackTop;
asm
mov cx, size
jcxz @noParams
mov ax, ss
mov es, ax
sub sp, cx
mov di, sp
push ds
lds si, Top
cld
rep movsb
pop ds
@noParams:
call DLLProc
end;
end;
One caveat: TStack.Push should raise an exception if you try to push data with an odd datasize; the CPU stack uses WORD as the smallest unit so only data with an even datasize can be pushed; odd size items like Char and Byte have to be extended to Words first!