I need to call some native DLLs in C# and redirect the stdout and stderr in the native DLLs to files. Have tried a very simple example but couldn't get it working.
My experiment started with building a native DLL (StdStreamRedirect.dll) with below code.
#include <stdio.h>
extern __declspec(dllexport) void Test();
void Test()
{
fprintf(stderr, "%s", "STDERR\n");
fprintf(stdout, "%s", "STDOUT\n");
}
Then I created a C# console application as shown below.
namespace StdStreamRedirectionTest
{
class Program
{
static void Main(string[] args)
{
var stdErrFilePath = "stderrredirect.txt";
var stdOutFilePath = "stdoutredirect.txt";
var errFileStream = File.Open(stdErrFilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
SetStdHandle(-12, errFileStream.SafeFileHandle.DangerousGetHandle());
var outFileStream = File.Open(stdOutFilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
SetStdHandle(-11, outFileStream.SafeFileHandle.DangerousGetHandle());
var dllAddress = LoadLibrary(@"StdStreamRedirect.dll");
var procAddress = GetProcAddress(dllAddress, "Test");
var action = Marshal.GetDelegateForFunctionPointer(procAddress, typeof(Action)) as Action;
action?.Invoke();
errFileStream.Flush();
outFileStream.Flush();
}
[DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, SetLastError = true)]
public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string fileName);
[DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern IntPtr GetProcAddress(IntPtr modulePtr, [MarshalAs(UnmanagedType.LPStr)] string procName);
[DllImport("Kernel32.dll", SetLastError = true)]
private static extern int SetStdHandle(int device, IntPtr handle);
}
}
I was expecting the stdout and stderr from the native DLL would be redirected into stdoutredirect.txt and stderrredirect.txt but nothing was found in the two files after ran the C# console app.
To eliminate the possibility that the native dll wasn't called correctly in the first place, I also tried to comment out the std stream redirect code (the first six lines of Main method), and ran the c# console app, the stdout and stderr were printed in the console correctly.
Would you please help to see what went wrong in my code? Thanks a lot.
Update
I figured it out. It turned out that there was nothing wrong with the C# code which redirects the stdout and stderr. The problem was from the C code. According to this answer, at least the stdout is buffered. That's why I didn't see them in the redirected files because they weren't flushed yet so that there was nothing to redirect. After I forced the stdout and stderr to flush as shown below, it worked!
void Test()
{
fprintf(stderr, "%s", "STDERR\n");
fflush(stderr);
fprintf(stdout, "%s", "STDOUT\n");
fflush(stdout);
}
I'll still keep this question here. Because it is still not clear to me that why after the the C# app terminated, the buffered stdout and stderr were still not flushed and got redirected?