2011-11-08 10 views
20

Se ho impostato il mio programma come Windows Application e utilizzato l'API AttachConsole(-1), come posso ottenere Console.WriteLine per scrivere sulla console da cui ho avviato l'applicazione? Non sta funzionando per me.AttachConsole (-1), ma Console.WriteLine non verrà restituito al prompt dei comandi padre?

Nel caso sia rilevante, sto usando Windows 7 x64 e ho UAC abilitato. Elevare non sembra tuttavia risolvere il problema, né l'utilizzo di start /wait.

Aggiornamento

Alcuni di fondo aggiuntivo che potrebbe aiutare:

Ho appena scoperto che se vado al prompt dei comandi e digitare cmd /c MyProgram.exe, Poi console di output funziona. Lo stesso è vero se lancio un prompt dei comandi, apro un sottoprocesso ed eseguo il programma da quella sotto-shell.

Ho anche provato a disconnettermi e rientrare, eseguendo da un cmd.exe lanciato dal menu di avvio (al contrario di clic con il pulsante destro del mouse -> prompt dei comandi) ed eseguendo da a console2 instance. Nessuno di quei lavori.

Sfondo

Ho letto su altri siti e in diversi SO risposte che posso chiamare l'API Win32 AttachConsole di legare la mia applicazione di Windows per la console che correva il mio programma, in modo da poter avere qualcosa che è "sia un'applicazione console che un'applicazione Windows".

Ad esempio, questa domanda: Is it possible to log message to cmd.exe in C#/.Net?.

Ho scritto un po 'di logica per fare questo lavoro (usando diverse altre API), e ho ottenuto ogni altro scenario di lavoro (incluso il reindirizzamento, che altri hanno sostenuto non funzionerà). L'unico scenario rimasto è quello di ottenere Console.WriteLine per scrivere sulla console con cui ho avviato il mio programma. Da tutto ciò che ho letto, questo dovrebbe funzionare se uso AttachConsole.

Repro

Ecco un esempio minimo - Si noti che il progetto è impostato per essere un 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); 
} 
  • Quando eseguo questo da un prompt dei comandi, non ottengo un errore, ma non ottengo alcun output della console. Questo è il problema
  • Se aggiungo caselle di messaggio aggiuntive in qualsiasi punto del flusso di esecuzione dell'app, la finestra di messaggio viene visualizzata. Mi aspetto questo, quindi tutto bene qui.
  • Quando eseguo questo da Visual Studio o facendo doppio clic su di esso, viene visualizzata una finestra di messaggio con un errore. Mi aspetto questo, quindi non ci sono problemi qui (userò AllocConsole nella mia vera app).

Se chiamo Marshal.GetLastWin32Error dopo la chiamata a Console.WriteLine, ho la "System.ComponentModel.Win32Exception (0x80004005): l'handle non valido" errore. Ho il sospetto che il collegamento alla console stia causando problemi con lo Console.Out, ma non sono sicuro di come risolverlo.

+0

Perché non si contrassegna il programma come applicazione per console? – CodesInChaos

+1

Molto strano ... Uso anche 'AttachConsole (-1)' in un'applicazione e funziona senza problemi. Comparerò il codice quando entrerò nel lavoro e nel post-back se c'è qualche differenza. @CodeInChoas: se lo crei come un'applicazione console, avrai sempre la finestra della console in background. Usando AttachConsole ottieni il meglio da entrambi: un'applicazione che, una volta aperta tramite la riga di comando (console), scriverà sui flussi della console. – Dennis

+0

@CodeInChaos: sembra il "bene, duh!" soluzione :) Ma non voglio realmente "sia una console che un'app per Windows", voglio * una * console o app per Windows, basata su opzioni della riga di comando. E non voglio che la console tocchi lo schermo quando lo avvio. Questo è uno scenario abbastanza comune, e ho trovato circa una dozzina di thread per far funzionare tutto, incluso questo pezzo. Tranne che questo pezzo non funziona. Se pensi che dovrei chiarire le mie esigenze nella mia domanda, fammelo sapere e lo sistemerò volentieri. –

risposta

14

Ecco come lo faccio in Winforms. L'utilizzo di WPF sarebbe simile.

static class SybilProgram 
{ 
    [STAThread] 
    static void Main(string[] args) 
    { 
     if (args.Length > 0) 
     { 
      // Command line given, display console 
      if (!AttachConsole(-1)) // Attach to a parent process console 
       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); 
} 
+2

Codice incantevole. Presenta lo stesso problema della mia applicazione. Lancio un prompt dei comandi, eseguo il programma da lì ('App.exe someargs'), e non viene emesso alcun testo. Se eseguo 'cmd/c App.exe someargs', sembra funzionare (?!?) –

+1

Funziona per me. (soggetto alle limitazioni descritte nel post sul blog di Raymond Chen di cui si fa riferimento altrove). Stai compilando '/ t: winexe' o'/t: exe'? Esibiscono un comportamento diverso, ma entrambi "funzionano" per me. – Cheeso

+1

Il prototipo di AllocConsole e AttachConsole non è esattamente corretto, sebbene possa funzionare, tutte le API di Windows utilizzano BOOL al posto del bool c99 (1 byte) o di C#, quindi il tipo di dati restituito non corrisponde tra codice nativo e codice gestito. Ho trovato un piccolo bug a causa di questo problema in .NET 1.1, non sono sicuro della versione CLR più alta. Il modo migliore per evitarlo è usare [return: MarshalAs (UnmanagedType.Bool)] – zhaorufei

0

non riesco a vedere alcun differenza significativa tra le nostre implementazioni. Per quello che vale, di seguito è quello che ho nella mia applicazione e funziona bene. Creo anche un'applicazione WPF di esempio e ha funzionato bene.

Sospetto che il problema sia altrove. Scusa, non potrei essere più d'aiuto.

[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); 
+0

La conferma da parte di chi sa che questo dovrebbe funzionare è un aggiornamento da dove ero un'ora fa. Grazie per l'input :) –

+0

Basta rileggere nuovamente la tua domanda Mi rendo conto (grazie al commento di @CodeInChaos) che ** tu ** stai effettivamente volendo un'applicazione Windows ** e ** un'applicazione console. Per ottenere questo è necessario 'AllocConsole' come dimostrato nella risposta di @ Cheeso. – Dennis

+0

Ho intenzione di fare entrambe le cose, come la sua risposta. Sto solo cercando di far funzionare 'AllocConsole'. Mi preoccuperò del problema di adattamento corretto (che tutti sembrano preoccuparsi per me, lol). Sto bene con cattivi comportamenti qui, e non sto presentando clienti ad esso, quindi non preoccuparti. Considerando tutto il respingimento che sto ottenendo, forse dovrei rendere la cosa dannata un'applicazione per console e chiamarla così. Ma mi scava davvero quando c'è una soluzione che tutti dicono che dovrebbe funzionare, e non funziona per me :) –

0

Avevo una situazione simile: non è possibile ottenere un'applicazione Windows per l'output di qualsiasi cosa nella console collegata al programma. Alla fine, si è scoperto che stavo usando Console.WriteLine una volta prima di AttachConsole, e che stava manomettendo tutto ciò che seguiva dopo.

+1

Grazie per il suggerimento, anche se guardi la mia minima replica vedrai che ho ancora quel problema in un programma che non fa Console.WriteLine prima. Come dicevano le persone, probabilmente è ambientale. Cosa potrebbe causarlo nel mio caso, non ne ho idea. Proverò a eseguire il mio codice di esempio su una scatola diversa e vedere se non lo risolve. –

0

+1

Ho avuto lo stesso problema. L'output della console non verrà visualizzato utilizzando vari tipi di AllocConsole o AttachConsole.

Verificare se si è disabilitato Abilitare il processo di hosting di studio visivo nella configurazione del progetto. L'abilitazione di questa opzione ha reso magicamente tutti i messaggi della console visualizzati come previsto per me. Sono in esecuzione VS2010 e .NET4, ma this post indica che la 'funzione' è ancora presente in VS2012.

1

avuto lo stesso problema e sembra che durante l'esecuzione in modalità cmd.exeamministratoreAttachConsole() chiamata riesce ma Console.Write() e Console.WriteLine() non funzionano. Se esegui normalmente cmd.exe (non amministratore), tutto sembra funzionare correttamente.

+0

Grazie! Verificherà questo aspetto (... alla fine) e se questo risulta essere il problema contrassegnerò questa risposta come accettata. –

+0

Hmm, non ho avuto questo problema durante l'esecuzione di cmd.exe anche se dice "Administrator: C: \ Windows \ system32 \ cmd.exe" nella barra del titolo. Tuttavia, la mia app viene eseguita contemporaneamente a cmd.exe, quindi viene visualizzato il prompt dei comandi successivo prima che il mio programma generi l'output. Ops. – Qwertie

+0

Per me almeno, questa è l'unica risposta che va bene! Tutti i miei normali comandi di comando sono eseguiti in alto. In questa situazione sembra che 'AttachConsole (-1)' riesca ma 'Console.WriteLine()' non va da nessuna parte. L'esecuzione dell'applicazione da un Prompt dei comandi con elevazione * non * visualizza l'output. Quindi, a un certo livello, Windows sta decidendo che l'app non può scrivere in una console elevata ereditata. – JonBrave

0

Stavo soffrendo dello stesso problema con la versione corrente della mia applicazione (con targeting .NET 4.0) ma sono sicuro che AttachConsole(-1) ha funzionato come previsto nelle versioni precedenti (che puntavano a .NET 2.0).

Ho scoperto che è possibile ottenere l'output della console non appena ho rimosso il mio (personalizzato) TraceListener dal file .exe.config della mia applicazione, anche se non so ancora perché.

forse questo è ciò che sta inghiottendo l'output della console così ...

Aggiornamento

In realtà ho avuto un Console.WriteLine() in c'tor mia abitudine traccia di chi ascolta che è stato a confondere le cose. Dopo aver rimosso questa linea, l'output della console dopo AttachConsole(-1) è tornato alla normalità.

1

Aveva lo stesso problema. Tutto ha funzionato alla grande quando ho lanciato il file .exe, ma non sono riuscito a girare all'interno del VS.

Soluzione:

  1. Controllare Attivare il processo di VS Hosting.
  2. Esegui VS come amministratore .

Forse questo aiuterà le altre persone a risolvere questo problema.

0

Lo stesso problema qui. Stavo usando gflags.exe, (parte di Strumenti di debug per Windows) per allegare un'applicazione WPF da riga di comando a vsjitdebugger.exe (vedere il messaggio this). Finché la mia applicazione è stata accoppiata con vsjitdebugger.exe, non è stato scritto alcun output sulla console.

Dal momento in cui ho scollegato l'applicazione, l'output sulla console, da dove ho avviato la mia applicazione, è stato ripristinato.

0

Ho avuto un problema simile. Per combinare le cose, sto usando WPF w/PRISM, quindi ho bisogno di sopprimere la creazione della 'Shell' anche quando in "modalità CLI", ma sto divagando ...

Questa era l'unica cosa che ho scoperto che finalmente ha funzionato

[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!"); 
    } 

Tutte le chiamate a Console.WriteLine uscita() per la finestra CLI dopo la chiamata a InitConsole().

Spero che questo aiuti.

Problemi correlati