1 つの実行可能ファイルをコンソールと GUI アプリケーションの両方にすることはできますか?
-
20-08-2019 - |
質問
を作りたいです C# 渡されるフラグに応じて CLI または GUI アプリケーションとして実行できるプログラム。これはできるでしょうか?
次の関連質問を見つけましたが、私の状況を正確にカバーしていません。
解決
ジェイデジタルの答え に指差す レイモンド・チェンのブログ, これは、コンソール プログラムと非コンソールの両方のアプリケーションを使用できない理由を説明しています。*
プログラム:OSが知る必要がある プログラムが実行を開始する前に どのサブシステムを使用するか。プログラムの実行が開始されてからでは、戻って他のモードをリクエストするには遅すぎます。
ケイドの答え に指差す コンソールを使用した .Net WinForms アプリケーションの実行に関する記事. 。通話というテクニックを使います。 AttachConsole
プログラムの実行開始後。これにより、プログラムが、プログラムを開始したコマンド プロンプトのコンソール ウィンドウに書き戻すことができるようになります。しかし、その記事のコメントは、私が致命的な欠陥であると考えているものを指摘しています。 子プロセスは実際にはコンソールを制御しません。 コンソールは親プロセスに代わって入力を受け入れ続けますが、親プロセスは、コンソールを他の目的で使用する前に子の実行が完了するまで待機する必要があることを認識しません。
チェン氏の記事が指摘するのは、 他のいくつかのテクニックを説明した Junfeng Zhang による記事.
1つ目は何ですか デベンヴ を使用します。実際には 2 つのプログラムがあることで機能します。1つは devenv.exe, 、これがメインの GUI プログラムであり、もう 1 つは devenv.com, 、コンソールモードのタスクを処理しますが、コンソール以外の方法で使用される場合は、そのタスクを devenv.exe そして出ます。この手法は、次の Win32 ルールに依存しています。 コム ファイルは先に選択されます EXE ファイル拡張子を付けずにコマンドを入力すると、ファイルが削除されます。
これには、Windows スクリプト ホストが行う簡単なバリエーションがあります。2 つの完全に別個のバイナリが提供されます。 wscript.exe そして cscript.exe. 。同様に、Java が提供するのは、 java.exe コンソールプログラムの場合と javaw.exe 非コンソール プログラムの場合。
ジュンフェンの2番目のテクニックは何ですか? イルダズム を使用します。彼はそのプロセスを次のように引用しています。 イルダズムの作者は、両方のモードで実行するときに経験しました。最終的に、それが何をするかは次のとおりです。
- プログラムはコンソール モード バイナリとしてマークされているため、常にコンソールから開始されます。これにより、入力および出力のリダイレクトが通常どおり機能するようになります。
- プログラムにコンソール モードのコマンド ライン パラメータがない場合、プログラム自体が再起動されます。
ただ電話をかけるだけでは十分ではありません FreeConsole
最初のインスタンスがコンソール プログラムでなくなるようにします。それは、プログラムを開始したプロセスが、 cmd.exe, は、コンソール モード プログラムを開始したことを「認識」しており、プログラムの実行が停止するのを待っています。電話をかける FreeConsole
なるだろう イルダズム コンソールの使用を停止しますが、親プロセスは作成されません 始める コンソールを使用して。
したがって、最初のインスタンスは自動的に再起動します (追加のコマンドライン パラメーターを使用すると思います)。電話をかけるとき CreateProcess
, 、試すことができる 2 つの異なるフラグがあります。 DETACHED_PROCESS
そして CREATE_NEW_CONSOLE
, どちらの方法でも、2 番目のインスタンスが親コンソールに接続されなくなります。その後、最初のインスタンスが終了し、コマンド プロンプトがコマンドの処理を再開できるようになります。
この手法の副作用として、GUI インターフェイスからプログラムを起動すると、コンソールが依然として存在することになります。画面上で一瞬点滅してから消えます。
Junfeng の記事の使用に関する部分 編集ビン プログラムのコンソールモードフラグを変更するのは危険だと思います。コンパイラまたは開発環境には、作成するバイナリの種類を制御する設定またはオプションが用意されている必要があります。後で何も変更する必要はありません。
つまり、結論は次のとおりです。 2 つのバイナリを使用することも、コンソール ウィンドウの瞬間的なちらつきを発生させることもできます。. 。どれがより少ない悪であるかを決定したら、実装を選択できます。
*
私は言う 非コンソール の代わりに GUI そうでなければ、それは誤った二分法になるからです。プログラムにコンソールがないからといって、GUI があるとは限りません。サービス アプリケーションがその代表的な例です。また、プログラムにはコンソールを含めることができます そして ウィンドウズ。
他のヒント
このトピックにレイモンドさんのブログをチェックします:
https://devblogs.microsoft.com/oldnewthing/20090101- 00 /?p = 19643 の
彼の最初の文:「あなたがすることはできませんが、偽物にそれを試すことができます」
http://www.csharp411.com/console-output- -のWinFormsアプリケーション/ の
だけのWinForms Application.
のものの前にコマンドライン引数を確認します。
私は、.NETで、単純にメインを除くすべてのアセンブリを共有し、同じ溶液中でコンソールとGUIのプロジェクトを作るために途方もなく容易であることを追加する必要があります。そして、この場合には、あなたはそれをパラメータなしで起動された場合、コマンドライン版は、単にGUIバージョンを起動させることができます。あなたが点滅し、コンソールになるだろう。
あなたが欲しいものを行うための簡単な方法があります。 CLIとGUIの両方を持っている必要がありアプリケーションを書くとき、私はいつもそれを使用しています。あなたはこれが動作するために、「ConsoleApplication」にあなたの「OUTPUTTYPE」を設定する必要があります。
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
});
}
}
}
私は、好ましい技術は、ロブは、2つの実行可能ファイルを使用してののdevenvをの技術を呼んだものだと思います。あなたは(下のリンク参照)で動作するように定型的なコードを持っている場合、これは使用することトリッキーではありません。
この技術は「.COM」は、標準入力/標準出力/標準エラー出力のプロキシで、同じ名前の.exeファイルを起動することを持っているのトリックを使用しています。これはまだコンソールの自由なGUIアプリケーションとして起動することが可能でありながら(特定のコマンドライン引数が検出された潜在的にのみ)コンソール形態呼び出されたとき、プログラムは、コマンドラインモードで予備成形することを可能にするの挙動を与える。
私は dualsubsystemはGoogle Code上と呼ばれるプロジェクトをアップデートすることをにホストされていますこの技術の古いcodeguru溶液とソースコードと実施例バイナリを提供します。
ここでは、私は、問題への単純な.NETのC#ソリューションであると信じるものです。あなたは、コンソールを実行するスイッチを使用して、コマンドラインからのアプリの「バージョン」ときだけ、問題を修正再表示するために、コンソールが(それは、コマンドプロンプトに戻らないとプロセスが稼働し続ける)待ち続けるあなたが持っている場合でも、あなたのコードの末尾にEnvironment.Exit(0)
。この問題を解決するには、ちょうどEnvironment.Exit(0)
を呼び出す前に、このメソッドを呼び出します:
SendKeys.SendWait("{ENTER}");
するとコンソールは、コマンドプロンプトに戻るために必要とプロセスが終了する最終Enterキーを取得します。注:SendKeys.Send()
を呼び出さないでください、またはアプリがクラッシュします。
これは、多くの記事で述べたようにAttachConsole()
を呼び出すことがまだ必要だが、これで私は、コマンドウィンドウのちらつきを取得していない。
ここで(リサイズコードなし)私が作成したサンプルアプリで全体のコードは次のとおりです。
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に行きます。
/*
** 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);
}
私は、コンソールフラッシュを避ける別のアプローチを書かれています。 の<のhref = "http://www.tillett.info/2013/05/13/how-to-create-a-windows-program-that-works-as-both-as-a-guiを参照してください。 -and-コンソール・アプリケーション/」のrel = "nofollowを"> GUIとコンソールアプリケーションの両方として動作するWindowsプログラムを作成する方法のの
私のための静的コンストラクタの作品に実行AllocConsole()