Разбираемся в WinAPI

Для кого эта статья


Эта статья адресована таким же, как и я новичкам в программировании на С++ которые по воле случая или по желанию решили изучать WinAPI.
Хочу сразу предупредить:
Я не претендую на звание гуру по C++ или WinAPI.
Я только учусь и хочу привести здесь несколько примеров и советов которые облегчают мне изучение функций и механизмов WinAPI.



В данной статье я предполагаю что вы уже достаточно ознакомились с С++, что бы уметь создавать классы и перегружать для них различные операторы и что вы уже «прятали» какие-то свои механизмы в класс.

Создание и использование консоли


Для отладки Win32 приложения или просто для того что посмотреть как оно там всё внутри происходит я всегда пользуюсь консолью.
Так как вы создаете GUI приложение, а не консольное, то консоль не подключается. Для того что бы её вызвать в недрах интернета был найден вот этот код

if (AllocConsole())
{
int hCrt = _open_osfhandle((long)GetStdHandle(STD_OUTPUT_HANDLE), 4);
*stdout = *(::_fdopen(hCrt, "w"));
::setvbuf(stdout, NULL, _IONBF, 0);
*stderr = *(::_fdopen(hCrt, "w"));
::setvbuf(stderr, NULL, _IONBF, 0);
std::ios::sync_with_stdio();
}
Для удобства советую обернуть его в функцию. Например:
void CreateConsole()
{
if (AllocConsole())
{
int hCrt = _open_osfhandle((long)GetStdHandle(STD_OUTPUT_HANDLE), 4);
*stdout = *(::_fdopen(hCrt, "w"));
::setvbuf(stdout, NULL, _IONBF, 0);
*stderr = *(::_fdopen(hCrt, "w"));
::setvbuf(stderr, NULL, _IONBF, 0);
std::ios::sync_with_stdio();
}


Вызванная консоль работает только в режиме вывода и работает он также как и в консольных приложениях. Выводите информацию как и обычно — cout/wcout.
Для работоспособности данного кода необходимо включить в прект следующие файлы:
#include <fcntl.h>
#include #include <io.h>
и включить пространство имен std в глобальное пространство имён:
using namespace std;
Конечно же, если вы не хотите этого делать, то просто допишите std:: ко всем сущностям которые в ней находятся.

Наследование объектов для вывода и арифм. операций


При создании и изучении самих «окошек» мне всегда требовалось выводить в консоль какое-нибудь значение.
Например:
Вы получаете размер клиентской области окна с помощью функции GetClientRect куда как параметр передается адрес объекта структуры RECT, что бы заполнить этот объект данными. Если вам нужно узнать размер полученной клиентский области вы просто можете вывести его в уже подключённую консоль

cout<<rect.right<<endl<<rect.bottom<<endl;

Но делать так каждый раз (особенно если вам часто приходиться делать что-то подобное) очень неудобно.
Здесь нам на помощь приходит наследование.
Создайте класс который открыто наследуется от структуры RECT и перегрузите оператор вывода << так, как вам угодно.
Например:

class newrect:public RECT
{
public:
friend ostream& operator<<(ostream &strm,newrect &rect)
{
strm<<"Prtint RECT object:\n";
strm<<rect.right<<endl<<rect.bottom<<endl;
return strm;
}
};


Теперь просто выводите обьект с помощью cout/wcout:

cout<<nrect;

И вам в удобном виде будет выводиться всё так, как вам требуется.
Так же вы можете сделать с любыми нужными вам операторами.
Например, если надо сравнивать или присваивать структуры (допустим тот же RECT или POINT) — перегрузите operator==() и operator=() соответственно.
Если хотите реализовать оператор меньше < что бы быстро сравнивать размеры окна и т.д. перегрузите operator<().
Так вы можете делать, я предполагаю, почти с любыми структурами и самое главное, что все функции которые работают с обычным объектом структуры RECT так же хорошо будут работать и с его наследником.
И еще рекомендую всю эту красоту вынести в отдельный подключаемый файл и использовать при необходимости.

Свой класс


Не знаю как у остальных, но так как я совсем зелёный, я решил для каждой изученной функции или для каждой главы/под главы книги создавать новый проект, что бы всё было по полочкам и можно было в любой момент вернуться и освежить в памяти необходимые моменты.
Так как в WinAPI даже для создания простейшего окна нужно заполнить структуру класса, зарегистрировать её и написать тривиальную оконную процедуру, я после третьего или четвертого проекта вспомнил что я всё таки на С++ пишу.
В итоге я всё спрятал в простенький класс. Хендл окна, его имя, имя класса, адрес оконной процедуры, класс окна (WNDCLASS) всё спрятано в private секцию класса.
Для их получения достаточно описать простенькие методы-Get'еры, к примеру:
HWND GetHWND()
LPCTSTR GetClsName() и т.д.
Заполнение и регистрация оконного класса, создание самого окна и его показ производиться в конструкторе.
Для удобства можно перегрузить конструктор, а заполнение и регистрацию оконного класса спрятать в отдельную private функцию класса и вызывать в каждом из конструкторов. Удобство перегрузки состоит в том, что мне иногда необходимо создать совсем простенькое окно и я вызываю конструктор с двумя параметрами — имя окна и hinstance приложения.
В другой раз мне нужно создать окно с особыми размерами, не с дефолтной процедурой окна и с каким-то другим определённым стилем — я вызываю конструктор с сопутствующими параметрами.
Этот класс у меня определён в отдельно подключаемом файле, который лежит в include папке IDE.
Шаблон такого класса:
class BaseWindow
{
WNDCLASSEX _wcex;
TCHAR _className[30];
TCHAR _windowName[40];
HWND _hwnd;
bool _WindowCreation();
public:
BaseWindow(LPCTSTR windowName,HINSTANCE hInstance,DWORD style,UINT x,UINT y,UINT height,UINT width);
BaseWIndow(LPCTSTR windowName,HINSTANCE hInstance);
const HWND GetHWND()const{return HWND;}
LPCTSTR GetWndName()const{return _windowName;}
};


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

P.S.


Всё описанное справедливо для:
Платформа — Windows 7 32 bit
IDE — Visual Studio 2010
Может у кого-то эти советы будут вызывать смех и иронию, но всё-таки мы все когда-то в чём-то были новичками/стажерами/junior'ами.
Прошу к посту отнестись с понимаем. Конструктивная критика, конечно же приветствуется.
Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 31

    +6
    А может расскажете, что тут происходит:
    if (AllocConsole()) 
    { 
      int hCrt = _open_osfhandle((long)GetStdHandle(STD_OUTPUT_HANDLE), 4); 
      *stdout = *(::_fdopen(hCrt, "w")); 
      ::setvbuf(stdout, NULL, _IONBF, 0); 
      *stderr = *(::_fdopen(hCrt, "w")); 
      ::setvbuf(stderr, NULL, _IONBF, 0);
      std::ios::sync_with_stdio();
    }

    а то нашли кусок в интернете а что он делает непонятно, вдруг программа через этот код будет мои пароли Биллу Гейтсу сливать? :)

      +6
      Мне не понятны следующие моменты:
      1) Зачем в изначально UI приложении создавать такими хаками консоль?
      2) Зачем наследовать от RECT новый класс, ведь можно глобальный оператор переопределить, Вам же не нужны приватные члены или функции класса newrect?
      3) Зачем создавать свои поделки для упрощения работы с окнами, если есть хорошо показавшие себя библиотеки. WTL, к примеру.
      4) И еще, как потоки относятся к WinAPI, ведь и в POSIX системах мы можем пользоваться стандартными средствами C++
        0
        про консоль — это вы зря.
        очень удобная штука, если разрешить не только вывод но и ввод, чтобы использовать можно было как-то так:

        Debug::Console::RegisterCommand( «my_command», [](){ std::cout << «It's working!» << std::endl; } );

        Очень удобно добавлять такие вот отладочные команды, позволяющие вывести некую внутреннюю информацию, которую дико неудобно вытаскивать из отладки. Что-то вроде Immediate Window, только последнее частенько отказывается выполнять функции с ошибкой «member function not found».
          0
          «Из пушки, да еще из кривой, по воробьям» это называется.
            0
            Аргументируйте. Я приведу пример использования консоли: есть в нас большой проект, с которым очень плохо дружат профайлеры памяти — приложение либо слишком сильно под ними тормозит, либо падает. Мне удалось за некоторое время написать аналогичный инструмент (профайлер), удовлетворяющий нашим требованиям. Управление им, в том числе генерация отчетов происходит через консоль. Предложите что– нибудь проще.
        +5
        Во-первых, в программе неплохо бы вести лог ошибок и прочего (практически в любой программе). Для этого проще всего написать один раз класс/библиотечку и использовать во всех проектах. Консоль у пользователя не включишь, а ошибки бывают ой какие разные (я 11 лет поддерживаю свой софт — разное видел). К тому же работа с консолью это куча кода которая потом попросту выкидывается и в релизе её быть не должно.
        Во-вторых, можно не мудрствовать и просто делать MessageBox(...) чем заморачиваться с консолью.
        В-третьих, есть простые макросы для вывода чего угодно в дебаг окно Visual Studio.
          +3
          class BaseWindow...

          … прошло 10 лет…
          … и создал FOXHOUND MFC/VCL/Qt и стало всем хорошо…
            +5
            В качестве конструктивной критики посоветую не пользоваться WinAPI таким образом. Или вообще не пользоваться.
              +4
              Решительно не понимаю, нафига такие извращения в 2011-м году. Есть ведь тот же Qt.
                0
                Холивар?
                  0
                  Вы меня с кем-то перепутали.
                    0
                    Переформулирую вопрос:
                    Тем, что Вы написали про абсолютно диаметральную технологию Qt в статье про обучение WinAPI Вы пытаетесь развить бессмысленный спор (Холивар)?
                      0
                      No.
                      Для протокола: времена, когда WinAPI использовали для интерфейсов в далеком прошлом.
                        0
                        Да, но ведь и Qt не панацея, годится лишь для Open Source, а коммерческая лицензия стоит денег. И многие фирмы предпочитают вообще не связываться с Open Source.

                        Знания по WinAPI не будут лишними при разработке под Windows, притом вне зависимости от выбранной технологии, т.к. любая графическая библиотека должна создать как минимум одно окно с помощью того-же WinAPI.

                        Для протокола:
                        Графических библиотек много, мы например недавно использовали HTMLayout.
                          +1
                          Да, но ведь и Qt не панацея, годится лишь для Open Source, а коммерческая лицензия стоит денег
                          Извините, но вы неправы. Почитайте про LGPL.

                          Знания по WinAPI не будут лишними при разработке под Windows, притом вне зависимости от выбранной технологии, т.к. любая графическая библиотека должна создать как минимум одно окно с помощью того-же WinAPI.
                          Странная логика. Чтобы пользоваться топором не обязательно знать сущность молекулярной структуры стали из которой он сделан (полезно только для общего развития или научной деятельности). Аналогично и здесь — гораздо проще мыслить терминами ООП и забить на сишное ололо в WinAPI, иными словами — нужно абстрагироваться от уровня WinAPI. Мы же в 2011-м году, вон уже подоспел С++11, а мы еще пользуемся сишными функциями из WinAPI напрямую и пишем свои велосипеды а-ля BaseWindow. Непорядок, хватит тормозить прогресс…

                          Графических библиотек много, мы например недавно использовали HTMLayout.
                          Qt — это не графическая библиотека :-)
                            0
                            Вот видите, а Вы говорите не Холивар :)
                            Этот спор не имеет смысла.
                              0
                              Я тут объективно комментирую несоответствия в ваших словах (немного даже в логике) вообще-то. Но если Вы говорите Холивар… Война за Qt!!11 Фигня, что я изначально привёл Qt просто как пример.
                                0
                                >>Извините, но вы неправы. Почитайте про LGPL.
                                LGPL это хорошо, но реалии таковы, что многие фирмы избегают GPL, LGPL и всего, что связанно с Open Source. Добиться разрешения использовать какую-либо технологию из мира Open Source бывает не так то просто.

                                >>Странная логика.
                                WinAPI — это не графическая библиотека, и многое из библиотек высокого уровня основано на низкоуровневых функциях и примитивах WinAPI и NativeAPI (файлы, примитивы синхронизации, и т.п.). Как минимум это может помочь при отладке и анализе креш-дампов, полученных из Windows Error Reporting.
                                Что знать, а чего не знать — Ваше личное дело, и не надо навязывать свое мнение другим.

                                >>Фигня, что я изначально привёл Qt просто как пример.
                                Как пример чего? Инструмента для изучения WinAPI?
                                Прочитайте заголовок стати.
                                  +1
                                  LGPL это хорошо, но реалии таковы, что многие фирмы избегают GPL, LGPL и всего, что связанно с Open Source. Добиться разрешения использовать какую-либо технологию из мира Open Source бывает не так то просто.
                                  Многие — это сколько? Статистика есть? Или это основано на ваших ощущениях? Никогда подобного негатива не встречал, как правило достаточно объяснить начальству что к чему в смысле лицензий, чтобы использовать ту или иную технологию. Вообще странное должно быть начальство, чтобы бояться LGPL.

                                  WinAPI — это не графическая библиотека, и многое из библиотек высокого уровня основано на низкоуровневых функциях и примитивах WinAPI и NativeAPI (файлы, примитивы синхронизации, и т.п.). Как минимум это может помочь при отладке и анализе креш-дампов, полученных из Windows Error Reporting.
                                  Кэп, конечно, на связи. Суть моих претензий в том, что в данной статье кроме как работы с интерфейсами, собственно, ничего содержательного не наблюдается. А как уже говорилось выше для этого есть более удобные средства. Вообще перестаньте нервничать и прочтите моё третье сообщение в этой ветке. Я тут не пытаюсь сделать так, чтобы WinAPI никто не изучал. Я за то, чтобы народ подбирал инструмент под задачу. И чтобы не было статей «Изучаем WinAPI», в которых новичок учит как правильно создавать базовый класс для окна. WinAPI нужно изучать для работы с тем, для чего оно реально предназначено — вызов системных функций.

                                  Как пример чего? Инструмента для изучения WinAPI?
                                  Инструмента для создания интерфейсов, работы с окнами, диалогами и прочей фигней, для которой не нужно много Си и которой до фига в этой статье :-)
                                    0
                                    >>Или это основано на ваших ощущениях?
                                    Данная информация основана на ситуации в моей компании, а так-же на информации от колег о прошлых местах работы.

                                    >>Многие — это сколько?
                                    Это может быть темой для голосования:
                                    Используются ли в Вашей компании 3rd-party проекты с лицензией LGPL?
                                    -Используются, у нас проекты с открытым исходным кодом
                                    -Используются, у нас проекты с закрытым исходным кодом
                                    -Не используются из-за лицензии LGPL
                                    -Не используются по другим причинам

                                    В остальном согласен.
                +9
                  0
                  Что это слева за штука вместо руки?
                  На перьевую ручку смахивает.
                    0
                    Это рука Dart из соседнего топика

                    P.S. Знаю, что Darth, но пофиг же)
                  0
                  WinRT же ну! Если уж так хочется гвоздями к винде прибиться.
                    0
                    Для обучения может быть сгодится и свой велосипед, но затем необходимо будет перейти на готовое решение, к примеру WTL, и чем раньше — тем лучше.

                    Также не стоит привыкать к дебагу при помощи консоли, в продакшн коде такое решение весьма спорно.
                    Кстати, окно можно показать и из консольного приложения, тогда не нужно будет дополнительно ее (консоль) показывать. Но опять таки, только в целях обучения.

                    В состав Visual Studio входит программа Spy++, иногда сильно помогает при разработке окошек.
                      0
                      таким же, как и я новичкам в программировании

                      Материалы ДЛЯ новичков, должны писать НЕ новички, иначе вы своими заблуждениями ещё и других заразите. Новички не поймут просто что вы хотели сказать, но посмотрят на неграмотный код и будут думать что так и надо.
                        0
                        Это я про
                        Для обучения может быть сгодится и свой велосипед
                          0
                          Ключевое слово здесь "свой", то-есть в целях обучения учеником пишется велосипед и затем можно сравнить то, что получилось с уже готовым решением, в данном случае имеется в виду WTL.
                          Можно попытаться понять почему в WTL что-то раализовано именно таким образом, а не так как в написанном велосипеде.

                          И да, я с вами полностью согласен, что следовать советам новичков очень опасно.
                      0
                      Да, всё-таки вы правы — писать для новичков статью должен специалист, а не такой же новенький.
                      Не очень весело всё получилось. Я то писал с надеждой, что «вот оно всё так классно и просто делается» и хотел просто поделиться советом, а не с тем что это может оказаться полной чепухой.
                      Но я надеюсь на то, что топик не совсем уж бесполезный. Мало того, что я, так еще и другие новички, читая комментарии, будут сразу мимо проходить)
                        0
                        *Имелось ввиду, что мало того что у меня от чтения комментов мозг немножко выравнивается в нужную сторону)
                          +2
                          Топик не бесполезный, он скорее даже вредный. Понимаете, ученик который учится у хорошего специалиста, просто не коснётся вашей проблемы. Ему нормальный «учитель» (или «самоучитель») порекомендует вовсе идти другим, более грамотным путём. Прочитав же ваш топик, новичок просто сделает шаг в сторону, потом вернётся обратно, а потом через некоторое время поймёт что на этом шаге он просто потерял время. То что он узнал от вас, он всё равно узнал бы, только глубже, и другими (более простыми и быстрыми) путями. Потому что ему объяснит это не такой же новичок как он, а специалист который в своё время всё это уже познал, и который знает чему обучать людей, что будет тратой времени.

                          Говоря короче, давайте заниматься своими делами. Учителя пусть учат, ученики пусть учатся, а программисты пусть программируют.

                        Only users with full accounts can post comments. Log in, please.