6

I'm preparing for writing an online judge core,
A program that can compile user's code and run the program to check the answer like uva online judge.

And I'm having problem in catching the exception of submit program like below.

int main()
{
    while(~scanf("%d %d",n,&m))
    {   
        printf("%d\n",n+m);
    }
    return 0;
}

it's access denied at first line because it scan an integer to error position.
how can I catch runtime error for my process?

I used to use "try catch" to catch the exception,
but it didn't reply anything about runtime error.

so I only check the exit code of the submit program although it's not a good method to check except for a process..
and it shows like photo

I have to close the error message box manually,
and I find a solution that is to use a SEH Handler DLL for the process.

SetErrorMode(SEM_NOGPFAULTERRORBOX);

but I don't know how to use it in C# process.

and below is my code of judger

timer = new Stopwatch();
timer.Reset();

submitProg = new Process();
submitProg.StartInfo.FileName = outputFile;
submitProg.StartInfo.UseShellExecute = false;
submitProg.StartInfo.CreateNoWindow = true;
submitProg.StartInfo.RedirectStandardInput = true;
submitProg.StartInfo.RedirectStandardOutput = true;
submitProg.StartInfo.RedirectStandardError = true;
submitProg.StartInfo.ErrorDialog = false;
submitProg.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
submitProg.EnableRaisingEvents = true;

submitProg.Start();
timer.Start();

progInput = submitProg.StandardInput;
progOutput = submitProg.StandardOutput;

progInput.Write(inputStream.ReadToEnd());
submitProg.StandardInput.Close();
while (!submitProg.HasExited)
{
    peakPagedMem = submitProg.PeakPagedMemorySize64;
    peakVirtualMem = submitProg.PeakVirtualMemorySize64;
    peakWorkingSet = submitProg.PeakWorkingSet64;
    if (peakPagedMem > memLimit)
    {
        submitProg.Kill();
    }
    if (timer.ElapsedMilliseconds > timeLimit)
    {
        timeLimitExceed = true;
        submitProg.Kill();
    }
}

timeUsed = timer.ElapsedMilliseconds;
timer.Stop();

if(submitProg.ExitCode!=0)systemRuntimeError = true;

Thanks for helping, and so sorry for my poor English.

==================================
p.s.
the question is how can I set error mode for the child process in C#.
My English is not good enough to explain the problem, so sorry.<(_ _)>

Nekosyndrome
  • 65
  • 1
  • 6
  • Having a few error boxes shouldn't be a problem since you need to sandbox your thing pretty well in any case. You can just kill the sandbox and the process with it. – Matti Virkkunen May 21 '11 at 08:59

2 Answers2

11

If you mean the Win32 API function SetErrorMode then you'll need to use P/Invoke, which is easy with such a simple signature:

[DllImport("kernel32.dll")]
static extern ErrorModes  SetErrorMode(ErrorModes uMode);

[Flags]
public enum ErrorModes : uint {
    SYSTEM_DEFAULT             = 0x0,
    SEM_FAILCRITICALERRORS     = 0x0001,
    SEM_NOALIGNMENTFAULTEXCEPT = 0x0004,
    SEM_NOGPFAULTERRORBOX      = 0x0002,
    SEM_NOOPENFILEERRORBOX     = 0x8000
}

(The Site http://www.pinvoike.net/ is always a good place to start to get the right declaration.)

Richard
  • 106,783
  • 21
  • 203
  • 265
  • @kannaduki There is no mention on MSDN that this is inherited by child processes -- therefore I would expe3ct it is *not* inherited. – Richard May 21 '11 at 09:54
  • It seems not to be inherited. How can I set error mode for my child process @@? thanks. – Nekosyndrome May 21 '11 at 10:20
  • 1
    @kannaduki Set it in process. I note from MSDN the recommendation to use `SetThreadErrorMode` in Win7/2008R2 -- where it is a new API -- as it is "less disruptive". Ie. changing global state like this is only safe if the exe and the libraries it uses are written and tested with different error modes in mind. – Richard May 21 '11 at 10:22
  • 4
    From MSDN: ["The default behavior is for the new process to inherit the error mode of the caller"](https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863%28v=vs.85%29.aspx) – Simon MᶜKenzie Oct 05 '15 at 03:24
5

You cannot set the error mode in another process without injecting code into that process. Which is impossible to do easily in C#. It isn't going to work well in C/C++ either, these kind of programs don't live long enough to give you time to inject.

It doesn't solve your problem anyway, you also have to protect against programs that never exit after they got stuck in an endless loop. The simple solution is to give every program a limited amount of time to execute. Say 10 seconds. If it doesn't complete then Process.Kill() it and declare a failure. This will also take care of the programs that bomb with a message box.

Trivial to implement with the Process.WaitForExit(milliseconds) overload.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536