VCL Style from DLL is affecting TMenuItem in Application - delphi

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);

Related

Can I enable texthint if styleservices is not enabled?

I'm injecting a number of forms into an existing application using a dll.
I don't control the existing application, nor do I have source code for it.
(I doubt the source code exists any more).
I want to show a TextHint in a TEdit.
In the form designer this works, but in the application it doesn't.
I traced it to the fact that StyleServices (This used to be called ThemeServices (now deprecated)) is not enabled, disabling the TextHint.
Obviously I cannot enable styles for the application, all I have is a dll.
Is there a way to show the texthint?
I prefer to use a stock TEdit.
The dll is written in DX and the old application is written in D7.
BTW I don't care a hood about any additional styling/theming or the like. I just want the texthint to display.
Is there a way to show the texthint?
Standard TextHint functionality in a stock TEdit is dependent on the EM_SETCUEBANNER message, which only works when Visual Styles are enabled:
Note To use this API, you must provide a manifest specifying Comclt32.dll version 6.0. For more information on manifests, see Enabling Visual Styles.
If Visual Styles are not enabled in the app you are injecting your code into, then the only way to do what you are asking for is to subclass the TEdit window and custom-draw it manually when its text is empty.

Using FMX forms in a VCL application in XE7?

Even though it is not officially supported by Embarcadero there are many examples showing that you can include a FMX form in a VCL application e.g. MonkeyMixer and this SO question.
However, when I create a test application with only one empty VCL form and one empty FMX form, I get two problems:
There are two application icons in the task bar (apparently one for
each instance of TApplication i.e. VCL and FMX)
It crashes when I close the application (when it calls TStyleManager.UnInitialize in FMX.Forms.FinalizeForms).
How can I make this work?
I need this combination as we want our application to be native on Windows, Mac OS and iOS. Therefore on Windows it is a VCL application and the other OS's are FMX using the TMS native components. We have some large custom graphical components that are made for FMX, and they must also work on Windows.
Edit:
I see only two alternative solutions, and I like none of them:
Use FMX on Windows too. I don't like the idea of styled components instead of native. Experienced users can easily tell the difference.
Maintain to sets of our custom components: VCL and FMX editions. That will require some work, and also the graphical features of FMX are much better than VCL.
I've needed to host an FMX app inside a VCL app for display and training purposes. The FMX app is really an Android target and the VCL a Windows 'demonstrator'. The FMX hosting is done using TFireMonkeyContainer hosting the FMX main form. Yes it's got slight wrinkles but it works ok and I'm sure we'll find a way to improve things.
My FMX main form is created at runtime and has visibility of only FMX.Forms. It is then passed to TFireMonkeyContainer and is destroyed by it when the VCL app closes.

Delphi XE6 TStyleManager on ActiveForm screws up host application [duplicate]

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);

Memory management issues between InnoSetup and Delphi DLL

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...

how to load delphi tframe from dll to delphi application

i have created a dll with tframe .
how can i load it inside my application, i dont like to use bpls , i only want to destribute exe and dlls with my app
Since a BPL is a DLL, go the BPL way: much easier.
It can be done but it's a hell of a job to get it working without errors or memory problems. To make matters worse, you will be using two VCL's in your application, one in your executable and another in the DLL. Your frame would try to refer to the DLL VCL, which would provide very different information than the EXE VCL. Especially when checking the global Screen and Application variables.
Still, a frame is nothing more than a special window control, just like forms. You could export a function from your DLL which would return a value of type TFrame. Your application would be able to call this function and thus create the frame, use it in any way it uses all other frames. It won't have any specific information about additional functionality within your frame, though.The next thing you'd have to work on is to synchronize the data between the EXE and the DLL that is VCL related. That's not very pretty. Plus, you will probably have some issues when using the tab key to tab through the controls on your screen, since the tab key won't be able to tab outside the frame. And you will notice a few more oddities like this.
I have worked on a simple application that used frames this way. Me, and two others spent two months getting some working solution, which did work reasonable well without memory leaks and other troubles. Before we started that project, it just seemed like a good idea. Afterwards, we decided that it didn't turn out to be the solution we'd wanted so we merged the code of the DLL's with the code of the executable to just create one executable. It was better that way.
We did use another alternative, though. We started using a webbrowser component in the mainform. The DLL would contain a HTML page, nicely formatted, and a bunch of methods that would be called when certain specific functions were used. We had this working in a simple test application with good results but then the company went chapter 11... My employer went dead broke since a deal with some customer misfired badly, leaving the company with some huge debts. And thus an interesting project ended...

Resources