c++ - 如何区分控制台程序是在 Powershell 中打开还是在 Windows 终端中打开?

标签 c++ windows powershell winapi

我正在编写一个库,它将使在控制台程序中设置颜色、模式等更容易。但是我遇到了 Windows Terminal 的问题。例如我有一个函数:

void WindowsCLI::setUnderlinedFont()
{
    auto consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
    config.underlined = true;
    SetConsoleTextAttribute(consoleHandle, getTextAttribute(config));
}

它使用 windows.h 中的 COMMON_LVB_UNDERSCORE 属性使文本带有下划线。这个函数在 powershell 中的结果如下所示: enter image description here , 在 Windows 终端中是这样的:enter image description here ,显然在第二种情况下我的功能无法正常工作。我认为问题是 Windows 终端在虚拟终端模式下运行。所以我为虚拟终端做了另一个功能:

void WindowsVirtualCLI::setUnderlinedFont()
{
    printf("\x1b[4m");
}

现在它对 Powershell 不起作用:enter image description here ,并在 Windows 终端上正常工作:enter image description here .但现在我有另一个问题。如何区分程序是在 Powershell 还是 Windows Terminal 中运行。我尝试使用此功能:

CLI& cli()
{
    DWORD consoleMode;
    auto consoleHandle = GetStdHandle(STD_INPUT_HANDLE);
    GetConsoleMode(consoleHandle, &consoleMode);

    if ((consoleMode & ENABLE_PROCESSED_OUTPUT) && (consoleMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING))
    {
        return windows::WindowsVirtualCLI::getInstance();
    }
    else
    {
        return windows::WindowsCLI::getInstance();
    }
}

但事实证明,Powershell 和 Windows 终端都启用了 ENABLE_PROCESSED_OUTPUTENABLE_VIRTUAL_TERMINAL_PROCESSING。现在,我不知道如何在运行时区分这些终端。你知道怎么做吗?

附言 我已将 cli() 方法更改为:

CLI& cli()
{
    DWORD consoleMode;
    auto consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
    GetConsoleMode(consoleHandle, &consoleMode);
    SetConsoleMode(consoleHandle, consoleMode);

    if ((consoleMode & ENABLE_PROCESSED_OUTPUT) && (consoleMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING))
    {
        return windows::WindowsVirtualCLI::getInstance();
    }
    else
    {
        return windows::WindowsCLI::getInstance();
    }
}

但它仍然无法正常工作。

最佳答案

简单但并非万无一失的解决方案:

Windows Terminal 定义了两个特定于应用程序的环境变量,WT_SESSIONWT_PROFILE_ID,因此您可以测试是否定义了其中一个变量(具有非空值) .

根据 this answer , getenv("WT_SESSION") 应该在 C++ 中工作,用于检索该变量的值(如果已定义)。

在 PowerShell 本身中,如果变量具有非空值,[bool] $env:WT_SESSION 返回 $true

这种方法并非万无一失,因为如果您启动常规控制台窗口 (conhost.exe)从 Windows 终端中运行的 shell - 例如使用 Start-Process cmd.exe - 它将继承 WT_SESSION 变量,从而在此类 session 中产生误报。


更详细但更可靠的解决方案:

一个稳健的解决方案是从当前进程开始遍历父进程链,直到找到具有关联主窗口句柄的第一个进程:

  • 如果该进程的名称是 WindowsTerminal,则可以安全地假设该进程正在 Windows 终端中运行 - 可能间接地在从以下位置启动的(根据定义的控制台子系统)进程中运行直接在 Windows 终端中运行的 shell。

  • 否则,可以安全地假设一个不同的应用程序正在托管进程,可能是进程本身(在常规控制台窗口中运行的进程本身拥有该窗口,并且有一个conhost.exe 进程)。

以下是 PowerShell 实现:

  • PowerShell(核心)7+ 实现:
$runningInWindowsTerminal = 
  if ($IsWindows) {
    $ps = Get-Process -Id $PID
    while ($ps -and 0 -eq [int] $ps.MainWindowHandle) { $ps = $ps.Parent }
    $ps.ProcessName -eq 'WindowsTerminal'
  } else {
    $false
  }
  • Windows PowerShell 和跨版本实现:

不幸的是,Windows PowerShell 没有修饰 System.Diagnostics.Process Get-Process 输出的对象具有报告父进程的 .Parent 属性,这使解决方案复杂化并且需要调用 Get-CimInstance 来检索父信息。

$runningInWindowsTerminal = 
  if ($env:OS -eq 'Windows_NT') {
    $ps = Get-Process -Id $PID
    while ($ps -and 0 -eq [int] $ps.MainWindowHandle) { 
      $ps = Get-Process -ErrorAction Ignore -Id (Get-CimInstance Win32_Process -Filter "ProcessID = $($ps.Id)").ParentProcessId 
    }
    $ps.ProcessName -eq 'WindowsTerminal'
  } else {
    $false
  }

关于c++ - 如何区分控制台程序是在 Powershell 中打开还是在 Windows 终端中打开?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72574412/

相关文章:

azure - 尝试使用 PowerShell 将 Bitlocker key 备份到 Azure AD 时收到错误

powershell - 将 perl GetOptions 转换为等效的 Powershell param 语句

c++ - 为什么我们使用预处理器指令来定义变量?

windows - 如何在 Windows 上运行的 ruby​​ 上捕获信号

windows - 使用 sbt 构建时检查错误级别

c++ - 我在注册表中的哪里可以找到音频设备名称的不同部分

powershell - 将文本文件中的多行组合/合并为一行 (Powershell)

c++ - C++随机正则表达式从文件替换

c++ - 如何实现对象指针的动态数组? "expression must be a modifiable lvalue"

c++ - 如何在选中/取消选中时更改 QToolBar 上 QAction 的图标?