1

I am writing an application in C++ which will send a message to an application written in Delphi.

This is my receiver app:

image

When the button is clicked, Edit1.Text will be sent via ShellExecute() as a command line parameter to the sender app (C++).

The sender app will send back the parameter as a WM_COPYDATA message to the receiver app, which will show it in the Edit2 text box.

This is the Delphi app's code (Delphi 10.3 Rio):

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShellExecute(0, 'open', 'deneme.exe', PWideChar(Edit1.Text), nil, SW_HIDE);
end;

procedure TForm1.MesajAl(var Mesaj: TMessage);
var
  Veri: PCopyDataStruct;
begin
  Veri := Pointer(Mesaj.LParam);
  Edit2.Text := PChar(Veri^.lpData);
end;

This is my C++ app's code (Code::Blocks IDE):

#include <iostream>
#include <windows.h>
#include <tchar.h>
using namespace std;

int main(int argc, char* argv[])
{
    if (argc < 2)
    {
        return 0;
    }
    else
    {
        HWND hwnd = FindWindow(NULL, "Form1");

        string alinanMesaj;

        LPCTSTR gonderilecekMesaj = alinanMesaj.c_str();

        COPYDATASTRUCT cds;
        cds.cbData = sizeof(TCHAR)*(_tcslen(gonderilecekMesaj) + 1);
        cds.dwData = 1;
        cds.lpData = (PVOID)gonderilecekMesaj;

        SendMessage(hwnd, WM_COPYDATA, (WPARAM)hwnd, (LPARAM)(LPVOID)&cds);

        return 0;
    }
}

The problem is the Edit2 text box shows nothing.

By the way, I have made a research in this website about WM_COPYDATA. But despite the fact that this situation, I could not fix my issue myself.

So, what should I do in order to fix my issue?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • use IPC (MMF, Pipe, MSMQ) for communication between applications – Zam Jan 08 '19 at 12:17
  • The pointer isn't valid in the other process address space. You need to send the content of the text. – David Heffernan Jan 08 '19 at 13:21
  • @Zam `WM_COPYDATA` is an IPC mechanism, the problem is not the choice of IPC mechanism, but the implementation – David Heffernan Jan 08 '19 at 13:37
  • 1
    You should assign argv[1] to `alinanMesaj` or it will be empty (so that the Edit2 shows nothing). – tunglt Jan 08 '19 at 13:37
  • @tunglt Ok, I have assigned alinanMesaj to string(argv[1]) and I have changed to PChar to PAnsiChar; because Delphi showed weird characters. Now, characters are normal; but the new problem is if message contains space character such as "Hello World!", Delphi app receives only string which front of the space (only "Hello"). So, what should I do in order to fix that issue? –  Jan 08 '19 at 14:46
  • 1
    @tunglt By the way, if I send string in quotation marks, I receive string correctly (Hello World). I guess my app traits messages as file paths. –  Jan 08 '19 at 14:50
  • 1
    Because the input args were separated by space character (' ') if you use double quotes (e.g: "Hello World" ), the arg[1] will take the whole string inside the quotation marks. – tunglt Jan 08 '19 at 15:04

1 Answers1

0

I see three issues with this code:

  • The sender is sending blank data, because alinanMesaj is not assigned any value.

  • There is an ANSI/UNICODE mismatch between the two apps. The Delphi code is working with Unicode strings, while the C++ code is working with ANSI strings instead. WM_COPYDATA operates on bytes, not characters. You have to pick a byte encoding for your string data and be consistent with it on both sides.

  • The VCL uses WM_COPYDATA internally, so the sender needs to set the cds.dwData field to a unique value, such as from RegisterWindowMessage(), that the receiver must verify before it interprets the cds.lpData data.

With at said, try this instead:

var
  MY_CDS_ID: UINT;

procedure TForm1.FormCreate(Sender: TObject);
begin
  MY_CDS_ID := RegisterWindowMessage('MYCDSID'); // use whatever unique name you want
  if MY_CDS_ID = 0 then
    RaiseLastOSError;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  // you really should be using CreateProcess() instead...
  ShellExecute(0, nil, 'deneme.exe', PChar(AnsiQuotedStr(Edit1.Text, '"')), nil, SW_HIDE);
end;

procedure TForm1.MesajAl(var Mesaj: TMessage);
var
  Veri: PCopyDataStruct;
  s: UnicodeString;
begin
  Veri := Pointer(Mesaj.LParam);
  if Veri^.dwData = MY_CDS_ID then
  begin
    SetString(s, PWideChar(Veri^.lpData), Veri^.cbData div SizeOf(WideChar));
    Edit2.Text := s;
  end else
    inherited;
end;
#include <iostream>
#include <windows.h>
#include <string>

int main(int argc, char* argv[])
{
    if (argc < 2)
        return 0;

    HWND hwnd = FindWindow("TForm1", "Form1");
    if (!hwnd)
        return 0;

    UINT MY_CDS_ID = RegisterWindowMessage("MYCDSID"); // must match the named used by the Delphi code
    if (!MY_CDS_ID)
        return 0;

    std::wstring alinanMesaj;

    int arglen = lstrlenA(argv[1]);
    int wlen = MultiByteToWideChar(CP_ACP, 0, argv[1], arglen, NULL, 0);
    if (wlen > 0)
    {
        alinanMesaj.resize(wlen);
        MultiByteToWideChar(CP_ACP, 0, argv[1], arglen, &alinanMesaj[0], wlen);
    }

    COPYDATASTRUCT cds;
    cds.cbData = sizeof(wchar_t) * alinanMesaj.size();
    cds.dwData = MY_CDS_ID;
    cds.lpData = const_cast<wchar_t*>(alinanMesaj.c_str());

    SendMessage(hwnd, WM_COPYDATA, reinterpret_cast<WPARAM>(hwnd), reinterpret_cast<LPARAM>(&cds));

    return 0;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770