delphi在application.run之前发送消息

delphi在application.run之前发送消息,delphi,Delphi,我正在开发一个应用程序,以防止出现多个实例。我尝试使用wm_copydata向第一个应用程序实例发送消息,但它不起作用,但我可以通过wm_SYSCOMMAND发送消息 if not checkInstance.RestoreIfRunning(Application.Handle,oldHandle, 1) then begin Application.Initialize; Application.MainFormOnTaskbar := True; Application.Cre

我正在开发一个应用程序,以防止出现多个实例。我尝试使用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;

看起来您将消息发送到了错误的窗口。或者从另一个角度来看,您试图在错误的窗口中处理消息

你有:

if not checkInstance.RestoreIfRunning(Application.Handle, oldHandle, 1) then
当然,我们看不到
checkInstance.RestoreIfRunning
后面的代码,但我的蜘蛛感知告诉我,
oldHandle
中返回的句柄是应用程序句柄,而不是主窗口句柄。让我产生这种怀疑的是:

  • 您将
    Application.Handle
    传递给
    RestoreIfRunning
    ,因此当第二个应用程序尝试运行时,可能会看到该句柄
  • 您成功地获得
    WM_SYSCOMMAND
    响应,可能是为了恢复应用程序的第一个实例,但这是一条由应用程序窗口过程处理的消息
  • 因此,在应用程序窗口过程而不是主窗体窗口过程中处理消息,这一切都应该很好。使用
    Application.HookMainWindow
    来实现这一点。

    这里有一个解决方案

    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.
    
    执行:项目-查看源代码

    项目1.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.
    

    仅此而已

    检查
    oldHandle
    是否包含
    mainForm
    窗口的有效句柄。此外,您还可以(应该)将
    True
    返回到接收方的
    Msg.Result
    属性,并检查发送方的
    SendMessage
    的返回值。此外,
    SendMessage
    函数的参数类型是
    WParam
    LParam
    ,而不是
    longint
    ,正如在Internet上的一些完全代码片段中所写的那样。oldhandle是有效的句柄。另外,我已经为Msg.Result输入了返回值,但没有工作。我使用了这个参数类型,它工作fwiw,你的类型转换是错误的。转换为
    WPARAM
    LPARAM
    。这样,您的代码可以在64位中工作。我已更改sendMessage,但不再工作:
    sendMessage(oldHandle、WM_COPYDATA、WPARAM(oldHandle)、LPARAM(@aCopyData))
    很好,因此,
    oldHandle
    没有包含有效的
    mainForm
    窗口句柄,因为我要求您检查。oldHandle和application.handle是不同的。另外,oldHandle是application.handle(不是表单句柄)。另外,oldHandle是application.handle(不是表单句柄)。这正是我上面的假设。解决方案正是我所描述的。消息传递给应用程序,而不是表单。所以您需要在应用程序窗口过程中处理它。谢谢@David,我只在那里放了一行代码来查找表单句柄:
    oldHandle:=FindWindow('mainForm',nil)一切正常@好吧,我不会那样做的。如果另一个应用程序的主窗体名称相同怎么办。你应该使用
    HookMainWindow
    。我认为询问者是想让他们的解决方案(基于互斥)发挥作用,而不是使用你不同的、稍微脆弱的解决方案。您的解决方案不可靠。