Получите ручку и напишите в консоль, которая запустила наш процесс - PullRequest
3 голосов
/ 19 апреля 2011

Как я могу записать на стандартный вывод какой-нибудь уже открытой консоли?Я нахожу нужную консоль с этим фрагментом кода:

    IntPtr ptr = GetForegroundWindow();           
    int u;
    GetWindowThreadProcessId(ptr, out u);
    Process process = Process.GetProcessById(u);

Проблема заключается в том, как получить стандартный указатель дескриптора вывода (stdHandle) этого процесса.

Тогда я бы хотел что-токак:

                SafeFileHandle safeFileHandle = new SafeFileHandle(stdHandle, true);
                FileStream fileStream = new FileStream(safeFileHandle, FileAccess.Write);
                Encoding encoding = Encoding.ASCII;
                StreamWriter standardOutput = new StreamWriter(fileStream, encoding);
                standardOutput.AutoFlush = true;
                Console.SetOut(standardOutput);

Код на C ++ с использованием Windows API в порядке - я могу использовать pInvoke.

По сути, я хотел бы написать текст в уже открытое консольное окно, не порожденное моимпроцесс (и это тот, который был на переднем плане при запуске моего процесса через командную строку - но мой процесс - WinApp, поэтому консоль не присоединяет стандартный).

Можно ли перенаправить стандартный вывод после процессабыл создан?

PS: я читал о каком-то COM-файле, который можно использовать для этого, так что это означает, что существует программный способ ...

Спасибо!

Ответы [ 4 ]

3 голосов
/ 19 апреля 2011

Я наконец-то понял, как прозрачно прикрепить к консоли, если это окно переднего плана при запуске приложения Windows.

Не спрашивайте меня, почему STD_ERROR_HANDLE должен быть передан вместо STD_OUTPUT_HANDLE, но он просто работаетВозможно, из-за того, что стандартной ошибкой можно поделиться.

Примечание: консоль может принимать пользовательский ввод при отображении сообщений вашего приложения внутри, но использовать его во время вывода stderr из вашего приложения немного запутанно.

С помощью этого фрагмента кода, если вы запустите свое приложение из окна консоли с хотя бы одним параметром, оно присоединит к нему Console.Write, а если вы запустите приложение с параметром / debug, то оно присоединит дажеDebug.Write на консоль.

Вызовите Cleanup () перед выходом из приложения, чтобы освободить консоль, и отправьте клавишу Enter, чтобы освободить последнюю строку, чтобы консоль можно было использовать, как и до запуска приложения.

PS.Вы не можете использовать перенаправление вывода с этим методом, т. Е. Yourapp.exe> ​​file.txt, потому что вы получите пустой файл.И даже не пытайтесь использовать myapp.exe> ​​file.txt 2> & 1, потому что вы можете вызвать сбой приложения (перенаправление ошибки на выход означает, что мы пытаемся подключиться к несделенному буферу).

Вот код:

[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);

[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);

[DllImport("kernel32.dll",
    EntryPoint = "GetStdHandle",
    SetLastError = true,
    CharSet = CharSet.Auto,
    CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr GetStdHandle(int nStdHandle);

[DllImport("kernel32", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);

[DllImport("kernel32.dll",
    EntryPoint = "AllocConsole",
    SetLastError = true,
    CharSet = CharSet.Auto,
    CallingConvention = CallingConvention.StdCall)]
private static extern int AllocConsole();

[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool FreeConsole();

private const int STD_OUTPUT_HANDLE = -11;
private const int STD_ERROR_HANDLE = -12;
private static bool _consoleAttached = false;
private static IntPtr consoleWindow;

[STAThread]
static void Main()
{
    args = new List<string>(Environment.GetCommandLineArgs());

    int prId;
    consoleWindow = GetForegroundWindow();            
    GetWindowThreadProcessId(consoleWindow, out prId);
    Process process = Process.GetProcessById(prId);

    if (args.Count > 1 && process.ProcessName == "cmd")
    {
        if (AttachConsole((uint)prId)) {
            _consoleAttached = true;
            IntPtr stdHandle = GetStdHandle(STD_ERROR_HANDLE); // must be error dunno why
            SafeFileHandle safeFileHandle = new SafeFileHandle(stdHandle, true);
            FileStream fileStream = new FileStream(safeFileHandle, FileAccess.Write);
            Encoding encoding = Encoding.ASCII;
            StreamWriter standardOutput = new StreamWriter(fileStream, encoding);
            standardOutput.AutoFlush = true;
            Console.SetOut(standardOutput);
            if (args.Contains("/debug")) Debug.Listeners.Add(new TextWriterTraceListener(Console.Out));
            Console.WriteLine(Application.ProductName + " was launched from a console window and will redirect output to it.");
        }
    }
    // ... do whatever, use console.writeline or debug.writeline
    // if you started the app with /debug from a console
    Cleanup();
}

private static void Cleanup() {
    try
    {
        if (_consoleAttached)
        {
            SetForegroundWindow(consoleWindow);
            SendKeys.SendWait("{ENTER}");
            FreeConsole();
        }    
    }        
}
1 голос
/ 17 марта 2013

Если целью является запись в родительскую консоль, если таковая имеется, вы можете использовать функцию AttachConsole с аргументом ATTACH_PARENT_PROCESS.(см. msdn attachconsole)

ATTACH_PARENT_PROCESS (DWORD) -1: использовать консоль родителя текущего процесса

И если вам нужно проверить родительский процесс, вы можете использовать CreateToolhelp32Snapshotи получить родительский процесс через член th32ParentProcessID структуры PROCESSENTRY32.

1 голос
/ 19 апреля 2011

Если вы просто хотите записать в консоль, которая используется каким-либо другим приложением, вы можете использовать следующее - вам понадобится P / Invoke, чтобы выполнить первый шаг:

  • AttachConsole (pid) для подключения к этой консоли - если ваш процесс уже связан с консолью, вам сначала нужно будет подключиться к FreeConsole, поскольку процесс может быть связан только с одной консолью одновременно. .
  • Теперь, когда вы подключены, получите дескриптор вывода консоли, используя CreateFile ("CONOUT $", GENERIC_WRITE, FILE_SHARE_WRITE, ...) - возможно, сможете выполнить эту часть в управляемом коде.
  • Теперь, когда у вас есть РУЧКА, заверните ее в управляемый код - эту часть вы уже знаете.

Сказав это, даже если вы можете это сделать, это не обязательно хорошая идея. Нет ничего, что могло бы помешать исходному процессу записывать данные на консоль, пока вы выполняете аналогичные действия, а выходные данные обоих были перепутаны, в зависимости от того, как процессы выполняют буферизацию. Если вы хотите что-то сделать, например, уведомить пользователя о чем-то, независимо от того, какое окно активно, возможно, есть лучший способ сделать это.

0 голосов
/ 19 апреля 2011

Системный процесс однозначно идентифицируется в системе по его идентификатору процесса. Как и многие ресурсы Windows, процесс также идентифицируется по его дескриптору, который может быть не уникальным на компьютере. Дескриптор - это общий термин для идентификатора ресурса. Операционная система сохраняет дескриптор процесса, доступ к которому осуществляется через свойство Process.Handle компонента Process, даже если процесс завершен. Таким образом, вы можете получить административную информацию процесса, такую ​​как Process.ExitCode (обычно либо ноль для успеха, либо ненулевой код ошибки) и Process.ExitTime. Дескрипторы являются чрезвычайно ценным ресурсом, поэтому утечка дескрипторов более опасна, чем утечка памяти.

Это не точный ответ на ваши вопросы, но на самом деле он помогает вам понять основную вещь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...