如何在 C++ 上执行 FreeConsole() 但更快
Posted
技术标签:
【中文标题】如何在 C++ 上执行 FreeConsole() 但更快【英文标题】:How can I do the FreeConsole() but faster on c++ 【发布时间】:2021-02-13 12:17:52 【问题描述】:我非常想知道如何才能实现与FreeConsole()
Windows API function 相同的功能,但速度更快。
FreeConsole()
确实隐藏了控制台,但是控制台窗口在隐藏之前会闪烁打开。如果可能的话,我希望控制台根本不打开。谢谢。
基于 cmets 更新
我的操作系统是 Windows。我使用带有 windows.h
标头的 gcc 编译器,并且正在使用 Visual Studio Code。
【问题讨论】:
那不是标准的 c++ 函数。你的操作系统、框架是什么? 请使用FreeConsole()
使用的源代码更新您的帖子,向我们展示您是如何做到的。
@πάνταῥεῖ 它的 Windows API。 docs.microsoft.com/en-us/windows/console/freeconsole
@RichardChambers 我知道,当然,OP 应该提到这一点,或者相应地标记问题。
请在问题中添加更多信息,例如您的环境!
【参考方案1】:
在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://***.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://***.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;
【讨论】:
感谢您对问题的详细回答和编辑!来晚了,我明天试试代码。再次感谢! @HalHana 让我知道它是如何为您工作的。我的测试是使用 Visual Studio 2019 进行的,它似乎运行良好。想法是将调用StartAnApp()
中的路径修改为您要启动的实际应用程序。这是一个命令行,因此您应该能够在字符串中添加参数以及可执行路径。
成功了!谢谢。抱歉,花了一些时间回复。这是忙碌的一周......以上是关于如何在 C++ 上执行 FreeConsole() 但更快的主要内容,如果未能解决你的问题,请参考以下文章
如何安装在 windowsservercore Docker 映像上执行本机 C++ DLL/EXE 所需的所有依赖二进制文件
如何通过 Makefile 将 cap_net_raw 功能添加到 Linux 上的 c++ 可执行文件