С++. Что нам стоит GUI построить
Всем доброго времени суток.
Я все время программировал на C++Builder с использованием компонентов VCL и чувствовал себя прекрасно, а вот те, к кому я потом переносил свои программы, - не очень. Т.к. чтобы на машине, где нет BCB, запустилось приложение, написанное с помощью VCL, требуется включить некоторые билдеровские bpl-ки и dll-ки в проект, что увеличит его размер. И хотя с современным оборудованием это - не проблема, хочется чтобы приложение было максимально компактным. Решение этой проблемы было найти не трудно - использовать консольные приложения без vcl.h =) Но этот вариант хорош, если приложение не требует "интенсивного общения" пользователя с программой и пишется не для домохозяек, а для людей, которым все понятно, и которых все устравиает в консоли. А как быть, если приложению действительно нужен GUI, и просто "черным окошком" не отделаться? Ответ на этот вопрос так же почти очевидный - использовать Win API.
Win API — это аббревиатура, которая расшифровывается как Windows Application Programming Interface, то есть, проще говоря, это - набор системных библиотек содержащих в себе множество различных функций. Этот способ создания приложений является самым низкоуровневым способом для написания программ, который доступен для прикладного программирования. Но, несмотря на наличие различных библиотек, облегчающих создание оконных приложений для windows, таких как MFC или VCL, данный способ не теряет своей актуальности, так как вы можете получить в результате очень компактные приложения, к тому же не зависящие от внешних ресурсов, для выполнения которых достаточно только стандартных возможностей windows.
Так же программирование с помощью Win API дает ряд других преимуществ: высокая скорость выполнения кода, маленький размер готовой программы, более широкие возможности для программиста. Но за все хорошее приходиться платить - программирование с использованием API является гораздо более трудоемким и приводит к значительно большим объемам исходного кода программы, чем программирование с использованием дополнительных библиотек.
Сегодня я покажу Вам как написать простую и, к сожалению, бесполезную программу на чистом Win API. Наша программа будет представлять из себя окно с тремя кнопками. При нажатии на первую кнопку у нас по таймеру будет меняться текст в главном окне программы, на вторую - таймер будет останавливаться, а при нажатии на третью программа будет закрываться.
Хоть программа и является бесполезной, это не значит, что данный материал так же бесполезен. Т.к. большинство Win API приложений имеют схожую структуру то на основе этой программы можно создать любое Win API приложение.
Подготовка к написанию
Писать программу я буду в Microsoft Visual Studio 2010. Если у Вас более ранние версии - ничего страшного. Процесс создания нового приложения мало чем отличается во всех версиях MVS начиная с 2005, а язык, на котором будем писать, не отличается вообще.
Запускаем Microsoft Visual Studio и создаем новое приложение (Рис. 1).

Рисунок 1. Создание нового приложения
У нас появилось новое окно. В "установленных шаблонах" выбираем Visual C++, а тип приложения - Win32 Project. Так же не забываем дать проекту имя, в моем случае это "winApi" (Рис. 2).

Рисунок 2. Выбираем тип проиложения
После нажатия на кнопку "OK" перед нами появляется так называемый визард нашего приложения. В нем мы указываем его дополнительные параметры. В окне визарда жмем кнопку "Next>" (Далее) и в появившемся окне указываем тип приложения (Application Type) "Windows application" (Приложение windows), а в качестве дополнительных параметров (Additional options) устанавливаем "Empty Project" (Пустой проект) (Рис. 3).

Рисунок 3. Настройка визарда
Жмем кнопку Finish (Готово).
С левого края монитора появилось структурное дерево нашего проекта. Т.к. мы создавали пустой проект, то нам требуется добавить .cpp файл, в котором мы будем писать код. Для этого проделайте действия, показанные на рисунке 4.

Рисунок 4. Добавление новго файла в проект
В появившемся окне введите имя Вашего .cpp файла, выбирите тип файла и нажмите кнопку "Add" (Добавить) (Рис. 5).

Рисунок 5. Добавляем .cpp файл
В появившемся окне редактора кода мы и будем писать нашу программу.
Когда мы полностью подготовили проект, можно смело приступать к основной части - написанию кода.
Код
Для начала следует подключить две библиотеки.
#include <windows.h> #include <tchar.h>
В первой библиотеке описано большинство API-Функций, а со второй библиотекой будет сложнее. Т.к. Microsoft Visual по умолчанию настроен на работу с юникодом, то и строки в наши дальнейшие функции надо подавать в совместимости с UNICODE. UNICODE строки отличаются от обычных строк вот чем: wchar_t - 16 бит; char - 8 бит. Т.к. наши сообщения и заголовки окон мы будем вводить в ANSI, то нам понадобиться функция, которая будет перегонять нашу ANSI-строку в UNICODE. Эта функция (а точнее макрос) и описана в библиотеке tchar.h и называется _T. Единственным параметром этой функции является ANSI-строка, а возвращает она эту строку в UNICODE.
Далее мы добавим пять глобальных переменных и прототип функции.
TCHAR szWindowClass[] = _T("win32app"), szTitle[] = _T("Win32API"); HINSTANCE hInst; bool timerEnabled=false, timerSignal=false; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
В первой строке мы объявляем имя класса и заголовок (т.е. то, что будет показано на title-bar'e нашего приложения). С типом tchar мы уже ознакомились, так что все должно быть понятно. Вторая строка - это переменная типа HINSTANCE. По сути HINSTANCE - это то же unsigned int, в нем будет хранится дескриптор (уникальный идентификатор) нашего окна. Третья строка - две переменные логического типа. Пока что скажу, что они нужны для обработки событий нашего таймера. Когда дойдем до обработки этих событий, ты поймешь для чего они нужны. И в последней строке у нас идет описание одной из важнейших функций любого Win32 API приложения - функции обработки сообщения. LRESULT эквивалентен типу long. CALLBACK значит, что функция вызывается операционной системой в ответ на поступившее новое сообщение. Ну, и WndProc - имя нашей функции. Вообще, оно может быть любым, но традиционно принято называть ее так, а против традиций не попрешь.
На вход функция принимает 4 параметра. Первый - дескриптор окна, в котором будут отлавливаться сообщения, второй - идентификатор сообщения, которое было перехвачено, третий и четвертый параметры - это дополнительные параметры для конкретного сообщения. Эти параметры будут разными для разных сообщений, а для некоторых сообщений Windows могут вообще не использоваться. Например, наша программа будет реагировать на сообщение таймера WM_TIMER, в нем параметр WPARAM - идентификатор таймера, отправившего сообщение WM_TIMER. Именно с помощью wParam ты можешь определить, что за таймер послал сообщение (а таймеров в программе может быть несколько). LPARAM - это указатель на CALLBACK-функцию, которая обрабатывает сообщение WM_TIMER. Конкретно что означают эти параметры для некоторого сообщения Windows, надо смотреть в справке по этому сообщению.
Далее, как и в любой с++ программе следует функция main, только в случае с API эта функция будет называться WinMain. Однако роль ее та же, с этой функции наша программа начинает свое выполнение.
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
В функцию WinMain передается 4 параметра. Первые 2 из них - указатели на экземпляры окон. Первый параметр при этом содержит указатель на текущий экземпляр приложения, второй всегда равен NULL. Раньше второй параметр содержал handle предыдущего запущенного экземпляра этого же приложения (например, если ты запускал второй экземпляр этого приложения). Третий параметр - указатель на строку, заканчивающуюся нуль-символом. Эта строка - это параметры командной строки, передаваемой в приложение. Параметры командной строки включают и имя программы. И, наконец, четвертый параметр определяет способ показа главного окна нашего приложения - т. е. показывать ли его развернутым на целый экран, или в нормальном виде, или в сложенном, и др.
Начинается функция WinMain созданием и заполнением переменной типа WNDCLASSEX. WNDCLASSEX - структура, в которой содержится информация о классе окна.
WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL; wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
Тут мы последовательно установили размер структуры в байтах; стиль класса, он определяет дополнительные элементы окна. Используемые нами элементы (CS_HREDRAW | CS_VREDRAW) означают, что окно будет перерисовано, если будет изменена его ширина и/или высота соответственно. Затем указали оконную процедуру, которая будет выполнятся переменной типа этой структуры; установили, что переменная hInstance нашей структуры должна содержать указатель на текущий экземпляр приложения. Задали значение иконки приложения и курсора, соответствующего нашему приложению, цвет фона, имя класса. О четерех оставшихся параметрах прочтешь на msdn.com, просто в поиске введи имя этой структуры.
Далее по коду идет регистрация класса с помощью функции RegisterClassEx(). Единственный параметр функции - указатель на структуру WNDCLASSEX. Важно сначала заполнить структуру, перед тем как вызывать эту функцию. А возвращает функция при успешном выполнении уникальный идентификатор зарегистрированного класса. Если же функция выполнилась не очень удачно, возвращает ноль.
if (!RegisterClassEx(&wcex)) { MessageBox(NULL, _T("Call to RegisterClassEx failed!"), _T("Win32 API"), NULL); return 1; }
Суть понятна: если функция, по каким-то причинам, провалила свое выполнения - нам пишут сообщение, о том, что произошел небольшой коллапс и мы выходим из функции WinMain.
После того, как мы зарегистрировали наш класс, загоним его дескриптор - в нашу глобальную переменную типа HINSTANCE.
hInst = hInstance;
Теперь мы, наконец-то, создаем окно. Делаем мы это с помощью функции CreateWindow(). В этой функции мы указываем имя класса, на основании которого мы создаем наше окно, имя окна, стиль окна, т.е. как будет выглядеть наше окно при создании, координаты по X и по Y, длину и ширину нашей формы. Восьмой и девятый параметры этой функции нам сейчас не нужны. О них я расскажу при создании кнопки (забегая вперед, скажу, что кнопка - это такое же окно, просто дочернее по отношению к главному окну). Предпоследний параметр - указатель на дескриптор окна, а последний - указатель на значение, которое передается окну параметром lParam сообщения WM_CREATE. У нас это значение - NULL, т.к. нам не нужны никакие дополнительные данные. При удачном выполнении функция возвращает дескриптор нового окна.
HWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 300, NULL,NULL,hInstance,NULL); if (!hWnd) { MessageBox(NULL, _T("Call to CreateWindow failed!"), _T("Win32 API"), NULL); return 1; }
Основной код я описал выше, но как ты видишь, тут тоже есть часть, которая реагирует на неудачное выполнение функции.
Теперь все операции с окном мы будем делать с помощью, полученного в результате выполнения функции, дескриптора. Используем этот дескриптора для того, чтобы показать и обновить наше окно. Делается это с помощью функций ShowWindow() и UpdateWindow() соответственно. В первую функцию мы передаем дескриптора окна и режим его отображения. Первый раз эта функции должна быть вызвана с параметром nCmdShow, а далее с любым из подходящих параметров (на msdn.com ты сможешь найти эти самые подходящие параметры). Вторая принимает дескриптора окна, которое надо обновить. Обновление происходит посредством отправки сообщения WM_PAINT. После того, как мы создали и показали окно, нам остается только обрабатывать входящие сообщения. Для этого мы запускаем цикл обработки сообщений.
Два последних шага показаны ниже.
Теперь создадим три кнопки.
CreateWindow(_T("button"),_T("Start"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 10, 100, 80, 30, hWnd, (HMENU)10000, hInstance, NULL); CreateWindow(_T("button"),_T("Stop"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 100, 100, 80, 30, hWnd, (HMENU)10001, hInstance, NULL); CreateWindow(_T("button"),_T("Exit"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 200, 100, 80, 30, hWnd, (HMENU)10002, hInstance, NULL);
Код из 7-го листинга создает на нашей форме три кнопки в заданных координатах, с заданными надписями, да и, вообще, с заданными параметрами в целом. Обрати внимание на первый параметр, в функции CreateWindow это - имя класса, на основании которого, мы создаем наше окно. Тут мы пишем не имя нашего класса, а имя предопределенного в Windows класса - button. Это - класс для различных видов кнопок, куда относятся, собственно, кнопки, радиокнопки, checkbox'ы. Что именно за тип кнопки создается определяется третьим параметром (как ты видишб, тут мы поставили BS_PUSHBUTTON - стиль обычной кнопки). Второй параметр определяет текст, который будет написан у нас на кнопке. Остальные параметры пропустим, с ними мы ознакомились в создании основного окна, но, как и обещал, про восьмой и девятый параметры расскажу. Восьмой параметр определяет дескриптор родительского окна для нашей кнопки, а девятый - идентификатор кнопки. Каждый элемент управления должен, как правило, иметь свой уникальный идентификатор.
ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } DestroyWindow(hWnd); UnregisterClass(szWindowClass, hInstance); return (int) msg.wParam;
С первыми двумя функциями из листинга 8 мы разобрались чуть выше, а вот далее часть, которую следует пояснить.
Сначала мы создаем переменную типа MSG, в которую будем загонять сообщения, полученные функцией GetMessage(). Кстати о ней...описывать первый параметр, я думаю, нет смысла, он и так понятен, второй параметр - это дескриптор окна, чье сообщение мы хотим получить. Если мы не знаем чье именно, то указываем это значение в NULL - это значит, что все сообщения будут обрабатываться этой функцией. Оставшиеся два параметра - соответственно минимальное и максимальное значение полученных сообщений. В случае, если оба параметра установлены в ноль, функция возвращает все возможные сообщения. Возвращаемое значение может быть 0, -1 и любое, отличное от этих двух чисел, число. Вообще лучше не использовать такой вид записи. Почему? Читай msdn =).
Функция TranslateMessage() переводит сообщение, хранящиеся в msg, из виртуального в символьное. А функция DispatchMessage() передает это сообщение оконной процедуре. Помимо цикла обработки сообщения тут есть еще две функции, наличие которых не обязательно, но по правилам "хорошего тона" мы их используем. Назначение этих функций понятно из их названия, а параметры, передаваемые в эти функции очевидны из предназначения функций и полученных знаний про функции CreateWindow и RegisterClassEx.
Возвращает функция WinMain код сообщения.
С функцией WinMain разобрались. Существующая на данном этапе функция еще работать не будет, т.к. мы не описали функцию WndProc. Оставим эту функцию в покое и перейдем к функции WndProc.
Передаваемые параметры функции WndProc мы уже разбирали, сейчас мы напишем ее тело. Как уже было сказано, эта функция обрабатывает сообщения, которые поступают от Windows. Не считая нескольких глобальных переменных, функция состоит из большого swithc'а, в котором мы обрабатываем каждое, нужное нам, сообщение. А те сообщения, которые нам обрабатывать не надо, мы оставляем операционной системе, а она сама с ними разбирается. Теперь я приведу весь код этой функции, а потом поясню что к чему.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; TCHAR Hello[] = _T("Hello"), VR[] = _T("VR-Online!"); switch (message) { case WM_COMMAND: if(LOWORD(wParam)==10000) { timerEnabled=SetTimer(hWnd, 1, 1000, NULL); } if(LOWORD(wParam)==10001) { if(timerEnabled) { KillTimer(hWnd,1); timerEnabled=false; } } if(LOWORD(wParam)==10002) { if(timerEnabled) { KillTimer(hWnd,1); timerEnabled=false; } PostQuitMessage(0); } break; case WM_TIMER: timerSignal=!timerSignal; InvalidateRect(hWnd, NULL, TRUE); case WM_PAINT: if(timerEnabled) { BeginPaint(hWnd, &ps); if(!timerSignal) TextOut(ps.hdc, 5, 5, Hello, _tcslen(Hello)); if(timerSignal) TextOut(ps.hdc, 5, 5, VR, _tcslen(VR)); EndPaint(hWnd, &ps); } break; case WM_ACTIVATE: UpdateWindow(hWnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
В первой строке этого чудо-кода объявляется переменная типа PAINTSTRUCT - это структура, которая содержит информацию для приложения. Эта информация может быть использована для закраски клиентской области окна, принадлежащего данному приложению. Вообще, эта структура используется только в двух функциях, и на msdn она очень плохо описана. Большинство полей этой структуры по msdn зарезервированы операционной системой. Единственное поле этой структуры, которое нам понадобится, - hdc. Это - дескриптор контекста устройств. Дело в том, что в Windows рисование происходит не на самом окне, а на некотором промежуточном слое, который присоединен к окну. Этот слой и называется контекстом устройства.
Выше я упомянул о двух функциях, в которых используется PAINTSTRUCT. Первая из них - BeginPaint(). Эта функция подготавливает указанное окно к прорисовыванию и заполняет поля PAINTSTRUCT информацией о разрисовке. Эта функция и устанавливает нужное значение полю hdc. Как ты понимаешь из кода, параметры, передаваемые в функцию, - дескриптор окна и адрес структуры PAINTSTRUCT. Вторая функция - EndPaint(). Назначение этой функции обратно назначению функции BeginPaint(): она указывает на то, что прорисовка указанного окна закончена и очищает поля структуры PAINTSTRUCT. Во второй строке этого листинга объявляются две строковые переменные, которые у нас будут заменять друг друга по сигналу таймера. Ну, а дальше начинается обработка сообщений.
Первое сообщение, которое мы обрабатываем - WM_COMMAND. Сообщение WM_COMMAND отправляется окну, когда пользователь выбирает пункт меню. Также это же сообщение посылается от различных элементов управления на окне (например, от кнопок в нашем случае).Так как почти все элементы управления посылают WM_COMMAND, то нам надо как-то различать их. Это мы делаем через идентификатор элемента управления (10000-10002 в нашем случае). Для сообщения WM_COMMAND он передается в младшей части wParam. Получив это сообщение, мы проверяем, нажатием какой кнопки оно было вызвано. Если идентификатор кнопки 10000 (это наша кнопка "Start") то мы запускаем таймер, посредством функции SetTimer(). Первый параметр, который мы передаем в функцию, - дескриптор окна, к которому будет относится таймер. Второй параметр устанавливает идентификатор таймера. Третий - время в миллисекундах, через которое будет срабатывать таймер. А последний параметр - функция, которая будет выполнятся при срабатывании таймера. У нас этот параметр принимает значение NULL, т.к. нужные нам действия мы опишем в обработке сообщения WM_TIMER. Функция возвращает ненулевое значение, если таймер запущен успешно, и нулевое в противном случае. Результат запуска таймера нам понадобится для вывода сообщения на экран. Как ты уже догадался, для этого мы и объявляли в начале кода глобальную переменную timerEnabled. Это - флаг, указывающий на то, запущен ли таймер или нет (кстати, это объясняет почему эта переменная была проинициализирована значением false, потому что таймер у нас изначально не запущен). В случае, если нажата кнопка "Stop" (ее идентификатор 10001), мы проверяем запущен ли таймер с помощью переменной timerEnabled, в которой хранится эта информация. Если таймер запущен, мы останавливаем его с помощью функции KillTimer() (первый параметр этой функции - дескриптор окна, которому принадлежит таймер, второй - идентификатор таймера) и устанавливаем значение флага false, что говорит о том, что таймер не запущен. Ну и, наконец, при нажатии на кнопку "Exit" (ее id 10002) мы выполняем все тоже самое, что и при нажатии на кнопку "Stop" и в дополнение вызываем функцию PostQuitMessage(), которая сообщает Windows о том, что наш поток отправил запрос на уничтожение.
Далее мы обрабатываем сообщение WM_TIMER. Оно посылается тогда, когда срабатывает таймер. Тут мы сталкиваемся со второй глобальной переменной bool-типа. С помощью ее мы сообщаем, что таймер сработал. Вообще, после успешного запуска таймер будет у нас срабатывать каждую секунду. Поэтому переменная timerSignal используется как флаг, что прошла секунда. Каждую секунду мы меняем значение флага на противоположное. Это надо, чтобы писать разный текст на каждое состояние переменной timerSignal. Функция InvalidateRect() создает обновляемый регион указанного окна. Это требуется, чтобы видеть изменения надписи. Если бы этой функции не было, мы бы увидели только одну надпись, хотя она бы изменялась, мы бы этого не видели. В функцию передается дескриптор окна, в котором находится обновляемая область, сама обновляемая область (значение NULL значит, что будет обновляться вся область окна), и последний параметр указывает на то, будет ли удален (скорее очищен) предыдущий фон области обновления.
WM_PAINT - сообщение, отправляемое когда приложение делает запрос на прорисовку. По сути оно вызывается всегда, когда с окном происходят какие-то изменения. Когда мы отловили это сообщение, мы проверяем запущен ли таймер или нет. Если таймер запущен - вызываем функцию BeginPaint() (с этой функцией мы уже ознакомились). Ну, а потом каждую секунду (а именно по изменению значения переменной timerSignal) мы выводим разный текст. Делаем мы это с помощью функции TextOut(). Первый параметр этой функции - контекст устройства окна, в котором мы будем писать наш текст. Его (контекст устройства) мы определили с помощью функции BeginPaint(). Второй и третий параметры - координаты по оси X и Y соответственно. Четвертый параметр - текст, который будем выводить, а пятый - его длина. Ну и после того как написали текст вызываем, уже известную нам, функцию EndPaint().
Основная часть написана, но есть небольшая проблема. Если свернуть, а потом развернуть наше окно - оно будет пустым. Это из-за того, что не происходит прорисовка. Поэтому нам надо делать это самим. Сообщение WM_ACTIVATE посылается, когда окно становится активным. Поэтому, словив это сообщение, вызываем функцию UpdateWindow(), в которой указываем дескриптор окна, которое будем обновлять. Эта функцию просто посылает сообщение WM_PAINT и окно вновь прорисовывается.
Последнее, обрабатываемое нами, сообщение - WM_DESTROY. Оно приходит при уничтожении окна, в ответ на это мы посылаем сообщаем системе, что наше приложение закрывается, с чувством выполненного долга переходим к обработке ситуации, когда для поступившего сообщения нами не предусмотрен вариант его обработки. Тут на помощь приходит функция DefWindowProc(). Эта функция обрабатывает все сообщения, которые не обработали мы. При этом ничего заметного для нас функция не делает. Но она важна тем, что двигает очередь сообщений Windows. Если бы мы ее не вызвали, то очередь сообщений не смогла бы двигаться, и сообщения, для которых есть свои обработчики, никогда бы не дошли до нужного места.
На рисунке 6 показано как выглядит наше приложение в "режиме ожидания" и в режиме работы.

Рисунок 6. Готовое приложение
Вот, собственно говоря, и все. Как ты заметил, этот процесс на самом деле требует бОльших усилий, чем просто накидать кнопок на форму и вписать пару строк кода в обработчик их событий. Те, кто пользовался C++Builder'ом или Delphi, знают, что подобная программа в этих средах пишется 2 минуты. Но сейчас ты будешь понимать из чего же состоят эти чудо-компоненты VCL и как они работают. Перед написанием этой статьи моей целью было показать тебе, что программирование на WinApi не так страшно и трудно как кажется на первый взгляд, и ознакомить тебя с базовыми функциями любой API программы. Я считаю, что у меня получилось.
Спасибо за внимание!
Written by: Сергей Дубовик aka SD
Исходник прилагается:
| Вложение | Размер |
|---|---|
| main_source.zip | 1.28 КБ |
- Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии
- 3014 просмотров



Комментарии
10 комментария(ев)Дата: ВС, 13/02/2011 - 23:34
SD, у тебя очень интересные статьи по С++
Пиши еще!
Дата: Пнд, 14/02/2011 - 14:04
Смотрю, тема C++ набирает обороты.
Дата: Пнд, 14/02/2011 - 14:34
SD - молодца. Он пишет статьи, качественно разжевывая даже для тех, кто в танке
Дата: Втр, 15/02/2011 - 03:59
Слушай, ну все понятно! Не понятно только одно - а на... то есть зачем C++... Нет, это моя молодость и я рад, что пишут на этом языке... Но, объясни - в чем фишка...
Дата: Втр, 15/02/2011 - 04:04
Не, я не подкалываю - я серьезно не понимаю?
Дата: Втр, 15/02/2011 - 04:16
А я не понимаю в чем вопрос. Вы так говорите как будто С++ это что то давно забытое и не используемое.
Дата: Втр, 15/02/2011 - 06:29
А продолжение будет? Вроде ряда статей? Или это единичный случай?
Дата: Втр, 15/02/2011 - 07:32
Виталий Рындин, согласен, сейчас С++ мало востребован. Это видно даже из содержания данного сайта (в основном delphi), я уже не говорю про вакансии С++ программистов в различные компании (там в основном java и C#).
Но мне нравится этот язык. Я считаю его более интересным чем ту же Java. Может и не всем тут интересно читать про "плюсы", но есть и те, кому это пригодится.
И вообще я не понимаю смысл Вашего вопроса.
psycho-coder, я думал об этом. Но потом просто засомневался в надобности последующего развития темы. Мне не тяжело написать еще пару-тройку статей про API, но есть ли смысл?
А вообще, всем спасибо за комментарии. Приятно было читать =)
Дата: Втр, 15/02/2011 - 10:38
2sd
Статьи по С++ нужно однозначно писать (если есть возможность). Мне на почту приходили письма с благодарностью, что в нашем журнале затрагивается тема С++.
Так, что Sd, Kastor и всем тем, кто писал про С++ - респект!
Дата: Втр, 15/02/2011 - 18:24
WinAPI - это же не только "форточки", там много чего.
Касательно форточек: полистав MSDN я не понял как мне создать ListView и как с ним работать. Как создавать таблицы (аналог StringGrid), как обмениваться данными между компонентами (это в принципе понятно, но хотелось бы по подробней) и т.д. и т.п. В сети мало что внятного нашел, в основном как раз простые примеры с созданием пары простых компонент, со словами "остальное аналогично" и "в том же духе"...