1

ReadConsoleOutput などを呼び出すために、(AttachConsole を介して) 別のプロセス コンソールのバッファにアクセスしたいと考えています。

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 です)。

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;
4

1 に答える 1

1

定義は次のとおりです。

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

したがって、それに続くコードは次のようになります。

if AttachConsole(ProcessId) then

これ以上、あなたを助けることはできません。

于 2012-05-09T06:55:20.877 に答える