2012-05-09 67 views
1

我想訪問另一個進程控制檯(通過AttachConsole)的緩衝區,用於調用ReadConsoleOutput等。如何從另一個進程訪問控制檯緩衝區? AttachConsole ERROR_INVALID_PARAMETER

是一個DOS 16位應用程序。我不能使用管道,因爲它不能很好地寫輸出(它模擬「窗口」,如果你知道我的意思,就像FAR指揮官)。

所以我應該:

1)啓動應用程序 2)得到的進程ID 3)調用AttachConsole(PROCID) 4)調用GetConsoleScreenBufferInfo得到大小 5)調用ReadConsoleOutput

問題出在3:當我調用AttachConsole時,ir返回0,並且在調用GetLastError之後,它報告ERROR_INVALID_PARAMETER 87(0x57)。

AttachConsole的唯一參數是ProcessId,我用ProcessExplorer檢查它是否正確(它實際上是模擬應用程序的ntvdm.exe的PID)。

Delphi代碼:

function AttachConsole(dwProcessId: DWORD): Cardinal; external kernel32 name 'AttachConsole'; 

var 
    Handle: HWND; 

function EnumWindowsProc(hwnd: HWND; lParam: LPARAM): BOOL; stdcall; 
var 
    s: string; 
    IsVisible, IsOwned, IsAppWindow: Boolean; 
begin 
    Result := True;//carry on enumerating 

    IsVisible := IsWindowVisible(hwnd); 
    if not IsVisible then 
    exit; 

    IsOwned := GetWindow(hwnd, GW_OWNER)<>0; 
    if IsOwned then 
    exit; 

    IsAppWindow := GetWindowLongPtr(hwnd, GWL_STYLE) and WS_EX_APPWINDOW<>0; 
    if not IsAppWindow then 
    exit; 

    SetLength(s, GetWindowTextLength(hwnd)); 
    GetWindowText(hwnd, PChar(s), Length(s)+1); 
    if AnsiContainsText(s, '????.EXE') then // set windows name to search 
    Handle := hwnd; 
end; 

procedure Test(Strings: TStrings); 
var 
    ProcessID: Cardinal; 
begin 
    Handle := 0; 
    EnumWindows(@EnumWindowsProc, 0); 
    Strings.Add('Handle: ' + IntToStr(Handle)); 
    if Handle <> 0 then 
    SetForegroundWindow(Handle); 
    Sleep(100); 

    GetWindowThreadProcessId(Handle, @ProcessID); 
    Strings.Add('ProcessId: ' + IntToStr(ProcessID)); 

    if AttachConsole(ProcessId) <> 0 then 
    Strings.Add('Ok Attached') 
    else 
    Strings.Add('Error: ' + IntToStr(GetLastError)); 
end; 

刪除備忘錄和按鈕形式。在OnClick調用Test(Memo1.Lines)。

=====編輯完整的解決方案=====

function AttachAndGetConsoleHandle(ProcessId: Cardinal): Cardinal; 
begin 
    if not AttachConsole(ProcessId) then 
    raise Exception.Create('AttachConsole error: ' + IntToStr(GetLastError)); 

    Result := GetStdHandle(STD_OUTPUT_HANDLE); 

    if Result = INVALID_HANDLE_VALUE then 
    raise Exception.Create('GetStdHandle(STD_OUTPUT_HANDLE) error: ' + IntToStr(GetLastError)); 
end; 

procedure DettachConsole; 
begin 
    if not FreeConsole then 
    raise Exception.Create('FreeConsole error: ' + IntToStr(GetLastError)); 
end; 

function ReadConsole(ConsoleHandle: Cardinal): TStringList; 
var 
    BufferInfo: _CONSOLE_SCREEN_BUFFER_INFO; 
    BufferSize, BufferCoord: _COORD; 
    ReadRegion: _SMALL_RECT; 
    Buffer: Array of _CHAR_INFO; 
    I, J: Integer; 
    Line: AnsiString; 
begin 
    Result := TStringList.Create; 

    ZeroMemory(@BufferInfo, SizeOf(BufferInfo)); 
    if not GetConsoleScreenBufferInfo(ConsoleHandle, BufferInfo) then 
    raise Exception.Create('GetConsoleScreenBufferInfo error: ' + IntToStr(GetLastError)); 

    SetLength(Buffer, BufferInfo.dwSize.X * BufferInfo.dwSize.Y); 

    BufferSize.X := BufferInfo.dwSize.X; 
    BufferSize.Y := BufferInfo.dwSize.Y; 
    BufferCoord.X := 0; 
    BufferCoord.Y := 0; 
    ReadRegion.Left := 0; 
    ReadRegion.Top := 0; 
    ReadRegion.Right := BufferInfo.dwSize.X; 
    ReadRegion.Bottom := BufferInfo.dwSize.Y; 

    if ReadConsoleOutput(ConsoleHandle, Pointer(Buffer), BufferSize, BufferCoord, ReadRegion) then 
    begin 
    for I := 0 to BufferInfo.dwSize.Y - 1 do 
    begin 
     Line := ''; 
     for J := 0 to BufferInfo.dwSize.X - 1 do 
     Line := Line + Buffer[I * BufferInfo.dwSize.X + J].AsciiChar; 
     Result.Add(Line) 
    end 
    end 
    else 
    raise Exception.Create('ReadConsoleOutput error: ' + IntToStr(GetLastError)); 
end; 
+1

嘗試將'stdcall'調用約定添加到AttachConsole函數中。 – RRUZ

回答

1

的定義應該是:

function AttachConsole(dwProcessId: DWORD): BOOL; stdcall; external 
kernel32 name 'AttachConsole'; 

所以下面的代碼應該是:

if AttachConsole(ProcessId) then 

不能再幫你了。

相關問題