开发者

WM_COPYDATA with and without quotes yields different results

Using WM_COPYDATA to pass command line params to another app instance with Delphi XE as follows:

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); //开发者_StackOverflow#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;

yields different results if the strings are quoted / appended to.

if #1 is used - the string comes in clean but is not usable if not quoted as filenames can have spaces and this:

C:\Users\MX4399\Research\delphi\instance\doc with spaces.doc

will be see as 3 paramaters in the end, while using #2 to quote the strings, or appending anything (#3, #4) causes

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


I believe that @TOndrej has spotted the main cause of the problem. However, I think you have a second more subtle bug.

Your app which receives the WM_COPYDATA message is, I think, treating lpData as a null-terminated string. If the data is malformed then you will have a buffer overrun. I believe that is exactly what is happening in your examples but it just turns out to be benign. The marshalling of WM_COPYDATA copies just the size of buffer specified in cbData. You must make sure you don't read beyond it. A malicious app could send you a WM_COPYDATA message with data to make you do just that. Instead I recommend you use cbData when reading.

So to send the string you write:

copyData.lpData := PChar(cmdParams);
copyData.cbData := ByteLength(cmdParams))
copyData.dwData := WaterMark; 

And then when you receive it you allocate a buffer and copy to that buffer based on the value of cbData.

SetString(cmdParams, PChar(copyData.lpData), copyData.cbData div SizeOf(Char));


I think you meant copyData.cbdata := 1 * SizeOf(Char) + ... instead of just 1 + ....


On a separate but related note, rather then using ParamStr() (which itself has a number of known bugs) to parse the original command-line and rebuild a new string from it, you could just use GetCommandLine() to get the original command-line instead and send it as-is.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜