What I'm trying to do
I would like to redirect std::cout
in a dll I P/Invoke
from C#
. To do so, I redirect via SetStdHandle
using named pipes wrapped in streams.
Problem
For some strange reason, this only works once. When calling Do1()
the second time, the redirect does not appear to work at all. Why is that?
Note:
I'm aware of lifetime issues in the background thread that drains the stream, which I will address but is not the root cause for this issue.
Source.cpp
#pragma once
__declspec(dllexport) void __cdecl main();
#include <iostream>
void main() {
std::cout << "Test 1" << std::endl;
std::cout << std::flush;
}
Program.cs
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Runtime.InteropServices;
using System.Threading;
namespace RedirectDllOutput
{
class Program
{
[DllImport("..\..\..\..\x64\Debug\NativeDll.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void main();
[DllImport("Kernel32.dll", SetLastError = true)]
static extern int SetStdHandle(int device, IntPtr handle);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
private static int STD_OUTPUT_HANDLE = -11;
static void Main(string[] args)
{
Do1();
Do1();
}
private static void Do1()
{
int id = Process.GetCurrentProcess()
.Id;
using (var serverPipe = new NamedPipeServerStream("consoleRedirect" + id, PipeDirection.In, 1))
using (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))
{
// Console.WriteLine(txt);
}
}
catch (IOException)
{
break;
}
}
}
},
null);
clientPipe.Connect();
var prevStdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
var newStdHandle = new HandleRef(clientPipe, clientPipe.SafePipeHandle.DangerousGetHandle());
var stdHandle = SetStdHandle(STD_OUTPUT_HANDLE, newStdHandle.Handle);
main();
var handle = SetStdHandle(STD_OUTPUT_HANDLE, prevStdHandle);
clientPipe.WaitForPipeDrain();
clientPipe.Close();
serverPipe.Close();
}
}
}
}
Thank you very much for your help!
Aucun commentaire:
Enregistrer un commentaire