防止多个实例 - 还要处理命令行参数?

12 delphi

我正在处理来自Windows的应用程序关联扩展文件.所以当你从Windows双击一个文件时,它将执行我的程序,我从那里处理文件,如:

procedure TMainForm.FormCreate(Sender: TObject);
var
  i: Integer;
begin
  for i := 0 to ParamCount -1 do
  begin
    if SameText(ExtractFileExt(ParamStr(i)), '.ext1') then
    begin
      // handle my file..

      // break if needed
    end else
    if SameText(ExtractFileExt(ParamStr(i)), '.ext2') then
    begin
      // handle my file..

      // break if needed
    end else
  end;
end;
Run Code Online (Sandbox Code Playgroud)

这几乎是我想要的,但是当我测试时,我意识到它不考虑只使用我的程序的一个实例.

因此,例如,如果我从Windows中选择了多个文件并同时打开它们,这将创建与我的程序相同数量的实例,并打开文件数.

什么是一个很好的方法来解决这个问题,以便不是打开我的程序的几个实例,而是打开Windows的任何其他文件只会集中回到唯一的实例,我正常处理文件?

谢谢

UPDATE

我在这里找到了一篇很好的文章:http://www.delphidabbler.com/articles? article = 13&part = 2我认为这就是我需要的,并展示了如何使用rhooligan提到的Windows API.我现在要读完它..

Dav*_*nan 8

这是一些简单的示例代码,可以完成工作.我希望这是不言自明的.

program StartupProject;

uses
  SysUtils,
  Messages,
  Windows,
  Forms,
  uMainForm in 'uMainForm.pas' {MainForm};

{$R *.res}

procedure Main;
var
  i: Integer;
  Arg: string;
  Window: HWND;
  CopyDataStruct: TCopyDataStruct;
begin
  Window := FindWindow(SWindowClassName, nil);
  if Window=0 then begin
    Application.Initialize;
    Application.MainFormOnTaskbar := True;
    Application.CreateForm(TMainForm, MainForm);
    Application.Run;
  end else begin
    FillChar(CopyDataStruct, Sizeof(CopyDataStruct), 0);
    for i := 1 to ParamCount do begin
      Arg := ParamStr(i);
      CopyDataStruct.cbData := (Length(Arg)+1)*SizeOf(Char);
      CopyDataStruct.lpData := PChar(Arg);
      SendMessage(Window, WM_COPYDATA, 0, NativeInt(@CopyDataStruct));
    end;
    SetForegroundWindow(Window);
  end;
end;

begin
  Main;
end.
Run Code Online (Sandbox Code Playgroud)

 

unit uMainForm;

interface

uses
  Windows, Messages, SysUtils, Classes, Controls, Forms, StdCtrls;

type
  TMainForm = class(TForm)
    ListBox1: TListBox;
    procedure FormCreate(Sender: TObject);
  protected
    procedure CreateParams(var Params: TCreateParams); override;
    procedure WMCopyData(var Message: TWMCopyData); message WM_COPYDATA;
  public
    procedure ProcessArgument(const Arg: string);
  end;

var
  MainForm: TMainForm;

const
  SWindowClassName = 'VeryUniqueNameToAvoidUnexpectedCollisions';

implementation

{$R *.dfm}

{ TMainForm }

procedure TMainForm.CreateParams(var Params: TCreateParams);
begin
  inherited;
  Params.WinClassName := SWindowClassName;
end;

procedure TMainForm.FormCreate(Sender: TObject);
var
  i: Integer;
begin
  for i := 1 to ParamCount do begin
    ProcessArgument(ParamStr(i));
  end;
end;

procedure TMainForm.ProcessArgument(const Arg: string);
begin
  ListBox1.Items.Add(Arg);
end;

procedure TMainForm.WMCopyData(var Message: TWMCopyData);
var
  Arg: string;
begin
  SetString(Arg, PChar(Message.CopyDataStruct.lpData), (Message.CopyDataStruct.cbData div SizeOf(Char))-1);
  ProcessArgument(Arg);
  Application.Restore;
  Application.BringToFront;
end;

end.
Run Code Online (Sandbox Code Playgroud)

  • @Craig 恶意程序可以向该窗口发送 WM_COPYDATA 并使其读取到“lpData”的末尾。这就是 `cbData` 参数的全部意义。您发现的 Delphi Dabbler 文章使用了与我相同的解决方案,但未能检查 `cbData`。你想让我展示如何正确地做到这一点吗? (2认同)
  • 好的,答案更新以防止`WM_COPYDATA` 处理程序中的缓冲区溢出。 (2认同)