如何在 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.hResource.htargetver.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++ 可执行文件

如何在 Windows 上使用 Visual C++ 强制加载链接库

在 2D 向量上执行单行算术以找到简化的行形式 C++

如何使用 C++ 在不同的线程上调用函数?

如何在 Android 上重新启动 Qt 或 c++ 中的程序?