Board index » delphi » DLL call a function in the calling app?

DLL call a function in the calling app?

Is it possible for a DLL to call a function in a calling application?
For example, the code to read and write to a com port is in the DLL.
When something arrives at the port, the DLL has to decode the message,
and send it to the calling app.

Possible?

 

Re:DLL call a function in the calling app?


Quote
Craig Lister wrote in message

<84cc68cf.0204120028.5db2a...@posting.google.com>...

Quote
>Is it possible for a DLL to call a function in a calling application?
>For example, the code to read and write to a com port is in the DLL.
>When something arrives at the port, the DLL has to decode the message,
>and send it to the calling app.

>Possible?

Quite possible. The mechanism is named "callback" and the
idea is that the calling program supplies the DLL with a
function pointer to call in response to a certain event
(like data received on the COM port).

The issues are exactly the same as when a program calls an
function imported from a DLL: calling convention, memory
spaces for buffers.

What you could do is define a variable in the DLL to hold
the function pointer, publish Get and Set access functions
to it[0], and have the program set an event handler^W^Wcallback
function before it initiates the serial communication. Look
familiar?

This is borrowed shamelessly from the Delphi event construction,
which is simply one way of doing it. C programmers would more
likely pass the callback function as an extra parameter to the
Write call, but that is of course equivalent.

Groetjes,
Maarten Wiltink

[0] Because you can't reach variables in a DLL from the calling
    program. If nowadays you can, that's easier of course.

Re:DLL call a function in the calling app?


Quote
"Maarten Wiltink" <maar...@kittensandcats.net> wrote in message

news:a968h7$7u0$1@news1.xs4all.nl...

Quote
> Craig Lister wrote in message
> <84cc68cf.0204120028.5db2a...@posting.google.com>...
> >Is it possible for a DLL to call a function in a calling application?
> >For example, the code to read and write to a com port is in the DLL.
> >When something arrives at the port, the DLL has to decode the message,
> >and send it to the calling app.

> >Possible?

> Quite possible. The mechanism is named "callback" and the
> idea is that the calling program supplies the DLL with a
> function pointer to call in response to a certain event
> (like data received on the COM port).

What will happen if the main application exported this functin and acted as
a dll. Will loading this app module from the dll start another copy of the
app?

Re:DLL call a function in the calling app?


Quote
Smola wrote in message ...
>"Maarten Wiltink" <maar...@kittensandcats.net> wrote in message
>news:a968h7$7u0$1@news1.xs4all.nl...
>> Craig Lister wrote in message
>> <84cc68cf.0204120028.5db2a...@posting.google.com>...
>> >Is it possible for a DLL to call a function in a calling application?
>> >For example, the code to read and write to a com port is in the DLL.
>> >When something arrives at the port, the DLL has to decode the message,
>> >and send it to the calling app.

>> >Possible?

>> Quite possible. The mechanism is named "callback" and the
>> idea is that the calling program supplies the DLL with a
>> function pointer to call in response to a certain event
>> (like data received on the COM port).

>What will happen if the main application exported this functin and acted
as
>a dll. Will loading this app module from the dll start another copy of the
>app?

I'm afraid nothing will happen. While executables can export
functions, I believe Windows does not let you import them.

Of course, a "program" is no different from a DLL with a
"main" (or any other convention) function. The different
treatment is probably for Historic Reasons. Meaning there
is _no_ good reason for it.

You could try this mechanism by writing most of your app
as a DLL, or two, and putting _only_ the "main" function
in an executable. There would be no problem at all. Well,
perhaps one: the called DLL would have to know from _which_
other DLL to import the uniformly-named callback function.
That was what you meant, wasn't it?

Groetjes,
Maarten Wiltink

Re:DLL call a function in the calling app?


Thanks Maarten.

I'm trying to do what you say, but am sticking! Unfortunaltly, there
is not a hell of a lot of help on the net, so this, at the moment, is
my only source of information.

Can you maybe see where I am going so wrong:

Application Code Snippets:

I have simply declared a normal procedure in my APPLICATION called
GetData:

procedure TForm1.GetData;
begin
   ShowMessage ('Get Data Envoked!'); // Simple code just to show it
works.
end;

This is probably and most certainly all wrong! I call the DLL, I use
the following call:

procedure TForm1.btnPurgeClick(Sender: TObject);
var
  CallBackProc : ^TProcedure;
  ErrorCode : Integer;
begin

  CallBackProc := addr(TForm1.GetData);
  memo1.lines.add ('Sending PURGE request to DLL.');
  ErrorCode := Purge (@CallBackProc);
end;

However, I get NO access violations yet. Good start?

Now we go to the DLL code.

I have declared a function in the DLL like this:

Procedure SendDataToApplication (InData : String );

I also create a variable in the DLL like this:

var
  CallBackFunction : TProcedure;

In this procedure, I'm hoping to send any data captured at the port,
back to the application.

In the function that the App calls, this is it here:

function Purge (CallBack : TProcedure): Integer; stdcall;
var
  intTimeout : Integer;
begin
  CallBackFunction := TProcedure(CallBack) ; ....

Still, no access violations, so maybe not as wrong as I think!

However, later in this Purge function, at the end, I do this:

  CallBackFuntion;

ACCESS VIOLATION!

Can you see what I am doing wrong here??

Quote
"Maarten Wiltink" <maar...@kittensandcats.net> wrote in message <news:a968h7$7u0$1@news1.xs4all.nl>...
> Craig Lister wrote in message
> <84cc68cf.0204120028.5db2a...@posting.google.com>...
> >Is it possible for a DLL to call a function in a calling application?
> >For example, the code to read and write to a com port is in the DLL.
> >When something arrives at the port, the DLL has to decode the message,
> >and send it to the calling app.

> >Possible?

> Quite possible. The mechanism is named "callback" and the
> idea is that the calling program supplies the DLL with a
> function pointer to call in response to a certain event
> (like data received on the COM port).

> The issues are exactly the same as when a program calls an
> function imported from a DLL: calling convention, memory
> spaces for buffers.

> What you could do is define a variable in the DLL to hold
> the function pointer, publish Get and Set access functions
> to it[0], and have the program set an event handler^W^Wcallback
> function before it initiates the serial communication. Look
> familiar?

> This is borrowed shamelessly from the Delphi event construction,
> which is simply one way of doing it. C programmers would more
> likely pass the callback function as an extra parameter to the
> Write call, but that is of course equivalent.

> Groetjes,
> Maarten Wiltink

> [0] Because you can't reach variables in a DLL from the calling
>     program. If nowadays you can, that's easier of course.

Re:DLL call a function in the calling app?


In article <84cc68cf.0204120548.7fe71...@posting.google.com>,

Quote
cra...@rebic.co.uk (Craig Lister) writes:
>  CallBackProc := addr(TForm1.GetData);

You have to provide an actual entry-point (or calling address) for the
callback. What you have provided is the address of a class or "template"
reference.

You have a created instance (maybe Form1) of your class. So an actual reference
to use as a callback would be a reference to Form1.GetData. So that a call to
From1.GetData would find the instance Form1 and find the offset to the GetData
method. Because Delphi happily interchanges references and variable names, you
might even be able to use Form1.GetData instead of addr(Form1.GetData), or
possibly Form1.MethodAddress(GetData).

Personally I would tend to use a simple procedure rather than a method, and get
everything running OK. Then introduce any complexities of a method.

MS APIs often take a DWord as an "instance" and return that value unchanged as
a parameter when the callback is called. One can then pass a reference to the
object and call a method from the callback.

This is an Audio Compression Manager enumeration call, passing a callback
reference in the first and an instance reference in the second parameter . . .

  acmDriverEnum(DriverEnumProc, integer(Self), ACM_DRIVERENUMF_DISABLED);

This is the callback function which is called from the API with the returned
instance . . .

function DriverEnumProc(hACMDrvId : HACMDRIVERID;
                        dwInstance : DWord; fdwSupport :DWord): bool; stdcall;
{calls method of form from driver enumeration callback}
begin
// Now we typecast the returned instance to an object, to call a method of the
object
  TForm1(dwInstance).ACMEnumDrvrMethod(hACMDrvId, fdwSupport);
  Result := true; // continue enumeration to enumerate all drivers
end;

This is the header for the method ...

procedure TForm1.ACMEnumDrvrMethod(hACMDrvId : HACMDRIVERID; fdwSupport
:DWord);

Alan Lloyd
alangll...@aol.com

Re:DLL call a function in the calling app?


Quote
Craig Lister wrote in message

<84cc68cf.0204120548.7fe71...@posting.google.com>...

Quote
>Thanks Maarten.

>I'm trying to do what you say, but am sticking! Unfortunaltly, there
>is not a hell of a lot of help on the net, so this, at the moment, is
>my only source of information.

>Can you maybe see where I am going so wrong:

>Application Code Snippets:

>I have simply declared a normal procedure in my APPLICATION called
>GetData:

>procedure TForm1.GetData;
>begin
>   ShowMessage ('Get Data Envoked!'); // Simple code just to show it
>works.
>end;

This is NOT a "normal procedure", it's a method. A method
is a procedure _in the context of a given object_, which
is passed in as the first, implicit, parameter "Self".

The value of the expression "Form1.GetData" (or indeed
"AnyOtherObjectOfClassTForm1.GetData") is eight bytes in
size, the pointer to GetData followed by the pointer to
the context object. This order is for ease of casting: you
_can_ cast a method to a procedure and the compiler merely
has to throw away the second pointer.

Quote
>This is probably and most certainly all wrong! I call the DLL, I use
>the following call:

>procedure TForm1.btnPurgeClick(Sender: TObject);
>var
>  CallBackProc : ^TProcedure;

Why "pointer to"? A TProcedure is already implemented as
a 32-bit pointer to the start of the code; there is no need
to introduce an extra level of indirection.

Quote
>  ErrorCode : Integer;
>begin

>  CallBackProc := addr(TForm1.GetData);

So here you take the address of a method, and later on it's
used as the address of a procedure. This is an implicit cast;
without wanting to you've worked around your mistake.

Quote
>  memo1.lines.add ('Sending PURGE request to DLL.');
>  ErrorCode := Purge (@CallBackProc);

...or have you? Here you take an address of something again.
Frankly, I lost track. I don't know any more where all those
pointers point.

Quote
>end;

>However, I get NO access violations yet. Good start?

Compiler errors, or run-time errors? Having no compiler
errors is a good start, certainly. A prerequisite, really.

- Show quoted text -

Quote
>Now we go to the DLL code.

>I have declared a function in the DLL like this:

>Procedure SendDataToApplication (InData : String );

>I also create a variable in the DLL like this:

>var
>  CallBackFunction : TProcedure;

>In this procedure, I'm hoping to send any data captured at the port,
>back to the application.

>In the function that the App calls, this is it here:

>function Purge (CallBack : TProcedure): Integer; stdcall;

Did you notice the change in datatype here?

Quote
>var
>  intTimeout : Integer;
>begin
>  CallBackFunction := TProcedure(CallBack) ; ....

That cast should be unnecessary. If it's needed, you
did something wrong.

Quote
>Still, no access violations, so maybe not as wrong as I think!

>However, later in this Purge function, at the end, I do this:

>  CallBackFuntion;

>ACCESS VIOLATION!

Kaboom! Yes, of course. You passed in a pointer to a variable
that contains a pointer to a procedure. Or perhaps to a variable
that contains yet another pointer to a procedure; as I said, I
lost count.

You should be able to get it right the next time, but there's
one more thing that's going to cause trouble. You exported the
Purge function and dutifully made it stdcall, but you have to
make sure of the calling convention of _every_ function that's
called across modules, and preferably standardise on stdcall.
I'm talking, of course, about the callback function itself.

Groetjes,
Maarten Wiltink

Other Threads