powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Программирование [игнор отключен] [закрыт для гостей] / WinAPI: AttachConsole(ATTACH_PARENT_PROCESS)/AttachConsole(dwProcessId)
1 сообщений из 1, страница 1 из 1
WinAPI: AttachConsole(ATTACH_PARENT_PROCESS)/AttachConsole(dwProcessId)
    #36995148
Сон Веры Павловны
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Недавно наткнулся на интересный казус. Имеется winforms-приложение, которому необходимо организовать вывод некоторой информации в консоль. Варианты: если приложение запущено из консоли (cmd, Far, etc.) - использовать консоль родительского процесса; если у родительского процесса консоли нет - приаттачиться к имеющейся консоли (она существует, id консольного процесса известен). Код таков:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
class ConsoleWrapper
{
    public uint ConsoleProcId { get; set; }
 
    public void AttachConsole()
    {
        if (GetConsoleWindow().ToInt32() == 0)
        {
            if (!AttachConsole(ATTACH_PARENT_PROCESS))
            {
                if (!AttachConsole(ConsoleProcId))
                    throw new ApplicationException(GetMessage);
            }
        }
    }
 
    #region WinAPI stuff
    private const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
    private const uint ATTACH_PARENT_PROCESS = 0x0ffffffff;
 
    [DllImport("kernel32.dll")]
    private static extern IntPtr GetConsoleWindow();
    [DllImport("kernel32")]
    private static extern bool AllocConsole();
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool AttachConsole(uint dwProcessId);
    [DllImport("kernel32.dll")]
    private static extern bool FreeConsole();
    [DllImport("kernel32.dll", EntryPoint = "FormatMessageW", CharSet = CharSet.Unicode)]
    private static extern int FormatMessage(int dwFlags, IntPtr lpSource, int dwMessageId,
        int dwLanguageId, StringBuilder lpBuffer, int nSize, IntPtr vaListArguments);
 
    private static string GetMessage
    {
        get
        {
            var errorCode = Marshal.GetLastWin32Error();
            var message = new StringBuilder(1025);
            var res = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, IntPtr.Zero, errorCode, 0,
                message, message.Capacity, IntPtr.Zero);
            return string.Format("Win32 Error 0x{0}{1}", errorCode.ToString("X"), res==0 ?
                string.Empty : ": "+message);
        }
    }
    #endregion
}

Вызов:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
    try
    {
        var cw = new ConsoleWrapper {ConsoleProcId = ...}; // Просто вставляем сюда значение 
                                                           // из Task Manager
        cw.AttachConsole();
        Console.WriteLine("{0}: success", DateTime.Now);
    }
    catch (Exception e)
    {
        MessageBox.Show(e.Message);
    }

ОС/среда/платформа: WinXP prof. SP3/MS Visual Studio prof. 2008/.Net FW 3.5 SP1. Аттач к консоли родительского процесса происходит нормально, аттач же к заданной консоли выдаёт ошибку 0x5 - это, согласно msdn , "If the calling process is already attached to a console" - что совсем не так: консоли нет ни у самого процесса, ни у родительского. Далее методом научного тыка (поскольку идей на предмет возникновения такой ситуации у меня не оказалось) выяснилось, что виной всему вызов AttachConsole(ATTACH_PARENT_PROCESS) - несмотря на то, что он возвращает false, и никакого аттача не происходит, последующий вызов AttachConsole с указанием id процесса в любом случае выбрасывает access denied. Если в коде закомментировать строчку с AttachConsole(ATTACH_PARENT_PROCESS), то AttachConsole(dwProcessId) происходит вполне нормально. Для очистки совести пробовал после попытки аттача к родительской консыли вызывать FreeConsole - без толку. Проблема вполне решаема заменой AttachConsole(dwProcessId) на AllocConsole(), но мне захотелось узнать причины такого поведения AttachConsole, т.к. в msdn каких-либо разъяснений по поводу вышеописанной ситуации мне найти не удалось. Между AttachConsole(ATTACH_PARENT_PROCESS) и AttachConsole(dwProcessId) никаких отладочных Console.WriteLine, потенциально создающих консоль, нет; FreeConsole перед вызовом AttachConsole(dwProcessId) возвращает false. Разбираемся дальше: посмотрим список процессов консоли, к которой пытаемся приаттачиться, и, поскольку у нас при AttachConsole(dwProcessId) GetLaStError=0x5, что вроде как должно свидетельствовать о том, что вызывающий процесс прицепился к какой-то консоли - посмотрим список консольных процессов в приложении, пытающемся приаттачиться к консоли, после AttachConsole(ATTACH_PARENT_PROCESS):

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern uint GetConsoleProcessList(uint[] processList, uint processCount);
...
    void ShowProcList()
    {
        var procList = new uint[1];
        var res = GetConsoleProcessList(procList, 1);
        if (res > 0)
        {
            procList = new uint[res];
            res = GetConsoleProcessList(procList, (uint)procList.Length);
        }
        var sb = new StringBuilder();
        sb.Append(res + "/" + GetMessage);
        procList.ToList().ForEach(p => sb.Append(p + " "));
        MessageBox.Show(sb.ToString()); // В консольном процессе, соответственно,
                                        // Console.EriteLine(sb.ToString());
    }

В процессе, к консоли которого пытаюсь приаттачиться, вызов GetConsoleProcessList показывает только один процесс - тот, которого консоль (и к которой я пытаюсь приаттачиться); вызов GetConsoleProcessList в точке между вызовами AttachConsole процесса, который пытается приаттачиться, возвращает 0, GetLastError возвращает ERROR_INVALID_HANDLE (6) (что, в принципе, логично - вызывающий процесс не имеет консоли). Наконец, исходя из того факта, что при всей вышеописанной ситуации AllocConsole всё же отрабатывает нормально, мне подсказали вот такой выход:

Код: plaintext
1.
2.
3.
4.
5.
6.
    if(!AttachConsole(ATTACH_PARENT_PROCESS)) // fails
    {
        if(AllocConsole()) // succeeds (?), should reset buggy state
            FreeConsole(); // killing dummy
        AttachConsole(dwProcessId); // real attach
    }

- и этот вариант отработал нормально, процесс смог-таки приаттачиться к требуемой консоли. Но осталось неясным, что за такая фантомная консоль привязывается, от которой не помогает отцепиться FreeConsole, и которую в упор не видит GetConsoleProcessList. Была высказана (не мной) гипотеза, что часть логики AttachConsole исходит из ложного предположения, что ATTACH_PARENT_CONSOLE всегда завершается успехом. В итоге в случае неудачи она оставляет процесс в промежуточном состоянии - по одним признакам процесс имеет приаттаченную консоль (и именно эти признаки она и проверяет, вероятно в силу этого же ложного предположения), а по другим признакам приаттаченной консоли всё еще нет (и эти другие признаки проверяют AllocConsole, FreeConsole и GetConsoleProcessList). Но это предположение - опять же гипотеза, точные причины всего вышеописанного так пока и не прояснились, поэтому я был бы рад, если бы кто-нибудь мне разъяснил, в чём тут дело.

P.S. Поскольку данная проблема, по моему скромному, касается больше WinAPI, чем .Net/C#, я счёл, что данная тема будет уместнее здесь, а не в 34-м разделе.
...
Рейтинг: 0 / 0
1 сообщений из 1, страница 1 из 1
Форумы / Программирование [игнор отключен] [закрыт для гостей] / WinAPI: AttachConsole(ATTACH_PARENT_PROCESS)/AttachConsole(dwProcessId)
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]