I'm creating a new component in Delphi, which instantiates a DLL
Unit UMyComponent
interface
type
TMyComponent = class(TComponent)
...
procedure MyDllCall;
end;
procedure Register;
implementation
function MyDll: Longint; stdcall; external 'MyDllName.dll' name 'MyFunction'
procedure TMyComponent.MyDllCall;
var
res: LongInt;
begin
res:= MyDll;
end;
...
procedure Register;
begin
RegisterComponents('My Tab', [TMyComponent]);
end;
end.
I have 2 questions:
When I install the component on the IDE it searches for the physical DLL and gives an error if it's not found in path. I'd like the component to look for it when it's going to be effectively used at runtime.
Is it possible to have the dll library file name to be set at runtime? i.e: 'MyDllName.dll' could change to '10029.dll' or 'ajjdwawd.dll'
Note that I put DLL declaration in the implementation in order not to expose the function call to callers.
Thanks for answering.
Your current code uses what is known as load-time linking. The dependency must be resolved when the module is loaded, otherwise it will fail to load. You need to use the alternative method, run-time linking.
In Delphi there are two ways to do that:
Use the Win32 directly by calling LoadLibrary, GetProcAddress and FreeLibrary.
Get the Delphi RTL to do that for you using the delayed keyword.
Both approaches are covered in more detail in the documentation:
http://docwiki.embarcadero.com/RADStudio/en/Libraries_and_Packages
http://docwiki.embarcadero.com/CodeExamples/en/DelayedLoading_(Delphi)
Related
I'm trying to make my program change the layout scale at runtime. I developed this code based on an existing powershell script. But it compiles but when running, it returns an error.
Error:
The procedure entry point SystemParametersInfo could not be located in the dynamic link library.
var
Form1: TForm1;
function SystemParametersInfo(uiAction, uiParam: UINT; pvParam: Pointer; fWinIni: UINT): BOOL; stdcall; external 'user32.dll';
implementation
{$R *.dfm}
function SetScaling: Boolean;
begin
Result := SystemParametersInfo($000F, 125, nil, 1);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
SetScaling;
end;
As it is said in the mentioned error there is no function named SystemParametersInfo in user32.dll.
There are two similarly named functions SystemParametersInfoA which is to be used by applications that work with ANSI strings and SystemParametersInfoW which is to be used by applications supporting wide strings like modern Delphi.
NOTE: SystemParametersInfo can only be found on Windows CE 5.0 and it resides in Coredll.lib library.
The WinAPI.Windows unit defines the SystemParametersInfo function. No reason to define it locally.
Your call to SystemParametersInfo with a first parameter of $000F is a request to set the screen saver timeout to 125 seconds ($000F=SPI_SETSCREENSAVETIMEOUT).
Setting DPI Scaling is a bit more complex than what you are attempting in the code that you posted. You can read the "how-to" details here.
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.
i have a Delphi7.
I use this article: http://www.delphidabbler.com/articles?article=22&part=2 / Step 2
Now, i created a problem with LoadTypeLib (undefined):
type
TMyClass = class(TAutoIntfObject, IMyIntf, IDispatch)
constructor Create();
protected
procedure helloWorld(); safecall;
end;
implementation
constructor TMyClass.Create();
var
TypeLib: ITypeLib;
s: WideString;
begin
s := ParamStr(0);
OleCheck(LoadTypeLib(PWideChar(s), TypeLib)); // ERR:LoadTypeLib is undefined.
inherited Create(TypeLib, IMyCallback);
end;
Any suggestions?
In Delphi 7, the LoadTypeLib function is declared in the ActiveX unit. You must include that unit in your uses clause.
You can find this stuff out for yourself the exact same way that I did it – by searching the source code. Use the Find in Files feature, search for the name of the symbol that is not declared, and search under the Source directory of your Delphi installation.
I posted this online: Show form from DLL in TScrollBox
What i am trying to do is call and show a form in a Delphi TScrollBox.
Not as Show or ShowModal
Example but not with any DLL:
Form1.Parent:= ScrollBox;
Form1.Show;
How do i use this example from a DLL with a form inside
Can anyone provide an example?
Regards,
You cannot pass a Delphi object between a DLL and a host executable. That's because objects can only be operated on in the module in which they are created. Now, if you were using runtime packages, you'd be able to escape that limitation.
You could export a function from your DLL that created and showed the form. The function might look like this:
function ShowMyForm(ParentWindow: HWND): Pointer; stdcall;
Note that you cannot pass the parent as a Delphi object for exactly the same reasons as I describe above.
You also cannot specify that the parent of the form be a control in your executable. So you have to pass the parent's window handle.
The implementation would be like so:
function ShowMyForm(ParentWindow: HWND): Pointer; stdcall;
var
Form: TMyForm;
begin
Form := TMyForm.CreateParented(ParentWindow);
Form.Show;
Result := Pointer(Form);
end;
You would call it like this:
Form := ShowMyForm(ScrollBox.Handle);
You'd also need to supply a function to destroy the form when you are done:
procedure DestroyMyForm(Form: Pointer); stdcall;
begin
TMyForm(Form).Free;
end;
And you need to watch out for window re-creation. If the host window is re-created then you need to manually re-create the child form.
In short, what you are attempting is rather brittle. If I were you I would look for a different approach.
problem solved and here is the code:
//This is the DLL
library Project1dll;
uses
SysUtils,
Windows,
Classes,
DllForm in 'DllForm.pas' {frmDllForm}; // this is the other form
procedure Create_Form(ph: HWND);
begin
frmDllForm:= TfrmDllForm.CreateParented(Ph);
frmDllForm.Show;
end;
Exports
Create_Form;
begin
end.
//---------------------END--------------------------------------
//This is the project
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
ScrollBox: TScrollBox;
procedure Button1Click(Sender: TObject);
private
end;
procedure Create_Form(ph: HWND) ; external 'Project1dll.dll' name 'Create_Form';
var
Form1: TForm1;
implementation
{$R *.DFM}
function ScrollBoxDll(ph: HWND): Pointer; stdcall;
begin
Create_Form(ph);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ScrollBoxDll(ScrollBox.Handle);
end;
end.
firstly the idea of and constructing the code was my idea, it was intended to be used as a simple way of showing forms stored in a dll file.
The main idea was to put what ever you wanted in the dll, call and show it in a TscrollBox, this can be actually a fully working database or some other path of a program that you would not really want to pass parameters to and from after it was started or closed.
I posted the question online and many did not really understand what I was trying to explain or wanted to do, they seem to think I wanted to created a from in a dll using a scrollbox, but my form or forms were already created and saved in the dll file, the scroll box was external to the dll in my main project.
All I wanted to do was call the forms and show in the scrolbox as parent to it.
I am not claiming any path of this code because many presented their ideas and I thank them all.
The code presented was already constructed long before I posted the question online but did not work as I intended because the form was only showing outside the scrollbox.
I then posted the entire project online with Board4All when a friend pointed out that I should alter a line of code.
He only has a nickname and said he was not sure if it would work depending on what version of delphi I used.
I then adjusted the line of code and it work so he the one who deserves all credits, I then decided to post the code so others would be able to use the code in their projects.
I have created a procedure in a dll that opens a form and then prints a report.
This procedure works perfectly from an exe.
I have wrapped the unit that contains this procedure and forms in a dll and exported the procedure as follows:
{$R *.res}
Procedure PrintTopSellers; stdcall;
begin
Form1 := TForm1.create(nil);
GetMonth := TGetMonth.create(nil);
Form1.PrintTopSellers;
end;
exports PrintTopSellers;
begin
end.
Now I call this procedure PrintTopSellers from an exe as follows:
procedure TForm1.Button5Click(Sender: TObject);
type
TRead_iButton = function :integer;
var
DLL_Handle: THandle;
Read_iButton: TRead_iButton;
Begin
DLL_Handle := LoadLibrary('c:\Catalog.dll');
if DLL_Handle <> 0 then
begin
#Read_iButton:= GetProcAddress(DLL_Handle, 'PrintTopSellers');
Read_iButton;
end;
application.ProcessMessages;
FreeLibrary(DLL_Handle);
end;
The call to the procedure works perfectly. However, after I close the calling exe, I get an access violation - "Access violation at address 00BAC89C. Read of address 00BAC89C."
Appreciate any assistance. I am using Delphi 7.
Thanks
You are creating Form1, a windowed control, in the DLL. But you never destroy it. Then you unload the DLL which unloads the code that implements the window procedures for all windows created by the DLL. Presumably when the process shuts down, the window procedures are called, but there is no code there anymore.
Fix the problem by destroying all objects that the DLL creates. It looks to me like the best approach is to do that when PrintTopSellers terminates.
Procedure PrintTopSellers; stdcall;
begin
Form1 := TForm1.create(nil);
try
GetMonth := TGetMonth.create(nil);
try
Form1.PrintTopSellers;
finally
GetMonth.Free;
end;
finally
Form1.Free;
end;
end;
In the code that loads the DLL, TRead_iButton is declared incorrectly. It should be
TRead_iButton = procedure; stdcall;
But that doesn't actually explain the problem here since the signature mismatch is benign for a parameterless procedure.
"TRead_iButton = function: integer; register;"
"Procedure PrintTopSellers; stdcall;"
Absolutely different conventions/types, ain't them ?
Make them the same.
And better ditch DLL and use packages (BPL), then compiler would make you safe from such errors
We also don't see the code neither in Form1.PrintTopSellers nor in TGetMonth. The all can leave some dangling pointers in the host exe, that would get accesses after DLL unloaded.
Show exactly chain of function calls leading to AV - it is called stack trace.
Debug info + some excaption interrupt like Jedi CodeLibrary (used by Delphi IDE) madExcept, EurekaLog, synopse log and a lot of other exist.
Display the call stack in a Delphi Win32 application
Does DLL or EXE use Runtime packages ?