Can Dlls provide modularity to main program? - delphi

Simple task:
I would like to make a program (parent.exe). There are three buttons. When I click Button1, Form1 appears; when Button 2, Form2 appears; when Button3, Form3 appears...
Form1, Form2, Form3 are stored in three different dlls (Form1dll.dll, Form2dll.dll, Form3dll.dll).
I wanted to make parent program (parent.exe) run modular. I planned to add and remove dlls, but Parent.exe requires all dlls to be present, otherwise an exception occures.
How can I solve the problem?
Thanx
Here is code from parent.exe:
procedure ShowForm1;stdcall;external 'Project1dll.dll' name 'ShowForm1';
procedure ShowForm2;stdcall;external 'Project2.dll' name 'ShowForm2';
procedure ShowForm3;stdcall;external 'Project3.dll' name 'ShowForm3';
var
ParentForm: TParentForm;
implementation
{$R *.DFM}
procedure TParentForm.Button1Click(Sender: TObject);
begin
ShowForm1;
end;
procedure TParentForm.Button2Click(Sender: TObject);
begin
ShowForm2;
end;
procedure TParentForm.Button3Click(Sender: TObject);
begin
ShowForm3;
end;

The way you've got it set up, the program looks for the DLLs at load time. What you need is to set the DLLs up as plugins. Take a look at the JVPlugin framework in the JVCL. It has exactly what you're looking for.

Yes it can by having the EXE Dynamically load the DLLs using LoadLibrary and GetProcAddress. See http://www.scalabium.com/faq/dct0130.htm for an example.
Next you might run into other problems because the memory manager and types are not shared between the EXE and the different DLLs. You might need to take extra care to circumvent these problems or look for solutions. Like runtime packages/BPLs or special memory managers.

Related

How to properly publish an event executed from the 'Loaded' procedure?

In a runtime only package, I've defined a TFrame descendant which publishes the OnLoaded event:
type
TMyMethod = procedure() of object;
TMyFrame = class(TFrame)
protected
FOnLoaded : TMyMethod;
procedure Loaded(); override;
published
property OnLoaded : TMyMethod read FOnLoaded write FOnLoaded;
end;
implementation
{$R *.dfm}
procedure TMyFrame.Loaded();
begin
inherited;
if(Assigned(FOnLoaded))
then FOnLoaded();
end;
In a designtime only package, I've registered TMyFrame component as follows:
unit uMyRegistrations;
interface
uses
Classes, uMyFrame;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('MyTestComponents', [
TMyFrame
]);
end;
I've installed the designtime package, I can find TMyFrame in the tool palette and its OnLoaded event is shown in the object inspector.
I've dragged a TMyFrame into a form, then I've assigned the OnLoaded event by doubleclicking from the object inspector.
After assigning the event, I noticed that an access violation error message appears each time I try to open the form's file in Delphi (It let me open the ".pas" file, but I can't switch to visual designer view).
Did I correctly published the OnLoaded event? If so, what else is wrong?
Further Informations:
I'm using Delphi 2007 (don't know if it matters).
The error also appears by doing the same thing with different parent classes (Not only for TFrame descendants).
Updated (somewhat less bogus) answer
You accepted my original answer, but what I wrote was not correct. Rob Kennedy pointed to an article by former Embarcadero developer Allen Bauer on the topic of Assigned.
Allen explains that the Assigned function only tests one pointer of the two pointers in a method pointer. The IDE at design time takes advantage of this by assigning sentinel values to any published method properties (i.e. events). These sentinel values have nil for one of the two pointers in the method pointer (the one that Assigned checks), and an index identifying the property value in the other pointer.
All this means that False is returned when you call Assigned at design time. So long as you check published method pointers with Assigned before calling them, then you will never call them at design time.
So what I originally wrote cannot be true.
So I dug a bit deeper. I used the following very simple code, testing with XE7:
type
TMyControl = class(TGraphicControl)
protected
FSize: Integer;
procedure Loaded; override;
end;
....
procedure TMyControl.Loaded;
begin
inherited;
FSize := InstanceSize;
end;
....
procedure Register;
begin
RegisterComponents('MyTestComponents', [TMyControl]);
end;
This was enough to cause an AV in the IDE at design time whenever the Loaded method was executed.
My conclusion is that the IDE does some rather underhand things when streaming, and your objects are not in a fit state to use when the Loaded method is called. But I don't really have a better understanding than that.
Original (very bogus) answer
You must not execute event handlers at design time, and your code does just that. The reason being that at design time the event handler's code is not available.
The control's code is available, the IDE has loaded it – but the code that implements the event handler is not. That code is not part of the design time package, it is part of the project that is currently open in the IDE. After all, it might not even compile yet!
The Loaded method should defend against this like so:
procedure TMyFrame.Loaded();
begin
inherited;
if not (csDesigning in ComponentState) and Assigned(FOnLoaded) then
FOnLoaded();
end;

Why doesn't my form receive WM_DropFiles when files are dropped on it?

I am using Embarcadero RAD Studio XE to develop an application. I am trying catch the file(s) drag and drop to the application with the following code
TMainForm = class(TForm)
public:
procedure WMDropFiles(var Msg: TWMDropFiles); message WM_DROPFILES;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
DragAcceptFiles(Self.Handle, True);
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
DragAcceptFiles(Self.Handle, False);
end;
procedure TMainForm.WMDropFiles(var Msg: TWMDropFiles);
begin
inherited;
showmessage('catch here');
// some code to handle the drop files here
Msg.Result := 0;
end;
This code complied without problem. Also, when I drag and drop files, the cursor show that the status changed to drag and drop but after things dropped, nothing happen (no message shown too). Is that anything wrong with that?
In a plain vanilla application, the code in the question results in WMDropFiles executing when an object is dropped on the form. So, clearly there's something else happening to stop it working. The most obvious potential causes are:
The main form's window handle is re-created after the initial call to DragAcceptFiles.
Your process is running at a higher integrity level than the process that is dropping files on it. For example, are you running your process as administrator. Note that running the Delphi IDE as administrator would lead to your process running as administrator when started from the IDE.
Something else in your process is interfering with drag/drop. Without knowing what's in your app, it's hard to guess what this could be. Start removing portions of your app until there's nothing left but the code in the question.
Option 2 seems quite plausible. To learn more see: Q: Why Doesn’t Drag-and-Drop work when my Application is Running Elevated? – A: Mandatory Integrity Control and UIPI
in TForm.Create use two lines
ChangeWindowMessageFilter (WM_DROPFILES, MSGFLT_ADD);
ChangeWindowMessageFilter (WM_COPYGLOBALDATA, MSGFLT_ADD);
that's all

TJvDockServer and dockable controls

I'm programming in Delphi (BDS 2006) and the JVCL library, using the docking modules. I have one problem - if the control has properties DragKind = dkDock and DragMode = dmAutomatic, then inexplicably TJvDockServer component takes the controls are both clients and provides docking. This is wrong, because, as I found out, JVCL's docking functions normally only control class TForm which contain a component class TJvDockClient. I would like to know whether it is possible in some way to prevent TJvDockServer from docking controls whose class is different from TForm? During a typical docking in Delphi for each event is called OnGetSiteInfo dock and it is possible to filter clients, but there is no such event in TJvDockServer.
The property DragKind and DragMode are standard VCL properties. Docking is built into the VCL, and from looking at it, it seems to work pretty good without any Jedi Code involved.
The ability to dock something other than a form, is already built into the VCL. Therefore that you find this inexplicable suggests to me that you thought Jedi added docking to the VCL. No, it just added some pretty things like "tabbed notebook docking" and "conjoined areas" with fake window titlebars.
That being said, Forms are also inheriting from TCustomControl, and any TCustomControl can in fact, be docked. And just like the VCl lets you drag and dock and land on top of TPanels. Okay it's a quirky feature, that your panel can turn into a form on you at runtime, but if you don't believe me, try it. It's the VCL doing this to you, not Jedi.
If in your wisdom, you want to block anything that is not a TForm, I thought that you can.
Surely you can right? Update. Yes you can. OnDockOver works fine to block docking on any panel you want to block docking on. The trick with the Jedi JvDockPanels is that you don't see them at designtime, so you need to access their events by hooking them up in code, at runtime.
Just like regular TPanels, JvDockPanels have a TPanel.OnDockOver event, and if you want to check the thing you're docking, and set the Accept to false, it will be prevented from docking.
Okay, this works:
type
TCustomControlAccess = class(TCustomControl);
procedure TMainForm.FormCreate(Sender: TObject);
begin
TCustomControlAccess(dockServer.TopDockPanel).OnDockOver := MyDockOverEvent;
TCustomControlAccess(dockServer.CustomDockPanel).OnDockOver := MyDockOverEvent;
...
end;
The JvDockPanel.OnDockOver panel events DO fire, but you need to resort to a hack like the above hack, to actually handle the events yourself.
Update previously thought there was no way to block this. But I was wrong. Figured it out.
while i cannot reproduce exactly your behaviour in Delphi XE2, generally i seem manage to block VCL-frag-n-drop for JediVCL components.
Maybe it is not the best possible way, but i don't know which were original ideas of the framework creator.
http://wiki.delphi-jedi.org/wiki/JVCL_Help:TJvDockServer claims only forms should be docked. Did not enforced that, just hardwired JVCL check routine to be always called.
unit JvDockSupportControl;
....
TJvDockCustomControl = class(TJvCustomControl)
....
protected
procedure GetSiteInfo(Client: TControl; var InfluenceRect: TRect;
MousePos: TPoint; var CanDock: Boolean); override;
...........
function TJvDockCustomControl.GetJvDockManager: IJvDockManager;
begin
// Result := IJvDockManager(DockManager);
DockManager.QueryInterface(IJvDockManager, Result);
end;
procedure TJvDockCustomControl.GetSiteInfo(Client: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);
var jdm: IJvDockManager; idm: IDockManager;
begin
idm := DockManager;
if nil <> idm then
idm.QueryInterface(IJvDockManager, jdm);
if nil = jdm
then CanDock := false
else jdm.GetSiteInfo(Client,InfluenceRect, MousePos, CanDock);
end;
unit JvDockTree;
.....
procedure TJvDockTree.GetSiteInfo(Client: TControl;
var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);
begin
CanDock := IsDockable(DockSite, Client);
If CanDock then begin
GetWindowRect(DockSite.Handle, InfluenceRect);
InflateRect(InfluenceRect, DefExpandoRect, DefExpandoRect);
end;
end;
http://issuetracker.delphi-jedi.org/view.php?id=5271
http://issuetracker.delphi-jedi.org/view.php?id=5974

BPL File needs Run-Time Packages !

I have created a Package and i want to use the BPL File of my Package ...
My Package have VCL.dcp and RTL.dcp as Required libraries , i load this Package in my application without any errors but when i want to unload it , an Access Violation shown !
If i Build my Application with Run-Time Packages ( "vcl" and "rtl" ) , Access Violation not shown !
What is this mean ?! My Application need VCL and RTL Libraries to Load BPLs ?! I want to Load my Package like a DLL File , is there any solution ?
I`m using Delphi 2010
thanks a lot ...
Your BPL requires the RTL and VCL packages. If your Application doesn't require them, then that means the RTL and VCL units are compiled into your EXE file. When your EXE loads your BPL, you now have two copies of the RTL and VCL units — one set of copies comes from within the EXE, and the second copies come from the RTL and VCL packages that your package implicitly causes to be loaded.
Delphi isn't intended to accommodate that situation. It's possible that you have memory that was allocated by one RTL and attempted to get freed by the other RTL. Or there might be function pointers in the EXE that refer to functions that were in the VCL package.
I see three options for you:
Compile your EXE to use packages. Specifically, it should require the same RTL and VCL packages that your BPL requires.
Make your BPL not require any other packages. If it doesn't require RTL and VCL, then any RTL and VCL units that your package uses will get compiled into your BPL. You'll end up with two separate copies again, but it should work better since neither copy will think it's supposed to be shared.
Load your package like a real DLL instead of like a package. You said you wanted to use it like a DLL, so do that. Use LoadLibrary, and then use GetProcAddress to get whatever functions you want to call. If you go this route, it's probably better to not make your code be a package at all. Make it a DLL, and export functions that only use parameter types that you'd expect to find in other DLLs, like integers, character pointers, and record pointers, not strings or objects.
It should be clear that the first option is the easiest. The second could probably work, and it sounds like that's the way you'd prefer, but I expect it will generate more headaches before it finally works. The third option is best if you'll ever have to use other development environments during the lifetime of this project.
What have your package inside?
What work do you do with it?
How do you charge and discharge? What's in it?
What do you do with the package before unload it?
When you Unload it, all the objects/forms/components/... that yo've used is released?
ADDED: I Think that you are using anything of the package when you try to Onload. This is the reason of AV.
In an EXE compiled without runtime package, I load the package:
OutputDebugString(PChar('Loading the package'));
hand := LoadPackage('r:\rrrrrrr\Package1.bpl');
I Unload the package with this code:
OutputDebugString(PChar('Ready to Unload Package'));
UnloadPackage(hand);
OutputDebugString(PChar('Unloaded'));
The package has a unit with a form (form1) and a unit Init.pas, for initialization like this:
unit Init;
interface
// prototipos
procedure Start_P;
procedure Finish_P;
implementation
uses
Unit1, Windows;
procedure Finish_P();
begin
OutputDebugString(PChar('Finish_P form free'));
Form1.Free;
end;
procedure Start_P();
begin
OutputDebugString(PChar('Start_P Creating form'));
Form1 := TForm1.Create(nil);
Form1.Show;
end;
Initialization;
Start_P();
Finalization;
Finish_P();
end.
The package is loaded and the form visualized without problems, and the same with the operation of Close and Unload. The project is compiled with "Build with rutime packages" unchecked.
Can you post any code.
The result of OutputDebugString is this (no AV error):
[2644] Loading the package
[2644] Start_P Creating form
[2644] Ready to Unload Package
[2644] Finish_P form free
[2644] Unloaded
Regards.
Thanks for your helps ...
I put an example of my package and my Application here to Find what is the problem !
We have a package without requiring to Run-Time Packages like VCL and RTL , in other words i removed all libraries from the Requires section in my package :
my package contains a form with code below :
unit MyUnit;
interface
uses
Windows, Forms, StdCtrls, Buttons, Controls, Classes, Dialogs;
type
TMyForm = class(TForm)
MyLabel: TLabel;
MyEdit: TEdit;
PostBtn: TBitBtn;
procedure PostBtnClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
MyForm: TMyForm;
implementation
{$R *.dfm}
function ShowForm(FCaption, LCaption : String) : String;
var
F : TMyForm;
begin
F := TMyForm.Create(nil);
try
F.Caption := FCaption;
F.MyLabel.Caption := LCaption;
F.ShowModal;
finally
Result := F.MyEdit.Text;
F.Free;
end;
end;
procedure TMyForm.PostBtnClick(Sender: TObject);
begin
if MyEdit.Text <> '' then
Close
else
ShowMessage('Please Enter Value !');
end;
exports
ShowForm;
end.
I Load this Package and Call ShowForm Function and then Unload package :
var
ShowF : function(FCaption, LCaption : String) : String;
MyPkg : HMODULE;
FC, LC : String;
begin
MyPkg := LoadPackage(ExtractFilePath(Application.ExeName)+'MyPackage.bpl');
FC := 'Enter Value ... ';
LC := 'Value : ';
if MyPkg <> 0 then
begin
try
#ShowF := GetProcAddress(MyPkg, 'ShowForm');
if Assigned(ShowF) then
Edit1.Text := ShowF(FC, LC)
else
ShowMessage('Function not found !');
finally
UnloadPackage(MyPkg);
end;
end;
end;
After the Procedure above done , the AV Shows !
#Neftalí : If I just do loading and unloading the Package , no AV Shows , but i think that is because i don`t call some routines or objects or ... that they need VCL or RTL Libraries , if i use objects and functions and ... of this package , after using them i will get an AV ...
is it true ?!
If I Build my application with Run-Time package ( VCL and RTL ) no AV will shown !
I`m confusing !! , I want to use an BPL package without any Run-Time package needed ...
thanks a lot ...
Yes, if you want to use runtime packages in your application you have to build it with runtime packages, and then it requires them (links statically with them).
The solution to your problem depends on what the problem actually is (which is unclear at the moment).
Ohhhhh, great oversight/neglect (mine).
With the code that you have posted, made a simple change a test it (use PChar).
function ShowForm(FCaption, LCaption : String) : PChar;
...
Result := PChar(F.MyEdit.Text);
...
The same when you define the sitaxis of the function:
ShowF : function(FCaption, LCaption : String):PChar;
Test it and say the result.
Regards.

How to recognize the Registered classes in a Delphi Package

I am going through most of my applications and porting them to D2009 and I have one application that makes use of dynamic packages. For the life of me I cannot get my host application to recognize classes registered in a package. I traced through and the initialization section in the package being loaded was called and RegisterClasses was called but when I do a GetClass() call the classes are not available. Is there someone out there who can enlighten me as to what might be going on? I have researched and looked to see if there are any issues with the D2009 release and dynamic packages and so far I have found nothing. I'm beginning to wonder if I have a corrupted installation of Delphi or some other problem.
TIA
If you are using a 3rd party memory manager then make sure it is proven to work with D2009 (actually 2007 and up).
With FastMM (which is the default MM since 2007) you would have to set the UseRuntimePackages define in FastMM4Options.inc
make sure that the following steps are done:
Create a new package in Delphi;
Insert a form in this package;
Insert a "inicialization" section in the form and uses the RegisterClass method. (registerClass(TForm1)); Don't forget the "T".
Save and compile the package;
Close all;
Copy the .bpl file (c:\Users\Public\Documents\RAD Studio\5.0\Bpl) to the application folder;
Create a new aplication in Delphi;
Go in Project > Options > Packages, and check the box "Build with runtime packages";
Leave only "vcl;rtl" in the text field and click OK button;
Insert a button;
In the source of the button, insert the code:
procedure TForm1.Button1Click(Sender: TObject);
var
PackageModule: HModule;
AClass: TPersistentClass;
begin
PackageModule := LoadPackage('Package1.bpl');
if PackageModule <> 0 then
begin
AClass := GetClass('TForm2');
if AClass <> nil then
with TComponentClass(AClass).Create(Application)
as TCustomForm do
begin
ShowModal;
Free;
end;
UnloadPackage(PackageModule);
end;
end;
Compile the application. =)

Resources