Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Problem with writing plugin for my application

Status
Not open for further replies.

Juhas0

Programmer
May 6, 2008
10
PL
Hi, I have to write an application that can use plugins. What's more, plugins should may use some functions from my application.

I have read some articles about writing applications with plugins(but, there isn't many), and got to know that I have to use interfaces.

But something's wrong with my code(although I think it should work), because I get an AccessViolation.

My interface looks like that:

[tt][blue]
type
IAppInterface = interface(IInterface) ['{CCC6FDCA-5F81-4C99-B0F2-BD4255564439}']

procedure EmployeesChanged;
procedure EventsChanged;
procedure ErrorOccured(Error: PChar; PluginName: PChar);
end;
[/blue][/tt]

GUID is created by SHIFT+CTRL+G of course ;)

I implement this interface in class TPlugin:
[tt][blue]
type
TPlugin = class(TInterfacedObject, IAppInterface)

public
procedure EmployeesChanged;
procedure EventsChanged;
procedure ErrorOccured(Error: PChar; PluginName: PChar);
end;
[/blue][/tt]


Then I have a class called TPluginManager that manages my plugins :)

I introduce there a variable:
[tt][blue]
FAppInterface: IAppInterface;
[/blue][/tt]

And create it:
[tt][blue]
FAppInterface:=TPlugin.Create;
[/blue][/tt]

Next I load library and look for function address:

[tt][blue]
procedure TPluginManager.ConnectToPlugin(PluginFileName: string);
var
ConnectProc: procedure(const AppIFace: IAppInterface; AppConnectionString: PChar);
invalidPlugin: boolean;
lHandle: THandle;
begin
invalidPlugin:=false;

lHandle:=LoadLibrary(PAnsiChar(PluginFileName));

@ConnectProc:=GetProcAddress(lHandle, 'Connect');
if @ConnectProc = nil then
begin
InvalidPlugin:=true;
end else
begin
ConnectProc(FAppInterface, PChar(AppDBConnectionString));
end;

@ConnectProc:=nil;
end;
[/blue][/tt]

When I call ConnectProc I get an AccessViolation error.

The error occurs in procedure in my dll file. This procedure looks like that:

[tt][blue]
var
AppInterface: IAppInterface;

procedure Connect(const AppIFace: IAppInterface; AppConnectionString: PChar); stdcall;
begin
AppInterface:=AppIFace; //here is the error

//other code
end;
[/blue][/tt]


Why does this error occur? What am I doing wrong?

In my application I tried also to create FAppInterface just before calling ConnectProc, but it didn't change anything.

Please, help me, because I am run out of ideas and time.
 
I just use straight DLL files with straight functions. As long as the functions are defined in a known way to the main application (the example I used in playing with this is a sorting algorithm tester)

You:
1) throw the implementation details of the sorting algorithm in the DLL.
2) expose the call with a standard the tester program knows. I.e. all plug-in modules have this same call.
3) when the tester program starts, scan for all plug-in modules and bring them up in the menu.
4) Then when the module is selected, it calls the proper DLL file with the proper function definition.

The Access Violation is likely because something didn't work correctly along the line.

As for using some functions in your main application, you pass their function pointers as a call back function, or define these functions as message handlers in your main application.

Measurement is not management.
 
I don't understand you or you don't understand me :)
The whole problem is that I want the plugins to use some functions from my application.
Only reasonable solution to that is by interfaces. But for any reason I can't pass my interfaced object to dll.
 
The whole problem is that I want the plugins to use some functions from my application.

Then implementing a callback function will be what you want to do this. The OS does it, so nothing really strange about it.

A short example:

Code:
library mydll;
   uses sysutils;
  type
    cbproc = function(num1, num2: integer): integer;

  procedure domath(var invar: integer; mycallback: cbproc);
    begin
      invar := invar * 3;
      invar := mycallback(invar, 2);
    end;


  exports
    domath  index 1;

  end.

The main program:
Code:
{$APPTYPE CONSOLE}
program main; uses windows;
type
  cbproc = function(num1, num2: integer): integer;
  dmproc = procedure(var invar: integer; mycallback: cbproc);
var
  libhandle: DWord;
  dllproc: dmproc;
  invar: integer;

function add_two_numbers(num1, num2: integer): integer;
  begin
    Result := Num1 + Num2;
  end;

begin
  write('Input invar: ');
  readln(invar);
  writeln;
  writeln('DLL function multiplies by three.');
  writeln('Callback function as called from DLL adds two.');
  writeln;
  libhandle := LoadLibrary('MYDLL.DLL');
  if libhandle <> 0 then
    begin
      @DLLProc := GetProcAddress(libhandle, 'domath');
      if @DLLProc <> nil then
        begin
          dllproc(invar, @add_two_numbers);
        end;
      FreeLibrary(libhandle);
    end;
  writeln('Invar is now: ', invar);
  readln;
end.

Note the DLL is calling the add_two_numbers function from the main program - more or less as it was passed from the main.

Only reasonable solution to that is by interfaces. But for any reason I can't pass my interfaced object to dll.

Because it's a dynamically created piece of memory. In DLLs (i.e. a separate process), memory generally isn't meant to be shared. You can try using the "sharemem" unit in both the DLL and the main program, but that might not be what you want to have happen.

And I'm giving you a "reasonable solution" outside of interfaces - simple DLLs work as plug-ins, without the necessity of creating COM objects or the like.

Measurement is not management.
 
A message handler example for VCL forms (if you go that route).

Define a forward in the object for the form like this:

Code:
 protected
     procedure UpdateStuff(var WinMsg: TMessage); message WM_USER+1;

You can pass a limited number of parms on TMessage. Then to call, you do this:

Code:
SendMessage(Form1.handle, WM_USER+1, 0, 0);

You can do this from the DLL as well (including the proper units of course), as long as you pass to the DLL routine the form handle.

Of course, the drawback of this one is that you can't return data from the main program. But this one is more preferable if you're just looking to update VCL form controls from your DLL functions.

Measurement is not management.
 
And how about writing plugins in other languages? Is this solution(passing function reference) enought? Or do I have to write COM plugins?
 
As long as you stick with standard 'C' type data types (ie. no Pascal strings) then you should be able to allow any language that can make conventional DLL calls.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top