To, co chcesz zrobić, nie jest możliwe w rozsądny sposób. Było podobne pytanie, więc spójrz na odpowiedzi .
Jest też szalone podejście (witryna nie działa - kopia zapasowa dostępna tutaj. ) Napisane przez Jeffrey Knight :
Pytanie: Jak utworzyć aplikację, która może działać w trybie GUI (Windows) lub w trybie wiersza poleceń / konsoli?
Pozornie wydaje się to łatwe: tworzysz aplikację Console, dodajesz do niej formularz Windows i zaczynasz działać. Jest jednak problem:
Problem: Jeśli uruchamiasz w trybie GUI, w tle czai się zarówno okno, jak i nieznośna konsola i nie masz żadnego sposobu, aby to ukryć.
Wydaje się, że ludzie chcą prawdziwej aplikacji dla płazów, która może działać płynnie w obu trybach.
Jeśli to zepsujesz, istnieją tutaj cztery przypadki użycia:
User starts application from existing cmd window, and runs in GUI mode
User double clicks to start application, and runs in GUI mode
User starts application from existing cmd window, and runs in command mode
User double clicks to start application, and runs in command mode.
Wysyłam kod, aby to zrobić, ale z zastrzeżeniem.
Właściwie myślę, że takie podejście sprawi, że w przyszłości będziesz miał o wiele więcej kłopotów, niż jest to warte. Na przykład będziesz musiał mieć dwa różne interfejsy użytkownika - jeden dla GUI i jeden dla polecenia / powłoki. Będziesz musiał zbudować jakiś dziwny centralny silnik logiczny, który abstrahuje od GUI i wiersza poleceń, a to będzie po prostu dziwne. Gdybym to był ja, cofnąłbym się i zastanowiłbym się, jak zostanie to wykorzystane w praktyce i czy tego rodzaju przełączanie trybu jest warte pracy. Tak więc, o ile nie wymaga tego jakiś specjalny przypadek, sam nie użyłbym tego kodu, ponieważ gdy tylko napotkam sytuacje, w których potrzebuję wywołań API, aby coś zrobić, zatrzymuję się i zadaję sobie pytanie „czy nadmiernie komplikuję sprawy? ”.
Typ danych wyjściowych = aplikacja systemu Windows
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using Microsoft.Win32;
namespace WindowsApplication
{
static class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AllocConsole();
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool FreeConsole();
[DllImport("kernel32", SetLastError = true)]
static extern bool AttachConsole(int dwProcessId);
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
[STAThread]
static void Main(string[] args)
{
string mode = args.Length > 0 ? args[0] : "gui";
if (mode == "gui")
{
MessageBox.Show("Welcome to GUI mode");
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
else if (mode == "console")
{
IntPtr ptr = GetForegroundWindow();
int u;
GetWindowThreadProcessId(ptr, out u);
Process process = Process.GetProcessById(u);
if (process.ProcessName == "cmd" )
{
AttachConsole(process.Id);
Console.WriteLine("hello. It looks like you started me from an existing console.");
}
else
{
AllocConsole();
Console.WriteLine(@"hello. It looks like you double clicked me to start
AND you want console mode. Here's a new console.");
Console.WriteLine("press any key to continue ...");
Console.ReadLine();
}
FreeConsole();
}
}
}
}
AttachConsole
z-1
(wartością stałej APIATTACH_PARENT_PROCESS
) zamiast mieć nadzieję, że okno pierwszego planu jest właściwym oknem poleceń do zapisu.To jest odrobinę stare (OK, jest BARDZO stare), ale teraz robię dokładnie to samo. Oto bardzo proste rozwiązanie, które działa dla mnie:
[DllImport("kernel32.dll", SetLastError = true)] static extern bool AllocConsole(); [DllImport("kernel32.dll")] static extern IntPtr GetConsoleWindow(); [DllImport("user32.dll")] static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); const int SW_HIDE = 0; const int SW_SHOW = 5; public static void ShowConsoleWindow() { var handle = GetConsoleWindow(); if (handle == IntPtr.Zero) { AllocConsole(); } else { ShowWindow(handle, SW_SHOW); } } public static void HideConsoleWindow() { var handle = GetConsoleWindow(); ShowWindow(handle, SW_HIDE); }
źródło
Console.WriteLine
logi nie są wyświetlane w przypadku rozpoczętym z cmd z argumentamiusing System.Runtime.InteropServices;
Najłatwiej jest uruchomić aplikację WinForms, przejść do ustawień i zmienić typ na aplikację konsolową.
źródło
Zrzeczenie się
Jest na to sposób, który jest dość prosty, ale nie sugerowałbym, że jest to dobre podejście do aplikacji, którą chcesz pokazać innym. Ale jeśli jakiś programista musiałby wyświetlać jednocześnie konsolę i formularze systemu Windows, można to zrobić całkiem łatwo.
Ta metoda obsługuje również wyświetlanie tylko okna konsoli, ale nie obsługuje wyświetlania tylko formularza systemu Windows - tzn. Konsola będzie zawsze wyświetlana. Możesz wchodzić w interakcje (tj. Odbierać dane -
Console.ReadLine()
,Console.Read()
) z oknem konsoli tylko wtedy, gdy nie pokazujesz formularzy Windows; wyjście do konsoli -Console.WriteLine()
- działa w obu trybach.Jest to dostarczane w stanie, w jakim jest; nie ma gwarancji, że to nie zrobi później czegoś strasznego, ale działa.
Etapy projektu
Zacznij od standardowej aplikacji konsolowej .
Oznacz
Main
metodę jako[STAThread]
Dodaj odwołanie w projekcie do System.Windows.Forms
Dodaj formularz systemu Windows do projektu.
Dodaj standardowy kod startowy systemu Windows do swojej
Main
metody:Wynik końcowy
Będziesz mieć aplikację, która pokazuje konsolę i opcjonalnie formularze Windows.
Przykładowy kod
Program.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; namespace ConsoleApplication9 { class Program { [STAThread] static void Main(string[] args) { if (args.Length > 0 && args[0] == "console") { Console.WriteLine("Hello world!"); Console.ReadLine(); } else { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } } }
Form1.cs
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace ConsoleApplication9 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Click(object sender, EventArgs e) { Console.WriteLine("Clicked"); } } }
źródło
Ponowne wskrzeszenie bardzo starego wątku, ponieważ żadna z odpowiedzi tutaj nie działała dla mnie bardzo dobrze.
Znalazłem prosty sposób, który wydaje się dość solidny i prosty. U mnie to zadziałało. Pomysł:
Przykład:
static class Program { [DllImport( "kernel32.dll", SetLastError = true )] static extern bool AllocConsole(); [DllImport( "kernel32", SetLastError = true )] static extern bool AttachConsole( int dwProcessId ); static void Main(string[] args) { bool consoleMode = Boolean.Parse(args[0]); if (consoleMode) { if (!AttachConsole(-1)) AllocConsole(); Console.WriteLine("consolemode started"); // ... } else { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } }
Uwaga: wydaje się, że jeśli spróbujesz napisać do konsoli przed dołączeniem lub przydzieleniem konsoli, to podejście nie działa. Domyślam się, że po raz pierwszy wywołujesz Console.Write / WriteLine, jeśli nie ma jeszcze konsoli, to Windows automatycznie tworzy dla ciebie ukrytą konsolę. (Więc może odpowiedź Anthony'ego na ShowConsoleWindow jest lepsza, gdy już napisałeś do konsoli, a moja odpowiedź jest lepsza, jeśli jeszcze nie napisałeś do konsoli). Ważne jest, aby pamiętać, że to nie działa:
static void Main(string[] args) { Console.WriteLine("Welcome to the program"); //< this ruins everything bool consoleMode = Boolean.Parse(args[0]); if (consoleMode) { if (!AttachConsole(-1)) AllocConsole(); Console.WriteLine("consolemode started"); //< this doesn't get displayed on the parent console // ... } else { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } }
źródło
\bin\Debug>shareCheck.exe /once
i naciskam Enter, pojawia się\bin\Debug>hello. It looks like you started me from an existing console.
wiersz polecenia, a następnie konsola zaczyna wyświetlać: a po zakończeniu programu nie ma wiersza polecenia, więc ostatnia linia wyjściowa i pusty ekran jest trochę szalonyUdało mi się napisać osobno aplikację konsolową, która zrobiła to, co chciałem, skompilować do exe, a następnie zrobić
Process.Start("MyConsoleapp.exe","Arguments")
źródło
Sprawdź ten kod źródłowy. Cały kod z komentarzem - służy do tworzenia konsoli w aplikacji Windows. Uncommented - aby ukryć konsolę w aplikacji konsoli. Od tutaj . (Wcześniej
tutaj.) Projektreg2run
.// Copyright (C) 2005-2015 Alexander Batishchev (abatishchev at gmail.com) using System; using System.ComponentModel; using System.Runtime.InteropServices; namespace Reg2Run { static class ManualConsole { #region DllImport /* [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool AllocConsole(); */ [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool CloseHandle(IntPtr handle); /* [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)] private static extern IntPtr CreateFile([MarshalAs(UnmanagedType.LPStr)]string fileName, [MarshalAs(UnmanagedType.I4)]int desiredAccess, [MarshalAs(UnmanagedType.I4)]int shareMode, IntPtr securityAttributes, [MarshalAs(UnmanagedType.I4)]int creationDisposition, [MarshalAs(UnmanagedType.I4)]int flagsAndAttributes, IntPtr templateFile); */ [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool FreeConsole(); [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)] private static extern IntPtr GetStdHandle([MarshalAs(UnmanagedType.I4)]int nStdHandle); /* [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SetStdHandle(int nStdHandle, IntPtr handle); */ #endregion #region Methods /* public static void Create() { var ptr = GetStdHandle(-11); if (!AllocConsole()) { throw new Win32Exception("AllocConsole"); } ptr = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, 3, 0, IntPtr.Zero); if (!SetStdHandle(-11, ptr)) { throw new Win32Exception("SetStdHandle"); } var newOut = new StreamWriter(Console.OpenStandardOutput()); newOut.AutoFlush = true; Console.SetOut(newOut); Console.SetError(newOut); } */ public static void Hide() { var ptr = GetStdHandle(-11); if (!CloseHandle(ptr)) { throw new Win32Exception(); } ptr = IntPtr.Zero; if (!FreeConsole()) { throw new Win32Exception(); } } #endregion } }
źródło
Właściwie AllocConsole z SetStdHandle w aplikacji GUI może być bezpieczniejszym podejściem. Problem z „przejmowaniem konsoli” już wspomnianym polega na tym, że konsola może w ogóle nie być oknem na pierwszym planie (szczególnie biorąc pod uwagę napływ nowych menedżerów okien w Vista / Windows 7).
źródło
W wind32 aplikacje w trybie konsoli są zupełnie inną bestią niż zwykłe aplikacje odbierające kolejki wiadomości. Są deklarowane i kompilowane inaczej. Możesz utworzyć aplikację, która ma zarówno część konsoli, jak i normalne okno, i ukryć jedną lub drugą. Ale podejrzewaj, że okaże się, że cała sprawa wymaga nieco więcej pracy, niż myślałeś.
źródło
Zgodnie z powyższym cytatem Jeffreya Knighta, gdy tylko napotkam sytuacje, w których potrzebuję wywołań API, aby coś zrobić, zatrzymuję się i zadaję sobie pytanie „czy zbytnio komplikuję?”.
Jeśli chcemy mieć jakiś kod i uruchomić go w trybie graficznego interfejsu użytkownika systemu Windows lub w trybie konsoli, rozważ przeniesienie kodu używanego w obu trybach do biblioteki DLL biblioteki kodu, a następnie posiadanie aplikacji Windows Forms korzystającej z tej biblioteki DLL i konsoli aplikacja korzystająca z tej biblioteki DLL (tj. jeśli w programie Visual Studio masz teraz rozwiązanie obejmujące trzy projekty: bibliotekę z większością kodu, interfejs GUI z samym kodem Win Forms i konsolę z samym kodem konsoli).
źródło
I jeszcze jedna spóźniona odpowiedź. Nie mogłem uzyskać żadnych danych wyjściowych do konsoli utworzonej
AllocConsole
zgodnie z wcześniejszymi sugestiami, więc zamiast tego zaczynam od aplikacji Console . Następnie, jeśli konsola nie jest potrzebna:[DllImport("kernel32.dll")] private static extern IntPtr GetConsoleWindow(); [DllImport("user32.dll")] private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); private const int SW_HIDE = 0; private const int SW_SHOW = 5; [DllImport("user32.dll", SetLastError = true)] static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); public static bool HideConsole() { var hwnd = GetConsoleWindow(); GetWindowThreadProcessId(hwnd, out var pid); if (pid != Process.GetCurrentProcess().Id) // It's not our console - don't mess with it. return false; ShowWindow(hwnd, SW_HIDE); return true; }
źródło