2013-03-24 11 views
15

Mam projekt WinForms, a jeśli użytkownik chce mieć konsolę debugowania, przydzielam konsolę z AllocConsole().Brak wyjścia konsoli podczas korzystania z AllocConsole i architektury docelowej x86

Wszystkie dane wyjściowe konsoli działają normalnie, a docelowa architektura jest ustawiona na "Dowolny procesor", ale gdy zmienię ją na "x86", nie wyświetla niczego (Console.Read() nadal działa zgodnie z oczekiwaniami). Jeśli otworzę EXE bezpośrednio, wyjście działa. Wygląda na to, że program Visual Studio przekierowuje go do własnego okna "Wyjście".

Próbowałem również odpowiedź this, ale to nie zadziałało, próbowałem też Console.SetOut(GetStdHandle(-11)), który też nie działał.

Ustawienie architektury docelowej na "Dowolny procesor" nie jest dla mnie odpowiednią opcją.

Więc oto moje dwa pytania:

  • Dlaczego jest to tylko w przypadku, gdy architektura docelowa jest ustawiona na x86?
  • Jak mogę wyprowadzać dane na moją konsolę podczas pracy w programie Visual Studio?
+1

Sprawdź w oknie Wyjście wyjątek "Pierwsza szansa". –

+0

@HansPassant: Nie, nie ma wyjątków, ale twój komentarz pomógł mi znaleźć rozwiązanie (częściowe): Kiedy otwieram plik .exe bezpośrednio, to działa, ale kiedy debuguję moje rozwiązanie, wszystkie dane wyjściowe przechodzą do widoku "Wyjście" w VS. Ale wciąż są tu dwa pytania: Dlaczego tylko z x86 i jak można uzyskać wyjście konsoli podczas debugowania mojego rozwiązania? (Jeszcze +1 dla Ciebie: D) – teamalpha5441

Odpowiedz

23

Kiedy „Włącz debugowanie kodu natywnego” jest włączona, wyjście z konsol crated z AllocConsole zostaje przekierowany do okna wyjściowego debugowania zamiast.

Powodem tego, że dzieje się to tylko w x86, a nie AnyCPU, jest to, że można debugować tylko kod natywny w aplikacji x86.

Należy zauważyć, że to zachowanie występuje tylko w przypadku konsol utworzonych za pomocą AllocConsole. Dane wyjściowe aplikacji konsoli nie są przekierowywane.

EDYCJA: Innym powodem, dla którego konsola nie wysyła tekstu, jest napisanie do konsoli przed wywołaniem AllocConsole.

Bez względu na przyczynę, kod ten przywróci dane wyjściowe, jeśli został przekierowany, i ponownie otworzy konsolę na wypadek, gdyby była ona nieważna. Używa magicznej liczby 7, która jest zwykle uchwytem standardowego wyjścia.

using System; 
using System.IO; 
using System.Runtime.InteropServices; 

public static class ConsoleHelper 
{ 
    public static void CreateConsole() 
    { 
     AllocConsole(); 

     // stdout's handle seems to always be equal to 7 
     IntPtr defaultStdout = new IntPtr(7); 
     IntPtr currentStdout = GetStdHandle(StdOutputHandle); 

     if (currentStdout != defaultStdout) 
      // reset stdout 
      SetStdHandle(StdOutputHandle, defaultStdout); 

     // reopen stdout 
     TextWriter writer = new StreamWriter(Console.OpenStandardOutput()) 
     { AutoFlush = true }; 
     Console.SetOut(writer); 
    } 

    // P/Invoke required: 
    private const UInt32 StdOutputHandle = 0xFFFFFFF5; 
    [DllImport("kernel32.dll")] 
    private static extern IntPtr GetStdHandle(UInt32 nStdHandle); 
    [DllImport("kernel32.dll")] 
    private static extern void SetStdHandle(UInt32 nStdHandle, IntPtr handle); 
    [DllImport("kernel32")] 
    static extern bool AllocConsole(); 
} 

Zobacz How to detect if Console.In (stdin) has been redirected? innego sposobu, aby wykryć, czy uchwyty konsoli zostały przekierowane.

+0

Brzmi świetnie, ale dziwne jest to, że natywny kod debugowanie jest wyłączone w moim właściwości projektu ... – teamalpha5441

+0

Czy jesteś pewien projekt startowy nie posiada ona włączona? To jest jedyne, które ma znaczenie. Dodałem rozwiązanie, które, mam nadzieję, powinno zadziałać w mojej odpowiedzi. – Stephen

+0

Epicki Stephen jest epicki! Działa z twoim rozwiązaniem. Pominąłem tylko część currentStdOut! = DefaultStdOut i TextWriter (również sprawdzanie przekierowania dla stdin nie działa, ponieważ stdin działa za każdym razem). Dziękujemy za pomoc! – teamalpha5441

4

Też miałem ten problem. Za każdym razem, gdy próbowałem debugować aplikację, konsola była pusta. O dziwo, uruchomienie exe bez debuggera działało dobrze.

Znalazłem, że musiałem Enable the Visual Studio hosting process z menu projektu Debug.

Stephen jest poprawny, ponieważ Enable native code debugging przekierowuje konsolę do okna Output. Jednak niezależnie od ustawienia debugowania kodu natywnego, nie widziałem absolutnie żadnego wyjścia w żadnym z tych obszarów, dopóki nie włączyłem procesu hostingu Visual Studio.

To mogło być powodem, że samo wyłączenie debugowania kodu natywnego nie rozwiązało problemu.

5

Obserwowany pracował dla mnie w vs 2015, żaden pracował od innych odpowiedzi:

Źródło: https://social.msdn.microsoft.com/profile/dmitri567/?ws=usercard-mini

using System; 
using System.Windows.Forms; 
using System.Text; 
using System.IO; 
using System.Runtime.InteropServices; 
using Microsoft.Win32.SafeHandles; 

namespace WindowsApplication 
{ 
    static class Program 
    { 
     [DllImport("kernel32.dll", 
      EntryPoint = "GetStdHandle", 
      SetLastError = true, 
      CharSet = CharSet.Auto, 
      CallingConvention = CallingConvention.StdCall)] 
     private static extern IntPtr GetStdHandle(int nStdHandle); 
     [DllImport("kernel32.dll", 
      EntryPoint = "AllocConsole", 
      SetLastError = true, 
      CharSet = CharSet.Auto, 
      CallingConvention = CallingConvention.StdCall)] 
     private static extern int AllocConsole(); 
     private const int STD_OUTPUT_HANDLE = -11; 
     private const int MY_CODE_PAGE = 437; 

     static void Main(string[] args) 
     { 
      Console.WriteLine("This text you can see in debug output window."); 

      AllocConsole(); 
      IntPtr stdHandle=GetStdHandle(STD_OUTPUT_HANDLE); 
      SafeFileHandle safeFileHandle = new SafeFileHandle(stdHandle, true); 
      FileStream fileStream = new FileStream(safeFileHandle, FileAccess.Write); 
      Encoding encoding = System.Text.Encoding.GetEncoding(MY_CODE_PAGE); 
      StreamWriter standardOutput = new StreamWriter(fileStream, encoding); 
      standardOutput.AutoFlush = true; 
      Console.SetOut(standardOutput); 

      Console.WriteLine("This text you can see in console window."); 

      MessageBox.Show("Now I'm happy!"); 
     } 
    } 
} 
0

Żaden z wcześniejszych odpowiedzi pracował dobrze dla mnie z VS2017 i Windows 10 (na przykład, że nie powiodło się, jeśli aplikacja uruchomi się w trybie debugowania).

Poniżej można znaleźć kod wzmocnionej trochę. Idea jest taka sama, ale magiczne liczby są usuwane (Ceztko już o tym wspomniała) i wszystkie niezbędne w \ out strumieniach są inicjowane.

Ten kod działa dla mnie, czy utworzyć nową konsolę (alwaysCreateNewConsole = true).

Mocowanie konsolę procesu macierzystego (alwaysCreateNewConsole = fałsz) ma kilka wad. Na przykład nie mogłem całkowicie naśladować zachowania aplikacji konsolowej uruchamianej z cmd. I nie jestem pewien, czy to w ogóle możliwe.

I najważniejsze: po rewizji Console class I rozważyć ogólną ideę korzystania z utworzonej klasy Console ręcznie konsoli. Działa dobrze (mam nadzieję) w większości przypadków, ale może przynieść wiele bólu w przyszłości.

static class WinConsole 
    { 
     static public void Initialize(bool alwaysCreateNewConsole = true) 
     { 
      bool consoleAttached = true; 
      if (alwaysCreateNewConsole 
       || (AttachConsole(ATTACH_PARRENT) == 0 
       && Marshal.GetLastWin32Error() != ERROR_ACCESS_DENIED)) 
      { 
       consoleAttached = AllocConsole() != 0; 
      } 

      if (consoleAttached) 
      { 
       InitializeOutStream(); 
       InitializeInStream(); 
      } 
     } 

     private static void InitializeOutStream() 
     { 
      var fs = CreateFileStream("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, FileAccess.Write); 
      if (fs != null) 
      { 
       var writer = new StreamWriter(fs) { AutoFlush = true }; 
       Console.SetOut(writer); 
       Console.SetError(writer); 
      } 
     } 

     private static void InitializeInStream() 
     { 
      var fs = CreateFileStream("CONIN$", GENERIC_READ, FILE_SHARE_READ, FileAccess.Read); 
      if (fs != null) 
      { 
       Console.SetIn(new StreamReader(fs)); 
      } 
     } 

     private static FileStream CreateFileStream(string name, uint win32DesiredAccess, uint win32ShareMode, 
           FileAccess dotNetFileAccess) 
     { 
      var file = new SafeFileHandle(CreateFileW(name, win32DesiredAccess, win32ShareMode, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero), true); 
      if (!file.IsInvalid) 
      { 
       var fs = new FileStream(file, dotNetFileAccess); 
       return fs; 
      } 
      return null; 
     } 

     #region Win API Functions and Constants 
     [DllImport("kernel32.dll", 
      EntryPoint = "AllocConsole", 
      SetLastError = true, 
      CharSet = CharSet.Auto, 
      CallingConvention = CallingConvention.StdCall)] 
     private static extern int AllocConsole(); 

     [DllImport("kernel32.dll", 
      EntryPoint = "AttachConsole", 
      SetLastError = true, 
      CharSet = CharSet.Auto, 
      CallingConvention = CallingConvention.StdCall)] 
     private static extern UInt32 AttachConsole(UInt32 dwProcessId); 

     [DllImport("kernel32.dll", 
      EntryPoint = "CreateFileW", 
      SetLastError = true, 
      CharSet = CharSet.Auto, 
      CallingConvention = CallingConvention.StdCall)] 
     private static extern IntPtr CreateFileW(
       string lpFileName, 
       UInt32 dwDesiredAccess, 
       UInt32 dwShareMode, 
       IntPtr lpSecurityAttributes, 
       UInt32 dwCreationDisposition, 
       UInt32 dwFlagsAndAttributes, 
       IntPtr hTemplateFile 
      ); 

     private const UInt32 GENERIC_WRITE = 0x40000000; 
     private const UInt32 GENERIC_READ = 0x80000000; 
     private const UInt32 FILE_SHARE_READ = 0x00000001; 
     private const UInt32 FILE_SHARE_WRITE = 0x00000002; 
     private const UInt32 OPEN_EXISTING = 0x00000003; 
     private const UInt32 FILE_ATTRIBUTE_NORMAL = 0x80; 
     private const UInt32 ERROR_ACCESS_DENIED = 5; 

     private const UInt32 ATTACH_PARRENT = 0xFFFFFFFF; 

     #endregion 
    } 
+0

Nie działa dla mnie w VS2017 (lub VS2015 z wyłączonym vhost). – ZorgoZ

Powiązane problemy