【问题标题】:How to fix 'Receiver application gets empty message via WM_COPYDATA' issue?如何解决“接收器应用程序通过 WM_COPYDATA 获取空消息”问题?
【发布时间】:2019-01-08 11:09:18
【问题描述】:

我正在用 C++ 编写一个应用程序,它将向用 Delphi 编写的应用程序发送一条消息。

这是我的接收器应用:

单击按钮时,Edit1.Text 将通过ShellExecute() 作为命令行参数发送到发送方应用程序 (C++)。

发送方应用程序会将参数作为WM_COPYDATA 消息发送回接收方应用程序,接收方应用程序将在Edit2 文本框中显示该参数。

这是 Delphi 应用程序的代码(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;

这是我的 C++ 应用程序代码(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;
    }
}

问题是Edit2 文本框什么也没有显示。

顺便说一下,我在这个网站上对WM_COPYDATA进行了研究。但尽管有这种情况,我自己也无法解决我的问题。

那么,我应该怎么做才能解决我的问题?

【问题讨论】:

  • 应用间通信使用IPC(MMF、Pipe、MSMQ)
  • 指针在其他进程地址空间中无效。您需要发送文本的内容。
  • 您应该将 argv[1] 分配给 alinanMesaj 否则它将为空(以便 Edit2 什么都不显示)。
  • @tunglt 顺便说一句,如果我用引号发送字符串,我会正确接收字符串(Hello World)。我猜我的应用程序特征消息是文件路径。
  • 如果使用双引号(例如:"Hello World"),输入参数由空格字符 (' ') 分隔,因此 arg[1] 将使用引号内的整个字符串。

标签: c++ delphi ipc wm-copydata


【解决方案1】:

我发现这段代码存在三个问题:

  • 发件人正在发送空白数据,因为alinanMesaj 未分配任何值。

  • 两个应用程序之间存在 ANSI/UNICODE 不匹配。 Delphi 代码使用 Unicode 字符串,而 C++ 代码使用 ANSI 字符串。 WM_COPYDATA 作用于字节,而不是字符。你必须为你的字符串数据选择一个字节编码,并在两边保持一致。

  • VCL 内部使用WM_COPYDATA,因此发送方需要将cds.dwData 字段设置为唯一值,例如来自RegisterWindowMessage(),接收方在解释cds.lpData 数据之前必须验证该值.

用 at 说,试试这个:

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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-21
    • 1970-01-01
    • 1970-01-01
    • 2014-02-04
    • 2020-06-27
    • 2019-03-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多