I got this to work. The setvbuf is critical. Here's my test code.
First, the dll:
#include <stdio.h>
__declspec(dllexport) void PrintMsg();
void PrintMsg()
{
setvbuf(stdout, NULL, _IONBF, 0);
printf("blah blah blah\n");
}
Second, the C# code:
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
namespace TestPrintf
{
class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
protected static extern bool SetStdHandle(int nStdHandle, IntPtr
hConsoleOutput);
[DllImport("PrintDll.dll", CallingConvention =
CallingConvention.Cdecl)]
protected static extern void PrintMsg();
[STAThread]
public static void Main(string[] args)
{
var id = Process.GetCurrentProcess().Id; // make this instance
unique
var serverPipe = new NamedPipeServerStream("consoleRedirect" + id,
PipeDirection.In, 1);
var clientPipe = new NamedPipeClientStream(".", "consoleRedirect" +
id, PipeDirection.Out, PipeOptions.WriteThrough);
ThreadPool.QueueUserWorkItem(state =>
{
serverPipe.WaitForConnection();
using (var stm = new StreamReader(serverPipe))
{
while (serverPipe.IsConnected)
{
try
{
var txt = stm.ReadLine();
if (!string.IsNullOrEmpty(txt))
MessageBox.Show("Got stdout with: " + txt);
}
catch (IOException)
{
break; // normal disconnect
}
}
}
}, null);
clientPipe.Connect();
var hr11 = new HandleRef(clientPipe,
clientPipe.SafePipeHandle.DangerousGetHandle());
SetStdHandle(-11, hr11.Handle); // redirect stdout to my pipe
var app = new Application();
var win = new Window { Width = 300, Height = 200 };
var sp = new StackPanel { Orientation = Orientation.Horizontal };
win.Content = sp;
var b1 = new Button { Content = "Direct", Width = 100 };
sp.Children.Add(b1);
var b2 = new Button { Content = "Indirect", Width = 100 };
sp.Children.Add(b2);
var b3 = new Button { Content = "DllImport", Width = 100 };
sp.Children.Add(b3);
b1.Click += (sender, e) => Console.Out.WriteLine("Direct Button was
clicked");
b2.Click += (sender, e) =>
{
using (var stdout = Console.OpenStandardOutput())
{
var bytes = Console.OutputEncoding.GetBytes("Indirect Button
was clicked" + Console.Out.NewLine);
stdout.Write(bytes, 0, bytes.Length);
}
};
b3.Click += (sender, e) => PrintMsg();
app.Run(win);
clientPipe.Dispose();
serverPipe.Dispose();
}
}
}