【问题标题】:Open & write to console from a C++ DLL从 C++ DLL 打开并写入控制台
【发布时间】:2013-05-11 19:07:21
【问题描述】:

作为开始我的问题之前的快速说明,我对 C++ 尤其是 DLL 还是很陌生。

我目前正在 VS2012 中编写 C++ DLL,用于程序。请注意,我没有特定程序的源代码访问权限。 当相关程序要求执行某些操作时,我想将一些输出从 DLL 写入单独的控制台窗口。

我的问题是,我将如何去做这样的事情?为了明确我想要什么,我希望在 DLL 附加到程序后立即打开一个单独的控制台窗口,然后让它将输出写入控制台。当 DLL 与程序分离时,它也应该关闭控制台窗口。

到目前为止,我的猜测是在我的解决方案中创建一个单独的 Win32 控制台应用程序项目,然后可能在我的 DLL 中引用它,并让它从中执行某些方法,输出包含在调用的参数中。我不知道该怎么做,特别是因为我希望这个单独的控制台项目包含在同一个 DLL 中。

感谢您的任何意见。

【问题讨论】:

  • 如果您还没有打开控制台,AllocConsole 会给您一个控制台。不知道这是否适用于 DLL。
  • 当我对此进行一些谷歌搜索时,我确实看到了AllocConsole,但我不知道如何以我想要的方式实现它。 (正如我所说,我对 c++ 还很陌生,关于这个主题的 MSDN 文档不是很广泛)
  • 嗯,msdn 说AllocConsole initializes standard input, standard output, and standard error handles for the new console,所以你可能只使用std::cout。但我从来没有这样做过,所以不知道它是否有效。
  • 我认为你不能使用 cout,因为它是一个 DLL。
  • 使用 AllocConsole 的每个进程只能打开一个控制台。这意味着,如果您尝试在 dll 中调用 AllocConsole,而调用进程已经有一个,它将失败。我能看到这项工作的唯一方法是在 dll 中创建一个新进程。不过我很好奇,为什么你不能只写一个日志文件?

标签: c++ visual-c++ dll console


【解决方案1】:

以下课程将让您了解您需要做什么。 在构建时,CConsole 尝试使用 AllocConsole 为进程创建一个新的控制台。

如果 AllocConsole 失败,则不会发生任何变化——控制台已经存在,并且该类假定 C-Runtime 句柄已在其他地方设置。

如果 AllocConsole 成功,则保存当前与 stdout 和 stdin 关联的对象,然后将这些对象替换为为新控制台创建的对象。现在 C-Runtime 输出函数(如 printf)将输出到新的控制台。 cout 和 cin 也将使用新的控制台。

RemoveMenu 调用将阻止用户关闭控制台窗口,从而终止进程。超出这个功能就不需要了。

当 CConsole 被销毁(并且 AllocConsole 成功)时,stdout 和 stdin 被恢复,然后通过调用 FreeConsole 关闭控制台。

当您希望控制台在创建它的函数之外持续存在时,我发现这个类很不方便——您需要获取一个新的 CConsole 并跟踪它的指针,直到您通过删除关闭它。但它的实施列出了您在自己的项目中需要采取的步骤。我从未尝试过使用 dll 进行此操作,但我认为这不会造成任何问题。

控制台.h:

#pragma once
#include <stdio.h>

class CConsole {
    FILE m_OldStdin, m_OldStdout;
    bool m_OwnConsole;
public:
    CConsole();
    ~CConsole();
};

控制台.cpp:

#include <windows.h>
#include <conio.h>
#include <FCNTL.H>
#include <io.h>
#include "Console.h"

static BOOL WINAPI MyConsoleCtrlHandler(DWORD dwCtrlEvent) { return dwCtrlEvent == CTRL_C_EVENT;}

CConsole::CConsole() : m_OwnConsole(false) {
    if(!AllocConsole()) return;

    SetConsoleCtrlHandler(MyConsoleCtrlHandler, TRUE);
    RemoveMenu(GetSystemMenu(GetConsoleWindow(), FALSE), SC_CLOSE, MF_BYCOMMAND);
    const int in  = _open_osfhandle(INT_PTR(GetStdHandle(STD_INPUT_HANDLE)),  _O_TEXT);
    const int out = _open_osfhandle(INT_PTR(GetStdHandle(STD_OUTPUT_HANDLE)), _O_TEXT);
    m_OldStdin  = *stdin;
    m_OldStdout = *stdout;

    *stdin  = *_fdopen(in,  "r");
    *stdout = *_fdopen(out, "w");

    m_OwnConsole = true;
}

CConsole::~CConsole() {
    if(m_OwnConsole) {
        fclose(stdout);
        fclose(stdin);
        *stdout = m_OldStdout;
        *stdin  = m_OldStdin;
        SetConsoleCtrlHandler(MyConsoleCtrlHandler, FALSE);
        FreeConsole();
    }
}

享受吧!

【讨论】:

    【解决方案2】:

    如果您创建一个单独的控制台项目,您将获得一个单独的控制台应用程序,它可能是您想要的,也可能不是您想要的。但是,在这种情况下,您可以使其正常工作,因为您可以避免使用控制台句柄的所有麻烦,而只需使用该应用程序。您可以从您的 dll 启动应用程序,通过拥有多个实例创建互斥锁,并且您可以让控制台应用程序通过命名管道实现一个简单的记录器。你的 dll 和你的控制台应用程序都打开了同一个命名管道的句柄,你从 dll 写入管道并在 exe 中读取它,然后写入 stdout。

    这些都不一定需要控制台应用程序;您可以从 GUI 应用程序轻松完成此操作。就此而言,正如@VoidStar 所说,您可以直接从应用程序写入日志。您还可以使用 OutputDebugString 并使用 SysInternals 中的 DebugView 查看输出。

    【讨论】:

    • 它可以打开一个单独的控制台应用程序,但最好仍然在同一个 DLL 中编译——如果这可能的话。控制台应用程序仍需要附加到原始程序,如果程序终止,控制台也应该终止。在记录到文件方面,我也打算这样做,但我想为用户提供控制台输出。 (注意——这个控制台问题并不是真正为我调试的——它是为了用户需要的调试)
    • 为了进一步阐述我的评论,用户需要能够看到程序对 DLL 的调用,以及 DLL 回馈给程序的内容。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多