delphi 强制应用程序窗口从后台到前台(通过来自其他进程的 msg)  
官方Delphi 学习QQ群: 682628230(三千人)
频道

delphi 强制应用程序窗口从后台到前台(通过来自其他进程的 msg)


创建一个新的 VCL 应用程序。将表单重命名为 MainForm,在其上放置一个 TListBox,将其与客户端对齐,将其重命名为 ListBox,然后为表单的 OnCreate 和 OnDestroy 创建空事件。


然后将这个 PASCAL 源代码复制/粘贴到主表单的 PAS 文件中,紧接在“interface”之后,覆盖已经存在的代码:


uses

  Winapi.Windows, Winapi.Messages,

  System.SysUtils, System.Variants, System.Classes,

  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;


CONST

  WM_PEEK       = WM_USER+1234;


type

  TMainForm = class(TForm)

    ListBox: TListBox;

    procedure FormDestroy(Sender: TObject);

    procedure FormCreate(Sender: TObject);

  private

    { Private declarations }

    Running     : HWND;

    PROCEDURE   PEEK(VAR MSG : TMessage); MESSAGE WM_PEEK;

    PROCEDURE   CopyData(VAR MSG : TMessage); MESSAGE WM_COPYDATA;

    PROCEDURE   BringForward(Sender : TObject);

    PROCEDURE   SendString(H : HWND ; CONST S : STRING ; E : TEncoding);

    FUNCTION    CommandLine : STRING;

    FUNCTION    MakeAtomName(H : HWND) : STRING;

    FUNCTION    FindGlobalAtom(CONST S : STRING) : ATOM;

    FUNCTION    AddGlobalAtom(CONST S : STRING) : ATOM;

    FUNCTION    GetGlobalAtomName(H : ATOM) : STRING;

    FUNCTION    AtomNameToHandle(CONST S : STRING) : HWND;

    FUNCTION    DeleteGlobalAtom(A : ATOM) : DWORD;

  public

    { Public declarations }

    PROCEDURE   LOG(CONST S : STRING);

  end;


var

  MainForm: TMainForm;


implementation


USES System.Character;


{$R *.dfm}


PROCEDURE TMainForm.FormDestroy(Sender : TObject);

  VAR

    S   : STRING;

    A   : ATOM;


  BEGIN

    S:=MakeAtomName(0);

    REPEAT

      A:=FindGlobalAtom(S);

      IF A=0 THEN BREAK;

    UNTIL DeleteGlobalAtom(A)<>ERROR_SUCCESS

  END;


FUNCTION TMainForm.AddGlobalAtom(CONST S : STRING) : ATOM;

  BEGIN

    Result:=WinAPI.Windows.GlobalAddAtom(PChar(S))

  END;


FUNCTION TMainForm.MakeAtomName(H : HWND) : STRING;

  CONST

    L   = 8*SizeOf(POINTER); // 32 or 64 (number of bits in a handle)


  VAR

    S   : STRING;

    I   : Cardinal;

    C   : CHAR;


  BEGIN

    Result:=ChangeFileExt(ExtractFileName(ParamStr(0)),''); S:='';

    FOR C IN Result DO IF CharInSet(C,['A'..'Z','a'..'z']) THEN S:=S+C;

    WHILE LENGTH(S)<L DO S:=S+S;

    SetLength(S,L);

    Result:='';

    FOR I:=1 TO L DO BEGIN

      IF H AND $01<>0 THEN C:=S[I].ToUpper ELSE C:=S[I].ToLower;

      Result:=C+Result; H:=H SHR 1

    END

  END;


FUNCTION TMainForm.AtomNameToHandle(CONST S : STRING) : HWND;

  VAR

    C   : CHAR;


  BEGIN

    Result:=0;

    FOR C IN S DO BEGIN

      Result:=Result SHL 1;

      IF CharInSet(C,['A'..'Z']) THEN Result:=Result OR 1

    END

  END;


PROCEDURE TMainForm.BringForward(Sender : TObject);

  BEGIN

    SetForegroundWindow(Running);

    SendString(Running,CommandLine,TEncoding.UTF8);

    ExitProcess(0)

  END;


FUNCTION TMainForm.CommandLine : STRING;

  BEGIN

    Result:=GetCommandLine

  END;


PROCEDURE TMainForm.CopyData(VAR MSG : TMessage);

  VAR

    CDS : PCopyDataStruct;

    S   : STRING;

    B   : TBytes;


  BEGIN

    CDS:=PCopyDataStruct(MSG.LParam);

    SetLength(B,CDS.cbData);

    MOVE(CDS.lpData^,POINTER(B)^,LENGTH(B));

    S:=TEncoding.UTF8.GetString(B);

    LOG('Child['+IntToHex(MSG.WParam)+']: '+S)

  END;


FUNCTION TMainForm.DeleteGlobalAtom(A : ATOM) : DWORD;

  BEGIN

    SetLastError(ERROR_SUCCESS);

    WinAPI.Windows.GlobalDeleteAtom(A);

    Result:=GetLastError

  END;


FUNCTION TMainForm.FindGlobalAtom(CONST S : STRING) : ATOM;

  BEGIN

    Result:=WinAPI.Windows.GlobalFindAtom(PChar(S))

  END;


PROCEDURE TMainForm.FormCreate(Sender : TObject);

  VAR

    A   : ATOM;

    H   : HWND;

    S,T : STRING;


  BEGIN

    S:=MakeAtomName(Handle);

    REPEAT

      A:=FindGlobalAtom(S);

      IF A=0 THEN BREAK;

      T:=GetGlobalAtomName(A); H:=AtomNameToHandle(T);

      IF H<>Handle THEN

        IF SendMessage(H,WM_PEEK,NativeInt(A),NativeInt(H))=NativeInt(A)+NativeInt(H) THEN BREAK

    UNTIL DeleteGlobalAtom(A)<>ERROR_SUCCESS;

    IF A=0 THEN BEGIN

      A:=AddGlobalAtom(S);

      LOG('Main['+IntToHex(Handle)+'] : '+CommandLine)

    END ELSE BEGIN

      Running:=H; OnDestroy:=NIL; OnActivate:=BringForward;

      BorderStyle:=TFormBorderStyle.bsNone;

      SetBounds(-10000,-10000,10,10)

    END

  END;


FUNCTION TMainForm.GetGlobalAtomName(H : ATOM) : STRING;

  BEGIN

    SetLength(Result,255);

    SetLength(Result,WinAPI.Windows.GlobalGetAtomName(H,@Result[LOW(Result)],LENGTH(Result)))

  END;


PROCEDURE TMainForm.LOG(CONST S : STRING);

  BEGIN

    ListBox.ItemIndex:=ListBox.Items.Add(S)

  END;


PROCEDURE TMainForm.PEEK(VAR MSG : TMessage);

  BEGIN

    MSG.Result:=NativeInt(MSG.WParam)+MSG.LParam

  END;


PROCEDURE TMainForm.SendString(H : HWND ; CONST S : STRING ; E : TEncoding);

  VAR

    B   : TBytes;

    CDS : TCopyDataStruct;


  BEGIN

    B:=E.GetBytes(S);

    CDS.dwData:=1;

    CDS.cbData:=LENGTH(B);

    CDS.lpData:=POINTER(B);

    SendMessage(H,WM_COPYDATA,Handle,NativeInt(@CDS));

  END;


end.

当您最初运行该应用程序时,它会在 ListBox 中显示命令行。如果您再次运行它,它会检测到另一个窗口已经存在(使用位编码的全局原子来表示初始实例的主窗体句柄)并将其移动到前台(在将自己的窗口放置在屏幕外之后) ,因此是一个不可见的前景窗口)。然后它将使用 WM_COPYDATA 将新实例的命令行发送到初始实例,然后初始实例将接收到的命令行记录到列表框。


注意事项:


它是被带到前面、接收和处理命令行的主窗体。如果您打开了子表单,则行为未定义(如:我没有测试过)。

Atom 名称是一个 32(或 64)个字符长的名称,由程序可执行文件的 AZ 字符的重复模式组成。如果您的应用程序名称中没有 AZ 字符,这将失败。

为了测试从全局原子解码的窗口是否是我们识别的窗口,我在该窗口上调用 WM_PEEK 消息。如果您的主实例被允许启动(并创建 Atom)然后没有正确终止(因此 Atom 在 FormDestroy 中被删除),这可能会导致对外部应用程序的意外消息调用。



推荐分享
图文皆来源于网络,内容仅做公益性分享,版权归原作者所有,如有侵权请告知删除!
 

Copyright © 2014 DelphiW.com 开发 源码 文档 技巧 All Rights Reserved
晋ICP备14006235号-8 晋公网安备 14108102000087号

执行时间: 0.26610803604126 seconds