Delphi 带引号和不带引号的WM_COPYDATA产生不同的结果

Delphi 带引号和不带引号的WM_COPYDATA产生不同的结果,delphi,unicode,delphi-xe,wm-copydata,Delphi,Unicode,Delphi Xe,Wm Copydata,使用WM_COPYDATA将命令行参数传递给另一个具有Delphi XE的应用程序实例,如下所示: function DAppInstance.SendParamsToPrevInstance(AWindowHandle: THandle): Boolean; var copyData: TCopyDataStruct; cmdParams : string; i : integer; begin cmdParams := ''; for i := 1 to ParamCou

使用WM_COPYDATA将命令行参数传递给另一个具有Delphi XE的应用程序实例,如下所示:

function DAppInstance.SendParamsToPrevInstance(AWindowHandle: THandle): Boolean;
var
  copyData: TCopyDataStruct;
  cmdParams : string;
  i : integer;
begin
  cmdParams := '';
  for i := 1 to ParamCount do
    cmdParams := cmdParams + ParamStr(i); //#1
  //cmdParams := cmdParams + '"' + ParamStr(i) + '" '; //#2
  //cmdParams := cmdParams + format('"%s" ', [ParamStr(i)]); //#3
  //cmdParams := cmdParams + format('%s;', [ParamStr(i)]); //#4

  copyData.lpData := pchar(cmdParams);
  copyData.cbData := 1 + (bytelength(cmdParams));
  copyData.dwData := WaterMark;  //ID for APP

  result := SendMessage(AWindowHandle, 
    WM_COPYDATA, 
    Application.Handle, 
    LPARAM(@copyData)) = 1;
end;
如果引用/追加字符串,则会产生不同的结果

如果使用1-字符串是干净的,但如果不引用,则不可用,因为文件名可以有空格,这是:

C:\Users\MX4399\Research\delphi\instance\doc with spaces.doc
最后将被视为3个参数,同时使用2引用字符串,或附加任何3、4原因

"C:\Users\MX4399\Research\delphi\instance\doc with spaces.doc"'#$FF00'궳獧

我想你的意思是copyData.cbdata:=1*SizeOfChar+。。。我想你的意思是copyData.cbdata:=1*SizeOfChar+。。。我相信@TOndrej已经发现了问题的主要原因,而不仅仅是1+..

。然而,我认为你还有第二个更微妙的错误

我认为,您接收WM_COPYDATA消息的应用程序将lpData视为以null结尾的字符串。如果数据格式不正确,则会出现缓冲区溢出。我相信这正是你们例子中发生的事情,但事实证明这是良性的。WM_COPYDATA的编组只复制cbData中指定的缓冲区大小。你必须确保你的阅读不会超出它。恶意应用程序可能会向您发送带有数据的WM_COPYDATA消息,让您这样做。相反,我建议您在阅读时使用cbData

因此,要发送您编写的字符串:

copyData.lpData := PChar(cmdParams);
copyData.cbData := ByteLength(cmdParams))
copyData.dwData := WaterMark; 
然后,当您收到它时,您会根据cbData的值分配一个缓冲区并复制到该缓冲区


我相信@TOndrej已经发现了问题的主要原因。然而,我认为你还有第二个更微妙的错误

我认为,您接收WM_COPYDATA消息的应用程序将lpData视为以null结尾的字符串。如果数据格式不正确,则会出现缓冲区溢出。我相信这正是你们例子中发生的事情,但事实证明这是良性的。WM_COPYDATA的编组只复制cbData中指定的缓冲区大小。你必须确保你的阅读不会超出它。恶意应用程序可能会向您发送带有数据的WM_COPYDATA消息,让您这样做。相反,我建议您在阅读时使用cbData

因此,要发送您编写的字符串:

copyData.lpData := PChar(cmdParams);
copyData.cbData := ByteLength(cmdParams))
copyData.dwData := WaterMark; 
然后,当您收到它时,您会根据cbData的值分配一个缓冲区并复制到该缓冲区


在一个单独但相关的注释中,您可以使用GetCommandLine来获取原始命令行并按原样发送,而不是使用ParamStr(ParamStr本身有许多已知错误)来解析原始命令行并从中重建新字符串。

在一个单独但相关的注释中,与其使用ParamStr来解析原始命令行并从中重建新字符串,不如使用GetCommandLine来获取原始命令行并按原样发送。

+1 Yes这就可以解释它了。当然,编写SizeOfChar*1+LengthcmdParams要比使用特殊的ByTeleLength函数更容易。实际上,我认为在接收代码中使用字节计数更安全,而不必费心编组零终止符。依赖于以零结尾的WM_COPYDATA会导致缓冲区溢出。ByTeleLength为您计算SizeOfChar*LenghtString实际上,它使用StringElementSize而不是SizeOf。它返回字符串使用的字节数,而不是字符数。WM_COPYDATA需要一个字节计数。@Remy Yes,但它忽略了空终止符,我们假设收件人代码将lpData视为C字符串。将其更改为COPYDATA.cbdata:=ByTeleLength 0+ByTeleLength CmdParams;它工作得很好-相当合乎逻辑+1是的,这可以解释它。当然,编写SizeOfChar*1+LengthcmdParams要比使用特殊的ByTeleLength函数更容易。实际上,我认为在接收代码中使用字节计数更安全,而不必费心编组零终止符。依赖于以零结尾的WM_COPYDATA会导致缓冲区溢出。ByTeleLength为您计算SizeOfChar*LenghtString实际上,它使用StringElementSize而不是SizeOf。它返回字符串使用的字节数,而不是字符数。WM_COPYDATA需要一个字节计数。@Remy Yes,但它忽略了空终止符,我们假设收件人代码将lpData视为C字符串。将其更改为COPYDATA.cbdata:=ByTeleLength 0+ByTeleLength CmdParams;它工作得很好-相当合乎逻辑你是对的,这样会更安全,我会调查的。奇怪的是,修改paramstrn会导致这个结果,如果附加到,则不会。您之前只是运气好而已。代码在没有附加的情况下被破坏了。你是对的,这样会更安全,我会调查的。奇怪的是,修改paramstrn会导致这个结果,如果附加到,则不会。您之前只是运气好而已。代码在没有附加的情况下被破坏了。