5

Delphi からアプリケーションを起動し、そのハンドルを取得したいので、そのアプリケーションのメイン ウィンドウを TFrame タイプのフレームに埋め込むことができます。これまでのところ、私は試しました:

Function TFrmEmbeddedExe.StartNewApplication : Boolean;
var
  SEInfo: TShellExecuteInfo;
  ExitCode : DWORD;
begin

  FillChar(SEInfo, SizeOf(SEInfo), 0) ;
  SEInfo.cbSize := SizeOf(TShellExecuteInfo) ;
  with SEInfo do
  begin
    fMask := SEE_MASK_NOCLOSEPROCESS;
    Wnd := self.Handle;
    lpFile := PChar(self.fexecuteFileName) ;//  Example could be 'C:\Windows\Notepad.exe'
    nShow := SW_SHOWNORMAL;//SW_HIDE;
  end;

  if ShellExecuteEx(@SEInfo) then
  begin
    sleep(1500);
    self.fAppWnd := FindWindow(nil, PChar(self.fWindowCaption)); //Example : 'Untitled - Notepad'
    if self.fAppWnd <> 0 then
    begin
      Windows.SetParent(self.fAppWnd, SEInfo.Wnd);
      ShowWindow(self.fAppWnd, SW_SHOWMAXIMIZED);
      result := true;
    end
    else
      result := false;

  end

  else
    result := false;
end ;

上記のコードは実際に動作findWindowしますが、私が開始したアプリケーションの任意のインスタンスを見つけます。シェルで実行した正確なインスタンスを埋め込みたい。そのため、メモ帳を数回起動した場合、FindWindow を使用して正しいメモ帳を取得する方法はありません。

私が試してみました:

Function TfrmEmbeddedExe.CreateProcessNewApplication : Boolean;
var
zAppName: array[0..512] of char;
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
Res : DWORD;
DoWait : Boolean;
begin
  DoWait := False;
  StrPCopy(zAppName, self.fexecuteFileName);  //'C:\Windows\Notepad.exe'
  FillChar(StartupInfo, Sizeof(StartupInfo), #0);
  StartupInfo.cb := Sizeof(StartupInfo);
  StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
  StartupInfo.wShowWindow := SW_SHOWNORMAL;

  if CreateProcess (zAppName,
  nil, { pointer to command line string }
  nil, { pointer to process security attributes }
  nil, { pointer to thread security attributes }
  false, { handle inheritance flag }
  CREATE_NEW_CONSOLE or { creation flags }
  NORMAL_PRIORITY_CLASS,
  nil, { pointer to new environment block }
  nil, { pointer to current directory name }
  StartupInfo, { pointer to STARTUPINFO }
  ProcessInfo) then   { pointer to PROCESS_INF }
  begin
    if DoWait then  //just set it to false... so it will never enter here
    begin
      WaitforSingleObject(ProcessInfo.hProcess, INFINITE);
      GetExitCodeProcess(ProcessInfo.hProcess, Res);
    end
    else
    begin
      self.fAppWnd := ProcessInfo.hProcess;

      Windows.SetParent(self.fAppWnd, self.Handle);
      ShowWindow(self.fAppWnd, SW_SHOWMAXIMIZED);
      CloseHandle(ProcessInfo.hProcess);
      CloseHandle(ProcessInfo.hThread);


    end;

    result := true;
  end
  else begin
    Result := false;
  end;
end;

上記のコードを実行しないでください。実行中のすべてのアプリケーションの任意の場所にある一見ランダムなウィンドウを選択し、それを埋め込むという奇妙な結果が生成されます (Windows のスタート メニューのメニュー項目でさえ..)

したがって、基本的に必要なのは、アプリケーションを起動し、アプリケーションのメイン ウィンドウへのハンドルを取得する方法です。

4

2 に答える 2

10

これがあなたがする必要があることの大まかな概要です。コーディングはお任せします:

  1. ShellExecuteExまたは のいずれかでプロセスを開始しますCreateProcess。これにより、プロセス ハンドルが生成されます。
  2. WaitForInputIdleプロセス ハンドルを呼び出します。これにより、プロセスがメッセージ ループを読み込んで開始する機会が与えられます。
  3. プロセス ハンドルを に渡してGetProcessId、プロセス ID を取得します。
  4. EnumWindows最上位のウィンドウを列挙するために使用します。
  5. これらの各ウィンドウをGetWindowThreadProcessIdに渡して、ターゲット プロセスの最上位ウィンドウが見つかったかどうかを確認します。
  6. プロセス ID がターゲット プロセスと一致するウィンドウを見つけたら、完了です。

処理が完了したら、プロセス ハンドルを閉じることを忘れないでください。

于 2012-09-05T14:00:02.867 に答える
0

このコードは私のために働きます:

次の「Utils」ユニットを作成します >>

....

interface

.....

function RunProg(PName, CmdLine: String; out ProcessHdl: HWND): HWND;


implementation


type
  TEnumData = record      // Record Type for Enumeration
    WHdl: HWND;
    WPid: DWORD;
    WTitle: String;
  end;
  PEnumData = ^TEnumData;  // Pointer to Record Type

// Enumeration Function for GetWinHandleFromProcId (below)
function EnumWindowsProcMatchPID(WHdl: HWND; EData: PEnumData): bool; stdcall;
var
  Wpid : DWORD;
begin
  Result := True; // continue enumeration
  GetWindowThreadProcessID(WHdl, @Wpid);
  // Filter for only visible windows, because the Pid is not unique to the Main Form
  if (EData.WPid = Wpid) AND IsWindowVisible(WHdl) then
  begin
    EData.WHdl := WHdl;
    Result := False; // stop enumeration
  end;
end;

// Find Window from Process Id and return the Window Handle
function GetWinHandleFromProcId(ProcId: DWORD): HWND;
var
  EnumData: TEnumData;
begin
  ZeroMemory(@EnumData, SizeOf(EnumData));
  EnumData.WPid := ProcId;
  EnumWindows(@EnumWindowsProcMatchPID, LPARAM(@EnumData));
  Result := EnumData.WHdl;
end;

    
// Run Program using CreateProcess >> Return Window Handle and Process Handle
function RunProg(PName, CmdLine: String; out ProcessHdl: HWND): HWND;
var
  StartInfo: TStartupInfo;
  ProcInfo: TProcessInformation;
  ProcessId : DWORD;
  WinHdl : HWND;
  bOK : boolean;
  ix : integer;
begin
  FillChar(StartInfo, SizeOf(StartInfo), 0);
  StartInfo.cb := SizeOf(StartInfo);
  StartInfo.dwFlags := STARTF_USESHOWWINDOW;
  StartInfo.wShowWindow := SW_Show;
  bOK := CreateProcess(PChar(PName), PChar(CmdLine), nil, nil, False, 0, nil, nil, StartInfo, ProcInfo);
  ProcessHdl := ProcInfo.hProcess;
  ProcessId := ProcInfo.dwProcessId;

  // Note : "WaitForInputIdle" does not always wait long enough, ... 
  //          so we combine it with a repeat - until - loop >>
  WinHdl := 0;
  if bOK then  // Process is running
  begin
    WaitForInputIdle(ProcessHdl,INFINITE);
    ix := 0;
    repeat     // Will wait (up to 10+ seconds) for a program that takes very long to show it's main window
      WinHdl := GetWinHandleFromProcId(ProcessId);
      Sleep(25);
      inc(ix);
    until (WinHdl > 0) OR (ix > 400);  // Got Handle OR Timeout
  end;

  Result := WinHdl;
  CloseHandle(ProcInfo.hThread);
end;

これを「Utils」を使用するメインプログラムに入れます-ユニット>>

var
  SlaveWinHdl : HWND;  // Slave Program Window Handle
  SlaveProcHdl : HWND;  // Slave Program Process Handle

// Button to run Notepad - Returning Window Handle and Process Handle
procedure TForm1.Button1Click(Sender: TObject);
var
  Pname, Pcmnd: string;
begin
  Pname := 'C:\WINDOWS\system32\notepad.exe';
  Pcmnd := '';
  SlaveWinHdl := RunProg(Pname, Pcmnd, SlaveProcHdl);
end;

// Button to Close program using Window Handle
procedure TForm1.Button2Click(Sender: TObject);
begin
  PostMessage(SlaveWinHdl, WM_CLOSE, 0, 0);
end;

// Button to Close program using Process Handle
procedure TForm1.Button3Click(Sender: TObject);
begin
  TerminateProcess(SlaveProcHdl, STILL_ACTIVE);
  CloseHandle(SlaveProcHdl);
end;

これで、外部プログラムを実行し、ウィンドウ ハンドルまたはプロセス ハンドルを使用してそれを閉じる方法の完全なソリューションが得られました。

おまけ:既に実行中のプログラムのハンドルを見つけなければならない場合があります。次のコード (「Utils」ユニットに追加) を使用して Window- Title に基づいて見つけることができます >>

function EnumWindowsProcMatchTitle(WHdl: HWND;  EData: PEnumData): bool; stdcall; 
var
  WinTitle: array[0..255] of char;
  Wpid : DWORD;
begin
  Result := True; // continue enumeration
  GetWindowText(WHdl, WinTitle, 256);
  if (Pos(EData.WTitle, StrPas(WinTitle)) <> 0) then // Will also match partial title
  begin
    EData.WHdl := WHdl;
    GetWindowThreadProcessID(WHdl, @Wpid);
    EData.WPid := Wpid;
    Result := False; // stop enumeration
  end;
end;

function GetHandlesFromWinTitle(WinTitle: String; out ProcHdl : HWND): HWND;  
var
  EnumData: TEnumData;
begin
  ZeroMemory(@EnumData, SizeOf(EnumData));
  EnumData.WTitle := WinTitle;
  EnumWindows(@EnumWindowsProcMatchTitle, LPARAM(@EnumData));
  ProcHdl := OpenProcess(PROCESS_ALL_ACCESS,False,EnumData.WPid);
  Result := EnumData.WHdl;
end;

そして、このように(メインプログラムから)呼び出します>>

strWT := ‘MyList.txt – Notepad’;  // example of Notepad Title
SlaveWinHdl  :=  GetHandlesFromWinTitle(strWT, SlaveProcHdl);
于 2021-11-17T01:30:29.230 に答える