I'm trying to write a component, to send string messages between applications by WM_COPYDATA.
I'd like trap the WM_COPYDATA, but this doesn't work:
TMyMessage = class(TComponent)
private
{ Private declarations }
…
protected
{ Protected declarations }
…
procedure WMCopyData(var Msg : TMessage); message WM_COPYDATA;
…
end;
Searching Google a lot, found some reference using wndproc. I tried it, but it isn't working either.
TMyMessage = class(TComponent)
…
protected
{ Protected declarations }
…
procedure WMCopyData(var Msg : TMessage); message WM_COPYDATA;
procedure WndProc(var Msg: TMessage);
…
end;
…
procedure TMyMessage.WndProc(var Msg: TMessage);
begin
//inherited;
if Msg.Msg = WM_COPYDATA then
WMCopyData(Msg);
end;
Please help, what is wrong?
What you have so far is fine, but you need to arrange for messages to be delivered to your component in the first place. That requires a window handle. Call AllocateHWnd
and pass it your component's WndProc
method. It will return a window handle, which you should destroy as your component is destroyed.
constructor TMyMessage.Create(AOwner: TComponent);
begin
inhreited;
FHandle := AllocateHWnd(WndProc);
end;
destructor TMyMessage.Destroy;
begin
DeallocateHWnd(FHandle);
inherited;
end;
Rather than testing for each message directly, you can let TObject
do that for you. That's what the Dispatch
method is for. Pass it a TMessage
record, and it will find and call the corresponding message-handler method for you. If there is no such handler, it will call DefaultHandler
instead. Override that can call DefWindowProc
.
procedure TMyMessage.WndProc(var Message);
begin
Dispatch(Message);
end;
procedure TMyMessage.DefaultHandler(var Message);
begin
TMessage(Message).Result := DefWindowProc(Self.Handle, TMessage(Message).Msg,
TMessage(Message).WParam, TMessage(Message).LParam);
end;
Your problem is that TComponent
is not a windowed component. WM_COPYDATA
is a windows message and is delivered via a window procedure. Hence you need a window handle. Use AllocateHwnd
to get hold of one of these.
type
TMyComponent = class(TComponent)
private
FWindowHandle: HWND;
procedure WndProc(var Msg: TMessage);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
constructor TMyComponent.Create(AOwner: TComponent);
begin
inherited;
FWindowHandle := AllocateHwnd(WndProc);
end;
destructor TMyComponent.Destroy;
begin
DeallocateHwnd(FWindowHandle);
inherited;
end;
procedure TMyComponent.WndProc(var Msg: TMessage);
begin
if Msg.Msg=WM_COPYDATA then
//do domething
else
Msg.Result := DefWindowProc(FWindowHandle, Msg.Msg, Msg.WParam, Msg.LParam);
end;
Whatever is sending the messages will need to find a way to get hold of the window handle.
I did it this way:
My web modules which are running in a thread need to send strings to a memo on the main form.
FReceiverFromWS is a THandle
On create:
procedure TWebModuleWebServices.WebModuleCreate(Sender: TObject);
begin
FReceiverFromWS := FindWindow(PChar('TFormWebServices'),PChar(cFormCaption + FormWebServices.Instance)); // Search by class name and caption of receiving form
// ==> you could to that without form caption, but I need to distinguish between running services
if FReceiverFromWS = 0 then
begin
Assert(False,'CopyData receiver NOT found!'); // Probably TFormWebServices not yet created
Exit;
end;
end;
To send messages:
procedure TWebModuleWebServices.SendAMessage(Msg: String);
// Windows will guarantee that the data sent in the COPYDATASTRUCT will exist until after the WM_COPYDATA message
// has been carried out. As such, we must use SendMessage() to send a WM_COPYDATA message. We cannot use PostMessage().
var
lCopyDataStruct: TCopyDataStruct;
begin
lCopyDataStruct.dwData := 0;
lCopyDataStruct.cbData := 1 + Length(Msg);
lCopyDataStruct.lpData := PChar(Msg);
SendMessage(FReceiverFromWS, WM_COPYDATA, wParam(FReceiverFromWS), lParam(@lCopyDataStruct));
end;
In the main form, public method
procedure WMCopyData(var Msg : TWMCopyData) ; message WM_COPYDATA;
is:
procedure TFormWebServices.WMCopyData(var Msg: TWMCopyData);
var
i : integer;
s : string;
begin
i := Msg.CopyDataStruct.dwData;
case i of
0: begin // Message to display
s := String(PChar(Msg.CopyDataStruct.lpData));
AddMemoLine(s);
end;
1: begin // Statistical data
s := String(PChar(Msg.CopyDataStruct.lpData));
FrmWebServiceStats.CollectStats(s);
end;
end;
end;
(As you can see, I actually use dwData to signal the kind of message and handle these differently)