Форум программистов, компьютерный форум, киберфорум
Assembler, MASM, TASM
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 5.00/1: Рейтинг темы: голосов - 1, средняя оценка - 5.00
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
06.01.2013, 07:55  [ТС]
Студворк — интернет-сервис помощи студентам
Win32 API. Урок 7c. Фокусы с иконкой и курсором
(часть первая)
В качестве курсора и иконки используем иконку из файла Cursor.cur. Убеждаемся, что разницы между курсором и иконкой нет.
Скачайте пример здесь.
ПРАКТИКА, СЕСТРА ШИЗОФРЕНИИ
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
.586p
.model tiny
include windows.inc
;for WinXP - 580 bytes
.code
exebase equ 400000h
main:
include capito.asm
;---------------------------------------------------------
start: xor ebx,ebx
    mov esi,exebase
    mov edi,offset wTitle+exebase
;------------------------------+
; registering the window class |
;------------------------------+
    invoke LoadCursorFromFile,offset FileName+exebase,COLOR_WINDOW+1,ebx,edi
    invoke RegisterClass,esp,ebx,offset window_procedure+exebase,ebx,\
    ebx,esi,eax,eax
;--------------------------+
; creating the main window |
;--------------------------+
    push ebx
    push esi
    shl esi,9
    invoke CreateWindowEx,ebx,edi,edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE,\
    esi,esi,esi,esi,ebx,ebx
    mov ebp,esp
;---------------------------+
; entering the message loop |
;---------------------------+
message_loop: invoke GetMessage,ebp,ebx,ebx,ebx
    invoke DispatchMessage,ebp
    jmp message_loop
;----------------------+
; the window procedure |
;----------------------+
window_procedure: cmp dword ptr [esp+8],WM_DESTROY
    je @@WM_DESTROY
    jmp DefWindowProc+exebase
@@WM_DESTROY: invoke ExitProcess,ebx
     ;exp = experiment
FileName db "Images\Cursor.cur",0
wTitle db 'Iczelion Tutorial #7-1:Курсор и иконка из файла *.cur в MASM'
;-------------------------------------------------------------------------------------------
import:
dd 0,0,0,user32_dll
dd user32_table
dd 0,0,0,kernel32_dll
dd kernel32_table
dd 0,0
kernel32_table:
ExitProcess             dd _ExitProcess,0
user32_table:
RegisterClass       dd _RegisterClass
CreateWindowEx          dd _CreateWindowEx
GetMessage              dd _GetMessage
DispatchMessage         dd _DispatchMessage
DefWindowProc           dd _DefWindowProc
LoadCursorFromFile  dd _LoadCursorFromFile
                        dw 0
_RegisterClass      db 0,0,'RegisterClassA'      
_CreateWindowEx     db 0,0,'CreateWindowExA'
_GetMessage     db 0,0,'GetMessageA'
_DispatchMessage    db 0,0,'DispatchMessageA'
_DefWindowProc      db 0,0,'DefWindowProcA'
_LoadCursorFromFile db 0,0,"LoadCursorFromFileA",0
user32_dll      db 'user32'
 
_ExitProcess        db 0,0,'ExitProcess',0
kernel32_dll        db 'kernel32'
end_import:
end main
________________________________
© Mikl___ 2013
Вложения
Тип файла: zip tut07c.zip (1.9 Кб, 149 просмотров)
5
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
06.01.2013, 07:55
Ответы с готовыми решениями:

Обсуждение темы "Сам себе Iczelion"
Win32 API. Урок 1. Основы Этот Урок предполагает, что читатель знает, как использовать MASM. Если вы не знакомы с MASM, скачайте c...

Уроки Iczelion'a на FASM
Уроки Iczelion'a на FASM Урок первый. MessageBox на FASM format PE GUI include 'win32ax.inc' ; import data in the same...

Запрос сам в себе
Ребята, вот например есть таблица Name Time a 12-04-2011 a 14-04-2011 a 05-04-2011 b...

120
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
06.01.2013, 09:41  [ТС]
Win32 API. Урок 7d. Фокусы с иконкой и курсором
(часть вторая)

ТЕОРИЯ, МАТЬ СКЛЕРОЗА
Добавляем курсор и иконку из файла Cursor.cur в секцию ресурсов. В MASM нет возможности добавлять содержимое бинарных файлов директивой incbin как в NASM'е мы либо должны воспользоваться компилятором ресурсов и в нем указать:
Кликните здесь для просмотра всего текста
Code
1
2
#define ID_CURSOR 18
ID_CURSOR CURSOR "Images\cursor.cur"
либо воспользоваться тузлой bintodb.exe из "\masm32\tools" и преворатить Cursor.cur в текстовый файл состоящий из наборов DB
Скачайте пример здесь.
ПРАКТИКА, СЕСТРА ШИЗОФРЕНИИ
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
.586p
.model tiny
include windows.inc
;for WinXP - 1501 bytes
.code
exebase     equ 400000h
ID_CURSOR   equ 18
main:
include capito_res.asm
;---------------------------------------------------------
start:  xchg ebx,eax
    mov esi,exebase
    mov edi,offset wTitle+exebase
;------------------------------
; registering the window class 
;------------------------------
    invoke LoadCursor,esi,ID_CURSOR,COLOR_WINDOW+1,ebx,edi
    invoke RegisterClass,esp,ebx,offset window_procedure+exebase,\
    ebx,ebx,esi,eax,eax
;--------------------------+
;  creating the main window |
;--------------------------+
    push ebx
    push esi
    shl esi,9
    invoke CreateWindowEx,ebx,edi,edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE,\
    esi,esi,esi,esi,ebx,ebx
    mov ebp,esp
;---------------------------+
;  entering the message loop |
;---------------------------+
message_loop: invoke GetMessage,ebp,ebx,ebx,ebx
    invoke DispatchMessage,ebp
    jmp message_loop
;----------------------+
;  the window procedure |
;----------------------+
window_procedure: cmp dword ptr [esp+8],WM_DESTROY
    je @@WM_DESTROY
    jmp DefWindowProc+exebase
@@WM_DESTROY: invoke ExitProcess,ebx
;---------------------------------------
wTitle db 'Iczelion Tutorial #7-2:Курсор и иконка из ресурсов в MASM'
;--------------------------------------------------------------------
;align 2
resource:
;RT_CURSOR=1
Characteristics1    dd 0
TimeDateStamp1      dd 0
MajorVersion1       dw 0
MinorVersion1       dw 0
NumberOfNamedEntries1   dw 0;количество ресурсов с именами
NumberOfIdEntries1  dw 2;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является типом ресурса
dw RT_CURSOR,0
dw cur1-resource,8000h
dw RT_GROUP_CURSOR,0
dw gr1-resource,8000h
cur1:
Characteristics2    dd 0
TimeDateStamp2      dd 0
MajorVersion2       dw 0
MinorVersion2       dw 0
NumberOfNamedEntries2   dw 0;количество ресурсов с именами
NumberOfIdEntries2  dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является идентификатором ресурса 
dw 1,0
dw cur2-resource,8000h
gr1:
Characteristics3    dd 0
TimeDateStamp3      dd 0
MajorVersion3       dw 0
MinorVersion3       dw 0;
NumberOfNamedEntries3   dw 0;количество ресурсов с именами
NumberOfIdEntries3  dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является идентификатором ресурса 
dw ID_CURSOR,0
dw gr2-resource,8000h
cur2:
Characteristics4    dd 0
TimeDateStamp4      dd 0
MajorVersion4       dw 0
MinorVersion4       dw 0;
NumberOfNamedEntries4   dw 0;количество ресурсов с именами
NumberOfIdEntries4  dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является идентификатором ресурса 
dw ID_CURSOR,0
dd cur3-resource
gr2:
Characteristics5    dd 0
TimeDateStamp5      dd 0
MajorVersion5       dw 0
MinorVersion5       dw 0;
NumberOfNamedEntries5   dw 0;количество ресурсов с именами
NumberOfIdEntries5  dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является идентификатором ресурса 
dw ID_CURSOR,0
dd gr3-resource
cur3 dd cursor,748,0,0
gr3 dd group1,14h,0,0
cursor dd 0      
; пропуск первых 22 байт размер курсора 766-22=748
db 40,0,0,0,32,0,0,0,64,0,0,0,1,0,4,31 dup(0)
db 128,0,0,128,0,0,0,128,128,0,128,0,0,0,128
db 0,128,0,128,128,0,0,192,192,192,0,128,128
db 128,0,0,0,255,0,0,255,0,0,0,255,255,0,255
db 0,0,0,255,0,255,0,255,255,0,0,255,255,255
db 0,0,10,5 dup(170),160,10 dup(0),3 dup(170)
db 160,12 dup(0),10,170,0,10,4 dup(170),0,128
db 8 dup(0),10,6 dup(170),168,6 dup(0),8,10,170
db 160,0,0,0,10,170,170,7 dup(0),170,170,1,16
db 5 dup(170),168,5 dup(0),10,170,160,17,10
db 4 dup(170),0,0,0,128,0,0,0,74,170,160,16
db 8 dup(170),160,0,8,0,0,170,160,10,9 dup(170)
db 0,128,10,3 dup(170),10,170,160,8,6 dup(170)
db 168,0,3 dup(170),160,0,170,138,160,136,160
db 4 dup(170),160,10,170,170,74,170,170,168,170
db 15,0,0,10,170,170,170,168,10,170,164,10,170
db 170,10,160,255,0,15,0,10,170,170,0,10,170
db 64,10,160,168,10,160,255,240,15,5 dup(0),10
db 160,0,170,160,8,10,160,4 dup(255),10,4 dup(0)
db 3 dup (170),0,128,10,160,255,255,15,255,10
db 0,0,0,7,10,170,160,8,248,10,170,15,240,15,240
db 170,0,0,0,15,112,0,15,255,255,0,170,160,10,168
db 10,160,128,0,0,15,5 dup(255),128,3 dup (170)
db 138,170,8,0,0,0,7,5 dup(255),248,10,170,160
db 96,0,0,0,0,0,0,6 dup(255),128,0,15,255,247
db 112,4 dup(0),9 dup(255),119,119,128,0,128
db 0,0,6 dup(255),127,136,136,128,8,255,255
db 248,0,8,127,5 dup(255),247,112,8,4 dup(255)
db 240,0,0,15,5 dup(255),248,143,5 dup(255),240
db 0,0,135,12 dup(255),248,0,0,0,12 dup(255)
db 4 dup(0),7,10 dup(255),240,5 dup(0),135
db 8 dup(255),247,7 dup(0),7,6 dup(255),247,8
db 8 dup(0),8,127,3 dup(255),247,8,10 dup(0)
db 128,0,0,0,8,5 dup(0),192,0,255,255,224,0,15
db 255,240,0,1,255,248,0,0,255,248,0,0,127,248
db 0,0,63,240,0,0,7,224,0,0,3,128,0,0,1,15 dup(0)
db 1,0,0,0,3,4 dup(0,0,0,31),0,0,0,63,0,0,0,127
db 128,0,0,63,128,0,0,7,128,0,0,3,128,0,0,3,192
db 0,0,3,192,0,0,3,224,0,0,7,240,0,0,15,248,0
db 0,31,254,0,0,63,255,128,0,255,255,240,3,255
  dd group1
  dd 14h,000h,0
group1 dd 20000h
  dw 1,20h,40h,1,4,748,0,1
;32 x 32 (2^4=16 colors) - Ordinal name: 1
;--------------------------------------------------
end_resource:
;align 2
import:
dd 0,0,0,user32_dll
dd user32_table
dd 0,0,0,kernel32_dll
dd kernel32_table
dd 0,0
kernel32_table:
ExitProcess             dd _ExitProcess,0
user32_table:
RegisterClass       dd _RegisterClass
CreateWindowEx          dd _CreateWindowEx
GetMessage              dd _GetMessage
DispatchMessage         dd _DispatchMessage
DefWindowProc           dd _DefWindowProc
LoadCursor      dd _LoadCursor
                        dw 0
_RegisterClass      db 0,0,'RegisterClassA'      
_CreateWindowEx     db 0,0,'CreateWindowExA'
_GetMessage     db 0,0,'GetMessageA'
_DispatchMessage    db 0,0,'DispatchMessageA'
_DefWindowProc      db 0,0,'DefWindowProcA'
_LoadCursor     db 0,0,"LoadCursorA",0
user32_dll      db 'user32'
 
_ExitProcess        db 0,0,'ExitProcess',0
kernel32_dll        db 'kernel32'
end_import:
end main
______________________________________
© Mikl___ 2013
Вложения
Тип файла: zip tut07d.zip (4.2 Кб, 122 просмотров)
5
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
06.01.2013, 09:49  [ТС]
Win32 API. Урок 7e. Добавляем курсор и иконку из секции данных функцией CreateIconFromResource
В секцию данных добавим рисунок из файла Cursor.cur, воспользовавшись программкой bintodb.exe из "\masm32\tools", которая превратит рисунок в текстовый файл состоящий из наборов DB. При помощи функции CreateIconFromResource получаем указатель на иконку, и, передав его DrawIcon, выводим рисунок из Cursor.cur на клиентскую область окна.
Скачайте пример здесь.
Функция CreateIconFromResource создает пиктограмму или курсор из битов ресурса, описывающих пиктограмму.
Синтаксис
Кликните здесь для просмотра всего текста
C
1
2
3
4
5
6
7
HICON CreateIconFromResource
(
PBYTE presbits, // указатель на биты пиктограммы или курсора
DWORD dwResSize, // число байтов в буфере битов
BOOL fIcon, // флажок пиктограммы или курсора
DWORD dwVer // версия формата Windows
0);
Параметры
  • presbits ― Указывает на буфер, содержащий биты ресурса пиктограммы или курсора. Эти биты обычно загружаются при помощи вызова функций LookupIconIdFromDirectory и LoadResource.
  • dwResSize ― Определяет размер, в байтах, набор битов, указанных параметром presbits.
  • fIcon ― Определяет, что должно быть создано ― пиктограмма или курсор. Если этот параметр ― TRUE, должна быть создана пиктограмма. Если ― FALSE, должен быть создан курсор.
  • dwVer ― Определяет номер версии формата пиктограммы или курсора для битов ресурса, указанных параметром presbits. Параметр dwVer может быть одним из следующих значений:
    Формат dwVer
    Windows 2.x 20000h
    Windows 3.x 30000h
    Прикладные программы, базирующиеся на Microsoft Win32, используют для пиктограмм и курсоров формат Windows 3.x.
Возвращаемые значения
Если функция завершается успешно, величина возвращаемого значения ― дескриптор пиктограммы или курсора. Если функция не выполняет задачу, величина возвращаемого значения ― NULL. Чтобы получать расширенные данные об ошибках, вызовите GetLastError.
Функция DrawIcon рисует пиктограмму в рабочей области окна заданным контекстом устройства.
Синтаксис
Кликните здесь для просмотра всего текста
C
1
2
3
4
5
6
BOOL DrawIcon
( HDC hDC, // дескриптор контекста устройства
int X, // x-координата верхнего левого угла
int Y, // y- координата верхнего левого угла
HICON hIcon // дескриптор иконы, которую надо нарисовать
);
Параметры
  • hDC ― Идентифицирует контекст устройства для окна.
  • X ― Определяет логическую x-координату левого верхнего угла пиктограммы.
  • Y ― Определяет логическую y-координату левого верхнего угла пиктограммы.
  • hIcon ― Идентифицирует пиктограмму, которая будет нарисована.
Возвращаемые значения
Если функция завершается успешно, величина возвращаемого значения отличная от нуля. Если функция не выполняет задачу, величина возвращаемого значения нулевая. Чтобы получать расширенные данные об ошибках, вызовите GetLastError.
Пpимеp:
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
.586p
.model tiny
include windows.inc
;for WinXP - 1450 bytes
.code
exebase equ 400000h
ICON_SIZE equ 744
main:
include capito.asm
;---------------------------------------------------------
start: xor ebx,ebx
    mov esi,exebase
    mov edi,offset wTitle+exebase
;------------------------------
; registering the window class 
;------------------------------
        invoke CreateIconFromResource,offset ptIcon+exebase,\
    ICON_SIZE,-1,30000h,COLOR_WINDOW+1,ebx,edi
        mov hIcon+exebase,eax
    invoke RegisterClass,esp,ebx,offset window_procedure+\
    exebase,ebx,ebx,esi,eax,eax
;--------------------------+
;  creating the main window |
;--------------------------+
    push ebx
    push esi
    shl esi,9
    invoke CreateWindowEx,ebx,edi,edi,WS_OVERLAPPEDWINDOW \
    or WS_VISIBLE,esi,esi,esi,esi,ebx,ebx
    mov ebp,esp
;---------------------------+
;  entering the message loop |
;---------------------------+
message_loop: invoke GetMessage,ebp,ebx,ebx,ebx
    invoke DispatchMessage,ebp
    jmp message_loop
;----------------------+
;  the window procedure |
;----------------------+
window_procedure:
hWnd    equ [ebp+8]
uMsg    equ [ebp+0Ch]
    enter sizeof(PAINTSTRUCT),0
        mov eax,uMsg
        mov edi,hWnd
    dec eax
        dec eax; cmp uMsg,WM_DESTROY
    je @@WM_DESTROY
    sub eax,WM_PAINT-WM_DESTROY
        jz @@WM_PAINT
    leave
    jmp DefWindowProc+exebase
@@WM_PAINT: invoke BeginPaint,edi,esp
    invoke DrawIcon,eax,134,68,hIcon+exebase;Рисуем иконку в клиентской области
    invoke EndPaint,edi,esp
exit:   leave
        retn 10h
@@WM_DESTROY: invoke ExitProcess,ebx
;----------------------------------------------------------
ptIcon:;sauter le header du fichier d'icone
; пропуск первых 22 байт 
db 40,0,0,0,32,0,0,0,64,0,0,0,1,0,4,31 dup(0)
db 128,0,0,128,0,0,0,128,128,0,128,0
db 0,0,128,0,128,0,128,128,0,0,192,192,192,0,128,128
db 128,0,0,0,255,0,0,255,0,0,0,255,255,0,255,0
db 0,0,255,0,255,0,255,255,0,0,255,255,255,0,0,10
db 5 dup(170),160,10 dup(0),3 dup(170),160,12 dup(0)
db 10,170,0,10,4 dup(170),0,128,8 dup(0)
db 10,6 dup(170),168,6 dup(0)
db 8,10,170,160,0,0,0,10,170,170,7 dup(0)
db 170,170,1,16,5 dup(170),168,5 dup(0)
db 10,170,160,17,10,4 dup(170),0,0,0,128,0,0,0
db 74,170,160,16,8 dup(170),160,0,8,0
db 0,170,160,10,9 dup(170),0,128,10
db 3 dup(170),10,170,160,8,6 dup(170),168,0,170
db 170,170,160,0,170,138,160,136,160,4 dup(170),160,10,170
db 170,74,170,170,168,170,15,0,0,10,170,170,170,168,10,170
db 164,10,170,170,10,160,255,0,15,0,10,170,170,0,10,170
db 64,10,160,168,10,160,255,240,15,0,0,0,0,0,10,160
db 0,170,160,8,10,160,4 dup(255),10,0,0,0,0,170
db 170,170,0,128,10,160,255,255,15,255,10,0,0,0,7,10
db 170,160,8,248,10,170,15,240,15,240,170,0,0,0,15,112
db 0,15,255,255,0,170,160,10,168,10,160,128,0,0,15,5 dup(255)
db 128,170,170,170,138,170,8,0,0,0,7,5 dup(255)
db 248,10,170,160,96,0,0,0,0,0,0,6 dup(255)
db 128,0,15,255,247,112,0,0,0,0,9 dup(255)
db 119,119,128,0,128,0,0,6 dup(255)
db 127,136,136,128,8,255,255,248,0,8,127
db 5 dup(255),247,112,8,4 dup(255),240,0,0,15
db 5 dup(255),248,143,5 dup(255),240,0,0,135
db 12 dup(255),248,0,0,0,12 dup(255),0,0,0,0
db 7,10 dup(255),240,5 dup(0),135,8 dup(255),247,7 dup(0)
db 7,6 dup(255),247,8,8 dup(0)
db 8,127,3 dup(255),247,8,10 dup(0)
db 128,0,0,0,8,5 dup(0),192,0
db 255,255,224,0,15,255,240,0,1,255,248,0,0,255,248,0
db 0,127,248,0,0,63,240,0,0,7,224,0,0,3,128,0
db 0,1,15 dup(0),1,0,0,0,3,4 dup(0,0,0,31)
db 0,0,0,63,0,0,0,127,128,0
db 0,63,128,0,0,7,128,0,0,3,128,0,0,3,192,0
db 0,3,192,0,0,3,224,0,0,7,240,0,0,15,248,0
db 0,31,254,0,0,63,255,128,0,255,255,240,3,255
wTitle db 'Iczelion Tutorial #7-3:создание курсора и иконки '
db 'функцией CreateIconFromResource в MASM'
;---------------------------------------------------------------
import:
dd 0
hIcon dd 0
dd 0,user32_dll
dd user32_table
dd 0,0,0,kernel32_dll
dd kernel32_table
dd 0,0
kernel32_table:
ExitProcess             dd _ExitProcess,0
user32_table:
RegisterClass       dd _RegisterClass
CreateWindowEx          dd _CreateWindowEx
GetMessage              dd _GetMessage
DispatchMessage         dd _DispatchMessage
DefWindowProc           dd _DefWindowProc
BeginPaint      dd _BeginPaint
EndPaint        dd _EndPaint
DrawIcon        dd _DrawIcon
CreateIconFromResource  dd _CreateIconFromResource
                        dw 0
_RegisterClass      db 0,0,'RegisterClassA'      
_CreateWindowEx     db 0,0,'CreateWindowExA'
_GetMessage     db 0,0,'GetMessageA'
_DispatchMessage    db 0,0,'DispatchMessageA'
_DefWindowProc      db 0,0,'DefWindowProcA'
_BeginPaint     db 0,0,'BeginPaint'
_EndPaint       db 0,0,'EndPaint'
_DrawIcon       db 0,0,'DrawIcon'
_CreateIconFromResource db 0,0,'CreateIconFromResource',0
 
user32_dll      db 'user32'
 
_ExitProcess        db 0,0,'ExitProcess',0
kernel32_dll        db 'kernel32'
end_import:
end main
__________________________
© Mikl___ 2013
Изображения
              
Вложения
Тип файла: zip tut07e.zip (3.5 Кб, 124 просмотров)
5
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
07.01.2013, 09:10  [ТС]
Win32 API. Урок 7f. Не используем функции LoadIcon и LoadCursor

Заполняя WNDCLASS(EX) можно и не использовать функции LoadIcon и LoadCursor. Дело в том, что эти функции нужны, если загружаемая иконка или курсор изготовлены собственноручно или позаимствованы из какой-либо понравившейся программы. Для стандартных (системных) иконок, битмапов и курсоров идентификаторы неизменны и их можно посмотреть при помощи программы System Resources by The Svin, находящейся на WASM.RU https://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrow Исходники https://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrow Система, а также при помощи «связки» из функций wsprintf + LoadIcon/Cursor/Bitmap + MessageBoxA посмотреть хэндл иконки, курсора или битмапы.
Название: handle_IDC_ARROW.GIF
Просмотров: 1072

Размер: 2.3 Кб
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
 invoke LoadCursorA, 0, IDC_ARROW
invoke wsprintfA, addr buffer, addr format, eax
add esp,12
invoke MessageBoxA, 0, addr buffer, addr Caption, 0
Полученный хэндл вставлялся в программу, выводящую окно, и проверялся результат.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
.686P
.model flat
includelib user32.lib
includelib kernel32.lib
include windows.inc
extern _imp__CreateWindowExA@48:dword
extern _imp__DefWindowProcA@16:dword
extern _imp__DispatchMessageA@4:dword
extern _imp__GetMessageA@16:dword
extern _imp__ExitProcess@4:dword
extern _imp__RegisterClassA@4:dword
.code
start: xor ebx,ebx ;ebx=0
mov edi,offset wTitle; Имя нашего класса окна
mov esi,400000h ; Хэндл нашей программы
; заполнение структуры wc
push edi ;lpszClassName
push ebx ;lpszMenuName
push COLOR_WINDOW+1 ;hbrBackground
push 10011h ;hCursor: Хэндл курсора вычисленный заранее
push ebx ;hIcon: Хэндл иконки
push esi ;hInstance
push ebx ;cbWndExtra
push ebx ;cbClsExtra
push offset WndProc;lрfnWndProc: Адрес процедуры окна
push ebx ;style
push esp ;адрес структуры WNDCLASSEX
call _imp__RegisterClassA@4 ; регистрация нашего класса окна
push ebx;lрParam
push esi ;hInstance
shl esi,9;esi=CW_USEDEFAULT
push ebx;hMenu
push ebx;hWndParent
push esi;X-координата верхнего левого угла окна.
push esi;Y-координата верхнего левого угла окна.
push esi;ширина окна в пикселях
push esi;высота окна в пикселях
push WS_OVERLAPPEDWINDOW + WS_VISIBLE ;dwStyle
push edi;lрWindowName
push edi;lрClassName
push ebx;dwExStyle
call _imp__CreateWindowExA@48 ;создать окно
mov ebp,esp
message_loop: push ebx ;цикл обработки сообщений
push ebx
push ebx
push ebp
call _imp__GetMessageA@16
push ebp
call _imp__DispatchMessageA@4 ;вернуть управление Windows
jmp short message_loop
WndProc:
cmp dword ptr [esp+8],WM_DESTROY;cmp uMsg,WM_DESTROY
je @@WM_DESTROY
jmp _imp__DefWindowProcA@16;все сообщения, не обрабатываемые в функции
;WndProc, направляются на обработку по умолчанию
@@WM_DESTROY: push ebx ;если пользователь закрывает окно
call _imp__ExitProcess@4; выходим из программы
;--данные------------------------------------------------------
wTitle db 'Iczelion Tutorial #3:A Simple Window in masm',0; Имя нашего окна
end start
Начав с курсоров IDC_ARROW, IDC_IBEAM очень скоро замечаем, что хэндлы курсоров могут быть только нечетными числами: 10011h, 10013h, 10015h и т.д. Теперь уже без «связки» wsprintf+LoadCursor+MessageBox подставляем значения в шаблон окна и смотрим, что будет выведено в качестве курсора. Если хэндл четный, то окно становится невидимым. Когда, хэндл окажется меньше 10011h, в качестве курсоров появятся стандартные системные иконки IDI_APPLICATION, IDI_QUESTION и т.д. то есть разница между иконкой и курсором отсутствует (на это я наткнулся случайно, когда во время заполнения WNDCLASSEX перепутал местами функции LoadIcon и LoadCursor). Для части системных курсоров и иконок в файле windows.inc константы IDC_ и IDI_ отсутствуют, константы IDI_APPLICATION и IDI_WINLOGO хотя и имеют разное значение (32512 и 32517 соответственно), но соответствуют одной и той же иконке. IDC_SIZE (=32640) и IDC_ICON (=32641), вероятно, устарели и в Windows XP не соответствуют никакому курсору. Часть курсоров, принадлежащих системе, находятся в «Панель управления»https://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrow«Оформление и темы» https://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrow«Указатели мыши»
Кликните здесь для просмотра всего текста
Результат исследования помещен в таблицу
Кликните здесь для просмотра всего текста
тип значение Оформление и темы: указатели мыши хэндл картинка системные иконки  
IDI_APPLICATION 32512 иконка программы большая 10003h Название: cursor34.GIF
Просмотров: 1069

Размер: 973 байт    
IDI_EXCLAMATION/IDI_WARNING 32515 предупреждение 10005h Название: cursor31.GIF
Просмотров: 1063

Размер: 1.1 Кб    
IDI_QUESTION 32514 вопрос 10007h Название: cursor30.GIF
Просмотров: 1064

Размер: 1.2 Кб    
IDI_HAND/IDI_ERROR 32513 ошибка 10009h Название: cursor32.GIF
Просмотров: 1067

Размер: 1.2 Кб    
IDI_ASTERISK/IDI_INFORMATION 32516 информация 1000Bh Название: cursor33.GIF
Просмотров: 1062

Размер: 1.2 Кб    
? ? иконка программы малая 1000Dh     
IDI_WINLOGO 32517 иконка программы большая 1000Fh Название: cursor34.GIF
Просмотров: 1069

Размер: 973 байт    
  системные курсоры      
IDC_ARROW 32512 Основной режим 10011h Название: cursor01.GIF
Просмотров: 1067

Размер: 900 байт    
IDC_IBEAM 32513 Выделение текста 10013h Название: cursor02.GIF
Просмотров: 1064

Размер: 878 байт    
IDC_WAIT 32514 Система недоступна 10015h Название: cursor03.GIF
Просмотров: 1071

Размер: 917 байт    
IDC_CROSS 32515 Графическое выделение 10017h Название: cursor04.GIF
Просмотров: 1071

Размер: 862 байт    
IDC_UPARROW 32516 Специальное выделение 10019h Название: cursor05.GIF
Просмотров: 1070

Размер: 876 байт    
IDC_SIZENWSE 32642 Изменение размера по диагонали 1 1001Bh Название: cursor09.GIF
Просмотров: 1071

Размер: 888 байт    
IDC_SIZENESW 32643 Изменение размера по диагонали 2 1001Dh     
IDC_SIZEWE 32644 Изменение горизонтальных размеров 1001Fh     
IDC_SIZENS 32645 Изменение вертикальных размеров 10021h     
IDC_SIZEALL 32646 Перемещение 10023h     
IDC_NO 32648 Операция невозможна 10025h     
IDC_APPSTARTING 32650 Фоновый режим 10027h     
IDC_HELP 32651 Выбор справки 10029h     
? 32631 Рукописный ввод 1002Bh Название: cursor06.GIF
Просмотров: 1091

Размер: 919 байт    
IDC_HAND 32649 Выбор ссылки 1002Dh     
? ? ? 1002Fh Название: cursor08.GIF
Просмотров: 1067

Размер: 908 байт     
6
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
08.01.2013, 04:04  [ТС]
Кликните здесь для просмотра всего текста
тип значение Оформление и темы: указатели мыши хэндл картинка
? 32663 Ожидание готовности CD/DVD 10031h Название: cursor29.GIF
Просмотров: 1021

Размер: 1,022 байт
IDC_SIZE 32640 - 0 -
IDC_ICON 32641 - 0 -
  32652 0C36023Fh Название: cursor18.GIF
Просмотров: 1034

Размер: 909 байт
  32653 0AA9023Bh Название: cursor19.GIF
Просмотров: 1037

Размер: 921 байт
  32654 17D1022Fh Название: cursor20.GIF
Просмотров: 1041

Размер: 946 байт
  32655 0C8A023Fh Название: cursor21.GIF
Просмотров: 1028

Размер: 880 байт
  32656 0EC40231h
  32657 0C95023Fh
  32658 09EF0191h
  32659 0DE2023Fh
  32660 1963022Fh
  32661 0CCF023Fh
  32662 01970022h Название: cursor28.GIF
Просмотров: 1044

Размер: 891 байт

________________________________
© Mikl___ 2013
4
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
08.01.2013, 09:56  [ТС]
Win32 API. Урок 8. Меню
В этом туториале мы научимся, как вставить в наше окно меню.
Скачайте пример #1 и пример #2.
ТЕОРИЯ, МАТЬ СКЛЕРОЗА
Меню ― это один из важнейших компонентов вашего окна. Меню ― список всех возможностей, которые программа предлагает пользователю.
Пользователь не обязан читать мануал, поставляемый с программой, чтобы использовать ее (весьма спорная точка зрения ― прим. пер.), он может досконально исследовать меню, чтобы получить представление о возможностях данной программы и начать 'играть' с ней немедленно. Так как меню ― это инструмент для того, чтобы дать пользователю 'быстрый старт', вы должны следовать стандарту.
Короче говоря, первые два пункта меню обязательно "File" и "Edit", а последний ― "Help". Вы можете вставить ваши собственные пункты между "Edit" и "Help". Если пункт меню вызывает диалоговое окно, вам нужно заканчивать название пункта эллипсисом (...).
Меню ― это разновидность ресурсов. Есть несколько видов ресурсов, таких как диалоговые окна, строковые таблицы, иконки, битмапы, меню и т.д. Ресурсы описываются в отдельном файле, называющемся файлом ресурсов, который, как правило, имеет расширение .rc. Вы можете соединять ресурсы с исходным кодом во время линковки. Окончательный продукт ― это исполняемый файл, который содержит как инструкции, так и ресурсы.
Файлы ресурсов можно создать используя любой текстовый редактор. Они состоят из набора фраз, определяющих внешний вид и другие атрибуты ресурсов, используемых в программе. Хотя вы можете писать файлы ресурсов в текстовом редакторе, это довольно тяжело. Лучшая альтернатива ― использовать редактор ресурсов, который позволит визуально создавать дизайн ваших ресурсов не отвлекаясь на текстовую рутину. Редакторы ресурсов обычно входят в пакет с IDE, таких как Visual C++, Borland C++ и т.д.
В ресурсах меню описано примерно так:
Кликните здесь для просмотра всего текста
C
1
2
3
4
 MyMenu MENU
{
[menu list here]
}
Си-программисты могут заметить, что это похоже на объявление структуры.
MyMenu ― это имя меню, за ним следует ключевое слово MENU и список пунктов меню, заключенный в фигурные скобки. Вместо них вы можете использовать BEGIN и END. Этот вариант больше понравится программистам на Паскале.
Список меню включает в себя выражения 'MENUITEM' или 'POPUP'.
  • 'MENUITEM' определяет пункт меню, который не является подменю. Его синтаксис следующий:
    Кликните здесь для просмотра всего текста
    C
    1
    
     MENUITEM "&tex", ID [,options]
    Выражение начинается ключевым словом 'MENUITEM', за который следует текст, который будет отображаться. Обратите внимание на & (амперсанд). Его действие заключается в том, что следующий за ним символ будет подчеркнут. Затем идет строка в качестве ID пункта меню. ID ― это номер, который будет использоваться для обозначения пункта меню в сообщении, посылаемое процедуре окно, когда этот пункт меню будет выбран. Каждое ID должно быть уникальным.
    Доступны следующие опции:
    • GRAYED ― пункт меню неактивен, и он не генерирует сообщение WM_COMMAND. Текст серого цвета.
    • INACTIVE ― пункт меню неактивен, и он не генерирует сообщение WM_COMMAND. Текст отображается нормально.
    • MENUBREAK ― этот пункт меню и последующие пункты отображаются после новой строки меню.
    • HELP ― этот пункт меню и последующие пункты выравнены по правой стороне.
    Вы можете использовать одну из вышеописанных опций или комбинировать их оператором "or". Учтите, что 'INACTIVE' и 'GRAYED' не могут комбинироваться вместе.
  • Выражение 'POPUP' имеет следующий синтаксис:
    Кликните здесь для просмотра всего текста
    C
    1
    2
    3
    4
    
     POPUP "&text" [,options]
    {
    [menu list]
    .}
    Выражение 'POPUP' определяет пункт меню, при выборе которого выпадает список пунктов в маленьком popup-окне. Список меню может быть выражением 'MENUITEM' или 'POPUP'. Есть специальный вид выражения 'MENUITEM' ― 'MENUITEM SEPARATOR', который отрисовывает горизонтальную линию в popup-окне.
  • Последний шаг ― это ссылка на ваш скрипт ресурса меню в программе.
Вы можете сделать это в двух разных местах.
  • В поле lрszMenuName структуры WNDCLASSEX. Скажем, если у вас было меню под названием "FirstMenu", вы можете присоединить меню к вашему окну следующим образом:
    Кликните здесь для просмотра всего текста
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    
     .DATA
    MenuName db "FirstMenu",0
    ...........................
    ...........................
    .CODE
    ...........................
    mov wc.lpszMenuName, OFFSET MenuName
    ...........................
  • С помощью параметра-хэндла меню в функции CreateWindowEx:
    Кликните здесь для просмотра всего текста
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
     .DATA
    MenuName db "FirstMenu",0
    hMenu HMENU ?
    ...........................
    ...........................
    .CODE
    ...........................
    invoke LoadMenu, hInst, OFFSET MenuName
    mov hMenu, eax
    invoke CreateWindowEx,NULL,OFFSET ClsName,OFFSET Caption,\
    WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USE­DEFAULT,\
    CW_USEDEFAULT,CW_USEDEFAULT,NULL,hMenu,h­Inst,NULL
    ...........................
В чем разница между этими двумя методами? Когда делается ссылка на меню в структуре WNDCLASSEX, меню становится "меню по умолчанию" для данного класса окна. Каждое окно этого класса будет иметь такое меню.
Если вы хотите, чтобы каждое окно, созданное из одного класса, имело разное меню, вы можете выбрать второй вариант. В этом случае, любое окно, которому передается хэндл меню в функции CreateWindowEx будет иметь меню, которое замещает меню по умолчанию, указанное в структуре WNDCLASSEX. Сейчас мы узнаем, как меню уведомляет процедуру окна о том, что пользователь выбрал пункт меню.
Когда пользователь выберет пункт меню, процедура окна получит сообщение WM_COMMAND. Младшее слово wParam'а содержит ID выбранного пункта меню.
Теперь у нас достаточно информации для того, чтобы создать и использовать меню. Давайте сделаем это.
ПРАКТИКА, СЕСТРА ШИЗОФРЕНИИ
Первый пример показывает нам как создать и использовать меню, указав имя меню в классе окна.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
 .386
.model flat,stdcall
option casemap:none
 
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
 
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
 
.data
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
MenuName db "FirstMenu",0 ; The name of our menu in the resource file.
Test_string db "You selected Test menu item",0
Hello_string db "Hello, my friend",0
Goodbye_string db "See you again, bye",0
 
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
 
.const
IDM_TEST equ 1 ; Menu IDs
IDM_HELLO equ 2
IDM_GOODBYE equ 3
IDM_EXIT equ 4
 
.code
start: invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
 
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
 
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdL­ine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
 
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,OFFSET MenuName ; Put our menu name here
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
 
invoke RegisterClassEx, addr wc
invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USE­DEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hI­nst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
 
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF ax==IDM_TEST
invoke MessageBox,NULL,ADDR Test_string,OFFSET AppName,MB_OK
.ELSEIF ax==IDM_HELLO
invoke MessageBox, NULL,ADDR Hello_string, OFFSET AppName,MB_OK
.ELSEIF ax==IDM_GOODBYE
invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, MB_OK
.ELSE
invoke DestroyWindow,hWnd
.ENDIF
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
Menu.rc
Кликните здесь для просмотра всего текста
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define IDM_TEST 1
#define IDM_HELLO 2
#define IDM_GOODBYE 3
#define IDM_EXIT 4
 
FirstMenu MENU
{
POPUP "&PopUp"
{
MENUITEM "&Say Hello",IDM_HELLO
MENUITEM "Say &GoodBye", IDM_GOODBYE
MENUITEM SEPARATOR
MENUITEM "E&xit",IDM_EXIT
}
MENUITEM "&Test", IDM_TEST
}
Разбор полетов
Давайте сначала проанализируем файл ресурсов.
Кликните здесь для просмотра всего текста
C
1
2
3
4
 #define IDM_TEST 1 /* все равно, что IDM_TEST equ 1*/
#define IDM_HELLO 2
#define IDM_GOODBYE 3
#define IDM_EXIT 4
Вышенаписанные строки определяют ID пунктов меню. Вы можете присваивать ID любое значение, главное, чтобы оно было уникально.
Кликните здесь для просмотра всего текста
Assembler
1
 FirstMenu MENU
Определите ваше меню ключевым словом 'MENU'.
Кликните здесь для просмотра всего текста
C
1
2
3
4
5
6
7
 POPUP "&PopUp"
{
MENUITEM "&Say Hello",IDM_HELLO
MENUITEM "Say &GoodBye", IDM_GOODBYE
MENUITEM SEPARATOR
MENUITEM "E&xit",IDM_EXIT
}
Определите рoрuр-меню с четырьмя пунктами меню, третье ― это разделитель пунктов меню.
Кликните здесь для просмотра всего текста
Assembler
1
 MENUITEM "&Test", IDM_TEST
Определите пункт меню в основном меню.
Далее мы изучим исходный код.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
 MenuName db "FirstMenu",0 ; Имя нашего меню в файле ресурсов
Test_string db "You selected Test menu item",0
Hello_string db "Hello, my friend",0
Goodbye_string db "See you again, bye",0
'MenuName' ― это имя меню в файле ресурсов. Заметьте, что вы можете определить более, чем одно меню в файле ресурсов, поэтому вы можете указывать, какое меню хотите использовать. Остающиеся три строки определяют текстовые строки, которые будут отображаться в messagebox'е при выборе соответствующего пункта меню пользователем.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
 IDM_TEST equ 1 ; ID меню
IDM_HELLO equ 2
IDM_GOODBYE equ 3
IDM_EXIT equ 4
Определите ID меню для использования в процедуре окна. Эти значения должны совпадать с теми, что были определены в файле ресурсов.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
 .ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF ax==IDM_TEST
invoke MessageBox,NULL,ADDR Test_string,OFFSET AppName,MB_OK
.ELSEIF ax==IDM_HELLO
invoke MessageBox, NULL,ADDR Hello_string, OFFSET AppName,MB_OK
.ELSEIF ax==IDM_GOODBYE
invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, MB_OK
.ELSE
invoke DestroyWindow,hWnd
.ENDIF
В процедуре окна мы обрабатываем сообщение WM_COMMAND. Когда пользователь выбирает пункт меню, его ID посылается процедуре окна в нижнем слове wParam'а вместе с сообщением WM_COMMAND. Поэтому, когда мы сохраняем значение wParam в eax, мы сравниваем значение в ax с ID пунктов меню, определенными ранее, и поступаем соответствующим образом. В первых трех случаях, когда пользователь выбирает 'Test', 'Say Hell' и 'Say GoodBye', мы отображаем текстовую строку в MessageBox'е.
Если пользователь выбирает пункт 'Exit', мы вызываем DestroyWindow с хэндлом нашего окна в качестве его параметра, которое закрывает наше окно.
Как вы можете видеть, указание имени меню в классе окна довольно просто и прямолинейно. Тем не менее, вы также можете использовать альтернативный метод для того, чтобы загружать меню в ваше окно. Я не буду воспроизводить здесь весь исходный код. Файл ресурсов такой же. Есть небольшие изменения в исходнике, которые я покажу ниже.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
 .data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hMenu HMENU ? ; handle of our menu
Определите переменную типа HMENU, чтобы сохранить хэндл нашего меню.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
 invoke LoadMenu, hInst, OFFSET MenuName
mov hMenu,eax
INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAUL­T,NULL,hMenu,\
hInst,NULL
Перед вызовом CreateWindowEx, мы вызываем LoadMenu, передавая ему хэндл процесса и указатель на имя меню. LoadMenu возвращает хэндл нашего меню, который мы передаем CreateWindowEx.
____________________________
© Iczelion, пер. Aquila.
Вложения
Тип файла: zip tut08-1.zip (3.3 Кб, 155 просмотров)
Тип файла: zip tut08-2.zip (3.3 Кб, 129 просмотров)
4
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
08.01.2013, 12:34  [ТС]
Win32 API. Урок 8a. Создание меню через файл ресурсов
В этом туториале мы научимся, как вставить в наше окно меню.
Скачайте пример.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
.686P
.model flat
includelib user32.lib
includelib kernel32.lib
include windows.inc
;вызываемые функции Windows (внешние ссылки)
extern _imp__CreateWindowExA@48:dword;создание окна
extern _imp__DefWindowProcA@16:dword;стандартна­я обработка сообщения
extern _imp__DispatchMessageA@4:dword;отправка сообщения
extern _imp__GetMessageA@16:dword;получение сообщения
extern _imp__ExitProcess@4:dword;выход из программы
extern _imp__RegisterClassA@4:dword;регистрация класса окна
extern _imp__MessageBoxA@16:dword
.const
IDM_TEST equ 0
IDM_HELLO equ 1
IDM_GOODBYE equ 2
IDM_EXIT equ 3
.code
start: xchg ebx,eax
mov edi,offset wTitle
mov esi,400000h
push edi ; Указатель на имя нашего класса
push offset MenuName ;Меню
push COLOR_WINDOW+1 ;Фон нашего окна
push 10011h ;Курсор окна по умолчанию (здесь указан ID обычной стрелки)
push ebx ;Иконка окна по умолчанию
push esi;Адрес нашей программы в памяти - Windows всегда её грузит
;по этому адресу
push ebx ;Эта хрень нам не нужна
push ebx ;и эта тоже
push offset WndProc ;Адрес процедуры обработки событий
push ebx ; Стиль нашего окна
push esp ;&WNDCLASSA
call _imp__RegisterClassA@4
push ebx
push esi
shl esi,9;esi=CW_USEDEFAULT
push ebx
push ebx
push esi
push esi
push esi
push esi
push WS_OVERLAPPEDWINDOW + WS_VISIBLE
push edi; szWinTitle
push edi; lpszClassName
push ebx
call _imp__CreateWindowExA@48 ;создать окно
mov ebp,esp
message_loop: push ebx ;цикл обработки сообщений
push ebx
push ebx
push ebp
call _imp__GetMessageA@16
push ebp
call _imp__DispatchMessageA@4 ;вернуть управление Windows
jmp short message_loop
;----------------------------------------------------------------
WndProc:
Msg equ dword ptr [esp+8]
hwnd equ dword ptr [esp+10h];поправка на аргументы,
wParam equ dword ptr [esp+0Ch];которые передаются MessageBox
 
mov eax,Msg
dec eax;cmp eax,WM_DESTROY=2
dec eax
je short @@WM_DESTROY
sub eax,WM_COMMAND-WM_DESTROY
je short @@WM_COMMAND
jmp _imp__DefWindowProcA@16
@@WM_DESTROY: push eax;0 ;завершение программы
call _imp__ExitProcess@4
@@WM_COMMAND: mov ebx,wParam
cmp ebx,IDM_EXIT
je short @@WM_DESTROY
push eax;MB_OK
push offset MenuName
push menu_handlers[ebx*4]
push hwnd
call _imp__MessageBoxA@16
retn 10h
;----------------------------------------------------------
wTitle db 'создание меню через файл ресурсов',0
MenuName db "FirstMenu",0
Test_string db "You selected Test menu item",0
Hello_string db "Hello, my friend",0
Goodbye_string db "See you again, bye",0
menu_handlers dd Test_string, Hello_string, Goodbye_string
end start
Для работы с ресурсами нам потребуется создать файл с таким же именем, но с расширением .RC, ниже показано его содержимое
Кликните здесь для просмотра всего текста
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define IDM_TEST 0
#define IDM_HELLO 1
#define IDM_GOODBYE 2
#define IDM_EXIT 3
 
FirstMenu MENU
{
POPUP "&PopUp"
{
MENUITEM "&Say Hello",IDM_HELLO
MENUITEM "Say &GoodBye", IDM_GOODBYE
MENUITEM SEPARATOR
MENUITEM "E&xit",IDM_EXIT
}
MENUITEM "&Test", IDM_TEST
}
Перед тем как заниматься основной программой, давайте посмотрим на файл ресурсов приложения Menu.rc, содержание которого приведено в листинге. Файлы ресурсов можно создавать в текстовом виде (при условии, что вы знакомы с их синтаксисом), а можно воспользоваться редактором ресурсов. Важно понимать, что файл ресурсов ― это обычный текстовый файл. После его создания его компилируют и превращают в двоичный файл с расширением .RES.
Важно!
Комментарии в файлах ресурсов, как и комментарии в программах на C++, начинаются с двойного символа наклонной черты.
В следующих четырех строках определяются четыре константы
Кликните здесь для просмотра всего текста
C
1
2
3
4
#define IDM_TEST 0
#define IDM_HELLO 1
#define IDM_GOODBYE 2
#define IDM_EXIT 3
Константы соответствуют идентификаторам ресурсов, с помощью которых программа обращается к командам меню. Точно такие же идентификаторы есть и в основном файле
Кликните здесь для просмотра всего текста
C
1
2
3
4
IDM_TEST equ 0
IDM_HELLO equ 1
IDM_GOODBYE equ 2
IDM_EXIT equ 3
Важно чтобы в разные файлы включались одни и те же идентификаторы.
Добавление меню в класс окна
Далее вы должны указать меню в классе окна. Если взглянуть на определение структуры WNDCLASS в основном приложении, можно найти следующую строку
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 . . .
push edi ; Указатель на имя нашего класса
push offset MenuName ;Меню
push COLOR_WINDOW+1 ;Фон нашего окна
push 10011h ;Курсор окна по умолчанию (здесь указан ID обычной стрелки)
push ebx ;Иконка окна по умолчанию
push esi;Адрес нашей программы в памяти -- Windows всегда её грузит
;по этому адресу
push ebx ;Эта хрень нам не нужна
push ebx ;и эта тоже
push offset WndProc ;Адрес процедуры обработки событий
push ebx ; Стиль нашего окна
push esp ;&WNDCLASSA
call _imp__RegisterClassA@4
. . .
MenuName db "FirstMenu",0
То же имя должно быть и в файле ресурсов
Кликните здесь для просмотра всего текста
C
1
2
FirstMenu MENU
{
Добавление имени меню в структуру WNDCLASS ― это все что требуется для установления связи между приложением и меню.
Обработка команд меню
Теперь, когда в главном окне нашего приложения появилось меню, вам наверняка хочется знать, как реагировать на действия пользователя при выполнении им определенной команды. При выполнении команды меню Windows посылает вашему приложению сообщение ― в данном случае WM_COMMAND. Это означает, что вы должны включить в процедуру окна обработку сообщения WM_COMMAND. Когда приложение получает сообщение WM_COMMAND в параметре wParam хранится идентификатор выполненной команды меню.
При выборе пунктов меню "Say Hello", "Say GoodBye" и "Test" должны быть выведены мессаджбоксы с сообщениями "Hello, my friend", "See you again, bye" и "You selected Test menu item" соответственно, а при выборе пункта меню "Exit" мы должны закрыть приложение.

Iczelion на нашем месте три раза повторяет одинаковые фрагменты
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
 .ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF ax==IDM_TEST
invoke MessageBox,NULL,ADDR Test_string,OFFSET AppName,MB_OK
.ELSEIF ax==IDM_HELLO
invoke MessageBox, NULL,ADDR Hello_string, OFFSET AppName,MB_OK
.ELSEIF ax==IDM_GOODBYE
invoke MessageBox,NULL,ADDR Goodbye_string, OFFSET AppName, MB_OK
.ELSE
invoke DestroyWindow,hWnd
.ENDIF
А мы будем только менять сообщение у мессаджбокса в зависимости от содержимого wParam и если wParam=IDM_EXIT перейдем на обработку сообщения WM_DESTROY. Простенько, но со вкусом!
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
@@WM_COMMAND: mov ebx,wParam
cmp ebx,IDM_EXIT
je short @@WM_DESTROY
push eax;MB_OK
push offset MenuName
push menu_handlers[ebx*4]
push hwnd
call _imp__MessageBoxA@16
. . .
Test_string db "You selected Test menu item",0
Hello_string db "Hello, my friend",0
Goodbye_string db "See you again, bye",0
menu_handlers dd Test_string, Hello_string, Goodbye_string
Небольшие пояснения
  • к моменту перехода на @@WM_COMMAND в регистре eax ноль, константа MB_OK=0, команда push 0 это два байта (6800h), а push eax ― это один байт (50h)
  • содержимое регистра ebx=wParam является индексом в таблице адресов menu_handlers:
    menu_handlers[0]=offset Test_string,
    menu_handlers[4]=offset Hello_string,
    menu_handlers[8]=offset Goodbye_string
Сам себе компилятор ресурсов.
Теперь уменьшим размер с 972 до 892 байт за счет уменьшения размера заголовка и ручной сборки ресурсов.
Скачайте ещё один пример.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
.686p
.model tiny,stdcall
;for WinXP - 892 bytes
include windows.inc
exebase equ 400000h
IDM_TEST equ 0
IDM_HELLO equ 1
IDM_GOODBYE equ 2
IDM_EXIT equ 3
ID_MENU equ 30
POPUP equ 10h
MFR_END equ 80h
MFR_POPUP equ 1
du macro string
local bslash
bslash=0
irpc c,<string>
if bslash eq 0
if '&c' eq "/"
bslash=1
elseif '&c' gt 127
db ('&c'- 0B0h),4
else
dw '&c'
endif
else
bslash=0
if '&c' eq "n"
DW 0Dh,0Ah
elseif '&c' eq "/"
dw '/'
elseif '&c' eq "r"
dw 0Dh
elseif '&c' eq "l"
dw 0Ah
elseif '&c' eq "s"
dw 20h
elseif '&c' eq "c"
dw 3Bh
elseif '&c' eq "t"
dw 9
endif
endif
endm
dw 0
endm
.code
main:
include capito_res.asm
;---------------------------------------------------------
start: xor ebx,ebx
mov edi,offset wTitle+exebase
mov esi,exebase
invoke RegisterClass,esp,ebx,offset WndProc+exebase,ebx,ebx,esi,ebx,\
10011h,COLOR_WINDOW+1,offset MenuName+exebase,edi
push ebx
push esi
shl esi,9;esi=CW_USEDEFAULT
invoke CreateWindowEx,ebx,edi,edi,WS_OVERLAPPED­WINDOW + WS_VISIBLE,\
esi,esi,esi,esi,ebx,ebx
mov ebp,esp
message_loop: invoke GetMessage,ebp,ebx,ebx,ebx;цикл обработки сообщений
invoke DispatchMessage,ebp ;вернуть управление Windows
jmp short message_loop
;----------------------------------------------------------------
WndProc:
Msg equ dword ptr [esp+8]
hwnd equ dword ptr [esp+10h];поправка на аргументы,
wParam equ dword ptr [esp+0Ch];которые передаются MessageBox
 
mov eax,Msg
dec eax;cmp eax,WM_DESTROY=2
dec eax
je short @@WM_DESTROY
sub eax,WM_COMMAND-WM_DESTROY
je short @@WM_COMMAND
jmp DefWindowProc+exebase
@@WM_DESTROY: invoke ExitProcess,eax;0 ;завершение программы
@@WM_COMMAND: mov ebx,wParam
cmp ebx,IDM_EXIT
je short @@WM_DESTROY
invoke MessageBox,hwnd,menu_handlers[ebx*4+exebase],\
offset MenuName+exebase,eax
retn 10h
;----------------------------------------------------------
wTitle db 'создание меню через файл ресурсов',0
MenuName db "FirstMenu",0
Test_string db "You selected Test menu item",0
Hello_string db "Hello, my friend",0
Goodbye_string db "See you again, bye",0
menu_handlers dd Test_string+exebase, Hello_string+exebase, Goodbye_string+exebase
;-------------------------------------------------------
align 2
resource:
Characteristics1 dd 0
TimeDateStamp1 dd 0
MajorVersion1 dw 0
MinorVersion1 dw 0;
NumberOfNamedEntries1 dw 0;количество ресурсов с именами
NumberOfIdEntries1 dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является типом ресурса
dw RT_MENU,0;номер типа ресурса
dw x-resource,8000h; если во 2-ом слове установлен старший бит - есть ссылка
;на оглавление второго уровня. В 1-ом слове смещение второго оглавления
;относительно начала раздела ресурсов
x:
Characteristics2 dd 0
TimeDateStamp2 dd 0
MajorVersion2 dw 0
MinorVersion2 dw 0;
NumberOfNamedEntries2 dw 1;количество ресурсов с именами
NumberOfIdEntries2 dw 0;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является идентификатором меню
dw 88,8000h
dw x1-resource,8000h; если во 2-ом слове установлен старший бит - есть ссылка
;на оглавление третьего уровня. В 1-ом слове смещение третьего оглавления
;относительно начала раздела ресурсов
x1:
Characteristics3 dd 0
TimeDateStamp3 dd 0
MajorVersion3 dw 0
MinorVersion3 dw 0;
NumberOfNamedEntries3 dw 0;количество ресурсов с именами
NumberOfIdEntries3 dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является идентификатором языка, который
;используется данным ресурсом 16 * SUBLANG_ + LANG_
dw (LANG_ENGLISH or SUBLANG_ENGLISH_CAN*100h),0,x2-resource,0
x2:;struct _IMAGE_RESOURCE_DATA_ENTRY
OffsetToData1 dd menu1
Size1 dd end_menu-menu1
CodePage1 dd 0
Reserved1 dd 0
dw 9;количество букв в слове FIRSTMENU
du <FIRSTMENU>
dw 0;выравниваем размер до четного количества слов
menu1:
dw 0,0,POPUP
du <&PopUp>
dw MFT_STRING or MFS_ENABLED,IDM_HELLO
du <&Say Hello>
dw MFT_STRING or MFS_ENABLED,IDM_GOODBYE
du <Say &GoodBye>
dw 0,0,0;NOTEXT
dw MFT_STRING or MFS_ENABLED or MFR_END,IDM_EXIT
du <E&xit>
dw MFT_STRING or MFS_ENABLED or MFR_END,IDM_TEST
du <&Test>
dw 0,0
end_menu:
end_resource:
import:
dd 0,0,0,user32_dll
dd user32_table
dd 0,0,0,kernel32_dll
dd kernel32_table
dd 0,0
kernel32_table:
ExitProcess dd _ExitProcess,0
user32_table:
RegisterClass dd _RegisterClass
CreateWindowEx dd _CreateWindowEx
GetMessage dd _GetMessage
DispatchMessage dd _DispatchMessage
MessageBox dd _MessageBox
DefWindowProc dd _DefWindowProc
dw 0
_RegisterClass db 0,0,'RegisterClassA'
_CreateWindowEx db 0,0,'CreateWindowExA'
_GetMessage db 0,0,'GetMessageA'
_DispatchMessage db 0,0,'DispatchMessageA'
_MessageBox db 0,0,'MessageBoxA'
_DefWindowProc db 0,0,'DefWindowProcA',0
user32_dll db 'user32'
_ExitProcess db 0,0,'ExitProcess',0
kernel32_dll db 'kernel32'
end_import:
end main
_____________________________________
© Mikl___ 2013
Вложения
Тип файла: zip tut08a-01.zip (3.6 Кб, 167 просмотров)
4
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
09.01.2013, 11:49  [ТС]
Win32 API. Урок 8 b. Создание меню через функцию LoadMenu
Кликните здесь для просмотра всего текста
В этом Уроке мы создадим полнофункциональную Windows программу, которое выводит сообщение — "Win32 assembly is great!".
Скачайте пример здесь.
ТЕОРИЯ, МАТЬ СКЛЕРОЗА
Windows предоставляет огромное количество ресурсов Windows-программам через Windows API (Application Programming Interface). Windows API — это большая коллекция очень полезных функций, располагающихся непосредственно в операционной системе и готовых для использования программами. Эти функции находятся в нескольких динамически подгружаемых библиотеках (DLLs), таких как kernel32.dll, user32.dll и gdi32.dll. Kernel32.dll содержит API-функции, взаимодействующие с памятью и управляющие процессами. User32.dll контролирует пользовательский интерфейс. Gdi32.dll ответственен за графические операции. Кроме этих трех "основных", существуют также другие dll, которые вы можете использовать, при условии, что вы обладаете достаточным количеством информации о нужных API-функциях. Windows программы динамически подсоединяется к этим библиотекам, то есть код API-функций не включается в исполняемый файл. Информация находится в библиотеках импорта. Вы должны слинковать ваши программы с правильными библиотеками импорта, иначе они не смогут найти эти функции. Когда Windows программа загружается в память, Windows читает информацию, сохраненную в программе. Эта информация включает имена функций, которые программа использует и DLL-ей, в которых эти функции располагаются. Когда Windows находит подобную информацию в программе, она вызывает библиотеки и исправляет в программе вызовы этих функций, так что контроль всегда будет передаваться по правильному адресу.
Существует две категории API функций: одни работают с ANSI-строками, а другие с Unicode-строками. Имена API-функций использующих ANSI-строки заканчиваются на "A", например, MessageBoxA. В конце имен функций для Unicode находится "W". Windows 95/98 от природы поддерживают ANSI, а Windows NT и производные от нее 2k/XP/Vista поддерживают Unicode. Обычно мы имеем дело с ANSI строками (массивы символов, оканчивающиеся NULL-ом. размер ANSI-символа — 1 байт. В то время как ANSI достаточна для европейских языков, она не поддерживает некоторые восточные языки, в которых есть несколько тысяч уникальных символов. Вот в этих случаях в дело вступает UniCode. размер символа UNICODE — 2 байта, и поэтому может поддерживать 65536 различных символов. Hо по большей части, вы будете использовать include-файл, который может определить и выбрать подходящую для вашей платформы функцию. Просто обращайтесь к именам API-функций без постфикса.
ПРАКТИКА, МАТЬ ШИЗОФРЕНИИ
Я приведу голый скелет программы ниже. Позже мы разберем его.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
.386
.model flat, stdcall
.data
.code
start:
end start
Выполнение начинается с первой инструкции, следующей за меткой, установленной после конца директив. В вышеприведенном каркасе выполнение начинается непосредственно после метки 'start'. Будут последовательно выполняться инструкция за инструкцией, пока не встретится операция передачи управления, такая как: jmр, jne, je, ret и так далее. Эти инструкции перенаправляют поток выполнения другим инструкциям. Когда программа выходит в Windows, ей следует вызвать API-функцию ExitProcess.
Assembler
1
ExitProcess proto uExitCode:DWORD
Строка выше называется прототипом функции. Прототип функции указывает ассемблеру/линкеру атрибуты функции, чтобы он сделал проверку типов данных и количество атрибутов передаваемых функции. Формат прототипа функции следующий:
Assembler
1
ИмяФункции PROTO [ИмяПараметра]:ТипДанных,[ИмяПараметра]:ТипДанных,...
Короче говоря, за именем функции следует ключевое слово PROTO, а затем список переменных с типом данных, разделенных запятыми. В приведенном выше примере с ExitProcess, эта функция была определена как принимающая только один параметр типа DWORD. Прототипы функций очень полезны, когда вы используете высокоуровневый синтаксический вызов — invoke. Вы можете считать об invoke как обычный вызов с проверкой типов данных. Например, если вы напишите:
Assembler
1
call ExitProcess
Линкер уведомит вас, что вы забыли положит в стек двойное слово. Я рекомендую вам использовать invoke вместо простого вызова. Синтаксис invoke следующий:
Assembler
1
invoke выражение [, аргументы]
Выражение может быть именем функции или указателем на функцию. Параметры функции разделены запятыми.
Большинство прототипов для API-функций содержатся в include-файлах. Если вы используете hutch'евский MASM32, они будут находится в директории MASM32/INCLUDE. Файлы подключения имеют расширение .inc и прототипы функций DLL находятся в .inc файле с таким же именем, как и у этой DLL.
Hапример, ExitProcess экспортируется из kernel32.lib, так что прототип ExitProcess находится в kernel32.inc.

Вы также можете создать прототипы для ваших собственных функций. Во всех моих экземплярах я использую hutch'евский windows.inc, который вы можете скачать с http://win32asm.cjb.net
Возвращаясь к ExitProcess: параметр uExitCode - это значение, которое программа вернет Windows после окончания программы. Вы можете вызвать функцию ExitProcess так:
Assembler
1
invoke ExitProcess, 0
Поместив эту строку непосредственно после стартовой метки, вы получите Win32-программу, немедленно выходящую в Windows, но тем не менее полнофункциональную.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.data
.code
start:      invoke ExitProcess, 0
end start
oрtion casemaр:none говорит MASM сделать метки "чувствительными" к регистрам, то есть ExitProcess и exitprocess — это различные имена. Отметьте новую директиву — include. После нее следует имя файла, который вы хотите вставить в то место, где эта директива располагается. В примере выше, когда MASM обрабатывает линию include \masm32\include\windows.inc, он открывает windows.inc, находящийся в директории \MASM32\INCLUDE, и далее анализирует содержимое windows.inc так, как будто вы "вклеили" подключаемый файл. Хатчевский windows.inc содержит в себе определения констант и структур, которые вам могут понадобиться для программирования под Win32. Этот файл не содержит в себе прототипов функций. Windows.inc ни в коем случае не является исчерпывающим и всеобъемлющим. Hutch и я пытаемся заполнить его как можно большим количеством констант и структур, но есть еще довольно, что следовало бы включить. Он постоянно обновляется. Заходите на хатчевскую и мою странички за свежими апдейтами. Из windows.inc, ваша программа будет брать определения констант и структур. Что касается прототипов функций, вы должны подключить другие include-файлы. Они находятся в директории \masm32\include.

В вышеприведенном примере, мы вызываем функцию, экспортированную из kernel32.dll, для чего мы должны подключить прототипы функций из kernel32.dll. Этот файл - kernel32.inc. Если вы открываете его текстовым редактором, вы увидите, что он состоит из прототипов функций из соответствующей dll. Если вы не подключите kernel32.inc, вы все еще можете вызвать ExitProcess, но уже с помощью ассемблерной команды call. Вы не сможете вызвать эту функцию с помощью invoke. Дело вот в чем: для того, чтобы вызвать функцию через invoke, вы должны поместить в исходном коде ее прототип. В примере выше, если вы не подключите kernel32.inc, вы можете определить прототип для ExitProcess где-нибудь до вызова этой функции и это будет работать. Файлы подключения нужны для того, что избавить вас от лишней работы и вам не пришлось набирать все прототипы самим.
Теперь мы встречаем новую директиву — includelib. Она работает не так, как include. Это всего лишь способ сказать ассемблеру какие библиотеки использует ваша программа должна прилинковать. Хотя вы вовсе не обязаны использовать именно этот метод. Вы можете указать имена библиотек импорта к командной строке при запуске линкера, но поверьте мне, это весьма скучно и утомительно, да и командная строка может вместить максимум 128 символов.
Теперь возьмите весь исходный текст примера этого Урока, сохраните его как msgbox.asm и ассемблируйте его так:
Code
1
ml /c /coff /Cp msgbox.asm
/c говорит MASM'у создать .obj файл в формате COFF. MASM использует вариант COFF (Common Object File Format), использующийся под Unix, как его собственный объектный и исполняемый формат файлов.
/Cр говорит MASM'у сохранять регистр имен, заданных пользователем. Если вы используете hutch'евский MASM32 пакет, вы можете вставить "option casemaр:none" в начале вашего исходника, сразу после директивы .model, чтобы добиться того же эффекта.
После успешной компиляции msgbox.asm, вы получите msgbox.obj. Это объектный файл, от которого один шаг до екзешника. Obj содержит инструкции/данные в двоичной форме. Отсутствуют только необходимая корректировка адресов, которая проводится линкером.
Теперь сделайте следующее:
Code
1
link /SUBSYSTEM:WINDOWS  /LIBPATH:c:\masm32\lib  msgbox.obj
/SUBSYSTEM:WINDOWS информирует линкер о том, какого вида является будущий исполняемый модуль.
/LIBPATH:<путь к библиотекам импорта> говорит линкеру, где находятся библиотеки импорта. Если вы используете MASM32, они будут в MASM32\lib.

Линкер читает объектный файл и корректирует его, используя адреса, взятые из библиотек импорта. После окончания линковки вы получите файл msgbox.exe. Запустите его. Вы увидите, что она ничего не делает.

Да, мы не поместили в код ничего не интересного. Hо тем не менее полноценная Windows программа. И посмотрите на размер! Hа моем PC — 1.536 байт.
Теперь мы готовы создать окно с сообщением. Прототип функции, которая нам для этого необходима следующая:
Assembler
1
MessageBox PROTO hwnd:DWORD, lpText:DWORD, lpCaption:DWORD, uType:DWORD
hwnd — это хэндл родительского окна. Вы можете считать хэндл числом, представляющим окно, к которому вы обращаетесь. Его значение для вас не важно. Вы только должны знать, что оно представляет окно. Когда вы захотите сделать что-нибудь с окном, вы должны обратиться к нему, используя его хэндл.
lрText — это указатель на текст, который вы хотите отобразить в клиентской части окна сообщения. Указатель ― это адрес чего-либо. Указатель на текстовую строку = адрес этой строки.
lpCaption — это указатель на заголовок окна сообщения.
uType — устанавливает иконку, число и вид кнопок окна.

Давайте изменим msgbox.asm для отображения сообщения.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
.data
MsgBoxCaption  db "Iczelion Tutorial #2",0
MsgBoxText     db "Win32 Assembly is Great!",0
.code
start: invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK
invoke ExitProcess, NULL
end start
Скомпилируйте и запустите. Вы увидите окошко с сообщением "Win32 Assembly is great!".
Давайте снова взглянем на исходник.
Мы определили две оканчивающиеся NULL'ом строки в секции .data. Помните, что каждая ANSI строка в Windows должна оканчиваться NULL'ом (0 в шестнадцатеричной системе). Мы используем две константы, NULL и MB_OK. Эти константы прописаны в windows.inc, так что вы можете обратиться к ним, указав их имя, а не значение. Это улучшает читабельность кода.
Оператор addr используется для передачи адреса метки (и не только) функции. Он действителен только в контексте директивы invoke. Вы не можете использовать его, чтобы присвоить адрес метки регистру или переменной, например. В данном примере вы можете использовать offset вместо addr. Тем не менее, есть некоторые различия между ними.
1. addr не может быть использован с метками, которые определены впереди, а offset может. Например, если метка определена где-то дальше в коде, чем строка с invoke, addr не будет работать.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
invoke MessageBox,NULL, addr MsgBoxText,addr MsgBoxCaption,MB_OK
......
MsgBoxCaption  db "Iczelion Tutorial #2",0
MsgBoxText       db "Win32 Assembly is Great!",0
MASM доложит об ошибке. Если вы используете offset вместо addr, MASM без проблем скомпилирует указанный отрывок кода.
2. Addr поддерживает локальные переменные, в то время как offset нет.
Локальная переменная — это всего лишь зарезервированное место в стеке. Вы только знаете его адрес во время выполнения программы. Offset интерпретируется во время компиляции ассемблером, поэтому неудивительно, что он не поддерживает локальные переменные. Addr же работает с ними, потому что ассемблер сначала проверяет ― глобальная переменная или локальная. Если она глобальная, он помещает адрес этой переменной в объектный файл. В этом случае оператор работает как offset. Если это локальная переменная, компилятор генерирует следующую последовательность инструкций, перед тем как будет вызвана функция:
Assembler
1
2
lea eax, LocalVar
push eax
Учитывая, что lea может определить адрес метки в "рантайме", все работает прекрасно.
________________________________________ ____
© Iczelion, пер. Aquila.
6
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
10.01.2013, 05:08  [ТС]
Win32 API. Урок 8 i. Работа с инструментальной панелью (toolbar)
Кликните здесь для просмотра всего текста
В этом Уроке мы создадим полнофункциональную Windows программу, которое выводит сообщение — "Win32 assembly is great!".
Скачайте пример здесь.
ТЕОРИЯ, МАТЬ СКЛЕРОЗА
Windows предоставляет огромное количество ресурсов Windows-программам через Windows API (Application Programming Interface). Windows API — это большая коллекция очень полезных функций, располагающихся непосредственно в операционной системе и готовых для использования программами. Эти функции находятся в нескольких динамически подгружаемых библиотеках (DLLs), таких как kernel32.dll, user32.dll и gdi32.dll. Kernel32.dll содержит API-функции, взаимодействующие с памятью и управляющие процессами. User32.dll контролирует пользовательский интерфейс. Gdi32.dll ответственен за графические операции. Кроме этих трех "основных", существуют также другие dll, которые вы можете использовать, при условии, что вы обладаете достаточным количеством информации о нужных API-функциях. Windows программы динамически подсоединяется к этим библиотекам, то есть код API-функций не включается в исполняемый файл. Информация находится в библиотеках импорта. Вы должны слинковать ваши программы с правильными библиотеками импорта, иначе они не смогут найти эти функции. Когда Windows программа загружается в память, Windows читает информацию, сохраненную в программе. Эта информация включает имена функций, которые программа использует и DLL-ей, в которых эти функции располагаются. Когда Windows находит подобную информацию в программе, она вызывает библиотеки и исправляет в программе вызовы этих функций, так что контроль всегда будет передаваться по правильному адресу.
Существует две категории API функций: одни работают с ANSI-строками, а другие с Unicode-строками. Имена API-функций использующих ANSI-строки заканчиваются на "A", например, MessageBoxA. В конце имен функций для Unicode находится "W". Windows 95/98 от природы поддерживают ANSI, а Windows NT и производные от нее 2k/XP/Vista поддерживают Unicode. Обычно мы имеем дело с ANSI строками (массивы символов, оканчивающиеся NULL-ом. размер ANSI-символа — 1 байт. В то время как ANSI достаточна для европейских языков, она не поддерживает некоторые восточные языки, в которых есть несколько тысяч уникальных символов. Вот в этих случаях в дело вступает UniCode. размер символа UNICODE — 2 байта, и поэтому может поддерживать 65536 различных символов. Hо по большей части, вы будете использовать include-файл, который может определить и выбрать подходящую для вашей платформы функцию. Просто обращайтесь к именам API-функций без постфикса.
ПРАКТИКА, МАТЬ ШИЗОФРЕНИИ
Я приведу голый скелет программы ниже. Позже мы разберем его.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
.386
.model flat, stdcall
.data
.code
start:
end start
Выполнение начинается с первой инструкции, следующей за меткой, установленной после конца директив. В вышеприведенном каркасе выполнение начинается непосредственно после метки 'start'. Будут последовательно выполняться инструкция за инструкцией, пока не встретится операция передачи управления, такая как: jmр, jne, je, ret и так далее. Эти инструкции перенаправляют поток выполнения другим инструкциям. Когда программа выходит в Windows, ей следует вызвать API-функцию ExitProcess.
Assembler
1
ExitProcess proto uExitCode:DWORD
Строка выше называется прототипом функции. Прототип функции указывает ассемблеру/линкеру атрибуты функции, чтобы он сделал проверку типов данных и количество атрибутов передаваемых функции. Формат прототипа функции следующий:
Assembler
1
ИмяФункции PROTO [ИмяПараметра]:ТипДанных,[ИмяПараметра]:ТипДанных,...
Короче говоря, за именем функции следует ключевое слово PROTO, а затем список переменных с типом данных, разделенных запятыми. В приведенном выше примере с ExitProcess, эта функция была определена как принимающая только один параметр типа DWORD. Прототипы функций очень полезны, когда вы используете высокоуровневый синтаксический вызов — invoke. Вы можете считать об invoke как обычный вызов с проверкой типов данных. Например, если вы напишите:
Assembler
1
call ExitProcess
Линкер уведомит вас, что вы забыли положит в стек двойное слово. Я рекомендую вам использовать invoke вместо простого вызова. Синтаксис invoke следующий:
Assembler
1
invoke выражение [, аргументы]
Выражение может быть именем функции или указателем на функцию. Параметры функции разделены запятыми.
Большинство прототипов для API-функций содержатся в include-файлах. Если вы используете hutch'евский MASM32, они будут находится в директории MASM32/INCLUDE. Файлы подключения имеют расширение .inc и прототипы функций DLL находятся в .inc файле с таким же именем, как и у этой DLL.
Hапример, ExitProcess экспортируется из kernel32.lib, так что прототип ExitProcess находится в kernel32.inc.

Вы также можете создать прототипы для ваших собственных функций. Во всех моих экземплярах я использую hutch'евский windows.inc, который вы можете скачать с http://win32asm.cjb.net
Возвращаясь к ExitProcess: параметр uExitCode - это значение, которое программа вернет Windows после окончания программы. Вы можете вызвать функцию ExitProcess так:
Assembler
1
invoke ExitProcess, 0
Поместив эту строку непосредственно после стартовой метки, вы получите Win32-программу, немедленно выходящую в Windows, но тем не менее полнофункциональную.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.data
.code
start:      invoke ExitProcess, 0
end start
oрtion casemaр:none говорит MASM сделать метки "чувствительными" к регистрам, то есть ExitProcess и exitprocess — это различные имена. Отметьте новую директиву — include. После нее следует имя файла, который вы хотите вставить в то место, где эта директива располагается. В примере выше, когда MASM обрабатывает линию include \masm32\include\windows.inc, он открывает windows.inc, находящийся в директории \MASM32\INCLUDE, и далее анализирует содержимое windows.inc так, как будто вы "вклеили" подключаемый файл. Хатчевский windows.inc содержит в себе определения констант и структур, которые вам могут понадобиться для программирования под Win32. Этот файл не содержит в себе прототипов функций. Windows.inc ни в коем случае не является исчерпывающим и всеобъемлющим. Hutch и я пытаемся заполнить его как можно большим количеством констант и структур, но есть еще довольно, что следовало бы включить. Он постоянно обновляется. Заходите на хатчевскую и мою странички за свежими апдейтами. Из windows.inc, ваша программа будет брать определения констант и структур. Что касается прототипов функций, вы должны подключить другие include-файлы. Они находятся в директории \masm32\include.

В вышеприведенном примере, мы вызываем функцию, экспортированную из kernel32.dll, для чего мы должны подключить прототипы функций из kernel32.dll. Этот файл - kernel32.inc. Если вы открываете его текстовым редактором, вы увидите, что он состоит из прототипов функций из соответствующей dll. Если вы не подключите kernel32.inc, вы все еще можете вызвать ExitProcess, но уже с помощью ассемблерной команды call. Вы не сможете вызвать эту функцию с помощью invoke. Дело вот в чем: для того, чтобы вызвать функцию через invoke, вы должны поместить в исходном коде ее прототип. В примере выше, если вы не подключите kernel32.inc, вы можете определить прототип для ExitProcess где-нибудь до вызова этой функции и это будет работать. Файлы подключения нужны для того, что избавить вас от лишней работы и вам не пришлось набирать все прототипы самим.
Теперь мы встречаем новую директиву — includelib. Она работает не так, как include. Это всего лишь способ сказать ассемблеру какие библиотеки использует ваша программа должна прилинковать. Хотя вы вовсе не обязаны использовать именно этот метод. Вы можете указать имена библиотек импорта к командной строке при запуске линкера, но поверьте мне, это весьма скучно и утомительно, да и командная строка может вместить максимум 128 символов.
Теперь возьмите весь исходный текст примера этого Урока, сохраните его как msgbox.asm и ассемблируйте его так:
Code
1
ml /c /coff /Cp msgbox.asm
/c говорит MASM'у создать .obj файл в формате COFF. MASM использует вариант COFF (Common Object File Format), использующийся под Unix, как его собственный объектный и исполняемый формат файлов.
/Cр говорит MASM'у сохранять регистр имен, заданных пользователем. Если вы используете hutch'евский MASM32 пакет, вы можете вставить "option casemaр:none" в начале вашего исходника, сразу после директивы .model, чтобы добиться того же эффекта.
После успешной компиляции msgbox.asm, вы получите msgbox.obj. Это объектный файл, от которого один шаг до екзешника. Obj содержит инструкции/данные в двоичной форме. Отсутствуют только необходимая корректировка адресов, которая проводится линкером.
Теперь сделайте следующее:
Code
1
link /SUBSYSTEM:WINDOWS  /LIBPATH:c:\masm32\lib  msgbox.obj
/SUBSYSTEM:WINDOWS информирует линкер о том, какого вида является будущий исполняемый модуль.
/LIBPATH:<путь к библиотекам импорта> говорит линкеру, где находятся библиотеки импорта. Если вы используете MASM32, они будут в MASM32\lib.

Линкер читает объектный файл и корректирует его, используя адреса, взятые из библиотек импорта. После окончания линковки вы получите файл msgbox.exe. Запустите его. Вы увидите, что она ничего не делает.

Да, мы не поместили в код ничего не интересного. Hо тем не менее полноценная Windows программа. И посмотрите на размер! Hа моем PC — 1.536 байт.
Теперь мы готовы создать окно с сообщением. Прототип функции, которая нам для этого необходима следующая:
Assembler
1
MessageBox PROTO hwnd:DWORD, lpText:DWORD, lpCaption:DWORD, uType:DWORD
hwnd — это хэндл родительского окна. Вы можете считать хэндл числом, представляющим окно, к которому вы обращаетесь. Его значение для вас не важно. Вы только должны знать, что оно представляет окно. Когда вы захотите сделать что-нибудь с окном, вы должны обратиться к нему, используя его хэндл.
lрText — это указатель на текст, который вы хотите отобразить в клиентской части окна сообщения. Указатель ― это адрес чего-либо. Указатель на текстовую строку = адрес этой строки.
lpCaption — это указатель на заголовок окна сообщения.
uType — устанавливает иконку, число и вид кнопок окна.

Давайте изменим msgbox.asm для отображения сообщения.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
.data
MsgBoxCaption  db "Iczelion Tutorial #2",0
MsgBoxText     db "Win32 Assembly is Great!",0
.code
start: invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK
invoke ExitProcess, NULL
end start
Скомпилируйте и запустите. Вы увидите окошко с сообщением "Win32 Assembly is great!".
Давайте снова взглянем на исходник.
Мы определили две оканчивающиеся NULL'ом строки в секции .data. Помните, что каждая ANSI строка в Windows должна оканчиваться NULL'ом (0 в шестнадцатеричной системе). Мы используем две константы, NULL и MB_OK. Эти константы прописаны в windows.inc, так что вы можете обратиться к ним, указав их имя, а не значение. Это улучшает читабельность кода.
Оператор addr используется для передачи адреса метки (и не только) функции. Он действителен только в контексте директивы invoke. Вы не можете использовать его, чтобы присвоить адрес метки регистру или переменной, например. В данном примере вы можете использовать offset вместо addr. Тем не менее, есть некоторые различия между ними.
1. addr не может быть использован с метками, которые определены впереди, а offset может. Например, если метка определена где-то дальше в коде, чем строка с invoke, addr не будет работать.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
invoke MessageBox,NULL, addr MsgBoxText,addr MsgBoxCaption,MB_OK
......
MsgBoxCaption  db "Iczelion Tutorial #2",0
MsgBoxText       db "Win32 Assembly is Great!",0
MASM доложит об ошибке. Если вы используете offset вместо addr, MASM без проблем скомпилирует указанный отрывок кода.
2. Addr поддерживает локальные переменные, в то время как offset нет.
Локальная переменная — это всего лишь зарезервированное место в стеке. Вы только знаете его адрес во время выполнения программы. Offset интерпретируется во время компиляции ассемблером, поэтому неудивительно, что он не поддерживает локальные переменные. Addr же работает с ними, потому что ассемблер сначала проверяет ― глобальная переменная или локальная. Если она глобальная, он помещает адрес этой переменной в объектный файл. В этом случае оператор работает как offset. Если это локальная переменная, компилятор генерирует следующую последовательность инструкций, перед тем как будет вызвана функция:
Assembler
1
2
lea eax, LocalVar
push eax
Учитывая, что lea может определить адрес метки в "рантайме", все работает прекрасно.
________________________________________ ____
© Iczelion, пер. Aquila.
Миниатюры
Сам себе Iczelion  
Изображения
 
3
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
10.01.2013, 07:55  [ТС]
Win32 API. Урок 8 j. Всплывающие подсказки
Скачайте пример.
Теория ― мать склероза
В современных программах, управляемых с помощью меню и инструментальных панелей, принято включать в состав меню всплывающие подсказки (tooltips) ― небольшие текстовые окошки, открываемые автоматически и содержащие краткое описание назначения кнопок, когда курсор мыши помещается на соответствующую кнопку инструментальной панели.
Для организации всплывающих подсказок следует прежде всего создать инструментальную панель, как это было описано в предыдущем подразделе, дополнив ее стиль, указываемый в качестве второго параметра функции CreateToolbarEx, константой TBSYLE_TOOLTIPS.
Отслеживание положения курсора мыши и вывод на экран требуемых подсказок осуществляется в ответ на сообщение Windows WM_NOTIFY, для обработки которого придется дополнить функцию WinProc. В момент прихода сообщения WM_NOTIFY параметр lParam имеет указатель на структуру NMHDR,
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
NMHDR STRUCT
hwndFrom DWORD ? ;дескриптор элемента управления
idFrom DWORD ? ;идентификатор элемента управления
icode DWORD ? ;код, передаваемый в сообщение
NMHDR ends
которая входит в большую по объему структуру типа TOOLTIPTEXT, в которую NMHDR входит в качестве первого элемента.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
TOOLTIPTEXT STRUCT
hdr NMHDR <>
lpszText DWORD ?
szText BYTE 80 dup (?)
union
hInst dd ?
hinst dd ?
ends
uFlags DWORD ?
lParam LPARAM ?
TOOLTIPTEXT ENDS
Таким образом, если использовать параметр lParam непосредственно, он будет служить указателем на структуру NMHDR, и с его помощью можно обращаться к элементам этой структуры, однако остальные поля структуры TOOLTIPTEXT будут недоступны. Если же преобразовать параметр lParam в тип LPTOOLTIPTEXT, то он будет указывать на всю структуру TOOLTIPTEXT, и через него можно получить доступ ко всем элементам этой структуры; через имя вложенной структуры hdr в этом случае можно получить доступ и к элементам структуры NMHDR.
Таким образом, допустимы, в частности, следующие обращения:
  • lParam->icode (lParam ― указатель на структуру NMHDR, а icode ― элемент этой структуры);
  • lParam->idFrom (lParam ― указатель на структуру NMHDR, а idFrom ― элемент этой структуры);
  • ((LPTOOLTIPTEXT)lParam)->szText (выражение (LPTOOLTIPTEXT)lParam является указателем на структуру TOOLTIPTEXT, а szText ― элемент этой структуры);
  • ((LPTOOLTIPTEXT)lParam)->hdr.idFrom (обращение к элементам вложенной структуры посредством указателя на внешнюю).
Сообщения WM_NOTIFY представляют для нас интерес лишь тогда, когда icode, входящий в структуру NMHDR, равен TTN_NEEDTEXT, а поле idFrom содержится идентификатор кнопки, для которой требуется подсказка. Текст подсказки можно задать одним из трех способов:
  1. скопировать текст подсказки в поле szText структуры TOOLTIPTEXT;
  2. записать в поле szText указатель на текстовую строку;
  3. задать идентификатор ресурса строки. В этом случае идентификатор строки записывается в поле lpszText, а поле hInst содержит дескриптор приложения, содержащего ресурс.
Простейшим является способ записать в lpszText указатель на строку, определенную в программе.

Следующий фрагмент обрабатывает запросы подсказок в нашей программе
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
@WM_NOTIFY: mov edx,[lParam] ;if( LPNMHDR(lParam)->code ==TTN_NEEDTEXT)
cmp dword ptr [edx+NMHDR.icode],TTN_NEEDTEXT
jnz end_wm_check ;LPTOOLTIPTEXT lpttt=(LPTOOLTIPTEXT) lParam;
mov eax,[edx+NMHDR.idFrom] ;switch (lpttt->hdr.idFrom) {
cmp eax,ID_3 ;case ID_1: strcpy(lpttt->szText,"Заполнить синим");break;
ja end_wm_check ;case ID_2: strcpy(lpttt->szText,"Заполнить зеленым");break;
lea edx,[edx+TOOLTIPTEXT.szText];case ID_3: strcpy(lpttt->szText,"выход");
push dword ptr [ps+eax*4]+exebase
push edx
call lstrcpy+exebase
После того, как текст подсказки определен, подсказка отображается автоматически при возврате управления к Windows, и программе больше не нужно выполнять никаких действий. Как видите, подсказки хорошо автоматизированы и просты в использовании.
Практика ― сестра шизофрении
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
.586p
.model tiny
include windows.inc
;for WinXP - 1164 bytes
.code
exebase equ 400000h
ID_1 equ 0
ID_2 equ 1
ID_3 equ 2
main:
include capito.asm
;---------------------------------------------------------
start: xor ebx,ebx
mov esi,exebase
mov edi,offset wTitle+exebase
;------------------------------
; registering the window class
;------------------------------
invoke RegisterClass,esp,ebx,offset window_procedure+exebase,\
ebx,ebx,esi,ebx,10011h,COLOR_WINDOW+1,eb­x,edi
;--------------------------+
; creating the main window |
;--------------------------+
push esi
push ebx
push esi
shl esi,9;esi=CW_DEFAULT
invoke CreateWindowEx,ebx,edi,edi,WS_OVERLAPPED­WINDOW or WS_VISIBLE,\
esi,esi,esi,esi,ebx,ebx
pop esi;esi=400000h
xchg edi,eax;в edi дескриптор главного окна
invoke LoadImage,esi,offset aMybp1+exebase,ebx,ebx,ebx,\
LR_LOADFROMFILE
;создается структурная переменная - массив из трех структур типа TBBUTTON
mov edx,sizeof(TBBUTTON)
inc ebx
inc ebx;ebx=2
lea ecx,[tbb+edx*2]+exebase
@@: mov [ecx+TBBUTTON.iBitmap],ebx;порядковый номер кнопки (кнопки нумеруются от 0);
mov [ecx+TBBUTTON.idCommand],ebx;идентификатор кнопки
mov byte ptr [ecx+TBBUTTON.fsState],TBSTATE_ENABLED;состояние кнопки.
;Значение TBSTATE_ENABLED разрешает управление с пмощью данной кнопки
test ebx,ebx;заполняем массив с ebx как индекс в массиве от 2 до 0
jz short @f
sub ecx,edx;переходим к следующей структуре TBBUTTON
dec ebx
jmp short @b
@@: invoke CreateToolbarEx,edi,WS_CHILD or WS_BORDER or WS_VISIBLE or TBSTYLE_TOOLTIPS,\
-1,3,ebx,eax,ecx,3,18,18,18,18,edx,edx
 
mov edi,offset hBlueBrush+exebase;edi на массив кистей
mov esi,offset CreateSolidBrush+exebase;функцию CreateSolidBrush вызываю два раза
mov ebp,08F0000h
push ebp;синий=08F0000h
call dword ptr [esi]
stosd;mov [edi],eax;hBlueBrush
shr ebp,8
push ebp;зеленый=08F00h
call dword ptr [esi]
stosd;mov [edi+4],eax;hGreenBrush
mov ebp,esp
; +---------------------------+
; | entering the message loop |
; +---------------------------+
message_loop: invoke GetMessage,ebp,ebx,ebx,ebx
invoke DispatchMessage,ebp
jmp message_loop
; +----------------------+
; | the window procedure |
; +----------------------+
window_procedure:
hWnd equ ebp+8
uMsg equ ebp+0Ch
wParam equ ebp+10h
lParam equ ebp+14h
lpttt equ ebp-4
enter sizeof(PAINTSTRUCT)+4,0
mov eax,[uMsg]
mov edi,[hWnd]
dec eax
dec eax; cmp uMsg,WM_DESTROY
je @@WM_DESTROY
sub eax,WM_SIZE-WM_DESTROY;cmp eax,WM_SIZE
je @@WM_SIZE
sub eax,WM_PAINT-WM_SIZE;cmp eax,WM_PAINT
je @@WM_PAINT
sub eax,WM_NOTIFY-WM_PAINT; cmp uMsg,WM_NOTIFY
je @@WM_NOTIFY
sub eax,WM_COMMAND-WM_NOTIFY; cmp uMsg,WM_COMMAND
je @@WM_COMMAND
leave
jmp DefWindowProc+exebase
@@WM_PAINT: invoke BeginPaint,edi,esp
mov ecx,[sw+exebase]
jecxz @f;в sw при старте программы ноль, если sw=0
;тогда не нужно изменять цвет окна
;заполняем окно выбранной кистью из массива кистей
lea esi,[esp].PAINTSTRUCT.rcPaint
invoke FillRect,eax,esi,dword ptr [hBlueBrush+ecx*4-4]+exebase
@@: invoke EndPaint,edi,esp;Освобождаем контекст отображения
jmp end_wm_check
@@WM_NOTIFY: mov edx,[lParam] ;if( LPNMHDR(lParam)->code ==TTN_NEEDTEXT)
cmp dword ptr [edx+NMHDR.icode],TTN_NEEDTEXT
jnz end_wm_check
mov [lpttt],edx ;LPTOOLTIPTEXT lpttt=(LPTOOLTIPTEXT) lParam;
mov eax,[edx+NMHDR.idFrom];switch (lpttt->hdr.idFrom) {
cmp eax,ID_3 ;case ID_1:strcpy(lpttt->szText,"синий");break;
ja end_wm_check ;case ID_2:strcpy(lpttt->szText,"зеленый");break;
;case ID_3:strcpy(lpttt->szText,"выход");
lea edx,[edx+TOOLTIPTEXT.szText]
invoke lstrcpy,edx,dword ptr [ps+eax*4]+exebase
jmp end_wm_check
@@WM_SIZE: mov eax,[lParam];при изменении размера окна - получаем
;размеры окна
mov word ptr [esp+PAINTSTRUCT.rcPaint.right],ax
shr eax,16
mov dword ptr [esp+PAINTSTRUCT.rcPaint.bottom],eax
jmp short end_wm_check
@@WM_COMMAND: mov eax,[wParam];в зависимости от значения в wParam
;либо закрываем окно (wParam=2), либо изменяем его цвет (wParam=0 или 1)
jmp [handler+eax*4]+exebase
a: inc eax
mov [sw+exebase],eax
;если wParam=0 или wParam=1 посылаем сообщение WM_PAINT
invoke InvalidateRect,edi,ebx,1
end_wm_check: leave
retn 10h
@@WM_DESTROY: invoke ExitProcess,ebx;завершение программы
;=======================================­===========
handler dd a+exebase,a+exebase,@@WM_DESTROY+exebase
wTitle db 'Iczelion Tutorial #8-10:инструментальная панель и всплывающие подсказки в MASM';,0
tbb db sizeof(TBBUTTON)*3 dup (0)
szText1 db 'Заполнить синим',0
szText2 db 'Заполнить зеленым',0
szText3 db 'Выход',0
ps dd szText1+exebase,szText2+exebase,szText3+­exebase
aMybp1 db "Images\btns.bmp"
import:
sw dd 0
hBlueBrush dd 0,0
;-----------------------------------------------------------------
dd user32_dll
dd user32_table
dd 0,0,0,gdi32_dll
dd gdi32_table
dd 0,0,0,comctl32_dll
dd comctl32_table
dd 0,0,0,kernel32_dll
dd kernel32_table
dd 0,0
kernel32_table:
ExitProcess dd _ExitProcess
lstrcpy dd _lstrcpy,0
user32_table:
RegisterClass dd _RegisterClass
DefWindowProc dd _DefWindowProc
GetMessage dd _GetMessage
DispatchMessage dd _DispatchMessage
LoadImage dd _LoadImage
BeginPaint dd _BeginPaint
EndPaint dd _EndPaint
InvalidateRect dd _InvalidateRect
FillRect dd _FillRect
CreateWindowEx dd _CreateWindowEx,0
gdi32_table:
CreateSolidBrush dd _CreateSolidBrush,0
comctl32_table:
CreateToolbarEx dd _CreateToolbarEx
dw 0
_RegisterClass db 0,0,'RegisterClassA'
_DefWindowProc db 0,0,'DefWindowProcA'
_GetMessage db 0,0,'GetMessageA'
_DispatchMessage db 0,0,'DispatchMessageA'
_LoadImage db 0,0,'LoadImageA'
_BeginPaint db 0,0,'BeginPaint'
_EndPaint db 0,0,'EndPaint'
_InvalidateRect db 0,0,'InvalidateRect'
_FillRect db 0,0,'FillRect'
_CreateWindowEx db 0,0,'CreateWindowExA',0
user32_dll db 'user32'
_lstrcpy db 0,0,'lstrcpyA'
_ExitProcess db 0,0,'ExitProcess',0
kernel32_dll db 'kernel32'
_CreateSolidBrush db 0,0,"CreateSolidBrush",0
gdi32_dll db 'gdi32'
_CreateToolbarEx db 0,0,"CreateToolbarEx",0
comctl32_dll db 'comctl32'
end_import:
end main
________________________________________ ­__________
© Mikl___ 2013
2
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
10.01.2013, 08:14  [ТС]
Win32 API. Урок 8k. Инструментальная панель

Кликните здесь для просмотра всего текста
В этом Уроке мы создадим полнофункциональную Windows программу, которое выводит сообщение — "Win32 assembly is great!".
Скачайте пример здесь.
ТЕОРИЯ, МАТЬ СКЛЕРОЗА
Windows предоставляет огромное количество ресурсов Windows-программам через Windows API (Application Programming Interface). Windows API — это большая коллекция очень полезных функций, располагающихся непосредственно в операционной системе и готовых для использования программами. Эти функции находятся в нескольких динамически подгружаемых библиотеках (DLLs), таких как kernel32.dll, user32.dll и gdi32.dll. Kernel32.dll содержит API-функции, взаимодействующие с памятью и управляющие процессами. User32.dll контролирует пользовательский интерфейс. Gdi32.dll ответственен за графические операции. Кроме этих трех "основных", существуют также другие dll, которые вы можете использовать, при условии, что вы обладаете достаточным количеством информации о нужных API-функциях. Windows программы динамически подсоединяется к этим библиотекам, то есть код API-функций не включается в исполняемый файл. Информация находится в библиотеках импорта. Вы должны слинковать ваши программы с правильными библиотеками импорта, иначе они не смогут найти эти функции. Когда Windows программа загружается в память, Windows читает информацию, сохраненную в программе. Эта информация включает имена функций, которые программа использует и DLL-ей, в которых эти функции располагаются. Когда Windows находит подобную информацию в программе, она вызывает библиотеки и исправляет в программе вызовы этих функций, так что контроль всегда будет передаваться по правильному адресу.
Существует две категории API функций: одни работают с ANSI-строками, а другие с Unicode-строками. Имена API-функций использующих ANSI-строки заканчиваются на "A", например, MessageBoxA. В конце имен функций для Unicode находится "W". Windows 95/98 от природы поддерживают ANSI, а Windows NT и производные от нее 2k/XP/Vista поддерживают Unicode. Обычно мы имеем дело с ANSI строками (массивы символов, оканчивающиеся NULL-ом. размер ANSI-символа — 1 байт. В то время как ANSI достаточна для европейских языков, она не поддерживает некоторые восточные языки, в которых есть несколько тысяч уникальных символов. Вот в этих случаях в дело вступает UniCode. размер символа UNICODE — 2 байта, и поэтому может поддерживать 65536 различных символов. Hо по большей части, вы будете использовать include-файл, который может определить и выбрать подходящую для вашей платформы функцию. Просто обращайтесь к именам API-функций без постфикса.
ПРАКТИКА, МАТЬ ШИЗОФРЕНИИ
Я приведу голый скелет программы ниже. Позже мы разберем его.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
.386
.model flat, stdcall
.data
.code
start:
end start
Выполнение начинается с первой инструкции, следующей за меткой, установленной после конца директив. В вышеприведенном каркасе выполнение начинается непосредственно после метки 'start'. Будут последовательно выполняться инструкция за инструкцией, пока не встретится операция передачи управления, такая как: jmр, jne, je, ret и так далее. Эти инструкции перенаправляют поток выполнения другим инструкциям. Когда программа выходит в Windows, ей следует вызвать API-функцию ExitProcess.
Assembler
1
ExitProcess proto uExitCode:DWORD
Строка выше называется прототипом функции. Прототип функции указывает ассемблеру/линкеру атрибуты функции, чтобы он сделал проверку типов данных и количество атрибутов передаваемых функции. Формат прототипа функции следующий:
Assembler
1
ИмяФункции PROTO [ИмяПараметра]:ТипДанных,[ИмяПараметра]:ТипДанных,...
Короче говоря, за именем функции следует ключевое слово PROTO, а затем список переменных с типом данных, разделенных запятыми. В приведенном выше примере с ExitProcess, эта функция была определена как принимающая только один параметр типа DWORD. Прототипы функций очень полезны, когда вы используете высокоуровневый синтаксический вызов — invoke. Вы можете считать об invoke как обычный вызов с проверкой типов данных. Например, если вы напишите:
Assembler
1
call ExitProcess
Линкер уведомит вас, что вы забыли положит в стек двойное слово. Я рекомендую вам использовать invoke вместо простого вызова. Синтаксис invoke следующий:
Assembler
1
invoke выражение [, аргументы]
Выражение может быть именем функции или указателем на функцию. Параметры функции разделены запятыми.
Большинство прототипов для API-функций содержатся в include-файлах. Если вы используете hutch'евский MASM32, они будут находится в директории MASM32/INCLUDE. Файлы подключения имеют расширение .inc и прототипы функций DLL находятся в .inc файле с таким же именем, как и у этой DLL.
Hапример, ExitProcess экспортируется из kernel32.lib, так что прототип ExitProcess находится в kernel32.inc.

Вы также можете создать прототипы для ваших собственных функций. Во всех моих экземплярах я использую hutch'евский windows.inc, который вы можете скачать с http://win32asm.cjb.net
Возвращаясь к ExitProcess: параметр uExitCode - это значение, которое программа вернет Windows после окончания программы. Вы можете вызвать функцию ExitProcess так:
Assembler
1
invoke ExitProcess, 0
Поместив эту строку непосредственно после стартовой метки, вы получите Win32-программу, немедленно выходящую в Windows, но тем не менее полнофункциональную.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.data
.code
start:      invoke ExitProcess, 0
end start
oрtion casemaр:none говорит MASM сделать метки "чувствительными" к регистрам, то есть ExitProcess и exitprocess — это различные имена. Отметьте новую директиву — include. После нее следует имя файла, который вы хотите вставить в то место, где эта директива располагается. В примере выше, когда MASM обрабатывает линию include \masm32\include\windows.inc, он открывает windows.inc, находящийся в директории \MASM32\INCLUDE, и далее анализирует содержимое windows.inc так, как будто вы "вклеили" подключаемый файл. Хатчевский windows.inc содержит в себе определения констант и структур, которые вам могут понадобиться для программирования под Win32. Этот файл не содержит в себе прототипов функций. Windows.inc ни в коем случае не является исчерпывающим и всеобъемлющим. Hutch и я пытаемся заполнить его как можно большим количеством констант и структур, но есть еще довольно, что следовало бы включить. Он постоянно обновляется. Заходите на хатчевскую и мою странички за свежими апдейтами. Из windows.inc, ваша программа будет брать определения констант и структур. Что касается прототипов функций, вы должны подключить другие include-файлы. Они находятся в директории \masm32\include.

В вышеприведенном примере, мы вызываем функцию, экспортированную из kernel32.dll, для чего мы должны подключить прототипы функций из kernel32.dll. Этот файл - kernel32.inc. Если вы открываете его текстовым редактором, вы увидите, что он состоит из прототипов функций из соответствующей dll. Если вы не подключите kernel32.inc, вы все еще можете вызвать ExitProcess, но уже с помощью ассемблерной команды call. Вы не сможете вызвать эту функцию с помощью invoke. Дело вот в чем: для того, чтобы вызвать функцию через invoke, вы должны поместить в исходном коде ее прототип. В примере выше, если вы не подключите kernel32.inc, вы можете определить прототип для ExitProcess где-нибудь до вызова этой функции и это будет работать. Файлы подключения нужны для того, что избавить вас от лишней работы и вам не пришлось набирать все прототипы самим.
Теперь мы встречаем новую директиву — includelib. Она работает не так, как include. Это всего лишь способ сказать ассемблеру какие библиотеки использует ваша программа должна прилинковать. Хотя вы вовсе не обязаны использовать именно этот метод. Вы можете указать имена библиотек импорта к командной строке при запуске линкера, но поверьте мне, это весьма скучно и утомительно, да и командная строка может вместить максимум 128 символов.
Теперь возьмите весь исходный текст примера этого Урока, сохраните его как msgbox.asm и ассемблируйте его так:
Code
1
ml /c /coff /Cp msgbox.asm
/c говорит MASM'у создать .obj файл в формате COFF. MASM использует вариант COFF (Common Object File Format), использующийся под Unix, как его собственный объектный и исполняемый формат файлов.
/Cр говорит MASM'у сохранять регистр имен, заданных пользователем. Если вы используете hutch'евский MASM32 пакет, вы можете вставить "option casemaр:none" в начале вашего исходника, сразу после директивы .model, чтобы добиться того же эффекта.
После успешной компиляции msgbox.asm, вы получите msgbox.obj. Это объектный файл, от которого один шаг до екзешника. Obj содержит инструкции/данные в двоичной форме. Отсутствуют только необходимая корректировка адресов, которая проводится линкером.
Теперь сделайте следующее:
Code
1
link /SUBSYSTEM:WINDOWS  /LIBPATH:c:\masm32\lib  msgbox.obj
/SUBSYSTEM:WINDOWS информирует линкер о том, какого вида является будущий исполняемый модуль.
/LIBPATH:<путь к библиотекам импорта> говорит линкеру, где находятся библиотеки импорта. Если вы используете MASM32, они будут в MASM32\lib.

Линкер читает объектный файл и корректирует его, используя адреса, взятые из библиотек импорта. После окончания линковки вы получите файл msgbox.exe. Запустите его. Вы увидите, что она ничего не делает.

Да, мы не поместили в код ничего не интересного. Hо тем не менее полноценная Windows программа. И посмотрите на размер! Hа моем PC — 1.536 байт.
Теперь мы готовы создать окно с сообщением. Прототип функции, которая нам для этого необходима следующая:
Assembler
1
MessageBox PROTO hwnd:DWORD, lpText:DWORD, lpCaption:DWORD, uType:DWORD
hwnd — это хэндл родительского окна. Вы можете считать хэндл числом, представляющим окно, к которому вы обращаетесь. Его значение для вас не важно. Вы только должны знать, что оно представляет окно. Когда вы захотите сделать что-нибудь с окном, вы должны обратиться к нему, используя его хэндл.
lрText — это указатель на текст, который вы хотите отобразить в клиентской части окна сообщения. Указатель ― это адрес чего-либо. Указатель на текстовую строку = адрес этой строки.
lpCaption — это указатель на заголовок окна сообщения.
uType — устанавливает иконку, число и вид кнопок окна.

Давайте изменим msgbox.asm для отображения сообщения.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
.data
MsgBoxCaption  db "Iczelion Tutorial #2",0
MsgBoxText     db "Win32 Assembly is Great!",0
.code
start: invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK
invoke ExitProcess, NULL
end start
Скомпилируйте и запустите. Вы увидите окошко с сообщением "Win32 Assembly is great!".
Давайте снова взглянем на исходник.
Мы определили две оканчивающиеся NULL'ом строки в секции .data. Помните, что каждая ANSI строка в Windows должна оканчиваться NULL'ом (0 в шестнадцатеричной системе). Мы используем две константы, NULL и MB_OK. Эти константы прописаны в windows.inc, так что вы можете обратиться к ним, указав их имя, а не значение. Это улучшает читабельность кода.
Оператор addr используется для передачи адреса метки (и не только) функции. Он действителен только в контексте директивы invoke. Вы не можете использовать его, чтобы присвоить адрес метки регистру или переменной, например. В данном примере вы можете использовать offset вместо addr. Тем не менее, есть некоторые различия между ними.
1. addr не может быть использован с метками, которые определены впереди, а offset может. Например, если метка определена где-то дальше в коде, чем строка с invoke, addr не будет работать.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
invoke MessageBox,NULL, addr MsgBoxText,addr MsgBoxCaption,MB_OK
......
MsgBoxCaption  db "Iczelion Tutorial #2",0
MsgBoxText       db "Win32 Assembly is Great!",0
MASM доложит об ошибке. Если вы используете offset вместо addr, MASM без проблем скомпилирует указанный отрывок кода.
2. Addr поддерживает локальные переменные, в то время как offset нет.
Локальная переменная — это всего лишь зарезервированное место в стеке. Вы только знаете его адрес во время выполнения программы. Offset интерпретируется во время компиляции ассемблером, поэтому неудивительно, что он не поддерживает локальные переменные. Addr же работает с ними, потому что ассемблер сначала проверяет ― глобальная переменная или локальная. Если она глобальная, он помещает адрес этой переменной в объектный файл. В этом случае оператор работает как offset. Если это локальная переменная, компилятор генерирует следующую последовательность инструкций, перед тем как будет вызвана функция:
Assembler
1
2
lea eax, LocalVar
push eax
Учитывая, что lea может определить адрес метки в "рантайме", все работает прекрасно.
________________________________________ ____
© Iczelion, пер. Aquila.
Миниатюры
Сам себе Iczelion  
4
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
10.01.2013, 09:51  [ТС]
Win32 API. Урок 8l. Подключаем стандартные диалоги к инструментальной панели
Кликните здесь для просмотра всего текста
В этом Уроке мы создадим полнофункциональную Windows программу, которое выводит сообщение — "Win32 assembly is great!".
Скачайте пример здесь.
ТЕОРИЯ, МАТЬ СКЛЕРОЗА
Windows предоставляет огромное количество ресурсов Windows-программам через Windows API (Application Programming Interface). Windows API — это большая коллекция очень полезных функций, располагающихся непосредственно в операционной системе и готовых для использования программами. Эти функции находятся в нескольких динамически подгружаемых библиотеках (DLLs), таких как kernel32.dll, user32.dll и gdi32.dll. Kernel32.dll содержит API-функции, взаимодействующие с памятью и управляющие процессами. User32.dll контролирует пользовательский интерфейс. Gdi32.dll ответственен за графические операции. Кроме этих трех "основных", существуют также другие dll, которые вы можете использовать, при условии, что вы обладаете достаточным количеством информации о нужных API-функциях. Windows программы динамически подсоединяется к этим библиотекам, то есть код API-функций не включается в исполняемый файл. Информация находится в библиотеках импорта. Вы должны слинковать ваши программы с правильными библиотеками импорта, иначе они не смогут найти эти функции. Когда Windows программа загружается в память, Windows читает информацию, сохраненную в программе. Эта информация включает имена функций, которые программа использует и DLL-ей, в которых эти функции располагаются. Когда Windows находит подобную информацию в программе, она вызывает библиотеки и исправляет в программе вызовы этих функций, так что контроль всегда будет передаваться по правильному адресу.
Существует две категории API функций: одни работают с ANSI-строками, а другие с Unicode-строками. Имена API-функций использующих ANSI-строки заканчиваются на "A", например, MessageBoxA. В конце имен функций для Unicode находится "W". Windows 95/98 от природы поддерживают ANSI, а Windows NT и производные от нее 2k/XP/Vista поддерживают Unicode. Обычно мы имеем дело с ANSI строками (массивы символов, оканчивающиеся NULL-ом. размер ANSI-символа — 1 байт. В то время как ANSI достаточна для европейских языков, она не поддерживает некоторые восточные языки, в которых есть несколько тысяч уникальных символов. Вот в этих случаях в дело вступает UniCode. размер символа UNICODE — 2 байта, и поэтому может поддерживать 65536 различных символов. Hо по большей части, вы будете использовать include-файл, который может определить и выбрать подходящую для вашей платформы функцию. Просто обращайтесь к именам API-функций без постфикса.
ПРАКТИКА, МАТЬ ШИЗОФРЕНИИ
Я приведу голый скелет программы ниже. Позже мы разберем его.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
.386
.model flat, stdcall
.data
.code
start:
end start
Выполнение начинается с первой инструкции, следующей за меткой, установленной после конца директив. В вышеприведенном каркасе выполнение начинается непосредственно после метки 'start'. Будут последовательно выполняться инструкция за инструкцией, пока не встретится операция передачи управления, такая как: jmр, jne, je, ret и так далее. Эти инструкции перенаправляют поток выполнения другим инструкциям. Когда программа выходит в Windows, ей следует вызвать API-функцию ExitProcess.
Assembler
1
ExitProcess proto uExitCode:DWORD
Строка выше называется прототипом функции. Прототип функции указывает ассемблеру/линкеру атрибуты функции, чтобы он сделал проверку типов данных и количество атрибутов передаваемых функции. Формат прототипа функции следующий:
Assembler
1
ИмяФункции PROTO [ИмяПараметра]:ТипДанных,[ИмяПараметра]:ТипДанных,...
Короче говоря, за именем функции следует ключевое слово PROTO, а затем список переменных с типом данных, разделенных запятыми. В приведенном выше примере с ExitProcess, эта функция была определена как принимающая только один параметр типа DWORD. Прототипы функций очень полезны, когда вы используете высокоуровневый синтаксический вызов — invoke. Вы можете считать об invoke как обычный вызов с проверкой типов данных. Например, если вы напишите:
Assembler
1
call ExitProcess
Линкер уведомит вас, что вы забыли положит в стек двойное слово. Я рекомендую вам использовать invoke вместо простого вызова. Синтаксис invoke следующий:
Assembler
1
invoke выражение [, аргументы]
Выражение может быть именем функции или указателем на функцию. Параметры функции разделены запятыми.
Большинство прототипов для API-функций содержатся в include-файлах. Если вы используете hutch'евский MASM32, они будут находится в директории MASM32/INCLUDE. Файлы подключения имеют расширение .inc и прототипы функций DLL находятся в .inc файле с таким же именем, как и у этой DLL.
Hапример, ExitProcess экспортируется из kernel32.lib, так что прототип ExitProcess находится в kernel32.inc.

Вы также можете создать прототипы для ваших собственных функций. Во всех моих экземплярах я использую hutch'евский windows.inc, который вы можете скачать с http://win32asm.cjb.net
Возвращаясь к ExitProcess: параметр uExitCode - это значение, которое программа вернет Windows после окончания программы. Вы можете вызвать функцию ExitProcess так:
Assembler
1
invoke ExitProcess, 0
Поместив эту строку непосредственно после стартовой метки, вы получите Win32-программу, немедленно выходящую в Windows, но тем не менее полнофункциональную.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.data
.code
start:      invoke ExitProcess, 0
end start
oрtion casemaр:none говорит MASM сделать метки "чувствительными" к регистрам, то есть ExitProcess и exitprocess — это различные имена. Отметьте новую директиву — include. После нее следует имя файла, который вы хотите вставить в то место, где эта директива располагается. В примере выше, когда MASM обрабатывает линию include \masm32\include\windows.inc, он открывает windows.inc, находящийся в директории \MASM32\INCLUDE, и далее анализирует содержимое windows.inc так, как будто вы "вклеили" подключаемый файл. Хатчевский windows.inc содержит в себе определения констант и структур, которые вам могут понадобиться для программирования под Win32. Этот файл не содержит в себе прототипов функций. Windows.inc ни в коем случае не является исчерпывающим и всеобъемлющим. Hutch и я пытаемся заполнить его как можно большим количеством констант и структур, но есть еще довольно, что следовало бы включить. Он постоянно обновляется. Заходите на хатчевскую и мою странички за свежими апдейтами. Из windows.inc, ваша программа будет брать определения констант и структур. Что касается прототипов функций, вы должны подключить другие include-файлы. Они находятся в директории \masm32\include.

В вышеприведенном примере, мы вызываем функцию, экспортированную из kernel32.dll, для чего мы должны подключить прототипы функций из kernel32.dll. Этот файл - kernel32.inc. Если вы открываете его текстовым редактором, вы увидите, что он состоит из прототипов функций из соответствующей dll. Если вы не подключите kernel32.inc, вы все еще можете вызвать ExitProcess, но уже с помощью ассемблерной команды call. Вы не сможете вызвать эту функцию с помощью invoke. Дело вот в чем: для того, чтобы вызвать функцию через invoke, вы должны поместить в исходном коде ее прототип. В примере выше, если вы не подключите kernel32.inc, вы можете определить прототип для ExitProcess где-нибудь до вызова этой функции и это будет работать. Файлы подключения нужны для того, что избавить вас от лишней работы и вам не пришлось набирать все прототипы самим.
Теперь мы встречаем новую директиву — includelib. Она работает не так, как include. Это всего лишь способ сказать ассемблеру какие библиотеки использует ваша программа должна прилинковать. Хотя вы вовсе не обязаны использовать именно этот метод. Вы можете указать имена библиотек импорта к командной строке при запуске линкера, но поверьте мне, это весьма скучно и утомительно, да и командная строка может вместить максимум 128 символов.
Теперь возьмите весь исходный текст примера этого Урока, сохраните его как msgbox.asm и ассемблируйте его так:
Code
1
ml /c /coff /Cp msgbox.asm
/c говорит MASM'у создать .obj файл в формате COFF. MASM использует вариант COFF (Common Object File Format), использующийся под Unix, как его собственный объектный и исполняемый формат файлов.
/Cр говорит MASM'у сохранять регистр имен, заданных пользователем. Если вы используете hutch'евский MASM32 пакет, вы можете вставить "option casemaр:none" в начале вашего исходника, сразу после директивы .model, чтобы добиться того же эффекта.
После успешной компиляции msgbox.asm, вы получите msgbox.obj. Это объектный файл, от которого один шаг до екзешника. Obj содержит инструкции/данные в двоичной форме. Отсутствуют только необходимая корректировка адресов, которая проводится линкером.
Теперь сделайте следующее:
Code
1
link /SUBSYSTEM:WINDOWS  /LIBPATH:c:\masm32\lib  msgbox.obj
/SUBSYSTEM:WINDOWS информирует линкер о том, какого вида является будущий исполняемый модуль.
/LIBPATH:<путь к библиотекам импорта> говорит линкеру, где находятся библиотеки импорта. Если вы используете MASM32, они будут в MASM32\lib.

Линкер читает объектный файл и корректирует его, используя адреса, взятые из библиотек импорта. После окончания линковки вы получите файл msgbox.exe. Запустите его. Вы увидите, что она ничего не делает.

Да, мы не поместили в код ничего не интересного. Hо тем не менее полноценная Windows программа. И посмотрите на размер! Hа моем PC — 1.536 байт.
Теперь мы готовы создать окно с сообщением. Прототип функции, которая нам для этого необходима следующая:
Assembler
1
MessageBox PROTO hwnd:DWORD, lpText:DWORD, lpCaption:DWORD, uType:DWORD
hwnd — это хэндл родительского окна. Вы можете считать хэндл числом, представляющим окно, к которому вы обращаетесь. Его значение для вас не важно. Вы только должны знать, что оно представляет окно. Когда вы захотите сделать что-нибудь с окном, вы должны обратиться к нему, используя его хэндл.
lрText — это указатель на текст, который вы хотите отобразить в клиентской части окна сообщения. Указатель ― это адрес чего-либо. Указатель на текстовую строку = адрес этой строки.
lpCaption — это указатель на заголовок окна сообщения.
uType — устанавливает иконку, число и вид кнопок окна.

Давайте изменим msgbox.asm для отображения сообщения.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
.data
MsgBoxCaption  db "Iczelion Tutorial #2",0
MsgBoxText     db "Win32 Assembly is Great!",0
.code
start: invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK
invoke ExitProcess, NULL
end start
Скомпилируйте и запустите. Вы увидите окошко с сообщением "Win32 Assembly is great!".
Давайте снова взглянем на исходник.
Мы определили две оканчивающиеся NULL'ом строки в секции .data. Помните, что каждая ANSI строка в Windows должна оканчиваться NULL'ом (0 в шестнадцатеричной системе). Мы используем две константы, NULL и MB_OK. Эти константы прописаны в windows.inc, так что вы можете обратиться к ним, указав их имя, а не значение. Это улучшает читабельность кода.
Оператор addr используется для передачи адреса метки (и не только) функции. Он действителен только в контексте директивы invoke. Вы не можете использовать его, чтобы присвоить адрес метки регистру или переменной, например. В данном примере вы можете использовать offset вместо addr. Тем не менее, есть некоторые различия между ними.
1. addr не может быть использован с метками, которые определены впереди, а offset может. Например, если метка определена где-то дальше в коде, чем строка с invoke, addr не будет работать.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
invoke MessageBox,NULL, addr MsgBoxText,addr MsgBoxCaption,MB_OK
......
MsgBoxCaption  db "Iczelion Tutorial #2",0
MsgBoxText       db "Win32 Assembly is Great!",0
MASM доложит об ошибке. Если вы используете offset вместо addr, MASM без проблем скомпилирует указанный отрывок кода.
2. Addr поддерживает локальные переменные, в то время как offset нет.
Локальная переменная — это всего лишь зарезервированное место в стеке. Вы только знаете его адрес во время выполнения программы. Offset интерпретируется во время компиляции ассемблером, поэтому неудивительно, что он не поддерживает локальные переменные. Addr же работает с ними, потому что ассемблер сначала проверяет ― глобальная переменная или локальная. Если она глобальная, он помещает адрес этой переменной в объектный файл. В этом случае оператор работает как offset. Если это локальная переменная, компилятор генерирует следующую последовательность инструкций, перед тем как будет вызвана функция:
Assembler
1
2
lea eax, LocalVar
push eax
Учитывая, что lea может определить адрес метки в "рантайме", все работает прекрасно.
________________________________________ ____
© Iczelion, пер. Aquila.
Вложения
Тип файла: zip tut09.zip (3.7 Кб, 149 просмотров)
5
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
11.01.2013, 11:50  [ТС]
Win32 API. Урок 10. Диалоговое окно как основное
Теперь время для действительно интересной темы, относящейся к GUI, о диалоговом окне. В этом туториале (и в следующем) мы научимся как использовать диалоговое окно в качестве основного.
Скачайте первый и второй примеры.
ТЕОРИЯ, МАТЬ СКЛЕРОЗА
Если вы изучили примеры в предыдущем туториалe достаточно подробно, вы заметили, что вы не могли перемещать фокус ввода от одного дочернего окна на другое, используя кнопку Tab. Вы могли сделать это только кликнув на нужном контроле, чтобы перевести на него фокус. Это довольно неудобно. Также вы могли заметить, что изменился цвет родительского окна на серый. Это было сделано для того, чтобы цвет дочерних окон не контрастировал с клиентской областью родительского окна. Есть путь, чтобы обойти эту проблему, но он не очень прост. Вы должны сабклассить все дочерние элементы управления в вашем родительском окне.
Причина того, почему возникают подобные неудобства состоят в том, что дочерние окна изначально проектировались для работы с диалоговым окном, а не с обычным. Цвет дочернего окна по умолчанию серый, так как это обычный цвет диалогового окна.
Прежде чем мы углубимся в детали, мы должны сначала узнать, что такое диалоговое окно. Диалоговое окно ? это не что иное, как обычное окно, которое спроектировано для работы с дочерними элементами управления.
Windows также предоставляет внутренний «менеджер диалоговых окон», который воплощает большую часть диалоговой логики, такую как перемещение фокуса ввода, когда юзеp нажимает Tab, нажатие кнопки по умолчанию, если нажатие на кнопку 'Enter, и так далее, так чтобы программисты могли заниматься более высокоуровневыми задачами. Поскольку диалоговое окно можно считать «черным ящиком» (это означает то, что вы не обязаны знать, как работает диалоговое окно, для того, чтобы использовать его), вы должно только знать, как с ним взаимодействовать. Это принцип объектно-ориентированного программирования, называемого скрытием информации. Если черный ящик спроектирован совершенно, пользователь может использовать его не зная, как он работает. Правда, загвоздка в том, что черный ящик должен быть совершенным, это труднодостижимо в реальном мире. Win32 API также спроектирован как черный ящик.
Ладно, похоже, что мы немного отклонились. Давайте вернемся к нашему сюжету. Диалоговые окна спроектированы так, чтобы снизить нагрузку на программиста. Обычно, если вы помещает дочерний элемент управления на обычное окно, вы должны сабклассить их и самостоятельно обрабатывать нажатия на клавиши.
Hо если вы помещаете их на диалоговое окно, оно обработает их за вас. Вы только должны как получать информацию, вводимую пользователем, или как посылать команды окну. Диалоговое окно определяется как ресурс (похожим образом, как и меню). Вы пишете шаблон диалогового окна, описывая характеристики диалогового окна и его элементов управления, а затем компилируете его с помощью редактора ресурсов.
Обратите внимание, что все ресурсы располагаются в одном скрипте ресурсов.
Вы можете использовать любой текстовый редактор, чтобы написать шаблон диалогового окна, но я бы не рекомендовал это. Вы должны использовать редактор ресурсов, чтобы сделать визуально расположить дочерние окна. Существует несколько прекрасных редакторов ресурсов. К большинству из основных компиляторов прилагаются подобные редакторы. Вы можете использовать их, чтобы создать скрипт ресурса. После этого стоит вырезать лишние строки, например, те, которые относятся к MFC.
Есть два основных вида диалоговых окон: модальные и независимые.
Независимые диалоговые окна дают вам возможность перемещать фокус ввода на другие окна. Пример ? диалоговое окно 'Find' в MS Word. Есть два подтипа модальных диалоговых окон: модальные к приложению и модальные к системе. Первые не дают вам переключаться на другое окно того же приложения, но вы можете переключиться на другое приложение. Вторые не дают вам возможности переключиться на любое другое окно.
Независимое диалоговое окно создается с помощью вызова функции CreateDialogParam. Модальное диалоговое окно создается вызовом DialogBoxParam. Единственное различие между диалоговым окном, модальным отношению к приложению, и диалоговым окном, модальным по отношению к системе, ? это стиль DS_SYSMODAL. Если вы включите стиль DS_SYSMODAL в шаблон диалогового окна, это диалоговое окно будет модальным к системе.
Вы можете взаимодействовать с любым дочерним элементом управления на диалоговом окне с помощью функции SendDlgItemMessage. Ее синтаксис следующий:
Кликните здесь для просмотра всего текста
Assembler
1
SendDlgItemMessage proto hwndDlg:DWORD,idControl:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
Эта API-функция неоценимо полезна при взаимодействии с дочерним окном.
Например, если вы хотите получить текст с элемента управления типа edit, вы можете сделать следующее:
Assembler
1
call SendDlgItemMessage,hDlg, ID_EDITBOX,WM_GETTEXT,256,ADDR text_buffer
Чтобы знать, какое сообщение когда посылать, вы должны проконсультироваться с вашим Win32 API-справочником.
Windows также предоставляет несколько специальных API-функций, заточенных под дочерние окна, для быстрого получения и установки нужных данных, например, GetDlgItemText, CheckDlgButton и т.д. Эти специальные функции создание, чтобы программисту не приходилось выяснять каждый раз значения wParam и lParam. Как правило, вы должны использовать данные функции, если хотите, чтобы управление кодом было легче. Используйте SendDlgItemMessage только, если нет соответствующей API-функции. Менеджер диалоговых окон посылает некоторые сообщения специальной callback-функции, называемой процедурой диалогового окна, которая имеет следующий формат:
Кликните здесь для просмотра всего текста
Assembler
1
 DlgProc proto hDlg:DWORD,iMsg:DWORD,wParam:DWORD,lParam:DWORD
Процедура диалогового окна очень похожа на процедуру окна, если не считать тип возвращаемого значения ? TRUE/FALSE, вместо обычных LRESULT. Внутренний менеджер диалоговых окон внутри Windows ? истинная процедура для диалоговых окон. Она вызывает нашу процедуру диалоговых окон, передавая некоторые из полученных сообщений. Поэтому главное правило следующее: если наша процедура диалогового окна обрабатывает сообщение, она должна вернуть TRUE в eax и если она не обрабатывает сообщение, тогда она должна вернуть в eax FALSE. Заметьте, что процедура диалогового окна не передает сообщения функции DefWindowProc, так как это не настоящая процедура окна.
Диалоговое окно можно использовать в двух целях. Вы можете использовать ее как основное окно или как вспомогательное для получения информации, вводимой пользователем. В этом туториале мы изучим первый вариант.
«Использование диалогового окна как основное окно» можно понимать двояко.
  • Вы можете использовать шаблон диалогового окна как шаблон класса, который вы регистрируете с помощью функции RegisterClassEx. В этом случае, диалоговое окно ведет себя как «нормальное»: оно получает сообщения через процедуру окна, на которую ссылается lрfnWndProc, а не через процедуру диалогового окна. Выгода данного подхода состоит в том, что вы не должны самостоятельно создавать дочерние элементы управления, Windows создает их во время создания диалогового окна. Также Windows берет на себя логику нажатий на клавиши (Tab и т.д.). Плюс вы можете указать курсор и иконку вашего окна в структуре класса окна.
  • Ваша программа создает диалоговое окно без создания родительского окна. Этот подход делает цикл сообщений ненужным, так как сообщения шлются напрямую процедуре диалогового окна. Вам даже не нужно регистрировать класс окна!
Похоже, что этот туториал будет довольно долгим.
ПРАКТИКА, СЕСТРА ШИЗОФРЕНИИ
текст файла dialog.asm
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
 .386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
 
.data
ClassName db "DLGCLASS",0
MenuName db "MyMenu",0
DlgName db "MyDialog",0
AppName db "Our First Dialog Box",0
TestString db "Wow! I'm in an edit box now",0
 
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
buffer db 512 dup(?)
 
.const
IDC_EDIT equ 3000
IDC_BUTTON equ 3001
IDC_EXIT equ 3002
IDM_GETTEXT equ 32000
IDM_CLEAR equ 32001
IDM_EXIT equ 32002
 
.code
start: invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
 
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hDlg:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,DLGWINDOWEXTRA
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_BTNFACE+1
mov wc.lpszMenuName,OFFSET MenuName
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
invoke CreateDialogParam,hInstance,ADDR DlgName,NULL,NULL,NULL
mov hDlg,eax
invoke ShowWindow, hDlg,SW_SHOWNORMAL
invoke UpdateWindow, hDlg
invoke GetDlgItem,hDlg,IDC_EDIT
invoke SetFocus,eax
 
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke IsDialogMessage, hDlg, ADDR msg
.IF eax ==FALSE
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDIF
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF lParam==0
.IF ax==IDM_GETTEXT
invoke GetDlgItemText,hWnd,IDC_EDIT,ADDR buffer,512
invoke MessageBox,NULL,ADDR buffer,ADDR AppName,MB_OK
.ELSEIF ax==IDM_CLEAR
invoke SetDlgItemText,hWnd,IDC_EDIT,NULL
.ELSE
invoke DestroyWindow,hWnd
.ENDIF
.ELSE
mov edx,wParam
shr edx,16
.IF dx==BN_CLICKED
.IF ax==IDC_BUTTON
invoke SetDlgItemText,hWnd,IDC_EDIT,ADDR TestString
.ELSEIF ax==IDC_EXIT
invoke SendMessage,hWnd,WM_COMMAND,IDM_EXIT,0
.ENDIF
.ENDIF
.ENDIF
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
Текст файла Dialog.rc
Кликните здесь для просмотра всего текста
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 #include "resource.h"
#define IDC_EDIT 3000
#define IDC_BUTTON 3001
#define IDC_EXIT 3002
#define IDM_GETTEXT 32000
#define IDM_CLEAR 32001
#define IDM_EXIT 32003
MyDialog DIALOG 10, 10, 205, 60
STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK
CAPTION "Our First Dialog Box"
CLASS "DLGCLASS"
BEGIN
EDITTEXT IDC_EDIT, 15,17,111,13, ES_AUTOHSCROLL | ES_LEFT
DEFPUSHBUTTON "Say Hello", IDC_BUTTON, 141,10,52,13
PUSHBUTTON "E&xit", IDC_EXIT, 141,26,52,13, WS_GROUP
END
 
MyMenu MENU
BEGIN
POPUP "Test Controls"
BEGIN
MENUITEM "Get Text", IDM_GETTEXT
MENUITEM "Clear Text", IDM_CLEAR
MENUITEM "", , 0x0800 /*MFT_SEPARATOR*/
MENUITEM "E&xit", IDM_EXIT
END
END
Разбор полетов
Давайте проанализируем первый пример.
Этот пример показывает, как зарегистрировать диалоговый шаблон как класс окна и создает «окно» из этого класса. Это упрощает вашу программу, так как вам не нужно создавать дочерние элементы управления самостоятельно.
Давайте проанализируем шаблон диалогового окна.
Кликните здесь для просмотра всего текста
C
1
 MyDialog DIALOG 10, 10, 205, 60
Объявление имя диалога, в данном случае ? «MyDialog», за которым следует ключевое слово «DIALOG». Следующие четыре числа ? это значения координат x, y, ширины и высоты диалогового окна в специальных единицах (не в пикселях).
C
1
2
 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK
Объявление стилей диалогового окна.
Кликните здесь для просмотра всего текста
Assembler
1
 CAPTION "Our First Dialog Box"
Это текст, который появится в заголовке окна.
Кликните здесь для просмотра всего текста
Assembler
1
 CLASS "DLGCLASS"
Это ключевая строка. Ключевое слово 'CLASS' позволяет нам использовать шаблон диалогового окна в качестве класса окна. Следующее слово ? это имя «класса окна».
Кликните здесь для просмотра всего текста
C
1
2
3
4
5
 BEGIN
EDITTEXT IDC_EDIT, 15,17,111,13, ES_AUTOHSCROLL | ES_LEFT
DEFPUSHBUTTON "Say Hello", IDC_BUTTON, 141,10,52,13
PUSHBUTTON "E&xit", IDC_EXIT, 141,26,52,13
END
Данный блок определяет дочерние элементы управления в диалоговом окне. Они определены между ключевыми словами BEGIN и END. Общий синтаксис таков:
Кликните здесь для просмотра всего текста
Assembler
1
 control-type "text" ,controlID, x, y, width, height [,styles]
типы элементов управления ? константы компилятора ресурсов, которые можно найти в руководстве по компилятору ресурсов.
Теперь мы углубляемся непосредственно в ассемблерный код. Интересующая нас часть находится в структуре класса окна.
Кликните здесь для просмотра всего текста
Assembler
1
2
 mov wc.cbWndExtra,DLGWINDOWEXTRA
mov wc.lpszClassName,OFFSET ClassName
Обычно этот параметр оставляется равным нулю, но если мы хотим зарегистрировать шаблон диалогового окна как класс окна, мы должны установить это параметр равным DLGWINDOWEXTRA. Заметьте, что имя класса должно совпадать с именем, что определено в шаблон диалогового окна. Остающиеся параметры инициализируются как обычно. После того, как вы заполните структуру класса окна, зарегистрируйте ее с помощью RegisterClassEx. Звучит знакомо. Точно также вы регистрируете обычный класс окна.
Кликните здесь для просмотра всего текста
Assembler
1
 invoke CreateDialogParam,hInstance,ADDR DlgName,NULL,NULL,NULL
После регистрации «класса окна», мы создаем наше диалоговое окно. В этом примере я создал его как независимое диалоговое окно функцией CreateDialogParam. Эта функция получает 5 параметров, но вам нужно заполнить только первые два: хэндл процесса и указатель на имя шаблона диалогового окна. Заметьте, что 2-ой параметр ? это не указатель на имя класса.
В этот момент, диалоговое окно и его дочерние элементы управления создаются Windows. Ваша процедура окна получит сообщение WM_CREATE как обычно.
Кликните здесь для просмотра всего текста
Assembler
1
2
 invoke GetDlgItem,hDlg,IDC_EDIT
invoke SetFocus,eax
После того, как диалоговое окно созданно, я хочу установить фокус ввода на edit control. Если я помещу соответвующий код в секцию WM_CREATE, вызов GetDlgItem провалится, так как дочерние окна еще не созданы. Единственный путь сделать это ? вызвать эту функцию после того, как диалоговое окно и все его дочерние окна будут созданы. Поэтому я помещаю данные две линии после вызова UpdateWindow. Функция GetDlgItem получает ID контрола и возвращает соответствующий хэндл окна. Так вы можете получить хэндл окна, если вы знаете его control ID.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
 invoke IsDialogMessage, hDlg, ADDR msg
.IF eax ==FALSE
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDIF
Программа входит в цикл сообщений и перед тем, как мы транслируем и передаем сообщения, мы вызываем функцию IsDialogMessage, чтобы позволить менеджеру диалоговых сообщений обрабатывать логику сообщений за нас. Если эта функция возвращает TRUE, это значит, что сообщение сделано для диалогового окна и обрабатывается менеджером диалоговых сообщений. Отметьте другое отличие от предыдущего туториала. Когда процедура окна хочет получить текст с edit контрола, она вызывает функцию GetDlgItemText, вместо функции GetWindowText. GetDlgItemText принимает ID контрола вместо хэндла окна. Это делает вызов проще в том случае, если вы используете диалоговое окно.
Теперь давайте перейдем ко второму подходу использования диалогового окна как основного окна. В следующем примере, я создам программно-модальное диалоговое окно. Вы не увидите цикл сообщений или процедуру окна, потому что они не нужны!
текст файла dialog.asm (вариант 2)
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
.386
.model flat,stdcall
option casemap:none
 
DlgProc proto :DWORD,:DWORD,:DWORD,:DWORD
 
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
 
.data
DlgName db "MyDialog",0
AppName db "Our Second Dialog Box",0
TestString db "Wow! I'm in an edit box now",0
 
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
buffer db 512 dup(?)
 
.const
IDC_EDIT equ 3000
IDC_BUTTON equ 3001
IDC_EXIT equ 3002
IDM_GETTEXT equ 32000
IDM_CLEAR equ 32001
IDM_EXIT equ 32002
 
.code
start: invoke GetModuleHandle, NULL
mov hInstance,eax
invoke DialogBoxParam, hInstance, ADDR DlgName,NULL, addr DlgProc, NULL
invoke ExitProcess,eax
 
DlgProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_INITDIALOG
invoke GetDlgItem, hWnd,IDC_EDIT
invoke SetFocus,eax
.ELSEIF uMsg==WM_CLOSE
invoke SendMessage,hWnd,WM_COMMAND,IDM_EXIT,0
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF lParam==0
.IF ax==IDM_GETTEXT
invoke GetDlgItemText,hWnd,IDC_EDIT,ADDR buffer,512
invoke MessageBox,NULL,ADDR buffer,ADDR AppName,MB_OK
.ELSEIF ax==IDM_CLEAR
invoke SetDlgItemText,hWnd,IDC_EDIT,NULL
.ELSEIF ax==IDM_EXIT
invoke EndDialog, hWnd,NULL
.ENDIF
.ELSE
mov edx,wParam
shr edx,16
.if dx==BN_CLICKED
.IF ax==IDC_BUTTON
invoke SetDlgItemText,hWnd,IDC_EDIT,ADDR TestString
.ELSEIF ax==IDC_EXIT
invoke SendMessage,hWnd,WM_COMMAND,IDM_EXIT,0
.ENDIF
.ENDIF
.ENDIF
.ELSE
mov eax,FALSE
ret
.ENDIF
mov eax,TRUE
ret
DlgProc endp
end start
Текст файла dialog.rc (вариант 2)
Кликните здесь для просмотра всего текста
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 #include "resource.h"
 
#define IDC_EDIT 3000
#define IDC_BUTTON 3001
#define IDC_EXIT 3002
#define IDR_MENU1 3003
#define IDM_GETTEXT 32000
#define IDM_CLEAR 32001
#define IDM_EXIT 32003
MyDialog DIALOG 10, 10, 205, 60
STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX |
WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK
CAPTION "Our Second Dialog Box"
 
MENU IDR_MENU1
BEGIN
EDITTEXT IDC_EDIT, 15,17,111,13, ES_AUTOHSCROLL | ES_LEFT
DEFPUSHBUTTON "Say Hello", IDC_BUTTON, 141,10,52,13
PUSHBUTTON "E&xit", IDC_EXIT, 141,26,52,13
END
 
IDR_MENU1 MENU
BEGIN
POPUP "&Test Controls";
BEGIN
MENUITEM "Get Text", IDM_GETTEXT
MENUITEM "Clear Text", IDM_CLEAR
MENUITEM "", , 0x0800 /*MFT_SEPARATOR*/
MENUITEM "E&xit", IDM_EXIT
END
END
Разбор полетов
Кликните здесь для просмотра всего текста
Assembler
1
 DlgProc proto :DWORD,:DWORD,:DWORD,:DWORD
Мы объявляем прототип функции для DlgProc, так что мы можем ссылаться на нее оператором addr:
Кликните здесь для просмотра всего текста
Assembler
1
 invoke DialogBoxParam, hInstance, ADDR DlgName,NULL, addr DlgProc, NULL
В вышеприведенной строке вызывается функция DialogBoxParam, которая получает 5 параметров: хэндл процесса, имя шаблона диалогового окна, хэндл родительского окна, адрес процедуры диалогового окна и специальные данные для диалогового окна. DialogBoxParam создает модальное диалоговое окно. Она не возвращается, пока диалоговое окно не будет уничтожено.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
 .IF uMsg==WM_INITDIALOG
invoke GetDlgItem, hWnd,IDC_EDIT
invoke SetFocus,eax
.ELSEIF uMsg==WM_CLOSE
invoke SendMessage,hWnd,WM_COMMAND,IDM_EXIT,0
Процедура диалогового окна выглядит как процедура окна, не считая того, что она не получает сообщение WM_CREATE. Первое сообщение, которое она получает ? это WM_INITDIALOG. Обычно вы помещаете здесь код инициализации. Заметьте, что вы должны вернуть в регистре eax значение TRUE, если вы обрабатываете это сообщение.
Внутренний менеджер диалогового окна не посылает нашей процедуре сообщение WM_DESTROY, а вот WM_CLOSE шлет. Поэтому если мы хотим отреагировать на то, что пользователь нажимает кнопку закрытия на нашем диалоговом окне, мы должны обработать сообщение WM_CLOSE. В нашем примере мы посылаем сообщение WM_CLOSE со значение IDM_EXIT в wParam. Это произведет тот же эффект, что и выбор пункта 'Exit' в меню. EndDialog вызывается в ответ на IDM_EXIT.
Обработка сообщений WM_COMMAND остается такой же.
Когда вы хотите уничтожить диалоговое окно, единственный путь ? это вызов функции EndDialog. Не пробуйте DestroyWindow! EndDialog не уничтожает диалоговое окно немедленно. Она только устанавливает флаг для внутреннего менеджера диалогового окна и продолжает выполнять следующие инструкции.
Теперь давайте изучим файл ресурсов. Заметное изменение ? это то, что вместо использования текстовой строки в качестве имени меню, мы используем значение IDR_MENU1. Это необходимо, если вы хотите прикрепить меню к диалоговому окну, созданному DialogBoxParam'ом. Заметьте, что в шаблоне диалогового окна вы должны добавить ключевое слово 'MENU', за которым будет следовать ID ресурса меню.
Различие между двумя примерами в этом туториале, которое вы можете легко заметить ? это отсутствие иконки в последнем примере. Тем не менее, вы можете установить иконку, послав сообщение WM_SETICON диалоговому окну во время обработки WM_INITDIALOG.
______________________________________
© Iczelion, пер. Aquila.
Вложения
Тип файла: zip tut10-1.zip (13.0 Кб, 150 просмотров)
Тип файла: zip tut10-2.zip (12.2 Кб, 138 просмотров)
3
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
12.01.2013, 12:57  [ТС]
Win32 API. Урок 10a. Комментарии kero к 10 и 11 урокам Iczelion'a
Кликните здесь для просмотра всего текста
В этом Уроке мы создадим полнофункциональную Windows программу, которое выводит сообщение — "Win32 assembly is great!".
Скачайте пример здесь.
ТЕОРИЯ, МАТЬ СКЛЕРОЗА
Windows предоставляет огромное количество ресурсов Windows-программам через Windows API (Application Programming Interface). Windows API — это большая коллекция очень полезных функций, располагающихся непосредственно в операционной системе и готовых для использования программами. Эти функции находятся в нескольких динамически подгружаемых библиотеках (DLLs), таких как kernel32.dll, user32.dll и gdi32.dll. Kernel32.dll содержит API-функции, взаимодействующие с памятью и управляющие процессами. User32.dll контролирует пользовательский интерфейс. Gdi32.dll ответственен за графические операции. Кроме этих трех "основных", существуют также другие dll, которые вы можете использовать, при условии, что вы обладаете достаточным количеством информации о нужных API-функциях. Windows программы динамически подсоединяется к этим библиотекам, то есть код API-функций не включается в исполняемый файл. Информация находится в библиотеках импорта. Вы должны слинковать ваши программы с правильными библиотеками импорта, иначе они не смогут найти эти функции. Когда Windows программа загружается в память, Windows читает информацию, сохраненную в программе. Эта информация включает имена функций, которые программа использует и DLL-ей, в которых эти функции располагаются. Когда Windows находит подобную информацию в программе, она вызывает библиотеки и исправляет в программе вызовы этих функций, так что контроль всегда будет передаваться по правильному адресу.
Существует две категории API функций: одни работают с ANSI-строками, а другие с Unicode-строками. Имена API-функций использующих ANSI-строки заканчиваются на "A", например, MessageBoxA. В конце имен функций для Unicode находится "W". Windows 95/98 от природы поддерживают ANSI, а Windows NT и производные от нее 2k/XP/Vista поддерживают Unicode. Обычно мы имеем дело с ANSI строками (массивы символов, оканчивающиеся NULL-ом. размер ANSI-символа — 1 байт. В то время как ANSI достаточна для европейских языков, она не поддерживает некоторые восточные языки, в которых есть несколько тысяч уникальных символов. Вот в этих случаях в дело вступает UniCode. размер символа UNICODE — 2 байта, и поэтому может поддерживать 65536 различных символов. Hо по большей части, вы будете использовать include-файл, который может определить и выбрать подходящую для вашей платформы функцию. Просто обращайтесь к именам API-функций без постфикса.
ПРАКТИКА, МАТЬ ШИЗОФРЕНИИ
Я приведу голый скелет программы ниже. Позже мы разберем его.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
.386
.model flat, stdcall
.data
.code
start:
end start
Выполнение начинается с первой инструкции, следующей за меткой, установленной после конца директив. В вышеприведенном каркасе выполнение начинается непосредственно после метки 'start'. Будут последовательно выполняться инструкция за инструкцией, пока не встретится операция передачи управления, такая как: jmр, jne, je, ret и так далее. Эти инструкции перенаправляют поток выполнения другим инструкциям. Когда программа выходит в Windows, ей следует вызвать API-функцию ExitProcess.
Assembler
1
ExitProcess proto uExitCode:DWORD
Строка выше называется прототипом функции. Прототип функции указывает ассемблеру/линкеру атрибуты функции, чтобы он сделал проверку типов данных и количество атрибутов передаваемых функции. Формат прототипа функции следующий:
Assembler
1
ИмяФункции PROTO [ИмяПараметра]:ТипДанных,[ИмяПараметра]:ТипДанных,...
Короче говоря, за именем функции следует ключевое слово PROTO, а затем список переменных с типом данных, разделенных запятыми. В приведенном выше примере с ExitProcess, эта функция была определена как принимающая только один параметр типа DWORD. Прототипы функций очень полезны, когда вы используете высокоуровневый синтаксический вызов — invoke. Вы можете считать об invoke как обычный вызов с проверкой типов данных. Например, если вы напишите:
Assembler
1
call ExitProcess
Линкер уведомит вас, что вы забыли положит в стек двойное слово. Я рекомендую вам использовать invoke вместо простого вызова. Синтаксис invoke следующий:
Assembler
1
invoke выражение [, аргументы]
Выражение может быть именем функции или указателем на функцию. Параметры функции разделены запятыми.
Большинство прототипов для API-функций содержатся в include-файлах. Если вы используете hutch'евский MASM32, они будут находится в директории MASM32/INCLUDE. Файлы подключения имеют расширение .inc и прототипы функций DLL находятся в .inc файле с таким же именем, как и у этой DLL.
Hапример, ExitProcess экспортируется из kernel32.lib, так что прототип ExitProcess находится в kernel32.inc.

Вы также можете создать прототипы для ваших собственных функций. Во всех моих экземплярах я использую hutch'евский windows.inc, который вы можете скачать с http://win32asm.cjb.net
Возвращаясь к ExitProcess: параметр uExitCode - это значение, которое программа вернет Windows после окончания программы. Вы можете вызвать функцию ExitProcess так:
Assembler
1
invoke ExitProcess, 0
Поместив эту строку непосредственно после стартовой метки, вы получите Win32-программу, немедленно выходящую в Windows, но тем не менее полнофункциональную.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.data
.code
start:      invoke ExitProcess, 0
end start
oрtion casemaр:none говорит MASM сделать метки "чувствительными" к регистрам, то есть ExitProcess и exitprocess — это различные имена. Отметьте новую директиву — include. После нее следует имя файла, который вы хотите вставить в то место, где эта директива располагается. В примере выше, когда MASM обрабатывает линию include \masm32\include\windows.inc, он открывает windows.inc, находящийся в директории \MASM32\INCLUDE, и далее анализирует содержимое windows.inc так, как будто вы "вклеили" подключаемый файл. Хатчевский windows.inc содержит в себе определения констант и структур, которые вам могут понадобиться для программирования под Win32. Этот файл не содержит в себе прототипов функций. Windows.inc ни в коем случае не является исчерпывающим и всеобъемлющим. Hutch и я пытаемся заполнить его как можно большим количеством констант и структур, но есть еще довольно, что следовало бы включить. Он постоянно обновляется. Заходите на хатчевскую и мою странички за свежими апдейтами. Из windows.inc, ваша программа будет брать определения констант и структур. Что касается прототипов функций, вы должны подключить другие include-файлы. Они находятся в директории \masm32\include.

В вышеприведенном примере, мы вызываем функцию, экспортированную из kernel32.dll, для чего мы должны подключить прототипы функций из kernel32.dll. Этот файл - kernel32.inc. Если вы открываете его текстовым редактором, вы увидите, что он состоит из прототипов функций из соответствующей dll. Если вы не подключите kernel32.inc, вы все еще можете вызвать ExitProcess, но уже с помощью ассемблерной команды call. Вы не сможете вызвать эту функцию с помощью invoke. Дело вот в чем: для того, чтобы вызвать функцию через invoke, вы должны поместить в исходном коде ее прототип. В примере выше, если вы не подключите kernel32.inc, вы можете определить прототип для ExitProcess где-нибудь до вызова этой функции и это будет работать. Файлы подключения нужны для того, что избавить вас от лишней работы и вам не пришлось набирать все прототипы самим.
Теперь мы встречаем новую директиву — includelib. Она работает не так, как include. Это всего лишь способ сказать ассемблеру какие библиотеки использует ваша программа должна прилинковать. Хотя вы вовсе не обязаны использовать именно этот метод. Вы можете указать имена библиотек импорта к командной строке при запуске линкера, но поверьте мне, это весьма скучно и утомительно, да и командная строка может вместить максимум 128 символов.
Теперь возьмите весь исходный текст примера этого Урока, сохраните его как msgbox.asm и ассемблируйте его так:
Code
1
ml /c /coff /Cp msgbox.asm
/c говорит MASM'у создать .obj файл в формате COFF. MASM использует вариант COFF (Common Object File Format), использующийся под Unix, как его собственный объектный и исполняемый формат файлов.
/Cр говорит MASM'у сохранять регистр имен, заданных пользователем. Если вы используете hutch'евский MASM32 пакет, вы можете вставить "option casemaр:none" в начале вашего исходника, сразу после директивы .model, чтобы добиться того же эффекта.
После успешной компиляции msgbox.asm, вы получите msgbox.obj. Это объектный файл, от которого один шаг до екзешника. Obj содержит инструкции/данные в двоичной форме. Отсутствуют только необходимая корректировка адресов, которая проводится линкером.
Теперь сделайте следующее:
Code
1
link /SUBSYSTEM:WINDOWS  /LIBPATH:c:\masm32\lib  msgbox.obj
/SUBSYSTEM:WINDOWS информирует линкер о том, какого вида является будущий исполняемый модуль.
/LIBPATH:<путь к библиотекам импорта> говорит линкеру, где находятся библиотеки импорта. Если вы используете MASM32, они будут в MASM32\lib.

Линкер читает объектный файл и корректирует его, используя адреса, взятые из библиотек импорта. После окончания линковки вы получите файл msgbox.exe. Запустите его. Вы увидите, что она ничего не делает.

Да, мы не поместили в код ничего не интересного. Hо тем не менее полноценная Windows программа. И посмотрите на размер! Hа моем PC — 1.536 байт.
Теперь мы готовы создать окно с сообщением. Прототип функции, которая нам для этого необходима следующая:
Assembler
1
MessageBox PROTO hwnd:DWORD, lpText:DWORD, lpCaption:DWORD, uType:DWORD
hwnd — это хэндл родительского окна. Вы можете считать хэндл числом, представляющим окно, к которому вы обращаетесь. Его значение для вас не важно. Вы только должны знать, что оно представляет окно. Когда вы захотите сделать что-нибудь с окном, вы должны обратиться к нему, используя его хэндл.
lрText — это указатель на текст, который вы хотите отобразить в клиентской части окна сообщения. Указатель ― это адрес чего-либо. Указатель на текстовую строку = адрес этой строки.
lpCaption — это указатель на заголовок окна сообщения.
uType — устанавливает иконку, число и вид кнопок окна.

Давайте изменим msgbox.asm для отображения сообщения.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
.data
MsgBoxCaption  db "Iczelion Tutorial #2",0
MsgBoxText     db "Win32 Assembly is Great!",0
.code
start: invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK
invoke ExitProcess, NULL
end start
Скомпилируйте и запустите. Вы увидите окошко с сообщением "Win32 Assembly is great!".
Давайте снова взглянем на исходник.
Мы определили две оканчивающиеся NULL'ом строки в секции .data. Помните, что каждая ANSI строка в Windows должна оканчиваться NULL'ом (0 в шестнадцатеричной системе). Мы используем две константы, NULL и MB_OK. Эти константы прописаны в windows.inc, так что вы можете обратиться к ним, указав их имя, а не значение. Это улучшает читабельность кода.
Оператор addr используется для передачи адреса метки (и не только) функции. Он действителен только в контексте директивы invoke. Вы не можете использовать его, чтобы присвоить адрес метки регистру или переменной, например. В данном примере вы можете использовать offset вместо addr. Тем не менее, есть некоторые различия между ними.
1. addr не может быть использован с метками, которые определены впереди, а offset может. Например, если метка определена где-то дальше в коде, чем строка с invoke, addr не будет работать.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
invoke MessageBox,NULL, addr MsgBoxText,addr MsgBoxCaption,MB_OK
......
MsgBoxCaption  db "Iczelion Tutorial #2",0
MsgBoxText       db "Win32 Assembly is Great!",0
MASM доложит об ошибке. Если вы используете offset вместо addr, MASM без проблем скомпилирует указанный отрывок кода.
2. Addr поддерживает локальные переменные, в то время как offset нет.
Локальная переменная — это всего лишь зарезервированное место в стеке. Вы только знаете его адрес во время выполнения программы. Offset интерпретируется во время компиляции ассемблером, поэтому неудивительно, что он не поддерживает локальные переменные. Addr же работает с ними, потому что ассемблер сначала проверяет ― глобальная переменная или локальная. Если она глобальная, он помещает адрес этой переменной в объектный файл. В этом случае оператор работает как offset. Если это локальная переменная, компилятор генерирует следующую последовательность инструкций, перед тем как будет вызвана функция:
Assembler
1
2
lea eax, LocalVar
push eax
Учитывая, что lea может определить адрес метки в "рантайме", все работает прекрасно.
________________________________________ ____
© Iczelion, пер. Aquila.
Миниатюры
Сам себе Iczelion   Сам себе Iczelion   Сам себе Iczelion  

Сам себе Iczelion  
Изображения
 
Вложения
Тип файла: rar cbt_hook.rar (9.2 Кб, 108 просмотров)
Тип файла: rar csrss_msgbox.rar (1.2 Кб, 105 просмотров)
Тип файла: rar main_variants.rar (13.9 Кб, 106 просмотров)
Тип файла: rar mb_ghost.rar (2.7 Кб, 105 просмотров)
Тип файла: rar HideDialogBox.rar (2.1 Кб, 103 просмотров)
Тип файла: rar nondialog.rar (1.8 Кб, 105 просмотров)
Тип файла: zip systimages.zip (4.2 Кб, 106 просмотров)
Тип файла: zip tut11-1-k.zip (3.4 Кб, 113 просмотров)
Тип файла: rar layoutrtl.rar (1.7 Кб, 115 просмотров)
4
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
12.01.2013, 14:58  [ТС]
Win32 API. Урок 10b. Создания диалогового окна через CreateDialogParam и DlgProc и WndProc
Кликните здесь для просмотра всего текста
В этом Уроке мы создадим полнофункциональную Windows программу, которое выводит сообщение — "Win32 assembly is great!".
Скачайте пример здесь.
ТЕОРИЯ, МАТЬ СКЛЕРОЗА
Windows предоставляет огромное количество ресурсов Windows-программам через Windows API (Application Programming Interface). Windows API — это большая коллекция очень полезных функций, располагающихся непосредственно в операционной системе и готовых для использования программами. Эти функции находятся в нескольких динамически подгружаемых библиотеках (DLLs), таких как kernel32.dll, user32.dll и gdi32.dll. Kernel32.dll содержит API-функции, взаимодействующие с памятью и управляющие процессами. User32.dll контролирует пользовательский интерфейс. Gdi32.dll ответственен за графические операции. Кроме этих трех "основных", существуют также другие dll, которые вы можете использовать, при условии, что вы обладаете достаточным количеством информации о нужных API-функциях. Windows программы динамически подсоединяется к этим библиотекам, то есть код API-функций не включается в исполняемый файл. Информация находится в библиотеках импорта. Вы должны слинковать ваши программы с правильными библиотеками импорта, иначе они не смогут найти эти функции. Когда Windows программа загружается в память, Windows читает информацию, сохраненную в программе. Эта информация включает имена функций, которые программа использует и DLL-ей, в которых эти функции располагаются. Когда Windows находит подобную информацию в программе, она вызывает библиотеки и исправляет в программе вызовы этих функций, так что контроль всегда будет передаваться по правильному адресу.
Существует две категории API функций: одни работают с ANSI-строками, а другие с Unicode-строками. Имена API-функций использующих ANSI-строки заканчиваются на "A", например, MessageBoxA. В конце имен функций для Unicode находится "W". Windows 95/98 от природы поддерживают ANSI, а Windows NT и производные от нее 2k/XP/Vista поддерживают Unicode. Обычно мы имеем дело с ANSI строками (массивы символов, оканчивающиеся NULL-ом. размер ANSI-символа — 1 байт. В то время как ANSI достаточна для европейских языков, она не поддерживает некоторые восточные языки, в которых есть несколько тысяч уникальных символов. Вот в этих случаях в дело вступает UniCode. размер символа UNICODE — 2 байта, и поэтому может поддерживать 65536 различных символов. Hо по большей части, вы будете использовать include-файл, который может определить и выбрать подходящую для вашей платформы функцию. Просто обращайтесь к именам API-функций без постфикса.
ПРАКТИКА, МАТЬ ШИЗОФРЕНИИ
Я приведу голый скелет программы ниже. Позже мы разберем его.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
.386
.model flat, stdcall
.data
.code
start:
end start
Выполнение начинается с первой инструкции, следующей за меткой, установленной после конца директив. В вышеприведенном каркасе выполнение начинается непосредственно после метки 'start'. Будут последовательно выполняться инструкция за инструкцией, пока не встретится операция передачи управления, такая как: jmр, jne, je, ret и так далее. Эти инструкции перенаправляют поток выполнения другим инструкциям. Когда программа выходит в Windows, ей следует вызвать API-функцию ExitProcess.
Assembler
1
ExitProcess proto uExitCode:DWORD
Строка выше называется прототипом функции. Прототип функции указывает ассемблеру/линкеру атрибуты функции, чтобы он сделал проверку типов данных и количество атрибутов передаваемых функции. Формат прототипа функции следующий:
Assembler
1
ИмяФункции PROTO [ИмяПараметра]:ТипДанных,[ИмяПараметра]:ТипДанных,...
Короче говоря, за именем функции следует ключевое слово PROTO, а затем список переменных с типом данных, разделенных запятыми. В приведенном выше примере с ExitProcess, эта функция была определена как принимающая только один параметр типа DWORD. Прототипы функций очень полезны, когда вы используете высокоуровневый синтаксический вызов — invoke. Вы можете считать об invoke как обычный вызов с проверкой типов данных. Например, если вы напишите:
Assembler
1
call ExitProcess
Линкер уведомит вас, что вы забыли положит в стек двойное слово. Я рекомендую вам использовать invoke вместо простого вызова. Синтаксис invoke следующий:
Assembler
1
invoke выражение [, аргументы]
Выражение может быть именем функции или указателем на функцию. Параметры функции разделены запятыми.
Большинство прототипов для API-функций содержатся в include-файлах. Если вы используете hutch'евский MASM32, они будут находится в директории MASM32/INCLUDE. Файлы подключения имеют расширение .inc и прототипы функций DLL находятся в .inc файле с таким же именем, как и у этой DLL.
Hапример, ExitProcess экспортируется из kernel32.lib, так что прототип ExitProcess находится в kernel32.inc.

Вы также можете создать прототипы для ваших собственных функций. Во всех моих экземплярах я использую hutch'евский windows.inc, который вы можете скачать с http://win32asm.cjb.net
Возвращаясь к ExitProcess: параметр uExitCode - это значение, которое программа вернет Windows после окончания программы. Вы можете вызвать функцию ExitProcess так:
Assembler
1
invoke ExitProcess, 0
Поместив эту строку непосредственно после стартовой метки, вы получите Win32-программу, немедленно выходящую в Windows, но тем не менее полнофункциональную.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.data
.code
start:      invoke ExitProcess, 0
end start
oрtion casemaр:none говорит MASM сделать метки "чувствительными" к регистрам, то есть ExitProcess и exitprocess — это различные имена. Отметьте новую директиву — include. После нее следует имя файла, который вы хотите вставить в то место, где эта директива располагается. В примере выше, когда MASM обрабатывает линию include \masm32\include\windows.inc, он открывает windows.inc, находящийся в директории \MASM32\INCLUDE, и далее анализирует содержимое windows.inc так, как будто вы "вклеили" подключаемый файл. Хатчевский windows.inc содержит в себе определения констант и структур, которые вам могут понадобиться для программирования под Win32. Этот файл не содержит в себе прототипов функций. Windows.inc ни в коем случае не является исчерпывающим и всеобъемлющим. Hutch и я пытаемся заполнить его как можно большим количеством констант и структур, но есть еще довольно, что следовало бы включить. Он постоянно обновляется. Заходите на хатчевскую и мою странички за свежими апдейтами. Из windows.inc, ваша программа будет брать определения констант и структур. Что касается прототипов функций, вы должны подключить другие include-файлы. Они находятся в директории \masm32\include.

В вышеприведенном примере, мы вызываем функцию, экспортированную из kernel32.dll, для чего мы должны подключить прототипы функций из kernel32.dll. Этот файл - kernel32.inc. Если вы открываете его текстовым редактором, вы увидите, что он состоит из прототипов функций из соответствующей dll. Если вы не подключите kernel32.inc, вы все еще можете вызвать ExitProcess, но уже с помощью ассемблерной команды call. Вы не сможете вызвать эту функцию с помощью invoke. Дело вот в чем: для того, чтобы вызвать функцию через invoke, вы должны поместить в исходном коде ее прототип. В примере выше, если вы не подключите kernel32.inc, вы можете определить прототип для ExitProcess где-нибудь до вызова этой функции и это будет работать. Файлы подключения нужны для того, что избавить вас от лишней работы и вам не пришлось набирать все прототипы самим.
Теперь мы встречаем новую директиву — includelib. Она работает не так, как include. Это всего лишь способ сказать ассемблеру какие библиотеки использует ваша программа должна прилинковать. Хотя вы вовсе не обязаны использовать именно этот метод. Вы можете указать имена библиотек импорта к командной строке при запуске линкера, но поверьте мне, это весьма скучно и утомительно, да и командная строка может вместить максимум 128 символов.
Теперь возьмите весь исходный текст примера этого Урока, сохраните его как msgbox.asm и ассемблируйте его так:
Code
1
ml /c /coff /Cp msgbox.asm
/c говорит MASM'у создать .obj файл в формате COFF. MASM использует вариант COFF (Common Object File Format), использующийся под Unix, как его собственный объектный и исполняемый формат файлов.
/Cр говорит MASM'у сохранять регистр имен, заданных пользователем. Если вы используете hutch'евский MASM32 пакет, вы можете вставить "option casemaр:none" в начале вашего исходника, сразу после директивы .model, чтобы добиться того же эффекта.
После успешной компиляции msgbox.asm, вы получите msgbox.obj. Это объектный файл, от которого один шаг до екзешника. Obj содержит инструкции/данные в двоичной форме. Отсутствуют только необходимая корректировка адресов, которая проводится линкером.
Теперь сделайте следующее:
Code
1
link /SUBSYSTEM:WINDOWS  /LIBPATH:c:\masm32\lib  msgbox.obj
/SUBSYSTEM:WINDOWS информирует линкер о том, какого вида является будущий исполняемый модуль.
/LIBPATH:<путь к библиотекам импорта> говорит линкеру, где находятся библиотеки импорта. Если вы используете MASM32, они будут в MASM32\lib.

Линкер читает объектный файл и корректирует его, используя адреса, взятые из библиотек импорта. После окончания линковки вы получите файл msgbox.exe. Запустите его. Вы увидите, что она ничего не делает.

Да, мы не поместили в код ничего не интересного. Hо тем не менее полноценная Windows программа. И посмотрите на размер! Hа моем PC — 1.536 байт.
Теперь мы готовы создать окно с сообщением. Прототип функции, которая нам для этого необходима следующая:
Assembler
1
MessageBox PROTO hwnd:DWORD, lpText:DWORD, lpCaption:DWORD, uType:DWORD
hwnd — это хэндл родительского окна. Вы можете считать хэндл числом, представляющим окно, к которому вы обращаетесь. Его значение для вас не важно. Вы только должны знать, что оно представляет окно. Когда вы захотите сделать что-нибудь с окном, вы должны обратиться к нему, используя его хэндл.
lрText — это указатель на текст, который вы хотите отобразить в клиентской части окна сообщения. Указатель ― это адрес чего-либо. Указатель на текстовую строку = адрес этой строки.
lpCaption — это указатель на заголовок окна сообщения.
uType — устанавливает иконку, число и вид кнопок окна.

Давайте изменим msgbox.asm для отображения сообщения.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
.data
MsgBoxCaption  db "Iczelion Tutorial #2",0
MsgBoxText     db "Win32 Assembly is Great!",0
.code
start: invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK
invoke ExitProcess, NULL
end start
Скомпилируйте и запустите. Вы увидите окошко с сообщением "Win32 Assembly is great!".
Давайте снова взглянем на исходник.
Мы определили две оканчивающиеся NULL'ом строки в секции .data. Помните, что каждая ANSI строка в Windows должна оканчиваться NULL'ом (0 в шестнадцатеричной системе). Мы используем две константы, NULL и MB_OK. Эти константы прописаны в windows.inc, так что вы можете обратиться к ним, указав их имя, а не значение. Это улучшает читабельность кода.
Оператор addr используется для передачи адреса метки (и не только) функции. Он действителен только в контексте директивы invoke. Вы не можете использовать его, чтобы присвоить адрес метки регистру или переменной, например. В данном примере вы можете использовать offset вместо addr. Тем не менее, есть некоторые различия между ними.
1. addr не может быть использован с метками, которые определены впереди, а offset может. Например, если метка определена где-то дальше в коде, чем строка с invoke, addr не будет работать.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
invoke MessageBox,NULL, addr MsgBoxText,addr MsgBoxCaption,MB_OK
......
MsgBoxCaption  db "Iczelion Tutorial #2",0
MsgBoxText       db "Win32 Assembly is Great!",0
MASM доложит об ошибке. Если вы используете offset вместо addr, MASM без проблем скомпилирует указанный отрывок кода.
2. Addr поддерживает локальные переменные, в то время как offset нет.
Локальная переменная — это всего лишь зарезервированное место в стеке. Вы только знаете его адрес во время выполнения программы. Offset интерпретируется во время компиляции ассемблером, поэтому неудивительно, что он не поддерживает локальные переменные. Addr же работает с ними, потому что ассемблер сначала проверяет ― глобальная переменная или локальная. Если она глобальная, он помещает адрес этой переменной в объектный файл. В этом случае оператор работает как offset. Если это локальная переменная, компилятор генерирует следующую последовательность инструкций, перед тем как будет вызвана функция:
Assembler
1
2
lea eax, LocalVar
push eax
Учитывая, что lea может определить адрес метки в "рантайме", все работает прекрасно.
________________________________________ ____
© Iczelion, пер. Aquila.
Вложения
Тип файла: zip tut10a.zip (4.2 Кб, 120 просмотров)
Тип файла: zip tut10b.zip (4.4 Кб, 113 просмотров)
4
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
14.01.2013, 05:10  [ТС]
Win32 API. Урок 10d. Создания диалогового окна через CreateDialogParam и WndProc и RegisterClassEx

Кликните здесь для просмотра всего текста
В этом Уроке мы создадим полнофункциональную Windows программу, которое выводит сообщение — "Win32 assembly is great!".
Скачайте пример здесь.
ТЕОРИЯ, МАТЬ СКЛЕРОЗА
Windows предоставляет огромное количество ресурсов Windows-программам через Windows API (Application Programming Interface). Windows API — это большая коллекция очень полезных функций, располагающихся непосредственно в операционной системе и готовых для использования программами. Эти функции находятся в нескольких динамически подгружаемых библиотеках (DLLs), таких как kernel32.dll, user32.dll и gdi32.dll. Kernel32.dll содержит API-функции, взаимодействующие с памятью и управляющие процессами. User32.dll контролирует пользовательский интерфейс. Gdi32.dll ответственен за графические операции. Кроме этих трех "основных", существуют также другие dll, которые вы можете использовать, при условии, что вы обладаете достаточным количеством информации о нужных API-функциях. Windows программы динамически подсоединяется к этим библиотекам, то есть код API-функций не включается в исполняемый файл. Информация находится в библиотеках импорта. Вы должны слинковать ваши программы с правильными библиотеками импорта, иначе они не смогут найти эти функции. Когда Windows программа загружается в память, Windows читает информацию, сохраненную в программе. Эта информация включает имена функций, которые программа использует и DLL-ей, в которых эти функции располагаются. Когда Windows находит подобную информацию в программе, она вызывает библиотеки и исправляет в программе вызовы этих функций, так что контроль всегда будет передаваться по правильному адресу.
Существует две категории API функций: одни работают с ANSI-строками, а другие с Unicode-строками. Имена API-функций использующих ANSI-строки заканчиваются на "A", например, MessageBoxA. В конце имен функций для Unicode находится "W". Windows 95/98 от природы поддерживают ANSI, а Windows NT и производные от нее 2k/XP/Vista поддерживают Unicode. Обычно мы имеем дело с ANSI строками (массивы символов, оканчивающиеся NULL-ом. размер ANSI-символа — 1 байт. В то время как ANSI достаточна для европейских языков, она не поддерживает некоторые восточные языки, в которых есть несколько тысяч уникальных символов. Вот в этих случаях в дело вступает UniCode. размер символа UNICODE — 2 байта, и поэтому может поддерживать 65536 различных символов. Hо по большей части, вы будете использовать include-файл, который может определить и выбрать подходящую для вашей платформы функцию. Просто обращайтесь к именам API-функций без постфикса.
ПРАКТИКА, МАТЬ ШИЗОФРЕНИИ
Я приведу голый скелет программы ниже. Позже мы разберем его.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
.386
.model flat, stdcall
.data
.code
start:
end start
Выполнение начинается с первой инструкции, следующей за меткой, установленной после конца директив. В вышеприведенном каркасе выполнение начинается непосредственно после метки 'start'. Будут последовательно выполняться инструкция за инструкцией, пока не встретится операция передачи управления, такая как: jmр, jne, je, ret и так далее. Эти инструкции перенаправляют поток выполнения другим инструкциям. Когда программа выходит в Windows, ей следует вызвать API-функцию ExitProcess.
Assembler
1
ExitProcess proto uExitCode:DWORD
Строка выше называется прототипом функции. Прототип функции указывает ассемблеру/линкеру атрибуты функции, чтобы он сделал проверку типов данных и количество атрибутов передаваемых функции. Формат прототипа функции следующий:
Assembler
1
ИмяФункции PROTO [ИмяПараметра]:ТипДанных,[ИмяПараметра]:ТипДанных,...
Короче говоря, за именем функции следует ключевое слово PROTO, а затем список переменных с типом данных, разделенных запятыми. В приведенном выше примере с ExitProcess, эта функция была определена как принимающая только один параметр типа DWORD. Прототипы функций очень полезны, когда вы используете высокоуровневый синтаксический вызов — invoke. Вы можете считать об invoke как обычный вызов с проверкой типов данных. Например, если вы напишите:
Assembler
1
call ExitProcess
Линкер уведомит вас, что вы забыли положит в стек двойное слово. Я рекомендую вам использовать invoke вместо простого вызова. Синтаксис invoke следующий:
Assembler
1
invoke выражение [, аргументы]
Выражение может быть именем функции или указателем на функцию. Параметры функции разделены запятыми.
Большинство прототипов для API-функций содержатся в include-файлах. Если вы используете hutch'евский MASM32, они будут находится в директории MASM32/INCLUDE. Файлы подключения имеют расширение .inc и прототипы функций DLL находятся в .inc файле с таким же именем, как и у этой DLL.
Hапример, ExitProcess экспортируется из kernel32.lib, так что прототип ExitProcess находится в kernel32.inc.

Вы также можете создать прототипы для ваших собственных функций. Во всех моих экземплярах я использую hutch'евский windows.inc, который вы можете скачать с http://win32asm.cjb.net
Возвращаясь к ExitProcess: параметр uExitCode - это значение, которое программа вернет Windows после окончания программы. Вы можете вызвать функцию ExitProcess так:
Assembler
1
invoke ExitProcess, 0
Поместив эту строку непосредственно после стартовой метки, вы получите Win32-программу, немедленно выходящую в Windows, но тем не менее полнофункциональную.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.data
.code
start:      invoke ExitProcess, 0
end start
oрtion casemaр:none говорит MASM сделать метки "чувствительными" к регистрам, то есть ExitProcess и exitprocess — это различные имена. Отметьте новую директиву — include. После нее следует имя файла, который вы хотите вставить в то место, где эта директива располагается. В примере выше, когда MASM обрабатывает линию include \masm32\include\windows.inc, он открывает windows.inc, находящийся в директории \MASM32\INCLUDE, и далее анализирует содержимое windows.inc так, как будто вы "вклеили" подключаемый файл. Хатчевский windows.inc содержит в себе определения констант и структур, которые вам могут понадобиться для программирования под Win32. Этот файл не содержит в себе прототипов функций. Windows.inc ни в коем случае не является исчерпывающим и всеобъемлющим. Hutch и я пытаемся заполнить его как можно большим количеством констант и структур, но есть еще довольно, что следовало бы включить. Он постоянно обновляется. Заходите на хатчевскую и мою странички за свежими апдейтами. Из windows.inc, ваша программа будет брать определения констант и структур. Что касается прототипов функций, вы должны подключить другие include-файлы. Они находятся в директории \masm32\include.

В вышеприведенном примере, мы вызываем функцию, экспортированную из kernel32.dll, для чего мы должны подключить прототипы функций из kernel32.dll. Этот файл - kernel32.inc. Если вы открываете его текстовым редактором, вы увидите, что он состоит из прототипов функций из соответствующей dll. Если вы не подключите kernel32.inc, вы все еще можете вызвать ExitProcess, но уже с помощью ассемблерной команды call. Вы не сможете вызвать эту функцию с помощью invoke. Дело вот в чем: для того, чтобы вызвать функцию через invoke, вы должны поместить в исходном коде ее прототип. В примере выше, если вы не подключите kernel32.inc, вы можете определить прототип для ExitProcess где-нибудь до вызова этой функции и это будет работать. Файлы подключения нужны для того, что избавить вас от лишней работы и вам не пришлось набирать все прототипы самим.
Теперь мы встречаем новую директиву — includelib. Она работает не так, как include. Это всего лишь способ сказать ассемблеру какие библиотеки использует ваша программа должна прилинковать. Хотя вы вовсе не обязаны использовать именно этот метод. Вы можете указать имена библиотек импорта к командной строке при запуске линкера, но поверьте мне, это весьма скучно и утомительно, да и командная строка может вместить максимум 128 символов.
Теперь возьмите весь исходный текст примера этого Урока, сохраните его как msgbox.asm и ассемблируйте его так:
Code
1
ml /c /coff /Cp msgbox.asm
/c говорит MASM'у создать .obj файл в формате COFF. MASM использует вариант COFF (Common Object File Format), использующийся под Unix, как его собственный объектный и исполняемый формат файлов.
/Cр говорит MASM'у сохранять регистр имен, заданных пользователем. Если вы используете hutch'евский MASM32 пакет, вы можете вставить "option casemaр:none" в начале вашего исходника, сразу после директивы .model, чтобы добиться того же эффекта.
После успешной компиляции msgbox.asm, вы получите msgbox.obj. Это объектный файл, от которого один шаг до екзешника. Obj содержит инструкции/данные в двоичной форме. Отсутствуют только необходимая корректировка адресов, которая проводится линкером.
Теперь сделайте следующее:
Code
1
link /SUBSYSTEM:WINDOWS  /LIBPATH:c:\masm32\lib  msgbox.obj
/SUBSYSTEM:WINDOWS информирует линкер о том, какого вида является будущий исполняемый модуль.
/LIBPATH:<путь к библиотекам импорта> говорит линкеру, где находятся библиотеки импорта. Если вы используете MASM32, они будут в MASM32\lib.

Линкер читает объектный файл и корректирует его, используя адреса, взятые из библиотек импорта. После окончания линковки вы получите файл msgbox.exe. Запустите его. Вы увидите, что она ничего не делает.

Да, мы не поместили в код ничего не интересного. Hо тем не менее полноценная Windows программа. И посмотрите на размер! Hа моем PC — 1.536 байт.
Теперь мы готовы создать окно с сообщением. Прототип функции, которая нам для этого необходима следующая:
Assembler
1
MessageBox PROTO hwnd:DWORD, lpText:DWORD, lpCaption:DWORD, uType:DWORD
hwnd — это хэндл родительского окна. Вы можете считать хэндл числом, представляющим окно, к которому вы обращаетесь. Его значение для вас не важно. Вы только должны знать, что оно представляет окно. Когда вы захотите сделать что-нибудь с окном, вы должны обратиться к нему, используя его хэндл.
lрText — это указатель на текст, который вы хотите отобразить в клиентской части окна сообщения. Указатель ― это адрес чего-либо. Указатель на текстовую строку = адрес этой строки.
lpCaption — это указатель на заголовок окна сообщения.
uType — устанавливает иконку, число и вид кнопок окна.

Давайте изменим msgbox.asm для отображения сообщения.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
.data
MsgBoxCaption  db "Iczelion Tutorial #2",0
MsgBoxText     db "Win32 Assembly is Great!",0
.code
start: invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK
invoke ExitProcess, NULL
end start
Скомпилируйте и запустите. Вы увидите окошко с сообщением "Win32 Assembly is great!".
Давайте снова взглянем на исходник.
Мы определили две оканчивающиеся NULL'ом строки в секции .data. Помните, что каждая ANSI строка в Windows должна оканчиваться NULL'ом (0 в шестнадцатеричной системе). Мы используем две константы, NULL и MB_OK. Эти константы прописаны в windows.inc, так что вы можете обратиться к ним, указав их имя, а не значение. Это улучшает читабельность кода.
Оператор addr используется для передачи адреса метки (и не только) функции. Он действителен только в контексте директивы invoke. Вы не можете использовать его, чтобы присвоить адрес метки регистру или переменной, например. В данном примере вы можете использовать offset вместо addr. Тем не менее, есть некоторые различия между ними.
1. addr не может быть использован с метками, которые определены впереди, а offset может. Например, если метка определена где-то дальше в коде, чем строка с invoke, addr не будет работать.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
invoke MessageBox,NULL, addr MsgBoxText,addr MsgBoxCaption,MB_OK
......
MsgBoxCaption  db "Iczelion Tutorial #2",0
MsgBoxText       db "Win32 Assembly is Great!",0
MASM доложит об ошибке. Если вы используете offset вместо addr, MASM без проблем скомпилирует указанный отрывок кода.
2. Addr поддерживает локальные переменные, в то время как offset нет.
Локальная переменная — это всего лишь зарезервированное место в стеке. Вы только знаете его адрес во время выполнения программы. Offset интерпретируется во время компиляции ассемблером, поэтому неудивительно, что он не поддерживает локальные переменные. Addr же работает с ними, потому что ассемблер сначала проверяет ― глобальная переменная или локальная. Если она глобальная, он помещает адрес этой переменной в объектный файл. В этом случае оператор работает как offset. Если это локальная переменная, компилятор генерирует следующую последовательность инструкций, перед тем как будет вызвана функция:
Assembler
1
2
lea eax, LocalVar
push eax
Учитывая, что lea может определить адрес метки в "рантайме", все работает прекрасно.
________________________________________ ____
© Iczelion, пер. Aquila.
Вложения
Тип файла: zip tut10c.zip (4.9 Кб, 107 просмотров)
3
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
14.01.2013, 05:21  [ТС]
Win32 API. Урок 10 e. Создания диалогового окна через DialogBoxParam и DlgProc
Кликните здесь для просмотра всего текста
В этом Уроке мы создадим полнофункциональную Windows программу, которое выводит сообщение — "Win32 assembly is great!".
Скачайте пример здесь.
ТЕОРИЯ, МАТЬ СКЛЕРОЗА
Windows предоставляет огромное количество ресурсов Windows-программам через Windows API (Application Programming Interface). Windows API — это большая коллекция очень полезных функций, располагающихся непосредственно в операционной системе и готовых для использования программами. Эти функции находятся в нескольких динамически подгружаемых библиотеках (DLLs), таких как kernel32.dll, user32.dll и gdi32.dll. Kernel32.dll содержит API-функции, взаимодействующие с памятью и управляющие процессами. User32.dll контролирует пользовательский интерфейс. Gdi32.dll ответственен за графические операции. Кроме этих трех "основных", существуют также другие dll, которые вы можете использовать, при условии, что вы обладаете достаточным количеством информации о нужных API-функциях. Windows программы динамически подсоединяется к этим библиотекам, то есть код API-функций не включается в исполняемый файл. Информация находится в библиотеках импорта. Вы должны слинковать ваши программы с правильными библиотеками импорта, иначе они не смогут найти эти функции. Когда Windows программа загружается в память, Windows читает информацию, сохраненную в программе. Эта информация включает имена функций, которые программа использует и DLL-ей, в которых эти функции располагаются. Когда Windows находит подобную информацию в программе, она вызывает библиотеки и исправляет в программе вызовы этих функций, так что контроль всегда будет передаваться по правильному адресу.
Существует две категории API функций: одни работают с ANSI-строками, а другие с Unicode-строками. Имена API-функций использующих ANSI-строки заканчиваются на "A", например, MessageBoxA. В конце имен функций для Unicode находится "W". Windows 95/98 от природы поддерживают ANSI, а Windows NT и производные от нее 2k/XP/Vista поддерживают Unicode. Обычно мы имеем дело с ANSI строками (массивы символов, оканчивающиеся NULL-ом. размер ANSI-символа — 1 байт. В то время как ANSI достаточна для европейских языков, она не поддерживает некоторые восточные языки, в которых есть несколько тысяч уникальных символов. Вот в этих случаях в дело вступает UniCode. размер символа UNICODE — 2 байта, и поэтому может поддерживать 65536 различных символов. Hо по большей части, вы будете использовать include-файл, который может определить и выбрать подходящую для вашей платформы функцию. Просто обращайтесь к именам API-функций без постфикса.
ПРАКТИКА, МАТЬ ШИЗОФРЕНИИ
Я приведу голый скелет программы ниже. Позже мы разберем его.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
.386
.model flat, stdcall
.data
.code
start:
end start
Выполнение начинается с первой инструкции, следующей за меткой, установленной после конца директив. В вышеприведенном каркасе выполнение начинается непосредственно после метки 'start'. Будут последовательно выполняться инструкция за инструкцией, пока не встретится операция передачи управления, такая как: jmр, jne, je, ret и так далее. Эти инструкции перенаправляют поток выполнения другим инструкциям. Когда программа выходит в Windows, ей следует вызвать API-функцию ExitProcess.
Assembler
1
ExitProcess proto uExitCode:DWORD
Строка выше называется прототипом функции. Прототип функции указывает ассемблеру/линкеру атрибуты функции, чтобы он сделал проверку типов данных и количество атрибутов передаваемых функции. Формат прототипа функции следующий:
Assembler
1
ИмяФункции PROTO [ИмяПараметра]:ТипДанных,[ИмяПараметра]:ТипДанных,...
Короче говоря, за именем функции следует ключевое слово PROTO, а затем список переменных с типом данных, разделенных запятыми. В приведенном выше примере с ExitProcess, эта функция была определена как принимающая только один параметр типа DWORD. Прототипы функций очень полезны, когда вы используете высокоуровневый синтаксический вызов — invoke. Вы можете считать об invoke как обычный вызов с проверкой типов данных. Например, если вы напишите:
Assembler
1
call ExitProcess
Линкер уведомит вас, что вы забыли положит в стек двойное слово. Я рекомендую вам использовать invoke вместо простого вызова. Синтаксис invoke следующий:
Assembler
1
invoke выражение [, аргументы]
Выражение может быть именем функции или указателем на функцию. Параметры функции разделены запятыми.
Большинство прототипов для API-функций содержатся в include-файлах. Если вы используете hutch'евский MASM32, они будут находится в директории MASM32/INCLUDE. Файлы подключения имеют расширение .inc и прототипы функций DLL находятся в .inc файле с таким же именем, как и у этой DLL.
Hапример, ExitProcess экспортируется из kernel32.lib, так что прототип ExitProcess находится в kernel32.inc.

Вы также можете создать прототипы для ваших собственных функций. Во всех моих экземплярах я использую hutch'евский windows.inc, который вы можете скачать с http://win32asm.cjb.net
Возвращаясь к ExitProcess: параметр uExitCode - это значение, которое программа вернет Windows после окончания программы. Вы можете вызвать функцию ExitProcess так:
Assembler
1
invoke ExitProcess, 0
Поместив эту строку непосредственно после стартовой метки, вы получите Win32-программу, немедленно выходящую в Windows, но тем не менее полнофункциональную.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.data
.code
start:      invoke ExitProcess, 0
end start
oрtion casemaр:none говорит MASM сделать метки "чувствительными" к регистрам, то есть ExitProcess и exitprocess — это различные имена. Отметьте новую директиву — include. После нее следует имя файла, который вы хотите вставить в то место, где эта директива располагается. В примере выше, когда MASM обрабатывает линию include \masm32\include\windows.inc, он открывает windows.inc, находящийся в директории \MASM32\INCLUDE, и далее анализирует содержимое windows.inc так, как будто вы "вклеили" подключаемый файл. Хатчевский windows.inc содержит в себе определения констант и структур, которые вам могут понадобиться для программирования под Win32. Этот файл не содержит в себе прототипов функций. Windows.inc ни в коем случае не является исчерпывающим и всеобъемлющим. Hutch и я пытаемся заполнить его как можно большим количеством констант и структур, но есть еще довольно, что следовало бы включить. Он постоянно обновляется. Заходите на хатчевскую и мою странички за свежими апдейтами. Из windows.inc, ваша программа будет брать определения констант и структур. Что касается прототипов функций, вы должны подключить другие include-файлы. Они находятся в директории \masm32\include.

В вышеприведенном примере, мы вызываем функцию, экспортированную из kernel32.dll, для чего мы должны подключить прототипы функций из kernel32.dll. Этот файл - kernel32.inc. Если вы открываете его текстовым редактором, вы увидите, что он состоит из прототипов функций из соответствующей dll. Если вы не подключите kernel32.inc, вы все еще можете вызвать ExitProcess, но уже с помощью ассемблерной команды call. Вы не сможете вызвать эту функцию с помощью invoke. Дело вот в чем: для того, чтобы вызвать функцию через invoke, вы должны поместить в исходном коде ее прототип. В примере выше, если вы не подключите kernel32.inc, вы можете определить прототип для ExitProcess где-нибудь до вызова этой функции и это будет работать. Файлы подключения нужны для того, что избавить вас от лишней работы и вам не пришлось набирать все прототипы самим.
Теперь мы встречаем новую директиву — includelib. Она работает не так, как include. Это всего лишь способ сказать ассемблеру какие библиотеки использует ваша программа должна прилинковать. Хотя вы вовсе не обязаны использовать именно этот метод. Вы можете указать имена библиотек импорта к командной строке при запуске линкера, но поверьте мне, это весьма скучно и утомительно, да и командная строка может вместить максимум 128 символов.
Теперь возьмите весь исходный текст примера этого Урока, сохраните его как msgbox.asm и ассемблируйте его так:
Code
1
ml /c /coff /Cp msgbox.asm
/c говорит MASM'у создать .obj файл в формате COFF. MASM использует вариант COFF (Common Object File Format), использующийся под Unix, как его собственный объектный и исполняемый формат файлов.
/Cр говорит MASM'у сохранять регистр имен, заданных пользователем. Если вы используете hutch'евский MASM32 пакет, вы можете вставить "option casemaр:none" в начале вашего исходника, сразу после директивы .model, чтобы добиться того же эффекта.
После успешной компиляции msgbox.asm, вы получите msgbox.obj. Это объектный файл, от которого один шаг до екзешника. Obj содержит инструкции/данные в двоичной форме. Отсутствуют только необходимая корректировка адресов, которая проводится линкером.
Теперь сделайте следующее:
Code
1
link /SUBSYSTEM:WINDOWS  /LIBPATH:c:\masm32\lib  msgbox.obj
/SUBSYSTEM:WINDOWS информирует линкер о том, какого вида является будущий исполняемый модуль.
/LIBPATH:<путь к библиотекам импорта> говорит линкеру, где находятся библиотеки импорта. Если вы используете MASM32, они будут в MASM32\lib.

Линкер читает объектный файл и корректирует его, используя адреса, взятые из библиотек импорта. После окончания линковки вы получите файл msgbox.exe. Запустите его. Вы увидите, что она ничего не делает.

Да, мы не поместили в код ничего не интересного. Hо тем не менее полноценная Windows программа. И посмотрите на размер! Hа моем PC — 1.536 байт.
Теперь мы готовы создать окно с сообщением. Прототип функции, которая нам для этого необходима следующая:
Assembler
1
MessageBox PROTO hwnd:DWORD, lpText:DWORD, lpCaption:DWORD, uType:DWORD
hwnd — это хэндл родительского окна. Вы можете считать хэндл числом, представляющим окно, к которому вы обращаетесь. Его значение для вас не важно. Вы только должны знать, что оно представляет окно. Когда вы захотите сделать что-нибудь с окном, вы должны обратиться к нему, используя его хэндл.
lрText — это указатель на текст, который вы хотите отобразить в клиентской части окна сообщения. Указатель ― это адрес чего-либо. Указатель на текстовую строку = адрес этой строки.
lpCaption — это указатель на заголовок окна сообщения.
uType — устанавливает иконку, число и вид кнопок окна.

Давайте изменим msgbox.asm для отображения сообщения.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
.data
MsgBoxCaption  db "Iczelion Tutorial #2",0
MsgBoxText     db "Win32 Assembly is Great!",0
.code
start: invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK
invoke ExitProcess, NULL
end start
Скомпилируйте и запустите. Вы увидите окошко с сообщением "Win32 Assembly is great!".
Давайте снова взглянем на исходник.
Мы определили две оканчивающиеся NULL'ом строки в секции .data. Помните, что каждая ANSI строка в Windows должна оканчиваться NULL'ом (0 в шестнадцатеричной системе). Мы используем две константы, NULL и MB_OK. Эти константы прописаны в windows.inc, так что вы можете обратиться к ним, указав их имя, а не значение. Это улучшает читабельность кода.
Оператор addr используется для передачи адреса метки (и не только) функции. Он действителен только в контексте директивы invoke. Вы не можете использовать его, чтобы присвоить адрес метки регистру или переменной, например. В данном примере вы можете использовать offset вместо addr. Тем не менее, есть некоторые различия между ними.
1. addr не может быть использован с метками, которые определены впереди, а offset может. Например, если метка определена где-то дальше в коде, чем строка с invoke, addr не будет работать.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
invoke MessageBox,NULL, addr MsgBoxText,addr MsgBoxCaption,MB_OK
......
MsgBoxCaption  db "Iczelion Tutorial #2",0
MsgBoxText       db "Win32 Assembly is Great!",0
MASM доложит об ошибке. Если вы используете offset вместо addr, MASM без проблем скомпилирует указанный отрывок кода.
2. Addr поддерживает локальные переменные, в то время как offset нет.
Локальная переменная — это всего лишь зарезервированное место в стеке. Вы только знаете его адрес во время выполнения программы. Offset интерпретируется во время компиляции ассемблером, поэтому неудивительно, что он не поддерживает локальные переменные. Addr же работает с ними, потому что ассемблер сначала проверяет ― глобальная переменная или локальная. Если она глобальная, он помещает адрес этой переменной в объектный файл. В этом случае оператор работает как offset. Если это локальная переменная, компилятор генерирует следующую последовательность инструкций, перед тем как будет вызвана функция:
Assembler
1
2
lea eax, LocalVar
push eax
Учитывая, что lea может определить адрес метки в "рантайме", все работает прекрасно.
________________________________________ ____
© Iczelion, пер. Aquila.
Вложения
Тип файла: zip tut10d.zip (3.8 Кб, 114 просмотров)
3
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
14.01.2013, 05:59  [ТС]
Win32 API. Урок 10f. Создания диалогового окна через DialogBoxParam и DlgProc и WndProc
Урок сделан по мотивам Iczelion и "main variants" от kero
Скачайте пример.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
.586p
.model tiny
include windows.inc
;for WinXP - 1065 bytes
du macro string
irpc c,<string>
dw '&c'
endm
dw 0
endm
.code
exebase equ 400000h
;-------------------------------------------------------
; Type of control
;-------------------------------------------------------
BUTTON equ 80h
EDIT equ 81h
STATIC equ 82h
LISTBOX equ 83h
SCROLLBAR equ 84h
COMBOBOX equ 85h
;--------------------------------------------------------
; Equs for menus
;--------------------------------------------------------
POPUP equ 10h
MENUBREAK equ 40h
ENDMENU equ 80h
DS_SETFONT equ 40h
;---------------------------------------------------------
IDM_GETTEXT equ 0
IDM_CLEAR equ 1
IDM_EXIT equ 2
IDC_BUTTON equ 3
IDC_EXIT equ 4
IDC_EDIT equ 5
IDC_MENU equ 100
IDC_DIALOG equ 200
lang equ 409h
main:
;signatures----------------------------
dosHeader dd IMAGE_DOS_SIGNATURE;'MZ'
ntHeader dd IMAGE_NT_SIGNATURE;'PE'
;image_header--------------------------
Machine dw IMAGE_FILE_MACHINE_I386; (Intel386)
Count_of_section dw 1
TimeStump dd 0
Symbol_table_offset dd 0
Symbol_table_count dd 0
Size_of_optional_header dw section_table-optional_header;
Characteristics dw IMAGE_FILE_32BIT_MACHINE or \
IMAGE_FILE_RELOCS_STRIPPED or IMAGE_FILE_EXECUTABLE_IMAGE or \
IMAGE_FILE_LINE_NUMS_STRIPPED or IMAGE_FILE_LOCAL_SYMS_STRIPPED
;-------------------------------------
optional_header:
Magic_optional_header dw IMAGE_NT_OPTIONAL_HDR32_MAGIC
Linker_version_major_and_minor dw 0
Size_of_code dd end_import-start
Size_of_init_data dd 0
Size_of_uninit_data dd 0
entry_point dd start
base_of_code dd start
base_of_data dd 0
image_base dd exebase;
e_lfanew dd ntHeader-dosHeader;section alignment
file_alignment dd 4
OS_version_major_minor dd 4
image_version_major_minor dd 0
subsystem_version_major_minor dd 4
reserved1 dd 0
size_of_image dd end_import
size_of_header dd start
checksum dd 0
subsystem_and_DLL_flag dd IMAGE_SUBSYSTEM_WINDOWS_GUI
Stack_allocation dd 100000h
Stack_commit dd 1000h
Heap_allocation dd 100000h
Heap_commit dd 1000h
loader_flag dd 0
number_of_dirs dd (section_table-export_RVA)/8
export_RVA dd 0
export_size dd 0
import_RVA dd import
import_size dd end_import-import
resuorce_RVA dd resource
resuorce_size dd end_resource-resource
;------------------------------------------------
section_table dd 'xet.','t'
virtual_size dd 0;exeption_RVA
virtual_address dd start;exeption_size
Physical_size dd end_import-start;security_RVA
Physical_offset dd start
Relocations dd 0
Linenumbers dd 0
Relocations_and_Linenumbers_count dd 0
Attributes dd 0
;---------------------------------------------------------
start: xor ebx,ebx
push ebx
push offset DlgProc+exebase
push ebx
push IDC_DIALOG
push exebase
call DialogBoxParam+exebase
retn
WndProc:
hDlg equ [ebp+8]
uMsg equ [ebp+0Ch]
wParam equ [ebp+10h]
lParam equ [ebp+14h]
enter 96,0
mov esi,esp;lea esi,buffer
mov eax,uMsg
mov edi,hDlg
sub eax,WM_CLOSE
jz wmCLOSE
sub eax,WM_COMMAND-WM_CLOSE
jz wmCOMMAND
leave
jmp dword ptr DefDlgProc+exebase
wmCOMMAND: mov ax,[wParam]
cmp [lParam],ebx;0
jz short a3; выбрали меню
; выбрали кнопку или EditBox
cmp [wParam+2],bx;IF BN_CLICKED
jne short wmBYE
a3: jmp dword ptr [handlers+exebase+eax*4]
GETTEXT: push 96
push esi
push IDC_EDIT
push edi
call GetDlgItemText+exebase
push ebx
push offset AppName+exebase
push esi
push ebx
call MessageBox+exebase
jmp short wmBYE
SAYHELLO: push offset TestString+exebase
jmp a4
CLEAR: push ebx
a4: push IDC_EDIT
push edi
call SetDlgItemText+exebase
jmp short wmBYE
wmCLOSE: push ebx
push edi
call EndDialog+exebase
wmBYE: leave
retn 10h
DlgProc: cmp dword ptr [esp+8],WM_INITDIALOG;cmp uMsg,WM_INITDIALOG
jne @f
push offset WndProc+exebase
push GWL_WNDPROC
push dword ptr [esp+4+8]
call SetWindowLong+exebase
@@: xor eax,eax
retn 16
handlers dd GETTEXT+exebase,CLEAR+exebase,wmCLOSE+ex­ebase,\
SAYHELLO+exebase,wmCLOSE+exebase
AppName db "Our Fifth Dialog Box",0
TestString db "Wow! I'm in an edit box now",0
;----------------------------------------------------------
align 4
resource:
Characteristics1 dd 0
TimeDateStamp1 dd 0
MajorVersion1 dw 0
MinorVersion1 dw 0;
NumberOfNamedEntries1 dw 0;количество ресурсов с именами
NumberOfIdEntries1 dw 2;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является типом ресурса
dw RT_MENU,0,m1-resource,8000h;номер типа ресурса
dw RT_DIALOG,0,d1-resource,8000h; если во 2-ом слове установлен
;старший бит - есть ссылка на оглавление второго уровня. В 1-ом слове
;смещение второго оглавления относительно начала раздела ресурсов
m1:
Characteristics2 dd 0
TimeDateStamp2 dd 0
MajorVersion2 dw 0
MinorVersion2 dw 0;
NumberOfNamedEntries2 dw 0;количество ресурсов с именами
NumberOfIdEntries2 dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является идентификатором меню
dw IDC_MENU,0,m2-resource,8000h
; если во 2-ом слове установлен старший бит - есть ссылка
;на оглавление третьего уровня. В 1-ом слове смещение третьего оглавления
;относительно начала раздела ресурсов
m2:
Characteristics3 dd 0
TimeDateStamp3 dd 0
MajorVersion3 dw 0
MinorVersion3 dw 0
NumberOfNamedEntries3 dw 0;количество ресурсов с именами
NumberOfIdEntries3 dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является идентификатором
;языка, который используется даннvм ресурсом 16 * SUBLANG_ + LANG_
dw lang,0,m3-resource,0
m3:;struct _IMAGE_RESOURCE_DATA_ENTRY
OffsetToData1 dd menu
Size1 dd end_menu-menu
CodePage1 dd 0
Reserved1 dd 0
d1:
Characteristics4 dd 0
TimeDateStamp4 dd 0
MajorVersion4 dw 0
MinorVersion4 dw 0;
NumberOfNamedEntries4 dw 0;количество ресурсов с именами
NumberOfIdEntries4 dw 1;количество ресурсов с идентификаторами
dw IDC_DIALOG,0,d2-resource,8000h
d2:
Characteristics5 dd 0
TimeDateStamp5 dd 0
MajorVersion5 dw 0
MinorVersion5 dw 0;
NumberOfNamedEntries5 dw 0;количество ресурсов с именами
NumberOfIdEntries5 dw 1;количество ресурсов с идентификаторами
dw lang,0,d3-resource,0;LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
d3:
OffsetToData2 dd dialog
Size2 dd end_dialog-dialog
CodePage2 dd 0
Reserved2 dd 0
 
menu:
dw 0, 0,POPUP or ENDMENU
du <Test Controls>
dw MFT_STRING or MFS_ENABLED,IDM_GETTEXT
du <Get Text>
dw MFT_STRING or MFS_ENABLED,IDM_CLEAR
du <Clear Text>
dw MENUBREAK,0
dw 0;du <>;NOTEXT
dw MFT_STRING or MFS_ENABLED or ENDMENU,IDM_EXIT
du <&Exit>
end_menu:
dialog:
style1 dd DS_CENTER or WS_MINIMIZEBOX or WS_VISIBLE or WS_CAPTION \
or WS_SYSMENU;DS_MODALFRAME |
dwExtendedStyle1 dd 0
cdit1 dw 3 ;число элементов управления, входящих в состав диалогового окна
x1 dw 10;отступ левой границы окна от левой границы экрана
y1 dw 10;отступ верхней границы окна от верхней границы экрана
cx1 dw 205;ширина окна
cy1 dw 60;высота окна
dw 0FFFFh;есть меню у диалога (=FFFF) или меню нет (=0)
dw IDC_MENU;идентификатор меню
dw 0
CAPTION: du <DialogBoxParam+DlgProc+WndProc>;заголов­ок диалога
align 4
;------------------------------------------------------
;элементы управления диалога
u1:
style2 dd ES_LEFT or ES_AUTOHSCROLL or WS_CHILD or WS_VISIBLE \
or WS_BORDER or WS_TABSTOP;50810080h;
dwExtendedStyle2 dd 0
x2 dw 15;x-координаты верхнего левого угла
y2 dw 17;y-координаты верхнего левого угла
cx2 dw 111;ширина объекта управления
cy2 dw 13;высота объекта управления
id2 dw IDC_EDIT;идентификатор объекта управления
dw 0FFFFh;тип объекта управления если равен FFFF - стандартный
;элемент управления
dw EDIT;тип объекта управления
du <>;надпись на объекте управления
dw 0
dw 0;терминатор
;---------------------------------------------------
u2:
style3 dd BS_DEFPUSHBUTTON or WS_CHILD or WS_VISIBLE or WS_TABSTOP
dwExtendedStyle3 dd 0
x3 dw 141;x-координаты верхнего левого угла
y3 dw 10;y-координаты верхнего левого угла
cx3 dw 52;ширина объекта управления
cy3 dw 14;высота объекта управления
id3 dw IDC_BUTTON;идентификатор объекта управления
dw 0FFFFh;тип объекта управления если равен FFFF - стандартный
;элемент управления
dw BUTTON;тип объекта управления
du <Say Hello>;надпись на объекте управления
dw 0;терминатор
;----------------------------------------------------
u3:
style4 dd BS_PUSHBUTTON or WS_CHILD or WS_VISIBLE or WS_TABSTOP
dwExtendedStyle4 dd 0
x4 dw 141;x-координаты верхнего левого угла
y4 dw 26
cx4 dw 52
cy4 dw 13
id4 dw IDC_EXIT;идентификатор объекта управления
dw 0FFFFh;тип объекта управления если равен FFFF - стандартный
;элемент управления
dw BUTTON;тип объекта управления
du <E&xit>;надпись на объекте управления
dw 0;терминатор
;----------------------------------------------------
end_dialog:
 
end_resource:
;----------------------------------------------------
import:
dd 0,0,0,user32_dll
dd user32_table
dd 0,0,0,0
user32_table:
MessageBox dd _MessageBox
SetDlgItemText dd _SetDlgItemText
DialogBoxParam dd _DialogBoxParam
GetDlgItemText dd _GetDlgItemText
SetWindowLong dd _SetWindowLong
DefDlgProc dd _DefDlgProc
EndDialog dd _EndDialog
dw 0
_MessageBox db 0,0,"MessageBoxA"
_SetDlgItemText db 0,0,'SetDlgItemTextA'
_DefDlgProc db 0,0,'DefDlgProcA'
_SetWindowLong db 0,0,'SetWindowLongA'
_DialogBoxParam db 0,0,'DialogBoxParamA'
_GetDlgItemText db 0,0,'GetDlgItemTextA'
_EndDialog db 0,0,'EndDialog',0
user32_dll db 'user32'
 
end_import:
end main
________________________________________ ­_
© Mikl___ 2013
Вложения
Тип файла: zip tut10e.zip (4.0 Кб, 101 просмотров)
3
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
14.01.2013, 06:29  [ТС]
Win32 API. Урок 10g. Создания диалогового окна через DialogBoxParam и WndProc и RegisterClassEx

Урок сделан по мотивам Iczelion и "main variants" от kero
Скачайте пример.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
.586p
.model tiny
include windows.inc
;for WinXP - 1091 bytes
du macro string
irpc c,
dw '&c'
endm
dw 0
endm
.code
exebase equ 400000h
;-----------------------------------------------------
; Type of control
;-----------------------------------------------------
BUTTON equ 80h
EDIT equ 81h
STATIC equ 82h
LISTBOX equ 83h
SCROLLBAR equ 84h
COMBOBOX equ 85h
;-------------------------------------------------------
; Equs for menus
;-------------------------------------------------------
POPUP equ 10h
MENUBREAK equ 40h
ENDMENU equ 80h
DS_SETFONT equ 40h
;--------------------------------------------------------
IDM_GETTEXT equ 0
IDM_CLEAR equ 1
IDM_EXIT equ 2
IDC_BUTTON equ 3
IDC_EXIT equ 4
IDC_EDIT equ 5
IDC_MENU equ 100
IDC_DIALOG equ 200
lang equ 409h
main:
;signatures----------------------------
dosHeader dd IMAGE_DOS_SIGNATURE;'MZ'
ntHeader dd IMAGE_NT_SIGNATURE;'PE'
;image_header--------------------------
Machine dw IMAGE_FILE_MACHINE_I386; (Intel386)
Count_of_section dw 1
TimeStump dd 0
Symbol_table_offset dd 0
Symbol_table_count dd 0
Size_of_optional_header dw section_table-optional_header;
Characteristics dw IMAGE_FILE_32BIT_MACHINE or \
IMAGE_FILE_RELOCS_STRIPPED or IMAGE_FILE_EXECUTABLE_IMAGE or \
IMAGE_FILE_LINE_NUMS_STRIPPED or IMAGE_FILE_LOCAL_SYMS_STRIPPED
;-------------------------------------
optional_header:
Magic_optional_header dw IMAGE_NT_OPTIONAL_HDR32_MAGIC
Linker_version_major_and_minor dw 0
Size_of_code dd end_import-start;
Size_of_init_data dd 0;
Size_of_uninit_data dd 0;
entry_point dd start;
base_of_code dd start;
base_of_data dd 0;
image_base dd exebase;
e_lfanew dd ntHeader-dosHeader;section alignment
file_alignment dd 4;
OS_version_major_minor dd 4;
image_version_major_minor dd 0;
subsystem_version_major_minor dd 4;
reserved1 dd 0;
size_of_image dd end_import;
size_of_header dd start;
checksum dd 0;
subsystem_and_DLL_flag dd IMAGE_SUBSYSTEM_WINDOWS_GUI
Stack_allocation dd 100000h;
Stack_commit dd 1000h;
Heap_allocation dd 100000h;
Heap_commit dd 1000h;
loader_flag dd 0;
number_of_dirs dd (section_table-export_RVA)/8;
export_RVA dd 0
export_size dd 0
import_RVA dd import
import_size dd end_import-import
resuorce_RVA dd resource
resuorce_size dd end_resource-resource
;------------------------------------------------
section_table dd 'xet.','t'
virtual_size dd 0;exeption_RVA
virtual_address dd start;exeption_size
Physical_size dd end_import-start;security_RVA
Physical_offset dd start
Relocations dd 0
Linenumbers dd 0
Relocations_and_Linenumbers_count dd 0
Attributes dd 0
;---------------------------------------------------------
start: xor ebx,ebx
mov esi,exebase
push ebx
push offset ClassName+exebase
push IDC_MENU
push COLOR_BTNFACE+1
push 10011h
push ebx
push esi
push DLGWINDOWEXTRA
push ebx
push offset WndProc+exebase
push CS_HREDRAW or CS_VREDRAW
push sizeof(WNDCLASSEX)
push esp
call RegisterClassEx+exebase
add esp,sizeof(WNDCLASSEX)
push ebx
push ebx
push ebx
push IDC_DIALOG
push esi
call DialogBoxParam+exebase
retn
WndProc:
hDlg equ [ebp+8]
uMsg equ [ebp+0Ch]
wParam equ [ebp+10h]
lParam equ [ebp+14h]
enter 96,0
mov esi,esp;lea esi,buffer
mov eax,uMsg
mov edi,hDlg
sub eax,WM_CLOSE
jz wmCLOSE
sub eax,WM_COMMAND-WM_CLOSE
jz wmCOMMAND
leave
jmp dword ptr DefDlgProc+exebase
wmCOMMAND: mov ax,[wParam]
cmp [lParam],ebx;0
jz short a3; выбрали меню
; выбрали кнопку или EditBox
cmp [wParam+2],bx;IF BN_CLICKED
jne short wmBYE
a3: jmp dword ptr [handlers+exebase+eax*4]
GETTEXT: push 96
push esi
push IDC_EDIT
push edi
call GetDlgItemText+exebase
push ebx
push offset AppName+exebase
push esi
push ebx
call MessageBox+exebase
jmp short wmBYE
SAYHELLO: push offset TestString+exebase
jmp a4
CLEAR: push ebx
a4: push IDC_EDIT
push edi
call SetDlgItemText+exebase
jmp short wmBYE
wmCLOSE: push ebx
push edi
call EndDialog+exebase
wmBYE: leave
retn 10h
 
handlers dd GETTEXT+exebase,CLEAR+exebase,wmCLOSE+ex­ebase,\
SAYHELLO+exebase,wmCLOSE+exebase
AppName db "Our Sixth Dialog Box",0
TestString db "Wow! I'm in an edit box now",0
ClassName db "DLGCLASS",0
;----------------------------------------------------------
align 4
resource:
Characteristics1 dd 0
TimeDateStamp1 dd 0
MajorVersion1 dw 0
MinorVersion1 dw 0;
NumberOfNamedEntries1 dw 0;количество ресурсов с именами
NumberOfIdEntries1 dw 2;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является типом ресурса
dw RT_MENU,0,m1-resource,8000h;номер типа ресурса
dw RT_DIALOG,0,d1-resource,8000h; если во 2-ом слове установлен
;старший бит - есть ссылка на оглавление второго уровня. В 1-ом
;слове смещение второго оглавления относительно начала раздела ресурсов
m1:
Characteristics2 dd 0
TimeDateStamp2 dd 0
MajorVersion2 dw 0
MinorVersion2 dw 0;
NumberOfNamedEntries2 dw 0;количество ресурсов с именами
NumberOfIdEntries2 dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является идентификатором меню
dw IDC_MENU,0,m2-resource,8000h
; если во 2-ом слове установлен старший бит - есть ссылка
;на оглавление третьего уровня. T 1-ом слове смещение третьего
;оглавления относительно начала раздела ресурсов
m2:
Characteristics3 dd 0
TimeDateStamp3 dd 0
MajorVersion3 dw 0
MinorVersion3 dw 0;
NumberOfNamedEntries3 dw 0;количество ресурсов с именами
NumberOfIdEntries3 dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является идентификатором
;языка, который используется даннvм ресурсом 16 * SUBLANG_ + LANG_
dw lang,0,m3-resource,0
m3:;struct _IMAGE_RESOURCE_DATA_ENTRY
OffsetToData1 dd menu
Size1 dd end_menu-menu
CodePage1 dd 0
Reserved1 dd 0
d1:
Characteristics4 dd 0
TimeDateStamp4 dd 0
MajorVersion4 dw 0
MinorVersion4 dw 0;
NumberOfNamedEntries4 dw 0;количество ресурсов с именами
NumberOfIdEntries4 dw 1;количество ресурсов с идентификаторами
dw IDC_DIALOG,0,d2-resource,8000h
d2:
Characteristics5 dd 0
TimeDateStamp5 dd 0
MajorVersion5 dw 0
MinorVersion5 dw 0;
NumberOfNamedEntries5 dw 0;количество ресурсов с именами
NumberOfIdEntries5 dw 1;количество ресурсов с идентификаторами
dw lang,0,d3-resource,0;LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
d3:
OffsetToData2 dd dialog
Size2 dd end_dialog-dialog
CodePage2 dd 0
Reserved2 dd 0
 
menu:
dw 0, 0,POPUP or ENDMENU
du
dw MFT_STRING or MFS_ENABLED,IDM_GETTEXT
du
dw MFT_STRING or MFS_ENABLED,IDM_CLEAR
du
dw MENUBREAK,0
dw 0;du ;NOTEXT
dw MFT_STRING or MFS_ENABLED or ENDMENU,IDM_EXIT
du
end_menu:
dialog:
style1 dd DS_CENTER or WS_MINIMIZEBOX or WS_VISIBLE or \
WS_CAPTION or WS_SYSMENU;DS_MODALFRAME |
dwExtendedStyle1 dd 0
cdit1 dw 3 ;число элементов управления, входящих в состав диалогового окна
x1 dw 10;отступ левой границы окна от левой границы экрана
y1 dw 10;отступ верхней границы окна от верхней границы экрана
cx1 dw 205;ширина окна
cy1 dw 60;высота окна
dw 0FFFFh;есть меню у диалога (=FFFF) или меню нет (=0)
dw IDC_MENU;идентификатор меню
CLASS: du
CAPTION: du ;заголовок диалога
align 4
;------------------------------------------------------
;элементы управления диалога
u1:
style2 dd ES_LEFT or ES_AUTOHSCROLL or WS_CHILD or WS_VISIBLE \
or WS_BORDER or WS_TABSTOP;50810080h
dwExtendedStyle2 dd 0
x2 dw 15;x-координаты верхнего левого угла
y2 dw 17;y-координаты верхнего левого угла
cx2 dw 111;ширина объекта управления
cy2 dw 13;высота объекта управления
id2 dw IDC_EDIT;идентификатор объекта управления
dw 0FFFFh;тип объекта управления если равен FFFF - стандартный
;элемент управления
dw EDIT;тип объекта управления
du ;надпись на объекте управления
dw 0
dw 0;терминатор
;---------------------------------------------------
u2:
style3 dd BS_DEFPUSHBUTTON or WS_CHILD or WS_VISIBLE or WS_TABSTOP
dwExtendedStyle3 dd 0
x3 dw 141;x-координаты верхнего левого угла
y3 dw 10;y-координаты верхнего левого угла
cx3 dw 52;ширина объекта управления
cy3 dw 14;высота объекта управления
id3 dw IDC_BUTTON;идентификатор объекта управления
dw 0FFFFh;тип объекта управления если равен FFFF - стандартный
;элемент управления
dw BUTTON;тип объекта управления
du ;надпись на объекте управления
dw 0;терминатор
;----------------------------------------------------
u3:
style4 dd BS_PUSHBUTTON or WS_CHILD or WS_VISIBLE or WS_TABSTOP
dwExtendedStyle4 dd 0
x4 dw 141;x-координаты верхнего левого угла
y4 dw 26
cx4 dw 52
cy4 dw 13
id4 dw IDC_EXIT;идентификатор объекта управления
dw 0FFFFh;тип объекта управления если равен FFFF - стандартный
;элемент управления
dw BUTTON;тип объекта управления
du ;надпись на объекте управления
dw 0;терминатор
;----------------------------------------------------
end_dialog:
 
end_resource:
;---------------------------------------------------
import:
dd 0,0,0,user32_dll
dd user32_table
dd 0,0,0,0
user32_table:
MessageBox dd _MessageBox
SetDlgItemText dd _SetDlgItemText
DialogBoxParam dd _DialogBoxParam
GetDlgItemText dd _GetDlgItemText
RegisterClassEx dd _RegisterClassEx
DefDlgProc dd _DefDlgProc
EndDialog dd _EndDialog
dw 0
_MessageBox db 0,0,"MessageBoxA"
_SetDlgItemText db 0,0,'SetDlgItemTextA'
_DefDlgProc db 0,0,'DefDlgProcA'
_RegisterClassEx db 0,0,'RegisterClassExA'
_DialogBoxParam db 0,0,'DialogBoxParamA'
_GetDlgItemText db 0,0,'GetDlgItemTextA'
_EndDialog db 0,0,'EndDialog',0
user32_dll db 'user32'
 
end_import:
end main
_______________________________________
© Mikl___ 2013
Вложения
Тип файла: zip tut10f.zip (4.1 Кб, 116 просмотров)
3
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16377 / 7689 / 1080
Регистрация: 11.11.2010
Сообщений: 13,768
14.01.2013, 07:06  [ТС]
Win32 API. Урок 11. Больше о диалоговых окнах

В этом туториале мы узнаем больше о диалоговых окнах. В частности, мы узнаем, как использовать диалоговые окна в качестве устройств ввода-вывода. Если вы читали предыдущий туториал, то этот будет для вас достаточно прост, так как небольшая модификация ― все, что требуется для использования диалоговых окон как дополнение к основному окну. Также в этом туториале мы научимся тому, как использовать предопределенные диалоговые окна.
Скачайте примеры здесь и здесь. Пример предопределенного диалогового окна скачайте здесь.

ТЕОРИЯ МАТЬ СКЛЕРОЗА
Очень немногое будет сказано о том, как использовать диалоговые окна в качестве устройств ввода-вывода. Программа создает основное окно как обычно, и когда вы хотите отобразить диалоговое окно, просто-напросто вызовите CreateDialogParam или DialogBoxParam. Вызвав DialogBoxParam, вам не нужно делать что-либо еще, просто обработайте сообщения в процедуре диалогового окна. При использовании CreateDialogParam, вам будет нужно вставить вызов IsDialogMessage в цикле сообщений, чтобы позволить менеджеру диалогового окна обработать навигацию клавиатуры в вашем диалоговом окне за вас. Поскольку эти два случая тривиальны, я не привожу здесь исходный код. Вы можете скачать примеры и изучить их самостоятельно.

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

Диалоговые окна находятся в comdlg32.dll. Чтобы использовать их, вы должны прилинковать comdlg32.lib. Вы создаете эти диалоговые окна вызовом соответствующих функций из библиотеки предопределенных диалоговых окон. Для открытия файлового диалогового окна существует функция GetOрenFileName, для сохранения ― GetSaveFileName, для диалогового окна принтера ― PrintDlg и так далее. Каждая из этих функций берет указатель на структуру в качестве параметра. Вам следует посмотреть их в справочнике Win32 API. В этом туториале я продемонстрирую как создавать и использовать файловое диалоговое окно.

Hиже приведен прототип функции GetOрenFileName.
Assembler
1
 GetOpenFileName proto lpofn:DWORD
Вы можете видеть, что она получает только один параметр, указатель на структуру OPENFILENAME. Возвращаемое значение TRUE показывает, что пользователь выбрал файл, который нужно открыть, FALSE означает обратное. Сейчас мы рассмотрим на структуру OPENFILENAME:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 OPENFILENAME STRUCT
lStructSize DWORD ?
hwndOwner HWND ?
hInstance HINSTANCE ?
lpstrFilter LPCSTR ?
lpstrCustomFilter LPSTR ?
nMaxCustFilter DWORD ?
nFilterIndex DWORD ?
lpstrFile LPSTR ?
nMaxFile DWORD ?
lpstrFileTitle LPSTR ?
nMaxFileTitle DWORD ?
lpstrInitialDir LPCSTR ?
lpstrTitle LPCSTR ?
Flags DWORD ?
nFileOffset WORD ?
nFileExtension WORD ?
lpstrDefExt LPCSTR ?
lCustData LPARAM ?
lpfnHook DWORD ?
lpTemplateName LPCSTR ?
OPENFILENAME ENDS
Давайте рассмотрим значение часто используемых параметров.
  • lStructSize ― размер структуры OPENFILENAME в байтах.
  • hwndOwner ― Хэндл файлового диалогового окна.
  • hInstance ― Хэндл процесса, который создает файловое диалоговое окно.
  • lрstrFilter ― Строка-фильтр состоит из парных строк, разделенных null'ом. Первая строка в каждой паре - это описание. Вторая строка ― это шаблон фильтра. Например:
    Assembler
    1
    2
    
    FilterString db "All Files (*.*)",0, "*.*",0
    db "Text Files (*.txt)",0,"*.txt",0,0
    Отметьте, что шаблон во второй строке каждой пары действительно используется для фильтрации названий файлов. Также отметьте, что вам нужно добавить дополнительный 0 в конце фильтровых строк, чтобы указать конец. Определите, какая пара фильтровых строк будет использоваться при первом отображении файлового диалогового окна. Индекс основывается на единице, то есть первая пара ― 1, вторая ― 2 и так далее. Поэтому в вышеприведенном экземпляре, если мы укажем nFilterIndex как 2, будет использован второй шаблон ― "*.txt".
  • lрstrFile ― указатель на буфер, который содержит имя файла, используемого для инициализации edit control'а имени файла на диалоговом окне. Буфер должен быть длиной по крайней мере 260 байтов. После того, как пользователь выберет файл для открытия, имя файла с полным путем будет сохранено в этом буфере. Вы можете извлечь информацию из него позже.
  • nMaxFile ― размер буфера.
  • lрstrTitle ― указатель на заголовок открытого файлового диалогового окна.
  • Flags ― определите стили и характеристики диалогового окна.
  • nFileOffset ― после того, как пользователь выбрал файл для открытия, этот параметр содержит индекс первого символа собственно названия файла. Например, если полное имя с путем "c:\windows\system\lz32.dll", то этот параметр будет содержать значение 18.
  • nFileExtension ― после того, как пользователь выберет файл для открытия, этот параметр содержит индекс первого символа расширения файла.

ПРАКТИКА СЕСТРА ШИЗОФРЕНИИ
Нижеприведенная программа отображает окно диалога открытия файла, когда пользователь выбирает пункт Filehttps://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrowOрen в меню. Когда пользователь выберет файл в диалоговом окне, программа отобразит сообщение, содержащее полное имя, собственно имя файла и расширение выбранного файла.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
 .386
.model flat,stdcall
option casemap:none
 
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
 
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comdlg32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\comdlg32.lib
 
.const
IDM_OPEN equ 1
IDM_EXIT equ 2
MAXSIZE equ 260
OUTPUTSIZE equ 512
 
.data
ClassName db "SimpleWinClass",0
AppName db "Our Main Window",0
MenuName db "FirstMenu",0
ofn OPENFILENAME
FilterString db "All Files",0,"*.*",0
db "Text Files",0,"*.txt",0,0
buffer db MAXSIZE dup(0)
OurTitle db "-=Our First Open File Dialog Box=-: Choose the file to open",0
FullPathName db "The Full Filename with Path is: ",0
FullName db "The Filename is: ",0
ExtensionName db "The Extension is: ",0
OutputString db OUTPUTSIZE dup(0)
CrLf db 0Dh,0Ah,0
 
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
 
.code
start: invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
 
WinMain proc
hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdL­ine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
 
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,OFFSET MenuName
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USE­DEFAULT,300,200,NULL,NULL,hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
 
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.if ax==IDM_OPEN
mov ofn.lStructSize,SIZEOF ofn
push hWnd
pop ofn.hwndOwner
push hInstance
pop ofn.hInstance
mov ofn.lpstrFilter, OFFSET FilterString
mov ofn.lpstrFile, OFFSET buffer
mov ofn.nMaxFile,MAXSIZE
mov ofn.Flags, OFN_FILEMUSTEXIST or \
OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY
mov ofn.lpstrTitle, OFFSET OurTitle
invoke GetOpenFileName, ADDR ofn
.if eax==TRUE
invoke lstrcat,offset OutputString,OFFSET FullPathName
invoke lstrcat,offset OutputString,ofn.lpstrFile
invoke lstrcat,offset OutputString,offset CrLf
invoke lstrcat,offset OutputString,offset FullName
mov eax,ofn.lpstrFile
push ebx
xor ebx,ebx
mov bx,ofn.nFileOffset
add eax,ebx
pop ebx
invoke lstrcat,offset OutputString,eax
invoke lstrcat,offset OutputString,offset CrLf
invoke lstrcat,offset OutputString,offset ExtensionName
mov eax,ofn.lpstrFile
push ebx
xor ebx,ebx
mov bx,ofn.nFileExtension
add eax,ebx
pop ebx
invoke lstrcat,offset OutputString,eax
invoke MessageBox,hWnd,OFFSET OutputString,ADDR AppName,MB_OK
invoke RtlZeroMemory,offset OutputString,OUTPUTSIZE
.endif
.else
invoke DestroyWindow, hWnd
.endif
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
Разбор полётов
Assembler
1
2
3
4
5
 mov ofn.lStructSize,SIZEOF ofn
push hWnd
pop ofn.hwndOwner
push hInstance
pop ofn.hInstance
Мы заполняем в процедуре члены структуры ofn.
Assembler
1
 mov ofn.lpstrFilter, OFFSET FilterString
FilterString ― это фильтр имен файлов, который мы определяем следующим образом.
Assembler
1
2
FilterString db "All Files",0,"*.*",0
db "Text Files",0,"*.txt",0,0
Заметьте, что все четыре строки заканчиваются нулем. Первая строка - это описание следующей строки. Первая строка является описанием первой. В качестве фильтра мы можем определить все, что захотим. Мы должны добавить дополнительный ноль после последнего фильтра, чтобы указать конец. Hе забудьте сделать это, иначе ваше диалоговое окно поведет себя весьма странно.
Assembler
1
2
 mov ofn.lpstrFile, OFFSET buffer
mov ofn.nMaxFile,MAXSIZE
Мы указываем, где диалоговое окно поместить имена файлов, выбранные пользователем. Учтите, что мы должны указать размер буфера в nMaxFile. Мы можем затем извлечь имя файла из этого буфера.
Assembler
1
2
3
 mov ofn.Flags, OFN_FILEMUSTEXIST or \
OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY
Флаги определяющие характеристики окна.
  • OFN_FILEMUSTEXIST и OFN_PATHMUSTEXIST ― указывают то, что имя файла и путь, который пользователь набирает в edit control'е имени файла, должен существовать.
  • OFN_LONGNAMES ― указывает диалоговому окну показывать длинные имена.
  • OFN_EXPLORER ― указывает на то, что появление диалогового окна должно быть похоже на explorer.
  • OFN_HIDEREADONLY ― прячет неизменяемый checkbox на диалоговом окне. Есть много других флагов, которые вы можете использовать. Проконсультируйтесь с вашим справочником по Win32 API.
Assembler
1
 mov ofn.lpstrTitle, OFFSET OurTitle
Указываем имя диалогового окна.
Assembler
1
 invoke GetOpenFileName, ADDR ofn
Вызов функции GetOрenFileName. Передача указателя на структуру ofn в качестве параметров. В тоже время, диалоговое окно открытия файла отображается на экране. Функция не будет возвращаться, пока пользователь не выберет файл или не нажмет кнопку 'Cancel' или закроет диалоговое окно. Функция возвратит TRUE, если пользователь выбрал файл, в противном случае FALSE.
Assembler
1
2
3
4
5
 .if eax==TRUE
invoke lstrcat,offset OutputString,OFFSET FullPathName
invoke lstrcat,offset OutputString,ofn.lpstrFile
invoke lstrcat,offset OutputString,offset CrLf
invoke lstrcat,offset OutputString,offset FullName
В случае, если пользователь выбирает файл, мы подготавливаем строку вывода, которая будет отображаться в окне сообщения. Мы резервируем блок памяти в переменной OutрutString и затем используем API-функцию, lstrcat, чтобы соединить обе строки. Чтобы разместить строку в несколько рядов, мы должны использовать символы переноса каретки.
Assembler
1
2
3
4
5
6
7
 mov eax,ofn.lpstrFile
push ebx
xor ebx,ebx
mov bx,ofn.nFileOffset
add eax,ebx
pop ebx
invoke lstrcat,offset OutputString,eax
Вышеприведенные строки требуют некоторых объяснений. nFileOffset содержит индекс в ofn.lрstrFile. Hо вы не можете сложить их в месте, так размерности этих переменных разные. Поэтому я поместил значение nFileOffset в младшее слово ebx'а и сложил его со значением lpstrFile'а.
Assembler
1
 invoke MessageBox,hWnd,OFFSET OutputString,ADDR AppName,MB_OK
Мы отображаем строку в окне сообщения.
Assembler
1
 invoke RtlZerolMemory,offset OutputString,OUTPUTSIZE
Мы должны очистить OutрutString перед тем, как заполнить его другой строкой. Поэтому мы используем функцию RtlZeroMemory для этого.
_______________________________________
© Iczelion, пер. Aquila.
Вложения
Тип файла: zip tut11-1.zip (4.0 Кб, 138 просмотров)
Тип файла: zip tut11-2.zip (3.9 Кб, 125 просмотров)
Тип файла: zip tut11-3.ZIP (3.8 Кб, 117 просмотров)
3
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
14.01.2013, 07:06

ПК перезагружается сам по себе
Здравствуйте, у меня есть проблема с компьютером. По заголовку вы поняли, что мой компьютер выключатся сам по себе, но это если очень...

А не навред(ж)ю ли я сам себе?
Когда регистрируешься то лучше через каждые 20-30 каталогов менять описание сайта и ключевиков! ТАК!!? B-) вот какой вопрос возник: ...

ПК сам по себе перезагружается
Добрый вечер,у меня такая же проблема,сам себе перезагружается,без синего экрана,без зависаний,просто раз и потух на доли...

Выключается сам по себе
После очистки компа от пыли (снимал кулер, проц, оперативку и видюху, так же менял термопасту). Я нажимаю на кнопку Power, происходит...

Вырубается ПК сам по себе
Нужна помощь в проблеме. Пару недель назад кулер в БП сильно шумел время от времени, я решил почистить пк. Почистил ПК от пыли полностью,...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Жизня: рисунок укладки багажа, сделанный клодом
anaschu 21.08.2026
Сделал 15 снимков, он по снимкам сделал схему.
Был там один разговор по поводу свободы в материальном мире.
kumehtar 19.08.2026
Суть: рассматривается живое существо, оказавшееся внутри довольно странной системы (этого мира) и пытающееся обустроить в ней свой кусок пространства. Жизнь действительно предъявляет каждому. . .
Когда логика программы не спасает от человеческих ошибок
Maks 18.08.2026
В последнее время всё чаще и чаще сталкиваюсь с таким явлением, как абсолютная невнимательность (или глупость) пользователей. Проявляется это чаще всего на работе в коллективе. Допустим, человек с. . .
Лето уходит
kumehtar 17.08.2026
Мысли в слух
kumehtar 17.08.2026
Забавно, насколько сейчас стала доступна информация. Например о магии, духовном развитии, медитациях, и других подобных направлениях, ранее зачастую тайных, передаваемых от учителя к ученику. Хотя. . .
Перемещение строк из ТЧ в другой документ с учетом текущего пробега
Maks 17.08.2026
Реализация из решения ниже выполнена на примере нетипового документа "Автозапчасти", с ТЧ "Шины". За основу взят алгоритм отсюда: https:/ / www. cyberforum. ru/ blogs/ 359708/ 10838. html Задача: . . .
Саморегулирующийся социальный контракт для сервера cross-section.
Hrethgir 14.08.2026
С кодом конечно таких глубоких размышлений пока не было, впрочем я уже привык к алгоритмизации. Суть предмета записи: снова в диалоге с нейросетью (я взял пока себе ник для учётки админа - Rector). . . .
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru