Как получить дескриптор окна консоли, запущенного из моего приложения GUI?
В моем приложении GUI я запускаю консольное приложение и нуждаюсь в дескрипторе его окна. Я попытался с EnumWindows(), см. Код ниже, но он не работает. В списке нет моего консольного приложения.
type
TEnumWindowsData = record
ProcessId: Cardinal;
WinHandle: THandle;
List: TStrings; // For test only
end;
PEnumWindowsData = ^TEnumWindowsData;
function FindWindow(hWnd: THandle; lParam: LPARAM): BOOL; stdcall;
var
ParamData: TEnumWindowsData;
ProcessId: Cardinal;
WinTitle: array[0..200] of Char; // For test only
begin
ParamData := PEnumWindowsData(lParam)^;
GetWindowThreadProcessId(hWnd, ProcessId);
if ProcessId <> ParamData.ProcessId then
Result := True
else begin
ParamData.WinHandle := hWnd;
Result := False;
end;
// For test only
GetWindowText(hWnd, WinTitle, Length(WinTitle) - 1);
ParamData.List.Add(IntToStr(ProcessId) + ' ' + IntToStr(hWnd) + ' ' + WinTitle);
end;
procedure TForm1.Button1Click(Sender: TObject);
function RunApp(const AProgram: string): Cardinal;
var
StartupInfo: TStartupInfo;
ProcessInformation: TProcessInformation;
begin
Result := 0;
...
if CreateProcess(nil, PChar(AProgram), nil, nil, False,
NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInformation)
then
Result := ProcessInformation.dwProcessId;
...
end;
var
ParamData: TEnumWindowsData;
begin
ParamData.ProcessId := RunApp('cmd.exe /C D:\TMP\TEST.exe');
ParamData.WinHandle := 0;
ParamData.List := Memo1.Lines;
EnumWindows(@FindWindow, THandle(@ParamData));
FWindowHandle := ParamData.WinHandle;
end;
Ответы
Ответ 1
Следующий код просто создает процесс (консольное приложение), присоединяет ваш процесс к вновь созданной консоли с помощью AttachConsole
и из эта прикрепленная консоль принимает дескриптор окна с помощью функции GetConsoleWindow
.
Самая большая слабость следующего кода заключается в том, что функция CreateProcess
возвращает сразу в то время, когда консоль еще не полностью инициализирована и когда вы попытаетесь сразу подключить консоль, вы потерпите неудачу. К сожалению, нет WaitForInputIdle
функция for console applications
, чтобы было возможно одно обходное решение Я бы выбрал попытку подключить консоль в каком-то ограниченном количестве циклов и как только это получится, возьмите ручку и отсоедините консоль.
В коде, который может выглядеть следующим образом. Функция RunApp
должна возвращать дескриптор окна консоли (при условии, что вы будете запускать из него только консольные приложения) и должен ждать ок. 1 секунда для консольного приложения, которое вы начали прикреплять. Вы можете изменить это значение, изменив начальное значение переменной Attempt
и/или изменив интервал Sleep
.
function GetConsoleWindow: HWND; stdcall;
external kernel32 name 'GetConsoleWindow';
function AttachConsole(dwProcessId: DWORD): BOOL; stdcall;
external kernel32 name 'AttachConsole';
function RunApp(const ACmdLine: string): HWND;
var
CmdLine: string;
Attempt: Integer;
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
begin
Result := 0;
FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
FillChar(ProcessInfo, SizeOf(TProcessInformation), 0);
StartupInfo.cb := SizeOf(TStartupInfo);
StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow := SW_SHOWNORMAL;
CmdLine := ACmdLine;
UniqueString(CmdLine);
if CreateProcess(nil, PChar(CmdLine), nil, nil, False,
CREATE_NEW_CONSOLE, nil, nil, StartupInfo, ProcessInfo) then
begin
Attempt := 100;
while (Attempt > 0) do
begin
if AttachConsole(ProcessInfo.dwProcessId) then
begin
Result := GetConsoleWindow;
FreeConsole;
Break;
end;
Sleep(10);
Dec(Attempt);
end;
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
end;
end;
Тогда вы можете, например, измените заголовок окна консоли вашего приложения, описанного ниже:
procedure TForm1.Button1Click(Sender: TObject);
var
S: string;
ConsoleHandle: HWND;
begin
ConsoleHandle := RunApp('cmd.exe');
if ConsoleHandle <> 0 then
begin
S := 'Hello! I''m your console, how can I serve ?';
SendTextMessage(ConsoleHandle, WM_SETTEXT, 0, S);
end;
end;
Ответ 2
-Создание консольного процесса
- загляните в окно процесса
-set подпись найденного окна консоли
-write, чтобы найти консоль
unit Unit3;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
PID: DWORD;
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
AProgram: String;
StartupInfo: TStartupInfoW;
ProcessInfo: TProcessInformation;
begin
AProgram := 'cmd /K Dir C:\temp'; // using /K for keeping console alive
UniqueString(AProgram); // ensure that AProgram is writeable by API
ZeroMemory(@StartupInfo, SizeOf(StartupInfo)); // create minimum startup info
StartupInfo.cb := SizeOf(StartupInfo);
StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow := SW_SHOW;
if CreateProcess(nil, PChar(AProgram), nil, nil, False, // Create Consoleprocess
CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo,
ProcessInfo) then
try
PID := ProcessInfo.dwProcessId; // Store ProcessId to PID
finally
// close not longer required handles
Showmessage(IntToStr(Integer(CloseHandle(ProcessInfo.hProcess))));
Showmessage(IntToStr(Integer(CloseHandle(ProcessInfo.hThread))));
end;
end;
type
PEnumInfo = ^TEnumInfo;
TEnumInfo = record ProcessID: DWORD; HWND: THandle; end;
function EnumWindowsProc(Wnd: DWORD; var EI: TEnumInfo): BOOL; stdcall;
var
PID: DWORD;
begin
GetWindowThreadProcessID(Wnd, @PID); // get processID from WND of Enumeration
// continue EnumWindowsProc if found PID is not our wished, visible and enabled processID (EI.ProcessID)
Result := (PID <> EI.ProcessID) or (not IsWindowVisible(WND)) or
(not IsWindowEnabled(WND));
if not Result then // WND found for EI.ProcessID
EI.HWND := WND;
end;
function FindMainWindow(PID: DWORD): DWORD;
var
EI: TEnumInfo;
begin
//Store our processID and invalid Windowhandle to EI
EI.ProcessID := PID;
EI.HWND := 0;
EnumWindows(@EnumWindowsProc, Integer(@EI));
Result := EI.HWND;
end;
function AttachConsole(dwProcessId: DWORD): BOOL; stdcall;
external kernel32 name 'AttachConsole';
procedure TForm1.Button2Click(Sender: TObject);
var
Wnd: HWND;
S: String;
begin
if PID <> 0 then // do we have a valid ProcessID
begin
Wnd := FindMainWindow(PID);
if Wnd <> 0 then // did we find the window handle
begin
S := 'Test';
// change caption of found window
SendMessage(Wnd, WM_SETTEXT, 0, LPARAM(@S[1]));
if AttachConsole(PID) then // are we able to attach to console of our proecess?
begin
Writeln('Here we are'); // write to attached console
FreeConsole; // free if not longer needed
end;
end;
end;
end;
end.