|
Модераторы: feodorv, GremlinProg, xvr, Fixin |
|
Fixin |
|
||||||||||||||
Ёжик Профиль Группа: Комодератор Сообщений: 1357 Регистрация: 6.1.2004 Репутация: 8 Всего: 18 |
Windows API.
Введение. Это не книга, не учебник. Это статья, призванная помочь Вам в изучении Windows API на языке C/C++, но не учит языку. Многие программы написаны с использованием макросов, описанных в заголовочном файле <WindowsX.h>. Начало. Многие, начинающие изучать API (application programming interface – интерфейс программирования приложений), удивляются объему кода, сложности и тп, говорят, что проще сделать в Borlan® Builder®. Если Вы так думаете, то зачем читаете этот текст? Вы еще здесь? Тогда продолжим серьезно и без лени. Вот простейший код: Код 1.0+NoWindow.cpp+------------------------------------------------
Код 1.0+NoWindow.cpp+------------------------------------------------Конец Что за прараметры? Начнем с параметров главной функции. Кстати, это САМАЯ главная функция. Функция WndProc (о которой позже) является главной оконной процедурой. Итак, параметры… HINSTANCE вообще интересный тип. Это своего рода дескриптор объекта (HANDLE). В первом случае (hInstance) он указывает на тот исполняемый модуль, на основе которого запущен процесс данного кода. Проще говоря, указывает на файл “NoWindow.exe”. Второй параметр (hPrevInstance) – пережиток Win16. В 32-разрядных приложениях не используется. Раньше обозначал предыдущий запущенный экземпляр данного приложения. Если не ноль, значит Бог есть! Однозначно. Третий (lpszCmdParam) – строка параметров, которые были посланы программе при запуске. Если их не было, то строка пуста. Четвертый (nCmdShow) – число, обозначающее вид окна при запуске. Например, свернутое, развернутое, нормальное. Если решили изменить состояние окна во время выполнения приложения, то это сообщение посылается в окно функцией ShowWindow, например так:
За одно, разберем параметры вызова MessageBox. Первый – дескриптор окна, к которому принадлежит сообщение. Это нужно для того, что бы Windows знала, какое окно его вызвало и, соответственно, сделать его неактивным, пока вы не ответите в нем нажатием какой-нибудь кнопки. Второй и третий – строки. Соответственно, текста сообщения и текста заголовка сообщения. Четвертый – самый интересный. В нем указывается стиль сообщения. Они находятся в файле <WinUser.h>. Смотрите MSDN. Делаем окно. Начнем сразу с кода: Код 2.0+SimWnd.cpp+------------------------------------------------
Код 2.0+SimWnd.cpp+------------------------------------------------Конец Разбор полетов. Не маленький код . Но не надо бояться – этот код копируется из приложения в приложение и не надо его учить наизусть (хотя, если есть желание…). Итак, посмотрим. Три шага создания окна: 1) Регистрация. (Register()) 2) Создание. (Create()) 3) Обработка сообщений. (WndProc()) Когда мы создаем оконное приложение, мы как бы делаем подкласс объекта «окно» благодаря этим трем шагам. Поясню. При создании кнопки (класс “button”) , регистрацию и обработку сообщений выполняет система, а создание – программист. Программист также может создать свой класс кнопки и сделать ей любые свойства такие, как подсветка или градиент. Но об этом когда-нибудь потом. При регистрации, мы сообщаем системе, в основном, название нового класса и процедуру обработки его сообщений, ну и некоторые элементы внешнего вида и другие параметры. Создание окна – тут все просто. Вызов функции CreateWindow(). А при создании нового окна, после этой вызываются ShowWindow() и UpdateWindow(). Они призваны соответственно показать и обновить окно. Самое сложное и обычно наиболее емкое по коды – это обработка сообщений. Есть такая штука – DefWindowProc – стандартная оконная процедура. Все наше окно полностью работает за счет этой процедуры. Только я поступил так, как делают многие программисты:
Просто дал ей свое название. Единственное сообщение, которое она не обрабатывает, это WM_DESTROY – сообщение о закрытии окна. Если его не обрабатывать, то окна видно не будет, но процесс так и останется. Не очень удобно. Сообщения. Все функционирование в Windows основано на сообщениях. Даже то, что вы передвигаете мышь. Точнее её перерисовка. А как их получает оконная процедура? Все начинается тут:
А продолжается тут:
Функция GetMessage получает сообщение от системы и пересылает его оконной процедуре. Там программист обрабатывает его так, как хочет. Если он этого не делает или ему этого не нужно, то оно обрабатывается стандартной процедурой. Сообщений очень много и приводить их здесь нецелесообразно – займет пару страниц. Скажу только то, что когда я не знал, как называется сообщение, то открывал файл <WinUser.h> искал WM_NULL и смотрел далее сообщения, а по названию определял назначение, далее по справке и, в конце-концов, можно найти. Рассмотрим параметры в функции WndProc: 1) Дескриптор окна, к которому относится сообщение. 2) Само сообщение. 3) Параметр сообщения. 4) Параметр сообщения. Зачем два одинаковых параметра? Во-первых, не совсем одинаковые, а во-вторых смотрим: один параметр – 32 бита и второй 32. Очень многое можно указать в 64 битах, да еще 4 с лишним миллиарда возможных сообщений. Перейдем к практическим занятиям. Делаем кнопки! Код 2.1+SimWnd.cpp+------------------------------------------------
Код 2.1+SimWnd.cpp+------------------------------------------------Конец У Вас мог возникнуть вопрос, как узнать сигнатуру функций обработки сообщений. Просто. В файле <WindowsX.h> есть такой шаблон Cls_OnXXXXX. То есть, если нам нужен обработчик сообщения WM_COMMAND, то ищем Cls_OnCommand и смотрим сигнатуру. Разбор полетов. В этом коде мы использовали два новых сообщения: WM_CREATE и WM_COMMAND. Первое программист использует для задания «предпусковых» установок и создания динамических элементов управления. В нашем примере был употреблен второй случай – создание кнопки. WM_COMMAND призвано реагировать на команды программе, в основном, от элементов управления. Например, на нажатие кнопки. Заметьте, что объем кода 2.1 не многим больше, чем в 2.0. Об этом я и говорил в начале, что основная часть кода копируется из приложения в приложение. В обработчике OnCreate вызывается функция CreateWindow, вот ее параметры: 1) Символьное название класса. 2) Название элемента или текс в новом элементе. 3) Стиль окна. Их много… 4,5,6,7) Координаты x, y, dx, dy. 8) Дескриптор окна-родителя. 9) Дескриптор меню или идентификатор элемента. 10) Дескриптор модуля. 11) Указатель на значение, которое будет послано в структуре CREATESTRUCT. В основном, этот обработчик используется для загрузки настроек, ресурсов, подключения динамических библиотек и т. п. Обработчик OnCommand имеет менее разнообразное назначение (хотя, кто знает, что можно выдумать…). Этот обработчик получает четыре параметра – дескриптор главного окна, идентификатор элемента, дескриптор этого элемента и дополнительная информация. В последнем примере обрабатывается идентификатор кнопки, который задается 9-м параметром функции CreateWindow, в котором указывается целое число, преобразованное с помощью HMENU. А пример достаточно полно показывает «способ употребления» этого обработчика. В файле тот же текст, но в формате "doc". Остаюсь без компьютера (еду в инст). Статью писал наскоро, потому прошу модераторов редактировать недостатки, сообщаяя о них на ПМ. |
||||||||||||||
|
|||||||||||||||
Fixin |
|
|||
Ёжик Профиль Группа: Комодератор Сообщений: 1357 Регистрация: 6.1.2004 Репутация: 8 Всего: 18 |
||||
|
||||
ShadowPhoenix |
|
||||||
Новичок Профиль Группа: Участник Сообщений: 4 Регистрация: 15.6.2006 Где: минск Репутация: нет Всего: нет |
По поводу первой програмки... попробовал откомпилировать в MVS2005 и получил такую козу:
что я тут не понял: 1. С какого перепугу "MessageBoxW" вместо "MessageBox", в MSDN такой фишки вообще нет... ну это ладно. 2. поповоду самого "MessageBox" в MSDN :
т.е. по логике вещей должно приводиться??? или я чего не так понимаю. далее начал химичить пытаясь его запустить хоть как-то... и пришел к такому вот обрезаному виду:
так собственно сам вопрос как строку к этому страшному LPСWSTR приводить... перепробовал все известные мне способы , из знакомых никто не знает Это сообщение отредактировал(а) ShadowPhoenix - 15.6.2006, 17:56 |
||||||
|
|||||||
Earnest |
|
|||
Эксперт Профиль Группа: Экс. модератор Сообщений: 5962 Регистрация: 17.6.2005 Где: Рязань Репутация: 33 Всего: 183 |
Очень многие имена API, которые описаны в MSDN, есть не функции, а макросы. К ним, в частности, относится и MessageBox. На самом деле есть 2 различных ф-и: MessageBoxA и MessageBoxW. Первая используется, если приложение работает с ANSI-строками, второе - с UNICODE. Соответственно, MessageBox - это либо MessageBoxA, либо MessageBoxW. Теперь о перепуге. Неизвестно, с какого перепуга, но начиная с VC 2005, проект по умолчанию генерится как UNICODE. А мужики-то не знают... Сколько уже несчастных новичков на эти грабли наступило. Тебе всего лишь надо свои строки заменить их широкими аналогами. Т.е. добавить L перед кавычками (L"XXX"). Есть способ сделать код независимым от типа используемых символов: 1) заворачивать все строки в макрос _T: _T("XXX") 2) всегда использовать t-аналоги CRT-функций: не strlen, а _tcslen, и т.д. 3) использовать типы TCHAR, LPTSTR вместо char, LPSTR... Теперь ты знаешь, что твое приложение настроено на UNICODE Это сообщение отредактировал(а) Earnest - 15.6.2006, 18:36 -------------------- ... |
|||
|
||||
maxim1000 |
|
|||
Эксперт Профиль Группа: Участник Сообщений: 3334 Регистрация: 11.1.2003 Где: Киев Репутация: 2 Всего: 110 |
ShadowPhoenix, возможно, эта тема будет полезна:
http://forum.vingrad.ru/index.php?showtopi...p;hl=messagebox -------------------- qqq |
|||
|
||||
0x07L |
|
|||
Опытный Профиль Группа: Участник Сообщений: 272 Регистрация: 10.6.2006 Где: Москва Репутация: 4 Всего: 5 |
Конечно, я здесь далеко не самый умный, но попытаюсь тебе помочь.
MessageBox, CreateWindow и еще много функций, которые ты встретишь в MSDN - это макросы вида #ifdef _UNICODE #define SomeFunction(...) SomeFunctionW (...) #else #define SomeFunction(...) SomeFunctionA (...) #endif Открой WinUser.h и найдешь там множество таких конструкций (в т.ч. и для MessageBox). Суть конструкций в следующем: если где-нибудь в проекте объявлен _UNICODE (это можно сделать с помощью директивы препроцессора #define или установить в свойствах проекта), то препроцессор (специальная вещь, обрабатывающая твой исходный код перед тем, как последний попадет в компилятор) заменяет вызовы функции SomeFunction на вызовы функции SomeFunctionW. Если _UNICODE не объявлен, подставляется вызов функции SomeFunctionA (A значит ANSI). Так в сообщении об ошибках появился MessageBoxW. Именно MessageBoxW, а не MessageBoxA, поскольку в 2005ой Студии Юникод по умолчанию включен. Если он тебе не нужен, его можно отключить в свойствах проекта, и никаких ошибок при компиляции примера у тебя не будет (будет, правда, пара предупреждений о том, что ты злонамеренно отключил Юникод). Зачем нужен этот Юникод? Дело в том, что обычно текстовые символы кодируются значениями от 0 до 255, занимающими 1 байт. Естественно, что 256 символов недостаточно, чтобы закодировать символы всех национальных алфавитов. На наших с тобой компьютерах русские символы кодируются значениями где-то в интервале 128..255. В индийских, для примера, версиях винды такие же коды будут соответствовать совсем другим символам. Таким образом, у разработчика слишком мало возможностей для локализации своего продукта. Для того чтобы дать разработчику такие возможности, и был придуман Юникод, в котором каждый символ кодируется двумя байтами (на самом деле все несколько сложнее, но в общем дело обстоит именно так). Для того чтобы обеспечить поддержку Юникода, в заголовочных файлах windows.h и tchar.h объявлено несколько полезных типов данных и макросов. Во-первых, чтобы все строковые литералы, которые имеются в твоей программе, нужно пометить как "юникодовые". Ставишь перед ними букву L. "Сказала собака баскервилей." -> L"Сказала собака баскервилей." Есть полезный макрос _TEXT("..."), чтобы подставлять букву L, когда объявлен _UNICODE. "Сказала собака баскервилей." -> _TEXT("Сказала собака баскервилей.") Во-вторых, пометить нужно и символьные константы. Делать это проще всего с помощью макроса _T. 'Ю' -> _T('Ю') В-третьих, вместо char нужно использовать wchar_t, а лучше макрос TCHAR. char a; -> TCHAR a; В-четвертых, указатели на char не забудь поменять на указатели на TCHAR, они же LPTSTR. char * MyString; -> TCHAR * MyString = LPTSTR MyString. Что такое LPCWSTR и прочие LP...STR, можно узнать в MSDN (ищи Windows Data Types). Вот в принципе, и все. Внимание! Макрос TCHAR объявлен в tchar.h. Подключи его #include <tchar.h> Это сообщение отредактировал(а) 0x07L - 15.6.2006, 21:32 |
|||
|
||||
ShadowPhoenix |
|
|||
Новичок Профиль Группа: Участник Сообщений: 4 Регистрация: 15.6.2006 Где: минск Репутация: нет Всего: нет |
понял, въехал и разобрался... а вообще по науськаванию комнилятора перешел на класс wchar_t ... пока все работает ... А потом просто перешел на ANSI...
Всех благодря за помощь и понимание . |
|||
|
||||
ShadowPhoenix |
|
||||||||||||
Новичок Профиль Группа: Участник Сообщений: 4 Регистрация: 15.6.2006 Где: минск Репутация: нет Всего: нет |
Хм... возник еще один вопрос... уже по HANDLE_MSG, точнее по тому откуда он берет параметры...
ну в данном случае
можно заменить на
а вот дальше... Совершенно непонятно откуда береться параметр pCreateStruct при вызове
так же хотелось бы получить комментарии к параметрам функции
попробовал изменить
на
PS. Вначале думал здесь тему оставить, но тут вопросов по приведённому тексту всетки больше Это сообщение отредактировал(а) ShadowPhoenix - 16.6.2006, 17:18 |
||||||||||||
|
|||||||||||||
0x07L |
|
|||
Опытный Профиль Группа: Участник Сообщений: 272 Регистрация: 10.6.2006 Где: Москва Репутация: 4 Всего: 5 |
2_ShadowPhoenix
Пользуйся поиском. В WindowsX.h: #define HANDLE_MSG(hwnd, message, fn) \ case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn)) Здесь вместо HANDLE_MSG в зависимости от message подставляется другой макрос (HANDLE_WM_CREATE для WM_CREATE, HANDLE_WM_COMMAND для WM_COMMAND) В том же WindowsX.h (немного ниже): #define HANDLE_WM_CREATE(hwnd, wParam, lParam, fn) \ ((fn)((hwnd), (LPCREATESTRUCT)(lParam)) ? 0L : (LRESULT)-1L) LPCREATESTRUCT - это есть то же самое, что CREATESTRUCT * Вот откуда CREATESTRUCT В WindowsX.h: #define HANDLE_WM_COMMAND(hwnd, wParam, lParam, fn) \ ((fn)((hwnd), (int)(LOWORD(wParam)), (HWND)(lParam), (UINT)HIWORD(wParam)), 0L) id - это LOWORD(wParam) Для того чтобы разобраться в этих макросах, посмотри в MSDN описание сообщений WM_CREATE и WM_COMMAND. PS Кстати, воспользуясь случаем, порекомендую книжку Румянцева "Азбука программирование в Win32 API" (вроде так) Хорошая книжка для начинающих. |
|||
|
||||
ShadowPhoenix |
|
|||
Новичок Профиль Группа: Участник Сообщений: 4 Регистрация: 15.6.2006 Где: минск Репутация: нет Всего: нет |
||||
|
||||
Dmitry_177 |
|
|||
Опытный Профиль Группа: Участник Сообщений: 418 Регистрация: 22.9.2006 Репутация: нет Всего: нет |
Я новичек в Visual C++, решил слезть с Delphi и писать на VC++. Подскажите пожалуйста, как создавать API-приложения VC++? Создаю я новый проект "Win32 Project", потом на сколько я понимаю "stdafx.h" не нужен для написания программ на чистом API, то я его удаляю и в Solution Explorer-е тоже, там еще и "stdafx.cpp".. потом удаляю всю заготовку кода и пишу следующее:
Возникает такое сообщение "c:\apiwindow\apiwindow.cpp(83) : fatal error C1010: unexpected end of file while looking for precompiled header. Did you forget to add '#include "stdafx.h"' to your source? И exe-файл не создается... Что делать? Нужен ли этот stdafx и как с ним обходиться? |
|||
|
||||
maxim1000 |
|
|||
Эксперт Профиль Группа: Участник Сообщений: 3334 Регистрация: 11.1.2003 Где: Киев Репутация: 2 Всего: 110 |
stdafx используется для прекомпилированных заголовков, это не зависит от использования WinAPI (насколько я знаю)
при создании проекта в VC++2005 можно снять галочку "Precompiled headers", в двургих версиях - не помню... -------------------- qqq |
|||
|
||||
Dmitry_177 |
|
|||
Опытный Профиль Группа: Участник Сообщений: 418 Регистрация: 22.9.2006 Репутация: нет Всего: нет |
У меня тоже VS2005 убрал я "Precompiled headers" только я не увидел там галочки, а выбрал из списка "Not Using Precompiled Headers" в параметре "Create/Use Precompiled Header".. с stdafx теперь проблем вроде как нету, но теперь появились другие ошибки:
1: "c:\api\api\api.cpp(30) : warning C4244: 'return' : conversion from 'WPARAM' to 'int', possible loss of data" - на строке return "Msg.wParam;" в функции WinMain. 2: "c:\api\api\api.cpp(46) : error C2440: '=' : cannot convert from 'char [7]' to 'LPCWSTR'" - на строке "WndClass.lpszClassName = szAppName;" в функции Register. 3: "c:\api\api\api.cpp(57) : error C2664: 'CreateWindowExW' : cannot convert parameter 2 from 'char [7]' to 'LPCWSTR'" - на строке "HWND hwnd = CreateWindow(szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);" в функции Create. Подскажите пожалуйста, что опять не так? Это сообщение отредактировал(а) Dmitry_177 - 12.10.2006, 12:19 |
|||
|
||||
maxim1000 |
|
|||
Эксперт Профиль Группа: Участник Сообщений: 3334 Регистрация: 11.1.2003 Где: Киев Репутация: 2 Всего: 110 |
2 и 3 связаны с одним изменением в VC++2005
раньше по умолчанию define UNICODE не добавлялся, так что программа получалась ANSI, ну и функции, соответственно, с окончанием A в VC++2005 UNICODE по умолчанию включён, так что надо сделать одно из двух: 1. передавать всем API-функциям unicode'овские строки (L"qqq") (ну или использовать TEXT("qqq"), чтобы универсально было) 2. залезть в свойства проекта и Use Unicode set первое - всего лишь предупреждение в принципе, конечно, можно внимания на него не обращать лично я предпочитаю в таких случаях написать явное преобразование (конечно же после того, как проанализирую, корректно ли оно), это приводит к тому, что если ошибка и будет, то её, по крайней мере, будет видно, что ускоряет её поиск... -------------------- qqq |
|||
|
||||
ressac |
|
|||
Опытный Профиль Группа: Участник Сообщений: 345 Регистрация: 25.11.2006 Репутация: нет Всего: 1 |
я компилю первые два примера под gcc и у меня вместе с окном открывается консоль, как этого избежать?
|
|||
|
||||
Правила форума "C/C++: Системное программирование и WinAPI" | |
|
На данный раздел распространяются Правила форума и Правила раздела С++:Общие вопросы . Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Chipset, Step, Fixin, GremlinProg, xvr. feodorv. |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | C/C++: Системное программирование и WinAPI | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |