PDA

View Full Version : Executing procedure by it address



Darkhog
04-05-2009, 05:50 PM
OS/Hardware: irrelevant, the question is pure technical - no errors
IDE: Lazarus
Api: None.
-------------------------------
I already posted this on Lazarus forum, but I don't got any response that I can use (this will very important for PlayMaker runtime)

Let's say that I have name of procedure or function as string. How to get procedure address of that function/proc and then execute it (normally I will use proc(parameters), but how with calling by proc address?). I do not want to checking it in long if..else, because procedures will be lot.

The only answer that I got was that I should use dynamic library and GetProcAddress. But was is the sense for making library that will be used only once, in one program?

AthenaOfDelphi
04-05-2009, 07:36 PM
Well, if the method is part of a class and you know it's name, you can get the address. You obviously need to know the function prototype so the compiler can validate the call, but it is relatively straight forward, and I wrote a tutorial about this thats available in the library here (http://www.pascalgamedevelopment.com/PGDLib/TTCF3).

In terms of standard routines, it is also possible, this time using getProcAddress. Here's an example:-


unit formMain;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure method1;
begin
showMessage('Hello world 1');
end;

procedure method2;
begin
showMessage('Hello world 2');
end;

type
TMyMethod = procedure;

procedure TForm1.Button1Click(Sender: TObject);
var
mp : TMyMethod;
begin
@mp:=GetProcAddress(getModuleHandle(nil),'method1' );

mp;
end;

exports
method1,
method2;

end.


The key thing with this is the exports section. If you don't include it, it will go bang.

Hope this helps.

Darkhog
04-05-2009, 08:34 PM
This will works on Linux too?

AthenaOfDelphi
04-05-2009, 08:41 PM
This will works on Linux too?


Didn't consider Linux to be honest, and I'm not able to say yes or no. The method used in my tutorial for accessing class methods should work as a similar mechanism should exist I believe. The getProcAddress approach... I can't say. Theoretically, if the calls exist it will be possible... if they don't then there may be an alternative you can use.

Of course much of this could potentially be irrelevant, depending on what it is you are trying to achieve exactly, and how this will be used. If you don't want to openly discuss it, drop me a PM or email, but some more details about what it is you are trying to do with this mechanism may present other options.

Darkhog
04-05-2009, 08:56 PM
Nope. Procedure getModuleHandle don't exist in LAZARUS. And please don't give me code for Delphi, because this simply don't work, on Linux :P ;D :D

pjpdev
04-05-2009, 09:16 PM
I'll see if I can come up with something... :D

AthenaOfDelphi
04-05-2009, 09:46 PM
Nope. Procedure getModuleHandle don't exist in LAZARUS. And please don't give me code for Delphi, because this simply don't work, on Linux :P ;D :D


First up, I don't see a massive number of replies, and given you've already asked the question on the Lazarus forums and gotten no replies... any information you get should be gratefully received as a possible solution or even better, a pointer to possible solutions.

Secondly, your statement about code for Delphi not working on Linux. Thats not strictly true. I have a cross platform (Windows/Linux) service that compiles with Delphi and Kylix, the wrapper is different as one is a service whilst the other is a daemon, but the core code itself is the same, so it's not that Delphi (or more accurately Object Pascal) won't work on Linux, it's a case of some of the features available within Delphi, namely the Windows API do not have equivalent functions under Linux. So, this rules out the example code I provided which makes use of getProcAddress... but did you read the article I pointed you to?

That utilises methodAddress, which of course means you can only find the address of a method of a class, but it DOES work with Free Pascal. Lazarus is built on Free Pascal is it not?

I hope you can see now why I asked for more information about what and how you were going to use it. The code I presented is more flexible in that you aren't limited to class methods, but it may not work everywhere, whilst the methodAddress approach is limited to class methods, but it does work everywhere because its a core language feature.

In future I would suggest you adopt a slightly more grateful tone when replying to someone who is trying to help. If you don't, people will pretty quickly stop helping.

So, I'll reiterate my questions... how are you planning on using this? Can the routines you want to call be class methods? If not, why not? If they can, the methodAddress approach can be used... although from a performance point of view, there may be better approaches. A more object orientated method for example.

pjpdev
04-05-2009, 10:05 PM
Hah!!!! Found it... It's almost the same as Athena's example....

First off... add dynlibs to your uses clause...

Now...


var
Form1: TForm1;
Adress: TLibHandle; //This is very important.

implementation

procedure Method1;
begin
ShowMessage('Hello world 1');
end;

procedure Method2;
begin
ShowMessage('Hello World 2');
end;

type
TMyMethod = procedure; stdcall;

procedure TForm1.Button1Click(Sender: TObject);
var
mp: TMyProcedure;
begin
mp := TMyProcedure(GetProcAdress(Adress, 'Method1'));

mp;
end;

//This is very important, declare exports for each exported method...
exports Method1 name 'Method1';
exports Method2 name 'Method2';

end.



And viola... I got it right, the GetProcAdress is in the dynlibs unit...

And I have to thank the FPC mailing lists for this...

IMO, it would be best to develop a dynamic library (*.so/*.dll) that contains the needed methods for your Game Development IDE. And then call from that.

Darkhog
05-05-2009, 04:54 PM
Thanks, PJP Dev. I'll try it. I will post it on Lazarus forums. Did you notice problems with DB? SMF get something like this very often, so i don't use it (some time ago I had to use it, and this wasn't good experience)

//EDIT: It's give me design-time error exports clause only allowed in libs. Can you post whole unit, please?
//EDIT #2: Silly me - I put exports before initialization.
//EDIT #3: Identifier not found: TLibHandle

pjpdev
05-05-2009, 06:55 PM
Thanks, PJP Dev. I'll try it. I will post it on Lazarus forums. Did you notice problems with DB? SMF get something like this very often, so i don't use it (some time ago I had to use it, and this wasn't good experience)

//EDIT: It's give me design-time error exports clause only allowed in libs. Can you post whole unit, please?
//EDIT #2: Silly me - I put exports before initialization.
//EDIT #3: Identifier not found: TLibHandle


First off... I didn't get any problems with SMF on my site yet, but thats a bit off-topic.

Solution for EDIT #1: Comment the exports out while placing components on the form. This is the reason why I suggest making a dynamic library.

Solution for EDIT #2: I did it and it worked for me... Nothing wrong with that.

Solution for EDIT #3: Did you add dynlibs to the uses clause?

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls, dynlibs;

TLibHandle is in dynlibs, so you'll need to add it.

Here is my full source...


unit Unit1;

{$mode objfpc}{$H+}

interface

uses
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls, dynlibs;

type

{ TForm1 }

TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ private declarations }
public
{ public declarations }
end;

var
Form1: TForm1;
adress: TLibHandle;


implementation

procedure Method1;
begin
ShowMessage('Hello world 1');
end;

procedure Method2;
begin
ShowMessage('Hello World 2');
end;

type
TMyMethod = procedure; stdcall;

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
var
mp: TMyMethod;
begin
mp := TMyMethod(GetProcAddress(adress, 'Method1'));

mp;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
mp: TMyMethod;
begin
mp := TMyMethod(GetProcAddress(adress, 'Method2'));

mp;
end;

exports Method1 name 'Method1';
exports Method2 name 'Method2';

initialization
{$I Unit1.lrs}

end.

Darkhog
05-05-2009, 07:27 PM
Yep., I added dynlibs. Eh... I suppose should do dynamic library... But I didn't use dynamic linking before. Have I to make different code for windows libraries and linux?

pjpdev
05-05-2009, 07:43 PM
Yep., I added dynlibs. Eh... I suppose should do dynamic library... But I didn't use dynamic linking before. Have I to make different code for windows libraries and linux?


I haven't tested this on linux yet. And that source I showed is what I did. No dynamic libraries used. IMO you shouldn't need to write seperate code for Windows and Linux, unless you use system dependent code.

But if you use dynamic linking then the Adress variable in my example will be used to load the library.


var
Form1: TForm1;
Adress: TLibHandle;

...

procedure TForm1.FormCreate(Sender: TObject);
begin
Adress := LoadLibrary('mylib.dll');
//OR
Adress := LoadLibrary('mylib.so');
end;


Toy around and see what you can come up with... :)

Darkhog
05-05-2009, 07:59 PM
Thanks for advice. This will be used later. For now I must get SDL to work and make designer