如何从另一个进程获取访问控制台缓冲区? AttachConsole ERROR_INVALID_PARAMETER

时间:2012-05-09 05:39:05

标签: windows delphi winapi console

我希望能够访问另一个进程控制台的缓冲区(通过AttachConsole),以便调用ReadConsoleOutput等。

是DOS 16bit应用程序。我不能使用管道,因为它不会有意识地写入输出(它模仿“窗口”......如果你知道我的意思,就像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)。

德尔福代码:

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 个答案:

答案 0 :(得分:1)

定义应该是:

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

所以它后面的代码应该是:

if AttachConsole(ProcessId) then

不能帮助你了。

相关问题