PDA

View Full Version : implementing a callback mechanism



JernejL
20-09-2006, 10:09 AM
i need help with adding callback mechanism to my program, i have this:

// global
var
CBackProc: Procedure(src: Longword); stdcall;

// a procedure
CBackProc:= Sources[src].DeleteCB; <-deletecb is pointer to a procedure, filled from another function

CBackProc(src); // call it

this causes my program to collapse and crash, any idea why? and perhaps what am i doing wrong?

jdarling
20-09-2006, 01:18 PM
From a quick glance, I don't see whats wrong. So I'll show you another way of doing it that is common:

unit test;

interface

type
TMyProc=procedure(SomeVar1, SomeVar2 : PChar); cdecl;

var
MyProc : TMyProc;

procedure LoadProcs;

implementation

procedure LoadProcs;
var
l : Cardinal;
begin
l := LoadLibrary('MyDLL.dll');
if l <> nil then
begin
@MyProc := GetProcAddress(l, 'FOO');
end;
end;

end.

Then later to call it:

MyProc('Test1', SomeOtherPChar);


While this is FPC source it should work for you. Also you might try taking a look at the Dr Bob's article (a bit dated, but still useful) at: http://www.drbob42.com/delphi/headconv.htm

JernejL
20-09-2006, 01:56 PM
no, what i am doing is right the opposite, and it has nothing to do with dlls, this will be used in a unit to notify main program that a sound source got deleted (within the application).. something for my audio library.

cairnswm
20-09-2006, 02:10 PM
Why not just make it a TNotifyEvent and use it as an event handler?

JernejL
20-09-2006, 02:50 PM
i don't use delphi's vcl... this is intended for raw coding..

cairnswm
20-09-2006, 02:53 PM
I dont use the VCL either but I still make use of TNotifyEvent :)

Maybe you need to make your own event handler then:


unit Unit1;

interface

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

Type
TMyNotify = Procedure(Value : Integer) of Object;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
FNotify: TMyNotify;
procedure SetNotify(const Value: TMyNotify);
{ Private declarations }
public
{ Public declarations }
Property Notify : TMyNotify read FNotify write SetNotify;
Procedure MyNotifyHandler(Value : Integer);
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
If Assigned(Notify) then
Notify(100);
end;

procedure TForm1.MyNotifyHandler(Value: Integer);
begin
Button1.Caption := IntToStr(Value);
end;

procedure TForm1.SetNotify(const Value: TMyNotify);
begin
FNotify := Value;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
Notify := MyNotifyHandler;
end;

end.

VilleK
20-09-2006, 02:55 PM
Why the need for stdcall? Have you tried without it?
Are you compiling in Delphi?

Try a simple example first, such as:

var
CBackProc: Procedure(src: Longword);

procedure test(src: Longword);
begin
WriteLn('Hello')
end;

begin
CBackProc:= Test;
CBackProc(src); // call it
end.

JernejL
20-09-2006, 03:23 PM
I'm doing just that, but i think the problem is storing the function reference in a pointer..

declarations:

Type
TDeleteNotifyproc = Procedure(src: Longword; userdata: pointer); stdcall;

Tsource = packed record
UserData: Pointer;
mode: TSourceMode;
ALsource: ALuint;
DeleteCB: TDeleteNotifyproc;
VirtualSource: Tvirtualsource;
end;

savage
20-09-2006, 03:40 PM
this works for me...


program Project1;

{$APPTYPE CONSOLE}

type
TDeleteNotifyproc = procedure( src : Longword; userdata : pointer ); stdcall;

TSource = packed record
DeleteCB : TDeleteNotifyproc;
end;

var
aSource : TSource;
aCallBack : TDeleteNotifyproc;

procedure test( src : Longword; userdata : pointer ); stdcall;
begin
WriteLn( 'Hello' )
end;

begin
aSource.DeleteCB := Test;
aCallBack := aSource.DeleteCB;
aCallBack( 0, nil ); // call it
end.


If you are doing everything within Pascal you really do not need the "stdcall" calling convention. If you plan to make it accessible to C and allow plugins then on windows use stdcall and on Linux I believe you need to use cdecl.

savage
20-09-2006, 03:49 PM
// a procedure
CBackProc:= Sources[src].DeleteCB; <-deletecb is pointer to a procedure, filled from another function


just looking back over this code are you sure that Sources[src].DeleteCB is pointing to a valid callback function?

to avoid the AV you could use some defensive programming and try

if Assigned ( aCallBack ) then
begin
aCallBack( 0, nil ); // call it
end
else
begin
WriteLn( 'No Callback function defined' )
end


Using "if aCallBack <> nil" will not work as the compiler treats that as an attempt to make a function/procedure call.

JernejL
20-09-2006, 05:30 PM
thanks for all the help, i got it working :)

i'll post the code, what i was doing here:
http://www.pascalgamedevelopment.com/forums/viewtopic.php?p=24603#24603

savage
20-09-2006, 07:51 PM
Can you tell us what the problem was so that some of us don't make the same mistake as well.

JernejL
20-09-2006, 10:26 PM
Can you tell us what the problem was so that some of us don't make the same mistake as well.

well if i am completely honest, i named a field in the record "DeleteCB" same as one of function parameters, and so the function was assigning the record's DeleteCB to the same variable instead grabbing it from parameter.. :oops:

i'd appreciate if people look at the other topic and test the audio management code a bit :)

savage
21-09-2006, 08:29 AM
Thanks for your honesty. We've all done stuff like that.