25

If I have set my program to be a Windows Application, and used the AttachConsole(-1) API, how do I get Console.WriteLine to write to the console I launched the application from? It isn't working for me.

In case it is relevant, I'm using Windows 7 x64, and I have UAC enabled. Elevating doesn't seem to solve the problem though, nor does using start /wait.

Update

Some additional background that might help:

I've just discovered that if I go to the command prompt and type cmd /c MyProgram.exe, Then console output works. The same is true if I launch a command prompt, open a cmd.exe sub-process, and run the program from that sub-shell.

I've also tried logging out and back in, running from a cmd.exe launched from the start menu (as opposed to right-click -> command prompt), and running from a console2 instance. None of those work.

Background

I've read on other sites and in several SO answers that I can call the win32 API AttachConsole to bind my Windows Application to the console that ran my program, so I can have something that is "both a console application, and a Windows application".

For example, this question: Is it possible to log message to cmd.exe in C#/.Net?.

I've written a bunch of logic to make this work (using several other APIs), and I have gotten every other scenario to work (including redirection, which others have claimed won't work). The only scenario left is to get Console.WriteLine to write to the console I launched my program with. From everything I've read this is supposed to work if I use AttachConsole.

Repro

Here's a minimal sample - Note that the project is set to be a Windows Application:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;

class Program
{
    [STAThread]
    static void Main(string[] args)
    {
        if (!AttachConsole(-1))
        {
            MessageBox.Show(
                new Win32Exception(Marshal.GetLastWin32Error())
                    .ToString()
                );
        }

        Console.WriteLine("Test");
    }

    [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
    private static extern bool AttachConsole(int processId);
}
  • When I run this from a command prompt, I don't get an error, but I don't get any console output either. This is the problem
  • If I add extra message boxes anywhere in the execution flow of the app, the message box gets displayed. I expect this, so all good here.
  • When I run this from Visual Studio or by double clicking on it, a message box with an error is displayed. I expect this, so no worries here (will use AllocConsole in my real app).

If I call Marshal.GetLastWin32Error after the call to Console.WriteLine, I get the error "System.ComponentModel.Win32Exception (0x80004005): The handle is invalid". I suspect that attaching to the console is causing Console.Out to get messed up, but I'm not sure how to fix it.

Community
  • 1
  • 1
Merlyn Morgan-Graham
  • 58,163
  • 16
  • 128
  • 183
  • Why don't you mark your program as console application? – CodesInChaos Nov 08 '11 at 08:54
  • 1
    Very strange ... I also use `AttachConsole(-1)` in an application and it works without issue. I will compare the code when I get into work and post-back if there is any difference. @CodeInChoas: If you create it as a Console Application, you will always have the console window in the background. By using AttachConsole you get the best of both - an application that when opened via the command line (console) it will write to the console streams. – Dennis Nov 08 '11 at 09:04
  • 2
    @CodeInChaos: That seems like the "well, duh!" solution :) But I don't really want "both a console and Windows app", I want *either* a console or Windows app, based on command line switches. And I don't want the console to blink off the screen when I launch it. This is a pretty common scenario, and I've found about a dozen threads to get everything working, including this piece. Except that this piece isn't working. If you think I should make my requirements more clear in my question, let me know and I'll gladly fix it. – Merlyn Morgan-Graham Nov 08 '11 at 09:07
  • You must have either a console app or a windows app. You choose that at the time you create the EXE. Windows treats them differently. The best approach is to create a Windows app, then at runtime decide whether to show the form. AttachConsole simply allows a windows app to write to a console. – Cheeso Nov 08 '11 at 09:24
  • 2
    @Cheeso: Except that `AttachConsole` doesn't allow the window to write to a console. That's exactly the part I'm trying to get working. – Merlyn Morgan-Graham Nov 08 '11 at 09:41
  • 1
    I don't know, smells environmental. What you're trying to do is fundamentally flawed though, there's a race between cmd.exe writing to the console window (displaying the prompt) and your program writing "Test". At least write more than 4 characters. This race otherwise pretty much ruins the idea. – Hans Passant Nov 08 '11 at 10:27
  • @Hans: Since everyone else says it should work as-is, yes it does smell environmental. Though if someone has an idea short of flattening my box, that would be nice :) I think the flaw here is that I haven't made it clear that I don't expect perfection. I don't expect my program to block the console when run from the console (unless I use the work-around `start /wait`, as people in other threads have suggested). But I expect that I should be able to output to the console if I attach to it, just not exclusively. Do you mean the race might cause my attach or output to fail? Or just look bad? – Merlyn Morgan-Graham Nov 08 '11 at 10:40
  • @Dennis The problem with `AttachConsole(-1)` is that it can't give you a normal console application. A normal console application blocks the console, `AttachConsole(-1)` doesn't. It seems to be impossible to create a single program that both well behaved console and gui application. – CodesInChaos Nov 08 '11 at 12:52
  • @CodeInChaos: That is correct. You should only be using `AttachConsole(-1)` where you want write to the console when you have a Windows Application. If the OP requirements are different (which on second reading of the sample code, appears to be) then another approach needs to be considered. – Dennis Nov 08 '11 at 14:19
  • @DennisRoche,CodeInChaos: Fine I'll cave to peer pressure. The thing will be a console application in real life. If you guys are this worked up about it, I'm sure other devs will be too when I use this solution on NUnit DLLs they're working with. I still am interested in why `AttachConsole` isn't working when it seems to work for everyone else. If someone can get me an answer for that... – Merlyn Morgan-Graham Nov 08 '11 at 19:54
  • 1
    @MerlynMorgan-Graham were you able to fix this? I am facing the same problem, AttackConsole, AllocConsole nothing working. – fhnaseer Mar 12 '13 at 11:05
  • @FaisalHafeez: Dunno; I gave up a while ago. See if this answer helps - http://stackoverflow.com/a/14470419/232593 - if it does, let me know and I'll accept it. – Merlyn Morgan-Graham Mar 17 '13 at 01:40
  • For anyone reading this, I found that http://stackoverflow.com/a/14470419/232593 does indeed give the correct reason. – JonBrave Feb 02 '17 at 12:32

12 Answers12

16

This is how I do it in Winforms. Using WPF would be similar.

static class SybilProgram
{
    [STAThread]
    static void Main(string[] args)
    {
        if (args.Length > 0)
        {
            // Command line given, display console
            if ( !AttachConsole(ATTACH_PARENT_PROCESS) )  // Attach to a parent process console (-1)
                AllocConsole(); // Alloc a new console if none available


            ConsoleMain(args);
        }
        else
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());  // instantiate the Form
        }
    }

    private static void ConsoleMain(string[] args)
    {
        Console.WriteLine("Command line = {0}", Environment.CommandLine);
        for (int ix = 0; ix < args.Length; ++ix)
            Console.WriteLine("Argument{0} = {1}", ix + 1, args[ix]);
        Console.ReadLine();
    }

    [System.Runtime.InteropServices.DllImport("kernel32.dll")]
    private static extern bool AllocConsole();

    [System.Runtime.InteropServices.DllImport("kernel32.dll")]
    private static extern bool AttachConsole(int pid);
}
Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
Cheeso
  • 189,189
  • 101
  • 473
  • 713
  • 2
    Lovely code. It exhibits the same problem as my application tho. I launch a command prompt, run the program from there (`App.exe someargs`), and no text is output. If I run `cmd /c App.exe someargs` though, it seems to work (?!?) – Merlyn Morgan-Graham Nov 08 '11 at 09:44
  • 1
    It works for me. (subject to the limitations described in the Raymond Chen blog post referenced elsewhere.) are you compiling with `/t:winexe` or `/t:exe`? They exhibit different behavior, but both "work" for me. – Cheeso Nov 08 '11 at 11:45
  • 1
    The prototype for AllocConsole and AttachConsole is not exactly correct, although it may works, all windows API uses BOOL instead of c99 bool(1 byte) or C#'s bool, so the returned data type mismatched between native and managed code. I've found a subtle bug due to this issue in .NET 1.1, I'm not sure about higher CLR version. The best way to avoid it is using [return: MarshalAs(UnmanagedType.Bool)] – zhaorufei Mar 07 '13 at 02:41
2

I had a similar situation: could not get a Windows Application to output anything in the programmatically attached console. Eventually, it turned out that I was using Console.WriteLine once before AttachConsole, and that was tampering with everything that followed after.

  • 1
    Thanks for the hint, tho if you look at my minimal repro you'll see I still have that problem in a program that doesn't do Console.WriteLine first. As people said, it is probably environmental. What could be causing it in my case, I have no idea. I'll try running my sample code on a different box and see if it doesn't fix it. – Merlyn Morgan-Graham Apr 20 '12 at 17:28
1

Had the same problem and it appears that when running cmd.exe in Administrator mode AttachConsole() call succeeds but Console.Write() and Console.WriteLine() don't work. If you run cmd.exe normally (non-admin) everything seems to work fine.

andr
  • 15,970
  • 10
  • 45
  • 59
  • Thanks! Will check this out (...eventually) and if this turns out to be the issue I'll mark this answer as accepted. – Merlyn Morgan-Graham Jan 23 '13 at 00:44
  • Hmm, I didn't have this problem while running cmd.exe although it says "Administrator: C:\Windows\system32\cmd.exe" in the title bar. However my app runs concurrently with cmd.exe so the next command prompt appears before my program produces output. Oops. – Qwertie Jun 11 '14 at 20:50
  • For me at least, this is the one and only answer that gets it right! All my normal Command Prompts are run elevated. In that situation it seems that `AttachConsole(-1)` succeeds but `Console.WriteLine()` goes nowhere. Running the application from a *non*-elevated Command Prompt displays the output. So at some level Windows is deciding that the app is not allowed to actually write into an inherited elevated Console. – JonBrave Feb 02 '17 at 12:28
1

I was suffering from the same problem with my application's current version (targeting .NET 4.0) but am sure AttachConsole(-1) did work as expected in earlier versions (which targeted .NET 2.0).

I found that I could get console output as soon as I removed my (custom) TraceListener from my application's .exe.config file, even though I don't know why yet.

Perhaps this is what's swallowing your console output as well...

Update

In fact I had a Console.WriteLine() in my custom trace listener's c'tor which was messing things up. After removing this line, console output after AttachConsole(-1) went back to normal.

user2206916
  • 119
  • 2
  • 5
1

Had the same problem. Everything worked great when I launched the built .exe file, but failed to run inside the VS.

Solution:

  1. Check Enable the VS Hosting process.
  2. Run VS as administrator.

Maybe this will help other people to fix this issue.

Andrey Bobrov
  • 201
  • 1
  • 13
1

I had a similar problem. To compound things, I'm using WPF w/PRISM, so I need to suppress the creation of the 'Shell' as well when in "CLI Mode," but I digress...

This was the only thing that I found that finally worked

    [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool AttachConsole(int processId);

    [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
    private static extern IntPtr GetStdHandle(int nStdHandle);

    public static void InitConsole()
    {
        const int STD_OUTPUT_HANDLE = -11;

        AttachConsole(-1);

        var stdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
        var safeFileHandle = new SafeFileHandle(stdHandle, true);
        var fileStream = new FileStream(safeFileHandle, FileAccess.Write);
        var standardOutput = new StreamWriter(fileStream) { AutoFlush = true };
        Console.SetOut(standardOutput);
        Console.WriteLine();
        Console.WriteLine("As seen on StackOverflow!");
    }

All calls to Console.WriteLine() output to the CLI window after the call to InitConsole().

Hope this helps.

nishanh
  • 11
  • 2
  • I'm going to have to use "As seen on StackOverflow" in all my samples now. /golf-clap – JJS Oct 25 '21 at 14:08
1

I cannot see any significant difference between our implementations. For what it is worth, below is what I have in my application and it works fine. I also create a sample WPF application and it also worked fine.

I suspect that your issue is elsewhere. Sorry I couldn't be more help.

[STAThread]
public static void Main()
{            
    AttachProcessToConsole();    
}

private static void AttachProcessToConsole()
{
    AttachConsole(-1);
}

// Attaches the calling process to the console of the specified process.
// http://msdn.microsoft.com/en-us/library/ms681952%28v=vs.85%29.aspx
[DllImport("Kernel32.dll")]
private static extern bool AttachConsole(int processId);
Dennis
  • 20,275
  • 4
  • 64
  • 80
  • Confirmation from people who know this should work is an upgrade from where I was an hour ago. Thanks for the input :) – Merlyn Morgan-Graham Nov 08 '11 at 10:42
  • Just re-reading your question again I realise (thanks to comment from @CodeInChaos) that **you** are actually wanting a Windows Application **and** a Console Application. To achieve this you need to `AllocConsole` as demonstrated in @Cheeso's answer. – Dennis Nov 08 '11 at 14:25
  • I'm going to do both, like his answer. I just am trying to get `AllocConsole` to work at all. I'll worry about the right fit problem (which everyone else seems to be worrying about for me, lol). I'm fine with bad behavior here, and I'm not submitting customers to it, so no worries. Considering all the push-back I'm geting though, maybe I should make the darn thing a console application and call it done tho. But it really digs at me when there's a solution everyone says should work, and it doesn't work for me :) – Merlyn Morgan-Graham Nov 08 '11 at 19:50
0

+1

I had the same problem. Console output would not show up using various flavours of AllocConsole or AttachConsole.

Check if you have disabled Enable the visual studio hosting process in your project configuration. Enabling this option magically made all console messages appear as expected for me. I'm running VS2010 and .NET4, but this post suggests the 'feature' is still there in VS2012.

Community
  • 1
  • 1
takrl
  • 6,356
  • 3
  • 60
  • 69
0

DETACHED_PROCESS and CREATE_NEW_CONSOLE is the problem here.
see https://stackoverflow.com/a/494000/883015

Community
  • 1
  • 1
Joe DF
  • 5,438
  • 6
  • 41
  • 63
0

Same problem here. I was using gflags.exe, (part of Debugging Tools for Windows) to attach a commandline WPF application to vsjitdebugger.exe (See this post). As long as my application was coupled with vsjitdebugger.exe, no output was written to the console.

From the moment I detached my application, output to the console, from where I launched my application, was restored.

Community
  • 1
  • 1
Kurt Van den Branden
  • 11,995
  • 10
  • 76
  • 85
0

[This addresses the intended use of the question rather than being a direct answer to the question as stated.]

When wanting to write a dual-mode app (console or GUI depending on parameters), the method that I prefer is to compile as a Console app and then FreeConsole() if it turns out that GUI mode is desired.

Advantage: parent console processes will properly wait for the app to finish when running in console mode, and not wait for it in GUI mode. (Although note that some other calling apps might continue to wait for it.) In particular you won't get the next command prompt mixed up with app output text, which is what happens when you use the AttachConsole method.

Disadvantage: when running the GUI version from a non-console parent, a console window will briefly appear (until FreeConsole() is executed).

Another alternative method (that Microsoft seems to prefer) is to compile App.exe as GUI and App.com as console (despite the suffix, this is just a renamed .exe), where the latter simply calls the former, passing on all the command line args, streams, and exit codes.

Miral
  • 12,637
  • 4
  • 53
  • 93
0

The problem is that to output to the console you need use a handle with the value 1. The "correct" way to get that value is to call GetStdHandle(STD_OUTPUT_HANDLE), but if you haven't got a console, that function returns 0. (On earlier release you always got 1.) The run time apparently calls GetStdHandle during startup, and even after you call AttachConsole your Console stream still has an output handle of 0. You need to call GetStdHandle(STD_OUTPUT_HANDLE) (which will return 1) after AttachConsole and then use the handle returned from that.

Dave Bush
  • 31
  • 3