Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AttachConsole(-1), but Console.WriteLine won't output to parent command prompt?

Tags:

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.

like image 308
Merlyn Morgan-Graham Avatar asked Nov 08 '11 08:11

Merlyn Morgan-Graham


2 Answers

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); } 
like image 169
Cheeso Avatar answered Sep 25 '22 13:09

Cheeso


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); 
like image 29
Dennis Avatar answered Sep 21 '22 13:09

Dennis