Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call a procedure or function when we are not aware of the parameters?

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:??;...) ).

like image 253
Javid Avatar asked Mar 18 '11 21:03

Javid


4 Answers

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.

like image 192
Ville Krumlinde Avatar answered Nov 18 '22 16:11

Ville Krumlinde


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;
like image 4
Andreas Rejbrand Avatar answered Nov 18 '22 14:11

Andreas Rejbrand


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.

like image 3
David Heffernan Avatar answered Nov 18 '22 16:11

David Heffernan


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.

like image 3
Warren P Avatar answered Nov 18 '22 15:11

Warren P