Win32应用程序使用printf将输出写入控制台

4

我有一个使用win32应用程序开发的exe文件。当我双击运行exe时,GUI界面应该出现,当我从命令提示符中调用exe时,输出应该出现在命令控制台中。

我的问题是如何使用printf将输出重定向到命令窗口?我能够使用AllocConsole()在命令窗口中打印,但会创建一个新的命令窗口并将输出重定向到新窗口。我想在使用Win32应用程序调用exe的同一命令窗口中打印输出。感谢任何帮助。


3
AttachConsole是一个Windows API函数,它允许应用程序将自己附加到另一个控制台进程。这个函数可以用于调试和诊断目的,使开发人员能够查看和操作其他进程的控制台输出。 - IInspectable
你是在问如何在命令提示符下运行时像一个命令行程序一样操作,并且在双击时以图形界面方式运行吗? - crashmstr
虽然不完全相同,但与https://dev59.com/v4Dba4cB1Zd3GeqPFo2k#29687965非常相似。 - Adrian McCarthy
没有好的方法来做这个。构建两个可执行文件,一个用于图形界面,一个用于命令行。 - Harry Johnston
5个回答

5

wilx 的基础上,您可以使用 AttachConsole(...); 来实现连接。因此,如果只有已经存在的控制台才能进行连接,则可以使用以下代码:

bool bAttachToConsole()
{
    if (!AttachConsole(ATTACH_PARENT_PROCESS))
    {
        if (GetLastError() != ERROR_ACCESS_DENIED) //already has a console
        {
            if (!AttachConsole(GetCurrentProcessId()))
            {
                DWORD dwLastError = GetLastError();
                if (dwLastError != ERROR_ACCESS_DENIED) //already has a console
                {
                    return false;
                }
            }
        }
    }

    return true;
}

然后在您的WinMain函数中,您可以这样做:
if (bAttachToConsole())
{
    //do your io with STDIN/STDOUT
    // ....
}
else
{
    //Create your window and do IO via your window
    // ....
}

此外,您需要“修复”C标准IO句柄以使用新控制台,请参阅以下文章,以获得如何执行此操作的绝佳示例。

2
问题是命令行界面不会等待您的程序退出,因此您的输出将与命令行提示混在一起。如果您需要输入,问题会更严重。 - Harry Johnston
好主意,Harry。从一个控制台应用程序开始,并通过参数隐藏控制台可能是实现只使用一个可执行文件关闭的唯一方法。 - Dustin

1
这个几乎做到了你想要的:
// Win32Project1.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include <stdio.h>  // printf, _dup2
#include <io.h>     // _open_osfhandle

void SetupConsole()
{
    AttachConsole(ATTACH_PARENT_PROCESS);
    HANDLE hConIn = GetStdHandle(STD_INPUT_HANDLE);
    int fd0 = _open_osfhandle((intptr_t)hConIn, 0);
    _dup2(fd0, 0);
    HANDLE hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
    int fd1 = _open_osfhandle((intptr_t)hConOut, 0);
    _dup2(fd1, 1);
    HANDLE hConErr = GetStdHandle(STD_ERROR_HANDLE);
    int fd2 = _open_osfhandle((intptr_t)hConErr, 0);
    _dup2(fd2, 2);
}

WNDPROC g_pOldProc;

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if (uMsg == WM_CLOSE)
    {
        PostQuitMessage(0);
        return 0;
    }

    return CallWindowProc(g_pOldProc, hwnd, uMsg, wParam, lParam);
}

void GUI(HINSTANCE hInstance)
{
    HWND hWnd = CreateWindow(
                        _T("EDIT"),
                        _T("GUI"),
                        WS_OVERLAPPEDWINDOW|WS_VISIBLE,
                        100, 100, 200,200,
                        NULL,
                        NULL,
                        hInstance,
                        NULL
                        );
    g_pOldProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)&WindowProc);

    SetWindowText(hWnd, _T("Hello world."));

    MSG m;
    while (GetMessage(&m, NULL, 0, 0))
    {
        DispatchMessage(&m);
    }

    DestroyWindow(hWnd);
}

void Console()
{
    SetupConsole();
    printf("Hello world.");
}

int APIENTRY _tWinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPTSTR    lpCmdLine,
                       int       nCmdShow)
{
    HANDLE hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
    if (!hConOut)
        GUI(hInstance);
    else
        Console();

    return 0;
}

1
我检查了这里提交的所有解决方案,它们对我都没有用。您可以始终创建(或更改为)一个控制台子系统。但是,如果您想要从未分类为控制台应用程序的WIN32应用程序中启用对控制台的写入,此代码将起作用。调用此函数后,您只需使用printf(或wprintf)。我的代码使用AllocConsole(),然后使用AttachConsole(),将当前进程ID(通过调用GetProcessID获得)传递给它。
void enableConsole()
{
    AllocConsole();
    AttachConsole(GetCurrentProcessId());
    HWND Handle = GetConsoleWindow();
    freopen("CON", "w", stdout);
}

0

试试这个:

// Win32Project1.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include <stdio.h>  // printf
#include <io.h>     // _open_osfhandle, _dup2

void SetupConsole()
{
    BOOL bCreated = AllocConsole();
    if (!bCreated)
        return; // We already have a console.

    HANDLE hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
    int fd = _open_osfhandle((intptr_t)hConOut, 0);
    _dup2(fd, 1);

}

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    SetupConsole();
    printf("Hello world!");
    Sleep(10000);

    return 0;
}

2
AllocConsole只有在你想创建一个新的控制台时才有用。楼主希望使用现有的控制台(如果有的话),或者根本不使用控制台(如果没有的话)。 - Harry Johnston
我尝试使用AttachConsole(ATTACH_PARENT_PROCESS),它会打开一个新的命令行窗口。我的问题是,我打开一个命令行窗口并转到我的exe所在的位置(C:\test.exe),然后从同一个命令行窗口运行exe。在包含AttachConsole()后,输出会显示在新的命令行窗口中。 - user1881297

0
尝试使用AttachConsole(ATTACH_PARENT_PROCESS)(或使用PID)来附加到现有的控制台。

虽然我已经发布了我的答案,但说实话,我不确定我是否完全理解你的问题。我有一些代码,其中有一个注释(在AllocConsole()上面):

我们忽略这里的返回值。如果我们已经有一个控制台,它会失败。

你确定你不能像我一样无条件地使用AllocConsole()吗?


AllocConsole只有在您想要创建新控制台时才有用。如果存在已有的控制台,OP想要使用它;如果没有控制台,OP希望不使用控制台。 - Harry Johnston
我尝试使用AttachConsole(ATTACH_PARENT_PROCESS)函数,它会打开一个新的命令行窗口。我的问题是,我在一个命令行窗口中打开并进入我的exe所在的位置(C:\test.exe),然后从同一个命令行窗口运行exe文件。当包含AttachConsole()函数时,输出会显示在新的命令行窗口中。 - user1881297
@user1881297:嗯,这不应该发生。AttachConsole() 不应该创建一个新的控制台!你能给我们提供一个最小完整可复现示例吗? - Harry Johnston

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接