I have the following issue: we're building a rather large application (win32, Delphi 6 Enterprise). In several part of the application, modal windows are used, usually containing the detail of the selection of the main window.
We included a modification of the handling of the WM_SYSCOMMAND messages so that, if the window is modal, then a SW_SHOWMINNOACTIVE message will be sent to the application's main window. This causes the whole application to be minimized instead of just the modal form.
There is, however, an issue happening in a specific case: if the calling window is set to full screen, then upon restoration, the modal window will appear UNDER the (disabled) maximized main window (this seems to happen on Windows 7)
My problem is two fold:
First, I don't seem to get any syscommand message when the application is restored any more so I cannot introduce code to restore the Z-Order because I don't know where to put it.
Second, it seems to me that, if the whole application is minimized, clicking on the app's button in the task bar should restore it in the same state, not with a modal window under it. Is there a way to fix that ?
Edit: we did some additional testing and it seems we can actually detect the problem in the WM_ACTIVATE handler for the main form. We can also identify the modal window at that stage. I cannot, however, find a way to restore it to the top of the Z-Order.
Edit2: here is the code that minimizes the application when the modal form is minimized:
procedure TfmGITForm.WMSysCommand(var Message: TWMSysCommand);
begin
if (fsModal in FormState) or
not Application.MainForm.Visible then
begin
case Message.CmdType of
SC_MINIMIZE:
begin
ShowWindow(Application.Handle, SW_SHOWMINNOACTIVE);
end;
SC_RESTORE:
begin
ShowWindow(Application.Handle, SW_SHOWNORMAL);
inherited;
end;
else
inherited;
end; // case
end
else
inherited;
end;
All our forms descend from that one.
Override the dialog's CreateParams function and set Params.WndParent to the full-screen window (or Owner.Handle if you're owning things properly). The default is Application.Handle, which will cause these kinds of problems. The PopupParent properties introduced in the later Delphi releases does the exact same thing.
This has to do with the Window ghosting by Windows which was introduced in (I think) XP. I have the same issues in a D5 app on these operating systems. Peter Below offered the following work around at the time and it still serves me well:
procedure DisableProcessWindowsGhosting;
type
TDisableProcessWindowsGhostingProc = procedure; stdcall;
const
sUser32 = 'User32.dll';
var
ModH: HMODULE;
_DisableProcessWindowsGhosting: TDisableProcessWindowsGhostingProc;
begin
ModH := GetModuleHandle(sUser32);
if ModH <> 0 then begin
#_DisableProcessWindowsGhosting := nil;
#_DisableProcessWindowsGhosting := GetProcAddress(ModH,
'DisableProcessWindowsGhosting');
if Assigned(_DisableProcessWindowsGhosting) then begin
_DisableProcessWindowsGhosting;
end;
end;
end;
I call it at the beginning of the app's main form's OnCreate handler.
Related
I'm having problems using TApplication.ModalPopupMode=pmAuto and I was wondering if my problems were caused by my usage of pmAuto or a bug in delphi.
Simple use case:
Form1(MainForm) and Form3 are permanent forms. (Created in the dpr)
Form2 is created when needed and
freed afterward.
Form3 contains a TComboBox with X items.
Sequence of actions :
Form1 create and show Form2 modal.
Form2 show form3 modal.
Close Form3
Close and free Form2
Show Form3 <---- The TComboBox now contains 0 items.
I use ComboBox as an example, but I guess any controls that saves information in the DestroyWnd procedure and restore it in the CreateWnd procedure isn't working right. I tested TListBox and it displays the same behavior too.
Is it a known fact that one shouldn't mix permanent and temporary form when ModalPopupMode is pmAuto?
If not, is there any known workaround for this problem?
If it's a bug, is this fixed in more recent version of Delphi? (I'm using XE4)
It is not really a bug, just a quirk in how the various windows interact with each other when dealing with modality.
When Form3 is first created, TComboBox.CreateWnd() is called during DFM streaming. When Form3.ShowModal() is called for the first time, Form3 calls RecreateWnd() on itself if its PopupMode is pmNone and Application.ModalPopupMode is not pmNone. OK, so TComboBox.DestroyWnd() gets called, saving the items, then TComboBox.CreateWnd() gets called, restoring the items. Recreating the TComboBox's window during ShowModal() is not ideal, but it works this time.
When Form3.ShowModal() is called the second time, TComboBox.CreateWnd() is called again without a previous call to TComboBox.DestroyWnd()! Since the items have not been saved, they cannot be restored. That is why the TComboBox is empty.
But why does this happen? When Form2 is freed, Form3's window is still associated with Form2's window. The first call to Form3.ShowModal set Form2's window as Form3's parent/owner window. When you close a TForm, it is merely hidden, its window still exists. So, when Form2 and Form3 are closed, they still exist and are linked together, and then when Form2 is destroyed, all of its child and owned windows get destroyed. TComboBox receives a WM_NCDESTROY message, resetting its Handle to 0 without notifying the rest of its code that the window is being destroyed. Thus, TComboBox does not have a chance to save its current items because DestroyWnd() is not called. DestroyWnd() is called only when the VCL itself is destroying the window, not when the OS destroys it.
Now, how can you fix this? You will have to destroy the TComboBox's window, triggering its DestroyWnd() method, before freeing Form2. The trick is that TComboBox.DestroyWnd() will save the items only if the csRecreating flag is enabled in the TComboBox.ControlState property. There are a few different ways you can accomplish that:
call TWinControl.UpdateRecreatingFlag() and TWinControl.DestroyHandle() directly. They are both protected, so you can use an accessor class to reach them:
type
TComboBoxAccess = class(TComboBox)
end;
Form2 := TForm2.Create(nil);
try
Form2.ShowModal;
finally
with TComboBoxAccess(Form3.ComboBox1) do
begin
UpdateRecreatingFlag(True);
DestroyHandle;
UpdateRecreatingFlag(False);
end;
Frm.Free;
end;
Form3.ShowModal;
call TWinControl.RecreateWnd() directly. It is also protected, so you can use an accessor class to reach it:
type
TComboBoxAccess = class(TComboBox)
end;
Form2 := TForm2.Create(nil);
try
Form2.ShowModal;
finally
TComboBoxAccess(Form3.ComboBox1).RecreateWnd;
Frm.Free;
end;
Form3.ShowModal;
The TComboBox window is not actually be created until the next time it is needed, in the subsequent ShowModal().
send the TComboBox window a CM_DESTROYHANDLE message and let TWinControl handle everything for you:
Form2 := TForm2.Create(nil);
try
Form2.ShowModal;
finally
if Form3.ComboBox1.HandleAllocated then
SendMessage(Form3.ComboBox1.Handle, CM_DESTROYHANDLE, 1, 0);
Frm.Free;
end;
Form3.ShowModal;
CM_DESTROYHANDLE is used internally by TWinControl.DestroyHandle() when destroying child windows. When a TWinControl component receives that message, it calls UpdateRecreatingFlag() and DestroyHandle() on itself.
Based on Remy's excellent answer I implemented something that fixes these issues in the whole application. You will need to descend all your modal forms from a custom TForm descendant - TMyModalForm in my example (which, IMO, is always a good practice anyway). All modal forms in my application descend from this. Please notice that I also set PopupMode to pmAuto in CreateParams() before calling the inherited method. This prevents the z-order problem when showing modal windows, but also causes the window handle problem described in your question. Also, I just broadcast the CM_DESTROYHANDLE if action is caHide. This skips an unnecessary notification for MDI child windows and modal windows which are destroyed on close.
BTW, for future reference, this issue still exists in Delphi 10.2.3 Tokyo.
type
TMyModalForm = class(TForm)
protected
procedure DoClose(var Action: TCloseAction); override;
procedure CreateParams(var Params: TCreateParams); override;
end;
procedure TMyModalForm.DoClose(var Action: TCloseAction);
var
Msg: TMessage;
begin
inherited DoClose(Action);
if Action = caHide then
begin
FillChar(Msg, SizeOf(Msg), 0);
Msg.Msg := CM_DESTROYHANDLE;
Msg.WParam := 1;
Broadcast(Msg);
end;
end;
procedure TMyModalForm.CreateParams(var Params: TCreateParams);
begin
PopupMode := pmAuto;
inherited;
end;
end;
Setting a form to WindowState = wsMaximized will sometimes cause the form to be maximized but not:
Long-time bug: this is a question I first asked in the Borland newsgroups in 2003:
Accepted fix for WindowState = wsMaximized?
and then again in 2006:
wsMaximized breaks it, NOT caused by Position=poScreenCenter, reproducible dfm
and then again in 2008:
Forms not starting maximized
Someone asked it on the Embarcadero forums in 2012:
Thread: Application not starting with maximized window
Now it's time to port the 18 year old bug to Stackoverflow. Maybe someone's finally figured out a workaround.
Steps to reproduce:
My posts contained half a dozen failure modes, but the easiest is:
Drop a Label and an Edit on a form:
Add an OnEnter event for the TEdit:
procedure TForm1.Edit1Enter(Sender: TObject);
begin
Label1.Font.Style := Label1.Font.Style + [fsBold];
end;
and set the form:
WindowState to wsMaximized
AutoScroll to False
And bazinga, fails.
One of the other set of steps from the 2008 post:
Create a new app and a form.
Set the form to maximized (WindowState = wsMaximized) at design time.
Drop a ListView control on the form
During OnShow, add 20 empty items to the list view:
procedure TForm1.FormShow(Sender: TObject);
var
i: Integer;
begin
for i := 1 to 20 do
ListView1.Items.Add;
end;
Set the form's AutoScroll property to false (AutoScroll = False) at design time
Of course what I'm not after is "fixed in version n of RadStudio. Just use that". I'm looking for an actual fix (if there is one); which could include quoting relevant changes to the VCL source when CodeGear finally did fix it. (If it is even fixed).
Note: Changing Position from poDesigned to anything else doesn't fix it.
Workaround
A horrible, ugly, awful, disgusting, workaround I had been using was to start a timer during OnShow, and then when the timer fires, maximize the form:
procedure TForm1.tmrVclMaximizeHackTimer(Sender: TObject);
begin
Self.WindowState := wsMaximized;
end;
I later improved this hack to post a message during OnShow; which is essentially the same as a timer message, without having to use a timer:
const
WM_MaximizeWindow = WM_APP + $03;
procedure TForm1.FormShow(Sender: TObject);
begin
if (Self.WindowState = wsMaximized) then
begin
Self.WindowState := wsNormal;
PostMessage(Self.Handle, WM_MaximizeWindow , 0, 0);
end;
end;
private
procedure WMMaximizeWindow(var Message: TMessage); message WM_MaximizeWindow;
procedure TForm1.WMMaximizeWindow(var Message: TMessage);
begin
Self.WindowState := wsMaximized;
end;
Sometimes I invent the OnAfterShow event that Delphi never did:
const
WM_AfterShow = WM_APP + $02;
procedure TForm1.FormShow(Sender: TObject);
begin
PostMessage(Self.Handle, WM_AfterShow, 0, 0);
if (Self.WindowState = wsMaximized) then
begin
Self.WindowState := wsNormal;
FMaximizeNeeded := True;
end;
end;
private
procedure WMAfterShow(var Message: TMessage); message WM_AfterShow;
procedure TForm1.WMAfterShow(var Message: TMessage);
begin
if FMaximizeNeeded then
begin
FMaximizeNeeded := False;
Self.WindowState := wsMaximized;
end;
end;
But no hacks are better than hacks.
I Can reproduce with D7/Win7.
I don't use wsMaximized at all (similar random problems as you describe).
Workaround: use OnActivate -> ShowWindow(Handle, SW_MAXIMIZE) e.g.:
procedure TForm1.FormActivate(Sender: TObject);
begin
// Maximize only once when the Form is first activated
if not FMaxsimized then
begin
FMaxsimized := True;
ShowWindow(Handle, SW_MAXIMIZE);
end;
end;
This method will not work during OnShow.
Better Workaround: use ShowWindowAsync during OnShow or OnCreate e.g:
procedure TForm1.FormCreate(Sender: TObject);
begin
ShowWindowAsync(Handle, SW_MAXIMIZE);
end;
This sets the show state of a window without waiting for the operation to complete.
I only tested the first reproduction case (with D7, D2007, XE2), and am able to duplicate the problem with D7 and D2007 but not with XE2.
The problem, as I see it, is that the label, having its font changed, requests its parent to re-align itself. This eventually leads to a SetWindowPos call on the form (in TWinControl.AdjustSize) with restored width/height even though the form is already maximized - which leads to the strange, behaviorally maximized but not visually maximized, form sitting on the screen.
I traced the code in D2007 and XE2 to be able to come up with what is different. The code in TWinControl.AlignControls is different between the two versions. What specifically matters is the last statement.
D2007:
procedure TWinControl.AlignControls(AControl: TControl; var Rect: TRect);
..
{ Apply any constraints }
if Showing then AdjustSize;
end;
XE2:
procedure TWinControl.AlignControls(AControl: TControl; var Rect: TRect);
..
// Apply any constraints
if FAutoSize and Showing then
DoAdjustSize;
end;
I hope this, somehow, helps you devising/deciding what workaround to use.
The workaround I could suggest (although I haven't tested it throughly) is to force show the form maximized early:
procedure TForm1.FormCreate(Sender: TObject);
var
wplc: TWindowPlacement;
begin
if not AutoScroll and (WindowState = wsMaximized) then begin
wplc.length := SizeOf(wplc);
GetWindowPlacement(Handle, #wplc);
wplc.rcNormalPosition.Right := wplc.rcNormalPosition.Left + Width;
wplc.rcNormalPosition.Bottom := wplc.rcNormalPosition.Top + Height;
wplc.showCmd := SW_MAXIMIZE;
SetWindowPlacement(Handle, #wplc);
end;
end;
The above works because it forces to set the focus to the edit control (OnEnter event) before the VCL sets the visible flag for the form. In turn, the label's alignment request does not result with form size adjustment. Also, since, by the time VCL calls ShowWindow the form's window is already visible, it doesn't cause the form to be shown in a restored state at any stage.
However, I don't know if it would help with different reproduction scenarios.
Finally, although I can see that the behavior is corrected in newer Delphi versions, I wouldn't consider this to be a bug in the VCL. In my opinion, user code should be responsible not to cause window adjustment while window showing state is changing. The course of action I'd take for the specific scenario would be to defer to modify label's font until the VCL is done displaying the form.
I don't think this is a bug in Delphi but rather a bug (or just odd behavior) in the Windows CreateWindow function. If you search for CreateWindow and WS_MAXIMIZE not working you'll find similarly very old threads and discussions from people calling CreateWindow or CreateWindowEx passing WS_MAXIMIZE in the style parameter and not seeing a maximized window when they run the application.
Excerpt from an old gamedev.net thread
the problem is that WS_MAXIMIZE apparently does not apply when using WS_OVERLAPPEDWINDOW. if you replace WS_OVERLAPPEDWINDOW with WS_POPUP you will get a maximized window. of course this may not apply to all versions of Windows, or even all versions of the Windows shell UI for that matter.
WS_OVERLAPPEDWINDOW is MS's old default window "type" and they apparently coded CreateWindow/Ex to ignore certain styles, thinking that ShowWindow would be called with SW_SHOWDEFAULT, which causes the window to be displayed according to the CreateProcess startup info parms. this ultimately gives the user the control of how an app's main window would be displayed by using the shell's shortcut settings.
The workaround is just to call ShowWindow. It should work in Delphi, too:
procedure TForm1.FormShow(Sender: TObject);
begin
ShowWindow(Handle, SW_MAXIMIZE);
end;
Hope the solution i use helps others (i known window is first shown with design time size):
Add a timer with interval as less as just 1 (do not put 0).
Code for it: theTimer.Enabled:=False;WindowState:=wsMaximized;
It never fail to me.
As soon as form is shown and all pending tasks for such show are finished, the timer triggers and window is maximized.
Some time ago, i was using the Trick of sending a mouse click where maximize button was, but i discovered checking Windows OS version, plugins (on Linux), etc makes the thing so hard. That one worked exactly as if the user asks for maximize the window.
The Timer i now use does exactly the same, but avoid OS checking, etc.
Not to mention: Put WindowState to wsNormal on DesignTime (do not set it to wsMinimized, neither to wsMaximized).
Wow! i did not see on the post:
ShowWindowAsync(Handle,SW_MAXIMIZE);
Thanks for that, with it my problem is solved much better than with a Timer, but not perfect, it still causes a little flicker (window is shown on incomplete render, then it goes to maxized state), it is much better than the timer, less time shown in non maximized state.
And it is compatible with a prior SetBounds() on the OnShow method.
I wish: Set initial size (Width, Height) and maybe also initial position (Left, Top) of a form prior to show it, but that form must be shown maximized; such position and sizes are for when the user un-maximize the window.
That ShowWindowAsync works perfect for such objective, and no need to add an ugly timer (with interval=1 and .Enabled=False on its code as first sentence).
How could i miss it when i read the post!
So, now on i will use (just as example os initial size relative to monitor):
procedure TtheForm.FormShow(Sender: TObject);
var
theInitialDefaultWidth,theInitialDefaultHeight:Integer;
begin
theInitialDefaultWidth:=Round(Screen.Width*3/5);
theInitialDefaultHeight:=Round(Screen.Height*3/5);
WindowState:=wsNormal; // So it can still have at design time wsMaximized, this is for the SetBounds to work on a non maximized state
SetBounds((Screen.Width-theInitialDefaultWidth)div 2,(Screen.Height-theInitialDefaultHeight)div 2,theInitialDefaultWidth,theInitialDefaultHeight); // Set default position and default size as i wish
ShowWindowAsync(Handle,SW_MAXIMIZE); // Make the window to be shown maximized when it will be visible
// ... // Rest of actions for the FormShow method
end;
Works perfect! I do not need to touch design time properties, i can let them as they are (WindowState=wsMaximized, Position=poScreenCenter, etc).. 100% code solution for the problem.
Thanks a lot!
P.D.: Will it work on Linux? I mean when code is compiled for Linux (in Lazarus), i must test it and see, if it does work it will be a great immprove on what i was using till now.
I need develop an application (Delphi) that never receive the focus, I use the DLL to display the video on second monitor (I found in Torrys Delphi - Dr.SAGURA Media Player v.1.0) that receive the focus every time play the video, how avoid this ?
I try :
procedure TForm.WMActivate(var Msg: TWMActivate);
begin
WA_ACTIVE :
begin
Msg.Result := 0;// cancel focus
end;
end;
Unsuccessful !
Thanks
Jean Alysson
In your project DPR set Application.MainFormOnTaskBar := False.
Override the form's OnShow and OnActivate events and add this to both of them: ShowWindow(Application.Handle, SW_HIDE);
Override your form's CreateParams procedure and add WS_EX_NOACTIVATE to Params.ExStyle.
The first two items hide the application from the taskbar and the Alt+Tab list and the last keeps it from gaining focus when it's shown and when clicking on it.
What's the intention behind not be focusable? You want to avoid the user from closing/minimize/maximize the window?
Maybe your need can be acomplished understanding the real problem.
Maybe having a sevice instead of a regular application can make the trick.
This is approach I found for Tray ... :
http://www.programmersheaven.com/mb/delphikylix/257563/257563/how-can-i-make-a-system-tray-flash/
Does the same technique works for Dialogs ( as they are forms with addition params, in fact )?
Or I can do it with way faster methods like getting handle / address / interface and overload or overdrive the function with FlashWindow(Ex) method?
I mean - can I make, for example ShowMessage(), window / dialog flash using FlashWindowEx() method and if I can, can it be done using the example in link given above?
Please, point to best direction or clarify my doubts ..
Thanks.
Sorry for bad formulation of question.
The same technique applies to any top-level window, including dialog boxes. If you can get the window's handle, you can pass it to FlashWindowEx.
There are many ways a modal form or dialog (both VCL or native from the system) can be shown from a Delphi program, so you need to somehow hook into message processing and catch messages that are sent when a modal form or dialog is shown.
For that a message hook can be set using the SetWindowsHookEx() API function. Since you need this only while the application is inactive you could set it in the handler of the OnDeactivate application event, and reset it in the handler for the OnActivate application event:
var
gNextHook: HHOOK;
procedure TForm1.AppActivate(Sender: TObject);
begin
if gNextHook <> 0 then
UnhookWindowsHookEx(gNextHook);
gNextHook := 0;
end;
procedure TForm1.AppDeactivate(Sender: TObject);
begin
gNextHook := SetWindowsHookEx(WH_CALLWNDPROC, #WndProcHook, 0,
GetCurrentThreadId);
end;
The hook function would watch for messages that are sent when a modal dialog or form is shown, and call FlashWindowEx() with the correct parameters:
function WndProcHook(nCode: integer; AWParam: WPARAM; ALParam: LPARAM): LRESULT; stdcall;
var
DataPtr: PCWPStruct;
Fwi: TFlashWInfo;
begin
DataPtr := PCWPStruct(ALParam);
if (DataPtr^.message = WM_INITDIALOG)
or ((DataPtr^.message = CM_ACTIVATE) and (DataPtr^.lParam = 0) and (DataPtr^.wParam = 0))
then begin
Fwi.cbSize := SizeOf(TFlashWInfo);
// flash caption of new modal window
Fwi.hwnd := DataPtr^.hwnd;
Fwi.dwFlags := FLASHW_ALL or FLASHW_TIMERNOFG;
Fwi.uCount := 0;
Fwi.dwTimeout := 0;
FlashWindowEx(Fwi);
// uncomment this to flash task bar button as well
(*
Fwi.hwnd := Application.MainForm.Handle;
Fwi.dwFlags := FLASHW_TRAY or FLASHW_TIMERNOFG;
FlashWindowEx(Fwi);
*)
end;
Result := CallNextHookEx(gNextHook, nCode, AWParam, ALParam);
end;
I chose WM_INITDIALOG which is sent for native dialogs like the open or save dialogs, and CM_ACTIVATE which is sent when a VCL form is shown modally. There may be more such messages that need to be caught. Above code works for the MessageDlg() function, the Application.MessageBox() function and TOpenDialog at least.
Since these dialogs don't have their own taskbar button I added (commented out) code to flash the taskbar button of the main form as well. This isn't optimal, as they flash out of sync.
Tested with Delphi 2009 on Windows XP, all error handling omitted, use it as a starting point only.
My application is based on modal forms. Main form opens one form with ShowModal, this form opens another with ShowModal, so we have stacked modal forms. There is sometimes a problem that when we call ShowModal in new form, it hides behind previous forms, instead of showing on top. After pressing alt+tab, form comes back to the top, but this is not good solution. Did You meet this problem and how did you handle it?
EDIT:
I use Delphi 7.
You didn't mention which version of Delphi...
Newer Delphi versions have added two new properties to TCustomForm: PopupMode and PopupParent. Setting PopupParent of your modal dialog to the form that's creating that dialog makes sure that the child form stays on top of it's parent. It usually fixes the problem you're describing.
I think this pair of properties were added in Delphi 2006, but it may have been 2005. They're definitely there in Delphi 2007 and up.
EDIT: After seeing you're using Delphi 7, the only suggestion I have is that, in the code that displays your modal form, you disable the form creating it, and re-enable on return. That should prevent the creating window from receiving input, which may help keep the Z-order correct.
Something like this may work (untested, as I'm no longer using D7):
procedure TForm1.ShowForm2;
begin
Self.Enabled := False;
try
with TForm2.Create(nil) do
begin
try
if ShowModal = mrOk then
// Returned OK. Do something;
finally
Free;
end;
end;
finally
Self.Enabled := True;
end;
end;
If Form2 creates a modal window (as you've mentioned), just repeat the process - disable Form2, create Form3 and show it modally, and re-enable Form2 when it returns. Make sure to use try..finally as I've shown, so that if something goes wrong in the modal form the creating form is always re-enabled.
Sorry for adding a separate answer, but I have done a bit more research, and some of it indicates that my previous answer (DisableProcessWindowsGhosting) doesn't help. Since I can't always reproduce this issue, I cannot say for sure.
I found a solution that appears to appropriate. I referenced the code in Delphi 2007 for the CreateParams method and it matches pretty close (without having all of the other code that handles PopupMode).
I created the unit below which subclasses TForm.
unit uModalForms;
interface
uses Forms, Controls, Windows;
type
TModalForm = class(TForm)
protected
procedure CreateParams(var params: TCreateParams); override;
end;
implementation
procedure TModalForm.CreateParams(var params: TCreateParams);
begin
inherited;
params.WndParent := Screen.ActiveForm.Handle;
if (params.WndParent <> 0) and (IsIconic(params.WndParent)
or not IsWindowVisible(params.WndParent)
or not IsWindowEnabled(params.WndParent)) then
params.WndParent := 0;
if params.WndParent = 0 then
params.WndParent := Application.Handle;
end;
What I do then is include this unit in with a form unit, and then change the form's class (in the .pas code file) from class(TForm) to class(TModalForm)
It works for me, appears to be close to CodeGear's solution.
From this link it appears that the problem is with the "Ghosting window" that was introduced in 2000/XP. You can disable the ghosting feature by calling the following code at startup.
procedure DisableProcessWindowsGhosting;
var
DisableProcessWindowsGhostingProc: procedure;
begin
DisableProcessWindowsGhostingProc := GetProcAddress(
GetModuleHandle('user32.dll'),
'DisableProcessWindowsGhosting');
if Assigned(DisableProcessWindowsGhostingProc) then
DisableProcessWindowsGhostingProc;
end;
The only issue that I can see is that it will cause problems with the feature that allows for the user to minimize, move, or close the main window of an application that is not responding. But in this way you do not have to cover each call with the Self.Enabled := False code.
Just set the Visible property of the form, that you want to open modal, to False. Then you can open it with .ShowModal(); and it will work.
I have found that using the "Always On Top" flag on more than one form causes problems with the Z order. And you may also find the need for the BringWindowToTop function.
When launching a message box using the built-in WinAPI (MessageBox), I have found that passing the calling window's handle is necessary in order to make sure that the the prompt appears on top all the time.
try it
OnShowForm:
PostMessage(Self.Handle, WM_USER_SET_FOCUS_AT_START, 0, 0);