-1

I have a Win32 console program written in C, that needs to terminate when a certain length of time has elapsed, even if it's still busy. At the moment I'm doing this:

static VOID CALLBACK timeout(PVOID a, BOOLEAN b) { ExitProcess(0); }

...

  HANDLE timer = 0;
  CreateTimerQueueTimer(&timer, 0, timeout, 0, (DWORD)(time_limit * 1000),
                        0, 0);

This works fine in the case where the program is computationally busy when the time limit is reached, e.g. it easily passes a test case where I put an infinite loop in main. However, there is a situation where it doesn't work, and the program just stays hung indefinitely. The situation has to do with being called by a parent process, I don't know exactly what's going on, have asked a separate question about that. My question here is:

Is there a way to tell Windows to really kill the current process after a certain number of seconds, no matter what?

Update: experimented just now, WT_EXECUTEINTIMERTHREAD seems to solve the problem. That leaves a few questions:

  1. Why does that flag matter?

  2. If I'm not using any other time operations in the program, is it safe to ignore the warning "This flag should be used only for short tasks or it could affect other timer operations."?

  3. If more than one choice of flag will solve the problem, which flag is it best to use?

rwallace
  • 31,405
  • 40
  • 123
  • 242
  • Exit process should *really* kill the current process. I am afraid you are going to need to tell us more about this situation which has to do with being called by a parent process that you don't know exactly what's going on. Maybe provide a link to that separate question? – Mike Nakis Feb 24 '17 at 06:34
  • @MikeNakis The question about what's going on with the parent process is http://stackoverflow.com/questions/42431166/parent-process-exit-causes-child-process-to-hang - if ExitProcess would do the job, that must mean my timeout function isn't being called; is there a way to guarantee the timeout function will always be called no matter what? – rwallace Feb 24 '17 at 06:38
  • 1
    Only by spawning a separate thread which does `Sleep( time_limit * 1000 )` before `ExitProcess( 0 )`. In your current setup, I cannot tell for sure, but it may be that the timer that you set relies on some successful interaction with the standard input / standard output, and that does not work because they were redirected by a now dead parent process, so the timer does not work, either. You can verify that by writing a long entry right before `ExitProcess(0)`. I bet you will never see the log entry. And I bet that you will see it if you use the separate thread approach. – Mike Nakis Feb 24 '17 at 06:45
  • Sounds like you are reinventing the [Task Scheduler](https://msdn.microsoft.com/en-us/library/windows/desktop/aa383614.aspx). – IInspectable Feb 24 '17 at 10:20
  • 2
    Try using `TerminateProcess(GetCurrentProcess(), 1);` rather than `ExitProcess`. – Harry Johnston Feb 24 '17 at 23:52

1 Answers1

0

You can use SleepEx

Suspends the current thread until the specified condition is met. Execution resumes when one of the following occurs: AN I/O completion callback function is called, AN asynchronous procedure call (APC) is queued to the thread OR The time-out interval elapses.

The 3rd, or 1st option is your best bet. The condition for the first function should be your desired situation, whatever the case is in your program. Or a pre-configured amount of time.

After SleepEx follow-up by a call to ZwTerminateProcess from NTDLL.DLL. This will ensure that the process is terminated as calling ExitProcess performs prior checks before calling ZwTerminateProcess/Thread. Here you can call it yourself and ensure termination! You can fill the HANDLE parameter for ZwTerminateProcess by passing GetCurrentProcess() to the argument. Alternatively, you obtain a HANDLE to a remote process by scanning the process list via ZwQuerySystemInformation->ZwOpenProcess, or Creating a snapshot (CreateToolhelp32Snapshot ... off the top of my head) followed by Process32First->Next->OpenProcess - You can then use ZwTerminateProcess to terminate the remote process given you have the SE_DEBUG_PRIVILEGE, and the current process is executing from the same integrity level as the other process!

Droopy
  • 124
  • 8
  • 1
    [ZwTerminateProcess](https://msdn.microsoft.com/en-us/library/windows/hardware/ff567115.aspx): *"If the call to this function occurs in user mode, you should use the name "**NtTerminateProcess**" instead of "**ZwTerminateProcess**"."* – IInspectable Feb 24 '17 at 11:29
  • 1
    No need to use either. Plain old TerminateProcess should work perfectly well. – Harry Johnston Feb 24 '17 at 23:50
  • 1
    @IInspectable NtTerminateProcess points to ZwTerminateProcess, there is no compatibility issue. Throughout my work, I've been working with it numerous times, and never had any issues with it! – Droopy Feb 25 '17 at 00:53
  • If you need to violate a contract, you need a very good reason. *"I never had any issues [violating contracts]"* is not a very good reason. – IInspectable Feb 25 '17 at 08:59
  • @IInspectable MSVS isn't a contract. It's simply documentation surrounding a given API. You can take advice from it, but it's not like school; you can approach it in any way you wish, not only according to what it says in the textbook :) – Droopy Feb 26 '17 at 06:21
  • I don't know what MSVS has to do with any of this. I linked to the MSDN. The MSDN hosts official documentation. Official documentation is contractual. Contracts are binding, even when not enforced. If you need to break a contract, you must have a very good reason to do so. This is not kindergarten. SO is for professionals. Professionals do not have the option to approach formal documentation *"in any way [they] wish"*. For them it's actually a lot easier: They only have one way to deal with formal documentation. – IInspectable Feb 26 '17 at 10:33