2012-12-10 6 views
12

私のGUIアプリケーションでは、私はコンソールアプリケーションを実行し、そのウィンドウのハンドルが必要です。 EnumWindows()で試してみましたが、以下のコードを見てください。しかし動作しません。リストには私のコンソールアプリはありません。GUIアプリケーションから起動したコンソールウィンドウのハンドルを取得するには?

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; 
+0

@Tlama - ありがとう、素晴らしいです! 'CreateProcess'の後に' Sleep(50) 'と一緒に私のために働いています。 – Branko

+0

Branko、それは私が好きではないです。私はより信頼できるものを見つけようとし、ここに結果を投稿します。 – TLama

+0

@traama - なぜ私は、良いソリューション( 'AttachConsole(PID)、GetConsoleWindow、FreeConsole')を削除したのか尋ねたがっています。なぜあなたは 'Sleep()'を数ミリ秒間心配していますか? – Branko

答えて

10

次のコードは、単にプロセス(コンソールアプリケーション)を作成し、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; 
+0

ありがとうございます。申し訳ありませんが、私は2つの答えを受け入れることができません。また、私の質問をより良く、より明確に定式化してくれてありがとう。これは悪い英語の私たちの問題です。 – Branko

+0

あなたの答えがであることを受け入れる必要があります。私のコードがなぜ機能しなかったのかを間接的に説明したからです。私は 'CreateProcess'の直後にウィンドウを列挙しましたが、Bummiは後でボタンをクリックした後、彼のコードは常に動作します。 'CreateProcess'の後に' Sleep(50) 'を入れても、私のコードはうまく動作します:) – Branko

+1

' ProcessInfo.hProcess'と 'ProcessInfo.hThread'をおそらく閉じますか? –

5

-Creatingコンソールプロセス

-findプロセス

たコンソールウィンドウの

-setキャプション

の窓:あなたlauchedアプリケーションのコンソールウィンドウのタイトルをこのように変更

-write to found console

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. 
+0

ありがとうございます、あなたのコードは正常に動作します!私はコードに何が間違っているのかを見なければなりません:) – Branko

+2

何をしているのか、なぜそれが書かれているのかについて、より説明的なテキストがあれば、これを投票します。 –

+1

@TLama改善に感謝します。 Marjan Venema後で情報を追加します。私は今、急いで申し訳ありません... – bummi

関連する問題