【问题标题】:How to grab parent process standard output?如何抓取父进程标准输出?
【发布时间】:2010-09-27 12:49:14
【问题描述】:

我正在编写一个实用程序 (http://reg2run.sf.net),以防不带参数的执行作为 Windows 应用程序(显示 OpenFileDialog 等),否则 - 作为控制台应用程序。

所以,在第一种情况下,我不想显示控制台窗口,这就是为什么项目是 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 Manager 执行)。我该怎么做?

我试过了

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;
}

StreamWriter newOut = GetParentProc().StandardInput;

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

GetParentProc().StartInfo.RedirectStandardOutput = false

【问题讨论】:

    标签: c# winapi console


    【解决方案1】:

    对于需要在 Windows 上根据上下文选择充当控制台应用程序还是 GUI 应用程序的应用程序,有多种方法:

    1. 有两个独立的应用程序,一个有条件地启动另一个。
    2. 上述策略的一种变体,有两个应用程序,一个名为“app.com”(即只是重命名一个带有 COM 扩展名的控制台 EXE),另一个名为“app.exe”,这样命令行调用就会找到app.com 首先。由于古老的 DOS 兼容性,.COM 可执行文件位于 .EXE 之前。 (这在 Windows 中是可配置的;请参阅 PATHEXT 环境变量。)
    3. rxvt/Cygwin 技术,这是我在其他任何地方都没有真正看到的文档。

    让我详细介绍一下 Cygwin 上的 rxvt 是如何工作的。 Rxvt 是一个终端仿真器,通常在 X Window 系统上运行。由于 Win32 控制台的限制,Cygwin 将其打包为功能更全面的控制台,支持诸如大量历史记录、动态调整大小、按实例可配置的字体和颜色主题、非应用程序冻结鼠标选择等功能为了在 Windows 上本地运行,Cygwin 附带的 rxvt 包含一个用于 Win32 的小型 X11 包装库。出于与现有本机 Win32 可执行文件的兼容性原因,Windows 上的 Rxvt 实际上是一个控制台应用程序,但大多数时候您从未看到控制台;你只会看到 rxvt 终端模拟器窗口本身。

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

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

    使用这种方法,您可以编写一个应用程序:

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

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

    【讨论】:

    • 非常感谢您提供这些信息!最糟糕的是'没有 MS 支持的方式来“附加”到父应用程序的控制台而不从控制台应用程序开始'(((我将更多地研究 rxvt/Cygwin 技术.. 但它是关于使用子控制台,不是父母(
    • rxvt 技术适用于您的具体情况。关键是,如果不从控制台应用程序开始(使用两个 exe,或者在不需要时隐藏窗口),您就无法实现目标。当您开始作为控制台应用程序时,使用父级是微不足道的:您继承它。
    【解决方案2】:

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

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

    【讨论】:

    • 请注意,典型的父进程(如 cmd.exe 之类的 shell)将在启动 GUI 应用程序后返回命令提示符。换句话说,这不会达到你的预期。
    猜你喜欢
    • 1970-01-01
    • 2019-05-22
    • 1970-01-01
    • 1970-01-01
    • 2020-03-03
    • 2022-01-04
    • 2019-03-17
    • 2021-03-16
    • 1970-01-01
    相关资源
    最近更新 更多