I have a frame:
TfrmMyFrame = class(TFrame)
...
end;
Which is placed on my main form as follows:
interface
uses Myframe;
type
TfrmMyMainForm = class(TForm)
FmyFrm: TfrmMyFrame;
...
end;
In design time I am unable to open my main form, unless I open the frame in the designer. I always have to open my frame form before I can open the main form. Please note that I am talking about design time only. Is this correct behavior? If not, how can I correct it? I am using XE5.
Frames must be included in the project for the designer to add them to or display them on a form. They can still be dynamically added to a form at runtime.
If you examine the dfm of your form you will notice frames are added using the word inline. This is translated by the VCL component streaming system to the enum value TComponentState.csInline and is used to indicate that the component is a top-level container that can be embedded into a form.
The form designer uses the same component streaming system as is used at runtime. My guess is it only parses forms and frames that are currently loaded into the IDE's internal cache. This would explain why you could open the form if you opened the frame first. By adding the frame to the project it will be loaded into a cache when the project is opened in the IDE.
Fun Fact
There is a trick used by IDE extension programmers to design forms that inherit from TDockForm, which is an Open Tools API
component that is only supplied in a precompiled binary package (no
source and no dfm). By implementing a fake TDockForm in a dummy
project that is part of the same project group as the descendent the
IDE can be fooled into loading the fake. This trick relies on the same
behavior you discovered by accident.
Related
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);
I made a custom component that needs a rather large (say 1MB) UTF8 text to function. For development purposes, I just loaded this from a file. Now I want to get rid of that file. What is the easiest way to store its contents in the component, such that the user of the component needn't bother with this?
First I tried making it string constant, but soon Delphi started to complain in many different ways (too long or too many, out of memory, etc.). When I switched to embedding it as a resource, I found that the resource will not be automatically compiled into the actual application as well, so it's not transparent to the component user...
Update
I got it working if I create a resource file myself, which I manually add to the unit. Then, I still need a pre-build event for it to be actual. The question remains why it doesn't work if I add it to the package, via the Delphi menu, rather than to the unit. And why {$R myresource.res myresource.rc} doesn't compile it automatically as it should...?
Update 2
Apparently the resource script needs to be added to the project for automatic compilation to work.
Adding the {$R myresource.res} line in the unit containing the component should work.
You can add the myresource.rc file to the component's package to make Delphi generate myresource.res automatically.
I am currently designing a database interface in DELPHI 2010, and I have designed several form interfaces, all encapsulated in one Delphi project structure.
I began designing a particular interface form first, and, as a result, an irrelevant form first appears when I run the .exe file, whereas I want a menu form to appear when the program is loaded. My questions are:
How can I get my menu form to open first when and .exe of my compiled project is run?
What function do I need to use to program navigation buttons on this menu to open new forms up?
What function should I use on the "Exit" buttons each of my forms to close the forms down individually, without closing the whole program?
I have good knowledge in Pascal, but I am new to programming object orientated solutions in this platform.
You should determine if you forms would be auto-created or created on demand.
You should also decide which form would be main one.
The corner question would be if you can have several open forms of the same class, for example if you made "File Viewer" form there may be sense to have several of them open for different files.
For auto-created forms:
1.1 Open project source (.dpr file, Project/View Source menu) or open Project options in Forms section. Set MenuForm the 1st (topmost) one in creation list.
1.2 Check that all other forms have their .visible property false
2: Depending on the logic of your program you should use Form1.Show or Form1.ShowModal
3: Self.Close or Self.Hide or Self.Visible := false. Better 1st: http://docwiki.embarcadero.com/Libraries/XE2/en/Vcl.Forms.TCustomForm.Close
If u use OnClose event of those forms - ensure you did not changed default caHide action for closing
For manually lifetime controlled forms:
1: Open project source (.dpr file, Project/View Source menu) or open Project options in Forms section. Set MenuForm the only one created.
2.1. some-temporary-variable := TFormClass.Create(Application);
2.2. Then you tweak some properties of some-temporary-variable like filename to open or some data source or whatever.
2.3. Then you do some-temporary-variable.Show or some-temporary-variable.ShowModal.
Beware: using ShowModal may freeze your application, in cases like TFormClass.Create(SomeAnotherForm), use Application for parent.
3: Self.Release http://docwiki.embarcadero.com/Libraries/XE2/en/Vcl.Forms.TCustomForm.Release
or Self.Close and specify caFree action in OnClose event - http://docwiki.embarcadero.com/Libraries/XE2/en/Vcl.Forms.TCustomForm.OnClose
I have some forms inherited from a TMyForm (TMyForm is a 3rdparty component with source code form).
When I open in the IDE my forms inherited from TMyForm I have:
Error creating form: Ancestor for
'TMyForm ' not found.
The workaround is to open the TMyForm unit in the IDE and then try top open my inherited forms.
But how can I avoid that Delphi gives this error and is able to open my forms even if TMyForm is not opened in the IDE?
You need to have the parent form opened in the IDE or added to the project, there's no workaround, unfortunately. Over here: Register custom form so I can inherit from it from multiple projects, without copying the form to the Object Repository folder I attempted lots and lots of things, without success.
My Workaround. There are several ways:
Add to project full path to all of the parent form. Uncomfortable, because of absolute path
Add package with parent forms to the project list. In this case, no problems there. But every time it is necessary to open a components
in the delphi environment.
Use a temporary solution from embarcadero.
There is automatically opens all the child forms:
Ticket, was created about 10 years ago: http://qc.embarcadero.com/wc/qcmain.aspx?d=8376
Here you can download the version for Delphi XE2: https://bitbucket.org/hemn/autoopenunit
I recommend it!
I've been searching all over for a solution to this and it seems I've finally found one.
I'm using a package with several base forms and frames.
I've added these to the repository, yet still the error came up.
Some of the forms and frames didn't show a 'dfm' in the Project Manager, it seems key to this, is to edit the .dpr file and add the name in curly brackets:
fIBSConnectionForm in 'GUI\BaseGUI\fIBSConnectionFrom.pas',
into
fIBSConnectionForm in 'GUI\BaseGUI\fIBSConnectionFrom.pas' {frmIBSConnectionForm},
and for frames, make sure to use {f...: TFrame}
After doing this and restarting Delphi, I could open descendant forms again!