2

I'm not a batch coder, but I'm trying to create a script that does the following once executed:

  • enables the dedicated video card (laptop NVIDIA card)
  • runs a specific video game and waits for its end
  • once the game is terminated, disables the dGPU

The following code works great with simple programs like notepad.exe, since they start immidiatly:

// enable dedicated GPU
devcon enable "@PCI\VEN_10DE&DEV_1C8C&SUBSYS_8259103C&REV_A1\4&3455E2C8&0&0008"

// start notepad and wait for its termination
start /b /wait "" "C:\Windows\notepad.exe" || goto QUIT

:QUIT

// disable dedicated GPU
devcon disable "@PCI\VEN_10DE&DEV_1C8C&SUBSYS_8259103C&REV_A1\4&3455E2C8&0&0008"

exit

The problem is that if I change "notepad.exe" for some heavy video game (BF1 for example) which takes some time to start, the script doesn't wait for game's actual launch and proceeds with other command. So the result is:

  • GPU is turned on
  • GPU is turned off (script exit)
  • After some time game starts (using Intel Graphics)

Is there a way to make the second command (start /wait) wait for game's actual launch?

The reason I need this script is: NVIDIA Optimus + Windows 10 = micro freezes

PS: sorry for my english.

Ordinary User
  • 117
  • 2
  • 2
  • 9
  • What happens if you don't use `start /b /wait "" ` and just put the path to your game executable? – Compo May 05 '17 at 22:31
  • 1
    Use: `start "" "C:\Windows\notepad.exe" | pause` – Aacini May 06 '17 at 00:13
  • @Compo same result – Ordinary User May 06 '17 at 01:21
  • @Aacini same result – Ordinary User May 06 '17 at 01:21
  • The default behavior in a batch file is to wait for the executed process to exit and set `%errorlevel%` to its exit code. Using `start /b /wait` restores the default behavior when you need to use `start` (well, almost -- since `/b` disables Ctrl+C events for a console process) such as for setting the working directory or priority class. – Eryk Sun May 06 '17 at 03:04
  • 2
    If it's not waiting, it's not because the process is taking a long time. It's because the executable is a loader that sets up spawning the real executable and then exits. cmd doesn't know anything about grandchild processes. Loaders that behave this way are frustrating. They should wait on the real process to proxy its exit code. – Eryk Sun May 06 '17 at 03:08
  • Did you tested _exactly_ this way: `start "" "C:\Windows\notepad.exe" | pause`? You may test this from the command line. In my Windows 8.1 computer, the command-prompt suspend execution until I manually close `notepad`. Don't this same test works in your computer? Does it work with `notepad` but not with the video game? – Aacini May 06 '17 at 03:53
  • @eryksun: About grandchild processes: accordingly to [this MCND's comment](http://stackoverflow.com/questions/43754374/execute-multiple-batch-files-concurrently-and-monitor-if-their-process-is-comple/43762349#comment74590803_43762349), the method I suggested should work even in such a case. – Aacini May 06 '17 at 04:16
  • @Aacini, you're relying on the started program to leak the inherited handle for the NamedPipe object to its child processes. In that case, `pause` won't exit until all of the potential writers (process that have a handle to write end of the pipe) have exited. That may work if the program calls `CreateProcess` directly with `bInheritHandles` as `TRUE`. – Eryk Sun May 06 '17 at 04:30
  • @Aacini, since it's the `StandardOutput` handle this `pause` trick also should work reliably for an unbroken chain of console processes that don't modify their `StandardOutput`. Windows automatically inherits the standard handles for console processes, even if `CreateProcess` is called with `bInheritHandles` as `FALSE`. A non-console process that doesn't inherit handles will break the chain. Also, the chain is broken if a child console process is started with a new `StandardOutput` handle. – Eryk Sun May 06 '17 at 05:06
  • @Aacini Yes, I've used exactly the one that you suggested, same result - wait command doesn't wait for game's exit since game takes time to launch, so batch proceeds with other commands. – Ordinary User May 06 '17 at 12:12
  • If this line: `start "" "C:\Windows\notepad.exe" | pause` don't waits until `notepad` is closed, then there is a problem with your computer, because this method works in all computers where had been tested (8 at least, with different Windows versions; see [here](http://stackoverflow.com/questions/33584587/how-to-wait-all-batch-files-to-finish-before-exiting/33586872#33586872) ). If previous line do wait with notepad, but not with your game, then the game behavior prevents to use this method... (Note that there is not any "wait command" here). – Aacini May 06 '17 at 15:36
  • @Aacini, notepad.exe works because it doesn't start another program, so `pause` waits until notepad closes its inherited handle for the pipe when it exits. OTOH, `start "" "%SystemRoot%\write.exe" | pause` is a more typical case of a non-console launcher that inherits the pipe handle (because cmd.exe starts it with `bInheritHandles` as `TRUE`) but spawns a child (wordpad.exe) that doesn't inherit the pipe handle and doesn't wait for the child to exit. The game launcher likely behaves the same way. – Eryk Sun May 06 '17 at 16:48
  • @Aacini `start "" "C:\Windows\notepad.exe" | pause` works fine – Ordinary User May 06 '17 at 17:38
  • @eryksun When I launch `bf1.exe`, before game's start there is Origin's start, after Origin is completely loaded, game starts and `bf1.exe` appears in the Task Manager. – Ordinary User May 06 '17 at 17:42
  • @Aacini, eryksun's example (`start "" "%SystemRoot%\write.exe" | pause`) is exactly what happens in my case. – Ordinary User May 06 '17 at 17:46

1 Answers1

3
// start game launcher 
start /b /wait "" "launcherexecutable"
:wait
timeout 5 >nul
tasklist |findstr /i /L /c:"launcherexecutable" /c:"gameexecutable" >nul
if not errorlevel 1 goto wait
:QUIT

which should repeatedly delay for (5 seconds) then see whether either the launcher or the game executable is running and repeat the loop if so.

Hence, should proceed to quit once the game executable finishes.

Obviously, change the 5 to some other value or add intermediate executablenames as appropriate.

Magoo
  • 77,302
  • 8
  • 62
  • 84
  • 1
    This may work in a pinch, but it's not useful as a general solution. To restrict it to just your current session, you could filter tasklist for the current `%SessionName%` (default "Console"). Still, in general there could be multiple instances of `launcherexecutable` running in the current session, and you only want the process tree for your particular instance. PowerShell's `start-process -wait` is better in this regard. It uses a Windows Job object (i.e. `CreateJobObject`, `AssignProcessToJobObject`) that doesn't allow breakaway and waits for all processes in the job to exit. – Eryk Sun May 06 '17 at 06:14
  • This worked, but I would like to avoid using loops and timers, because the time may change depending on the CPU load and game. I'm sure there is a way to solve this in more "elegant" way. – Ordinary User May 06 '17 at 12:35
  • @eryksun I've tried PowerShell's start-process -wait, same result. – Ordinary User May 06 '17 at 12:37
  • @OrdinaryUser, which versions of PowerShell and Windows are you using? I tested using PowerShell 5.1 in Windows 10. Also, in the [published source](https://github.com/PowerShell/PowerShell/blob/v6.0.0-alpha.18/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs#L1947), I see they create the process suspended and then resume it *before* assigning it to the job. Depending on relative thread scheduling, and if the launcher is a quick native app, it could exit before PowerShell adds it to the job, so a grandchild process won't be in the job either. – Eryk Sun May 06 '17 at 13:04
  • @eryksun PowerShell: 5.1.15063.168 Windows: 1703, 15063.250 – Ordinary User May 06 '17 at 13:56
  • @eryksun I've tried -wait, -passthru... same result – Ordinary User May 06 '17 at 14:01