I am trying to call a DLL function at runtime. I know the Name of the DLL, of the FUNCTION, and the PARAMETERS which need to be passed to it. The only problem is that I do NOT know these items at the compile time of the App. They will be retrieved from an INI file.
So far: I used LoadLibrary to get a handle to the DLL, I used GetProcAddress to get the address of the Function. Now I have a TFarProc which is pointing to the function and I am STUCK!!!! :-(
A:
What you are trying to do is hard in a compiled language. I think you will have to drop to assembler for the actual function call.
I would go at it this way:
Do the LoadLibrary and GetProcAddress stuff as you do now, that is the easy part <g>. Then you need to evaluate the parameters and construct an image of the call stack frame in a buffer. That can be done in Pascal code without problem. Wrap the whole mess into a TStack object, for example, that has a Push method like
Procedure TStack.Push( Var data; datasize: Word );
A quick sketch of such a beast would perhaps look like this:
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;
{ Methods of TStack }
Function TStack.GetFree: Word;
Begin
Result := FSize - FUsed;
End; { TStack.GetFree }
Constructor TStack.Create( size: Word );
Begin
inherited Create;
FSize := size;
GetMem( FPtr, size );
FStackTop := FPtr;
Inc( PtrRec( FStackTop ).ofs, size );
End; { TStack.Create }
Destructor TStack.Destroy;
Begin
FreeMem( FPtr, size );
End; { TStack.Destroy }
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 { If }
Else
raise Exception.Create( 'Stack full!'
);
End; { TStack.Push }
So you create an instance of this animal and Push your parameters onto the stack. The actual call would then look something like this:
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
{ add sp, size only if DLLProc is cdecl
and size <> 0! }
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!