DELPHI: Crash when unloading DLL if context help is shown - delphi

We have a complex Delphi application with a main exe and several dlls.
In main form (contained in application exe) we have menubar.
On selecting a menu the corresponding dll is loaded and a form conatained in the dll is shown modal.
The scenario is as follows:
Press F1 and the context help for the modal form is open.
Close the modal form (without closing the context help).
in the source code we have:
result := FreeLibrary(MH);
And the whole application crashes with the following windows error:
ERROR_ALLOCATE_BUCKET
602 (0x25A)
The bucket array must be grown. Retry transaction after doing so.
If we first close the context help and then close the modal form everything is ok.
The strangest thing is that we have 3 dlls crashing and 3 not crashing.
The flow goes thru the same code.
There is nothing in the Windows Event Log.
I thought as a workaround I could force close the context help on closing the modal form before unloading the dll but I cannot find pid or some task identifier which I could kill.
There is no child processes of the application pid.
In Task Manager 'Processes' tab I see the name of our application and the name of chm-file (without .chm) like a subtask (at least seems like so) and I can right-click 'Stop task' but programatically I cannot find this...
P.S:
We use .chm file and do not have HtmlHelpViewer in uses of any module.
We have associated specific help topic to controls in forms so they automatically display the corresponding help topic when the user focuses it and presses F1. So in fact we do not use Application.HelpCommand(.....) to show the help
**** edited ****
Currently we use Delphi Tokyo. The product is stared 15 years ago in Delphi7. It has several dll-s (some of them are extra product modules) with plenty of forms and frames.
**** edited ****
**LU RD's answer solved my problem:**
Application.HelpCommand(HELP_QUIT, 0);
P.S:
I cannot mark the answer as accepted. I do not know why.

"I thought as a workaround I could force close the context help on closing the modal form before unloading the dll but I cannot find pid or some task identifier which I could kill. There is no child processes of the application pid".
In order to close the context help, add this line in the modal form OnClose() event:
Application.HelpCommand(HELP_QUIT, 0);
Application.HelpCommand: Provides access to any of the Help commands in the application programming interface (API) of native Help handling functions HTMLHelp, WinHelp, or other.
See Vcl.Forms.TApplication.HelpCommand for further details.

Related

Why do datasource of database-controls dissapear when Delphi crashes

I'm using Delphi 10.4.1 Enterprise and wrote a VCL program using an SQLite database.
As I suspect a OneDrive issue, I mention that the source code folder is part of the zone managed by OneDrive.
At times, whilst debugging, my Delphi program hangs for unknown reasons, and Delphi quits without further notice. Whenever this happens, the connections between the MainForm and the DataModule (DM1) are lost: in the Object Inspector of the MainForm, all DataSource assignments, ie connections to DataSources in the DataModule, are gone. Moreover, when I reload the program, there is the error message
Access violation at address 500C3553 in module 'rtl270.bpl'. Read of address 00000008
Code-checking gives no errors, but when compiling the following message/request appears:
Module 'MainForm' links to module 'DM1' which cannot be found in the current project. Do you wish to remove/redirect the links to another module?
The weird thing is that the DataModule design form in the IDE can no longer be made visible, only its code remains. Therewith, from within the MainForm the DataModule neither is visible, since dropdowns in the DataSource connectors of the DB-controls remain empty. Last, but not least, when I try to save the MainForm, it says:
Module "*MainForm" references another module and cannot be saved until DM1 is loaded
Whatever I tried to get it back to normal, it was unsuccessful.
However, in the OneDrive root folder, I see a file named ".849C9593-D756-4E56-8D6E-..etc..", which seems to be associated with my program, because of its file date. It can only be deleted or modified when I close OneDrive, but it revives after restart.
I am stuck, for many days already.
Does anyone have an idea what is going on, and can give me a hint on how to proceed?
I found out what has caused the Access Violation that showed up after I tried to bring the datasource connections back in place. I did that by editing the DFM-file of the Mainform, rather than direct in the Object Inspector.
I normally do editing in dfm-files, but adding lines with datasource assignments appears not to be allowed. This was quite a tough lesson for me!
I am happy to have the program running again. Thank you all for your time, #Remy in particular.
Thanks,
JGMS

Chromium-embedded for Delphi - TChromiumOSR.OnPaint is not fired when a modal window is shown

I'm using the Off-screen rendering component TChromiumOSR in the dcef3 package - the Delphi wrapper for Chromium-embedded library.
Situation
FormA contains a TChromiumOSR and paints the output.
Modal FormB modifies the web page by executing some js code against FormA.TChromiumOSR.
Issue
The TChromiumOSR.OnPaint event (in FormA) is not triggered until FormB.ShowModal returns.
Notes
There is no such issue in the above described situation (under a modal form) with the standard TChromium control.
I assume the `TForm.ShowModal' method only blocks the input of the background forms, but not the painting?
Or does it caused by the internal working of cef3?
Anyway, how to solve it? Thanks.
dcef3 master branch is here
I've succeeded updating an HTML element by using the ExecuteJavaScript method called from a modal form. But you might have use CEF V8 as well (that's what I haven't tested). For cases when you need to invalidate the current view manually, you can call Invalidate:
MyChromiumOSR.Browser.Host.Invalidate(PET_VIEW);
But that's workaround rather than solution. Invalidating of relevant elements should happen by the CEF engine for you. And if you come up with an MCVE, I can investigate more about your specific problem.
Ok, I found the source of the problem - it's not a bug in cef3 or dcef3 but was caused by my improper use of Delphi Event Bus, and the following are the steps to reproduce the issue:
In one the 'delphi event bus' handler, the program shows a modal form at this point the execution of the main thread is blocked.
On top of the modal form, the user doing certain actions will start a background thread, which in turn will send a message to the main thread, which in turn will calls the 'delphi event bus' to post another new event, which in turn will execute some js code to update the web page inside dcef3, which in turn will trigger some of the dcef3 events (in the main thread), and here is where the program stuck - since the TEventBus.Post() method is locked by a TCriticalSection.
Solution:
In Step #1, don't call ShowModal directly, but use a technique such as PostMessage winapi to 'delay' the execution of ShowModal.
I'm not sure if I have described it clearly...

Tab order in a console app with a single VCL form

I have a Windows console app created with Embarcadero XE 6 (in fact converted from a Borland C++Builder5 project). It has a single form with a few buttons and edit controls. All these controls have set TabStop=True and appropriate TabOrder's. However, pressing Tab in runtime when the form is shown does not do anything (it just produces a sound when a cursor/focus is in an Edit control and does nothing when a button is focused).
I have read in docs that Tab order would not work unless the Parent of the form is set. However, this is the only VCL form (the other windows are the console and the GLUT window), so there is no VCL parent AFAIK. I tried to set
Parent=Application->MainForm;
in the Form's constructor, but the Application->MainForm is also NULL. Any ideas?
Your problem is that you don't have a message loop. This is because console applications are not expected to have windows and do not come with message loops by default.
You can run a message loop by calling:
Application->Run();
However this will probably stop the console part of your application from working properly. How can your main thread service the console synchronously and the asynchronous GUI message loop at the same time?
I suspect you will need to have a more serious re-think of your application design.
Regarding your update, it seems that you do have a message loop, but it is the message loop for the GLUT framework. The VCL framework requires its message loop to handle dialog messages like TAB key presses.
It's plausible that running the VCL message loop in place of the GLUT message loop would give better results. But it's quite likely that would just break the GLUT part of the app.
Trying to run two incompatible GUI frameworks out of a single message loop is hard to get right. There's probably no quick fix here. You'll need to dig deeper. Perhaps it would be best to give up on the VCL and stick to the one GUI framework.

ShowMessage is hidden

I'm using Delphi XE3 in a complex application that has MDI forms, and forms that stays on top also.
Sometimes a simple ShowMessage('...') does not appear and stays behind the main application window.
Either people think that the application has crashed, or go to task manager and put back the application on top, and then showmessage windows come back on top.
This is occuring in XP, I haven't seen that under Win8 so far.
Any idea why?
You can try this:
Wrote your own myShowMSG function. In this function wrote the simple custom message form and some code to control - "Are this form are visible to user?" In control code write to error.log all tracelog information about this problem. Include the list of actually created forms and their states to .log
Replace all ShowMessage in your project to myShowMSG
Run all tests
Read your error.log's. Post it here to disquss

Why is a dialog box being displayed behind the main form?

Earlier today I went to open a file in a Delphi app I wrote. For some reason the connection to the file's network was down--reasons unimportant--and Windows created a dialog box alerting me to the problem. My app's main form, however, was on top of the newly created dialog (i.e., there was a form for my app, for the File Open dialog, and for the warning dialog box). The warning dialog was modal, and hidden behind the main form. Obviously, I had a problem.
Any idea what's going on, or how I can remedy the issue? The main form's position property is set to poDesigned, and I save/load the form's position on close/startup, I'm too much of a newbie to even know what info would help you diagnose the problem. To be clear, though, the issue was not the File Open dialog--that was displayed where it was supposed to be displayed--the issue was the warning dialog.
Thanks, as always --
I don't understand why is Windows creating a dialog in your app. Which API call resulted in that happening? Normal file operations don't show UI.
Most likely you were using an API function that can show UI, perhaps from the shell API. Any function that can show a modal dialog will request an owner HWND.
For example consider MessageBox(), a function that you know will show a modal dialog in your app. Its first parameter is called hWnd and is documented
A handle to the owner window of the
message box to be created. If this
parameter is NULL, the message box has
no owner window.
Raymond Chen has a whole series of articles on modality which explain why setting this is important.
I have a hunch that you are calling some Win32 API function that shows modal UI, and are not setting the owner HWND correctly. Of course, I could be completely wrong, but there's not more information to go on.
You can stop this by using SetErrorMode before trying to open the file on the network share:
var
OldErrorMode: Integer;
begin
OldErrorMode := SetErrorMode(SEM_NOOPENFILEERRORBOX);
try
if OpenDialog1.Execute then
begin
// ....
end;
finally
SetErrorMode(OldErrorMode);
end;
end;
Later versions of Delphi (IIRC, D2007 and higher) added an overloaded version of TOpenDialog.Execute that accepts a window handle as a parameter; this sets the TOpenDialog's parent and prevents the OpenDialog (and any error window it generates) from appearing behind the main window.
NOTE: You can get to the background dialog (from Windows) using Alt+Tab to cycle through until your application comes back up; this usually brings the hidden dialog forwaard on top of your form.

Resources