#c# #c #windows #winapi
#c# #c #Windows #winapi
Вопрос:
Как я мог бы записать в стандартный вывод какой-нибудь уже открытой консоли? Я нахожу нужную мне консоль с помощью этого фрагмента кода:
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, поэтому консоль не подключает std).
Можно ли перенаправить стандартный вывод после создания процесса?
PS: Я читал о каком-то COM-файле, который можно использовать для этого, так что это означает, что существует программный способ …
Спасибо!
Ответ №1:
Я, наконец, понял, как прозрачно подключиться к консоли, если это окно переднего плана при запуске приложения Windows.
Не спрашивайте меня, почему STD_ERROR_HANDLE должен быть передан вместо STD_OUTPUT_HANDLE , но это просто работает, вероятно, потому, что стандартная ошибка может быть передана совместно.
Примечание: консоль может принимать вводимые пользователем данные при отображении сообщений вашего приложения внутри, но использовать ее, когда stderr выводится из вашего приложения, немного запутанно.
С помощью этого фрагмента кода, если вы запустите приложение из окна консоли с хотя бы одним параметром, оно подключит консоль.Запишите в нее, и если вы запустите приложение с параметром /debug, то оно прикрепит даже отладку.Записываем в консоль.
Вызовите Cleanup() перед выходом из приложения, чтобы освободить консоль, и отправьте нажатие клавиши Enter для освобождения последней строки, чтобы консоль можно было использовать так же, как и до запуска приложения.
PS. Вы не можете использовать перенаправление вывода с помощью этого метода, т.Е.: yourapp.exe > file.txt потому что вы получите пустой файл. И даже не пытайтесь myapp.exe > file.txt 2>amp;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 amp;amp; 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();
}
}
}
Ответ №2:
Если предполагается выполнить запись в родительскую консоль, если таковая имеется, вы можете использовать функцию AttachConsole с аргументом ATTACH_PARENT_PROCESS. (см. msdn attachconsole)
ATTACH_PARENT_PROCESS (DWORD)-1 : Используем консоль родительского элемента текущего процесса
И если вам действительно нужно проверить родительский процесс, вы можете использовать CreateToolhelp32Snapshot и получить родительский процесс через элемент th32ParentProcessID структуры PROCESSENTRY32.
Ответ №3:
Если вы просто хотите записать в консоль, которая используется каким-либо другим приложением, то вы можете воспользоваться следующим — вам нужно использовать P / Invoke для выполнения первого шага:
- AttachConsole(pid) для подключения к этой консоли — если ваш процесс уже связан с консолью, вам придется сначала освободить консоль, поскольку процесс может быть связан только с одной консолью одновременно.
- Теперь, когда вы подключены, получаем дескриптор вывода консоли с помощью CreateFile(«CONOUT $», GENERIC_WRITE, FILE_SHARE_WRITE, … ) — возможно, удастся выполнить эту часть в управляемом коде.
- Теперь, когда у вас есть ДЕСКРИПТОР, оберните его в управляемый код — эту часть вы уже знаете.
Сказав это, даже если вы можете это сделать, это не обязательно хорошая идея. Ничто не мешает исходному процессу выполнять запись в консоль, пока вы делаете то же самое, и выходные данные обоих будут перепутаны, в зависимости от того, как процессы выполняют буферизацию. Если вы хотите сделать что-то вроде уведомления пользователя о чем-то, независимо от того, какое окно активно, возможно, есть лучший способ сделать это.
Ответ №4:
Системный процесс однозначно идентифицируется в системе по его идентификатору процесса. Как и многие ресурсы Windows, процесс также идентифицируется по его дескриптору, который может быть не уникальным на компьютере. Дескриптор — это общий термин для обозначения идентификатора ресурса. Операционная система сохраняет дескриптор процесса, доступ к которому осуществляется через Процесс.Обрабатываем свойство компонента процесса, даже когда процесс завершен. Таким образом, вы можете получить административную информацию о процессе, такую как сам процесс.Код выхода (обычно либо нулевой для успеха, либо ненулевой код ошибки) и процесс.Время выхода. Дескрипторы — чрезвычайно ценный ресурс, поэтому утечка дескрипторов более опасна, чем утечка памяти.
Это не точный ответ на ваши вопросы, но он помогает вам понять основную вещь на самом деле.
Комментарии:
1. Я понимаю, что дескрипторы деликатны, но я бы не стал увеличивать количество ссылок, делающих плохие вещи с дескриптором. Когда процесс-владелец удалит дескриптор, мой код просто завершится ошибкой … чего я и ожидал. Нет способа записать в стандартный вывод процесса (то есть в Windows, потому что в Linux вы можете)?
2. да, я знаю, что в Linux мы можем, но я думаю, что сейчас необходимы некоторые исследования COM, потому что я уверен, что есть способы сделать это через COM. Уведомлю вас, если найду что-то релевантное. Между тем, вопрос и тема выглядят интересными для меня.