It doesn’t seem like this should be so hard, but it is. The Win32 CreateProcess API call has two nifty ways to do this.
1) Inherit handles from the parent process. So open the log file as your own stdout and stderr, then launch the child process.
2) Specify the stdout and stderr handles explicitly to the CreateProcess call.
Now, we all know that in .NET using win32 api calls is naughty, so let’s try using System.Diagnosis.Process.Start() instead. It has a very convient methods for capturing the output, but no way to set the handles to a file. Rats.
So all we have left is a hack. Launch cmd.exe and have it redirect the output as described here: http://weblogs.asp.net/israelio/archive/2004/08/31/223447.aspx
Process.Start(“cmd.exe”, “/c foo.exe -arg >” + dumpDir + “\\foo_arg.txt”);
icky Oh and wait, the process exits immediatly, and the Process.ExitCode is always 1. Arg!
Ok, so we need another hack. How about instead of starting cmd.exe we just run a bat file.
StreamWriter bat = File.CreateText(“foo.bat”);
bat.WriteLine(“foo.exe -arg >” + dumpDir + “\\foo_arg.txt”);
Process task = new Process();
task.StartInfo.UseShellExecute = false;
task.StartInfo.FileName = “foo.bat”;
task.StartInfo.Arguments = “”;
Truly horrific, but it has the advantage of working! Of course this is subject to a race condition where foo.bat could be replaced with another file by a malicious process, and it’s evil commands would be executed within our user context. I don’t know how to fix that. Perhaps generate a random path name, create a directory, change the ACL’s to prevent anyone from creating files in there, create the batch file in the directory, and then run it. Maybe.
The ffcall library which is used in the GNUStep base library has a problem on Windows XP SP2 (and later) if Data Execution Prevention is turned on for all programs.
Here is a patch to ffcall 1.10, that allows the trampoline code to work with Windows Data Execution Prevention.
In Windows Server 2003 & Windows XP SP2, Microsoft added a feature (if you have the hardware to support it), which will prevent the execution of code in areas of memory marked for data. So specifically this protects against a class of buffer overrun attacks.
I know you are saying, “So what, I don’t have self modifying code” that is what I thought too. So go and change your settings and turn on DEP. Go do it now, I’ll wait here…..
Ok, perhaps your program still runs fine, but mine didn’t. Turns out that the GNUStep implementation of NSInvocation uses the ffcall library to make the dynamic function calls. The ffcall implementaion of trampolines (basically a function pointer with context) allocates some memory and writes some opcodes in to it to set up the context state then JMP to the real function. Oops, that is self modifying code and doesn’t work any more.
The Solution: Windows doesn’t actually ban all execution in writable pages, just in data pages. Most unix OSes have similar limitations. We just need to tell Windows that we’d like a writable data page to put our code into. This is done with the VirtualAlloc function. For example:
void *addr = VirtualAlloc(NULL, bytesneeded, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
Now, VirtualAlloc can only allocate multiples of the pagesize, and the bytesneeded in that call will be rounded up. So don’t be using it like malloc and calling VirtualAlloc over and over again with tiny little values. The default page size is 4k on workstation and either 4k or 2M (yes 2 megabytes!) on server platforms.
The linked article is a nice library for doing stack backtraces from the result of an exception in your program.
Here are the quoting rules I have discovered through trial and error. If
you follow these guidelines, MSVCRT.dll will decode the arguments properly
and pass the correct parameters to main(argc,argv).
I assume here that the result will be passed to CreateProcess.
CommandLine = all args (including exe name) joined by spaces.
for each arg
If the arg contains any of newline, tab, space or ” (double quote), then
If the arg is a null string, then quote it.
If the arg contains double back slash \\, then quote it.
Quoting in this context means:
” before and after
if you find a \, then look at the next non-\ character. If it is a
” or the end of the string, then replace \ with \\
embedded ” becomes \”
foo -> foo
foo bar -> “foo bar”
foo\bar -> foo\bar
foo\ bar -> “foo\ bar”
foo”bar -> “foo\”bar”
foo\\bar -> foo\\bar
fo o\\bar -> “fo o\\bar”
foo bar\ -> “foo bar\\”
fo\\o\\\”ba\\\r -> “fo\\o\\\\\\”ba\\\r”
See the documentation for CommandLineToArgvW for more infomation.
Then call CreateProcess(0, CommandLine, …..). I always call CreateProcess
since I don’t trust exec or spawn to properly quote the arguments.