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...
Related
I have the community edition 10.3 of Delphi on Windows 10 and trying to use the JEDI (JVCL v3.50) TJvHidDeviceControllerClass (v 1.0.35) to control my HID device in a VCL form application.
This exception also gets thrown when I start up Delphi and when closing it down showing a dialog, provided that the component is already present on the form when loading the project. When ran with the OnDeviceCreateError() event handler assigned, the application behaves OK as I set the debugger to let the application handle this particular exception and setting the "Handled" boolean to true inside the OnDeviceCreateError() handler. It also works fine when run outside the Delphi GUI.
I have been searching for clues about fixing this issue and have seen that other people have encountered it as well. As most of the posts about this is quite old I wonder if I don't have the correct version of the library (https://github.com/project-jedi/jvcl).
The JEDI library was fetched from github just a week ago using the instructions on the readme page. My local repo was cloned from the master branch. I built it and installed it locally with no hassle.
Albeit working fine when running the application this exception is a bit of a nuisance as it messes with the Delphi GUI itself during startup (the exception dialog is shown, parts of the GUI goes missing after clicking OK).
I don't think that my code is the culprit here as the problem presents itself before any application code has even begun executing. I suspect that Delphi itself runs the constructor for the component before the OnDeviceCreateError() handler has been assigned and so the exception propagates all the way up to the GUI during palette placement or startup. Why the exception shows while shutting Delphi down is harder for me to explain.
Is there a fix to this or a setting to prevent Delphi running the constructor at design time?
Should I create the TJvHidDeviceControllerClass instance during form creation instead?
Thanks in advance /Thom
After some trial and error I worked around the problem by removing the component from the form designer and adding the TJvHidDeviceControllerClass manually with method bindings to the class in code instead.
The default constructor should not be used as it does not bind the exception handler at create time. Use the other one that does the binding.
It now works fine without stray exceptions at start/stop times.
When creating manually, be shure to explicitly free the TJvHidDeviceControllerClass object instance at form close.
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);
This is occurring on 2 machines that are both running Windows XP Pro SP3, yet it runs OK on my development machine within in or outside the Delphi IDE.
Running Windows XP Pro. Exe compiled under Delphi 2010.
When I run the exe I get the Windows Reporting error "Neopos.exe has encountered a problem and needs to close. We are sorry for the inconvenience"
I know it is happening somewhere in the form create of the main form.
Application.Initialize; //Runs this
Application.CreateForm(TfmMain, fmMain); //FAILS HERE
It does not get to: procedure TfmMain.FormCreate(Sender: TObject); in the Main Form and I don't know how to track down this error and debug it.
What happens between: Application.CreateForm(TfmUDF, fmUDF); AND procedure TfmMain.FormCreate(Sender: TObject) in my main form.
How can I trace this to find out what the hell is causing the Windows Error.
Of course the Windows Error report contains a long listing of information. Where can I look in that to find the cause or at least a clue on the cause of the error.
This error has now stopped all development work (and ruined my weekend) so I urgently need to fix this.
The most straightforward route to take would be to include a product like MadExcept or JCL Debugger into your application, to get a full call stack (including line number) of the point of failure. We've rolled our own years ago, and it has been a tremendous help in situations like this.
One alternative, but lots more cumbersome, would be to generate a MAP file from your project, use MAP2DBG to generate a .MAP file, and use the Windows Debbuging Tools to get about the same information. This approach is a lot more hardcore, and only advisable if you really want to learn a lot about the internals of windows debugging (and enjoy working with arcane tools).
Another alternative would be to attach to the failing application from your development environment using Remote Debugging. Only applicable if you have a fair amount of control over the failing machines.
#user576639, here are some debugging ideas:
Look into the System's Event Viewer
If you got the exe has encountered a problem and needs to close chances are you'll find something about it in the System's Event Viewer. That should be your first step.
Any special DLL's required?
Do you need MIDAS.DLL?
Are you using an database engine? Does it require some sort of client library?
I'm talking from experience here: My development machine obviously has all the libraries I might need. Most of my clients also have most of those libraries because they have my software installed. At times I put out small helping applications that don't go throw extensive testing and they fail to work on SOME machines but work fine on other machines. Why? I used TClientDataset and forgot to include MIDAS.DLL with the application; Or the application is trying to access a Firebird SQL server but the user doesn't have Firebird client library installed.
Printer driver issues
Boy I hate Delphi's printer handling. Also hate buggy printer drivers, haven't made up my mind about what's worst. If you have something on your main form that might be requesting information about the default Windows printer (example: an REPORT) give this a try: Install an sane/simple printer and set it as the default printer. If the user has Office 2007+ installed, set the "Microsoft XPS Document Writer" the default printer.
I have seen bad printer driver + delphi issues manifest themselves with the "exe needs to close" symptom.
Prepare an special build of your application
If you got this far without fixing your issue it's time to create an special build of your application that's capable of providing more information. First of all I'd try adding this to your DPR file; Don't know if this is still useful for Delphi 2010 but it did help me see some early exceptions with a Delphi 7 application:
function HandleUnhandledException:integer;stdcall;
begin
Result := 1; // 1 = EXCEPTION_EXECUTE_HANDLER
end;
// and then immediately after "begin" in your DPR file:
begin
SetUnhandledExceptionFilter(#HandleUnhandledException);
// ... the usual stuff goes here
end;
Add some ShowMessage-s to your Main Form's code, in your OnCreate handler (if you have one), in your Create constructor (again, if you have one). If you're adding an ShowMessage to your Create destructor, make sure it's after the "inherited" call. This will help pin-point how far the loading of the form goes before it fails.
If all else fails...
Create a new, blank form; Make it the new Main Form (so it's initialized before your former Main Form). Test it on the client's machine - does it show up? It most likely will, if it doesn't you've got some serious problems.
Start copying the components from the former main form to the new main form; Only the components need to be copied, not the code: Your error is probably caused by some component failing to initialize properly. Make sure no component has "Active=True"! Copy the components in small batches, test often. If you spot the component that causes your form not to load on the client's computer, tell us about it and we'll try to help.
If you manage to get all your components on the new form, write an OnCreate handler that sets Active := True for all the components that need that. Did that fix the issue?
If you got this far then all the components you used on your main form can load properly. The problem's related to YOUR CODE. Start copying all the code from your old main form to your new main form, in small bits, and test. You're bound to find the peace of code that causes your application to stop loading.
Use dependency walker to see if you're missing a required DLL.
You can use information from system reporting (your error and suggestion to send it ) with Error Report Grabber ( http://www.maxerist.net/main/soft-for-win/err-rep-grabber ). I developed this tool when I desperately needed to track a error that appeared very rarely so almost non-reproducible. It helped me to track the information from stack to find actual place in the code.
The tool works only on XP (MS removed this dialog in Win7 and probably Vista), but I see that your cases are XP so this can help.
UPDATE: if you're not familiar with assembler and everything, this can work like this.
You should compile you program and don't change anything. Save the report on a bad machine, copy the file to your developer machine and open to view the contents. Look at the stack of your main thread in the report and find numbers more than $00400000, they're usually the addresses inside the procedures that called some other procedure and wait for return. In your developer machine, start the program and stop at any line, open CPU Window and on the main list with assembler instruction right-click and choose go to address, enter this address. You will see other assembler lines, but wrapped with pascal constructions you can probably recognize as yours
Thanks a lot for the help.
In the end I reverted to a recent backup and traced it down to a particular form.
I did not actually find the error, which is a bit worrying, but in any case I am back up and running (phew!!)
I made the error to occur on my development machine also, when, and only when, I use my install program (Inno Setup) to compile a setup.exe and which installs the exe as well as installing postgreSQL. Seems really strange, as though there is a problem with the setup compiler. In any case I have not seen the error again. I guess it will remains a mystery, like women.
In Delphi withing debugging options select debug dcu's, this will allow you to debug into the Delphi source code for TForm and its descendants and you may be able to track down a more likely culprit.
Set a breakpoint on
Application.CreateForm(TfmMain, fmMain); //FAILS HERE
and then step into the code to see where the issue is.
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...