【问题标题】:Sendmessage with WM_COPYDATA from vb6 to delphi is garbled从 vb6 到 delphi 的带有 WM_COPYDATA 的发送消息是乱码
【发布时间】:2012-09-25 06:58:20
【问题描述】:

我正在使用来自 vb6 应用程序的 WM_COPYDATA 向 delphi 应用程序发送数据。在我本地为英语的系统中,我正确接收数据,但在另一个使用荷兰语本地的系统上,接收文本是乱码。

接收app是delphi,代码是

procedure TReceiverMainForm.WMCopyData(var Msg: TWMCopyData);
var
  copyDataType: TCopyDataType;
begin
  copyDataType := TCopyDataType(Msg.CopyDataStruct.dwData);

  //Handle of the Sender
  mmoResult.Lines.Add(Format('WM_CopyData from: %d', [msg.From]));

  case copyDataType of
    cdtString: HandleCopyDataString(Msg.CopyDataStruct);
  end;

  //Send something back
  msg.Result := mmoResult.Lines.Count;
end;

procedure TReceiverMainForm.HandleCopyDataString(
  copyDataStruct: PCopyDataStruct);
var
  s: string;
begin
  s := PChar(copyDataStruct.lpData);
  mmoResult.Lines.Add(s);
end;

编辑

这里是发送数据的vb6代码,发送的数据是字符串

Dim buf() As Byte
ReDim buf(1 To LenB(Message))
Call CopyMemory(buf(1), ByVal Message, Len(Message))
cds.dwData = 0
cds.cbData = Len(Message) + 1
cds.lpData = VarPtr(buf(1))
' Send the string.
Dim i As Long
i = SendMessage(lHwnd, WM_COPYDATA, MainForm.hwnd, cds)

谁能告诉我做错了什么?

【问题讨论】:

  • 你是如何发送数据的?根据您使用的 Delphi 版本,该字符串可以解释为 unicode 或 ANSI。如果你能确认你的VB代码发送方式,以及Delphi版本,我可以做一个合适的回答。
  • @Deanna,它有 Delphi 7 标签,所以你离它更近了一半;-)
  • 我看到这意味着它正在进行 ANSI 转换。我还需要知道VB6的代码才能给出明确的答案。 D7 seems to support wide chars but the UI doesn't.
  • @Deanna,关于 D7,就像你说的那样。您可以声明WideStrings,但当您使用ANSI 的内置组件时,它们将无法正确显示。
  • 该代码似乎混淆了字节长度、字符长度和字符编码。我会尽快回答,解释发生了什么。

标签: delphi vb6 delphi-7 sendmessage wm-copydata


【解决方案1】:

VB 字符串基于 COM BSTR 字符串类型,就像 Delphi 的 WideString 字符串类型一样。 BSTR 是一个 UTF-16 编码的 Unicode 字符串。 LenB() 返回 VB 字符串在转换为本地机器的当前语言环境时占用的字节数。你没有考虑到这一点。您没有将字符串字节正确复制到缓冲区中,也没有将 cds.cbData 字段设置为正确的值。 Len() 返回字符串中 UTF-16 编码字符的数量,而 LenB() 返回字节数。对于英文字符串,Len()LenB() 将返回相同的值,但对于外语则无法保证。

我建议您按原样发送原始的 VB Unicode 编码数据,并更改您的 Delphi 代码以将传入数据视为 Unicode,而不是像当前那样处理 Ansi(PChar 在 Delphi 7 中是 Ansi,但是是 Unicode在 Delphi 2009+ 中)。

您还需要为cds.dwData 字段分配一个唯一值。 WM_COPYDATA 被 VCL 用于它自己的一些内部数据,因此您必须区分 WM_COPYDATA 消息和 VCL 的消息。

试试这个:

cds.dwData = RegisterWindowMessage("MyWMCopyData")
If cds.dwData <> 0 Then
  cds.cbData = Len(Message) * 2 ' characters are 2-bytes each
  cds.lpData = StrPtr(Message) ' access the string's character buffer directly
  ' Send the string. 
  Dim i As Long 
  i = SendMessage(lHwnd, WM_COPYDATA, MainForm.hwnd, cds) 
End If

.

var
  uMyWMCopyDataMsg: UINT = 0;

procedure TReceiverMainForm.WMCopyData(var Msg: TWMCopyData); 
var 
  s: WideString; // you can use UnicodeString in D2009+ 
begin 
  if (uMyWMCopyDataMsg = 0) or (Msg.CopyDataStruct.dwData <> uMyWMCopyDataMsg) then
  begin
    inherited;
    Exit;
  end;

  mmoResult.Lines.Add(Format('WM_CopyData from: %d', [msg.From])); 

  SetString(s, PWideChar(Msg.CopyDataStruct.lpData), Msg.CopyDataStruct.cbData div SizeOf(WideChar)); 
  mmoResult.Lines.Add(s); 

  msg.Result := mmoResult.Lines.Count; 
end; 

initialization
  uMyWMCopyDataMsg := RegisterWindowMessage('MyWMCopyData');

【讨论】:

  • 我不明白它是如何处理英文字符串的,BSTR 中的 char 总是 2 个字节,不是吗?
  • 是的,BSTR 字符是 2 个字节。但是,在 VB 中,传递字符串 ByVal 会将其转换为 Ansi,以便与基于 char 的 API 兼容。因此,代码在调用CopyMemory() 时将源字符串转换为Ansi,但它使用Len() 而不是LenB() 作为第三个参数。这仅适用于单字节语言,例如英语和基于 Latin1 的语言。
  • 谢谢。但这仍然不应该是问题的一部分,因为除了远东语言之外,所有的 ansi 字符集都是每个字符一个字节。据我所知,荷兰语使用 latin-1 (win-1252)。
  • @RemyLebeau 正在使用 d7,那么这个 s: WideString; // you can use UnicodeString in D2009+ 如何应用?关于你谈到的WM_COPYDATA和VLC的事情,这个例子delphi.about.com/od/windowsshellapi/a/wm_copydata.htm会变得不合适
  • 如果您使用我展示的发送 UTF-16 数据而不是 Ansi 数据的代码,则适用 WideString。是的,那篇文章是不完整的。它涉及到在发送消息时需要填写cds.dwData 来识别WM_COPYDATA,但在接收消息时会忽略这些标识符。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-05
  • 1970-01-01
  • 1970-01-01
  • 2011-12-16
  • 2019-08-12
  • 1970-01-01
相关资源
最近更新 更多