Как получить хэндл окна консольного приложения?

Авторы: Игорь Вартанов
Александр Шаргин
Опубликовано: 30.04.2001
Версия текста: 1.0

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

Способ 1

Основная идея этого способа описана в статье Q124103 из Microsoft Knowledge Base. Она состоит в том, чтобы заменить название консоли на уникальную строку символов, посредством FindWindow() выполнить поиск хэндла окна, а затем вернуть окну прежнее название. Там же приведен код получения уникальной строки, основанный на комбинировании информации о текущем времени и идентификаторе процесса.

Приведенный ниже код использует несколько иной способ получения уникальной строки для названия консоли, который, на мой взгляд, нисколько не уступает способу от Microsoft, поскольку использует в качестве строки GUID - гарантированно уникальный идентификатор. Вы можете использовать любой из этих вариантов, равно как и разработать собственную методику.

/////////////////////////////////////////////////////////////
//  
//  Как спрятать и восстановить окно консоли?
//

#include <windows.h>
#include <stdio.h>
#include <objbase.h>
#include <locale.h>

#pragma comment(lib, "user32")
#pragma comment(lib, "ole32")

void main()
{
    HRESULT hr;
    GUID    guid;    
    char    title[256];
    char    newtitle[256];
    wchar_t lpsz[256];
    int     size;
    HWND    hwnd;

    // Инициализация окружения
    setlocale( LC_ALL, ".ACP" );
    CoInitialize( NULL );

    __try
    {
        // Создаем GUID
        hr = CoCreateGuid( &guid );
        if( FAILED( hr ) )
            __leave;
        // Конвертируем его в текстовое представление (UNICODE)
        size = StringFromGUID2( ( REFGUID ) guid, lpsz, 256 );
        // Конвертируем UNICODE в ANSI
        wcstombs( newtitle, lpsz, size );
        // Сохраняем текущий заголовок консоли
        GetConsoleTitle( title, sizeof( title ) );
        // Устанавливаем уникальный заголовок 
        SetConsoleTitle( newtitle );

        // Microsoft рекомендует сделать паузу 40 мсек
        // для обновления заголовка консоли
        Sleep(40);
        // Поиск хэндла окна с известным нам заголовком
        hwnd = FindWindow( NULL, newtitle );
        if(!hwnd)
        {
            printf( "Can't find window\n" );
            __leave;
        }
        // Поиск успешно завершен, возвращаем старый заголовок
        SetConsoleTitle( title );
        // Ждем 40мсек согласно рекомендациям MS
        Sleep(40);
        // Проверяем правильность найденного хэндла окна
        GetWindowText( hwnd, newtitle, sizeof( newtitle ) );
        if( lstrcmp( title, newtitle ) )
        {
            printf("Bad console title\n");
            __leave;
        }
        // Хэндл получен правильно, используем его...
        if( IDNO == MessageBox( NULL, title, "Hide console?", MB_YESNO ) )
            __leave;
        ShowWindow( hwnd, SW_HIDE );

        MessageBox( NULL, "Press to bring it back", "Hidden!", MB_OK );
        ShowWindow( hwnd, SW_SHOW );
    }

    __finally
    {
        CoUninitialize();
    }
}

Способ 2

Идея этого способа состоит в том, чтобы перечислить все открытые окна с помощью EnumWindows, а затем выбрать из них окно, созданное текущим потоком - это и будет окно консоли. Конечно, для перечисления гораздо лучше подошла бы функция EnumThreadWindows, но она почему-то отказывается перечислять консольные окна.

#include <windows.h>

BOOL CALLBACK EnumWndProc(HWND hwnd, LPARAM lParam)
{
   if(GetWindowThreadProcessId(hwnd, NULL) == GetCurrentThreadId())
   {
      *(HWND*)lParam = hwnd;
      return FALSE;
   }

   return TRUE;
}

int main()
{
   HWND hWnd;
   EnumWindows(EnumWndProc, (LPARAM)&hWnd);
   // hWnd содержит дескриптор.

   ...
}
ПРИМЕЧАНИЕ
Разумеется, такой способ будет работать при условии, что ваше консольное приложение не создаёт дополнительных графических окон верхнего уровня (редкий случай, но иногда бывает и такое). Если другие окна всё-таки создаются, можно ввести дополнительные условия проверки, которые отсекут все лишние окна, кроме консоли. Какие именно проверки могут потребоваться, зависит от конкретного приложения. Другой вариант - воспользоваться методом, описанном в предыдущем разделе.

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