Dot-Net

是否可以建構點兩下時不顯示控制台視窗的控制台應用程序?

  • October 6, 2009

相關:

我應該在我的應用程序中包含命令行模式嗎?

如何抓取父程序標準輸出?

控制台應用程序是否可以檢測它是否已從資源管理器執行?

我想建構一個控制台應用程序,通常從命令行執行。

但是,當從資源管理器中點兩下它(而不是從 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 只是檢查它是否使用命令行參數呼叫。

引用自:https://stackoverflow.com/questions/1528152