是否可以建構點兩下時不顯示控制台視窗的控制台應用程序?
相關:
我想建構一個控制台應用程序,通常從命令行執行。
但是,當從資源管理器中點兩下它(而不是從 cmd.exe 提示符執行)時,我希望程序不顯示控制台視窗。
我想避免這種情況:
可能嗎?
編輯我想另一種方法是,程序是否有可能知道它是如何被呼叫的——無論是通過點兩下還是通過命令行?
我在 Windows 上的 .NET 中工作。
**編輯 2:**從這篇Old New Thing部落格文章中,我學到了一些好東西。以下是我現在所知道的…
在 Windows 中,EXE 文件被標記為 GUI 或非 GUI。對於 csc.exe,使用
/target:winexe或進行選擇/target:exe。在程序中的第一條指令執行之前,Windows 核心會設置執行環境。此時,如果 EXE 被標記為 GUI,核心將程序的標準輸入/標準輸出設置為 NULL,如果非 GUI(命令行),核心創建一個控制台並將程序的標準輸入/標準輸出設置為安慰。啟動程序時,如果沒有 stdin/stdout (==
/target:winexe),則呼叫立即返回。因此,從 cmd.exe 啟動一個 gui 應用程序,您將立即返回您的 cmd 提示符。如果有標準輸入/標準輸出,並且如果從 cmd.exe 執行,則父 cmd.exe 等待程序退出。“立即返回”很重要,因為如果您編寫一個 GUI 應用程序以附加到其父控制台,您將能夠執行 console.writeline 等。但是 cmd.exe 提示符是活動的。使用者可以鍵入新命令、啟動新程序等。換句話說,從一個winexe,簡單地附加到父控制台
AttachConsole(-1)不會“把它變成”一個控制台應用程序。在這一點上,我認為允許應用程序在從 cmd.exe 呼叫時使用控制台並且在點兩下時不使用它的唯一方法是將 exe 定義為正常控制台 exe (
/target:exe),並且如果合適,在啟動時*隱藏視窗。*您仍然會短暫出現一個控制台視窗。我仍然不知道如何知道它是從資源管理器還是從 cmd.exe 啟動的,但我越來越接近了..
答案
無法建構不顯示控制台視窗的控制台應用程序。
可以建構一個非常快速地隱藏其視窗的控制台應用程序,但不會太快以至於好像視窗從未出現過一樣。
現在,要確定控制台應用程序是否從資源管理器啟動,一些人建議查看它正在執行的控制台
(來自mgb 的回答和知識庫文章 99115):
int left = Console.CursorLeft; int top = Console.CursorTop; bool ProcessWasRunFromExplorer = (left==0 && top==0);這會告訴您該程序是否在其自己的控制台中啟動,而不是它是否是資源管理器。在資源管理器中點兩下可以做到這一點,但應用程序中的 Start.Process() 也會做同樣的事情。
如果您想以不同的方式處理這些情況,請使用它來了解父程序的名稱:
System.Console.WriteLine("Process id: {0}", Process.GetCurrentProcess().Id); string name = Process.GetCurrentProcess().ProcessName ; System.Console.WriteLine("Process name: {0}", name); PerformanceCounter pc = new PerformanceCounter("Process", "Creating Process Id", name); Process p = Process.GetProcessById((int)pc.RawValue); System.Console.WriteLine("Parent Process id: {0}", p.Id); System.Console.WriteLine("Parent Process name: {0}", p.ProcessName); // p.ProcessName == "cmd" or "Explorer" etc要在程序啟動後快速隱藏視窗,請使用以下命令:
private static readonly int SW_HIDE= 0; [System.Runtime.InteropServices.DllImport("user32.dll")] private static extern Boolean ShowWindow(IntPtr hWnd, Int32 nCmdShow); .... { IntPtr myHandle = Process.GetCurrentProcess().MainWindowHandle; ShowWindow(myHandle, SW_HIDE); }如果您生成一個
winexe(WinForms 應用程序),並在適當的時候選擇附加到父控制台AttachConsole(-1),則您不會獲得與正常控制台應用程序等效的功能。對於 winexe,父程序(如 cmd.exe)將在啟動 GUI 應用程序後立即返回命令提示符。換句話說,命令提示符處於活動狀態並準備好輸入,而剛剛啟動的程序可能正在發出輸出。這很令人困惑,可能僅對調試 winforms 應用程序有用。這對我有用。
請參閱Win32 控制台應用程序能否檢測到它是否已從資源管理器執行?
或者我認為官方的方法是檢查父程序是cmd.exe還是explorer.exe
因此,我編寫了帶有 GUI 和 CLI 的工具。困難的部分是弄清楚要打開哪一個——不過,在我們的例子中,CLI 版本需要參數,所以如果沒有任何參數,我就打開 GUI。然後,如果他們確實需要控制台,請呼叫如下所示的函式:
private const int ATTACH_PARENT_PROCESS = -1; private const int ERROR_INVALID_HANDLE = 6; [DllImport("kernel32.dll", SetLastError = true)] static extern bool AttachConsole(int dwProcessId); [DllImport("kernel32.dll")] static extern bool AllocConsole(); [DllImport("kernel32.dll")] static extern bool FreeConsole(); private static bool StartConsole() { if (!AttachConsole(ATTACH_PARENT_PROCESS)) // try connecting to an existing console { if (Marshal.GetLastWin32Error() == ERROR_INVALID_HANDLE) // we don't have a console yet { if (!AllocConsole()) // couldn't create a new console, either return false; } else return false; // some other error } return true; }返回是否創建了控制台。完成後不要忘記 FreeConsole()!
當然,在我們的例子中,如果我們不創建控制台,我們會創建一個 GUI。不過,創建控制台或不創建UI都同樣容易。
編輯:當然,這完全沒有回答我開始編寫時不存在的編輯中的問題。除此之外,我們的 hack 只是檢查它是否使用命令行參數呼叫。
