C# 如何获取父进程标准输出?

C# 如何获取父进程标准输出?,c#,winapi,console,C#,Winapi,Console,我正在编写一个实用程序(),在没有参数的情况下,它可以作为windows应用程序(显示OpenFileDialog等)执行,否则,它可以作为控制台应用程序 所以,在第一种情况下,我不想显示控制台窗口,这就是为什么project是Windows应用程序。 但在第二步中,我需要展示它,它是用 if (ptrNew == IntPtr.Zero) { ptrNew = GetStdHandle(-11); } if (!AllocConsole()) { throw new Exter

我正在编写一个实用程序(),在没有参数的情况下,它可以作为windows应用程序(显示OpenFileDialog等)执行,否则,它可以作为控制台应用程序

所以,在第一种情况下,我不想显示控制台窗口,这就是为什么project是Windows应用程序。 但在第二步中,我需要展示它,它是用

if (ptrNew == IntPtr.Zero)
{
    ptrNew = GetStdHandle(-11);
}
if (!AllocConsole())
{
    throw new ExternalCallException("AllocConsole");
}
ptrNew = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, 3, 0, IntPtr.Zero);
if (!SetStdHandle(-11, ptrNew))
{
    throw new ExternalCallException("SetStdHandle");
}
StreamWriter newOut = new StreamWriter(Console.OpenStandardOutput());
newOut.AutoFlush = true;
Console.SetOut(newOut);
Console.SetError(newOut);
我想要的是——获取父进程标准输出并使用它,如果它存在的话(在通过cmd.exe或Far管理器执行的情况下)。我怎么做

我试过了

static Process GetParentProc()
{
int pidParent = 0;
int pidCurrent = Process.GetCurrentProcess().Id;

IntPtr hSnapshot = CreateToolhelp32Snapshot(2, 0);
if (hSnapshot == IntPtr.Zero)
{
    return null;
}

PROCESSENTRY32 oProcInfo = new PROCESSENTRY32();
oProcInfo.dwSize = (uint)Marshal.SizeOf(typeof(PROCESSENTRY32));

if (!Process32First(hSnapshot, ref oProcInfo))
{
    return null;
}
do
{
    if (pidCurrent == oProcInfo.th32ProcessID)
    {
        pidParent = (int)oProcInfo.th32ParentProcessID;
    }
}
while (pidParent == 0 && Process32Next(hSnapshot, ref oProcInfo));

if (pidParent > 0)
{
    return Process.GetProcessById(pidParent);
}
else
{
    return null;
}

但得到了InvalidOperationException:StandardIn未被重定向。因为

GetParentProc().StartInfo.RedirectStandardOutput = false

对于需要在Windows上选择是充当控制台还是GUI应用程序的应用程序,有几种方法,具体取决于上下文:

  • 有两个独立的应用程序,其中一个有条件地启动另一个
  • 上述策略的一个变体,有两个应用程序,一个称为“app.com”(即仅使用com扩展名重命名控制台EXE),另一个称为“app.EXE”,以便命令行调用首先找到app.com。由于古老的DOS兼容性,.COM可执行文件出现在.EXEs之前。(这在Windows中是可配置的;请参阅PATHEXT环境变量。)
  • rxvt/Cygwin技术,这是我在其他任何地方都没有真正看到过的技术
  • 让我详细介绍一下Cygwin上的rxvt是如何工作的。Rxvt是一种终端仿真器,通常在X窗口系统上运行。由于Win32控制台的局限性,Cygwin将其打包为功能更全面的控制台,支持大量历史记录、动态调整大小、每个实例可配置的字体和颜色主题、非应用程序冻结鼠标选择和复制等,以便在Windows上本机运行,Cygwin附带的rxvt包含一个用于Win32的小型X11包装库。出于与现有本机Win32可执行文件兼容的原因,Windows上的Rxvt实际上是一个控制台应用程序,但大多数情况下,您从未见过控制台;您只看到rxvt终端模拟器窗口本身

    它的工作方式在rxvt源代码树中名为
    hideConsole()
    的函数中的
    rxvt/W11/wrap/wrap.c
    中具体实现。基本上,它打开控制台(使用
    CreateFile(“CONOUT$”)
    ),并检查光标位置是否在(0,0)(使用控制台手柄上的
    GetConsoleScreenBufferInfo()

    如果是,则推断它是作为独立应用程序启动的,而不是从控制台父应用程序启动的,因此它知道操作系统已为该进程创建了专用的Win32控制台。它继续隐藏这个控制台窗口,但必须首先找到它。它使用
    SetConsoleTitle
    将控制台窗口的标题设置为基于应用程序名称和当前线程ID的唯一值。然后使用
    FindWindow
    查找此窗口的句柄(如果需要更改标题,请定期
    Sleep
    ing几毫秒,因为控制台窗口实际上完全由windows中的另一个进程控制)。当它最终找到窗口句柄时,会使用
    ShowWindowAsync
    隐藏它,并传入
    SW_HIDE

    使用此方法,您可以编写以下应用程序:

    • 如果从控制台父级启动,它可以继续使用此控制台
    • 如果作为应用程序启动,它可以选择是否隐藏控制台

    唯一的缺点是在应用程序启动时控制台窗口会短暂闪烁。

    您始终可以使用以下p/Invoke方法:

    [DllImport("kernel32.dll")]
    static extern bool AttachConsole(int dwProcessId);
    
    const int ATTACH_PARENT_PROCESS = -1;
    

    非常感谢您提供这些信息!!最糟糕的是“没有MS支持的方式可以“附加”到父应用程序的控制台,而不以控制台应用程序的形式启动”((我将更多地了解rxvt/Cygwin技术。)但这是关于使用子控制台,而不是父控制台(rxvt技术适用于您的特定情况。关键是,如果不作为控制台应用程序启动(使用两个EXE,或者在不需要时隐藏窗口),您就无法实现目标。当您作为控制台应用程序启动时,使用父进程很简单:您继承了它。请注意,典型的父进程(类似cmd.exe的shell)将在启动GUI应用程序后返回命令提示符。换句话说,这不会达到预期效果。
    [DllImport("kernel32.dll")]
    static extern bool AttachConsole(int dwProcessId);
    
    const int ATTACH_PARENT_PROCESS = -1;