omnibrain 15 Posted July 2, 2023 I want to host a console in my Delphi application. Editors/IDEs like VS Code and IntelliJ IDEA have an embedded console. I want essentially achieve the same. For the moment it doesn't matter if it is the "old" console oder the new "windows terminal". I want to have a tab or panel in my application with a console that runs cmd or powershell. Trouble is, I don't know where to start. When I search I only find how to write Delphi console applications. Or I find information on how to read the console output of other programs (like with TurboPack DOSCommand). Share this post Link to post
FPiette 384 Posted July 2, 2023 5 hours ago, omnibrain said: I want to have a tab or panel in my application with a console that runs cmd or powershell. You have to launch the corresponding command interpreter executable. If you don't want to have it in a separate floating window, you must redirect input and output to your own application using pipes. You'll easily find numerous Delphi libraries for the purpose. Search "Delphi Redirect input output" or "interprocess communication using pipes" (remove double quotes) with your favorite search engine. Share this post Link to post
PeterBelow 238 Posted July 2, 2023 11 hours ago, omnibrain said: I want to host a console in my Delphi application. Editors/IDEs like VS Code and IntelliJ IDEA have an embedded console. I want essentially achieve the same. For the moment it doesn't matter if it is the "old" console oder the new "windows terminal". I want to have a tab or panel in my application with a console that runs cmd or powershell. Trouble is, I don't know where to start. When I search I only find how to write Delphi console applications. Or I find information on how to read the console output of other programs (like with TurboPack DOSCommand). Start by reading the docs for the AllocConsole API function. Share this post Link to post
FPiette 384 Posted July 2, 2023 52 minutes ago, PeterBelow said: Start by reading the docs for the AllocConsole API function. No need to call AllocConsole: it is enough to check "make console application" in the project options AFTER having created a GUI application. Delphi RTL will create a console Window. Not that it is not enough to run cmd and powershell! See my previous message. Share this post Link to post
omnibrain 15 Posted July 2, 2023 14 hours ago, FPiette said: You have to launch the corresponding command interpreter executable. If you don't want to have it in a separate floating window, you must redirect input and output to your own application using pipes. You'll easily find numerous Delphi libraries for the purpose. Search "Delphi Redirect input output" or "interprocess communication using pipes" (remove double quotes) with your favorite search engine. Yes, but redirecting input and output is not what I want to do. That's possible with the TurboPack DOSCommand I mentioned. But this way I lose everything that makes a (modern) console magic. Colours, clickable links. So I want to "host" the real deal. I suppose I can start a Console, pass a handle to a panel or something as the parent and have it draw within my program. I don't need the output. I could send input via Windows Messages if necessary. Share this post Link to post
David Heffernan 2347 Posted July 2, 2023 7 minutes ago, omnibrain said: Yes, but redirecting input and output is not what I want to do. That's possible with the TurboPack DOSCommand I mentioned. But this way I lose everything that makes a (modern) console magic. Colours, clickable links. So I want to "host" the real deal. I suppose I can start a Console, pass a handle to a panel or something as the parent and have it draw within my program. I don't need the output. I could send input via Windows Messages if necessary. Read the code of an open source project that does this and work out what they do Share this post Link to post
timfrost 78 Posted July 3, 2023 In one of my applications I also need just to show a console window in a tab or panel, and I also have no need to capture its output. It's a rarely used option, not a central part of the application. You can get the command window handle by calling GetProcessWindow(pi.dwProcessID), where pi is the PROCESS_INFOMATION available after calling Create Process; and then calling SetParent to set the parent of the command window as your target component. You will also need to call SetWindowPos with the command window handle to position and size the command window over the target component. It's not very pretty, and there may well be more elegant ways to do this, but it is simple, and has been sufficient for me over several Delphi releases up to and including 11. Share this post Link to post
David Schwartz 427 Posted July 3, 2023 (edited) See if this helps: https://www.dropbox.com/s/rbslh6vrzyxio43/RedCon.pas?dl=0 It's from D5 era, so it might need some touching-up. Edited July 3, 2023 by David Schwartz Share this post Link to post
Remy Lebeau 1403 Posted July 3, 2023 10 hours ago, timfrost said: You can get the command window handle by calling GetProcessWindow(pi.dwProcessID), where pi is the PROCESS_INFOMATION available after calling Create Process; There is no GetProcessWindow() function in the Win32 API, since windows are not tied to processes, only to threads. So I'm assuming that function is implemented in your own code, yes? Probably a combination of EnumWindows() and GetWindowThreadProcessId(), though using EnumThreadWindows() with the pi.dwThreadID instead might make more sense. 10 hours ago, timfrost said: and then calling SetParent to set the parent of the command window as your target component. That can be very dangerous if you are not careful. Is it legal to have a cross-process parent/child or owner/owned window relationship? Share this post Link to post
timfrost 78 Posted July 3, 2023 Yes, you are right. This code is so old that I had forgotten that GetProcessWindow is in a library function I borrowed from somewhere else, and which as you say uses EnumWindows. And thanks for the Raymond Chen link; luckily no problem has ever surfaced and this small program is anyway about to be dropped because it is used to configure a service which has reached EOL. Share this post Link to post
A.M. Hoornweg 144 Posted July 4, 2023 12 hours ago, Remy Lebeau said: That can be very dangerous if you are not careful. Is it legal to have a cross-process parent/child or owner/owned window relationship? Isn't this what modern internet browser do, having separate child processes for tabs contained in a common host process ? Share this post Link to post
David Heffernan 2347 Posted July 4, 2023 2 hours ago, A.M. Hoornweg said: Isn't this what modern internet browser do, having separate child processes for tabs contained in a common host process ? They control the code for all processes involved. That's the crucial difference. 1 Share this post Link to post
Remy Lebeau 1403 Posted July 4, 2023 (edited) 9 hours ago, A.M. Hoornweg said: Isn't this what modern internet browser do, having separate child processes for tabs contained in a common host process ? Yes, they do, but those child processes are specifically designed to interoperate and sync with their host process. That is not the case when you simply host someone else's UI inside of your own. That is when bad things can happen, because the child process doesn't know it's being hosted and so the parent and child don't sync their states together. Edited July 4, 2023 by Remy Lebeau Share this post Link to post
Fr0sT.Brutal 900 Posted July 10, 2023 On 7/3/2023 at 10:31 PM, Remy Lebeau said: That can be very dangerous if you are not careful. Is it legal to have a cross-process parent/child or owner/owned window relationship? I suppose these issues are not a concern in case of console window? Share this post Link to post
David Heffernan 2347 Posted July 10, 2023 If you look at other IDEs that have console windows, they don't do cross process window parenting. Share this post Link to post
aehimself 396 Posted July 10, 2023 Minimum code I used is: procedure TForm1.FormCreate(Sender: TObject); Var windowstyle: Integer; appthreadid: Cardinal; cmdhandle: THandle; Begin cmdhandle := FindWindow('ConsoleWindowClass', 'Command Prompt'); // Hide title bars and borders of launched application windowstyle := GetWindowLong(cmdhandle, GWL_STYLE); windowstyle := windowstyle - WS_CAPTION - WS_BORDER - WS_OVERLAPPED - WS_THICKFRAME; SetWindowLong(cmdhandle,GWL_STYLE,windowstyle); // Attach the container applications input thread to the launched ones, so that we receive user input appthreadid := GetWindowThreadProcessId(cmdhandle, nil); AttachThreadInput(GetCurrentThreadId, appthreadid, True); // Docking. Change the parent of the launched application WinApi.Windows.SetParent(cmdhandle, Self.Handle); SendMessage(Self.Handle, WM_UPDATEUISTATE, UIS_INITIALIZE, 0); UpdateWindow(cmdhandle); // Make the docked window fill all the client area of the container SetWindowPos(cmdhandle, 0, 0, 0, Self.ClientWidth, Self.ClientHeight, SWP_NOZORDER); // This prevents the parent control to redraw on the area of its child windows (the launched application) SetWindowLong(Self.Handle, GWL_STYLE, GetWindowLong(Self.Handle, GWL_STYLE) Or WS_CLIPCHILDREN); // SetForegroundWindow(WindowHandle); // SetFocus(WindowHandle); End; This does not take care of resizing the docked window if the form resizes and you also have to keep an eye on if / when your docked application closes. Also it includes no error checking / handling. The result is as expected: 1 Share this post Link to post