Context
I am trying to prepare an environment for a game. More exactly, I would like to be able to copy and restore the current state of a game (when the game is inactive of course) to be able to restart the game from a previous state.
The reason is that the Diablo II game normally allows to only save the current state.
To be able to do that, I would like to build a small application that will manage the saved files of the game and launch the game waiting for its end.
Problem
I can find and manage the files, and start the game (using CreateProcess
) but I could not find a way to way for the end of the game.
Current research
I have already found Why those two different behaviors with WaitForSingleObject function in CPP and WaitForSingleObject doesn’t wait the end of the process. So I know that I have to use a job object.
But even the job object cannot wait for a DirectX game.
Code to reproduce:
I could reproduce the problem in a minimal C code with debug traces using notepad.exe
(waiting correctly) and the free Battle for Wesnoth game:
#include <stdio.h>
#include <Windows.h>
#include <time.h>
LPCWSTR path = L"C:\Windows\notepad.exe";
//LPCWSTR path = L"C:\Users\serge\Games\battle-for-wesnoth\wesnoth.exe";
int main() {
STARTUPINFOW si = { sizeof(si) };
PROCESS_INFORMATION pi;
time_t tim;
BOOL cr;
tim = time(NULL);
printf("BEFORE: %s", ctime(&tim));
HANDLE hJob = CreateJobObjectW(NULL, L"TestJob");
printf("Job handle: %pn", hJob);
HANDLE ioCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
printf("IO Completion Port: %pn", ioCP);
JOBOBJECT_ASSOCIATE_COMPLETION_PORT port = { hJob, ioCP };
cr = SetInformationJobObject(hJob, JobObjectAssociateCompletionPortInformation,
&port, sizeof(port));
printf("Associate Completion Port: %xn", cr);
cr = CreateProcessW(path, NULL, NULL, NULL, FALSE,
CREATE_SUSPENDED, NULL, NULL, &si, &pi);
printf("CreateProcess: %xn", cr);
cr = AssignProcessToJobObject(hJob, pi.hProcess);
printf("Assign Process to job: %pn", ioCP);
ResumeThread(pi.hThread);
WaitForSingleObject(pi.hProcess, INFINITE); // useless: does not wait even for notepad.exe
WaitForSingleObject(pi.hThread, INFINITE);
tim = time(NULL);
printf("AFTER (process) %s", ctime(&tim));
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
DWORD code;
ULONG_PTR key;
LPOVERLAPPED overlapped;
while (GetQueuedCompletionStatus(ioCP, &code,
&key, &overlapped, INFINITE)) {
if ((HANDLE)key == hJob && code == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO) {
printf("%sn", "JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO");
break;
}
if ((HANDLE)key == hJob && code == JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT) {
printf("%sn", "JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT");
}
if ((HANDLE)key == hJob && code == JOB_OBJECT_MSG_EXIT_PROCESS) {
printf("%s - %pn", "JOB_OBJECT_MSG_EXIT_PROCESS", overlapped);
}
if ((HANDLE)key == hJob && code == JOB_OBJECT_MSG_NEW_PROCESS) {
printf("%s - %pn", "JOB_OBJECT_MSG_NEW_PROCESS", overlapped);
}
}
printf("AFTER (job) %s", ctime(&tim));
CloseHandle(hJob);
CloseHandle(ioCP);
return 0;
}
I get for notepad:
BEFORE: Sat May 11 11:05:29 2024
Job handle: 00000000000000B4
IO Completion Port: 00000000000000B8
Associate Completion Port: 1
CreateProcess: 1
Assign Process to job: 00000000000000B8
AFTER (process) Sat May 11 11:05:30 2024
JOB_OBJECT_MSG_NEW_PROCESS - 0000000000005B5C
JOB_OBJECT_MSG_NEW_PROCESS - 0000000000006A54 <- I can see a new process
JOB_OBJECT_MSG_EXIT_PROCESS - 0000000000005B5C
JOB_OBJECT_MSG_EXIT_PROCESS - 0000000000006A54
JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO
AFTER (job) Sat May 11 11:05:40 2024 <- correctly waited for the end of notepad
and for Wesnoth:
BEFORE: Sat May 11 11:03:06 2024
Job handle: 00000000000000A8
IO Completion Port: 00000000000000BC
Associate Completion Port: 1
CreateProcess: 1
Assign Process to job: 00000000000000BC
AFTER (process) Sat May 11 11:03:19 2024
JOB_OBJECT_MSG_NEW_PROCESS - 0000000000006990
JOB_OBJECT_MSG_EXIT_PROCESS - 0000000000006990
JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO
AFTER (job) Sat May 11 11:03:19 2024 <- same time as after process
The traces show that notepad
does start a second process and that the job correctly waits for the end of both. But nothing comes for the game…