function EXE to DLL (Delphi) - delphi

I am modulating my application to work with separate modules (plugin).
I have already successfully made my EXE application read and load the plugins, including the forms.
Now I need to do the inverse, export functions from the executable to DLL.
Example:
Inside my executable, it has a TMemo component. I want to create a function like this
function GetMemo(): widestring;
In my idea, whoever wrote the DLL plugin, when calling the function GetMemo(), would already take the contents of the TMemo in DLL.
It is possible?

The simplest way to handle this is to define a record of function pointers, and then have the EXE pass an instance of that record to each plugin while initializing it. The EXE can then implement the functions as needed and pass them to the plugins, without actually exporting them from its PE exports table like a DLL would.
For example:
type
PPluginExeFunctions = ^PluginExeFunctions;
PluginExeFunctions = record
GetMemo: function: WideString; stdcall;
...
end;
function MyGetMemoFunc: WideString; stdcall;
begin
Result := Form1.Memo1.Text;
end;
...
var
ExeFuncs: PluginExeFunctions;
hPlugin: THandle;
InitFunc: procedure(ExeFuncs: PPluginExeFunctions); stdcall;
begin
ExeFuncs.GetMemo := #MyGetMemoFunc;
...
hPlugin := LoadLibrary('plugin.dll');
#InitFunc := GetProcAddress(hPlugin, 'InitializePlugin');
InitFunc(#ExeFuncs);
...
end;
var
ExeFuncs: PluginExeFunctions;
procedure InitializePlugin(pExeFuncs: PPluginExeFunctions); stdcall;
begin
ExeFuncs := pExeFuncs^;
end;
procedure DoSomething;
var
S: WideString;
begin
S := ExeFuncs.GetMemo();
...
end;

unit uDoingExport;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
procedure testproc; stdcall;
implementation
{$R *.dfm}
procedure testproc;
begin
ShowMessage('testproc');
End;
exports
testproc;
end.
I simply added the method I want to publish from within my EXE in the unit's Interface, and on the Implementation I added exports (method name). I am using stdcall not cdecl.
In my child, I can loadlibrary the exe file... or you can go a little crazy like Apache does, and in the previous code, add a loadlibrary, which loads a DLL, which intern can loadlibrary the caller.
My point was to show, your EXE is simply like a DLL (just a different binary header) and vise versa. Just slap EXPORTS. Proof it works, I ran tdump against the EXE:
Exports from ProjDoingExport.exe
1 exported name(s), 1 export addresse(s). Ordinal base is 1.
Sorted by Name:
RVA Ord. Hint Name
-------- ---- ---- ----
0005294C 1 0000 testproc
I know, a late answer, but, a great question!

Related

My first call to a DLL

I'm becoming crazy.
I'm a beginner and I want to make my first DLL.
I've followed this guide:
http://www.tutorialspoint.com/dll/dll_delphi_example.htm
I want to set a text information about a program version and read it when I want, so display it to the user through main application. This is just an example to keep confidence with DLLs, I already know that there are many other way to achieve this.
Now I'm trying to read the variable "versione" from a DLL like this:
library Clientdll;
uses SysUtils, Classes, Dialogs;
{$R *.res}
function Versione(var messaggio, versione: String):string; export; stdcall;
begin
versione:='Nessun dato ricavato. Valore di chiamata alla DLL errato!';
if messaggio='chiama' then versione:='v4.0.0 build 31';
end;
exports versione;
begin
end.
In the main application I've write this:
[...]
implementation
uses unit2;
{$R *.dfm}
function Versione(var messaggio, versione:string):string; stdcall; external 'Clientdll.dll'
[...]
Now I said 'OK, I've just to call the DLL and that's all...'. So:
procedure TForm1.Button1Click(Sender: TObject);
var x, y:string;
begin
x:='chiama';
Versione(x,y);
showmessage(y);
end;
I can read v4.0.0 build 31 in the dialog box, but when I press OK I've receive this error:
"Invalid Pointer Operation".
Any ideas?
I've try to google it, but my english is poor and some answers are difficult to understand, also with translation tools!
Don't use String as the parameter type. This is clearly explained in the comment that the IDE generates when you use File->New->Other->Delphi Projects->DLL Wizard to create a new DLL:
{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }
In addition, using Delphi strings means that your DLL functions are not callable from other languages such as C.
You should also expect the calling application to provide you with the memory in which to place the results (and a length parameter that tells you how large that memory buffer is, too).
Here's a minimal (quite useless) example of a Delphi dll with a single function, along with a test application that calls it. (The DLL is, as I said, quite meaningless. Any actual DLL should be designed to put the functional code in its own unit and not in the project file.)
The sample DLL source:
library SimpleTest;
{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }
uses
SysUtils,
Classes;
{$R *.res}
// Parameters:
// arg: Argument that indicates whether this is a test or
// something else, so we know which value to return
// Buffer: The space in which to place the result
// Len: The length of the buffer provided
function TestDLL(const arg: PChar; const Buffer: PChar;
const Len: Integer): Boolean; stdcall;
begin
// Make sure we use the Len parameter, so we don't overflow
// the memory we were given. StrLCopy will copy a maximum of
// Len characters, even if the length of the string provided
// as the 'source' parameter is longer.
if arg = 'Test' then
StrLCopy(Buffer, 'Test result', Len)
else
StrLCopy(Buffer, 'Non-test result', Len);
Result := True;
end;
exports
TestDll;
begin
end.
The form for the test application that calls it:
unit DLLTestForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form4: TForm4;
implementation
{$R *.dfm}
function TestDLL(const arg: PChar; const Buffer: PChar; const Len: Integer): Boolean; stdcall;
external 'SimpleTest.dll';
procedure TForm4.Button1Click(Sender: TObject);
var
Parm1: String;
Parm2: String;
BuffLen: Integer;
begin
Parm1 := 'Test';
// Length of buffer (including null terminator) for DLL call
// Chosen arbitrarily - I know the DLL won't return more than 15 + the
// null. I'm pretending I don't, though, and allowing extra space. The
// DLL won't return more than 30 characters, even if it has more to say,
// because it uses StrLCopy to limit the result to Len characters.
BuffLen := 30;
// Allocate space for return value
SetLength(Parm2, BuffLen);
// Call the DLL with `Test` arg
if TestDLL(PChar(Parm1), PChar(Parm2), BuffLen) then
ShowMessage(Parm2);
// Call the DLL with a different parameter value
Parm1 := 'Other';
if TestDLL(PChar(Parm1), PChar(Parm2), BuffLen) then
ShowMessage(Parm2);
end;
end.

How to share functions in Delphi?

For example, I have a couple of functions written for my form. Now, I need the exact same functions in another form. So, how can I share them between the two forms? Please, provide a simple example if possible.
Don't put them in your form. Separate them and put them in a common unit, and add that unit to the uses clause where you need access to them.
Here's a quick example, but you can see many of the Delphi RTL units (for instance, SysUtils) that do this. (You should learn to use the VCL/RTL source and the demo apps that are included in Delphi; they could answer many of the questions you've posted more quickly than waiting for an answer here.)
SharedFunctions.pas:
unit
SharedFunctions;
interface
uses
SysUtils; // Add other units as needed
function DoSomething: string;
implementation
function DoSomething: string;
begin
Result := 'Something done';
end;
end.
UnitA.pas
unit
YourMainForm;
uses
SysUtils;
interface
type
TMainForm = class(TForm)
procedure FormShow(Sender: TObject);
// other stuff
end;
implementation
uses
SharedFunctions;
procedure TMainForm.FormShow(Sender: TObject);
begin
ShowMessage(DoSomething());
end;
end.
In more recent versions of Delphi than Delphi 7, you can create the functions/methods in a record instead:
unit
SharedFunctions;
interface
uses
SysUtils;
type
TSharedFunctions = record
public
class function DoSomething: string;
end;
implementation
function TSharedFunctions.DoSomething: string;
begin
Result := 'Something done';
end;
end;
UnitB.pas
unit
YourMainForm;
uses
SysUtils;
interface
type
TMainForm = class(TForm)
procedure FormShow(Sender: TObject);
// other stuff
end;
implementation
uses
SharedFunctions;
procedure TMainForm.FormShow(Sender: TObject);
begin
ShowMessage(TSharedFunctions.DoSomething());
end;
end.
If you need forms. You could use inherited forms. Creating a form that inherit the functions of a parent form.
The most interesting. Any changes in the parent form is reflected a change in inherited forms. You even can inherit form controls (tbutton, tlabel, etc...).
In GUI Delphi7. Option "new form", option "inherited from a existing form".
Example:
//MainForm.pas
type
TMainForm = class(TForm)
procedure MiFunction();
.
.
end;
//ChilForm.pas
type
TChildForm = class(TMainForm)
.
.
end;

Calling a method in main app from dll

Can I call a method which is placed in main application from dll code?
seems there is only one way to do it - create a callback object.
in your application you have to declare interface, wich describes your method, for example:
IMyMethodInterface = interface(IInterface)
procedure MyMethod(); stdcall;
end;
next you have to create class, wich implements this interface (and your method):
TMyMethodObject = class(TInterfacedObject, IMyMethodInterface)
public
procedure MyMethod(); stdcall;
end;
when you load DLL, you have to create TMyMethodObject instance and pass its IMyMethodInterface to dll; of course dll has to have corresponding method and export it (wich takes interface as parameter) SetMethodCallback wich stores interface reference:
vars:
var mmo : IMyMethodInterface;
dllHandle : THandle;
smc : procedure (mmi : IMyMethodInterface); stdcall;
code:
mmo := TMyMethodObject.Create();
dllHandle := LoadLibrary('mydll.dll');
smc := GetProcAddress(dllHandle, 'SetMethodCallback');
if assigned(smc) then
smc(mmo);
now, you can use IMyMethodInterface reference in your dll to call method.
of course you can statically link dll and use it directly:
procedure SetMethodInteface(mmi : IMyMethodInterface); stdcall; external 'mydll.dll';
here is an DLL sample code:
library Project3;
// uses YourMethodIntf.pas
{$R *.res}
var AppMethod : IMyMethodInterface;
procedure SetAppMethodCallback(mmi : IMyMethodInterface); stdcall;
begin
AppMethod := mmi;
end;
procedure AnotherDllMethod();
begin
//here you can use AppMethod.MyMethod();
end;
exports
SetAppMethodCallback name 'SetMethodcallback';
begin
end.
take into account that your mmo object (TMyMethodInterface) will not be destroyed until you set AppMethod in dll to nil (or FreeLibrary dll ), so be careful

detect usb drive/device using delphi

i can't figure out the formatting rules here .. too many lines of code in my example to add 4 spaces to each line, so here is the link to the code i need help with
http://nitemsg.blogspot.com/2011/01/heres-unit-written-in-delphi-7-that-you.html
The problem I have is that I don't know enough about delphi to use this code with a form.
I am a drag and drop programmer only.
An example with a showmessage('friendly name =' + ... ) when a USB device is detected is what I need.
cheers,
If you are only familiar with drag-and-drop programming, and don't know much about objects or other units, then you need to get yourself familiarized with using objects other than auto-created forms and the components you drop in them.
The code at this link is an entire unit. You need to create a new Unit in your project (File > New > Unit). It will look something like this:
unit Unit1;
interface
implementation
end.
Now when you save the unit, the name of the unit will automatically change to the filename (without the extension) like this:
unit MahUSB;
interface
implementation
end.
In this example, you should use the same unit name as that source you're trying to use. Save the unit as 'MahUSB.pas', and should be in the same folder as the rest of your project (or elsewhere, just a suggestion). Copy/Paste all the code from that website and replace everything in this unit now.
Now in order to actually use this, you need to create an instance of this object. ONLY ONE INSTANCE (I say that just because by the looks of this, there's no need for more than one).
Very important: Seeing as you are not familiar with objects, let me quickly explain something. Objects need to be created in order to work. At the same time, anything that's created also needs to be free'd when you're done with it. In this case, we will create this object when your application starts, and free the object when your application closes.
Now on your MAIN FORM (not any other forms) you need to put an event handler for both OnCreate and OnDestroy. You also need to declare a variable to represent this object. In the declaration of your main form, add a variable 'USB' with the type of this object. Make sure that goes under the 'private' or 'public' section, either one is ok. Also make sure you declare the "MahUSB" unit at the top of your main unit in the uses clause.
Declaring the object in your main form:
type
TForm1 = class(TForm)
private
USB: TUsbClass;
public
end;
Creating/freeing object when your app starts/closes:
procedure TForm1.FormCreate(Sender: TObject);
begin
USB:= TUsbClass.Create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if assigned(USB) then USB.Free;
end;
Now we're not done yet. Now we need to add the event handlers. Notice at the top of this unit you got, there are two types called TOnDevVolumeEvent and TOnUsbChangeEvent. These are event types. The parameters in the event handlers must be identical to the parameters declared in these types. So now in your main form, declare these event handler procedures...
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
USB: TUsbClass;
procedure VolumeEvent(const bInserted : boolean; const sDrive : string);
procedure ChangeEvent(const bInserted : boolean;
const ADevType,ADriverName, AFriendlyName : string);
public
end;
Now just one more thing we have to do before this will work. The USB object needs to know what event handlers to use, therefore, we need to assign these procedures to the events. Upon your form's creation, we need to assign these events...
procedure TForm1.FormCreate(Sender: TObject);
begin
USB:= TUsbClass.Create;
USB.OnUsbChange:= Self.ChangeEvent;
USB.OnDevVolume:= Self.VolumeEvent;
end;
When all is said and done, your main form unit should look something like this:
unit uUSBTest;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, MahUSB;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
USB: TUsbClass;
procedure VolumeEvent(const bInserted : boolean; const sDrive : string);
procedure ChangeEvent(const bInserted : boolean;
const ADevType,ADriverName, AFriendlyName : string);
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.ChangeEvent(const bInserted: boolean; const ADevType,
ADriverName, AFriendlyName: string);
begin
ShowMessage('Change event for "'+AFriendlyName+'"');
end;
procedure TForm1.VolumeEvent(const bInserted: boolean;
const sDrive: string);
begin
ShowMessage('Volume event for "'+sDrive+'\"');
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
USB:= TUsbClass.Create;
USB.OnUsbChange:= Self.ChangeEvent;
USB.OnDevVolume:= Self.VolumeEvent;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if assigned(USB) then USB.Free;
end;
end.
And there you are! You will have these two event handler procedures where you can further handle either of those two events.

Got a big problem: TDictionary and dll in Delphi 2010!

I got a very serious problem when I'm trying to access TDictionary variable in host program from a dynamicly loaded dll. Here is the complete code, anyone can give some help? thanks!
===========main program project source code===================
program main;
uses
ShareMem,
Forms,
uMain in 'uMain.pas' {Form1},
uCommon in 'uCommon.pas';
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
==============unit uMain================
unit uMain;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, uCommon;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
type
Tfoo = function(ADic: TMyDic): string; stdcall;
procedure TForm1.Button1Click(Sender: TObject);
var
Dic: TMyDic;
HLib: THandle;
foo: Tfoo;
begin
Dic := TMyDic.Create;
try
Dic.Add(1, 'World!');
Dic.Add(2, 'Hello, ');
HLib := LoadLibrary('Mydll.dll');
try
#foo := GetProcAddress(HLib, 'foo');
ShowMessage(foo(Dic));
finally
FreeLibrary(HLib);
end;
finally
Dic.Free;
end;
end;
end.
=================dll project source code=====================
library MyDll;
uses
ShareMem,
SysUtils,
Classes,
uCommon in 'uCommon.pas';
function foo(ADic: TMyDic):string; stdcall;
var
I: Integer;
S: string;
begin
for I in ADic.Keys do
begin
S := S + ADic[I];
end;
Result := s;
end;
exports
foo;
end.
================unit uCommon==============
unit uCommon;
interface
uses
SysUtils, Generics.Collections;
type
TMyDic = TDictionary<Integer, string>;
implementation
end.
Are you getting exceptions? Maybe access violations or invalid pointer operations?
You can't share strings and objects between Delphi and a DLL if the DLL has its own memory manager. Since you're using Delphi 2010, you should have FastMM installed by default. Add "SimpleShareMem" as the first thing in the uses list for both the DLL and the EXE, and see if that doesn't fix the problem?
EDIT: In response to additional information from the poster:
You're calling dic.free after you unload the DLL. Even if you share memory managers, that's going to give you an access violation. Here's why.
Free calls TObject.Destroy, which is a virtual method. The compiler generates code to look it up in the object's Virtual Method Table. But the VMT is stored in static memory that's specific to the module, not in shared memory allocated by the memory manager. You unloaded the DLL and pulled the rug out from underneath the VMT pointer in the object, and so when it tries to call a virtual method you get an access violation.
You can fix this by making sure to call Free before unloading the DLL. Or you can use runtime packages instead of a DLL, which gets around this problem by putting the VMT for the object in an external package that won't be unloaded before you're done with it.
I would strongly discourage passing object instances between an executable and a regular DLL. Mainly for the exact reasons you are are encountering. What happens if the DLL is rebuilt and you've changed the object in some incompatible subtle way?
As Mason points out, packages are the preferred way to partition your application into modules.
I finally found what the real problem is! It seems like this: "For..in keys" loop will cause TDictionary create an instance for its data field FKeyCollection:
function TDictionary<TKey,TValue>.GetKeys: TKeyCollection;
begin
if FKeyCollection = nil then
FKeyCollection := TKeyCollection.Create(Self);
Result := FKeyCollection;
end;
So when the dll is unloaded, the memory that FKeyCollection pointed is also freed, thus left a "dangling pointer".
destructor TDictionary<TKey,TValue>.Destroy;
begin
Clear;
FKeyCollection.Free; //here will throw an exception
FValueCollection.Free;
inherited;
end;

Resources