Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / C# / July 2008

Tip: Looking for answers? Try searching our database.

dllimport stdout gets eaten

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
not_a_commie - 17 Jul 2008 23:37 GMT
I changed the stdout in my C# app using Console.SetOut. It works fine
for all my Console.Out.Write calls and with log4net. However, I don't
see any output from native dlls that write to stdout. What am I doing
wrong?
Arne Vajhøj - 18 Jul 2008 00:52 GMT
> I changed the stdout in my C# app using Console.SetOut. It works fine
> for all my Console.Out.Write calls and with log4net. However, I don't
> see any output from native dlls that write to stdout. What am I doing
> wrong?

Nothing.

Why should changing something inside the Console class
change what some native code does ?

You need to do something in your native code to
get output where you want it.

Arne
Ben Voigt [C++ MVP] - 18 Jul 2008 15:03 GMT
>> I changed the stdout in my C# app using Console.SetOut. It works fine
>> for all my Console.Out.Write calls and with log4net. However, I don't
[quoted text clipped - 5 lines]
> Why should changing something inside the Console class
> change what some native code does ?

If for example, that managed code was a wrapper around SetStdHandle.  But
it's not, it changes Console.Out rather than changing stdout underneath.

> You need to do something in your native code to
> get output where you want it.

SetStdHandle, most likely.  And then it doesn't have to be a change to the
native code, as long as it's called from the same process.

> Arne
Arne Vajhøj - 19 Jul 2008 01:25 GMT
>>> I changed the stdout in my C# app using Console.SetOut. It works fine
>>> for all my Console.Out.Write calls and with log4net. However, I don't
[quoted text clipped - 7 lines]
> If for example, that managed code was a wrapper around SetStdHandle.  But
> it's not, it changes Console.Out rather than changing stdout underneath.

I can not see how it could be a wrapper around SetStdHandle. Out is a
TextWriter. If I write my own class that extends TextWriter and call
SetOut with it. There are no handle to pass to native and no way
for the native code to call my class.

>> You need to do something in your native code to
>> get output where you want it.
>
> SetStdHandle, most likely.  And then it doesn't have to be a change to the
> native code, as long as it's called from the same process.

I was thinking freopen, but if it is documented that SetStdHandle
interacts "nicely" with the C RTL, then  that can obviously be
used.

Arne
not_a_commie - 21 Jul 2008 16:26 GMT
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();
        }

    }
}
Ben Voigt [C++ MVP] - 21 Jul 2008 19:46 GMT
>>>> I changed the stdout in my C# app using Console.SetOut. It works
>>>> fine for all my Console.Out.Write calls and with log4net. However,
[quoted text clipped - 13 lines]
> SetOut with it. There are no handle to pass to native and no way
> for the native code to call my class.

I agree.  I was just trying to show that it wasn't an entirely unreasonable
expectation though, by mentioning a function that would behave as the OP
expected.  And, as you point out, it is limited to redirecting to an OS
handle, not an arbitrary TextWriter.

>>> You need to do something in your native code to
>>> get output where you want it.
[quoted text clipped - 5 lines]
> interacts "nicely" with the C RTL, then  that can obviously be
> used.

I didn't see any assurance that the native code was written in C, or that
the OP knew which RTL was being used.  If the DLL was statically linked, it
would be next-thing-to-impossible to call the "right" freopen.

> Arne
Arne Vajhøj - 28 Jul 2008 02:30 GMT
>>>> You need to do something in your native code to
>>>> get output where you want it.
[quoted text clipped - 7 lines]
> the OP knew which RTL was being used.  If the DLL was statically linked, it
> would be next-thing-to-impossible to call the "right" freopen.

Good point.

Arne

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.