elishacloud / dxwrapper

Fixes compatibility issues with older games running on Windows 10/11 by wrapping DirectX dlls. Also allows loading custom libraries with the file extension .asi into game processes.
zlib License
1.17k stars 83 forks source link

[Improvement] RunProcess After the Game Closes #41

Closed AndhikaWB closed 4 years ago

AndhikaWB commented 4 years ago

Is it possible to make the wrapper to run a process not only at the start of the game but also after the game closes?

I need this feature to move save game folder at the start of the game and move it back to the previous directory after the game closes (instead of making a looping command that check if the game is still running). Of course this will require extra attention to some people if the game use a launcher that closes after launching the actual game EXE file, but I think it's already handled by IncludeProcess or ExcludeProcess.

AndhikaWB commented 4 years ago

This improvement will also make the WaitForProcess option to be a lot more useful, since the looping command will not work if this option is set to 1 (because it wait for the process to exit before launching the game).

I think WaitForProcess is necessary on my case and should be enabled by default on your future release because there is no point of moving save game folder (for example) if the game run at the same time as the process while my batch script (the process) is still moving the save game folder. Of course the game will likely to ignore my save games and create a new one instead.

AndhikaWB commented 4 years ago

Feel free to close this issue if you don't think this is important.

mirh commented 4 years ago

Just as a curiosity.. Why would you need to move saves back and forth?

AndhikaWB commented 4 years ago

To portabilize the game, useful if you want to keep your Windows tidy and clean, also useful if you need to easily copy it to flashdisk and share it to your friend and so on.

This is just an example, another example is to auto-delete the game unimportant log files after the game closes and probably many more examples.

elishacloud commented 4 years ago

I thought about adding this feature before. Dxwrapper does get notified when the game closes, most of the time. However, if the game crashes or exits prematurely for any reason it is possible that dxwrapper will not get notified and thus will do nothing.

The only real way to make this function reliable would be to launch a separate process that monitors the first process and runs the command when the first process closes. That can be done, but again it is more work.

However, I already wrote a batch file that does this for loading iso images. The use case is that you want to mount the CD/DVD image when the game starts and ununmount it after the game closes. This allows you to play games without using the CD, just by mounting the ISO.

Below is the batch file I created to do this:

@echo off
Set iso=SimCopter.iso
Set exe=SimCopter.exe
if "%1"=="CD" goto loop1
powershell.exe "Mount-DiskImage '%cd%\%iso%'"
start "" Loadiso.exe CD
goto exit
:loop1
Sleep 1
tasklist /FI "IMAGENAME eq %exe%" 2>NUL | find /I /N "%exe%">NUL
if "%ERRORLEVEL%"=="0" goto loop1
powershell.exe "while((Get-DiskImage '%cd%\%iso%' | Get-Volume).DriveLetter) { Dismount-DiskImage '%cd%\%iso%' }"
exit

In this case I am using this for SimCopter, but you can change the iso and exe names to be something different if you wanted to load a different image when a different exe file launched. You can also use this as a template to allow you to run something when the game starts up and when it closes.

Here is what you can do:

  1. Save the batch file below as MyBatch.bat and copy to game folder.
  2. Modify the batch file below for the command you want to run at game start and game exit.
  3. Modify the batch file and change the exe name to the exe name of the game you are running.
  4. Update the dxwrapper.ini file to launch the batch file like the following:
    RunProcess                 = MyBatch.bat
    WaitForProcess             = 1

Here is an updated batch file for you:

@echo off
Set exe=SimCopter.exe
if "%1"=="CD" goto loop1
REM *** Type the command here to run at game start ***
start "" /min "%0" CD
goto exit
:loop1
choice /C YN /D Y /T 1
tasklist /FI "IMAGENAME eq %exe%" 2>NUL | find /I /N "%exe%">NUL
if "%ERRORLEVEL%"=="0" goto loop1
REM *** Type the command here to run at game exit ***
exit

Edited: I changed Sleep 1 to choice /C YN /D Y /T 1, since not everyone has a Sleep program.

Edit 2: I changed the script from start "" /min LoadIso.bat CD to start "" /min "%0" CD so that it would work no mater what the batch file name is. I also added step 1 above and change the batch file name to "MyBtach.bat".

AndhikaWB commented 4 years ago

Thanks, actually I already made one for my game. Here is my batch script for comparison purpose. It is a bit more complex than yours since it contains more advanced check (to prevent running the game multiple times and so on). It is not actually intended for you but maybe useful for others who are not familiar with coding xD.

@echo off
:: %~dp0 will make this script's directory as the current directory.
:: Useful if you need to run this script as administrator since the
:: current directory will be set to C:\Windows\System32 by default.
pushd "%~dp0"
:: If this script is run with "-exec" as its parameter then just go
:: directly to ":execute" section. Refer to line 48.
if "%~1"=="-exec" goto execute
:: List all running programs and find a program file named as "launcher.exe".
:: Please be aware that this command is case sensitive, "launcher.exe" and
:: "LaUnchEr.eXe" are different.
tasklist | findstr /C:"launcher.exe"
:: If a program named "launcher.exe" is already running then exit this script.
if not errorlevel 1 (
    msg "%username%" /TIME:3 "Program already running! Exiting batch script..."
    exit /B
)
:: ==================================================================
:: These lines are reserved for your commands. What to do before
:: launching your game/program? For example, delete a file named
:: "useless.log" in temp directory and make a folder named "save"
:: in this script's directory.
del /Q "%temp%\useless.log"
mkdir "save"
:: ==================================================================
:: Make a visual basic script named "silent.vbs" in temp directory.
:: This VBS script will re-run this batch script invisibly (with
:: no annoying command prompt window) with "-exec" as its parameter.
:: Please refer to line 6 to know what is this parameter doing.
echo If WScript.Arguments.Count ^>= 1 Then >"%temp%\silent.vbs"
echo    ReDim arr(WScript.Arguments.Count-1) >>"%temp%\silent.vbs"
echo    For i = 0 To WScript.Arguments.Count-1 >>"%temp%\silent.vbs"
echo        Arg = WScript.Arguments(i) >>"%temp%\silent.vbs"
echo        If InStr(Arg, " ") ^> 0 Then Arg = """" ^& Arg ^& """" >>"%temp%\silent.vbs"
echo        arr(i) = Arg >>"%temp%\silent.vbs"
echo    Next >>"%temp%\silent.vbs"
echo    RunCmd = Join(arr) >>"%temp%\silent.vbs"
echo    CreateObject("Wscript.Shell").Run RunCmd, 0, True >>"%temp%\silent.vbs"
echo End If >>"%temp%\silent.vbs"
:: Make a 1 second delay before re-starting this batch script with invisible window.
:: If you don't make this 1 second delay, Windows will complain that the "silent.vbs"
:: script in temp directory does not exist (since Windows is still creating it).
timeout 1
:: Make another instance of this script with "-exec" as its parameter (with invisible
:: window, thanks to the VBS script) and exit the older instance (aka this one) immediately.
:: Please refer to this link to know what are the "%~nx0" or "%~dp0" (in line 5) meaning:
:: https://stackoverflow.com/questions/112055/what-does-d0-mean-in-a-windows-batch-file
start "" "wscript.exe" "%temp%\silent.vbs" "%~nx0" -exec
exit /B
:: The ":execute" section, if this script is re-run with "-exec" parameter.
:: Start your game/program directly without executing commands in the previous lines.
:execute
:: Make a 5 seconds delay before re-executing commands in ":execute" section.
:: Reducing the delay will re-execute commands in ":execute" section more frequently.
:: Not very useful in most cases since you may play games for a long duration (hours).
:: Just reduce the delay if you really-really need more "responsiveness".
timeout 5
:: Refer to line 9
tasklist | findstr /C:"launcher.exe"
:: Go to the ":execute" section (again) to re-execute commands if the game is still running.
if not errorlevel 1 goto execute
:: ==================================================================
:: These lines are reserved for your commands. What to do after you
:: exited your game/program? These commands will not be executed
:: directly because of that 5 seconds delay, but it doesn't matter.
:: For example, delete the "silent.vbs" file that we created before.
del /Q "%temp%\silent.vbs"
:: ==================================================================
:: This is the "true" exit, the previous exit (line 49) will run
:: another instance of this script (invisibly).
exit /B

Some notes for people who are not familiar with batch script: 1) Using >nul will prevent your command from showing what they do. For example timeout 3 >nul will prevent Command Prompt from showing Waiting for 3 seconds... 2) Some commands can be run using parameters such as /Q, /S, and so on. You may need to run the parameter /? to know what are they. For example tasklist /?

An example for Split Second, this will move your savegame from "YOURNAME\Documents\Disney..." to "GAMEDIR\save"

@echo off
pushd "%~dp0"
if "%~1"=="exec" goto execute
if not exist "save" mkdir "save"
set "disney=%userprofile%\Documents\Disney Interactive Studios"
if exist "%disney%" robocopy "%disney%" "save" /MOVE
mklink /D /J "%disney%" "save"
echo If WScript.Arguments.Count ^>= 1 Then >"%temp%\silent.vbs"
echo    ReDim arr(WScript.Arguments.Count-1) >>"%temp%\silent.vbs"
echo    For i = 0 To WScript.Arguments.Count-1 >>"%temp%\silent.vbs"
echo        Arg = WScript.Arguments(i) >>"%temp%\silent.vbs"
echo        If InStr(Arg, " ") ^> 0 Then Arg = """" ^& Arg ^& """" >>"%temp%\silent.vbs"
echo        arr(i) = Arg >>"%temp%\silent.vbs"
echo    Next >>"%temp%\silent.vbs"
echo    RunCmd = Join(arr) >>"%temp%\silent.vbs"
echo    CreateObject("Wscript.Shell").Run RunCmd, 0, True >>"%temp%\silent.vbs"
echo End If >>"%temp%\silent.vbs"
timeout 1
start "" "wscript.exe" "%temp%\silent.vbs" "%~nx0" "exec"
exit /B
:execute
timeout 5
tasklist | findstr /C:"SplitSecond.exe"
if not errorlevel 1 goto execute
rd /S /Q "%temp%\SplitSecond_Data_DFE"
rd /Q "%disney%"
del /Q "%temp%\silent.vbs"
exit /B
elishacloud commented 4 years ago

Closing issue. I don't plan to add this option. You can use a script like the ones above to achieve this.