【问题标题】:delphi send message before application.rundelphi 在 application.run 之前发送消息
【发布时间】:2014-03-08 09:41:17
【问题描述】:

我正在开发一个应用程序来防止多个实例。我尝试使用 wm_copydata 向第一个应用程序实例发送消息,但它不起作用,但我可以通过 WM_SYSCOMMAND 发送消息

if not checkInstance.RestoreIfRunning(Application.Handle,oldHandle, 1) then
begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(Tfrm_main, frm_main);
  Application.Run;
end
else
begin

stringToSend := 'My Message';

aCopyData.dwData := 0; //use it to identify the message contents
aCopyData.cbData := 1 + Length(stringToSend) ;
aCopyData.lpData := PChar(stringToSend) ;

SendMessage(oldHandle,WM_COPYDATA,longint(oldHandle),longint(@aCopyData));
end;

....

mainform:

private
procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA;
.
.
.
procedure mainForm.WMCopyData(var Msg: TWMCopyData);
begin
ShowMessage('received!');
end;

【问题讨论】:

  • 检查oldHandle 是否包含您的mainForm 窗口的有效句柄。此外,您可以(应该)将True 返回到接收方的Msg.Result 属性,并检查发送方的SendMessage 的返回值。此外,SendMessage函数的参数类型是WParamLParam,而不是像网上一些纯代码sn-ps写的longint
  • oldhandle 是有效的句柄。另外,我已经为 Msg.Result 设置了返回值,但没有工作。我已经使用了这个参数类型并且它工作了
  • FWIW,你的演员阵容错了。投射到WPARAMLPARAM。这样你的代码就可以在 64 位下运行。
  • 我已更改 sendMessage 但无法再次工作:SendMessage(oldHandle, WM_COPYDATA, WPARAM(oldHandle), LPARAM(@aCopyData)) ;
  • 好吧,所以 oldHandle 没有包含您的 mainForm 窗口的有效句柄,因为我要求您检查。

标签: delphi


【解决方案1】:

您似乎将消息发送到了错误的窗口。或者以另一种方式查看它,您正试图在错误的窗口中处理消息。

你有:

if not checkInstance.RestoreIfRunning(Application.Handle, oldHandle, 1) then

当然,我们看不到checkInstance.RestoreIfRunning 背后的代码,但我的蜘蛛感知告诉我oldHandle 返回的句柄是应用程序句柄而不是主窗口句柄。让我产生这种怀疑的原因是:

  1. 您将Application.Handle 传递给RestoreIfRunning,因此可能是第二个应用程序尝试运行时看到的句柄。
  2. 您成功让WM_SYSCOMMAND 响应,大概是为了恢复应用程序的第一个实例,但这是由应用程序窗口过程处理的消息。

所以,在应用程序窗口程序而不是主窗体窗口程序中处理消息,应该一切都好。使用Application.HookMainWindow 来实现。

【讨论】:

  • oldHandle 和 application.handle 是不同的。此外,oldHandle 是 application.handle(不是表单句柄)。
  • 另外,oldHandle 是 application.handle(不是表单句柄)。 这正是我上面的假设。解决方案与我描述的完全一样。消息被传递到应用程序而不是表单。所以需要在应用程序窗口过程中处理。
  • 谢谢@David,我只是放了一行代码来查找表单句柄:oldHandle := FindWindow('mainForm', nil); 一切正常!
  • @poostchi 好吧,我不会那样做。如果另一个应用程序的主窗体具有相同的名称怎么办。你应该使用HookMainWindow
【解决方案2】:

这是一个解决方案

Unit1.pas

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

type
  TForm1 = class(TForm)
  private
    { Private declarations }
  public
    procedure WMApp(var msg: TMessage); message WM_APP;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TForm1 }

procedure TForm1.WMApp(var msg: TMessage);
begin
  Application.Restore;
end;

end.

执行:项目 - 查看源代码

Project1.dpr

program Project1;

uses
  Vcl.Forms,
  Windows,
  Messages,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}
var
  Hwnd: THandle;

begin
  Hwnd := FindWindow('TForm1', nil);

  if Hwnd = 0 then
  begin
    Application.Initialize;
    Application.MainFormOnTaskbar := True;
    Application.CreateForm(TForm1, Form1);
    Application.Run;
  end else
  begin
    if not IsWindowVisible(Hwnd) then
      PostMessage(Hwnd, WM_APP, 0, 0);
    SetForegroundWindow(Hwnd);
  end;
end.

就是这样

【讨论】:

  • 我认为提问者正试图让他们的解决方案(基于互斥锁)工作,而不是使用你不同的、更脆弱的解决方案。您的解决方案不可靠。
猜你喜欢
  • 1970-01-01
  • 2015-12-23
  • 1970-01-01
  • 2017-02-23
  • 1970-01-01
  • 1970-01-01
  • 2015-03-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多