My first call to a DLL - delphi

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.

Related

function EXE to DLL (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!

E2037 Declaration of 'ReadBytes' differs from previous declaration

Part of the code in which the error:
function ReadBytes(maxLen: integer): TBytes; virtual;
...
uses sysutils, windows, SysConst, commutil;
...
function TBasicBufferedStream.ReadBytes(maxLen: Integer): TBytes;
begin
if maxLen > 0 then
begin
if maxLen <= (fBufFill - fInBufPos) then
begin
SetLength(result, maxLen);
Move(fBuf[fInBufPos], result[0], maxLen);
inc(fInBufPos, maxLen);
end
else
begin
result := Peek(maxLen);
Skip(maxLen);
end;
end
else
result := nil;
end;
In Delphi 10.2 it doesn't compile, in Delphi 7 it compiled!
What is my mistake that I do not understand?
This is just an educated guess as the provided code is sparse. The small hints the provided code gave:
uses provided in the code sample after the declaration contain
System.SysUtils, the unit where TBytes is defined
The provided uses are after the declaration, so TBytes there must
have a different source than System.SysUtils
Therefore it is likely that TBytes is not actually referring to the same type in declaration and implementation part. This can be visualized for example by hovering the mouse over a type. The tool tip will tell you what the exact type is the compiler is referring to.
I can for example reproduce your problem with two small units. TBytes is declared in System.SysUtils, but I declare another one - like it is defined in Delphi 2009 (see below) in Unit3:
unit Unit3;
interface
type
TBytes = array of Byte;
implementation
end.
When I now create a unit like the following, I'm mixing up the usage of TBytes from two different Units, that aren't compatible:
unit Unit2;
interface
uses
Unit3;
function ReadBytes(maxLen: integer): TBytes;
implementation
uses
System.SysUtils;
function ReadBytes(maxLen: integer): TBytes;
begin
//
end;
end.
The types that the tool tip will show are type Unit3.TBytes: array of Byte and type System.SysUtils.TBytes: System.Array<System.Byte>.
So in fact the signature of my function in the declaration differs from the signature in the implementation.
This can be resolved by
Inspecting if the used units are actually correct and needed, can you
get rid of the one causing ambiguity?
If not possible to solve it with the first point, it is possible
to refer explicitly to which type is meant by prefixing with the
containing unit:
function ReadBytes(maxLen: integer): Unit3.TBytes;
I looked at the history of System.SysUtils.TBytes retroactively, couldn't find it for Delphi 7, but in Delphi 2009 the definition of TBytes was following: TBytes = array of Byte;
I have changed my code example with that in mind and rephrased part of the answer.

Accessing the content of TClientDataset loaded within a DLL in a Delphi Application

I have DLL that has functions to be performed on the TClientDataSet like set file to be loaded and loading and saving of file.
unit dll_dmunit;
interface
uses
System.SysUtils, System.Classes, Data.DB, Datasnap.DBClient, Vcl.Dialogs,Vcl.DBGrids;
type
TStudentModule = class(TDataModule)
StudentSet: TClientDataSet;
StudentSource: TDataSource;
StudentSetNAME: TStringField;
StudentSetID: TIntegerField;
StudentSetAGE: TIntegerField;
StudentSetSLNo: TAutoIncField;
dlgOpen: TOpenDialog;
dlgSave: TSaveDialog;
private
{ Private declarations }
public
end;
function loadfile:tdbgrid;stdcall;
procedure setfile(fname:string);stdcall;
procedure savefile;stdcall;
var
StudentModule: TStudentModule;
filename:string;
grid:TDBgrid;
const
path:string='C:\Users\GlobalLogic\Documents\RAD Studio\Projects\Student\test.cds';
implementation
{%CLASSGROUP 'Vcl.Controls.TControl'}
{$R *.dfm}
procedure setfile(f_name: string);stdcall;
begin
filename:=f_name;
end;
function loadfile:tdbgrid;stdcall;
var
_xmldata:string;
begin
StudentModule := TStudentModule.Create(nil);
grid:=TDBGrid.Create(nil);
result:=grid;
try
filename:='C:\Users\GlobalLogic\Documents\RAD Studio\Projects\Student\test.cds';
StudentModule.StudentSet.LoadFromFile(filename);
grid.DataSource:=StudentModule.StudentSource;
_xmldata :=StudentModule.StudentSet.XMLData;
result:=grid;
finally
StudentModule.Free;
end;
showmessage('End of the function');
end;
procedure savefile;stdcall;
begin
StudentModule.StudentSet.SaveToFile(filename);
end;
end.
I am able to perform the loadfile method, but now I need to export the content of the TClientDataSet to the Delphi application. For that I am trying to get the content in a TDbgrid and then return this object to application layer, but I am unable to do so.
Then I tried to read in XML format but couldn't understand how to pass and decode the XML format. I need to move the content of the loaded dataset to my application where I want to display the data.
Please help me in doing so.
Thank You
Below is a simple implemetation that should do what you want without having to export objects from your dll, which tends to be a bit ackward. Instead, simply export the XML string containing your data.
The important points are the signature of your exported function, (in this case function ExportXML:pwideChar;) and the export section of your dll. Make sure to export your XML data as pwidechar.
var Xmldata:widestring;
...
function loadfile...
...
Xmldata :=StudentModule.StudentSet.XMLData;
function ExportXML:pwideChar;stdcall;
begin
result:= pwideChar( Xmldata);
end;
exports
ExportXML name 'ExportXML';
In your application simply load the result of the DLL callExportXML into aTClientDataSet instance and plug it into your controls.
See Using Export Clause in Libraries for more ways to use theexportssection of your dll, which seems to be what you're missing.
As an aside, if you're going from Delphi to Delphi, you don't need the stdcall directive. See:
If you want your library to be available to applications written in
other
languages,
it's safest to specify stdcall in the declarations of exported
functions. Other languages may not support Delphi's default register
calling convention.

Delphi 2006 - routine parameter untyped

It is possible to have a parameter in a routine which can be in the same time either an type, either an string? I know I can accomplish this by overloading a routine, I ask if it possible to do it in another way.
Assume that I have this type - TTest = (t1,t2,t3). I want to have a routine which accepts a parameter of type TTest, but in the same time to be a String, so I can call it myproc(t1) or myproc('blabla')
You should use an overloaded function.
You already have the perfect solution to the problem and there is no need to look for a different way to do this. You could try with a single function that receives a Variant, but then that function will also receive anything which means that the following would also be legal:
myproc(0.5);
myproc(intf);
myproc(-666);
Using an overload allows you to maintain compile time type safety and there is absolutely no loss of generality in using an overload.
Even this can be easily accomplished with overloaded functions, considering it's a good exercise, based on David Hefferman's and Sertac Akyuz answers I made a small example to test both solutions. It is not perfect, it only shows both possibilities.
unit Unit4;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
ttest = (t1,t2);
TForm4 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
function my(aVar:Variant):String;
function MyUntype(const aVar):String;
end;
var
Form4: TForm4;
implementation
{$R *.dfm}
{ TForm4 }
procedure TForm4.FormCreate(Sender: TObject);
var aTestTypeVar : ttest;
aString : String;
begin
my(t1);
my(t2);
my('ssss');
//with untyped params
aString := 'aaaa';
MyUntype(aString);
aTestTypeVar := t1;
aString := IntToStr(Ord(aTestTypeVar));
MyUntype(aString);//can not be a numeral due to delphi Help
end;
function TForm4.my(aVar: Variant): String;
begin
showmessage(VarToStr(aVar));//shows either the string, either position in type
end;
function TForm4.MyUntype(const aVar): String;
begin
//need to cast the parameter
try
ShowMessage(pchar(aVar))
except
showmessage(IntToStr(Ord(ttest(aVar))));
end;
end;
end.
Also I know that Variants are slow and must be used only needed.

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