How can I receive message (WM_COPYDATA)? - delphi

I am sending message WM_COPYDATA to my application which has a lot of forms, but I cannot receive it.
Simple Application with one form works fine.
I have this code in main form. No message received
private
procedure ReceiveMessage(var Msg: TWMCopyData); message WM_COPYDATA;
...
procedure TForm1.ReceiveMessage;
begin
ShowMessage(PAnsiChar(Msg.CopyDataStruct.lpData));
end;
I checked Application and main form handles. So handles looks like:
Form1.Handle (main form) = 3348672
FindWindow result = Application.Handle = 7148290
FindWindowEx(Application.Handle,0,nil,nil) = 0
I read here(Delphi: What is Application.Handle?) in Don's answer that messages which were sent to Application handle are redirected to main form, but in my situation it doesnt happen.
How can I receive messages?
Why messages are not redirected to main form?
Testing under Delphi XE8, Windows 10

Except the fact that you don't provide any real code that you need help with, your question is probably wrong. If I assume correctly, you can send the message, you can recieve it but you send it to wrong place.
If your form has dynamic title, you can find its handle like this. Set some fixed part of the title "MyWindowTitle" and find that with this function.
function FindFormHandle: HWND;
var
NextHandle: HWND;
NextTitle: array[0..260] of char;
begin
Result:=0;
NextHandle := GetWindow(Application.Handle, GW_HWNDFIRST);
while NextHandle > 0 do
begin
GetWindowText(NextHandle, NextTitle, 255);
if Pos('MyWindowTitle', NextTitle)>0 then
Exit(NextHandle);
NextHandle := GetWindow(NextHandle, GW_HWNDNEXT);
end;
end;

I found the solution to receive messages:
I set Main form's Caption equal to Application.Title. So now FindWindow returns Main form's handle.
But now I cannot use dynamic caption of main form.

Related

DELPHI Edit.OnExit by TAB, show window result on focus bug

I'm having trouble with the following scenario:
2 Edit's
Type something in Edit1 and press TAB, focus goes to Edit2
Edit1.OnExit -> show a Form with a message "Processing..." (makes a lengthy validation)
After the form closes, the focus on Edit2 seems to be "crashed"...
- the hole TEXT in Edit2 isn't selected
- the carret isn't flashing
Example:
Create a new form
Put 2 edits
Set this as OnExit event in Edit1:
procedure TForm1.Edit1Exit(Sender: TObject);
begin
with TForm.CreateNew(self) do
try
Width := 100;
Height := 50;
Position := poMainFormCenter;
show;
sleep(200);
finally
Free;
end;
end;
Run the application
Set focus in the Edit1 and press TAB
I'm using:
Delphi 7 Enterprise
Windows 7 x64
This is a known problem. Windows has problems when you change focus before it's completed the last focus change (eg., focus starts changing from Edit1 to Edit2, but Edit1.OnExit does something to change focus to another control or form.
This happens, for instance, when apps try to do validations in an OnExit event and then try to return focus to the original control when the validation fails.
The easiest solution is to post a message to your form handle in the OnExit instead, and handle the focus change need there. It will fire once the target control gets the input focus, and Windows doesn't get confused.
const
UM_EDIT1_EXITED = WM_USER + 1;
type
TForm1=class(TForm)
...
private
procedure UMEdit1Exited(var Msg: TMessage); message UM_EDIT1_EXITED;
end;
implementation
procedure TForm1.Edit1Exit(Sender: TObject);
begin
PostMessage(Handle, UM_EDIT1_EXITED, 0, 0);
end;
procedure TForm1.UMEdit1Exited(var Msg: TMessage);
begin
// Show your other form here
end;
From an old Borland NG post by Dr. Peter Below of TeamB:
here is my general sermon on the "show dialog from OnExit" problem:
If an OnExit handler is triggered (which happens in response to the
Windows
message WM_KILLFOCUS) Windows is in the midst of a focus change. If you do
something in the handler that causes another focus change (like popping up
a message box or doing a SetFocus call) Windows gets terribly confused.
The
missing cursor is a symptom of that.
If you have to display a message to your user from an OnExit handler, do
it
this way:
Define a constant for a user message somewhere in the INterface
section
of your unit, above the type declaration for your form
'Const
UM_VALIDATE = WM_USER + 200;'
Give your Form a handler for this message, best placed in the private
section of the class declaration:
Procedure UMValidate( Var Msg: TMessage ); message UM_VALIDATE;
Post a UM_VALIDATE message to the form from the OnExit handler if
the contents of the field are not ok. You can pass additional
information in the wparam and lparam parameters of the message, e.g.
an error number and the Sender object. In fact you could do the whole
validation in the UMValidate handler!
I'm not sure precisely what's going on here, but it looks like the order of processing of messages is a bit messed up. Instead of killing your other form with Free, use Release and the focus will behave as you desire.
Another option is to use ShowModal instead of Show. Normally you show a processing dialog modally because you don't want the user making modifications to the main form whilst you are processing. If you do that then you can carry on using Free.

Main Form not displaying when showing modal form in main form's OnShow?

I have created one application in which Main Form Calls Sub Form on FormShow event of Main Form. Sub Form is displayed and gives two options to choose. If First option on sub form is selected then then a Message is displayed and after that Main form will be displayed. Now when application runs on first time then after option selected on subform Meassage will be displayed. But i want to display message with Main Form as Background. So any solution to this. below is the FormShow code.
Procedure TMainForm.FormShow(Sender:TObject);
begin
if (SubForm.ShowModal = mrOK) and bOption1 then
begin
ShowMessage('Enter the value');
end;
end;
If I understand correctly then your problem is that when the message box show up your main form is still invisible.
If this is the case then you have two options:
Don't show your SubForm from the OnShow event of the main form, but at a later time
Don't show the message directly after ShowModal returns, but at a later time
For point number 2 you can use a similar approach as I suggested here, using PostMessage. So your code would look somethind like this:
procedure TMainForm.FormShow(Sender:TObject);
begin
if (SubForm.ShowModal = mrOK) and bOption1 then
begin
PostMessage(Self.Handle, WM_SHOWMYDIALOG, 0, 0);
end;
end;
The handler of WM_SHOWMYDIALOG then displays the actual message. This approach can also work for point 1, see ain's answer.
PostMessageposts a message to your application's message queue which will be processed after the main form finished becoming visible.
Another Option would be to use OnActivate of the Mainform instead of onShow.
If I understand you right you want
const
UM_AFTERSHOW = WM_APP + 1;
type
TForm1 = class(TForm)
protected
procedure UMAfterShow(var Msg: TMessage); message UM_AFTERSHOW;
procedure DoShow; override;
end;
procedure TForm1.DoShow;
begin
inherited;
PostMessage(Self.Handle, UM_AFTERSHOW, 0, 0);
end;
procedure TForm1.UMAfterShow(var Msg: TMessage);
begin
ShowMessage('Enter the value');
end;
By showing your message in the UMAfterShow handler you give the main form opportunity to become visible and thus to be in background.
The problem you are seeing (if I understand properly) is that FormShow is called before your main form is actually visible. So the message dialog shows before your main form.
What you need to do is use PostMessage to post a message to your main form that you then handle. This will allow your FormShow code to finish and the code will be triggered after the form is shown.
Take a look here for an example.
Yet another option would be to drop a TTimer component on your main form to trigger the message dialog.
Drop a TTimer component on your main form and set the enabled property to False and change the time from 1000 to 100. Code your message dialog and also set the Timer.Enabled property to False in the timer event to avoid repeated firings.
Now you can Enable the Timer at the point where you would have shown the message dialog in your main form's OnShow event.

Flash ShowMessage(Pos) or any other predefined window in Dialogs unit in Delphi for Post-WInXP OS

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.

Sending WM_COMMAND to a TMenuItem

In my Delphi form's OnShow method, I determine that a dialog must be opened automatically once the form is opened - and I should be able to do this by simulating a click on a menuitem.
However, calling menuitem.Click brings up the dialog before the main form has opened - which is not what I want.
I expect that should do what I want, but I cannot find what parameters to pass for "wparam" to send the click to my menuitem.
PostMessage(handle, WM_COMMAND, wparam, 0)
The MSDN WM_COMMAND docs talk about IDM_* identifiers, but how does that appear in Delphi?
(I know this is a very old question but despite being resolved in some way the real question has really gone unanswered.)
--
The command item identifier of a 'TMenuItem' is in the Command property. According to WM_COMMAND's documentation the high word of 'wParam' would be '0' and the low word would be the menu identifier;
PostMessage(Handle, WM_COMMAND, MakeWParam(MyMenuItem.Command, 0), 0);
or simply;
PostMessage(Handle, WM_COMMAND, MyMenuItem.Command, 0);
With a popup menu item there would be a slight difference: the VCL handles popup menus' messages with a different utility window. The global PopupList variable has the handle to it in its Window property;
PostMessage(PopupList.Window, WM_COMMAND, MyPopupMenuItem.Command, 0);
Perhaps you can try to open the dialog in the OnActivate event ?
I am not really sure if the OnActivate gets fired again other than when the form is shown but if it does you can use :
procedure TForm1.FormActivate(Sender: TObject);
begin
Form2.ShowModal;
Self.OnActivate := nil;
end;
Wouldn't you have to do this with a one-time timer, if you want the form to appear as per a normal Show/ShowModal, get drawn (etc) fully, and then immediately do something else?
tmrKickOff : a TTimer, 100 ms interval, disabled at design time,
fires off a 'tmrKickOffTimer' event.
in form create,
tmrKickOff.Enabled:=false; //just in case something happened in IDE
in form show, at end of all other stuff;
tmrKickOff.Enabled:=true;
in tmrKickOffTimer
begin
tmrKickOffTimer.Enabled:=false;
menuItemClick(nil);
end;
with apologies for style, form and any error-trapping :-)
Alternatively, handle the Application.OnIdle event with something along the lines of ...
if not DialogDone then
begin
MyDialogForm.ShowModal; // or menuItem.Click ....
DialogDone := true;
end;
OnIdle won't fire (for the first time) until the Form is shown and the message queue is empty.
I don't think you can send a message directly to your menu item, but you can just post it to the main window and show your dialog from there. I do this and it works great so that the dialog box (in my case, a login prompt) appears on top of the main window to avoid confusion.
-Mark
procedure WMPostStartup(var Message: TMessage); message WM_POSTSTARTUP;
procedure TMainForm.WMPostStartup(var Message: TMessage);
begin
Self.Refresh;
// Now show the dialog box.
end;
One method I have used, which is very simular to MarkF's solution, is to create a new user defined message type and send a message using that type to yourself when you determine that you need to perform this other process after your main form displays:
const
wm_SpecialProc = wm_User + 1;
procedure TForm1.WMSpecialProc(var Message:tMessage); message wm_SpecialProc;
begin
Form2.ShowModal;
end;
procedure TForm1.OnShow(Sender:tObject);
begin
if true then
PostMessage(Application.MainForm.Handle,wm_SpecialProc,0,0);
end;
The nice thing about this method is that you are in control of the message generation, so can populate ANY lparam or wparam you want to later use by your handler. I sent the message directly through the application.mainform but you could also just say handle for the current form.

Delphi custom message handlers

When a user double-clicks a dbgrid, I show a non-modal form.
When they close that form, I want to refresh the grid.
To accomplish that, I have tried the following:
1 - Define a custom message constant:
const
WM_REFRESH_MSG = WM_USER + 1; //defined in a globally available unit
2 - In the OnClose event of my non-modal form, I have this:
procedure TMyNonModalForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
PostMessage(Self.Handle,WM_REFRESH_MSG,0,0);
end;
3 - In the private declarations of the form that holds the dbGrid, I have this:
procedure OnRefreshRequest(var Msg: TMessage); message WM_REFRESH_MSG;
...
procedure TMyFormWithADBGrid.OnRefreshRequest(var Msg: TMessage);
begin
RefreshGrid;
end;
After doing these things, the PostMessage fires fine, but the OnRefreshRequest procedure never runs. What am I doing wrong?
Note that WM_USER is not the correct constant to base your own messages on, unless you are writing a custom control directly descending from TWinControl. Use WM_APP instead.
Also, it's considered good style to use UM_ for User Message instead of WM_ because that prefix is supposed to be reserved for the Windows header files.
Aside from the message name in the other answer, you are posting a message to Self.Handle while Self is going away. You probably meant to post to a different handle (the window that launched the modeless one). Give your modeless window access to that handle when you create it, and post the message there instead.
The post message needs to be sent to the other window handle, not the self.handle that you have listed. One way to do this would be to create a new property on your non-modal form and assign it the handle of the other form just before you show your non-modal one.
Other than that, and implementing the WM_REFRESH_MSG properly (CheGueVerra has it correct) it should work fine.
You might try and change the end of the declaration to match the message you are trying to send.
procedure OnRefreshRequest(var Msg: TMessage); message WM_CEA_REFRESH;
Should be this
procedure OnRefreshRequest(var Msg: TMessage); message WM_REFRESH_MSG;
I've uploaded an example of "What would Kevin do?" to Embarcadero's newsgroup forum embarcadero.public.attachments.
Basically it's a custom event that the main form (or whatever form/object you want) subscribes to when the non-modal form closes. In the main (or whatever) form...
var
NonModalForm :TfmNonModalForm;
begin
NonModalForm := TfmNonModalForm.Create(nil);
NonModalForm.Execute(NonModalFormClosingListener);
In the Execute method
procedure TfmNonModalForm.Execute(YourListenerMethod: THeyIClosedEvent);
begin
FHeyIClosedEvent := YourListenerMethod;
Show();
end;
If you can't get to the forum and need the additional code, leave a comment and I'll post the missing pieces.
Good luck

Resources