My application has to provide the ability of calling different functions and procedures from external DLLs. So we don't know parameters' count and their types. What should I do to do this?
Let me explain it more. My application is a RAD tool and it has its own scripting and syntax... I want to let users to use ANY dll file and call any function or procedure they want. I can't use the simple method of calling dll (LoadLibrary
and then GetProcAddress
) because I don't know what type the GetProcAddress
refers to ( var Proc:procedure (A:??;B:??;...)
).
I have a Delphi implementation in the scripting functionality of my ZGameEditor-project, search for "TExpExternalFuncCall.Execute" in the file below:
http://code.google.com/p/zgameeditor/source/browse/trunk/ZExpressions.pas
Tested and working under Windows (x86 and x64), Linux, Android (ARM) and OS X (x86). Handles stdcall and cdecl calling conventions.
But libFFI is probably way more general than my implementation so I would recommended that approach.
This is a simple example that works on my machine, but I am not an expert on the subject.
procedure TForm4.Button1Click(Sender: TObject);
var
hmod: HMODULE;
paddr: pointer;
c1, c2, ret: cardinal;
begin
c1 := 400; //frequency
c2 := 2000; // duration
hmod := LoadLibrary('kernel32'); // Of course, the name of the DLL is taken from the script
if hmod <> 0 then
try
paddr := GetProcAddress(hmod, 'Beep'); // ...as is the name of the exported function
if paddr <> nil then
begin
// The script is told that this function requires two cardinals as
// arguments. Let's call them c1 and c2. We will assume stdcall
// calling convention. We will assume a 32-bit return value; this
// we will store in ret.
asm
push c2
push c1
call [paddr]
mov ret, eax
end;
end;
finally
FreeLibrary(hmod);
end;
end;
What you are describing is known as a Foreign Function Interface (FFI) and is not for the feint of heart.
I would not recommend that you attempt to develop your own FFI from scratch. A very common choice of FFI is libffi.
The Wikipedia page for libffi lists the following projects as users of libffi:
Python, Dalvik, F-Script, PyPy, PyObjC, RubyCocoa, JRuby, Rubinius, MacRuby, gcj, GNU Smalltalk, IcedTea, Cycript, Pawn, Squeak, Java Native Access, PLT Scheme, Embeddable Common Lisp and Mozilla.
I personally make extensive use of libffi through a Python/ctypes interface to my Delphi DLL, although thankfully Python/ctypes wraps it up at quite a high level.
If I were setting off down the route you describe, I would strongly consider using libffi. If you take that route you'll have to do some work to be able to use it from Delphi since it is written in C/asm.
As David H says, about FFI, it's hardly for the faint of heart.
However, you could use the source code, for example, to the Python ctypes extension modules for FFI, as a source of information on how libFFI (ctypes) is bound to a particular syntax (in this case python). THe python source code, and its standard modules, are very readable.
Here is an example of using the libraries David mentions, in Python:
http://code.activestate.com/recipes/146847/
Since the sources for python (in C) are available, and since Python itself can be an extension in Delphi, you could use that to start with. If you are up to writing your own complete dynamic language (as part of your RAD tool), then you are up to the challenge of FFI too.
I am personally not up for the challenge of inventing a complete, workable programming language and all its libraries, from scratch, so I prefer to hybridize what I know, together. Native code in C or Delphi, and dynamic scripts in Python. You can combine all three easily into a single application, as needed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With