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.
Related
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!
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)
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'm just trying to make a new procedure so i don't need to rewrite it again.
i'm very sorry cause i'm new in delphi. i just wanna make a simple code on writing dnname.text but i don't wanna write it in every event.
like this
procedure link ;
var
dbnam : string;
form : Tform1;
begin
dbnam := 'squire';
form.dnname.text := dbnam;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
link;
end;
procedure Tmain.activate(Sender: TObject);
begin
link;
end;
procedure Tmain.datachange(Sender: TObject; Field: TField);
begin
link;
end;
but i've got error on 0074A43C6 from Form1.
The variable declaration
var
form : TForm1
only tells the compiler which type (TForm1) the variable has, but not which instance of this form should be used (there could be many forms of type TForm1 instantiated). In Delphi, you have to always initialize local variables before you use them.
You have a couple of options:
Do not use a local variable, but use the global form instance instead (it should be called Form1 in your case). But in this case your utility function is bound to a specific form instance, which is usually not a good idea.
Implement your utility function as a method of class TForm1. In this case you can reference the form instance implicitly or using Self.
Pass the form instance as a parameter to your utility function.
The default, Delphi generated, code for a new form will look something like this:
type
TForm1 = class(TForm)
{ lots of stuff }
end
This describes what "TForm1" looks like but does not create an "instance" of one.
(think of an "instance" as a UFO: just because you told me what the UFO looked like doesn't mean that there is a UFO. You have to actually create one for me to think you're not crazy)
You will also see something like the following in the same file:
var
Form1: TForm1;
This allocates some memroy to store an instanance of TForm1, but, also, does not create one.
In your .dpr file you may see something like this:
Application.CreateForm(TForm1, Form1);
That creates and instance of TForm1 and stores it in the variable Form1.
You can also do a similar thing manually:
Form1 := TForm1.Create(nil)
That also creates an instance of TForm1 and stores it in the variable Form1.
Your (slightly simplified) supplied code, looks like this:
procedure link;
var
form1: TForm1:
begin
form1.dnname.text := 'squire';
end;
In the context of procedure "link", "form1" has not been assigned a value, therefore it will contain a random value
(this is not entirely true, but will surfice for now)
You will either need to assign "form1" a (valid) value or ensure that "form1" already references a valid value.
The following may work for you:
procedure link;
begin
form1.dnname.text := 'squire';
end;
But please, please, take the time to understand what is happening.
There will be a lightbuld moment.
And I can assure you that every single super-high-karma stack overflow poster has had that lightbulb moment for themselfs...
Local variable form is not initialized here, so Access Violation occurs.
Would you transform this procedure to TForm method - to freely using form components?
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.