INTELLIGENT WORK FORUMS
FOR COMPUTER PROFESSIONALS

Log In

Come Join Us!

Are you a
Computer / IT professional?
Join Tek-Tips Forums!
  • Talk With Other Members
  • Be Notified Of Responses
    To Your Posts
  • Keyword Search
  • One-Click Access To Your
    Favorite Forums
  • Automated Signatures
    On Your Posts
  • Best Of All, It's Free!

*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.

Posting Guidelines

Promoting, selling, recruiting, coursework and thesis posting is forbidden.

Jobs

How To

How do I put a called window inside a Panel Control? by Glenn9999
Posted: 23 Apr 15 (Edited 23 Apr 15)

This FAQ will describe how to put a window of a called process into a Panel control. The sample here will be very analogous to FAQ102-7402: Use Pipes (or receive input and output from a console) as it will produce nearly the same result (and I used the same form). But the difference here is that the code for the control posted below will put the actual command prompt into a panel control. This means you can see the actual console window, and still send commands to it. Keep in mind, the code can be adapted to any window or process which will allow this.

CODE

unit comprompt;
// written by Glenn9999 @ tek-tips.com on 04/23/2015
interface
  uses controls, extctrls, classes, windows, messages, graphics, sysutils;

type
  TPColorType = (tctBlack, tctDarkBlue, tctDarkGreen, tctDarkCyan, tctDarkRed,
                tctDarkMagenta, tctDarkYellow, tctLightGray, tctDarkGray,
                tctLightBlue, tctLightGreen, tctLightCyan, tctLightRed,
                tctLightMagenta, tctLightYellow, tctWhite);

  TComPromptPanel = class(TCustomPanel)
  private
    fForeground: TPColorType;
    fBackground: TPColorType;

    InputPipeRead, InputPipeWrite: THandle;
    ProcessInfo : TProcessInformation;

    function GetConsoleWindow(cpid: Dword): HWND;
    procedure PromptSize(var thewidth, theheight: integer);
    procedure RunPrompt(Exec: String);
    procedure RunQuit;
    function GetComSpec: String;
    procedure HideTitlebar(Handle: THandle);
  protected
     procedure Loaded; override;
  public
    procedure WritePipeOut(InString: string);
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Foreground: TPColorType read fForeground write fForeground;
    property Background: TPColorType read fBackground write fBackground;
  end;

procedure Register;

implementation

function TComPromptPanel.GetComSpec: String;
  { return the COMSPEC line in the environment }
  var
    PathName: PChar;
    Buffer: array[0..255] of char;
  begin
    PathName := PChar('COMSPEC');
    GetEnvironmentVariable(PathName, @Buffer, Sizeof(Buffer));
    Result := String(Buffer);
  end;

procedure TComPromptPanel.HideTitlebar(Handle: HWND);
  // hides the title bar of the window handle given.
var
  Style: Longint;
begin
  Style := GetWindowLong(Handle, GWL_STYLE);
  SetWindowLong(Handle, GWL_STYLE, Style and (not (WS_CAPTION))
           or DS_MODALFRAME or WS_DLGFRAME);
end;

constructor TComPromptPanel.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  // set the parent window.
  Parent := AOwner as TWinControl;

  // set defaults for the control.
  fForeground := tctWhite;
  fBackground := tctBlack;
end;

procedure TComPromptPanel.Loaded;
  { initializations of the control }
  begin
    inherited loaded;
    // only load the command prompt if not in designing state.
    if not (CsDesigning in ComponentState) then
      RunPrompt(GetComSpec);
  end;

destructor TComPromptPanel.Destroy;
begin
  // if not in the designer, quit.
  if not (CsDesigning in ComponentState) then
    RunQuit;
  inherited;
end;

procedure TComPromptPanel.WritePipeOut(InString: string);
// writes Instring to the command prompt.
  var
    byteswritten: DWord;
  begin
// most console programs require CR/LF after their input.
    InString := InString + #13#10;
    WriteFile(InputPipeWrite, Instring[1], Length(Instring), byteswritten, nil);
  end;

var
  thewndhandle: HWnd;

function GCCallBackFunc(TheHWnd: HWND; cpid: LParam): Bool; stdcall;
// enumwindows callback function for GetConsoleWindow
var
  wpid: DWord;
begin
  Result := true;
  GetWindowthreadProcessID(TheHWND, @wpid);
  if wpid = DWord(cpid) then  // does window handle belong to the process started?
    begin
      Result := false;
      thewndhandle := TheHWnd;
    end;
end;

function TComPromptPanel.GetConsoleWindow(cpid: Dword): HWND;
// find the window handle associated with the process id entered in.
begin
  EnumWindows(@GCCallbackFunc, LParam(cpid));
  Result := thewndhandle;
end;

procedure TComPromptPanel.RunQuit;
  // does the tasks to quit the command prompt.
  begin
    // close process handles
    WritePipeOut('EXIT');
    CloseHandle(ProcessInfo.hProcess);
    CloseHandle(ProcessInfo.hThread);
    // close pipe handles
    CloseHandle(InputPipeRead);
    CloseHandle(InputPipeWrite);
  end;

procedure TComPromptPanel.PromptSize(var thewidth, theheight: integer);
// this tries to find the number of the command prompt characters that can
// fit in the panel and returns that in thewidth and theheight.  This is a
// kludge fix as I really didn't want to focus too much on this as I was
// more interested in putting the called process window into the panel and
// making it work.
var
  tc: TBitmap;
  gutter: integer;
begin
  tc := TBitmap.Create;
  try
    tc.canvas.font.name := 'Lucida Console';
    gutter := tc.Canvas.TextWidth('88') - tc.Canvas.TextWidth('8');
    thewidth := Self.Width div (tc.canvas.textwidth('8'));
    thewidth := thewidth - (thewidth div gutter);
    theheight := Self.Height div (tc.canvas.textheight('8'));
    theheight := theheight - (theheight div gutter);
  finally
    tc.free;
  end;
end;

procedure TComPromptPanel.RunPrompt(Exec: String);
// runs "Exec" and puts the window into the Panel.
  var
    DosApp: String;
    Security : TSecurityAttributes;
    start : TStartUpInfo;
    thewnd: HWND;
  begin
    Dosapp := Exec;
    UniqueString(DosApp); // necessary for CreateProcess.
  // create pipe to feed input from the main program into the prompt.
    With Security do
      begin
        nlength := SizeOf(TSecurityAttributes) ;
        binherithandle := true;
        lpsecuritydescriptor := nil;
      end;
    CreatePipe(InputPipeRead, InputPipeWrite, @Security, 0);

  // start command-interpreter
    FillChar(Start,Sizeof(Start),#0) ;
    start.cb := SizeOf(start) ;
    // input pipe set, standard handles otherwise.
    start.hStdInput := InputPipeRead;
    start.hStdOutput := GetStdHandle(STD_OUTPUT_HANDLE);
    start.hStdError :=  GetStdHandle(STD_ERROR_HANDLE);
    // flags indicating things we will set.
    start.dwFlags := STARTF_USESTDHANDLES + STARTF_USECOUNTCHARS
                     + STARTF_USESIZE + STARTF_USEFILLATTRIBUTE;

    // prompt size in characters, will try to fit them into the panel.
    PromptSize(start.dwxcountchars, start.dwycountchars);
    // pixel size of the command prompt.  -3 is to kludge for the panel border.
    Start.dwXSize := Width-3;
    Start.dwYSize := Height-3;
    // set the foreground and background colors.
    Start.dwFillAttribute := (DWord(fBackground) shl 4) or DWord(fForeground);

    if CreateProcess(nil, PChar(DosApp), @Security, @Security, true,
               CREATE_NEW_CONSOLE or SYNCHRONIZE,
               nil, nil, start, ProcessInfo) then
      begin
        // wait until the process stops.
        WaitForInputIdle(ProcessInfo.hProcess, INFINITE);
        // give the process a chance to create its window so it can be found.
        sleep(50);
        // find window handle for the process we just started.
        thewnd := GetConsoleWindow(ProcessInfo.dwProcessID);
        // set the parent window to the panel's window.
        Windows.SetParent(thewnd, Handle);
        // send a maximize message so the process window fills the panel window.
        PostMessage(thewnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
        // hide the title bar of the process window
        HideTitleBar(thewnd);
      end;
end;

procedure Register;
begin
  RegisterComponents('My Controls', [TComPromptPanel]);
end;

end. 

Usage is pretty simple. Place the control, and then if you want to send input to it, call WritePipeOut.

Back to Embarcadero: Delphi FAQ Index
Back to Embarcadero: Delphi Forum

My Archive

Resources

Close Box

Join Tek-Tips® Today!

Join your peers on the Internet's largest technical computer professional community.
It's easy to join and it's free.

Here's Why Members Love Tek-Tips Forums:

Register now while it's still free!

Already a member? Close this window and log in.

Join Us             Close