Palamedes
asked on
WM_COPYDATA
Howdy all..
I am trying to write an application that communicates between itself and another application. It does this through a handle.. and uses WM_COPYDATA. I have never used this before and am unsure as to how it really works.
The original code was in C++ (written by some others) and it looks like this;
case WM_COPYDATA:
{
COPYDATASTRUCT *cds = (COPYDATASTRUCT *) lParam;
if (NULL == cds->lpData)
{
SetWindowLong(hDlg, DWL_MSGRESULT, 0);
return (TRUE); // No data received; ask for disconnection.
}
//
ExternalEventData *event_data = (ExternalEventData *)cds->lpData;
//
if (event_data->valid_fields & EEDF_TEXT_PRESENT)
{
OutputDebugString("Chat received is:");
OutputDebugString(event_da ta->text_m essage);
}
//
SetWindowLong(hDlg, DWL_MSGRESULT, 1);
return (TRUE);
I asked on here for a conversion and dragonslayer came through with flying colors with this;
type
TForm1 = class(TForm)
..
protected
procedure HandleWMCopyData(var Msg: TWMCopyData); message WM_COPYDATA;
end;
and later to use it
procedure TForm1.HandleWMCopyData(va r Msg: TWMCopyData);
var
event_data: PExternalEventData;
begin
if (Msg.CopyDataStruct.lpData = nil) then
SetWindowLong(hDlg, DWL_MSGRESULT, 0)
else begin
event_data := PExternalEventData(Msg.Cop yDataStruc t.lpData);
if Boolean(event_data.valid_f ields and EEDF_TEXT_PRESENT) then
begin
OutputDebugString('...')
SetWindowLong(hDlg, DWL_MSGRESULT, 1);
end;
end;
end;
Now... I don't know how to use that.. haha I have the code working.. but it isn't pulling data from the other application like it should..
My question then is, where do I call this? Or how do I call this? Does this get automatically called when a bit of data is pushed down to the handle?
For the life of me I can't figure this out.. Please help..
I am trying to write an application that communicates between itself and another application. It does this through a handle.. and uses WM_COPYDATA. I have never used this before and am unsure as to how it really works.
The original code was in C++ (written by some others) and it looks like this;
case WM_COPYDATA:
{
COPYDATASTRUCT *cds = (COPYDATASTRUCT *) lParam;
if (NULL == cds->lpData)
{
SetWindowLong(hDlg, DWL_MSGRESULT, 0);
return (TRUE); // No data received; ask for disconnection.
}
//
ExternalEventData *event_data = (ExternalEventData *)cds->lpData;
//
if (event_data->valid_fields & EEDF_TEXT_PRESENT)
{
OutputDebugString("Chat received is:");
OutputDebugString(event_da
}
//
SetWindowLong(hDlg, DWL_MSGRESULT, 1);
return (TRUE);
I asked on here for a conversion and dragonslayer came through with flying colors with this;
type
TForm1 = class(TForm)
..
protected
procedure HandleWMCopyData(var Msg: TWMCopyData); message WM_COPYDATA;
end;
and later to use it
procedure TForm1.HandleWMCopyData(va
var
event_data: PExternalEventData;
begin
if (Msg.CopyDataStruct.lpData
SetWindowLong(hDlg, DWL_MSGRESULT, 0)
else begin
event_data := PExternalEventData(Msg.Cop
if Boolean(event_data.valid_f
begin
OutputDebugString('...')
SetWindowLong(hDlg, DWL_MSGRESULT, 1);
end;
end;
end;
Now... I don't know how to use that.. haha I have the code working.. but it isn't pulling data from the other application like it should..
My question then is, where do I call this? Or how do I call this? Does this get automatically called when a bit of data is pushed down to the handle?
For the life of me I can't figure this out.. Please help..
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
I just have written a tiny component for the Jedi VCL.
The window to send (with SendMessage!) the message to is Application.Handle. So your receiving app has to hand the handle of that window to the sending app.
The data to send must be a single block of memory not containing any pointers to other data.
The reason is that the pointer is only meaningful in the sending app. The receiving app has its own virtual address space. Windows copies the data from one address space to the other.
-------------------------- ---------- ---------- -------
unit JvCopyData;
interface
uses
Windows, Messages, SysUtils, Classes, Forms;
type
TJvCopyDataEvent = procedure(const Sender: HWND; const Data: TCopyDataStruct) of object;
TJvCopyData = class(TComponent)
private
FCopyData: TJvCopyDataEvent;
FData: TCopyDataStruct;
function EventPipe(var Msg: TMessage): Boolean;
protected
procedure DoCopyData(Win: HWND; const Data: TCopyDataStruct);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Data: TCopyDataStruct read FData;
published
property OnCopyData: TJvCopyDataEvent read FCopyData write FCopyData;
end;
procedure Register;
implementation
constructor TJvCopyData.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FCopyData := nil;
FillChar(FData, SizeOf(TCopyDataStruct), #0);
Application.HookMainWindow (EventPipe );
end;
destructor TJvCopyData.Destroy;
begin
FCopyData := nil;
Application.UnhookMainWind ow(EventPi pe);
if FData.lpData <> nil then
FreeMem(FData.lpData);
inherited Destroy;
end;
function TJvCopyData.EventPipe(var Msg: TMessage): Boolean;
var
Data: PCopyDataStruct;
begin
Result := Msg.Msg = WM_COPYDATA;
if Result then
begin
if FData.lpData <> nil then
FreeMem(FData.lpData);
Data := PCopyDataStruct(Msg.LParam );
FData.dwData := Data.dwData;
FData.cbData := Data.cbData;
GetMem(FData.lpData, Data.cbData);
Move(Data.lpData^, FData.lpData^, Data.cbData);
DoCopyData(HWND(Msg.WParam ), FData);
end;
end;
procedure TJvCopyData.DoCopyData(Win : HWND; const Data: TCopyDataStruct);
begin
if Assigned(FCopyData) then
FCopyData(Win, FData);
end;
procedure Register;
begin
RegisterComponents('Sample s', [TJvCopyData]);
end;
end.
-------------------------- --------
The window to send (with SendMessage!) the message to is Application.Handle. So your receiving app has to hand the handle of that window to the sending app.
The data to send must be a single block of memory not containing any pointers to other data.
The reason is that the pointer is only meaningful in the sending app. The receiving app has its own virtual address space. Windows copies the data from one address space to the other.
--------------------------
unit JvCopyData;
interface
uses
Windows, Messages, SysUtils, Classes, Forms;
type
TJvCopyDataEvent = procedure(const Sender: HWND; const Data: TCopyDataStruct) of object;
TJvCopyData = class(TComponent)
private
FCopyData: TJvCopyDataEvent;
FData: TCopyDataStruct;
function EventPipe(var Msg: TMessage): Boolean;
protected
procedure DoCopyData(Win: HWND; const Data: TCopyDataStruct);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Data: TCopyDataStruct read FData;
published
property OnCopyData: TJvCopyDataEvent read FCopyData write FCopyData;
end;
procedure Register;
implementation
constructor TJvCopyData.Create(AOwner:
begin
inherited Create(AOwner);
FCopyData := nil;
FillChar(FData, SizeOf(TCopyDataStruct), #0);
Application.HookMainWindow
end;
destructor TJvCopyData.Destroy;
begin
FCopyData := nil;
Application.UnhookMainWind
if FData.lpData <> nil then
FreeMem(FData.lpData);
inherited Destroy;
end;
function TJvCopyData.EventPipe(var Msg: TMessage): Boolean;
var
Data: PCopyDataStruct;
begin
Result := Msg.Msg = WM_COPYDATA;
if Result then
begin
if FData.lpData <> nil then
FreeMem(FData.lpData);
Data := PCopyDataStruct(Msg.LParam
FData.dwData := Data.dwData;
FData.cbData := Data.cbData;
GetMem(FData.lpData, Data.cbData);
Move(Data.lpData^, FData.lpData^, Data.cbData);
DoCopyData(HWND(Msg.WParam
end;
end;
procedure TJvCopyData.DoCopyData(Win
begin
if Assigned(FCopyData) then
FCopyData(Win, FData);
end;
procedure Register;
begin
RegisterComponents('Sample
end;
end.
--------------------------
ASKER
Thank you all for your help so far.. And Merry Christmas..
Okay here is where my confusion comes in.. Does the WM_COPYDATA function happen automatically when another application pushes data to it? OR do I have to call it every now and then with my program? Set up a timer or loop or something?
The "sender" application is not my code.. but rather was written by another.. ( Here is his documentation: http://www.nicelycrafted.com/todc/main.aspx?a=3rdparty )
His code is in C++ and I am very much not a C++ programmer.. (hardly a Delphi programmer.. hehehe) So I'm trying to convert his C++ code to Delphi 5.
As it is I can get my client to register with his program.. (his program has a method that allows me to check what programs are registered) but I am not recieving anything.. I am sure its something I'm doing/not doing.. Thanks guys.
Okay here is where my confusion comes in.. Does the WM_COPYDATA function happen automatically when another application pushes data to it? OR do I have to call it every now and then with my program? Set up a timer or loop or something?
The "sender" application is not my code.. but rather was written by another.. ( Here is his documentation: http://www.nicelycrafted.com/todc/main.aspx?a=3rdparty )
His code is in C++ and I am very much not a C++ programmer.. (hardly a Delphi programmer.. hehehe) So I'm trying to convert his C++ code to Delphi 5.
As it is I can get my client to register with his program.. (his program has a method that allows me to check what programs are registered) but I am not recieving anything.. I am sure its something I'm doing/not doing.. Thanks guys.
It works like any other message, except that the system has to do some additional work.
Aha, you hand over a window handle of your app. Best use Application.Handle for this because a forms window can be recreated sometimes.
typedef struct ExternalEventData
{
unsigned long valid_fields;
ExternalEventID event_id;
char text_message[MAX_EED_TEXT_ LENGTH];
TODOBJECT object_identifier;
TODPOSITION theatre_position;
TODISLAND island_identifier;
TODPLAYER player_identifier;
} ExternalEventData;
type
ExternalEventData = record
valid_fields: Cardinal;
event_id: ExternalEventID;
text_message: array [0..MAX_EED_TEXT_LENGTH-1] of Char;
object_identifier: TODOBJECT;
theatre_position: TODPOSITION;
island_identifier: TODISLAND;
player_identifier: TODPLAYER;
end;
I am not sure if it is packed record or not. I will check. If no info jis available then you should tell them to define it. I will convert the other records later.
In my component you will get an OnCopyData event. The Data parameter contains a pointer to an ExternalEventData record.
Aha, you hand over a window handle of your app. Best use Application.Handle for this because a forms window can be recreated sometimes.
typedef struct ExternalEventData
{
unsigned long valid_fields;
ExternalEventID event_id;
char text_message[MAX_EED_TEXT_
TODOBJECT object_identifier;
TODPOSITION theatre_position;
TODISLAND island_identifier;
TODPLAYER player_identifier;
} ExternalEventData;
type
ExternalEventData = record
valid_fields: Cardinal;
event_id: ExternalEventID;
text_message: array [0..MAX_EED_TEXT_LENGTH-1]
object_identifier: TODOBJECT;
theatre_position: TODPOSITION;
island_identifier: TODISLAND;
player_identifier: TODPLAYER;
end;
I am not sure if it is packed record or not. I will check. If no info jis available then you should tell them to define it. I will convert the other records later.
In my component you will get an OnCopyData event. The Data parameter contains a pointer to an ExternalEventData record.
ASKER
Slick812 I have played with your example there.. Thank you.. You have answered my question with your example..
DragonSlayer, The ShowMessages aren't coming through at all which implies to me that I am never recieving a WM_COPYDATA from the sender application. Frustrating because other people are.. that tells me that for whatever reason, my program isn't registering with the sender program.. even though the sender program lists my app.. Confusing still .. *sigh*
Robert, Thanks for you efforts.. To be honest with you.. You're a much better programmer than I am asI don't understand your code.. hehehe But I will go over it and see what I can gleen from it.. Thanks =)
DragonSlayer, The ShowMessages aren't coming through at all which implies to me that I am never recieving a WM_COPYDATA from the sender application. Frustrating because other people are.. that tells me that for whatever reason, my program isn't registering with the sender program.. even though the sender program lists my app.. Confusing still .. *sigh*
Robert, Thanks for you efforts.. To be honest with you.. You're a much better programmer than I am asI don't understand your code.. hehehe But I will go over it and see what I can gleen from it.. Thanks =)
???, , , , Can you put a test in your WM_COPYDATA message func to just tell you if you ever get a message, not get any data from it. . . .
procedure GetCopyData(var Msg: TWmCopyData); message WM_COPYDATA;
procedure TForm1.GetCopyData(var Msg: TWmCopyData);
begin
Msg.Result := 1;
Label1.Caption := 'Got a WM_COPYDATA message';
end;
and by the way, if you return a zero it says it wil disconect you
procedure GetCopyData(var Msg: TWmCopyData); message WM_COPYDATA;
procedure TForm1.GetCopyData(var Msg: TWmCopyData);
begin
Msg.Result := 1;
Label1.Caption := 'Got a WM_COPYDATA message';
end;
and by the way, if you return a zero it says it wil disconect you
ASKER
I did exactly that.. I never get a WM_COPYDATA from the sender.. which tells me that I am not registering myself with it correctly.. yet when I ask that sender what clients are registered, it lists my client. And others have gotten it to work..
ASKER
Well I finally got it working..
The problem was I was sending the sender application the wrong handle.
Application.handle was not the handle I needed to use, but rather my forms handle. (Evidently they are different.. )
Thanks again everyone.
The problem was I was sending the sender application the wrong handle.
Application.handle was not the handle I needed to use, but rather my forms handle. (Evidently they are different.. )
Thanks again everyone.
Thank you for good info too.
I faced the same problem.
Thank you to let's me know that WM_USER can't PostMessage
data record across the process.
I lost my time 2 days to discover this behavier.
---
By the way SendMessage WM_DATACOPY is too late for
keyboard HOOK because it place in thread message queue.
Thank you to let's me know that WM_USER can't PostMessage
data record across the process.
I lost my time 2 days to discover this behavier.
---
By the way SendMessage WM_DATACOPY is too late for
keyboard HOOK because it place in thread message queue.
procedure TForm1.HandleWMCopyData(va
var
event_data: PExternalEventData;
begin
ShowMessage('Received WM_COPYDATA!');
if (Msg.CopyDataStruct.lpData
begin
ShowMessage('Received nil in the struct!');
SetWindowLong(hDlg, DWL_MSGRESULT, 0);
end else
begin
event_data := PExternalEventData(Msg.Cop
if Boolean(event_data.valid_f
begin
OutputDebugString('...')
SetWindowLong(hDlg, DWL_MSGRESULT, 1);
end;
end;
end;
end;