it-swarm.com.ru

Может ли один исполняемый файл быть одновременно консольным и графическим приложением?

Я хочу сделать C # программу, которая может быть запущена как приложение CLI или GUI в зависимости от того, какие флаги передаются в него. Можно ли это сделать?

Я нашел эти связанные вопросы, но они не совсем охватывают мою ситуацию:

72
BCS

Ответ Jdigital указывает на блог Раймонда Чена , в котором объясняется, почему у вас не может быть приложения, которое является одновременно консольной и не консольной *-программой: ОС должна знать до запуска программы) выполняется какую подсистему использовать. Как только программа запустилась, уже поздно возвращаться и запрашивать другой режим.

Ответ Кейда указывает на статью о запуске приложения .Net WinForms с консолью . Он использует технику вызова AttachConsole после запуска программы. Это позволяет программе писать обратно в консольное окно командной строки, которая запустила программу. Но комментарии в этой статье указывают на то, что я считаю фатальным недостатком: Дочерний процесс на самом деле не контролирует консоль. Консоль продолжает принимать входные данные от имени родительского процесса и родительского процесса. не знает, что ему следует дождаться, пока ребенок закончит бег, прежде чем использовать консоль для других целей.

Статья Чена указывает на статью Junfeng Zhang, которая объясняет несколько других техник .

Первый - то, что использует devenv. Это работает на самом деле две программы. Одним из них является devenv.exe, который является основной программой с графическим интерфейсом, а другим является devenv.com, который обрабатывает задачи в режиме консоли, но если он используется в не консольном режиме. таким же образом он передает свои задачи devenv.exe и завершает работу. Этот метод основан на правиле Win32, согласно которому файлы com выбираются перед файлами exe при вводе команды без расширения файла.

Существует более простой вариант, чем Windows Script Host. Он предоставляет два совершенно разных двоичных файла: wscript.exe и cscript.exe. Аналогично, Java предоставляет Java.exe для консольных программ и javaw.exe для неконсольных программ.

Второй метод Junfeng - это то, что использует ildasm. Он цитирует процесс, через который прошел автор ildasm при его запуске в обоих режимах. В конечном счете, вот что он делает:

  1. Программа помечена как двоичный файл в режиме консоли, поэтому она всегда начинается с консоли. Это позволяет перенаправлению ввода и вывода работать как обычно.
  2. Если программа не имеет параметров командной строки в режиме консоли, она перезапускается сама.

Недостаточно просто вызвать FreeConsole, чтобы первый экземпляр перестал быть консольной программой. Это связано с тем, что процесс, который запустил программу, cmd.exe, «знает», что он запустил программу в режиме консоли и ожидает остановки программы. Вызов FreeConsole заставил бы ildasm прекратить использование консоли, но это не заставило бы родительский процесс start использовать консоль.

Таким образом, первый экземпляр перезапускается сам (с дополнительным параметром командной строки, я полагаю). Когда вы вызываете CreateProcess, нужно попробовать два разных флага: DETACHED_PROCESS И CREATE_NEW_CONSOLE , каждый из которых гарантирует, что второй экземпляр не будет подключен к родительской консоли. После этого первый экземпляр может завершиться и разрешить команде Prompt возобновить обработку команд.

Побочным эффектом этого метода является то, что при запуске программы из интерфейса графического интерфейса все равно будет консоль. Он будет мигать на экране на мгновение, а затем исчезнет.

Часть в статье Junfeng об использовании editbin для изменения флага режима консоли программы, я думаю, представляет собой красную сельдь. Ваш компилятор или среда разработки должны предоставить параметр или опцию для управления типом двоичного файла, который он создает. Там не должно быть необходимости что-либо изменить потом.

Суть в том, что у вас может быть два двоичных файла или кратковременное мерцание окна консоли. Как только вы решите, какое меньшее зло, у вас есть выбор реализации.

* Я говорю не консольный вместо GUI, потому что в противном случае это ложная дихотомия. Тот факт, что программа не имеет консоли, не означает, что она имеет графический интерфейс. Приложение службы является ярким примером. Также программа может иметь консольные и окна.

85
Rob Kennedy

Посмотрите блог Раймонда на эту тему:

http://blogs.msdn.com/oldnewthing/archive/2009/01/01/9259142.aspx

Его первое предложение: «Вы не можете, но вы можете попытаться подделать это».

10
jdigital

http://www.csharp411.com/console-output-from-winforms-application/

Просто проверьте аргументы командной строки перед WinForms Application..

Я должен добавить, что в .NET просто смешно создавать консоль и проекты с графическим интерфейсом в одном решении, которые совместно используют все свои сборки, кроме основной. И в этом случае вы можете заставить версию командной строки просто запустить версию GUI, если она запускается без параметров. Вы бы получили мигающую консоль.

6
Cade Roux

Существует простой способ сделать то, что вы хотите. Я всегда использую его при написании приложений, которые должны иметь как CLI, так и GUI. Вы должны установить свой «OutputType» в «ConsoleApplication», чтобы это работало.

class Program {
  [DllImport("kernel32.dll", EntryPoint = "GetConsoleWindow")]
  private static extern IntPtr _GetConsoleWindow();

  /// <summary>
  /// The main entry point for the application.
  /// </summary>
  [STAThread]
  static void Main(string[] args) {
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    /*
     * This works as following:
     * First we look for command line parameters and if there are any of them present, we run the CLI version.
     * If there are no parameters, we try to find out if we are run inside a console and if so, we spawn a new copy of ourselves without a console.
     * If there is no console at all, we show the GUI.
     * We make an exception if we find out, that we're running inside visual studio to allow for easier debugging the GUI part.
     * This way we're both a CLI and a GUI.
     */
    if (args != null && args.Length > 0) {

      // execute CLI - at least this is what I call, passing the given args.
      // Change this call to match your program.
      CLI.ParseCommandLineArguments(args);

    } else {
      var consoleHandle = _GetConsoleWindow();

      // run GUI
      if (consoleHandle == IntPtr.Zero || AppDomain.CurrentDomain.FriendlyName.Contains(".vshost"))

        // we either have no console window or we're started from within visual studio
        // This is the form I usually run. Change it to match your code.
        Application.Run(new MainForm());
      else {

        // we found a console attached to us, so restart ourselves without one
        Process.Start(new ProcessStartInfo(Assembly.GetEntryAssembly().Location) {
          CreateNoWindow = true,
          UseShellExecute = false
        });
      }
    }
  }
5
user1566352

Я думаю, что предпочтительной техникой является то, что Роб назвал техникой devenv с использованием двух исполняемых файлов: средства запуска «.com» и оригинального «.exe». Это не так сложно использовать, если у вас есть стандартный код для работы (см. Ссылку ниже).

Техника использует трюки, чтобы этот «.com» был прокси для stdin/stdout/stderr и запускал файл с тем же именем .exe. Это дает поведение, позволяющее программе выполнять преформу в режиме командной строки при вызове из консоли (возможно, только при обнаружении определенных аргументов командной строки), при этом все еще имея возможность запускаться как приложение с графическим интерфейсом без консоли.

Я разместил проект под названием dualsubsystem в Google Code , который обновляет старое решение Codeguru для этой техники и предоставляет исходный код и рабочие примеры двоичных файлов.

3
gabeiscoding

Вот что я считаю простым решением проблемы .NET C #. Просто для повторения проблемы, когда вы запускаете консольную «версию» приложения из командной строки с переключателем, консоль продолжает ждать (она не возвращается к командной строке и процесс продолжает работать), даже если у вас есть Environment.Exit(0) в конце вашего кода. Чтобы это исправить, перед вызовом Environment.Exit(0), вызовите это:

SendKeys.SendWait("{ENTER}");

Затем консоль получает последний ключ Enter, который необходимо вернуть в командную строку, и процесс завершается. Примечание: не вызывайте SendKeys.Send(), иначе приложение вылетит.

По-прежнему необходимо вызывать AttachConsole(), как упоминалось во многих постах, но при этом я не получаю мерцания окна команды при запуске версии приложения WinForm.

Вот весь код в образце приложения, которое я создал (без кода WinForms):

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

namespace ConsoleWriter
{
    static class Program
    {
        [DllImport("kernel32.dll")]
        private static extern bool AttachConsole(int dwProcessId);
        private const int ATTACH_PARENT_PROCESS = -1;

        [STAThread]
        static void Main(string[] args)
        {
            if(args.Length > 0 && args[0].ToUpperInvariant() == "/NOGUI")
            {
                AttachConsole(ATTACH_PARENT_PROCESS);
                Console.WriteLine(Environment.NewLine + "This line prints on console.");

                Console.WriteLine("Exiting...");
                SendKeys.SendWait("{ENTER}");
                Environment.Exit(0);
            }
            else
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Form1());
            }
        }
    }
}

Надеюсь, что это поможет кому-то также потратить дни на эту проблему. Спасибо за подсказку, перейдите на @dantill.

3
LTDev
/*
** dual.c    Runs as both CONSOLE and GUI app in Windows.
**
** This solution is based on the "Momentary Flicker" solution that Robert Kennedy
** discusses in the highest-rated answer (as of Jan 2013), i.e. the one drawback
** is that the console window will briefly flash up when run as a GUI.  If you
** want to avoid this, you can create a shortcut to the executable and tell the
** short cut to run minimized.  That will minimize the console window (which then
** immediately quits), but not the GUI window.  If you want the GUI window to
** also run minimized, you have to also put -minimized on the command line.
**
** Tested under MinGW:  gcc -o dual.exe dual.c -lgdi32
**
*/
#include <windows.h>
#include <stdio.h>

static int my_win_main(HINSTANCE hInstance,int argc,char *argv[],int iCmdShow);
static LRESULT CALLBACK WndProc(HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam);
static int win_started_from_console(void);
static BOOL CALLBACK find_win_by_procid(HWND hwnd,LPARAM lp);

int main(int argc,char *argv[])

    {
    HINSTANCE hinst;
    int i,gui,relaunch,minimized,started_from_console;

    /*
    ** If not run from command-line, or if run with "-gui" option, then GUI mode
    ** Otherwise, CONSOLE app.
    */
    started_from_console = win_started_from_console();
    gui = !started_from_console;
    relaunch=0;
    minimized=0;
    /*
    ** Check command options for forced GUI and/or re-launch
    */
    for (i=1;i<argc;i++)
        {
        if (!strcmp(argv[i],"-minimized"))
            minimized=1;
        if (!strcmp(argv[i],"-gui"))
            gui=1;
        if (!strcmp(argv[i],"-gui-"))
            gui=0;
        if (!strcmp(argv[i],"-relaunch"))
            relaunch=1;
        }
    if (!gui && !relaunch)
        {
        /* RUN AS CONSOLE APP */
        printf("Console app only.\n");
        printf("Usage:  dual [-gui[-]] [-minimized].\n\n");
        if (!started_from_console)
            {
            char buf[16];
            printf("Press <Enter> to exit.\n");
            fgets(buf,15,stdin);
            }
        return(0);
        }

    /* GUI mode */
    /*
    ** If started from CONSOLE, but want to run in GUI mode, need to re-launch
    ** application to completely separate it from the console that started it.
    **
    ** Technically, we don't have to re-launch if we are not started from
    ** a console to begin with, but by re-launching we can avoid the flicker of
    ** the console window when we start if we start from a shortcut which tells
    ** us to run minimized.
    **
    ** If the user puts "-minimized" on the command-line, then there's
    ** no point to re-launching when double-clicked.
    */
    if (!relaunch && (started_from_console || !minimized))
        {
        char exename[256];
        char buf[512];
        STARTUPINFO si;
        PROCESS_INFORMATION pi;

        GetStartupInfo(&si);
        GetModuleFileNameA(NULL,exename,255);
        sprintf(buf,"\"%s\" -relaunch",exename);
        for (i=1;i<argc;i++)
            {
            if (strlen(argv[i])+3+strlen(buf) > 511)
                break;
            sprintf(&buf[strlen(buf)]," \"%s\"",argv[i]);
            }
        memset(&pi,0,sizeof(PROCESS_INFORMATION));
        memset(&si,0,sizeof(STARTUPINFO));
        si.cb = sizeof(STARTUPINFO);
        si.dwX = 0; /* Ignored unless si.dwFlags |= STARTF_USEPOSITION */
        si.dwY = 0;
        si.dwXSize = 0; /* Ignored unless si.dwFlags |= STARTF_USESIZE */
        si.dwYSize = 0;
        si.dwFlags = STARTF_USESHOWWINDOW;
        si.wShowWindow = SW_SHOWNORMAL;
        /*
        ** Note that launching ourselves from a console will NOT create new console.
        */
        CreateProcess(exename,buf,0,0,1,DETACHED_PROCESS,0,NULL,&si,&pi);
        return(10); /* Re-launched return code */
        }
    /*
    ** GUI code starts here
    */
    hinst=GetModuleHandle(NULL);
    /* Free the console that we started with */
    FreeConsole();
    /* GUI call with functionality of WinMain */
    return(my_win_main(hinst,argc,argv,minimized ? SW_MINIMIZE : SW_SHOWNORMAL));
    }


static int my_win_main(HINSTANCE hInstance,int argc,char *argv[],int iCmdShow)

    {
    HWND        hwnd;
    MSG         msg;
    WNDCLASSEX  wndclass;
    static char *wintitle="GUI Window";

    wndclass.cbSize        = sizeof (wndclass) ;
    wndclass.style         = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc   = WndProc;
    wndclass.cbClsExtra    = 0 ;
    wndclass.cbWndExtra    = 0 ;
    wndclass.hInstance     = hInstance;
    wndclass.hIcon         = NULL;
    wndclass.hCursor       = NULL;
    wndclass.hbrBackground = NULL;
    wndclass.lpszMenuName  = NULL;
    wndclass.lpszClassName = wintitle;
    wndclass.hIconSm       = NULL;
    RegisterClassEx (&wndclass) ;

    hwnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,wintitle,0,
                          WS_VISIBLE|WS_OVERLAPPEDWINDOW,
                          100,100,400,200,NULL,NULL,hInstance,NULL);
    SetWindowText(hwnd,wintitle);
    ShowWindow(hwnd,iCmdShow);
    while (GetMessage(&msg,NULL,0,0))
        {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
        }
    return(msg.wParam);
    }


static LRESULT CALLBACK WndProc (HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam)

    {
    if (iMsg==WM_DESTROY)
        {
        PostQuitMessage(0);
        return(0);
        }
    return(DefWindowProc(hwnd,iMsg,wParam,lParam));
    }


static int fwbp_pid;
static int fwbp_count;
static int win_started_from_console(void)

    {
    fwbp_pid=GetCurrentProcessId();
    if (fwbp_pid==0)
        return(0);
    fwbp_count=0;
    EnumWindows((WNDENUMPROC)find_win_by_procid,0L);
    return(fwbp_count==0);
    }


static BOOL CALLBACK find_win_by_procid(HWND hwnd,LPARAM lp)

    {
    int pid;

    GetWindowThreadProcessId(hwnd,(LPDWORD)&pid);
    if (pid==fwbp_pid)
        fwbp_count++;
    return(TRUE);
    }
2
willus
1
dantill

Запуск AllocConsole () в статическом конструкторе у меня работает

0
Shaggy