terminal icon indicating copy to clipboard operation
terminal copied to clipboard

Add --wait command line parameter

Open g0blue opened this issue 4 years ago • 14 comments

Add ability to wait for console close for scripts

I would like to add a command line parameter that causes wt.exe invocations to wait until the newly created window closes before returning.

Perhaps something like:

wt.exe --wait [other args]

This would block the caller of wt.exe until the newly created window closes. Useful for scripts that want output visible in another window but needs to know when the launched command exits.

Proposed technical implementation details (optional)

g0blue avatar Jan 22 '21 23:01 g0blue

So for the record, I don't think this is possible.

For some background: there are two "subsystems" of processes on Windows. "Console" apps, which are attached to a console by default, and "Windows" apps, which aren't attached to a console. Windows apps are expected to have a HWND and be interactable through their own windows. wt is a "Windows" application, not a console one, because we don't want a new conhost.exe spawning every time you open wt.exe.

I believe that both powershell and cmd.exe won't wait for a Windows subsystem process to exit, by default. When they detect the exe they're about to launch is a Windows one, they'll create the process, and immediately return to the prompt. For console subsystem applications, they'll wait until the process exits before returning to the prompt.

So this isn't really something that can be solved by wt.exe.

Now that I'm thinking on this, I wonder how feasible this is with the legacy conhost. If you're a commandline exe, and you spawn a child commandline exe in a new console (with a new conhost window), then you should be able to wait for that process. Might need to do it with Win32 APIs, but that should be possible. I'm gonna tag in @miniksa here, because we'll want to make sure that's still possible to do once defterm (#492) lands.

The trick is getting at the child process ID. If you launch wt -- {some process}, then the child process is wt.exe, but the one you really want to wait on is {some process}. That's a thinker.

zadjii-msft avatar Jan 25 '21 13:01 zadjii-msft

Thanks for the detailed explanation. First, let me say what I really want, and you can tell me if it's possible or not. :)

Current Scenario

We have some simulators that rely on a launching program to launch both a workload application and an external simulator process and coordinating communication between those two child processes. The workload application can inherit the command console of the launching program. However, I ideally (if executed to begin with in Windows Terminal), I would like the simulator process to open a new pane that is automatically closed after process termination.

I currently do some variation of:

  • Workload: CreateProcess with cmd /C <process_exe> [process_args] | tee.exe workload.log
  • Sim: CreateProcess with cmd /C start "Simulator Window" /WAIT cmd /C <process_exe> [process_args] | tee simulator.log

At end of workload I can wait for the Sim "process" (actually a cmd.exe process) to finish (or kill it, etc.)

Desired Scenario

What I really want is for (#4472) to be implemented and, if launched within a WT session, have the Sim launched in a separate pane.

And, what I really, really want is for the Visual Studio Integrated Terminal to be replaced with Windows Terminal and have all of this work within VS. But, that's an issue for another day. :)

Possible Solution

Given that cmd and powershell have no way of waiting for a windows subsystem app to finish, I propose adding a wtc.exe console application interface that communicates with wt.exe. This wtc.exe would take mostly the same parameters as wt.exe, but would also provide the needed --wait argument. It could then use some private IPC mechanism to retrieve the child process ID (or handle) launched by wt.exe and wait for it to complete.

For example, I could use the following to launch the Sim workload:

wtc.exe --wait --session %WT_SESSION_ID% split-pane -p %WT_PROFILE_ID% -- <sim_command> [sim_command_args]

g0blue avatar Jan 25 '21 16:01 g0blue

We've discussed (in specs and as a team) adding a wtc in the past, though it's always been in the context of "a helper for working with wt from the commandline", and less "a launcher for wt.exe". Our vision with wtc was more for things like wtc --version, wtc --help, etc. - things that usually would be printed to the console, but we have to use a gross message box for, since we're a windowed application.

I think part of the problem with your proposal is: what happens when multiple subcommands are provided to wtc? Ex: wtc -s 0 --wait new-tab commandline1.exe ; split-pane commandline2.exe ; etc.exe ? Do we wait for all of them? The first one?

Uhg this gets even more complicated. If you spawn wt -w 0 child.exe, then the child process isn't actually spawned as a child of the wt.exe you're spawning. It's going to be spawned as a child of the wt that ends up actually hosting it. So in a diagram, parent.exe is running in a terminal window.

wt.exe (1)
└── parent.exe

parent.exe now tries to create a new child in the same terminal window

wt.exe (1)
└── parent.exe
    └── wt.exe -w 0 child.exe

However, that second wt.exe instance is actually just going to tell the first one to create the child process, so the tree will end up looking like:

wt.exe (1)
├── parent.exe
└── child.exe

Getting the parent to wait on the child process is gonna be really challenging here.

zadjii-msft avatar Jan 25 '21 16:01 zadjii-msft

Our vision with wtc was more for things like wtc --version, wtc --help, etc. - things that usually would be printed to the console, but we have to use a gross message box for, since we're a windowed application.

That makes sense, too.

I think part of the problem with your proposal is: what happens when multiple subcommands are provided to wtc? Ex: wtc -s 0 --wait new-tab commandline1.exe ; split-pane commandline2.exe ; etc.exe ? Do we wait for all of them? The first one?

Naively, I would wait for all of them. Or, disallow multiple subcommands with --wait and throw an error.

parent.exe now tries to create a new child in the same terminal window

wt.exe (1)
└── parent.exe
    └── wt.exe -w 0 child.exe

The parent would actually spawn wtc.exe, which is a different process than wt.exe (1). The wtc.exe instance would communicate with wt.exe (1) to launch the process and retrieve the pid(s) and wait for it(them). I'm fairly confident this would work fine. I don't think you have to be a parent process to wait on another process - is this a false assumption?

wt.exe (1)
├── parent.exe
| └── wtc.exe
└── child.exe

Alternatively, instead of --wait, directly expose a method to return the pid of the launched process from wtc.exe (return value?). This would definitely require limiting to one launched process at a time.

g0blue avatar Jan 25 '21 17:01 g0blue

Alternatively, instead of --wait, directly expose a method to return the pid of the launched process from wtc.exe (return value?). This would definitely require limiting to one launched process at a time.

I don't like this. I don't want PIDs or hierarchy as an API. We want the ability and freedom to restructure how our processes launch at any given time in the future. I've seen too many things in the OS need to change their hierarchy, split into processes, combine processes, or what not for security or performance reasons over time that I am not willing to commit to a direct dependency on the process model.

So for the record, I don't think this is possible.

For some background: there are two "subsystems" of processes on Windows. "Console" apps, which are attached to...

Yeah this is down the right path. The thing is that we are beholden to what the caller shell is doing in terms of WaitForSingleObject or not. It's really just a CMD.exe policy that it will WaitForSingleObject on the process handle for any application that is !IMAGE_SUBSYSTEM_WINDOWS_GUI (and note there are more than 2 choices.... technically.... though today really only CUI and GUI are used...) What each individual shell decides to do is up to it.

So while wt.exe as a IMAGE_SUBSYSTEM_WINDOWS_GUI will for sure not block cmd.exe and a wtc.exe with IMAGE_SUBSYSTEM_WINDOWS_CUI stamped in would.... I can't say (without more research than I'm willing to do in this instant) what it would do for Python, Perl, PowerShell, and so on.

If we had a wtc.exe eventually... that, by nature of being part of the Windows Terminal package/suite of applications, could know the intricate details of process hierarchy and IPC to appropriately WaitForSingleObject or WaitForMultipleObjects itself on the correct subprocess/thread/etc... And therefore if you did wtc.exe --wait, it could opaquely wait on the correct thing and I wouldn't be too upset by that.

Current Scenario

We have some simulators that rely on a launching program to launch both a workload application and an external simulator process and coordinating communication between those two child processes. The workload application can inherit the command console of the launching program. However, I ideally (if executed to begin with in Windows Terminal), I would like the simulator process to open a new pane that is automatically closed after process termination.

I currently do some variation of:

  • Workload: CreateProcess with cmd /C <process_exe> [process_args] | tee.exe workload.log
  • Sim: CreateProcess with cmd /C start "Simulator Window" /WAIT cmd /C <process_exe> [process_args] | tee simulator.log

At end of workload I can wait for the Sim "process" (actually a cmd.exe process) to finish (or kill it, etc.)

Do note that in the Default Application world, which I'm still working on, that launching the child cmd.exe detached would end up coming back into a new tab or pane on the already opened Windows Terminal.

(In specific, if you had registered Windows Terminal as your default application and you had it set to have "inbound connections" drop into new panes... then any detached console application or any fresh console application started on the system that is bound to be interactive would be relayed from the old conhost.exe in the box to whatever handler is registered to take it... for instance Windows Terminal, and display per whatever preferences the user set. And of course, this would be preference based, not explicitly configurable per script or scenario like calling wtc.exe from a script would be.)

And, what I really, really want is for the Visual Studio Integrated Terminal to be replaced with Windows Terminal and have all of this work within VS. But, that's an issue for another day. :)

I thought Visual Studio's Integrated Terminal WAS now using the Windows Terminal control...

miniksa avatar Jan 25 '21 18:01 miniksa

Do note that in the Default Application world, which I'm still working on, that launching the child cmd.exe detached would end up coming back into a new tab or pane on the already opened Windows Terminal.

That's great, but I have a lot of tabs / panes open already. I think I need the ability to control which current pane gets split. Maybe that's another feature request.

I thought Visual Studio's Integrated Terminal WAS now using the Windows Terminal control...

Perhaps it is, but is there any way to split panes in the integrated terminal? I really want the integrated terminal to obey all of the settings that are in place for Windows Terminal.

g0blue avatar Jan 25 '21 18:01 g0blue

<off-topic>

I thought Visual Studio's Integrated Terminal WAS now using the Windows Terminal control...

Perhaps it is, but is there any way to split panes in the integrated terminal? I really want the integrated terminal to obey all of the settings that are in place for Windows Terminal.

They're using the same core that we implemented, but that doesn't include support for things like panes, tabs, etc. Their control is implemented in WPF, because they couldn't get WinUI ingested into VS just for the terminal control.

</off-topic>

zadjii-msft avatar Jan 25 '21 18:01 zadjii-msft

Given that cmd and powershell have no way of waiting for a windows subsystem app to finish

In cmd, START /WAIT notepad waits for the notepad process to finish. And if a batch file runs notepad, then it waits even without START /WAIT.

In PowerShell, Start-Process -Wait notepad likewise waits.

KalleOlaviNiemitalo avatar Jan 26 '21 15:01 KalleOlaviNiemitalo

Given that cmd and powershell have no way of waiting for a windows subsystem app to finish

In cmd, START /WAIT notepad waits for the notepad process to finish. And if a batch file runs notepad, then it waits even without START /WAIT.

In PowerShell, Start-Process -Wait notepad likewise waits.

That's a good point. Using either start or Start-Process allows one to wait for any launched GUI application to exit. Given this fact, I probably don't require having a console wtc.exe front-end if the existing wt.exe were to handle --wait. Although, as stated earlier, having console output from wtc.exe may be useful.

I did notice that start /WAIT will return the return value of the launched process. This isn't necessarily required in my scenario, but I can see how that would be useful. So, there probably needs to be some thought to how combining --wait with creating multiple tabs / panes would work. I suppose the last process to exit would have it's return value propagated? Maybe return the first non-zero value? Not sure what makes sense.

g0blue avatar Jan 26 '21 15:01 g0blue

@KalleOlaviNiemitalo thanks for the correction. I forgot about that flag when looking yesterday. I guess the spirit of it is that I cannot guarantee what any particular shell would do. But if it's capable of waiting on a process handle no matter what, that makes it easier.

miniksa avatar Jan 26 '21 17:01 miniksa

One last requirement I thought of:

If wt.exe / wtc.exe is alive and waiting for processes to complete, and that process is forcefully killed, it should also kill all of the processes that it is waiting for. This will probably require some explicit handling as there is no parent-child relationship of the processes being waited on.

g0blue avatar Jan 26 '21 17:01 g0blue

One last requirement I thought of:

If wt.exe / wtc.exe is alive and waiting for processes to complete, and that process is forcefully killed, it should also kill all of the processes that it is waiting for. This will probably require some explicit handling as there is no parent-child relationship of the processes being waited on.

That's usually possible with a job object so it shouldn't be tough for either the caller or for wt.exe/wtc.exe to classify things launched downstream into a job.

miniksa avatar Jan 26 '21 17:01 miniksa

You know, I've thought about this for a long time. I no longer hate it. I'm going to put it on the backlog.

DHowett avatar Jul 02 '21 17:07 DHowett

I think there needs to also be an option to wait until the terminal window is open. Not until it closes. This would entirely be for use with the -w flag though, so maybe just build the wait into that.

JesseRussell411 avatar Jan 30 '23 20:01 JesseRussell411

Hello, i'm having the need to wait for the launched wt session to be over, in order to do some subsequent tasks in a script. So i second this proposal for a --wait parameter, or something similar.

In the meanwhile i ask: is there any way at all to do this, as of today? Is there any way (for example) to determine the PID of the launched session (whether it's a window, tab, or panel) so that i can wait for that process to be over? I'm using powershell. Solutions like listing every running process with WindowsTerminal.exe as a parent process and filtering on the command line (for example filtering for myapp.exe if i launched wt.exe myapp.exe) wouldn't work if i don't specify something to launch or if i launch the same program multiple times.

aetonsi avatar Feb 20 '23 14:02 aetonsi

Hello, i'm in need of a wait command too as i figured out that using Start /wait otherbatchscript.bat while the script is elevated opens the "normal" CMD window and not the terminal even tho i got the terminal set to being the default terminal application

biast12 avatar Jan 12 '24 11:01 biast12