在Microsoft documentation About Consoles中有这样的文字:
系统在启动控制台进程时会创建一个新控制台,
入口点是 main 函数的字符模式进程。为了
例如,系统在启动命令时会创建一个新控制台
处理器 cmd.exe。当命令处理器启动一个新控制台时
进程,用户可以指定系统是否新建控制台
对于新进程或者它是否继承了命令处理器的
控制台。
文档表明,为了不显示控制台,您需要创建一个不是控制台应用程序的可执行文件。
第二个更好的答案
在考虑了更多之后,我意识到我的第一个答案可以简化。您可以编写一个作为非控制台应用程序的 Windows GUI 应用程序,而不是一个启动非控制台进程的 Windows GUI 应用程序。
我认为控制这一点的两件事是:(1) C++ 源代码和指定的主入口点以及 (2) 确定可执行环境和入口点的 /SUBSYSTEM 链接器选项链接器用于应用程序。
有关 /SUBSYSTEM 链接器指令的详细信息,请参阅这篇 Microsoft 文章 /SUBSYSTEM (Specify Subsystem)。
/SUBSYSTEM 选项指定可执行文件的环境。
子系统的选择会影响入口点符号(或入口点
函数)链接器将选择。
使用 Visual Studio 2019,我创建了一个 Windows 桌面应用程序,然后我削减了它,消除了所有 Windows GUI 应用程序源代码,只保留了 wWinMain() 入口点。然后我修改了这个应用程序以使用标准 C++ 库来创建一个简单的文本文件并退出以证明这是可行的。
我最终得到了以下 C++ 测试应用程序的源代码。
// cmndline_non_console.cpp : Defines the entry point for the application.
//
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>
#include <iostream>
#include <fstream>
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
std::ofstream myfile;
myfile.open("test.txt");
myfile << "Writing this to a file. more\n";
myfile.close();
return 0;
}
链接的解决方案属性使用 /SUBSYSTEM:WINDOWS。
第一个答案
此Microsoft documentation Windows Console and Terminal Ecosystem Roadmap 描述了 Windows 控制台的历史以及对较新的伪控制台和虚拟终端功能的更改
这是一个简化的 GUI,它启动、启动一个没有控制台的子进程,然后退出。它使用CreateProcess() Windows API 函数。请参阅The Command CreateProcess C++,另请参阅Creating Processes in the Microsoft Docs。
我使用 Visual Studio 2019 IDE 创建了主应用程序,创建了一个新的 Windows 桌面 GUI 应用程序,然后对生成的代码进行了一些更改。您可能可以减少更多。依赖于几个标准生成的文件:framework.h、Resource.h 和 targetver.h。头文件startconsoleless.h 只包含Resource.h 的include 指令。
-
InitInstance() 函数返回FALSE 以便应用程序退出
-
InitInstance() 函数使用CActualApp 类在没有控制台的情况下创建新进程
-
About 对话处理因未使用而被删除
此示例使用硬编码命令行来指示要启动的可执行文件。需要使用 wchar_t 字符数组,否则 CreateProcess() 函数将根据所引用的 SO 帖子触发异常。
文件 startconsoless.cpp
// startconsoleless.cpp : Defines the entry point for the application.
//
#include "framework.h"
#include "startconsoleless.h"
#include "CActualApp.h"
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
CActualApp myApp;
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
// Initialize global strings
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_STARTCONSOLELESS, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_STARTCONSOLELESS));
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_STARTCONSOLELESS));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_STARTCONSOLELESS);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
// Comment out following code in order to not show the created window.
// ShowWindow(hWnd, nCmdShow);
// UpdateWindow(hWnd);
// At this point we can now have our main thread of this process
// start another thread to do the work we want to do
// See https://stackoverflow.com/questions/49610277/the-command-createprocess-c
wchar_t appLine[] = L"C:\\WINDOWS\\system32\\notepad.exe";
myApp.StartAnApp(appLine);
// return FALSE so that this Windows GUI app will exit after
// starting the child process.
return FALSE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
这是上面用来启动应用程序的一个非常简单的类。
文件 CAcutalApp.h
#pragma once
class CActualApp
{
public:
CActualApp() {}
~CActualApp() {}
int StartAnApp(wchar_t * pAppComndLine);
};
文件 CActualApp.cpp
#include "framework.h"
#include "CActualApp.h"
int CActualApp::StartAnApp(wchar_t * pAppComndLine)
{
// See https://stackoverflow.com/questions/49610277/the-command-createprocess-c
int retStatus = 0;
PROCESS_INFORMATION processInformation = { 0 };
STARTUPINFO startupInfo = { 0 };
startupInfo.cb = sizeof(startupInfo);
BOOL apStart = CreateProcess(NULL, pAppComndLine, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInformation);
if (!apStart)
{
// create process failed
DWORD errorCode = GetLastError();
retStatus = errorCode;
}
else {
// close process and thread handles
CloseHandle(processInformation.hProcess);
CloseHandle(processInformation.hThread);
}
return retStatus;
}