프로그래밍/델파이

[Pascal] 외부 프로그램을 실행하고, 실행한 프로램의 메인 윈도우 핸들 얻기

채윤아빠 2021. 12. 6. 23:26
728x90
반응형

개요

외부 프로그램을 실행하고, 실행한 프로그램의 메인 윈도우 핸들을 얻는 방법을 설명합니다.

구현 방법

외부 프로그램을 실행하기 위하여 CreateProcess() 함수를 이용합니다. 외부 프로그램이 정상적으로 실행된다면 해당 프로그램의 Process ID를 얻을 수 있고, Process ID를 이용하여 메인 윈도우 핸들을 구할 수 있습니다.

먼저 CreateProcess() 함수를 이용하여 다음과 같이 메모장을 실행할 수 있습니다.

uses Windows;

procedure TForm1.btnExecuteNotepadClick(Sender: TObject);
var
  //  StdInPipeR, StdInPipeW : THandle;
  //  StdOutPipeR, StdOutPipeW : THandle;
  dwPID, hwndNotepad: Cardinal;
  aStartInfo: TStartUpInfo;
  aProcessInfo: TProcessInformation;
  strExternalApp: string;
begin
  strExternalApp := 'C:\Windows\Notepad.exe';
  ZeroMemory(@aProcessInfo, SizeOf(aProcessInfo));
  ZeroMemory(@aStartInfo, SizeOf(aStartInfo));
  aStartInfo.cb := SizeOf(aStartInfo);
  //  StartInfo.hStdOutput := StdOutPipeW;
  //  StartInfo.hStdInput := StdInPipeR;
  aStartInfo.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
  //  StartInfo.wShowWindow := SW_HIDE;
  aStartInfo.wShowWindow := SW_SHOWNORMAL;

  if CreateProcess(nil,
    PChar(strExternalApp),
    nil,
    nil,
    True,
    NORMAL_PRIORITY_CLASS,
    nil,
    nil,
    aStartInfo,
    aProcessInfo) then
  begin
    try
      dwPID := aProcessInfo.dwProcessId;
      MessageBox(Handle, PChar(Format('External App PID = %d', [dwPID])), 'confirm', MB_OK);
      hwndNotepad := GetHwndFromProcessID(dwPID);
      if (hwndNotepad > 0) then
        MessageBox(Handle, PChar(Format('find windows handle = %d', [hwndNotepad])), 'confirm2', MB_OK);
    finally
      CloseHandle(aProcessInfo.hThread);
      CloseHandle(aProcessInfo.hProcess);
    end;
  end;
end;

실행된 외부 프로그램의 Process ID를 구하였지만, 외부 프로그램에 대한 메인 윈도우 핸들을 직접 구할 수 없습니다. 외부 프로그램에 대한 메인 윈도우 핸들을 얻기 위하여 현재 최상위 데스크톱에서 실행된 프로그램들의 Process ID를 비교하여 동일한 Process ID를 갖는 윈도우를 찾는 방법을 사용하였습니다. 이를 위하여 FindWindow() 및 GetWindow()를 이용하였습니다.

다음 그림을 보시면, MS Spy++의 일부를 갈무리한 것으로 "Desktop" 윈도우에서 실행된 프로그램들이 관리되고 있는 것을 보실 수 있습니다.

데스크톱 윈도우에 대하여 GetWindow() 함수를 이용하여 순차적으로 실행된 프로그램들의 윈도우 핸들을 가져오고, 윈도우 핸들로부터 Process ID를 구하여 앞서 CreateProcess() 함수로 실행된 외부 프로그램의 Proocess ID와 같은 경우, 실행된 외부 프로그램의 메인 윈도우 핸들로 반환하게 됩니다.

다음 GetHwndFromProcessID() 함수를 보시면 어떻게 사용하였는지 확인하실 수 있습니다.

function GetHwndFromProcessID(dwTargetProcessID: DWORD): THandle;
var
  hWnd, hwndParent: THandle;
  dwProcessID, dwParentProcessID: DWORD;
begin
  Result := 0;

  // 최상위 윈도우 핸들 찾기
  hWnd := FindWindow(nil, nil);
  while (hWnd <> 0) do
  begin
    GetWindowThreadProcessId(hWnd, @dwProcessID);
//    MessageBox(0, PChar(Format('hWnd = %d, dwProcessID = %d', [hWnd, dwProcessID])), 'debug1', MB_OK);
    if (dwProcessID = dwTargetProcessID) then
    begin // 동일 프로세스에 대한 윈도우 핸들을 찾음
      hwndParent := GetParent(hWnd);
      GetWindowThreadProcessId(hwndParent, @dwParentProcessID);
//      MessageBox(0, PChar(Format('hwndParent = %d, dwParentProcessID = %d', [hwndParent, dwParentProcessID])), 'debug2', MB_OK);
      if (dwParentProcessID <> dwProcessID) then
      begin // 메인 윈도우 핸들인지 체크, 버튼 등도 핸들을 가질 수 있으므로 무시하기 위해
        Result := hWnd;
        break;
      end;
    end;
    hWnd := GetWindow(hWnd, GW_HWNDNEXT); // 다음 윈도우 핸들 찾기
  end;
end;

결론

위와 같이 GetHwndFromProcessID() 함수를 이용하면 외부 프로그램을 실행 후, 해당 프로그램의 메인 윈도우 핸들을 얻을 수 있고, 구해 놓은 외부 프로그램의 메인 윈도우 핸들을 이용하여, 자동 제어(특정 컨트롤의 값을 설정 또는 가져오기 및 버튼 선택 동작 모의 등)에 응용할 수도 있습니다.

다음에 기회가 되면 위와 연계하여, 외부 프로그램을 실행하고 원하는 제어를 수행하는 것에 대하여 정리해 보도록 하겠습니다.