【问题标题】:Concat PChar with string in Delphi在 Delphi 中用字符串连接 PChar
【发布时间】:2023-04-01 23:34:01
【问题描述】:

我需要构造一个字符串并通过PostMessage发送,即。

FileName := String_1 + String_2 + String_3;
PostMessage(FWndHandle, WM_BLA_BLA, NotifyData^.Action, LParam(FileName));

但有些东西不起作用。另外,FileName 是一个 PChar。代码如下所示:

var
   FileName : PChar;
   Directory_Str : String;
   AnotherString : String;
begin
    // Get memory for filename and fill it with data
    GetMem(FileName, NotifyData^.FileNameLength + SizeOf(WideChar));
    Move(NotifyData^.FileName, Pointer(FileName)^, NotifyData^.FileNameLength);
    PWord(Cardinal(FileName) + NotifyData^.FileNameLength)^ := 0;

    // TODO: Contact string before sending message
    // FileName := AnotherString + Directory_Str + FileName;

    PostMessage(FWndHandle, WM_BLA_BLA, NotifyData^.Action, LParam(FileName));

    ...
end;

现在我需要在调用PostMessage之前联系另一个字符串到变量FileName,即。

FileName := AnotherString + Directory_Str + FileName;
PostMessage(FWndHandle, WM_BLA_BLA, NotifyData^.Action, LParam(FileName));

如果 FileName 是一个字符串,这将起作用,但这里不是这种情况。

任何人都知道如何使用 PChar 做到这一点?我尝试了这些方法,有时可以,但最后总是会出现问题:

StrPCopy(FileName, FDirectory + String(FileName));

FileName := PChar(AnotherString + Directory_Str + FileName);

【问题讨论】:

  • “有些东西不工作”和“最后总是有些东西坏了”是什么意思?您遇到了什么具体问题?你为什么要使用PCharGetMem? (IOW,你为什么首先混合类型?)
  • PostMessaging 字符串的一个问题是,当字符串在目标线程/窗口中接收之前超出范围时往往会被破坏 - PostMessage 只是传达指针,它对 refCount 一无所知在字符串上,因此不会增加它。
  • @MartinJames 这就是问题中的代码使用GetMem 分配缓冲区的原因。
  • @KenWhite:你说的很对,现在真的晚了,我只是快速输入问题而没有详细说明,但大卫明白了,它的解决方案运行良好!

标签: string delphi pointers delphi-2010


【解决方案1】:

您不能轻易地将PostMessage 与通过引用传递的数据一起使用。原因是 PostMessage 异步执行,您需要保持传递的内存处于活动状态,直到消息被其接收者处理。我想这就是你的GetMem 代码背后的原因。

显然,这只适用于同一进程。而且您还会发现Windows 不允许您将PostMessage 用于接收指针的任何消息。例如,PostMessageWM_SETTEXT 总是失败。您只能希望使用用户定义的消息来做到这一点。当然,您需要在接收消息的代码中释放内存。

我将假设您使用的是用户定义的消息,它允许发送带有PostMessage 的字符串。在这种情况下,您已经有了解决方案。使用字符串变量进行连接,然后在答案中使用第一段代码。

虽然你可以像这样让它更干净:

function HeapAllocatedPChar(const Value: string): PChar;
var
  bufferSize: Integer;
begin
  bufferSize := (Length(Value)+1)*SizeOf(Char);
  GetMem(Result, bufferSize);
  Move(PChar(Value)^, Result^, bufferSize);
end;

procedure PostString(Window: HWND; Msg: UINT; wParam: WPARAM; 
  const Value: string);
var
  P: PChar;
begin
  P := HeapAllocatedPChar(Value);
  if not PostMessage(Window, Msg, wParam, LPARAM(P)) then
    FreeMem(P);
end;

您可以像这样调用该过程:

PostString(FWndHandle, WM_BLA_BLA, NotifyData^.Action, FDirectory + FileName);

您当前的代码失败,因为:

  1. 当您调用 StrPCopy 时,您不会为较长的字符串分配任何内存。
  2. 当您编写PChar(AnotherString + Directory_Str + FileName) 时,您会陷入您试图通过GetMem 避免的陷阱。这是一个本地字符串,在处理消息时已被释放。

如果您能找到解决问题的方法而不使用PostMessage 传递字符串,那么这可能比所有这些复杂性更可取。

【讨论】:

  • 这是一个很好的例子,说明了为什么将代码分成小的功能部分是值得的。通过将 GetMem/缓冲区复制代码与消息发布代码混合在一起,重新处理该代码变得更加困难。但是,将关注点分离到单独的函数中这种微不足道的权宜之计,突然变得容易了。当然,事后诸葛亮总是更容易看到正确的功能分解。但请始终关注此类问题,并注意更好地分解您的代码。
【解决方案2】:

查看 David 的回答,了解您的代码失败的原因。

我总是定义一个通过PostMessage 操作分配字符串的类。 没有泄漏的风险,并且可以通过更多信息轻松扩展。

Type
  TMyMessage = class
    msg : String;
    Constructor Create(aMessage : String);
  end;


// Sending the message
var
  myMsg : TMyMessage;
...
myMsg := TMyMessage.Create('A message');

if not PostMessage(someHandle,WM_something,WParam(myMsg),0)
then begin
  myMsg.free;
  ... Take care of this condition if PostMessage fails !!
end;  

// Receiving the message

procedure TSomeForm.GetMessage(var msg : TMessage);
var
  aMsg : TMyMessage;
begin
  ...
  aMsg := TMyMessage(msg.WParam);
  try
    ...
    // Do something with the message
  finally
    aMsg.Free;
  end;
end;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-01
    • 1970-01-01
    相关资源
    最近更新 更多