платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 3 - простое окно

Предыдущая тема Следующая тема Перейти вниз

платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 3 - простое окно

Сообщение  electrik в Вс Июн 28, 2009 7:16 pm

Win32 API. Урок 3. простое окно

В этом Уроке мы создадим Windows программу, которая отображает полнофункциональное окно на рабочем столе.


Теория:

Windows программы для создания графического интерфейса пользуются функциями API. Этот подход выгоден как пользователям, так и программистам. Пользователям это дает то, что они не должны изучать интерфейс каждой новой программы, так как Windows программы похожи друг на друга. Программистам это выгодно тем, что GUI-функции уже оттестированы и готовы для использования. Обратная сторона - это возросшая сложность программирования. Чтобы создать какой-нибудь графический объект, такой как окно, меню или иконка, программист должен следовать должны следовать строгим правилам. Но процесс программирования можно облегчить, используя модульное программирование или OOП-философию. Я коротко изложу шаги, требуемые для создания окна:


1. Взять хэндл вашей программы (обязательно)
2. Взять командную строку (не нужно до тех пор, пока программе не потребуется ее проанализировать)
3. Зарегистрировать класс окна (необходимо, если вы не используете один из предопределенных классов окна, таких как MessageBox или диалоговое окно)
4. Создать окно (необходимо)
5. Отобразить его на экране
6. Обновить содержимое экрана на окне
7. Запустить бесконечный цикл, в котором будут проверятся сообщения от операционной системы.
8. Прибывающие сообщения передаются специальной функции, отвечающая за обработку окна
9. Выйти из программы, если пользователь закрывает окно.


Как вы можете видеть, структура Windows программы довольно сложна по сравнению с досовской программой. но мир Windows разительно отличается от мира DOS'а.
Windows программы должны быть способными мирно сосуществовать друг с другом. Они должны следовать более строгим правилам. Вы как программист должны быть более внимательными к вашему стилю программирования и привычкам.

Суть:

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

Код:

' простое окно
#include "windows.bi"

declare function WinMain ( byval hInst as HINSTANCE, _
byval hPrevInst as HINSTANCE, _
  byval szCmdLine as LPSTR, _
  byval iCmdShow as integer ) as integer

' начало программы
#define ClassName "SimpleWinClass" ' Имя нашего класса окна
#define AppName "Our First Window" ' Имя нашего окна
dim hModule as HINSTANCE ' Хэндл нашей программы
dim CommandLine as LPSTR 'Указатель на командную строку
hModule = GetModuleHandle( NULL ) ' Взять хэндл программы
CommandLine = GetCommandLine() ' Взять командную строку.
'Вы не обязаны вызывать эту функцию ЕСЛИ ваша программа не обрабатывает командную строку.

end WinMain( hModule,NULL,CommandLine, SW_SHOWNORMAL ) ' вызвать основную функцию
' здесь заканчивается программа

' процедура окна
function  WndProc _
(byval hwnd as HWND, _ ' хэндл окна
byval uMsg as UINT, _ ' сообщение
byval wParam as WPARAM, _ ' дополнительный  параметр сообщений
byval lParam as LPARAM) as LRESULT ' дополнительный параметр сообщений
function = 0
select case uMsg  'начинаем обработку сообщений
case WM_DESTROY ' если пользователь закрывает окно
PostQuitMessage(0) ' выходим из программы
exit function
end select
function = DefWindowProc(hWnd,uMsg,wParam,lParam) ' Дефаултная функция обработки окна
end function

'функция WinMain
function WinMain _
(byval hInst as HINSTANCE, _ ' хэндл программы
byval hPrevInst as HINSTANCE, _ 'в win32 всегда 0
byval szCmdLine as LPSTR, _  'указатель на командную строку
byval iCmdShow as integer ) as integer  ' состояние окна при первом появлении

dim wc as WNDCLASSEX ' структура параметров окна
dim wMsg as MSG  ' структура сообщений
dim hWnd as HWND ' хэндл окна

'структура класса окна wc
with wc ' заполняем структуру wc
.cbSize = SIZEOF( WNDCLASSEX )  ' размер структуры WNDCLASSEX
.style = CS_HREDRAW or CS_VREDRAW  ' Стиль окна
.lpfnWndProc = @WndProc ' Адрес процедуры окна WndProc
.cbClsExtra = NULL  ' резервирование  дополнительных байт за концом структуры
.cbWndExtra = NULL
.hInstance = hInst  ' хэндл модуля
.hbrBackground = cast(HGDIOBJ, COLOR_WINDOW+1) ' Цвет фона
.lpszMenuName = NULL ' Хэндл меню
.lpszClassName = @ClassName ' класс окна
.hIcon = LoadIcon( NULL,IDI_APPLICATION ) ' Хэндл иконки
.hIconSm = .hIcon 'Хэндл маленькой иконки
.hCursor = LoadCursor( NULL,IDC_ARROW) ' Хэндл курсора
end with

' регистрация нашего класса окна
if(RegisterClassEx(@wc) = FALSE) then
MessageBox(0,"Не могу зарегистрировать класс окна","Ошибка",0)
end 1
end if

' Создадим окно
hwnd = CreateWindowEx _
(NULL, _ ' дополнительные стили
@ClassName, _ ' указатель на строку с именем класса окна
@AppName, _ ' указатель на строку с именем окна
WS_OVERLAPPEDWINDOW, _ ' стиль окна
CW_USEDEFAULT, _ ' X
CW_USEDEFAULT, _ ' Y
CW_USEDEFAULT, _ ' ширина окна
CW_USEDEFAULT, _ ' высота окна
NULL, _ ' хэндл родительского окна
NULL, _ ' хэндл меню
hInst, _ ' хэндл модуля
NULL) ' указатель на структуру данных

ShowWindow( hwnd,iCmdShow) ' отобразить наше окно на десктопе
UpdateWindow( hwnd) ' обновить клиентскую область

while( GetMessage( @wMsg, NULL, 0, 0 ) <> FALSE )  'цикл сообщений
TranslateMessage( @wMsg )
DispatchMessage( @wMsg )
wend
function = wMsg.wParam
end function

Анализ:

Вы можете быть ошарашены тем, что простая Windows программа требует так много кода. но большая его часть - это "шаблонный" код, который вы можете копировать из одного исходника в другой. Или, если вы хотите, вы можете скомпилировать часть этого кода в библиотеку, которая будет использоваться как прологовый и эпилоговый код. Вы можете писать код уже только в функции WinMain. Фактически, это то, что делают C-компилятор. Они позволяют вам писать WinMain без беспокойства о коде, который должен быть в каждой программе. Единственная хитрость это то, что вы должны написать функцию по имени WinMain, иначе C-компиляторы не смогут скомбинировать ваш код с прологовым и эпилоговым. Такого ограничения нет во FreeBasic программировании. Вы можете назвать эту функцию так как вы хотите. Готовьтесь! Это будет долгий, долгий туториал. Давайте же проанализируем эту программу до самого конца.

#include "windows.bi"

declare function WinMain ( byval hInst as HINSTANCE, _
byval hPrevInst as HINSTANCE, _
 byval szCmdLine as LPSTR, _
 byval iCmdShow as integer ) as integer

сначала мы должны потключить заголовочные файлы. Они содержат важные структуры и константы, которые потребуются нашей программе. Эти файлы всего лишь текстовые файлы. Вы можете открыть их с помощью любого текстового редактора. во FreeBasic эти файлы объединены файлом windows.bi.
Наша программа вызывает API функции, находящиеся в user32.dll (CreateWindowEx,
RegisterWindowClassEx, например) и kernel32.dll (MessageBox),

Следом идет объявление  функции WinMain. Перед тем, как мы вызовем в дальнейшем эту функцию, мы должны сначала ее определить.

dim shared as zstring * 15 ClassName = >"SimpleWinClass"
dim shared as zstring * 17 AppName = >"Our First Window"
dim hModule as HINSTANCE
dim CommandLine as LPSTR

Далее мы объявляем оканчивающиеся NULL'ом строки (ASCIIZ)(zString): ClassName - имя нашего класса окна и AppName - имя нашего окна.
так же объявлены две переменные: hInstance (хэндл нашей программы) и CommandLine (командная строка нашей программы). Незнакомые типы данных - HINSTANCE и LPSTR - на самом деле новые имена для integer. Вы можете увидеть их в заголовочных файлах.

hModule = GetModuleHandle( NULL )
CommandLine = GetCommandLine()
end WinMain( hModule,NULL,CommandLine, SW_SHOWNORMAL ) ' вызвать основную функцию

соответственно, вызываем WinApi функцию GetModuleHandle, чтобы получить хэндл нашей программы. Под Win32, instance хэндл и module хэндл - одно и тоже. Вы можете воспринимать хэндл программы как ее ID. Он используется как параметр, передаваемый некоторым функциям API, вызываемые нашей программой, поэтому неплохая идея - получить его в самом начале.

Примечание по встроенному ассемблеру:
В действительности, под Win32, хэндл программы - это ее линейный адрес в памяти. По возвращению из Win32 функции, возвращаемое ею значение находится в eax. Все другие значения возвращаются через переменные, переданные в параметрах функции.
Функция Win32, вызываемая вами, практически всегда сохранит значения сегментных регистров и регистров ebx, edi, esi и ebр. Обратно, eax, ecx и edx этими функциями не сохраняются, так что не ожидайте, что значения в этих трех регистрах останутся неизменными после вызова API функции.
Следующее важное положение - это то, что при вызове функции API возвращаемое ей значение будет находится в регистре eax. Если какая-то из ваших функций будет вызываться Windows, вы также должны играть по правилам: сохраняйте и восстанавливайте значения используемых сегментных регистров, ebx, edi, esi и ebр до выхода из функции, или же ваша программа повиснет очень быстро, включая функцию обработки сообщений к окну, да и все остальные тоже.

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

Далее идет вызов WinMain. Она получает четыре параметра: хэндл программы, хэндл предыдущего экземпляра программы, командную строку и состояние окна при первом появлении. Под Win32 нет такого понятия, как предыдущий экземпляр программы. Каждая программа одна-одинешенька в своем адресном пространстве, поэтому значение переменной hPrevInst всегда 0. Это пережиток времен Win16, когда все экземпляры программы запускались в одном и том же адресном пространстве, и экземпляр мог узнать, были ли запущены еще копии этой программы. Под Win16, если hPrevInst равен NULL, тогда этот экземпляр является первым.

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

По возвращению из WinMain, возвращается значение кода выхода. Мы передаем код выхода как параметр функции end, которая завершает нашу программу.

функция WinMain
function WinMain _
(byval hInst as HINSTANCE, _
byval hPrevInst as HINSTANCE, _
byval szCmdLine as LPSTR, _
byval iCmdShow as integer ) as integer

В вышенаписанном мы объявляем начало функции WinMain.

dim wc as WNDCLASSEX
dim wMsg as MSG  
dim hWnd as HWND

далее мы определяем не обходимые переменные, wc- структура класса нашего окна, wMsg- переменная через которую будут передаваться сообщения, и hWnd- переменная, хэндл окна.

with wc
.cbSize = SIZEOF( WNDCLASSEX )
.style = CS_HREDRAW or CS_VREDRAW
.lpfnWndProc = @WndProc
.cbClsExtra = NULL
.cbWndExtra = NULL
.hInstance = hInst
.hbrBackground = cast(HGDIOBJ, COLOR_WINDOW+1)
.lpszMenuName = NULL
.lpszClassName = @ClassName
.hIcon = LoadIcon( NULL,IDI_APPLICATION )
.hIconSm = .hIcon
.hCursor = LoadCursor( NULL,IDC_ARROW)
end with

if(RegisterClassEx(@wc) = FALSE) then
MessageBox(0,"Не могу зарегистрировать класс окна","Ошибка",0)
end 1
end if

Все написанное выше в действительности весьма просто. Это инициализация класса окна. Класс окна - это не что иное, как наметки или спецификации будущего окна. Он определяет некоторые важные характеристики окна, такие как иконка, курсор, функцию, ответственную за окно и так далее. Вы создаете окно из класса окна. Это некоторый сорт концепции ООП. Если вы создаете более, чем одно окно с одинаковыми характеристиками, есть резон для того, чтобы сохранить все характеристики только в одном месте, и обращаться к ним в случае надобности. Эта схема спасет большое количество памяти путем избегания повторения информации.
Помните, Windows создавался во времена, когда чипы памяти стоили непомерно высоко и большинство компьютеров имели 1 MB памяти. Windows должен был быть очень эффективным в использовании скудных ресурсов памяти. Идея вот в чем: если вы определите ваше собственное окно, вы должны заполнить желаемые характеристики в структуре WNDCLASSEX или WNDCLASSEX и вызвать RegisterClass или RegisterClassEx, прежде чем вы сможете создать ваше окно. Вы  должны только один раз зарегистрировать
класс окна для каждой их разновидности, из которых вы будете создавать окна.


Последний раз редактировалось: electrik (Вт Май 23, 2017 3:43 pm), всего редактировалось 2 раз(а)

electrik

Сообщения : 391
Дата регистрации : 2008-09-02
Возраст : 36
Откуда : галактика Млечный путь, система Солнечная, планета Земля, страна россия, город Санкт Петербург

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 3 - простое окно

Сообщение  electrik в Вс Июн 28, 2009 7:23 pm

Win32 API. Урок 3. простое окно - продолжение

В Windows есть несколько предопределенных классов, таких как класс кнопки или окна редактирования. Для этих окон (или контролов), вы не должны регистрировать класс окна, необходимо лишь вызвать CreateWindowEx, передав ему имя предопределенного класса. Самый важный член WNDCLASSEX - это lpfnWndProc. lpfn означает
дальний указатель на функцию. Под Win32 нет "близких" или "дальних" указателей, а лишь просто указатели, так как модель памяти теперь FLAT. но это опять же пережиток времен Win16. Каждому классу окна должна быть сопоставлена процедура окна, которая ответственна за обработку сообщения всех окон этого класса.
Windows будет слать сообщения процедуре окна, чтобы уведомить его о важных событиях, касающихся окон, за которые ответственна эта процедура, например о вводе с клавиатуры или перемещении мыши. Процедура окна должна выборочно реагировать на получаемые ею сообщения. Вы будете тратить большую часть вашего времени на написания обработчиков событий.

Ниже я объясню каждый из членов структуры WNDCLASSEX:

type WNDCLASSEX
cbSize as UINT
style as UINT
lpfnWndProc as WNDPROC
cbClsExtra as integer
cbWndExtra as integer
hInstance as HINSTANCE
hIcon as HICON
hCursor as HCURSOR
hbrBackground as HBRUSH
lpszMenuName as LPCSTR
lpszClassName as LPCSTR
hIconSm as HICON
end type


• cbSize: размер структуры WDNCLASSEX в байтах. Мы можем использовать оператор SIZEOF, чтобы получить это значение.
• style: Стиль окон, создаваемых из этого класса. Вы можете комбинировать несколько стилей вместе, используя оператор "or".
• lpfnWndProc: Адрес процедуры окна, ответственной за окна, создаваемых из класса.
• cbClsExtra: Количество дополнительных байтов, которые нужно зарезервировать (они будут следовать за самой структурой). По умолчанию, операционная система инициализирует это количество в 0. Если приложение использует WNDCLASSEX структуру, чтобы зарегистрировать диалоговое окно, созданное директивой CLASS
в файле ресурсов, оно должно приравнять этому члену значение DLGWINDOWEXTRA.
• hInstance: Хэндл модуля.
• hIcon: Хэндл иконки. Получите его функцией LoadIcon.
• hCursor: Хэндл курсора. Получите его функцией LoadCursor.
• hbrBackground: Цвет фона
• lpszMenuName: Хэндл меню для окон, созданных из класса по умолчанию.
• lpszClassName: Имя класса окна.
• hIconSm: Хэндл маленькой иконки, которая сопоставляется классу окна. Если этот член равен NULL'у, система ищет иконку, определенную для члена hIcon, чтобы использовать ее как маленькую иконку.

hwnd = CreateWindowEx _
(NULL, _
ClassName, _
AppName, _
WS_OVERLAPPEDWINDOW, _
CW_USEDEFAULT, _
CW_USEDEFAULT, _
CW_USEDEFAULT, _
CW_USEDEFAULT, _
NULL, _
NULL, _
hInst, _
NULL)

После регистрации класса окна, мы должны вызвать CreateWindowEx, чтобы создать наше окно, основанное на этом классе.

function CreateWindowEx _
(byval dwExStyle as DWORD, _
byval lpClassName as LPCSTR, _
byval lpWindowName as LPCSTR, _
byval dwStyle as DWORD, _
byval X as integer, _
byval Y as integer, _
byval nWidth as integer, _
byval nHeight as integer, _
byval hWndParent as HWND, _
byval hMenu as HMENU, _
byval hInstance as HINSTANCE, _
byval lpParam as LPVOID) as HWND

Давайте посмотрим детальное описание каждого параметра:

• dwExStyle: Дополнительные стили окна. Это новый параметр, который добавлен в старую функцию CreateWindow. Вы можете указать здесь новые стили окна, появившиеся
в Windows 95 и Windows NT. Обычные стили окна указываются в dwStyle, но если вы хотите определить некоторые дополнительные стили, такие как toрmost окно (которое всегда наверху), вы должны поместить их здесь. Вы можете использовать NULL, если вам не нужны дополнительные стили.
• lpClassName: (Обязательный параметр). Адрес ASCIIZ строки, содержащей имя класса окна, которое вы хотите использовать как шаблон для этого окна. Это может быть ваш собственный зарегистрированный класс или один из предопределенных классов. Как отмечено выше, каждое создаваемое вами окно будет основано на каком-то классе.
• lpWindowName: Адрес ASCIIZ строки, содержащей имя окна. Оно будет показано на title bar'е окно. Если этот параметр будет равен NULL'у, он будет пуст.
• dwStyle: Стили окна. Вы можете определить появление окна здесь. Можно передать NULL без проблем, тогда у окна не будет кнопок изменения резмеров, закрытия и системного меню. Большого прока от этого окна нет. Самый общий стиль - это WS_OVERLAPPEDWINDOW. Стиль окна всегд лишь битовый флаг, поэтому вы можете комбинировать различные стили окна с помощью оператора "or", чтобы получить желаемый результат.Стиль WS_OVERLAPPEDWINDOW в действительности комбинация большинства общих стилей с помощью этого метода.
• X, Y: Координаты верхнего левого угла окна. Обычно эти значения равны CW_USEDEFAULT, что позволяет Windows решить, куда поместить окно. nWidth, nHeight: Ширина и высота окна в пикселях. Вы можете также использовать CW_USEDEFAULT, чтобы позволить Windows выбрать соответствующую ширину и высоту для вас.
• hWndParent: Хэндл родительского окна (если существует). Этот параметр говорит Windows является ли это окно дочерним (подчиненным) другого окна, и, если так, кто родитель окна. Заметьте, что это не родительско-дочерние отношения в окна MDI (multiрly document interface). Дочерние окна не ограничены границами клиентской области родительского окна. Эти отношения нужны для внутреннего использования Windows. Если родительское окно уничтожено, все дочерние окна уничтожаются автоматически. Это действительно просто. Так как в нашем примере всего лишь одно окно, мы устанавливаем этот параметр в NULL.
• hMenu: Хэндл меню окна. NULL - если будет использоваться меню, определенное в классе окна. Взгляните на код, объясненный ранее, член структуры WNDCLASSEX lрszMenuName. Он определяет меню "по умолчанию" для класса окна. Каждое окно, созданное из этого класса будет иметь тоже меню по умолчанию, до тех пор, пока вы не определите специально меню для какого-то окна, используя параметр hMenu. Этот параметр - двойного назначения. В случае, если ваше окно основано на предопределенном классе окна, оно не может иметь меню. Тогда hMenu используется как ID этого контрола. Windows может определить действительно ли hMenu- это хэндл меню или же ID контрола, проверив параметр lрClassName. Если это имя предопределенного класса, hMenu - это идентификатор контрола. Если нет,
это хэндл меню окна.
• hInstance: Хэндл программного модуля, создающего окно.
• lpParam: Опциональный указатель на структуру данных, передаваемых окну. Это используется окнами MDI, чтобы передать структуру CLIENTCREATESTRUCT. Обычно этот параметр установлен в NULL, означая, что никаких данных не передается через CreateWindow(). Окно может получать значение этого параметра через вызов функции GetWindowsLong.

ShowWindow( hwnd,iCmdShow)
UpdateWindow( hwnd)

Окно, которое мы только что создали, не покажется на экране автоматически. Вы должны вызвать ShowWindow, передав ему хэндл окна и желаемый тип отображения на экране, чтобы оно появилось на рабочем столе. Затем вы должны вызвать UpdateWindow для того, чтобы окно перерисовало свою клиентскую область. Эта функция полезна, когда вы хотите обновить содержимое клиентской области. Тем не менее, вы sможете пренебречь вызовом этой функции.

while( GetMessage( @wMsg, NULL, 0, 0 ) <> FALSE ) 'цикл сообщений
TranslateMessage( @wMsg )
DispatchMessage( @wMsg )
wend

Теперь наше окно на экране. но оно не может получать ввод из внешнего мира. Поэтому мы должны проинформировать его о соответствующих событиях. Мы достигаем
этого с помощью цикла сообщений. В каждом модуле есть только один цикл сообщений. В нем функцией GetMessage последовательно проверяется, есть ли сообщения
от Windows. GetMessage передает указатель на MSG структуру Windows. Эта структура будет заполнена информацией о сообщении, которые Windows хотят послать окну этого модуля. Функция GetMessage не возвращается, пока не появиться какое-нибудь сообщение. В это время Windows может передать контроль другим программам.
Это то, что формирует схему многозадачности в платформе Win16. GetMessage возвращает FALSE, если было получено сообщение WM_QUIT, что прерывает цикл обработки сообщений и происходит выход из программы. TranslateMessage - это вспомогательная функция, которая обрабатывает ввод с клавиатуры и генерирует новое сообщение (WM_CHAR), помещающееся в очередь сообщений. Сообщение WM_CHAR содержит ASCII-значение нажатой клавиши, с которым проще иметь дело, чем непосредственно со скан-кодами. Вы можете не использовать эту функцию, если ваша программа не обрабатывает ввод с клавиатуры.
DispatchMessage пересылает сообщение процедуре соответствующего окна.

function = wMsg.wParam
end function

Если цикл обработки сообщений прерывается, код выхода сохраняется в члене MSG структуры wParam. Вы можете вернуть этот код выхода Windows. В настоящее время код выхода не влияет никаким образом на Windows, но лучше подстраховаться и играть по правилам.

процедура окна WndProc
function WndProc _
(byval hwnd as HWND, _
byval uMsg as UINT, _
byval wParam as WPARAM, _
byval lParam as LPARAM) as LRESULT

Это наша процедура окна. Вы не обязаны называть ее WndProc. Первый параметр, hWnd, это хэндл окна, которому предназначается сообщение. uMsg - сообщение.
Отметьте, что uMsg - это не MSG структура. Это всего лишь число. Windows определяет сотни сообщений, большинством из которых ваша программа интересоваться не будет. Windows будет слать подходящее сообщение, в случае, если произойдет что-то, относящееся к этому окну. Процедура окна получает сообщение и реагирует на это соответствующе. wParam и lParam всего лишь дополнительные параметры, использующиеся некоторыми сообщениями. Некоторые сообщения шлют сопроводительные данные в добавление к самому сообщению. Эти данные передаются процедуре окна в переменных wParam и lParam.

function = 0
select case uMsg 'начинаем обработку сообщений
case WM_DESTROY ' если пользователь закрывает окно
PostQuitMessage(0) ' выходим из программы
exit function
end select
function = DefWindowProc(hWnd,uMsg,wParam,lParam) ' Дефаултная функция обработки окна
end function

Это ключевая часть - там, где располагается логика действий вашей программы. Код, обрабатывающий каждое сообщение от Windows - в процедуре окна. Ваш код должен проверить сообщение, чтобы убедиться, что это именно то, которое вам нужно. Если это так, сделайте все, что вы хотите сделать в качестве реакции
на это сообщение, а затем возвратитесь, вернув функции ноль. Если же это не то сообщение, которое вас интересует, вы ДОЛЖНЫ вызвать DefWindowProc, передав
ей все параметры, которые вы до этого получили. DefWindowProc - это API функция , обрабатывающая сообщения, которыми ваша программа не интересуется.

Единственное сообщение, которое вы ОБЯЗАНЫ обработать - это WM_DESTROY. Это сообщение посылается вашему окну, когда оно закрывается. В то время, когда процедура окна его получает, окно уже исчезло с экрана. Это всего лишь напоминание, что ваше окно было уничтожено, поэтому вы должны готовиться к выходу в Windows.
Если вы хотите дать шанс пользователю предотвратить закрытие окна, вы должны обработать сообщение WM_CLOSE. Относительно WM_DESTROY - после выполнения необходимых вам действий, вы должны вызвать PostQuitMessage, который пошлет сообщение WM_QUIT, что вынудит GetMessage вернуть нулевое значение, что в свою очередь, повлечет выход из цикла обработки сообщений, а значит из программы.

Вы можете послать сообщение WM-DESTROY вашей собственной процедуре окна, вызвав функцию DestroyWindow.

[C] Iczelion, пер. Aquila.

electrik

Сообщения : 391
Дата регистрации : 2008-09-02
Возраст : 36
Откуда : галактика Млечный путь, система Солнечная, планета Земля, страна россия, город Санкт Петербург

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 3 - простое окно

Сообщение  electrik в Вс Июн 28, 2009 7:24 pm

Win32 API. Урок 3. простое окно - продолжение

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

' простое окно
#include "windows.bi"

declare function WinMain ( byval hInst as HINSTANCE, _
byval hPrevInst as HINSTANCE, _
  byval szCmdLine as LPSTR, _
  byval iCmdShow as integer ) as integer

' начало программы
#define ClassName "SimpleWinClass" ' Имя класса окна
#define AppName "Our First Window" ' имя программы

end WinMain( GetModuleHandle( NULL ) ,NULL,GetCommandLine() , SW_SHOWNORMAL ) ' вызвать основную функцию
'сдесь заканчивается программа

' процедура окна
function  WndProc _
(byval hwnd as HWND, _ ' хэндл окна
byval uMsg as UINT, _ ' сообщение
byval wParam as WPARAM, _ ' дополнительный  параметр сообщений
byval lParam as LPARAM) as LRESULT ' дополнительный параметр сообщений
function = 0
select case uMsg  'начинаем обработку сообщений
case WM_DESTROY ' если пользователь закрывает окно
PostQuitMessage(0) ' выходим из программы
exit function
end select
function = DefWindowProc(hWnd,uMsg,wParam,lParam) ' Дефаултная функция обработки окна
end function

'функция WinMain
function WinMain _
(byval hInst as HINSTANCE, _ ' хэндл программы
byval hPrevInst as HINSTANCE, _ 'в win32 всегда 0
byval szCmdLine as LPSTR, _  'указатель на командную строку
byval iCmdShow as integer ) as integer  ' состояние окна при первом появлении

dim wc as WNDCLASSEX ' структура параметров окна
dim wMsg as MSG  ' структура сообщений
dim hWnd as HWND ' хэндл окна

 'структура класса окна wc
with wc ' заполняем структуру wc
.cbSize = SIZEOF( WNDCLASSEX )  ' размер структуры WNDCLASSEX
.style = CS_HREDRAW or CS_VREDRAW  ' Стиль окна
.lpfnWndProc = @WndProc ' Адрес процедуры окна WndProc
.cbClsExtra = NULL  ' резервирование  дополнительных байт за концом структуры
.cbWndExtra = NULL
.hInstance = hInst  ' хэндл модуля
.hbrBackground = cast(HGDIOBJ, COLOR_WINDOW+1) ' Цвет фона
.lpszMenuName = NULL ' Хэндл меню
.lpszClassName = @ClassName ' указатель на имя класса окна
.hIcon = LoadIcon( NULL,IDI_APPLICATION ) ' Хэндл иконки
.hIconSm = .hIcon 'Хэндл маленькой иконки
.hCursor = LoadCursor( NULL,IDC_ARROW) ' Хэндл курсора
end with

' регистрация нашего класса окна
if(RegisterClassEx(@wc) = FALSE) then
MessageBox(0,"Не могу зарегистрировать класс окна","Ошибка",0)
end 1
end if

' Создадим окно
hwnd = CreateWindowEx _
(NULL, _ ' дополнительные стили
ClassName, _ ' строка с именем класса окна
AppName, _ ' строка с именем окна
WS_OVERLAPPEDWINDOW, _ ' стиль окна
CW_USEDEFAULT, _ ' X
CW_USEDEFAULT, _ ' Y
CW_USEDEFAULT, _ ' ширина окна
CW_USEDEFAULT, _ ' высота окна
NULL, _ ' хэндл родительского окна
NULL, _ ' хэндл меню
hInst, _ ' хэндл модуля
NULL) ' указатель на структуру данных

ShowWindow( hwnd,iCmdShow) ' отобразить наше окно на десктопе
UpdateWindow( hwnd) ' обновить клиентскую область

while( GetMessage( @wMsg, NULL, 0, 0 ) <> FALSE )  'цикл сообщений
TranslateMessage( @wMsg )
DispatchMessage( @wMsg )
wend
function = wMsg.wParam
end function

как говорилось выше, FreeBasic умеет работать без функции WinMain. перепишем наш пример без этой функции.

Код:

' простое окно
#include "windows.bi"

' процедура окна
function  WndProc _
(byval hwnd as HWND, _ ' хэндл окна
byval uMsg as UINT, _ ' сообщение
byval wParam as WPARAM, _ ' дополнительный  параметр сообщений
byval lParam as LPARAM) as LRESULT ' дополнительный параметр сообщений
function = 0
select case uMsg  'начинаем обработку сообщений
case WM_DESTROY ' если пользователь закрывает окно
PostQuitMessage(0) ' выходим из программы
exit function
end select
function = DefWindowProc(hWnd,uMsg,wParam,lParam) ' Дефаултная функция обработки окна
end function

'начало программы
#define ClassName "SimpleWinClass" ' Имя класса окна
#define AppName "Our First Window" ' имя программы
dim hModule as HINSTANCE ' Хэндл нашей программы
dim CommandLine as LPSTR 'Указатель на командную строку
hModule = GetModuleHandle( NULL ) ' Взять хэндл программы
CommandLine = GetCommandLine() ' Взять командную строку.
'Вы не обязаны вызывать эту функцию ЕСЛИ ваша программа не обрабатывает командную строку.
dim wc as WNDCLASSEX ' структура параметров окна
dim wMsg as MSG  ' структура  сообщений
dim hWnd as HWND ' хэндл окна

'структура класса окна wc
with wc ' заполняем структуру wc
.cbSize = SIZEOF( WNDCLASSEX )  ' размер структуры WNDCLASSEX
.style = CS_HREDRAW or CS_VREDRAW  ' Стиль окна
.lpfnWndProc = @WndProc ' Адрес процедуры окна WndProc
.cbClsExtra = NULL  ' резервирование  дополнительных байт за концом структуры
.cbWndExtra = NULL
.hInstance = hModule  ' хэндл модуля
.hbrBackground = cast(HGDIOBJ, COLOR_WINDOW+1) ' Цвет фона
.lpszMenuName = NULL ' Хэндл меню
.lpszClassName = @ClassName ' указатель на имя класса окна
.hIcon = LoadIcon( NULL,IDI_APPLICATION ) ' Хэндл иконки
.hIconSm = .hIcon 'Хэндл маленькой иконки
.hCursor = LoadCursor( NULL,IDC_ARROW) ' Хэндл курсора
end with

' регистрация нашего класса окна
if(RegisterClassEx(@wc) = FALSE) then
MessageBox(0,"Не могу зарегистрировать класс окна","Ошибка",0)
end 1
end if

' Создадим окно
hwnd = CreateWindowEx _
(NULL, _ ' дополнительные стили
ClassName, _ ' строка с именем класса окна
AppName, _ ' строка с именем окна
WS_OVERLAPPEDWINDOW, _ ' стиль окна
CW_USEDEFAULT, _ ' X
CW_USEDEFAULT, _ ' Y
CW_USEDEFAULT, _ ' ширина окна
CW_USEDEFAULT, _ ' высота окна
NULL, _ ' хэндл родительского окна
NULL, _ ' хэндл меню
hModule, _ ' хэндл модуля
NULL) ' указатель на структуру данных

ShowWindow( hwnd,SW_SHOWNORMAL ) ' отобразить наше окно на десктопе
UpdateWindow( hwnd) ' обновить клиентскую область

while( GetMessage( @wMsg, NULL, 0, 0 ) <> FALSE )  'цикл сообщений
TranslateMessage( @wMsg )
DispatchMessage( @wMsg )
wend

end wMsg.wParam
'сдесь заканчивается программа
как вы видите, без WinMain, код более понятен, но все-же нам придется работать с WinMain, так как вроде уже все к этому привыкли.

electrik

Сообщения : 391
Дата регистрации : 2008-09-02
Возраст : 36
Откуда : галактика Млечный путь, система Солнечная, планета Земля, страна россия, город Санкт Петербург

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 3 - простое окно

Сообщение  Eric-S в Чт Июл 16, 2009 6:15 am

Я, гэ-ха-эм, придираюсь наверное к мелочам. Но всё же.
функция WndProc
должна возвращать определённое значение. А в первом же коде злое exit function.
Электрик, конечно знаю твою нелюбовь к return. Но именно здесь return 0, будет более уместен. Да ещё и более нагляден.
Или я не прав?

Eric-S

Сообщения : 738
Дата регистрации : 2008-08-06
Возраст : 34
Откуда : Россия, Санкт-Петербург

Посмотреть профиль http://eric50.narod.ru

Вернуться к началу Перейти вниз

Re: платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 3 - простое окно

Сообщение  electrik в Сб Июл 18, 2009 10:38 am

да нет, ты не предираешься. ретурн хорошая штука, и вроде я не говорил что его не люблю. а в начале есть такая строчка:
function = 0
когда мы делаем exit function, то нуль возвращается. да и в самих экзэмплах FreeBasic, так пишут.
я долго думал, как лучше делать примеры с ретурн, или с выходом из функции, и потом подумал, пусть народ знает и эту технику. конечно в большом коде, лучше всеже с ретурном, потому, что можно запутаться, это типа как с goto.
если такие операторы есть, почему бы их не использовать.
по количеству кода это не больше.
сейчас я вам расскажу тайну, eslи написать так:
return 1
это тоже самое что:
function = 1
exit function
только со вторым вариантом, можно вернуть значение функции из любой части кода, а потом когда понадобиться, вывалиться из нее, делаем exit function.
вслучае с ретурном, он сразу вываливается из функции.
в наших туториалах, может правильней было бы с return, но надеюсь, что у народа затруднений не возникнет. вконце концов, перепишу код.

electrik

Сообщения : 391
Дата регистрации : 2008-09-02
Возраст : 36
Откуда : галактика Млечный путь, система Солнечная, планета Земля, страна россия, город Санкт Петербург

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 3 - простое окно

Сообщение  Eric-S в Сб Июл 18, 2009 9:44 pm

Я это знаю. А вот function = 0 не заметил. Отсюда и мой вопрос.

Я сам люблю поиграться с этим. Но в данном случае... Не знаю. Действительно трудно выбрать наиболее красивый вариант.

Eric-S

Сообщения : 738
Дата регистрации : 2008-08-06
Возраст : 34
Откуда : Россия, Санкт-Петербург

Посмотреть профиль http://eric50.narod.ru

Вернуться к началу Перейти вниз

Re: платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 3 - простое окно

Сообщение  trew в Сб Окт 16, 2010 9:16 am

Подскажите пожалуйста, а зачем здесь преобразование типов?

Код:
.hbrBackground = cast(HGDIOBJ, COLOR_WINDOW+1) ' Цвет фона
Ведь можно вроде просто записать любое число не больше 31
например:
Код:
.hbrBackground = 28 ' Цвет фона

trew

Сообщения : 331
Дата регистрации : 2010-10-14

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 3 - простое окно

Сообщение  Eric-S в Сб Окт 16, 2010 10:25 pm

Вообще-то этот вопрос не по этой теме. Ну да фиг с ним.

И так, во freebasic'е есть разные типы. Для совместимости со старыми диалектами, прямое приведение переменных, близких типов, например указатель и число, не должно вызывать ошибку. Но по сути эти действия нельзя считать корректными, так как они могут привести к весьма неожиданным результатам.
Если для процедурного языка, это не так страшно, то для объектноориентированного, это уже может вызвать серьёзные последствия.
Freebasic находится где-то между. По этому рекомендуется использовать явное приведение типов.

Кстати, если не использовать оператор cast, то в режиме по умолчанию, это действительно всё равно.
Но в режиме отображения всех предупреждений, ключ компилятора -w all, неявное приведение разных типов приведёт к warning'у.

Кому интересно, то в языке C, неявное приведение тоже довольно мягкое. Можно конечно сделать и принудительное приведение, с указанием типа в скобочках, перед приводимым значением. Но язык C старый и мало того процедурный.
Но вот уже в языке C++ для приведения используются четыре разных оператора const_cast, static_cast, dinamic_cast, reinterpret_cast. И это гораздо правильнее, с моей точки зрения.

Хотя... Любое приведение, если конечно в этом нет особой нужды, в объектноориентированном языке программирования, особенно с шаблонами, является ошибкой.
Убидился на собственной практике. Код с приведением, как потом выясняется, содержит разные логические ошибки. Это вот такой финомен.
Так что если вы хотите использовать приведение, перепроверьте логику программы, скорее всего, вы где-то напутали с алгоритмом. Вероятно на стадии планирования.

Но в процедурных языках, без приведения работать трудновато. Если вообще возможно. И по этому каждое такое место требует дополнительной проверки.

Eric-S

Сообщения : 738
Дата регистрации : 2008-08-06
Возраст : 34
Откуда : Россия, Санкт-Петербург

Посмотреть профиль http://eric50.narod.ru

Вернуться к началу Перейти вниз

Re: платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 3 - простое окно

Сообщение  trew в Вс Окт 17, 2010 5:58 am

Благодарю, за разъяснения

trew

Сообщения : 331
Дата регистрации : 2010-10-14

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 3 - простое окно

Сообщение  Спонсируемый контент


Спонсируемый контент


Вернуться к началу Перейти вниз

Предыдущая тема Следующая тема Вернуться к началу


 
Права доступа к этому форуму:
Вы не можете отвечать на сообщения