i have a test.bpl package in my application and it has a form named myPackageForm.
after loading my Package when i want to close myPackageForm, Application will Terminate.
Main Project Initialization:
Application.Initialize;
Application.CreateForm(TMainForm,MainForm);
Application.Run;
MainForm FormCreate :
aPackage := LoadPackage('my bpl path'+test.bpl);
#P_ItemClick := GetProcAddress(aPackage,'ItemClickExcecute');
MainForm ButtonClick:
P_ItemClick(Sender);
testPackage CommandsUnit :
Procedure ItemClickExecute(Sender : TObject)
begin
TmyPackageForm.ShowForm();
end;
exports
ItemClickExecute;
end.
myPackagForm has a class procedure to show it:
Class Procedure TmyPackagForm.ShowForm;
begin
if not Assigned(myPackagForm)
myPackagForm := Application.CreateForm(TmyPackagForm ,myPackagForm );
myPackagForm.Show;
end;
And in OnClose Procedure has:
Release;
As a solution i try another command:
myPackagForm := TmyPackagForm.Create(Application);
to myPackagForm creation;
Can anybody know what happend in Delphi XE4 with CreateForm command on the packages?
Sir Rufo has got the basics covered. The first form created with a call to CreateForm becomes the main form. When the main form closes, the application closes.
Now, you have shown two calls to CreateForm. One in the host application, and one in your dynamically loaded package. And it seems clear that the first one to run is from the host application. So, how can the form in the package can bring down the application? Well, that can only happen, as Sir Rufo said, if there are two application instances in your process.
So the next question is, how can there be two application instances in your process? That should not happen. The entire point of packages is to allow Delphi units to be shared between different modules. If you have built your application correctly then you will have only one instance of TApplication that is shared between the host executable, and all of your packages.
So, the only reasonable conclusion to reach is that one of your modules is not built using runtime packages. For instance, perhaps the host application includes the RTL/VCL, linked statically inside the executable file. And your packages link to the RTL/VCL runtime packages. Or perhaps it is the dynamically loaded package that includes the RTL/VCL linked statically. Although IIRC, the compiler prevents that happening.
Whatever has gone wrong, the solution is that all your modules must be built using runtime packages. All the RTL/VCL libraries must be linked via runtime packages and must not be linked statically.
So, replacing Application.CreateForm with TMyPackageForm.Create is simply suppressing a wider problem. It is critical that you have only one instance of the RTL/VCL in your application. Achieve that by using runtime packages in all your modules.
It is certainly the case that TMyPackageForm.Create is the right way to create a form in your package. I only use Application.CreateForm for the main form. I never use it anywhere else. But don't try to fix your immediate problem solely by removing Application.CreateForm from your package. Fix the linking to the RTL/VCL.
TApplication.CreateForm not only just create a form, but also make the first form created by this method the TApplication.MainForm.
And closing the MainForm results in closing the application.
UPDATE
You have 2 TApplication instances inside your application.
your normal application
inside the BPL
So your form inside the BPL will become the MainForm of your BPL.Application and closing this form will perform a PostQuitMessage(0) in the context of your main application process and this forces the whole application to quit.
Related
We have a COM server written in Delphi 10.2 Tokyo which contains 4 classes, all of which inherit from TAutoObject, and all have an initialization section containing the call to TAutoObjectFactory.Create.
In a typical installation, we have approximately 60 other programs that make use of the classes in the COM server. This all works fine. Each of the classes has their own GUID and the Delphi COM server does its usual thing of ensuring they are all registered correctly as part of the InitComServer process that runs from Application.Initialize.
We now have a need to run multiple installations side-by-side, where they may be different versions. Since we don't control all of the programs, we can't produce a different version of the COM server with different GUID's for every installation.
I had found this Microsoft document:
DLL/COM Redirection on Windows
But that approach does not work. When the main program starts and creates an instance of the first COM server class, I can see that it is the registered EXE that is running, not the COM server EXE in the same folder as the main program. This might be because it's an EXE and not a DLL, and Delphi's initialization is circumventing the DLL/COM redirection.
Here's an example of using one of the COM classes:
function ProduceReport(const ATenantID, AReportID: Integer; const AFilter: String): String;
var
AReportServer: IReportServer;
begin
Result := '';
AReportServer := CoReportServer.Create;
try
if AReportServer.Connect(ATenantID) then
begin
if not AReportServer.Print(AReportID, AFilter) then
Result := AReportServer.GetLastError;
end
else
Result := AReportServer.GetLastError;
except on E: Exception do
Result := E.Message;
end;
AReportServer := nil;
end;
The main thing of note is that the CoReportServer.Create does this internally:
Result := CreateComObject(CLASS_ReportServer) as IReportServer;
which in turn is doing this:
OleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, IUnknown, Result));
I can't see what CoCreateInstance is doing, but I am wondering if there is a way to call CoGetInstanceFromFile instead? Perhaps by changing the CoReportServer.Create in the TLB to use my own code instead of the CreateComObject?
Would changing the call to TAutoObjectFactory.Create to use a different TClassInstancing, such as ciSingleInstance, make any difference?
I just want it so that when a program calls CoReportServer.Create, it creates an instance of the server from the EXE it has in the program's folder, not by looking up the InprocServer from the Registry. I don't want the Registry to be used at all.
[Edit]
Since we also have an OCX that will need to be registry-free, I followed the steps discussed in this post:
Generate manifest files for registration-free COM
I have a manifest file for the OCX test app and a manifest file the OCX itself, both of which are in the same folder as the test app and the OCX. The manifest file specifies the same version number of the OCX as the one in the folder.
I then copied an old version of the OCX to a different folder, ran an elevated CMD prompt, and used Regsvr32 to register that OCX in that folder.
Running the OCX test app, which includes the ActiveX control on the main form, I can see from Process Explorer that it is using the registered OCX, and not the OCX in the same folder.
This may indicate that Delphi EXE programs can't handle the manifest approach to this, even if I include the custom manifest in the Project Options.
We have a mix of COM server EXE, ActiveX visual control OCX, and ActiveX type library DLL, all written in Delphi 10.2 Tokyo, that we need to make registry-free.
[Edit 23/03/2022]
I've made some progress on this by referencing the information contained in the answer for Registration-free COM/DLL?
I now have a manifest file for the DLL containing an <assemblyIdentity> tag, a <file> tag containing a list of <comClass> tags for each CLSID in my DLL, and a list of <comInterfaceExternalProxyStub> tags to link the CLSID's and IID's with the interface name.
And there is a manifest file for the host EXE, that includes the same <assemblyIdentity> tag information, but contained within a <dependentAssembly> tag.
Using SxSTrace, I can see "INFO: Activation Context generation succeeded.", so at least the side-by-side configuration is correct.
However, when running the test program, at the point it is calling CoReportServer.Create, it is falling over with a "Error loading type library/DLL" message. Based on the stack trace, this is from the LoadTypeLibrary call inside the TAutoObjectFactory.Create process from the initialization section of the first class implementation of the first interface.
Since the SO answer I looked at mentions making sure it will work when the DLL is registered, I tried using regsvr32 from an elevated command prompt, but get a "Runtime error 217" popup, followed by "The module "reportserver.dll" failed to load. A dynamic link library (DLL) initialization routine failed."
The Result from LoadTypeLibEx is -2147312566, which doesn't seem to match the TYPE_E_CANTLOADLIBRARY result.
So it looks like I may have got the registry-free COM part to work, but the conversion of the out-of-process COM server to an in-process COM library isn't right.
So I went back to the COM server EXE, and re-registered that to confirm the test program still works. It did. Then I altered the test program manifest to point to the COM server EXE instead of my trial conversion DLL, made a new manifest file for the COM server EXE, containing all the IID's and CLSID's, and un-registered the EXE.
The test program reports "Class not registered", so that pretty much confirms the activation context approach only works on DLL's.
That leaves me with figuring out why the DLL will not load, although another issue I just thought of is that the DLL is in-process, meaning every program that uses the library has to create it's own instance. That could be very resource intensive.
[Edit 25/03/2022]
Skipping the out-of-process COM server for the moment, I've now taken a look at our ActiveX Control, which is a descendent of a TProgressBar and is used either on a VCL form or created at runtime.
Following the same approach for defining the manifest file, I've created the <assemblyIdentify>, <file> and <comInterfaceExternalProxyStub> tags, but I noticed that in the _TLB.pas, there is one extra TGUID for DIID_IReportControlEvents.
Reviewing the Microsoft documentation for manifest files, I cannot see any reference to an events type of interface, so I'm not sure how that one will work.
In any case, a manifest file doesn't work for an OCX. I just get the "Class not registered" error.
The SxStrace file shows "INFO: Activation Context generation succeeded." but it doesn't include any information about the manifest for the OCX, so it is not loading it at all.
This is probably because the _TLB.pas for the OCX shows it is using OleControl rather than COM. Removing the {$E ocx} from the DPR means Delphi creates a DLL instead, but that also fails.
I am using Delphi XE6 and VCL styles. I have main application and dlls. My main application has enabled runtime themes and I am using vcl style files. I did quite similar thing to my DLLs. I enabled runtime themes and added VCL.Themes, VCL.Styles under uses and resource file with VCL style file within it. When DLL is loaded I load VCL style from resources and set it for DLL gui. Main app and DLL are not built with runtime packages.
Now I have main app GUI styled with own style and DLL gui styled with own style too. This seems to work fine until...
When I click on button in my main app which event opens TPopupMenu it's styled with same style as DLL GUI instead of main app style. If I navigate through menu I get AV too and program crashes. Take a look at the attached image.
What am I doing wrong? The only workaround I currently see would be to make my own customized TPopupMenu derived from some other control.
As I promised I prepared simple demo program which is similar to my application. It consists of host application with own style and DLL with style added to resource. Run it and click on button Popup then try select something from popup. It will crash and stop in some StdWindowProc or something like that. Also if you go to window system menu (left top corner) when you try to select something from that menu you will notice that system menu is styled as DLL gui and crashes too. Link to rar file: dropbox.com/sh/f2jmbsmw18akpyg/AAA6SWdBmVhf6n6K-mvYLLmua?dl=0
Thanks for your help.
This is a fundamental problem with VCL styles and the way that they style menus. The styling is implemented with a process wide hook. Specifically a CBT hook installed by a call to SetWindowsHookEx from TCustomStyleEngine.CreateSysHook in the Vcl.Themes unit. In fact, the hook applies just to the GUI thread, but that is process wide in the sense that there is exactly one GUI thread in the process.
Since you have multiple instances of the VCL in your application (one in the DLL and one in the application), two hooks are installed. That is one too many. The hook installed most recently (the DLL as it happens) wins, and that's why the DLL menu styling infects your executable. And why you encounter an access violation. The DLL is trying to operate on a menu that belongs to the executable. And so, in spite of your best efforts, you've ended up with the DLL code accessing VCL objects from the host executable.
There's no simple way to work around this and support styles fully in both modules. What we have here is a fundamental consequence of the design. The system was not designed to support multiple VCL instances. If you wish to use VCL styles in multiple modules, then the designers expect you to use runtime packages.
I suppose that you might be able to get some traction by operating the DLL out of a completely different thread. That would involve loading the DLL from that different thread so that the VCL is initialized in the thread. And all calls to the DLL would have to be from that thread. And you'd need to run a message loop in that thread. It's possible that you might be able to make that work, but I doubt it. Even with all the provisos mentioned you still have to handle the fact that you have two GUI threads which presents all sorts of issues with the input queue handling.
Perhaps another approach would be to uninstall the hook from the DLL. So long as your DLL is not showing menus then you may well be able to get away with uninstalling that hook. It would disable styling for menus shown by the DLL, but perhaps that's acceptable.
This version of your DLL (after I simplified it somewhat also) uninstalls the hook.
library VCLStyleDLL;
{$R 'Style.res' 'Style.rc'}
uses
VCL.Styles,
VCL.Themes,
VCL.SysStyles; // to gain access to TSysPopupStyleHook
{$R *.res}
begin
TStyleManager.TrySetStyle('Glossy', false);
TCustomStyleEngine.UnRegisterSysStyleHook('#32768', TSysPopupStyleHook);
end.
With this version of the DLL, the host executable does not suffer the problems your describe in your question.
As David says this is caused because each VCL instance install a hook to detect when a popup menu (#32768) is created. So there is two hook instances working at the same time.
As workaround you can disable the popupmenu style hook in the dll (or in the app) using the UnRegisterSysStyleHook function defined in the Vcl.SysStyles unit.
TCustomStyleEngine.UnRegisterSysStyleHook('#32768', TSysPopupStyleHook);
I am using Delphi XE6 and VCL styles. I have main application and dlls. My main application has enabled runtime themes and I am using vcl style files. I did quite similar thing to my DLLs. I enabled runtime themes and added VCL.Themes, VCL.Styles under uses and resource file with VCL style file within it. When DLL is loaded I load VCL style from resources and set it for DLL gui. Main app and DLL are not built with runtime packages.
Now I have main app GUI styled with own style and DLL gui styled with own style too. This seems to work fine until...
When I click on button in my main app which event opens TPopupMenu it's styled with same style as DLL GUI instead of main app style. If I navigate through menu I get AV too and program crashes. Take a look at the attached image.
What am I doing wrong? The only workaround I currently see would be to make my own customized TPopupMenu derived from some other control.
As I promised I prepared simple demo program which is similar to my application. It consists of host application with own style and DLL with style added to resource. Run it and click on button Popup then try select something from popup. It will crash and stop in some StdWindowProc or something like that. Also if you go to window system menu (left top corner) when you try to select something from that menu you will notice that system menu is styled as DLL gui and crashes too. Link to rar file: dropbox.com/sh/f2jmbsmw18akpyg/AAA6SWdBmVhf6n6K-mvYLLmua?dl=0
Thanks for your help.
This is a fundamental problem with VCL styles and the way that they style menus. The styling is implemented with a process wide hook. Specifically a CBT hook installed by a call to SetWindowsHookEx from TCustomStyleEngine.CreateSysHook in the Vcl.Themes unit. In fact, the hook applies just to the GUI thread, but that is process wide in the sense that there is exactly one GUI thread in the process.
Since you have multiple instances of the VCL in your application (one in the DLL and one in the application), two hooks are installed. That is one too many. The hook installed most recently (the DLL as it happens) wins, and that's why the DLL menu styling infects your executable. And why you encounter an access violation. The DLL is trying to operate on a menu that belongs to the executable. And so, in spite of your best efforts, you've ended up with the DLL code accessing VCL objects from the host executable.
There's no simple way to work around this and support styles fully in both modules. What we have here is a fundamental consequence of the design. The system was not designed to support multiple VCL instances. If you wish to use VCL styles in multiple modules, then the designers expect you to use runtime packages.
I suppose that you might be able to get some traction by operating the DLL out of a completely different thread. That would involve loading the DLL from that different thread so that the VCL is initialized in the thread. And all calls to the DLL would have to be from that thread. And you'd need to run a message loop in that thread. It's possible that you might be able to make that work, but I doubt it. Even with all the provisos mentioned you still have to handle the fact that you have two GUI threads which presents all sorts of issues with the input queue handling.
Perhaps another approach would be to uninstall the hook from the DLL. So long as your DLL is not showing menus then you may well be able to get away with uninstalling that hook. It would disable styling for menus shown by the DLL, but perhaps that's acceptable.
This version of your DLL (after I simplified it somewhat also) uninstalls the hook.
library VCLStyleDLL;
{$R 'Style.res' 'Style.rc'}
uses
VCL.Styles,
VCL.Themes,
VCL.SysStyles; // to gain access to TSysPopupStyleHook
{$R *.res}
begin
TStyleManager.TrySetStyle('Glossy', false);
TCustomStyleEngine.UnRegisterSysStyleHook('#32768', TSysPopupStyleHook);
end.
With this version of the DLL, the host executable does not suffer the problems your describe in your question.
As David says this is caused because each VCL instance install a hook to detect when a popup menu (#32768) is created. So there is two hook instances working at the same time.
As workaround you can disable the popupmenu style hook in the dll (or in the app) using the UnRegisterSysStyleHook function defined in the Vcl.SysStyles unit.
TCustomStyleEngine.UnRegisterSysStyleHook('#32768', TSysPopupStyleHook);
Situation:
a DLL that:
is written and compiled with Delphi XE3 32bit
exports a function that displays a modal VCL-form:
function EditOptions(AIniFileName: PAnsiChar; ALCID: Integer): Boolean; stdcall;
is also a COM-server
an installer created with InnoSetup 5.5.4:
imports said DLL function via external 'EditOptions#files:mydll.dll stdcall'-directive and calls it when needed
explicitly unloads the DLL before registering the COM server via the Inno utility function UnloadDLL
Problem:
When...
Windows is using a theme other than Classic
the modal dialog from the DLL is displayed during installation
the installer hangs upon unloading the DLL
no error messages are shown
logs indicate no runtime exceptions
Please note that to keep this brief, I deliberately left out the explanations for some of the more idiosyncratic details, e.g. why I'm explicitly unloading the DLL or why a COM server needs to have custom exports. Let me know if you think these details would be relevant and should be added.
To further analyse this I created a dummy host EXE that calls the DLL function so I can easier run the whole thing in the debugger. Doing so leads to an AV being raised in TUxThemeStyle.UnloadThemeData on the line that calls CloseThemeData(). When placing a breakpoint directly on that line and stepping forward from the stop, I actually get a "too many consecutive exceptions" error eventually. I can only reproduce the error when I enable runtime themes for the host EXE.
Going back through our git history it appears that this issue exists ever since we switched from Delphi 2010 to XE3 (unfortunately, the installer was not part of our regular test suite so this only cropped up now as work on the actual release comes to a close as the DLL exports exist exclusively for the benefit of the installer).
Any ideas what might be causing this? Could this really be theme-related (I have no custom theme-related code in this project) or is that maybe just a symptom of memory corruption caused somewhere else entirely? E.g. I once had an error that manifested in the form of AVs in the theme tear-down code before but in that case it actually turned out to be caused by the leaking of anonymous method references from initialization sections...
Update: I've worked hard trying to produce a dumbed-down sample project to demonstrate the issue in isolation but so far without luck. I have managed to narrow down a few more things, though. First, here are some more facts about the code:
The dialog I'm showing is a third-degree descendant of TForm. The ancestors accomplish several things:
TAppModalDialog implements a special constructor CreateOnTop that ensures it appears as the topmost window in the current process (the project is a plugin that loads into multi-windowed third-party hosts that do unfortunately not communicate their window handles for use as modal parents) - it accomplishes this by manipulating the global Application.Handle.
TTabbedDialog is part of an interface-based framework that allows registration of "pages" (actually TFrame-descendants) via a factory pattern - at runtime the form will dynamically create TTabSheets in a TPageControl and instantiate and parent the registered page frames to them
I've replaced the call to the actual dialog with a dummy, gradually working myself through the inheritance chain from a blank TForm up to my TAppModalDialog and eventually to my TTabbedDialog class. The problem only starts to occur at that last level. I also commented out all page registrations around the project so that the dialog comes up with an empty page control (this rules out code in any of the pages as the cause for the problem). I then commented out all code and data members in the routines of the TTabbedDialog class itself, leaving only empty method bodies to fulfill the interface implementation requirement and the TPageControl component (along with two empty TImageLists and a TBalloonHint component) -> the error still occurs.
I then copied the call to the dummy dialog into a new DLL project that contains nothing else but this and the tabbed dialog framework. If I load that DLL into my dummy host EXE, the problem does not occur anymore... sigh
So, my suspicion is still that there must be some sort of memory corruption occuring somewhere else entirely and the error I see is only a symptom of that. So the real question is how can I finally get to the bottom of this?
I will now continue by commenting out / removing bits from the production DLL...
I have a class in a unit. Usually, when I changed the algorithm of its methods, I have to recompile it and deliver the patch as a whole.
I think to create the instance of the class using DLL. After searching in delphi.about.com, I found that instead of using DLL, I can use BPL. It is a DLL for Delphi. The problem is almost all examples I found is only telling how to export a function.
I want to dynamically load the BPL, and whenever I replace the BPL, I can get the latest algorithm of the class, not only the functions I export.
Article I have read:
- http://delphi.about.com/od/objectpascalide/a/bpl_vs_dll.htm
- Plugins system for Delphi application - bpl vs dll?
- http://delphi.about.com/library/weekly/aa012301a.htm
Any URL or SAMPLE how to create a BPL from scratch to encapsulate a component or a class is greatly appreciated.
Dear Guru,
Suppose I have code like this:
unit unitA;
interface
type
B = class(TObject)
public
procedure HelloB;
end;
A = class(TObject)
public
function GetB: B;
function HelloA: String;
procedure Help;
end;
implementation
uses
Dialogs;
{ B }
procedure B.HelloB;
begin
ShowMessage('B');
end;
{ A }
function A.GetB: B;
begin
Result := B.Create;
end;
function A.HelloA: String;
begin
Result := 'Hello, this is A';
end;
procedure A.Help;
begin
//do something
end;
end.
I want to export all public methods of A. How to make it a DLL?
How to use it from another unit where to import it?
let's say:
var a: A;
a := A.Create;
a.GetB;
showMessage(a.HelloA);
A is not declared in the unit (it is in the DLL).
Please advise.
Hurray. I got it last night. All I have to do is make the object implement an interface which is used in the caller unit to catch the instance of object returned by the DLL.
Thank you all.
Mason nailed it already, but let me elaborate on why BPLs aren't what you are looking for.
BPLs are a means for the Delphi IDE to load components that share the same memory manager and RTL. (Type identity works almost transparently using BPLs)
However, the dependencies you are getting tied up in are almost always unacceptable. Except for the IDE, which cannot handle different versions of RTL and VCL anyway.
When you pass only interface references between your application and its DLLs, then you don't have to share RTL, VCL or shared packages at all.
It also means that you could write some DLLs in another language (C++, C#, FPC, another Delphi version), and still use objects. Which can be tempting when you don't want to port your main app but still want to use existing libraries that are not available for Delphi, or your version of Delphi.
The problem with putting a class in an external file is that your main application needs to know some way to refer to it. It will either have to descend from a base class that exposes all the methods you need as virtual methods, or implement an interface that contains all the functionality you need from it.
If you already know what the interface of the object should look like, and all you're changing is implementation details such as internal algorithms, probably the easiest thing would be to make your class implement an interface and put it in a DLL that exports a function that returns an instance of this interface. That way you don't need to worry about breaking your app up into packages, which can be a real hassle.
I see nothing in your problem description suggesting you would need to explicitly export anything from the package or that you would need to load it dynamically at run time. Instead, it's enough that your functions reside in a run-time package that can be replaced separately from the main program.
Start a new package project and move your class's unit into that project along with any other units it depends on. Compile the project. If the compiler warns about "implicitly including" any other units, add those to the package, too.
Now, remove any of the package units from the EXE project. There should be no units that are members of both projects. Next, turn on the "build with run-time packages" checkbox in your EXE's project options. Add your package to the semicolon-separated list of package names. The RTL and VCL packages will probably also be on that list.
Compile both projects, and you're done.
If you make changes to your class implementation, you can recompile the package only and send a new version to customers. The program will automatically get the new changes when you replace the original file with the new one. The package is listed in the program's import table, so the OS will automatically load the BPL file when it loads the EXE. The EXE doesn't need to run any special code to load the package.
Delphi can create DLL to export functions or BPL to export component.
You can create component, compile it (use the same compiler settings as in your main app), and Delphi will create .bpl. Then import this component to Delphi and compile your app with this compomponent as a package.
My experience with components created with Delphi 4 proved that one, big application is more reliable than application with separate .bpls. It was multithreaded server and it worked fine if compiled standalone, while crashed after short time if compiled with packages. I hope newer versions of Delphi improved in this area.
Be aware of memory management (in app do not free memeory allocated in package and vice versa) and compiler settings.
If you like about.com then this link will be useful: Introduction to Packages; BPLs are special DLLs!
BPLs have their usage. For example if you have to make a very huge application like an Erp, you need to try to use BPLs seriously.
In the other hand, BPLs aren't responsible of crashing applications. Bad usage of BPLs does it.
you can try the MAF Components, they handle plugins and much more for you without extra code. Comes with tutorials and a demo application with source.
http://www.maf-components.com