【问题标题】:Using the console in a GUI app in windows, only if its run from a console在 Windows 的 GUI 应用程序中使用控制台,仅当它从控制台运行时
【发布时间】:2013-04-03 21:22:37
【问题描述】:

我的应用程序是一个 GUI 应用程序,它通过终端(通过 cout)提供有用的(尽管是可选的)信息。

在 Windows 中,我要么显示控制台(通过编译为控制台应用程序,或动态分配它),要么不显示。

我的意图是使用控制台,如果它是从控制台运行的,但如果不是,则完全忽略控制台。 (基本上是在 Linux 和 OS X 中发生的事情)。

我不希望重定向到文件(在使用 cin 的情况下,无论如何这不是一个可行的解决方案)。

有没有办法将 Windows 中的 GUI 应用程序附加到运行它的控制台,当且仅当它从控制台运行?

【问题讨论】:

  • 如果您的应用程序正在通过控制台运行,您是否只想打印到控制台?您可以使用cout,如果程序不是从控制台启动的,它将被忽略。
  • @slavik262 我没有在 Windows 上花费太多时间,但是根据以前的经验,如果它不是控制台应用程序,打印到控制台会被忽略。 cout、stdout、printf 等是否重要?
  • stackoverflow.com/a/12782581/5987 - 这个问题是特定于 MFC 但答案只是 Win32 调用。我已经通过 Windows 应用对其进行了测试,并且可以正常工作。

标签: c++ windows console cout cin


【解决方案1】:

您可以在启动时检查CONSOLE_SCREEN_BUFFER_INFO(通过GetConsoleScreenBufferInfo)以确定您是否在现有控制台中运行。如果缓冲区的位置是 0,0,那么您是从控制台外部运行的。有关详细信息,请参阅描述该过程的 Microsoft Knowledgebase Article

为了使其工作,您需要将您的应用程序编译为控制台应用程序(使用/SUBSYSTEM:CONSOLE),然后如果应用程序启动了一个新控制台(0,0 处的缓冲区),则将您自己与控制台分离。这将导致程序在从命令行启动时正确“附加”到调用控制台。

【讨论】:

  • 虽然这绝对是我需要的答案的一部分,但当我打印到控制台并运行我的 GUI 时,它并不完整(并且我回忆起以前将应用程序移植到 Windows 的记忆)来自 Windows 控制台中的应用程序,打印命令只是......消失了。
  • @latreides,恐怕他是对的 - 如果您不首先将其创建为控制台应用程序,则需要将cincout 连接到控制台的启动代码被跳过。但是要知道是否有控制台,您应该使用GetConsoleWindow 而不是GetConsoleScreenBufferInfo
  • 所以让我确保我理解这个过程。制作一个控制台应用程序,检查它是否在控制台中运行,如果是,我们很好(它将使用现有的控制台),如果没有,则释放控制台(因为我不希望它弹出控制台)。那是对的吗?如果是这样,控制台(我不想要的)会在释放之前“显示”吗?
  • @latreides,是的,您是对的 - 如果您启动控制台应用程序,您将拥有一个控制台,无论它是从现有控制台运行还是 Windows 为您创建了一个新控制台。知道它是否是新分配的唯一方法是查看此答案中所述的位置。您无法阻止控制台暂时闪烁。但是请参阅我在您的问题上发布的链接,以获得不同的答案。
【解决方案2】:

在使用 cin 的情况下,这无论如何都不是一个可行的解决方案

这是您问题中的杀手级细节。这在纸上很简单,只需首先调用 AttachConsole(ATTACH_PARENT_PROCESS) 来尝试附加到现有控制台。当您的程序从诸如资源管理器之类的 GUI 程序或桌面快捷方式启动时,这将失败。所以如果它返回 FALSE 然后调用 AllocConsole() 来创建你自己的控制台。

然而,使用 cin 是一个问题。命令处理器会注意您的 EXE 并检查它是控制台模式应用程序还是 GUI 应用程序。它会在您的情况下检测到 GUI 应用程序,然后等待该过程完成。它再次显示提示并等待输入。然后,您还将等待输入,但您会输,命令处理器首先到达那里。您的输出还与命令提示符混合在一起,这是很容易解决的问题。

有一个简单的解决方法,您的用户应该使用start /wait yourapp 启动您的程序,以告诉命令处理器等待该过程完成。问题是:没有人使用它。并且用户不会意识到当他们键入输入时会发生什么,打算将其输入您的程序,但它实际上是由命令处理器解释的。产生令人费解的错误消息或格式化硬盘。

解决这个无法解决的问题只有两种好方法。将您的程序构建为控制台模式应用程序,并在您发现要显示 GUI 时调用 FreeConsole()。或者总是调用 AllocConsole()。这些都不是很好的选择。第一种方法是 Java JVM 在 Windows 上使用的方法。针对 JVM 提出的最古老的错误之一,使 Java 程序员完全无法从闪烁的控制台窗口中获取信息。

第三种选择是唯一不错的选择,而您不想要的选择是创建另一个始终使用控制台的 EXE。像 Java 一样,javaw.exe 与 java.exe。

一个技巧是可能的,您可以将该文件从“yourapp2.exe”重命名为“yourapp.com”。当用户在命令行提示符下键入“yourapp”时,它将首先被选中,桌面快捷方式仍然可以指向“yourapp.exe”。 Visual Studio 使用了这个技巧,devenv.com vs devenv.exe。

【讨论】:

  • +1 在今天特别起泡的表格上。我认为 Java 和 Python 都选择了单独的可执行文件选项 javaw 和 pythonw。这是保持理智的唯一方法。
  • 虽然不是我所希望的,但这是解决所有问题的最佳、最完整的答案。我希望没有这样的牺牲是可行的。已接受,谢谢。
【解决方案3】:

正如其他人指出的那样,您必须创建一个控制台应用程序和一个窗口应用程序。所以,你最终会得到console.exeapp.exe。为了使其在命令行中不那么明显,您可以利用 PATHEXT 技巧,如 devenv 所做的那样。如果文件的扩展名在 PATHEXT 环境变量中,则 cmd.exe 将文件视为命令。 COM 默认情况下存在,因此您可以将 console.exe 重命名为 app.com,从而允许命令 app 启动附加到当前控制台的控制台应用程序。

注意:当然,如果需要,控制台应用程序可以显示 GUI。

app.com 和 app.exe 之间的构建差异取决于您的构建系统,但它可能只是设置输出类型的一个属性。使用 msbuild(对于 .vcxproj 文件),这只是另一个构建配置的问题。

【讨论】:

    【解决方案4】:

    您可以在控制台中创建一个应用程序,使用 argc 获取一行并打印它;

    ////
    int main(int argc, char *argv[])
    {
        //here print argv....using cout or printf
    }
    

    将文件作为 console.exe 保存在您应用的文件夹中。 现在在您的应用程序中,如果您想在控制台中查看任何行,您可以调用命令

    system("console.exe this is the line i want to print and see in console");
    

    【讨论】:

      猜你喜欢
      • 2011-01-22
      • 1970-01-01
      • 2013-02-11
      • 1970-01-01
      • 2010-11-19
      • 2015-03-04
      • 2019-01-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多