Note that there are some explanatory texts on larger screens.

plurals
  1. POhow to get access console buffer from another process? AttachConsole ERROR_INVALID_PARAMETER
    text
    copied!<p>I want to get access to the buffer of another process console (via AttachConsole), for calling ReadConsoleOutput, etc.</p> <p>Is a DOS 16bit application. I can't use pipes because it doesn't writes output secuentially (it emulates "windows".. like FAR commander if you know what I mean).</p> <p>So I should:</p> <p>1) launch the app 2) get the process id 3) call AttachConsole(ProcId) 4) call GetConsoleScreenBufferInfo to get the size 5) call ReadConsoleOutput</p> <p>The problem is at 3: when I call AttachConsole ir returns 0, and after a call to GetLastError it reports ERROR_INVALID_PARAMETER 87 (0x57).</p> <p>The only parameter of AttachConsole is the ProcessId and I've checked it with ProcessExplorer that is right (it's actually the PID of ntvdm.exe that emulates the app).</p> <p>Delphi code:</p> <pre><code>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)&lt;&gt;0; if IsOwned then exit; IsAppWindow := GetWindowLongPtr(hwnd, GWL_STYLE) and WS_EX_APPWINDOW&lt;&gt;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 &lt;&gt; 0 then SetForegroundWindow(Handle); Sleep(100); GetWindowThreadProcessId(Handle, @ProcessID); Strings.Add('ProcessId: ' + IntToStr(ProcessID)); if AttachConsole(ProcessId) &lt;&gt; 0 then Strings.Add('Ok Attached') else Strings.Add('Error: ' + IntToStr(GetLastError)); end; </code></pre> <p>Drop memo and button in form. At OnClick call Test(Memo1.Lines).</p> <p>===== EDIT complete solution =====</p> <pre><code>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; </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload