Delphi 10.3 - Weird DLL behaviour - delphi

I have met a really weird behaviour using Delphi and DLL.
I want to learn how to create and use DLL's. To do so, I created two files: one containing the DLL, and the other one calling it.
The DLL retrieves informations from a *.ini file (the GetInfos function). When calling it in a form, I get the following error: "Access violation at address XXXXXXXX in "dlltest.dll" module. Read of address 00000000.
I wanted to try some other things, so I created a simple procedure, that just display a message.
The interesting thing is that when I call this procedure (which only purpose is to display a message), I no longer get the Access violation error.
I really do not understand how it can make my code work. I'd really like if someone could give me some explanation. I am using Delphi 10.3 Rio Version 26.0.36039.7899
Here is the code I produced :
dlltest:
library dlltest;
uses
System.SysUtils, System.Classes, System.IniFiles, Winapi.Windows, vcl.dialogs;
var
// Ini file var
iniConfig: TInifile;
procedure SayHello;
var
hello: PChar;
begin
hello := 'Hello world!';
ShowMessage(hello);
end;
// -----------------------------------------------------------------------------
// Retrieving .ini file
function GetIni: TInifile;
begin
result := TInifile.Create('.\monitoring.ini');
end;
// -----------------------------------------------------------------------------
function GetInfos: ShortString;
begin
iniConfig := GetIni;
try
result := iniConfig.ReadString('filename', 'start', 'start');
finally
iniConfig.Free;
end; { try }
end; { function GetInfos }
exports
SayHello, GetInfos;
begin
end.
Using dll:
unit testUtilisationDll;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils,
System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms,
Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Déclarations privées }
public
{ Déclarations publiques }
end;
var
Form1: TForm1;
procedure SayHello; StdCall; external 'dlltest.dll';
function GetInfos: ShortString; StdCall;
external 'dlltest.dll';
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
SayHello;
ShowMessage(GetInfos(self));
end;
end.

When you export the functions, they are using the default calling convention (Register), whereas when you import them, you are telling the import that they're using StdCall calling convention.
The normal case for DLL exported functions is StdCall convention, so you should declare your functions to use this calling convention in your .DLL source:
function GetInfos: ShortString; stdcall;
and
procedure SayHello; stdcall;
Another problem is that when you call GetInfos:
ShowMessage(GetInfos(self));
you are passing it a parameter (self), but your declaration of the import:
function GetInfos: ShortString; StdCall; external 'dlltest.dll';
doesn't list any parameters. You won't be able to compile the program as it is shown in your question...

Related

How display a background image and center a panel in a dll Form?

I want load a image that will be the background of a maximized Form that stays in a dll.
The dll is called from a Vcl Form Application but have a trouble where not is possible load the background image on Form, the dll always crashes.
Thank you by you help.
===========================================================================
Executable
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm2 = class(TForm)
btn1: TButton;
procedure btn1Click(Sender: TObject);
end;
var
Form2: TForm2;
implementation {$R *.dfm}
procedure LoadDLL;
type
TShowformPtr = procedure; stdcall;
var
HDLL: THandle;
Recv: TShowformPtr;
begin
HDLL := LoadLibrary('lib.dll');
if HDLL <> 0 then
begin
#Recv := GetProcAddress(HDLL, 'Recv');
if #Recv <> nil then
Recv;
end;
//FreeLibrary(HDLL);
end;
procedure TForm2.btn1Click(Sender: TObject);
begin
LoadDLL;
end;
end.
Dll
Main:
library Project2;
uses
SysUtils, Classes, Unit1, Unit2;
{$R *.res}
procedure Recv; stdcall;
begin
showform;
end;
exports
Recv;
begin
end.
Unit1 (Form):
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
img1: TImage;
pnl1: TPanel;
procedure FormShow(Sender: TObject);
private
{ Private declarations }
procedure CreateParams(var Params: TCreateParams); override;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.WndParent:= Application.Handle;
Params.ExStyle := Params.ExStyle or WS_EX_TOPMOST or WS_EX_TRANSPARENT;
Params.ExStyle := WS_EX_TRANSPARENT or WS_EX_TOPMOST;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
brush.Style := bsclear;
img1.Picture.LoadFromFile(IncludeTrailingBackslash(GetCurrentDir) + 'background.bmp');
SetWindowPos(Form1.handle, HWND_TOPMOST, Form1.Left, Form1.Top, Form1.Width,
Form1.Height, 0);
ShowWindow(Application.handle, SW_HIDE);
pnl1.Top := (self.Height div 2) - (pnl1.Height div 2);
pnl1.Left := (self.Width div 2) - (pnl1.Width div 2);
end;
end.
Unit2:
unit Unit2;
interface
Uses
windows,
Unit1,
SysUtils;
procedure showform;
implementation
procedure showform;
begin
Form1 := TForm1.Create(Form1);
sleep(100);
Form1.Show;
Form1.Pnl1.Visible := True;
end;
end.
Your question has a lot of problems, so I would try to answer it as best I can, considering the lack of details.
You are using forms so you are building a VCL application. You need to let the IDE assign the VCL framework to your project.
This line is terribly wrong:
Form1 := TForm1.Create(Form1);
In rare circumstances show a from own itself. I would go and say that most probably this is why your application crashes. See this for details about forms in DLLs.
If you cannot properly debug your application put a beep before that line and one after (make a delay between them).
I think your question should be rather called "how to debug a Delphi project".
What you need to do is to get the exact line on which the program crashes. This will give you an insight of why the error/crash (by the way, you never shown the exact error message) appears.
Go check HadShi (recommended) or EurekaLog (buggy) or Smartinspect (I never tried it. Price is similar to the other two). Make sure that you are running in debug mode, the Integrated debugger is on (see IDE options) and that the debug information is present in your EXE/DLL.
PS: you can still debug your app without have one of the three loggers shown above. Just configure your project properly to run in Debug mode!
To debug the DLL see the 'Run->Parameters' menu. Define there a host application that will load your DLL. If the error is the DLL, the debugger will take control and put the cursor to the line of code that generated the crash.
I don't know what the final purpose/what is that you want to achieve. Because of this I must warn you that you might need to take into consideration these questions:
Do you need to use ShareMM?
Why are you building this as a DLL? Can't the application be written as a single EXE? Or two EXEs that communicate with each other?

Firemonkey app fail when calling FreeLibrary

I have a simple Firemonkey app, that loads a DLL, call a procedure within that DLL, which just calls ShowMessage('Dialog from DLL ...'); and returns
That all OK, but the calling application fails, when FreeLibrary has been called and I try closing the main app ?
If I leave out the FreeLibrary call, the application closes without error.
Anybody seen this before, and why ?
I'm using Delphi 10.1 Berlin, but Delphi 10 Seattle also fails on Windows 7.
The application code:
unit Unit2;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
FMX.Controls.Presentation, FMX.StdCtrls;
type
TForm2 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.fmx}
uses
Winapi.Windows;
type
TTest = procedure (); stdcall;
procedure TForm2.Button1Click(Sender: TObject);
var
LibHandle: NativeUInt;
Test: TTest;
begin
LibHandle:= LoadLibrary('.\TESTDLL.DLL');
if LibHandle > 32 then try
#Test:= GetProcAddress(LibHandle, 'Test');
if #Test <> nil then
Test;
finally
FreeLibrary(LibHandle);
end;
end;
And the DLL code:
library TESTDLL;
uses
System.SysUtils,
System.Classes,
FMX.Dialogs
;
{$R *.res}
procedure Test; stdcall;
begin
ShowMessage('Dialog from DLL ...');
end;
exports
Test name 'Test';
begin
end.
If the dll procedure don't contain any calls related to display things, the app behaves as expected.
Any hints help or suggestions, greatly appreciated.
Edit: Included the complete code from main app.

Try to understanding Delphi Interfaces

I'm reading the Nick Hodges' book "Coding in Delphi" and I'm trying to understand the interface usage.
In a unit I've put asimple interface:
unit INameInterface;
interface
type
IName = interface
['{CE5E1B61-6F44-472B-AE9E-54FF1CAE0D70}']
function FirstName: string;
function LastName: string;
end;
implementation
end.
and in another unit I've put the implementation of this interface, according with the book sample:
unit INameImplementation;
interface
uses
INameInterface;
type
TPerson = class(TInterfacedObject, IName)
protected
function FirstName: string;
function LastName: string;
end;
implementation
{ TPerson }
function TPerson.FirstName: string;
begin
Result := 'Fred';
end;
function TPerson.LastName: string;
begin
Result := 'Flinstone';
end;
end.
At this point I've created a simple VCL form application in order to use the object I've created. The form code is this:
unit main;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls, INameImplementation;
type
TfrmMain = class(TForm)
lblFirtName: TLabel;
lblLastName: TLabel;
txtFirstName: TStaticText;
txtLastName: TStaticText;
btnGetName: TButton;
procedure btnGetNameClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
Person: TPerson;
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.FormCreate(Sender: TObject);
begin
txtFirstName.Caption := '';
txtLastName.Caption := '';
end;
procedure TfrmMain.btnGetNameClick(Sender: TObject);
begin
txtFirstName.Caption := ...
end;
end.
My question is this: how can I use the interface? The two funcions are declared as protected so how can I access them from the form? I've to define them as public, or should I use the INameInterface interface unit?
I'm terribly confused about interfaces!!!
Eros
Essentially there are three things for you to know, beyond what you have already demonstrated understanding.
1. How to call methods of an interface
If you have a reference to an interface, then you can call methods just as you would on a class reference:
var
Name: IName;
....
Writeln(Name.FirstName);
Writeln(Name.LastName);
2. How to obtain interface references
Typically you do this by instantiating a class that implements the interface you wish to use:
var
Name: IName;
....
Name := TPerson.Create;
// now you can use Name as before
There are other ways to obtain interface references, but let's leave those to one side for now.
3. How to pass around interfaces
You might not wish to create a new object every time you need to use an interface. So you can get other parties to pass you the interface to use. For instance interfaces can be passed as method parameters:
procedure Foo(Name: IName);
begin
// use Name as before
end;
You can obtain interface references via function calls and properties, etc.
The two functions are declared as protected so how can I access them from the form?
Well, they are declared protected in the implementing object. But you are not going to access them via the implementing object. You will access them via the interface. Which means that the visibility in the implementing object is not relevant from the perspective of the interface.
Your form unit references INameImplementation which is needed to create the object that implements the interface. You'll also need to use INameInterface so that your code can see the interface itself.
This example isn't very powerful yet because you can still see the implementing object's type. But imagine if that was hidden from you and all you could see was a function that returned an IName. It's when you reach this point that interfaces can achieve their potential.

Event is not getting fired

I am trying to use the WebWorkerStarted and WebWorkerFinished from TWebbrowser however the events just don't get fired at all.
How can I get these events working? I want to see which worker threads are getting launched from TWebbrowser.
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.OleCtrls, SHDocVw;
type
TForm2 = class(TForm)
WebBrowser1: TWebBrowser;
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure WebBrowser1WebWorkerFinsihed(ASender: TObject; dwUniqueID: Cardinal);
procedure WebBrowser1WebWorkerStarted(ASender: TObject; dwUniqueID: Cardinal; const bstrWorkerLabel: WideString);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
begin
WebBrowser1.Navigate('www.stackoverflow.com');
end;
procedure TForm2.WebBrowser1WebWorkerFinsihed(ASender: TObject; dwUniqueID: Cardinal);
begin
// does not fire
end;
procedure TForm2.WebBrowser1WebWorkerStarted(ASender: TObject; dwUniqueID: Cardinal; const bstrWorkerLabel: WideString);
begin
// does not fire
end;
end.
As documented here :
By default, TWebBrowser uses IE7 Standards mode even if the run-time environment installed the latest IE (for example, IE11).
WebWorkers were introduced in IE10 so you must have IE running in a more current mode. At least two registry keys need to be set (more if supporting both 32/64 bit):
HKEY_LOCAL_MACHINE (or HKEY_CURRENT_USER)
{\Wow6432Node}
\SOFTWARE
\Microsoft
\Internet Explorer
\Main
\FeatureControl
\FEATURE_BEHAVIORS
{NEW DWORD -> 'YourApplication.exe'
{ VALUE -> 1
Also (for example, IE11 mode)
HKEY_LOCAL_MACHINE (or HKEY_CURRENT_USER)
{\Wow6432Node}
\SOFTWARE
\Microsoft
\Internet Explorer
\Main
\FeatureControl
\FEATURE_BROWSER_EMULATION
{NEW DWORD -> 'YourApplication.exe'
{ VALUE -> 0x2AF8
This will cause the internet explorer instance wraped by TWebBrowser to run in IE11 mode, supporting WebWorkers, etc. You should probably do some sort of sanity check against the installed version of IE before setting this value. More information about valid entries can be found on MSDN.
This still does not raise any WebWorker events for me when navigating to StackOverflow (are you sure it uses them?). As a verification test, this WebWorkers demo page does raise a OnWebWorkerStarted event :
WebBrowser1.Navigate('https://whatwg.org/demos/workers/primes/page.html');

godex ez2250i delphi cant print

I have a label printer GoDex EZ2250i and i found some samples with delphi but none seems to works for me i try to compile with couple of dll's and the program doesnt even launch does anyone have experience on printing with this printer with delphi? thanks in advance, also the OS should be supported Windows7 and Windows 8.
the code that i tried:
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm2 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
const
TestLib = 'Ezio64.dll'; // tried with Ezio32.dll same result
procedure openport(port:PChar); stdcall; external TestLib ;
procedure setup(a, b, c, d, e, f:Integer); stdcall; external TestLib;
procedure sendcommand(command:PChar); stdcall; external TestLib;
procedure intloadimage(filename, image_name, image_type:PChar); stdcall; external TestLib;
procedure extloadimage(filename, image_name, image_type:PChar); stdcall; external TestLib;
procedure ecTextOut(x:Integer; y:Integer; b:Integer; c:PChar; d:PChar); stdcall; external TestLib;
procedure closeport; stdcall; external TestLib;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
begin
openport('0'); // 0-> LPT1; 1-> COM1; 2->COM2
setup(30, 7, 2, 1, 0, 0);
sendcommand('W70');
sendcommand('^P1');
sendcommand('^L');
sendcommand('AC,20,60,1,1,1,0,TEST');
ecTextOut(20, 10, 34, 'Ariel', 'Windows font - Ariel');
sendcommand('E');
closeport();
end;
end.
The program doesn't even launch.
This is symptomatic of a loader failure. For some reason, under the Delphi debugger, loader failures are not reported.
A loader failure typically occurs when:
A dependent DLL cannot be found, or,
A dependent DLL is found, but cannot be loaded, or,
A dependent DLL is loaded, but the imported functions cannot be found within.
To get more diagnostics run the executable outside of the debugger. Quite possibly you are trying to load 64 bit DLLs into your 32 bit process.
If diagnosis proves tricky, go back to the documentation for the library. Make sure you have installed all pre-requisites. Another common failure mode is a missing C++ runtime.
As a last resort, use Dependency Walker. Use the 32 bit version, and execute you program under Profile mode. This will pin-point the dependency failure.

Resources