Вам еще не надоело создавать консольные приложения? Я так и знал: Ну что ж, тогда я не зря старался при написании данного материала, который обязан пролить свет на программирование в среде Windows, и научить вас создавать полноценные оконные приложения, в зависть вашему соседу, который еще этого не умеет.
Система, основанная на сообщениях
Windows можно назвать объектно-ориентированной системой, хотя формально она таковой не является. Представьте себе систему в виде набора объектов, основным из которых является окно. В процессе работы, Windows «общается» с каждым объектом посредством системных сообщений. При возникновении определенных событий, Windows сообщает об этом окну приложения, посылая ему соответствующее сообщение. Окно, после получения сообщения, должно обработать его и возвратить результат обратно в систему.
Как программа узнает о том, что ее окну послано сообщение? Ответ напрашивается сам. На протяжении всей работы программы необходимо проверять, имеются ли в очереди сообщения. Делается это, конечно-же, с помощью цикла, который выполняется в течении всей работы программы. При каждом проходе цикла проверяется наличие сообщений в очереди, и если таковые имеются, тогда программа поочередно получает их и обрабатывает.
ПОДКЛЮЧАЕМ SERIAL COM PORT — C++ WINAPI ЧАСТЬ #8
Win32 API
Win32 API (Application Programming Interface — интерфейс прикладного программирования) — это набор функций, позволяющих программисту создавать приложения для Windows. Win32 API является основой для каждой Windows-программы.
Программисты, пишущие на С++ уже привыкли, что точкой входа в программу является функция main(). Но в системе Windows это не так. Все Win32-приложения используют в качестве точки входа в программу функцию WinMain. Ее объявление можно найти в заголовочном файле winbase.h:
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd );
Давайте рассмотрим все ее аргументы:
- hInstance — идентификатор экземпляра приложения.
- hPrevInstance — идентификатор предыдущего экземпляра приложения. В Win32 он всегда равен нулю.
- lpCmdLine — указатель на командную строку.
- nShowCmd — флаги для окна.
Как видите — все не так сложно! Вас, наверное, удивили только какие-то непонятные типы данных. Не волнуйтесь — разберемся.
Типы данных в Windows
При первом взгляде на исходный код Windows-приложения, начинающих программистов начинают пугать «странные» типы данных, используемые в программе. Но это только на первый взгляд. На самом, деле разобраться в них вам не составит особого труда. Основные типы данных Win32 API приведены в таблице 1.
| BOOL, BOOLEAN | Булев. Имеет только 2 значения: TRUE или FALSE |
| CHAR | 8-битный символ (ANSI Windows) |
| WCHAR | 16-битный символ (Unicode) |
| TCHAR | CHAR или WCHAR (если используется Unicode) |
| USHORT, WORD | Целое беззнаковое 16-битное число |
| DWORD, DWORD32, UINT32 | Целое беззнаковое 32-битное число |
| DWORD64, UINT64, ULONGLONG | Целое беззнаковое 64-битное число |
| FLOAT | Число с плавающей точкой |
| SHORT | Целое знаковое 16-битное число |
| INT, INT32, LONG, LONG32 | Целое знаковое 32-битное число |
| INT64, LONG64, LONGLONG | Целое знаковое 64-битное число |
| VOID | Пустой тип |
Уроки WinApi C++ | Работа с окнами виндовс | HWND на примерах
Структура Windows-программ
Каждая Windows-программа состоит как минимум из двух основных функций. Это WinMain и функция окна.
Давайте напишем простую программу, создающую пустое окно. Для этого в Visual C++ создайте пустой проект Win32 Application, добавьте новый файл (например, myprog.cpp), и вставьте туда следующий код:
Теперь давайте рассмотрим приведенный код подробнее.
К сожалению, привести здесь описания всех типов данных, структур, функций и т.д., используемых в Win32 API, не представляется возможным, поэтому советую обратиться к официальному источнику, где можно найти всю необходимую информацию о программировании под Windows — Microsoft Developer Network (msdn.microsoft.com).
Для того чтобы создать окно, сначала необходимо зарегистрировать в системе новый оконный класс. Для этого нужно заполнить структуру WNDCLASS и передать указатель на область памяти, содержащую эту стуктуру, функции RegisterClass(). Данная функция создает и регистрирует новый оконный класс, используя значения элементов переданной ей структуры для определения характеристик класса. Ниже приведены описания всех элементов структуры WNDCLASS.
- style — стиль окна данного класса.
- lpfnWndProc — указатель на функцию окна, которая вызывается каждый раз при получении окном нового сообщения.
- cbClsExtra и cbWndExtra — дополнительный размер резервируемой памяти (в байтах) для структур класса и окна.
- hInstance — идентификатор приложения.
- hIcon — идентификатор иконки, связанной с классом.
- hCursor — идентификатор курсора, связанного с классом.
- hbrBackground — идентификатор кисти, которой будет закрашен фон окна.
- lpszMenuName — имя меню.
- lpszClassName — имя создаваемого класса окна.
Как вы, наверное, заметили, в приведенном выше коде явно заполняются не все поля структуры, а только те, которые необходимы в для данного класса. Все остальные поля инициализируются нулевыми значениями с помощью функции ZeroMemory(), которая выполняет аналогичное ее имени действие.
После регистрации класса окна можно приступать к созданию самого окна. Для этого используется функция CreateWindow(), которая возвращает хендл создаваемого окна.
Создав окно, нам необходимо отобразить его на экране. Этим занимается функция ShowWindow(), которая принимает в качестве аргументов идентификатор окна, и флаг, указывающий на способ отображения окна (в данном случае SW_SHOW, определяющий, что окно необходимо показать на экране). Затем, с помощью функции UpdateWindow() мы посылаем нашему окну сообщение WM_PAINT, указывающее на необходимость перерисовать клиентскую область окна.
Далее следует цикл обработки сообщений. Он состоит из управляющей структуры while, которая при каждом проходе цикла получает очередное сообщение из очереди, посредством функции GetMessage(), затем переводит все сообщения от виртуальных клавиш в символьные сообщения с помощью функции TranslateMessage() (о предназначении данной операции мы поговорим позже), и после этого отсылает полученное сообщение на обработку оконной процедуре, используя функцию DispatchMessage().
Функция GetMessage() возвращает ненулевое значение, поэтому цикл не завершается до момента завершения программы. При завершении программы окну посылается сообщение WM_QUIT, которое является единственным сообщением, при получении которого функция GetMessage() возвращает ноль, и цикл обработки сообщений завершается, а код выхода из программы, хранящийся в элементе wParam структуры MSG, возвращается функцией WinMain.
И наконец, пора разобраться каким же образом оконная процедура обрабатывает переданные ей сообщения. У функции окна имеется четыре аргумента:
- hWnd — идентификатор окна.
- msg — код текущего сообщения.
- wParam и lParam — дополнительная информация о сообщении.
В Windows существует более тысячи стандартных сообщений. Конечно же, программист не должен обрабатывать их все. Для этого существует функция DefWindowProc(), которая обрабатывает переданное ей сообщение по умолчанию. Таким образом, вы должны обрабатывать только те сообщения, обработка по умолчанию которых вас не устраивает.
Также, функция DefWindowProc() не обрабатывает сообщение WM_DESTROY, поэтому вы должны предусмотреть его обработку самостоятельно. В приведенном примере, при получении окном сообщения WM_DESTROY, мы, с помощью функции PostQuitMessage(), ставим в очередь сообщение WM_QUIT, чтобы завершить работу программы.
Заметьте, каким образом сообщения обрабатываются по умолчанию. В структуре switch оконной процедуры предусмотрена метка default, которая пересылает все необрабатываемые нашей программой сообщения функции DefWindowProc() и возвращает результат этой функции. А если сообщение обрабатывается нашей программой, тогда возвращается ноль.
Вот оно, чудо
Теперь, разобравшись с кодом программы, откомпилируйте созданный проект и запустите приложение. Вы увидите пустое окно, с которым уже можно выполнять стандартные действия — перемещать, изменять размеры, сворачивать/разворачивать, и даже закрыть! : Все это достигается одним единственным стилем окна WS_OVERLAPPEDWINDOW, определенным при создании окна функцией CreateWindow().
Примечание: в элементе style, структуры WNDCLASS, определяется общий стиль для всех окон данного класса. Следует заметить, что стиль класса это не тоже самое что и стиль окна, указанный в вызове функции CreateWindow(). Тот стиль, который устанавливается посредством функции CreateWindow(), является индивидуальным стилем окна, а не общим стилем, определенным в классе.
Ресурсы программы
Практически в каждой Windows-программе можно увидеть различные элементы управления, меню, и другие ресурсы программы. Создать в окне какой либо элемент управления, например, кнопку, можно двумя способами. Первый, это создать новое окно используя функцию CreateWindow() с предопределенным в системе оконным классом «button».
Второй способ, это использовать файлы ресурсов, в которых содержится описания всех ресурсов программы, будь то меню, элементы управления, иконки и даже диалоговые окна. Каждый элемент управления имеет свой уникальный идентификатор (хендл) определяемый программистом. Когда пользователь совершает какие либо действия над элементом управления, сообщение об этом поступают окну, и после этого выполняются соответствующие действия. К примеру, при нажатии на кнопку окно получает сообщение WM_COMMAND, которое в параметре wParam содержит идентификатор кнопки.
Подробнее о ресурсах приложения и работе с сообщениями мы поговорим несколько позже.
Источник: codenet.ru
Ввод и вывод данных Win32 API
В этой статье мы научимся обрабатывать данные, поступающие с клавиатуры. В дальнейшем это будет полезным пособием при создании простейшего текстового редактора типа notepad.exe.
Ниже представлен листинг:
#include LRESULT CALLBACK WindowProcess(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR pCommandLine, int nCommandShow) < TCHAR className[] = L»Мой класс»; HWND hWindow; MSG message; WNDCLASSEX windowClass; windowClass.cbSize = sizeof(windowClass); windowClass.style = CS_HREDRAW | CS_VREDRAW; windowClass.lpfnWndProc = WindowProcess; windowClass.lpszMenuName = NULL; windowClass.lpszClassName = className; windowClass.cbWndExtra = NULL; windowClass.cbClsExtra = NULL; windowClass.hIcon = LoadIcon(NULL, IDI_WINLOGO); windowClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO); windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); windowClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); windowClass.hInstance = hInst; if(!RegisterClassEx( MessageBox(NULL, L»Не получилось зарегистрировать класс!», L»Ошибка», MB_OK); return NULL; >hWindow = CreateWindow(className, L»Программа ввода символов», WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, NULL, CW_USEDEFAULT, NULL, (HWND)NULL, NULL, HINSTANCE(hInst), NULL ); if(!hWindow) < MessageBox(NULL, L»Не получилось создать окно!», L»Ошибка», MB_OK); return NULL; >ShowWindow(hWindow, nCommandShow); UpdateWindow(hWindow); while(GetMessage( TranslateMessage( DispatchMessage( >return message.wParam; > LRESULT CALLBACK WindowProcess(HWND hWindow, UINT uMessage, WPARAM wParameter, LPARAM lParameter) < HDC hDeviceContext; PAINTSTRUCT paintStruct; RECT rectPlace; HFONT hFont; static char text[2]=; switch (uMessage) < case WM_CREATE: MessageBox(NULL, L»Пожалуйста, вводите символы и они будут отображаться на экране!», L»ВНИМАНИЕ. «, MB_ICONASTERISK|MB_OK); break; case WM_PAINT: hDeviceContext = BeginPaint(hWindow, GetClientRect(hWindow, SetTextColor(hDeviceContext, NULL); hFont=CreateFont(90,0,0,0,0,0,0,0, DEFAULT_CHARSET, 0,0,0,0, L»Arial Bold» ); SelectObject(hDeviceContext,hFont); DrawText(hDeviceContext, (LPCWSTR)text, 1, EndPaint(hWindow, break; case WM_KEYDOWN: switch (wParameter) < case VK_HOME:case VK_END:case VK_PRIOR: case VK_NEXT:case VK_LEFT:case VK_RIGHT: case VK_UP:case VK_DOWN:case VK_DELETE: case VK_SHIFT:case VK_SPACE:case VK_CONTROL: case VK_CAPITAL:case VK_MENU:case VK_TAB: case VK_BACK:case VK_RETURN: break; default: text[0]=(char)wParameter; InvalidateRect(hWindow, NULL, TRUE); break; >break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWindow, uMessage, wParameter, lParameter); > return NULL; >
Данная программа выводит цифры и буквы, при этом не реагирует на нажатие дополнительных клавиш, типа enter, shift и т.д.
В функции WinMain() ничего нового. А в WindowProcess() мы добавили обработчики событий нажатия клавиши.
Нажатие любой клавиши автоматически посылает системе аппаратное сообщение, которое обрабатывается и высылается приложению. За эту операцию отвечает сообщение WM_KEYDOWN .
Его описание сложно и на данном этапе не имеет смысла, так как мы не включили кириллицу. При посылке этого сообщения в wParameter записывается значение нажатой клавиши. Конечно же, всё зависит от клавиатуры, раскладки. Поэтому всего кодов для клавиш много и мы не будем описывать, что делать при нажатии той или иной, а просто исключим значения не нужных нам клавиш.
С 87-й по 101-ю строки мы обрабатываем сообщение WM_KEYDOWN (нажатие клавиши). Сначала мы создаём переключатель switch для значения wParameter , куда записан идентификатор клавиши. С 90-й по 95-ю строки мы исключаем значения home, end, page up, page down, стрелка влево, стрелка вправо, стрелка вверх, стрелка вниз, delete, shift, space, ctrl, caps lock, alt, tab, backspace, enter соответственно. Возможно, на вашей клавиатуре есть ещё клавиши, вы можете также их исключить при необходимости, потому что при нажатии будет «абракадабра». То есть если в идентификаторе wParameter у нас эти значения, мы ничего не делаем – в 96-й строке оператор break .
А под значениями default у нас выполняется преобразование. В 66 строке мы создали статическую строку типа char из 2-х элементов. Статическая она, потому что в процессе работы программы мы неоднократно выходим из WindowProcess() в цикл обработки с GetMessage() , расположенный в WinMain() , а значение клавиши нам нужно сохранять. Ну а в 98 строке мы инициализируем её первый элемент wParameter -ом.
В следующей строке мы вызываем функцию InvalidateRect() , после чего выходим из WM_KEYDOWN . Первым параметром идёт – дескриптор окна, вторым – область будущего «затирания» окна ( NULL – всё окно перерисовывается в WM_PAINT ), третьим параметром идёт «затирание» фона (если TRUE , то стираем). При этом мы автоматически посылаем приложению message WM_PAINT и окно перерисовывается заново. Для чего это нужно?
Как мы помним в случаях сворачивания, перемещения окна вызывается WM_PAINT . Нам же это нужно сделать явно, то есть сделать окно недействительным, для того, чтобы сразу вывести значение нажатой клавиши на экран. Иначе она появится только тогда, когда мы начнём манипулировать окном.
Теперь возвратимся 65-й строке. В ней объявлен экземпляр класса HFONT , который отвечает за шрифт, его размер и другие премудрости. Он будет использоваться в WM_PAINT .
В 69-й строке есть обработчик сообщения WM_CREATE . Он срабатывает, когда создаётся окно, то есть вызывается функция CreateWindow() . На этом этапе решено создать дополнительное окно с сообщением для удобства (строки 70-72).
Наконец, обработчик WM_PAINT . С 75-й по 77-ю строки происходит вызов функций контекста устройства (описывалось в статье).
А с 78-й по 82-ю строки вызывается функция CreateFont() . С её помощи мы меняем шрифт, и размер буквы. Вот её описание:
HFONT CreateFont(int, // высота шрифта int, // ширина символов int, // угол наклона букв int, // угол наклона строки int, // толщина букв («жирность») DWORD, // курсив DWORD, // подчеркивание DWORD, // перечеркивание DWORD, // набор символов DWORD, // точность вывода DWORD, // точность отсечения DWORD, // качество вывода DWORD, // шаг между буквами LPCTSTR // имя шрифта );
Шрифт выставлен Arial Bold – то есть Arial жирным. В строке 83 мы задаём контексту изменённые параметры шрифта. С 84-й по 85-ю строки ничего нового. Мы вводим на экран полученный идентификатор клавиши, преобразованный в тип char . В функции DrawText() его нужно преобразовать к типу LPCWSTR , что она и требует.
Результат работы программы при вводе буквы «с» такой:


В следующей статье мы научимся обрабатывать дополнительные клавиши, переключать раскладку, создавать таймеры отсчёта.
Источник: cppstudio.com
Примеры программ на си для windows

Вызов функции, которая находится в dll-файле:
Краткое описание примера:
При нажатии на кнопку вызывается функция, которая выводит сообщение «Hello, you’re calling a function in this DLL» на экран. Функция находится в dll-файле.
Инструкция:
— скачать и установить инструментарий
— создать dll-файл (с именем: testfunc.dll) со следующем кодом (см. примечание 2):
#include #include __declspec(dllexport) void __cdecl TestHello(void); BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) < switch (ul_reason_for_call) < case DLL_PROCESS_ATTACH: MessageBox(NULL, _T(«The DLL is loaded»), _T(«DLL Skeleton»), MB_OK); break; case DLL_THREAD_ATTACH: MessageBox(NULL, _T(«The DLL is unloaded»), _T(«DLL Skeleton»), MB_OK); break; case DLL_THREAD_DETACH: MessageBox(NULL, _T(«A thread is created in this process»), _T(«DLL Skeleton»), MB_OK); break; case DLL_PROCESS_DETACH: MessageBox(NULL, _T(«A thread is destroyed in this process»), _T(«DLL Skeleton»), MB_OK); break; >return TRUE; > void TestHello(void)
— создать релизный dll-файл
— создать проект Visual 2008
— создать диалоговое окно в ResEdit
— добавить на диалоговое окно следующие элементы GUI из панели Toolbox: Button — 1 шт.
— выравнять элемент GUI
— сохранить проект ResEdit (Ctrl+S)
— подключить файл ресурсов к проекту Visual 2008
— скопировать релизный dll-файл в созданный проект Visual 2008 (в туже директорию, где main.c)
— скопировать следующий код в main.c
#include #include #define IDD_DIALOG1 100 INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); LPCTSTR DlgName = MAKEINTRESOURCE(IDD_DIALOG1); int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) < DialogBoxParam(hInstance, DlgName, NULL, DialogProc, (LPARAM) NULL); return TRUE; >#define IDC_BUTTON1 3000 HMODULE hLib; FARPROC TestHelloAddr; void (*TestHello)(void); INT_PTR CALLBACK DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) < switch(uMsg) < case WM_COMMAND: if (LOWORD(wParam) == IDC_BUTTON1) < hLib = LoadLibrary(_T(«testfunc.dll»)); if (hLib == NULL) < MessageBox(NULL, _T(«Cannot load library»), _T(«Load Library»), MB_OK); break; >TestHelloAddr = GetProcAddress(hLib, «TestHello»); if (TestHelloAddr == NULL) < MessageBox(NULL, _T(«TestHello function not found»), _T(«Load Library»), MB_OK); break; >TestHello = (void(*)(void))TestHelloAddr; (*TestHello)(); FreeLibrary(hLib); > break; case WM_CLOSE: EndDialog(hWnd, 0); break; default: return FALSE; > return TRUE; >
Примечания:
1. В файле ресурсов «*.rc» нужно определить:
#define IDC_BUTTON1 3000
2. Об использовании динамических библиотек можно почитать на wasm’e. В данном примере, использован код из урока Iczelion’а, почти без изменений. Далее по ссылке код урока Iczelion’а на Си и ссылка на туториал на wasm’e: http://www.programmersforum.ru/showp. 3 20.09.2012 в 19:27 .
Источник: www.programmersforum.ru