Форум программистов, компьютерный форум, киберфорум
Наши страницы

Assembler, MASM, TASM

Войти
Регистрация
Восстановить пароль
 
 
Mikl___
Заблокирован
Автор FAQ
15.01.2013, 10:33  [ТС] #46
Win32 API. Урок 14a. Процесс
Скачайте пример здесь.
Практика ― сестра шизофрении
Кликните здесь для просмотра всего текста
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
.586p
.model tiny
include windows.inc
;for WinXP - 1153 bytes
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
exebase         equ 400000h
MI_PROCESS_CREATE   equ 0
MI_PROCESS_TERMINATE    equ 1
MI_EXIT         equ 2
IDC_MENU        equ 30
main:
include capito_res.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,IDC_MENU,edi
      ; +--------------------------+
      ; | creating the main window |
      ; +--------------------------+
    push ebx
    push esi
    shl esi,9
    invoke CreateWindowEx,WS_EX_CLIENTEDGE,edi,edi,\
    WS_OVERLAPPEDWINDOW or WS_VISIBLE,esi,esi,300,200,ebx,ebx
    invoke  GetMenu,eax;[hWnd]
    mov hMenu+exebase,eax
    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+8h
uMsg        equ ebp+0Ch
wParam      equ ebp+10h
lParam      equ ebp+14h
    enter sizeof(STARTUPINFO),0
    mov eax,[uMsg]
    mov edi,offset processInfo+exebase
    mov esi,offset proExitCode+exebase
    dec eax
    dec eax; cmp uMsg,WM_DESTROY
    je wmDESTROY
    sub eax,WM_COMMAND-WM_DESTROY; cmp uMsg,WM_PAINT
    je wmCOMMAND
    sub eax,WM_INITMENUPOPUP-WM_COMMAND;cmp     [uMsg],WM_INITMENUPOPUP
    je wmINITMENUPOPUP
    leave
    jmp DefWindowProc+exebase
 
wmINITMENUPOPUP: invoke GetExitCodeProcess,[edi+PROCESS_INFORMATION.hProcess],esi
    xchg eax,ecx
    jecxz a2;GetExitCodeProcess_TRUE
    cmp dword ptr [esi],STILL_ACTIVE;cmp     [proExitCode],STILL_ACTIVE
    jne a2;     GetExitCodeProcess_STILL_ACTIVE
    push ebx
    push MF_GRAYED
    jmp a5
a2: push MF_GRAYED
    push ebx;MF_ENABLED
a5: invoke EnableMenuItem,[hMenu+exebase],ebx;MI_PROCESS_CREATE
    invoke EnableMenuItem,[hMenu+exebase],MI_PROCESS_TERMINATE
    jmp wmBYE
wmCOMMAND: mov ax,[wParam]
    cmp  dword ptr [lParam],ebx
    jne  wmBYE
    jmp dword ptr [menu_handlers+eax*4+exebase]
PROCESS_CREATE: cmp [edi+PROCESS_INFORMATION.hProcess],ebx
    je pi_hProcess_IS_0;a3;
    invoke  CloseHandle,[edi+PROCESS_INFORMATION.hProcess]
    mov [edi+PROCESS_INFORMATION.hProcess],ebx
pi_hProcess_IS_0: mov esi,esp;[progStartInfo]
    invoke GetStartupInfo,esi
    invoke CreateProcess,offset progName+exebase,ebx,ebx,ebx,ebx,\
    NORMAL_PRIORITY_CLASS,ebx,ebx,esi,edi
    invoke CloseHandle,[edi+PROCESS_INFORMATION.hThread]
    jmp wmBYE
TERMINATE: invoke  GetExitCodeProcess,[edi+PROCESS_INFORMATION.hProcess],esi;proExitCode
    cmp dword ptr [esi],STILL_ACTIVE
    jne proExitCode_NOT_STILL_ACTIVE;a4;
    invoke  TerminateProcess,[edi+PROCESS_INFORMATION.hProcess],ebx
proExitCode_NOT_STILL_ACTIVE: invoke  CloseHandle,[edi+PROCESS_INFORMATION.hProcess]
    mov [edi+PROCESS_INFORMATION.hProcess],ebx;0
    jmp wmBYE
EXIT:   invoke  DestroyWindow,dword ptr [hWnd]
wmBYE:  leave
    retn 10h
wmDESTROY:  invoke ExitProcess,ebx
;--------данные------------------------------
wTitle    db   'Iczelion Tutorial #14:Process in MASM',0;name of our window
menu_handlers  dd PROCESS_CREATE+exebase, TERMINATE+exebase, EXIT+exebase
progName    db  'tut02.exe',0
;-------------------------------------------------------------------------------------------
MFR_END     equ 80h
MFR_POPUP   equ 1
MFT_STRING  equ 0
MFS_ENABLED equ 0
MFT_SEPARATOR   equ 800h
RT_MENU     equ 4
 
POPUP       equ 0010h
MENUBREAK       equ 0040h
ENDMENU     equ 0080h
DS_SETFONT  equ 0040h
align 4
resource:       
Characteristics0    dd 0
TimeDateStamp0      dd 0
MajorVersion0       dw 0
MinorVersion0       dw 0
NumberOfNamedEntries0   dw 0
NumberOfIdEntries0  dw 1
dw RT_MENU,0
dw x-resource,8000h
x:
Characteristics1    dd 0
TimeDateStamp1      dd 0
MajorVersion1       dw 0
MinorVersion1       dw 0
NumberOfNamedEntries1   dw 0
NumberOfIdEntries1  dw 1
dw IDC_MENU,0
dw x1-resource,8000h
x1:
Characteristics2    dd 0
TimeDateStamp2      dd 0
MajorVersion2       dw 0
MinorVersion2       dw 0
NumberOfNamedEntries2   dw 0
NumberOfIdEntries2  dw 1
dw 40Ah,0,x2-resource,0
x2:;struct _IMAGE_RESOURCE_DATA_ENTRY
OffsetToData0   dd menu
Size0       dd end_menu-menu
CodePage0   dd 0
Reserved0   dd 0
menu dw 0,0,POPUP or MFR_END
du <&Process>
dw MFT_STRING or MFS_ENABLED,MI_PROCESS_CREATE
du <&Create Process>
dw MFT_STRING or MFS_ENABLED or MF_GRAYED,MI_PROCESS_TERMINATE
du <&Terminate Process>
dw MENUBREAK,0,0;NOTEXT
dw MFT_STRING or MFS_ENABLED or MFR_END,MI_EXIT
du <&Exit>
end_menu:
end_resource:
;---------------------------------------------------------------------
import:         
hMenu       dd 0    ;menu handle
proExitCode dd 0    ;process exit code
dd 0,user32_dll  ,user_table
dd 0,0,0,kernel32_dll,kernel_table
processInfo PROCESS_INFORMATION <0>;dd 0,0,0,0
user_table:
RegisterClass       dd _RegisterClass
CreateWindowEx          dd _CreateWindowEx
GetMessage              dd _GetMessage
DispatchMessage         dd _DispatchMessage
DefWindowProc           dd _DefWindowProc
DestroyWindow           dd _DestroyWindow
GetMenu         dd _GetMenu
EnableMenuItem      dd _EnableMenuItem,0
 
kernel_table:
GetExitCodeProcess  dd _GetExitCodeProcess
GetStartupInfo      dd _GetStartupInfo
CreateProcess       dd _CreateProcess
TerminateProcess    dd _TerminateProcess
CloseHandle     dd _CloseHandle
ExitProcess             dd _ExitProcess,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'
_DestroyWindow      db 0,0,"DestroyWindow"
_GetMenu        db 0,0,'GetMenu'
_EnableMenuItem     db 0,0,'EnableMenuItem',0
user32_dll      db 'user32'
_GetExitCodeProcess db 0,0,'GetExitCodeProcess'
_GetStartupInfo     db 0,0,'GetStartupInfoA'
_CreateProcess      db 0,0,'CreateProcessA'
_TerminateProcess   db 0,0,'TerminateProcess'
_CloseHandle        db 0,0,'CloseHandle'
_ExitProcess        db 0,0,'ExitProcess',0
kernel32_dll        db 'kernel32'
end_import:
end main
_____________________________________
© Mikl___ 2013
3
Вложения
Тип файла: zip tut14a.zip (4.4 Кб, 83 просмотров)
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
15.01.2013, 10:33
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Сам себе Iczelion (Assembler):

создать програму которая содержит в себе команды обработки строк языка асемблер - Assembler
Создать програму которая содержит в себе команды обработки строк языка асемблер. Выполнить введение строки из 40 символов. Слова в строке...

Регистры eax & edx сами по себе принимают непонятные значения - Assembler
Задача программы сортировка массива состоящего из 5ти двойных слов. Ассемблер конечно сортирует, но только максимум 3 первых числа, а 2...

Создать программу, которая содержит в себе элементы цикла и разветвления - Assembler
в массиве из n=10 элементов найти наибольшее число

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

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

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

131
Mikl___
Заблокирован
Автор FAQ
15.01.2013, 11:09  [ТС] #47
Win32 API. 15. Треды (ветви)
Мы узнаем, как создать мультитредную программу. Мы также изучим методы, с помощью которых треды могут общаться друг с другом.

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

Треды выполняются в том же процесс, поэтому они имеют доступ ко всем ресурсам процесса: глобальным переменным, хэндлам и т.д. Тем не менее, каждый тред имеет свой собственный стек, так что локальные переменные в каждом треде приватны. Каждый тред также имеет свой собственный набор регистров, поэтому когда Windows переключается на другой тред, предыдущий "запоминает" свое состояние и может "восстановить" его, когда он снова получает контроль. Это обеспечивается внутренними средствами Windows. Мы можем поделить треды на две категории:
  1. Тред интерфейса пользователя: тред такого типа создает свое собственное окно, поэтому он получает оконные сообщения. Он может отвечать пользователю с помощью своего окна. Этот тип тредов действуют согласно Win16 Mutex правилу, которое позволяет только один тред пользовательского интерфейсав 16-битном пользовательском и gdi-ядре. Пока один подобный тред выполняет код 16-битного пользовательского и gdi-ядра, другие UI треды не могут использовать сервисы этого ядра. Заметьте, что этот Win16 Mutex свойственен Windows 9x, так как его функции обращаются к 16-битному коду. В Windows NT нет Win16 Mutex'а, поэтому треды пользовательского интерфейса под NT работают более плавно, чем под Windows 95.
  2. рабочий тред: Этот тип тредов не создает окно, поэтому он не может принимать какие-либо windows-сообщения. Он существует только для того, чтобы делать предназначенную ему работу на заднем фоне (согласно своему названию).
Я советую следующую стратегию при использовании мультитредовых способностей Win32: позвольте основному треду делать все, что связанно с пользовательским интерфейсом, а остальным делать тяжелую работу в фоновом режиме. В этому случае, основной тред ― Правитель, другие треды ― его помощники. Правитель поручает им определенные задания, в то время как сам общается с публикой. Его помощники послушно выполняют работу и докладывают об этом Правителю. Если бы Правитель делал всю работу сам, он бы не смог уделять достаточно внимания народу или прессе. Это похоже на окно, которое занято продолжительной работой в основном треде: оно не отвечает пользователю, пока работа не будет выполнена. Такая программа может быть улучшена созданием дополнительного треда, который возьмет часть работы на себя и позволит основной ветви отвечать на команды пользователя.
Мы можем создать тред с помощью вызова функции CreateThread, которая имеет следующий синтаксис:
Кликните здесь для просмотра всего текста
Assembler
1
2
   CreateThread proto lpThreadAttributes:DWORD,dwStackSize:DWORD,lpStartAddress:DWORD,\
              lpparameter:DWORD,dwCreationFlags:DWORD, lpThreadId:DWORD
Функция CreateThread похожа на CreateProcess.
  • lpThreadAttributes ― Вы можете использовать NULL, если хотите, чтобы у треда были установки безопасности по умолчанию.
  • dwStackSize ― укажите размер стека треда. Если вы хотите, чтобы тред имел такой же размер стека, как и у основного, используйте NULL в качестве параметра.
  • lрStartAddress ― адрес функции треда. Эта функция будет выполнять предназначенную для треда работу. Эта функция должна получать один и только один 32-битный параметр и возвращать 32-битное значение.
  • lрParametr ― Параметр, который вы хотите передать функции треда.
  • dwCreationFlags ― 0 означает, что тред начинает выполняться сразу же после его создания. Для обратного можно использовать флаг CREATE_SUSPEND.
  • lpThreadId ― CreateThread поместит сюда ID созданного треда.
Если вызов CreateThread прошел успешно, она возвращает хэндл созданного треда, в противном случае она возвращает NULL.


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


Когда функция треда возвращается (с помощью инструкции ret) Windows косвенно вызывает ExitThread для функции треда. Вы можете сами вызвать ExitThread, но в этом немного смысла.
Вы можете получить код выхода треда с помощью функции GetExitCodeThread.


Если вы хотите прервать тред из другого треда, вы можете вызвать функцию TerminateThread. Hо вы должны использовать эту функцию только в экстремальных условиях, так как эта функция немедленно прерывать тред, не давая ему шанса произвести необходимую чистку за собой.

Теперь давайте рассмотрим методы коммуникации между тредами. Вот три из них:
  • Использование глобальных переменных
  • Windows-сообщения
  • События
Треды разделяют ресурсы процесса, включая глобальные переменные, поэтому треды могут использовать их для того, чтобы взаимодействовать друг с другом. Тем не менее, этот метод должен использоваться осторожно.
Синхронизацию нужно внимательно спланировать. Hапример, если два треда используют одну и ту же структуру из 10 членов, что произойдет, если Windows вдруг передаст управление от одного треда другому, когда структура обновлена еще только наполовину. Другой тред получит неправильную информацию!
Hе сделайте никакой ошибки, мультитредовые программы тяжелее отлаживать и поддерживать. Этот тип багов случается непредсказуемо и их очень трудно отловить.
Вы также можете использовать windows-сообщения, чтобы осуществлять взаимодействие между тредами. Если все треды имеют юзерский интерфейс, то нет проблем: этот метод может использоваться для двухсторонней коммуникации. Все, что вам нужно сделать ― это определить один или более дополнительных windows-сообщений, которые будут использоваться тредами.
Вы определяете сообщение, используя значение WM_USER как базовое, например так:
Кликните здесь для просмотра всего текста
Assembler
1
           WM_MYCUSTOMMSG equ WM_USER+100h
Windows не использует сообщения с номером выше WM_USER, поэтому мы можем использовать значение WM_USER и выше для наших собственных сообщений.
Если один из тредов имеет пользовательский интерфейс, а другой является рабочим, вы не можете использовать данный метод для двухстороннего общения, так как у рабочего треда нет своего окна, а следовательно и очереди сообщений. Вы можете использовать следующие схемы:
  • Тред с пользовательским интерфейсом http://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrow глобальная переменная(ные) http://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrow рабочий тред
  • рабочий тред http://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrow windows-сообщение http://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrow Тред с пользовательским интерфейсом

Фактически, мы будем использовать этот метод в нашем примере.


Последний метод, используемый для коммуникации ― это объект события. Вы можете рассматривать его как своего рода флаг. Если объект события "не установлен", значит тред спит. Когда объект события "установлен", Windows "пробуждает" тред и он начинает выполнять свою работу.
Практика ― сестра шизофрении
Вам следует скачать zip-файл с примером запустить thread1.exe. Hажмите на пункт меню "Savage Calculation". Это даст команду программе выполнить "add eax,eax" 600.000.000 раз. Заметьте, что во время этого времени вы не сможете ничего сделать с главным окном: вы не сможете его двигать, активировать меню и т.д. Когда вычисление закончится, появится окно с сообщением. После этого окно будет нормально реагировать на ваши команды.


Чтобы избежать подобного неудобства для пользователя, мы должны поместить процедуру вычисления в отдельный рабочий тред и позволить основному треду продолжать взаимодействие с пользователем. Вы можете видеть, что хотя основное окно отвечает медленнее, чем обычно, оно все же делает это.
Кликните здесь для просмотра всего текста
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
   .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
 
   .const
   IDM_CREATE_THREAD equ 1
   IDM_EXIT equ 2
   WM_FINISH equ WM_USER+100h
 
   .data
   ClassName db "Win32ASMThreadClass",0
   AppName  db "Win32 ASM MultiThreading Example",0
   MenuName db "FirstMenu",0
   SuccessString db "The calculation is completed!",0
 
   .data?
   hInstance HINSTANCE ?
   CommandLine LpSTR ?
   hwnd HANDLE ?
   ThreadID DWORD ?
 
   .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
 
       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_USEDEFAULT,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 lparam==0
               .if ax==IDM_CREATE_THREAD
                   mov  eax,OFFSET Threadproc
                   invoke CreateThread,NULL,NULL,eax,0,ADDR ThreadID
                   invoke CloseHandle,eax
               .else
                   invoke DestroyWindow,hWnd
               .endif
           .endif
       .ELSEIF uMsg==WM_FINISH
           invoke MessageBox,NULL,ADDR SuccessString,ADDR AppName,MB_OK
       .ELSE
           invoke DefWindowproc,hWnd,uMsg,wparam,lparam
           ret
       .ENDIF
       xor    eax,eax
       ret
   Wndproc endp
 
 
   Threadproc proc uses ecx param:DWORD
           mov  ecx,600000000
   Loop1:  add  eax,eax
           dec  ecx
           jz   Get_out
           jmp  Loop1
   Get_out:  invoke postMessage,hwnd,WM_FINISH,NULL,NULL
           ret
   Threadproc endp
   end start
Разбор полетов
Основную программу пользователь воспринимает как обычное окно с меню. Если пользователь выбирает в последнем пункт "Создать тред", программа создает тред:
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
               .if ax==IDM_CREATE_THREAD
                   mov  eax,OFFSET Threadproc
                   invoke CreateThread,NULL,NULL,eax,\
                                           NULL,0,ADDR ThreadID
                   invoke CloseHandle,eax
Вышеприведенная функция создает тред, который запустит процедуру под названием Threadрroc параллельно с основным тредом. Если вызов функции прошел успешно, CreateThread немедленно возвращается и Threadproc начинает выполняться. Так как мы не используем хэндл треда, нам следует закрыть его, чтобы не допустить бессмысленное расходование памяти.
Закрытие хэндла не прерывает сам тред. Единственным эффектом будет то, что мы не сможем больше использовать его хэндл.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
   ThreadProc proc uses ecx param:DWORD
           mov  ecx,600000000
Loop1: add  eax,eax
           dec  ecx
           jz   Get_out
           jmp  Loop1
Get_out: invoke postMessage,hwnd,WM_FINISH,NULL,NULL
           ret
   ThreadProc endp
Как вы можете видеть ThreadProc выполняет подсчет, требующий некоторого времени, и когда она заканчивает его, она отправляет сообщение WM_FINISH основному окну. WM_FINISH ― это наше собственное сообщение, определенное следующим образом:
Кликните здесь для просмотра всего текста
Assembler
1
       WM_FINISH equ WM_USER+100h
Вам не обязательно добавлять к WM_USER 100h, но будет лучше сделать это. Сообщение WM_FINISH имеет значение только в пределах нашей программы. Когда основное окно получает WM_FINISH, она реагирует на это показом окна с сообщением о том, что подсчет закончен.

Вы можете создать несколько тредов, выбрав "Create Thread" несколько раз. В этом примере применяется односторонняя коммуникация, то есть только тред может уведомлять основное окно о чем-либо. Если вы хотите, что основной тред слал команды рабочему, вы должны сделать следующее:
  • добавить пункт меню "Kill Thread".
  • добавить глобальную переменную, используемую в качестве флага. TRUE=остановить тред, FALSE=продолжить тред.
  • Изменить ThreadProc так, чтобы та проверяла в цикле значение флага.
Когда пользователь выберет "Kill Thread", основная программа установит флаг в TRUE. Когда Threadproc видит, что значение флага равно TRUE, она выходит из цикла и возвращается, что заканчивает действие треда.
_____________________________________
© Iczelion, пер. Aquila.
3
Вложения
Тип файла: zip tut15.zip (4.5 Кб, 62 просмотров)
Mikl___
Заблокирован
Автор FAQ
15.01.2013, 11:28  [ТС] #48
Win32 API. 15a. Треды (ветви)
Скачайте пример здесь.

Вам следует скачать zip-файл с примером запустить thread1.exe. Hажмите на пункт меню "Savage Calculation". Это даст команду программе выполнить "add eax,eax" 600.000.000 раз. Заметьте, что во время этого времени вы не сможете ничего сделать с главным окном: вы не сможете его двигать, активировать меню и т.д. Когда вычисление закончится, появится окно с сообщением. После этого окно будет нормально реагировать на ваши команды.
Чтобы избежать подобного неудобства для пользователя, мы должны поместить процедуру вычисления в отдельный рабочий тред и позволить основному треду продолжать взаимодействие с пользователем. Вы можете видеть, что хотя основное окно отвечает медленнее, чем обычно, оно все же делает это.
Кликните здесь для просмотра всего текста
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
.586p
.model tiny
include windows.inc
;for WinXP - 948 bytes
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
exebase         equ 400000h
IDM_CREATE_THREAD   equ 0
IDM_EXIT        equ 1
IDC_MENU        equ 30
WM_FINISH       equ WM_USER+100h
main:
include capito_res.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,IDC_MENU,edi
      ; +--------------------------+
      ; | creating the main window |
      ; +--------------------------+
    push ebx
    push esi
    shl esi,9
    invoke CreateWindowEx,WS_EX_CLIENTEDGE,edi,edi,\
    WS_OVERLAPPEDWINDOW or WS_VISIBLE,esi,esi,450,200,ebx,ebx
    mov hWnd+exebase,eax
    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 dword ptr [ebp+8h]
uMsg        equ dword ptr [ebp+0Ch]
wParam      equ dword ptr [ebp+10h]
lParam      equ dword ptr [ebp+14h]
    enter sizeof(STARTUPINFO),0
    mov eax,uMsg
    dec eax
    dec eax; cmp uMsg,WM_DESTROY
    je wmDESTROY
    sub eax,WM_COMMAND-WM_DESTROY
    je wmCOMMAND
    sub eax,WM_FINISH-WM_COMMAND
    je wmFINISH
    leave
    jmp DefWindowProc+exebase
wmFINISH: invoke MessageBox,ebx,offset wTitle+exebase,offset wTitle+exebase,ebx
    jmp wmBYE
wmCOMMAND: movzx eax,word ptr wParam
    cmp lParam,ebx
    jne  wmBYE
    jmp dword ptr [menu_handlers+eax*4+exebase]
CREATE_THREAD: invoke CreateThread,ebx,ebx,offset ThreadProc+exebase,ebx,\
        NORMAL_PRIORITY_CLASS,offset ThreadID+exebase
    invoke  CloseHandle,eax
    jmp wmBYE
EXIT:   invoke  DestroyWindow,hwnd
wmBYE:  leave
    retn 10h
wmDESTROY:  invoke ExitProcess,ebx
menu_handlers dd CREATE_THREAD+exebase,EXIT+exebase
ThreadProc proc
    mov ecx,30000000
@@:     add eax,eax
    loop @b
    invoke SendMessage,hWnd+exebase,WM_FINISH,ebx,ebx
    ret
ThreadProc ENDP
;--------данные------------------------------
wTitle db 'Iczelion Tutorial #15: Multithreading Programming',0
;--------------------------------------------------------------
MFR_END     equ 80h
MFR_POPUP   equ 1
MFT_STRING  equ 0
MFS_ENABLED equ 0
MFT_SEPARATOR   equ 800h
RT_MENU     equ 4
 
POPUP       equ 0010h
MENUBREAK       equ 0040h
ENDMENU     equ 0080h
DS_SETFONT  equ 0040h
align 4
resource:       
Characteristics0    dd 0
TimeDateStamp0      dd 0
MajorVersion0       dw 0
MinorVersion0       dw 0
NumberOfNamedEntries0   dw 0;количество ресурсов с именами
NumberOfIdEntries0  dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является типом ресурса
dw RT_MENU,0;номер типа ресурса
dw x-resource,8000h; если во 2-ом слове установлен старший бит - есть ссылка 
;на оглавление второго уровня. В 1-ом слове смещение второго оглавления 
;относительно начала раздела ресурсов
x:
Characteristics1    dd 0
TimeDateStamp1      dd 0
MajorVersion1       dw 0
MinorVersion1       dw 0
NumberOfNamedEntries1   dw 0;количество ресурсов с именами
NumberOfIdEntries1  dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является идентификатором меню
dw IDC_MENU,0
dw x1-resource,8000h; если во 2-ом слове установлен старший бит - есть ссылка 
;на оглавление третьего уровня. В 1-ом слове смещение третьего оглавления 
;относительно начала раздела ресурсов
x1:
Characteristics2    dd 0
TimeDateStamp2      dd 0
MajorVersion2       dw 0
MinorVersion2       dw 0
NumberOfNamedEntries2   dw 0;количество ресурсов с именами
NumberOfIdEntries2  dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является идентификатором языка, который 
;используется данным ресурсом 16 * SUBLANG_ + LANG_
dw 40Ah,0,x2-resource,0
x2:;struct _IMAGE_RESOURCE_DATA_ENTRY
OffsetToData0   dd menu
Size0       dd end_menu-menu
CodePage0   dd 0
Reserved0   dd 0
menu dw 0,0,POPUP or MFR_END
du <&Process>
dw MFT_STRING or MFS_ENABLED,IDM_CREATE_THREAD
du <&Create Thread>
dw MENUBREAK,0,0;NOTEXT
dw MFT_STRING or MFS_ENABLED or MFR_END,IDM_EXIT
du <&Exit>
end_menu:
end_resource:
;---------------------------------------------------------------------
import:
ThreadID    dd 0    ;process exit code
hWnd        dd 0
dd 0,user32_dll
dd user_table
dd 0,0,0,kernel32_dll
dd kernel_table
dd 0,0,0,0
user_table:
CreateWindowEx          dd _CreateWindowEx
DefWindowProc           dd _DefWindowProc
DestroyWindow           dd _DestroyWindow
DispatchMessage         dd _DispatchMessage
GetMessage              dd _GetMessage
MessageBox              dd _MessageBox
SendMessage     dd _SendMessage
RegisterClass       dd _RegisterClass,0
kernel_table:
CreateThread        dd _CreateThread
CloseHandle     dd _CloseHandle
ExitProcess             dd _ExitProcess
                        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'
_DestroyWindow      db 0,0,"DestroyWindow"
_SendMessage        db 0,0,'SendMessageA'
_MessageBox     db 0,0,'MessageBoxA',0
user32_dll      db 'user32'
_CreateThread       db 0,0,'CreateThread'
_CloseHandle        db 0,0,'CloseHandle'
_ExitProcess        db 0,0,'ExitProcess',0
kernel32_dll        db 'kernel32'
end_import:
end main
_____________________________________
© Mikl___ 2013
3
Вложения
Тип файла: zip tut15a.zip (3.8 Кб, 55 просмотров)
Mikl___
Заблокирован
Автор FAQ
15.01.2013, 12:25  [ТС] #49
Win32 API. Урок 16. Объект события
Мы изучим, что такое объект события и как использовать его в мультитредной программе.
Скачайте пример здесь.
Теория ― мать склероза
В предыдущем туториале я продемонстрировал, как треды взаимодействуют друг с другом через собственные windows-сообщения. Я пропустил два других метода: глобальная переменная и объект события. В этом туториале мы используем оба.

Объект события ― это что-то вроде переключателя: у него есть только два состояния: вкл и выкл. Вы создаете объект события и помещаете его в коде соответствующего треда, где наблюдаете за состояние объекта. Если объект события выключен, ждущие его треды "спать". В подобном состоянии треды мало загружают CPU.

Вы можете создать объект события, вызвав функцию CreateEvent, которая имеет следующий синтаксис:
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
   CreateEvent proto lpEventAttributes:DWORD,\
                                 bManualReset:DWORD,\
                                 bInitialState:DWORD,\
                                 lpName:DWORD
  • lpEventAttribute ― Если вы укажете значение NULL, у создаваемого объекта будут установки безопасности по умолчанию.
  • bManualReset ― Если вы хотите, чтобы Windows автоматически переключал объект события в "выключено", вы должны присвоить этому параметру значение FALSE. Иначе вам надо будет выключить объект вручную с помощью вызова ResetEvent.
  • bInitialStae ― Если вы хотите, чтобы объект события при создании был установлен в положение "включено", укажите TRUE в качестве данного параметра, в противном случае объект события будет установлен в положение "выключен".
  • lpName ― Указатель на ASCIIZ-строку, которая будет именем объекта события. Это имя будет использоваться, когда вы захотите вызвать OpenEvent.
Если вызов прошел успешно, CreateEvent возвратит хэндл на созданный объект события. В противном случае она возвратит NULL.

Вы можете изменять состояние объекта события с помощью двух API-функций:
SetEvent и ResetEvent. Функция SetEvent устанавливает объект события в положение "включено". ResetEvent делает обратное.

Когда объект события создан, вы должны поместить вызов функции WaitForSingleObject в тред, который должен следить за состоянием объекта события. Эта функция имеет следующий синтаксис:
Assembler
1
   WaitForSingleObject proto hObject:DWORD, dwTimeout:DWORD
  • hObject ― Хэндл одного из синхронизационных объектов. Объект события ― это вид синхронизационного события.
  • dwTimeout ― Указывает в миллисекундах время, которое эта функция будет ждать, пока объект события не перейдет во включенное состояние. Если указанное время пройдет, а объект события все еще выключен, WaitForSingleObject вернет управление. Если вы хотите, чтобы функция наблюдала за объектом бесконечно, вы должны указать значение INFINITE в качестве этого параметра.
Практика ― сестра шизофрении
Нижеприведенный пример отображает окно, ожидающее пока пользователь не выберет какую-либо команду из меню. Если пользователь нажмет на "run thread", тред начнет подсчет. Когда счет закончится, появится сообщение, информирующее пользователя о том, что работа выполнена. Во время того, как проводится подсчет, пользователь может выбрать команду "stop thread", чтобы остановить тред.
Кликните здесь для просмотра всего текста
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
   .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
 
   .const
   IDM_START_THREAD equ 1
   IDM_STOp_THREAD equ 2
   IDM_EXIT equ 3
   WM_FINISH equ WM_USER+100h
 
   .data
   ClassName db "Win32ASMEventClass",0
   AppName  db "Win32 ASM Event Example",0
   MenuName db "FirstMenu",0
   SuccessString db "The calculation is completed!",0
   StopString db "The thread is stopped",0
   EventStop BOOL FALSE
 
   .data?
   hInstance HINSTANCE ?
   CommandLine LpSTR ?
   hwnd HANDLE ?
   hMenu HANDLE ?
   ThreadID DWORD ?
   ExitCode DWORD ?
   hEventStart HANDLE ?
 
   .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
 
       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_USEDEFAULT,300,200,NULL,NULL,hInst,NULL
       mov   hwnd,eax
       invoke ShowWindow, hwnd,SW_SHOWNORMAL
       invoke UpdateWindow, hwnd
       invoke GetMenu,hwnd
       mov  hMenu,eax
       .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_CREATE
           invoke CreateEvent,NULL,FALSE,FALSE,NULL
           mov  hEventStart,eax
           mov  eax,OFFSET Threadproc
           invoke CreateThread,NULL,NULL,eax,NULL,0,ADDR ThreadID
           invoke CloseHandle,eax
       .ELSEIF uMsg==WM_DESTROY
           invoke postQuitMessage,NULL
       .ELSEIF uMsg==WM_COMMAND
           mov eax,wparam
           .if lparam==0
               .if ax==IDM_START_THREAD
                   invoke SetEvent,hEventStart
                   invoke EnableMenuItem,hMenu,IDM_START_THREAD,MF_GRAYED
                   invoke EnableMenuItem,hMenu,IDM_STOp_THREAD,MF_ENABLED
               .elseif ax==IDM_STOP_THREAD
                   mov  EventStop,TRUE
                   invoke EnableMenuItem,hMenu,IDM_START_THREAD,MF_ENABLED
                   invoke EnableMenuItem,hMenu,IDM_STOP_THREAD,MF_GRAYED
               .else
                   invoke DestroyWindow,hWnd
               .endif
           .endif
       .ELSEIF uMsg==WM_FINISH
           invoke MessageBox,NULL,ADDR SuccessString,ADDR AppName,MB_OK
       .ELSE
           invoke DefWindowproc,hWnd,uMsg,wparam,lparam
           ret
   .ENDIF
       xor    eax,eax
       ret
   Wndproc endp
   Threadproc proc uses ecx param:DWORD
           invoke WaitForSingleObject,hEventStart,INFINITE
           mov  ecx,600000000
           .WHILE ecx!=0
                   .if EventStop!=TRUE
                           add  eax,eax
                           dec  ecx
                   .else
                           invoke MessageBox,hwnd,ADDR StopString,ADDR AppName,MB_OK
                           mov  EventStop,FALSE
                           jmp Threadproc
                   .endif
           .ENDW
           invoke PostMessage,hwnd,WM_FINISH,NULL,NULL
           invoke EnableMenuItem,hMenu,IDM_START_THREAD,MF_ENABLED
           invoke EnableMenuItem,hMenu,IDM_STOp_THREAD,MF_GRAYED
           jmp   Threadproc
           ret
   Threadproc endp
   end start
Анализ:
В этом примере я демонстрирую другую технику работы с тредами.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
       .IF uMsg==WM_CREATE
           invoke CreateEvent,NULL,FALSE,FALSE,NULL
           mov  hEventStart,eax
           mov  eax,OFFSET Threadproc
           invoke CreateThread,NULL,NULL,eax,\
                                NULL,0,ADDR ThreadID
           invoke CloseHandle,eax
Вы можете видеть, что я создал объект события и тред во время обработки сообщения WM_CREATE. Я создаю объект события, установленного в состояние "выключено" и обладающего свойством автоматического выключения. После того, как объект события создан, я создаю тред. Тем не менее, тред не начинает выполняться немедленно, так как он ждет, пока не включится объект события:
Кликните здесь для просмотра всего текста
Assembler
1
2
3
   Threadproc proc uses ecx param:DWORD
           invoke WaitForSingleObject,hEventStart,INFINITE
           mov  ecx,600000000
Первая строка процедуры треда ― это вызов WainForSingleObject. Она ждет, пока не включится объект события, а затем возвращается. Это означает, что даже если тред создан, мы помещаем его в спящее состояние.
Когда пользователь выбирает в меню команду "run thread", мы включаем объект события:
Кликните здесь для просмотра всего текста
Assembler
1
2
               .if ax==IDM_START_THREAD
                   invoke SetEvent,hEventStart
Вызов SetEvent включает объект события, после чего WainForSingleObject возвращается и тред начинает выполняться. Когда пользователь выбирает команду "stoр thread", мы устанавливаем значение глобальной переменной в TRUE.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
                   .if EventStop==FALSE
                           add  eax,eax
                           dec  ecx
                   .else
                           invoke MessageBox,hwnd,ADDR StopString,ADDR AppName,MB_OK
                           mov  EventStop,FALSE
                           jmp Threadproc
                   .endif
Это останавливает тред и снова передает управление функции WaitForSingleObject. Заметьте, что мы не должны вручную выключать объект, так как мы указали при вызове функции CreateEvent, что значение bManualReset равно FALSE.
_____________________________________
© Iczelion, пер. Aquila.
3
Вложения
Тип файла: zip tut16.zip (3.8 Кб, 55 просмотров)
Mikl___
Заблокирован
Автор FAQ
15.01.2013, 13:03  [ТС] #50
Win32 API. Урок 16a. Объект события
Скачайте пример здесь.
Практика ― сестра шизофрении
Кликните здесь для просмотра всего текста
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
.586p
.model tiny
include windows.inc
;for WinXP - 1263 bytes
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
exebase         equ 400000h
IDM_START_THREAD    equ 0
IDM_STOP_THREAD     equ 1
IDM_EXIT        equ 2
IDC_MENU        equ 30
WM_FINISH       equ WM_USER+100h
main:
include capito_res.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,IDC_MENU,edi
;+--------------------------+
;| creating the main window |
;+--------------------------+
    push ebx
    push esi
    shl esi,9
    invoke CreateWindowEx,WS_EX_CLIENTEDGE,edi,edi,\
    WS_OVERLAPPEDWINDOW or WS_VISIBLE,esi,esi,400,200,ebx,ebx
    mov hwnd+exebase,eax
    invoke  GetMenu,eax;[hWnd]
    mov hMenu+exebase,eax
        invoke CreateEvent,ebx,ebx,ebx,ebx
    mov hEventStart+exebase,eax
    invoke CreateThread,ebx,ebx,offset ThreadProc+exebase,\
        ebx,NORMAL_PRIORITY_CLASS,offset ThreadID+exebase
    mov hThread+exebase,eax
    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 dword ptr [ebp+8h]
uMsg    equ dword ptr [ebp+0Ch]
wParam  equ dword ptr [ebp+10h]
lParam  equ dword ptr [ebp+14h]
    push ebp
    mov ebp,esp
    mov eax,uMsg
    dec eax
    dec eax; cmp uMsg,WM_DESTROY
    je wmDESTROY
    sub eax,WM_COMMAND-WM_DESTROY
    je wmCOMMAND
    sub eax,WM_FINISH-WM_COMMAND
    je wmFINISH
    leave
    jmp DefWindowProc+exebase
wmDESTROY:  invoke ExitProcess,ebx
wmCOMMAND: mov eax,wParam
    cmp lParam,ebx
    jne wmBYE
    jmp dword ptr [menu_handlers+eax*4+exebase]
START_THREAD: invoke SetEvent,hEventStart+exebase
    xor esi,esi;esi=MF_GRAYED
    inc esi
    jmp @f
STOP_THREAD: or EventStop+exebase,TRUE
    xor esi,esi;esi=MF_ENABLED
@@: invoke EnableMenuItem,hMenu+exebase,ebx,esi
    xor esi,1
    invoke EnableMenuItem,hMenu+exebase,IDM_STOP_THREAD,esi
    jmp wmBYE
wmFINISH: invoke MessageBox,ebx,offset SuccessString+exebase,offset wTitle+exebase,ebx;MB_OK=0
        jmp wmBYE
EXIT:   invoke  DestroyWindow,hWnd+exebase
wmBYE:  leave
    retn 10h
ThreadProc: invoke WaitForSingleObject,hEventStart+exebase,INFINITE
        mov  ecx,600000000
@@:     cmp EventStop+exebase,ebx;.if EventStop+exebase==FALSE
    jne @f
        add eax,eax
    loop @b
        invoke PostMessage,hwnd+exebase,WM_FINISH,ebx,ebx
        invoke EnableMenuItem,hMenu+exebase,ebx,ebx;IDM_START_THREAD,MF_ENABLED
        invoke EnableMenuItem,hMenu+exebase,IDM_STOP_THREAD,MF_GRAYED
        jmp ThreadProc
@@:     invoke MessageBox,hwnd+exebase,offset StopString+exebase,offset wTitle+exebase,ebx;MB_OK=0
        mov EventStop+exebase,ebx;FALSE
        jmp ThreadProc
;--------данные------------------------------
wTitle    db   'Iczelion Tutorial #16: ASM Event Example',0;name of our window
menu_handlers  dd START_THREAD+exebase, STOP_THREAD+exebase, EXIT+exebase
SuccessString db "The calculation is completed!",0
StopString db "The thread is stopped",0
;-------------------------------------------------------------------------------------------
MFR_END     equ 80h
MFR_POPUP   equ 1
MFT_STRING  equ 0
MFS_ENABLED equ 0
MFT_SEPARATOR   equ 800h
RT_MENU     equ 4
 
POPUP       equ 0010h
MENUBREAK       equ 0040h
ENDMENU     equ 0080h
DS_SETFONT  equ 0040h
align 4
resource:       
Characteristics0    dd 0
TimeDateStamp0      dd 0
MajorVersion0       dw 0
MinorVersion0       dw 0;
NumberOfNamedEntries0   dw 0;количество ресурсов с именами
NumberOfIdEntries0  dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является типом ресурса
dw RT_MENU,0;номер типа ресурса
dw x-resource,8000h; если во 2-ом слове установлен старший бит - есть ссылка 
;на оглавление второго уровня. В 1-ом слове смещение второго оглавления 
;относительно начала раздела ресурсов
x:
Characteristics1    dd 0
TimeDateStamp1      dd 0
MajorVersion1       dw 0
MinorVersion1       dw 0;
NumberOfNamedEntries1   dw 0;количество ресурсов с именами
NumberOfIdEntries1  dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является идентификатором меню
dw IDC_MENU,0
dw x1-resource,8000h; если во 2-ом слове установлен старший бит - есть ссылка 
;на оглавление третьего уровня. В 1-ом слове смещение третьего оглавления 
;относительно начала раздела ресурсов
x1:
Characteristics2    dd 0
TimeDateStamp2      dd 0
MajorVersion2       dw 0
MinorVersion2       dw 0;
NumberOfNamedEntries2   dw 0;количество ресурсов с именами
NumberOfIdEntries2  dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является идентификатором языка, который 
;используется данным ресурсом 16 * SUBLANG_ + LANG_
dw 40Ah,0,x2-resource,0
x2:;struct _IMAGE_RESOURCE_DATA_ENTRY
OffsetToData0   dd menu
Size0       dd end_menu-menu
CodePage0   dd 0
Reserved0   dd 0
menu dw 0,0,POPUP or MFR_END
du <&Thread>
dw MFT_STRING or MFS_ENABLED,IDM_START_THREAD
du <&Run Thread>
dw MFT_STRING or MFS_ENABLED or MF_GRAYED,IDM_STOP_THREAD
du <&Stop Thread>
dw MENUBREAK,0,0;NOTEXT
dw MFT_STRING or MFS_ENABLED or MFR_END,IDM_EXIT
du <&Exit>
end_menu:
end_resource:
;---------------------------------------------------------------------
import:
hMenu       dd 0    ;menu handle
EventStop   BOOL FALSE
hEventStart dd 0
dd user32_dll
dd user_table
hwnd        dd 0
ThreadID    dd 0
hThread     dd 0
dd kernel32_dll
dd kernel_table
dd 0,0,0,0
user_table:
RegisterClass       dd _RegisterClass
PostMessage     dd _PostMessage
CreateWindowEx          dd _CreateWindowEx
GetMessage              dd _GetMessage
DispatchMessage         dd _DispatchMessage
DefWindowProc           dd _DefWindowProc
DestroyWindow           dd _DestroyWindow
GetMenu         dd _GetMenu
MessageBox      dd _MessageBox
EnableMenuItem      dd _EnableMenuItem,0
 
kernel_table:
SetEvent        dd _SetEvent
CreateEvent     dd _CreateEvent
CreateThread        dd _CreateThread
WaitForSingleObject     dd _WaitForSingleObject
ExitProcess             dd _ExitProcess
            dw 0
_RegisterClass      db 0,0,'RegisterClassA'      
_PostMessage        db 0,0,'PostMessageA'
_CreateWindowEx     db 0,0,'CreateWindowExA'
_GetMessage     db 0,0,'GetMessageA'
_DispatchMessage    db 0,0,'DispatchMessageA'
_DefWindowProc      db 0,0,'DefWindowProcA'
_DestroyWindow      db 0,0,"DestroyWindow"
_GetMenu        db 0,0,'GetMenu'
_MessageBox     db 0,0,'MessageBoxA'
_EnableMenuItem     db 0,0,'EnableMenuItem',0
user32_dll      db 'user32'
_SetEvent       db 0,0,'SetEvent'
_CreateEvent        db 0,0,'CreateEventA'
_CreateThread       db 0,0,'CreateThread'
_WaitForSingleObject    db 0,0,'WaitForSingleObject'
_ExitProcess        db 0,0,'ExitProcess',0
kernel32_dll        db 'kernel32'
end_import:
end main
_____________________________________
© Mikl___ 2013
3
Вложения
Тип файла: zip tut16a.zip (4.3 Кб, 56 просмотров)
Mikl___
Заблокирован
Автор FAQ
16.01.2013, 05:08  [ТС] #51
Win32 API. Урок 17. Динамические библиотеки
В этом туториале мы узнаем о dll, что это такое и как их создавать.

Вы можете скачать пример здесь.
ТЕОРИЯ ― МАТЬ СКЛЕРОЗА

Если вы программируете достаточно долго, вы заметите, что программы, которые вы пишете, зачастую используют один и те же общие процедуры. Из-за того, что вам приходиться переписывать их снова и снова, вы теряете время. Во времена DOS'а программисты сохраняли эти общие процедуры в одной или более библиотеках. Когда они хотели использовать эти функции, они всего лишь прилинковывали библиотеку к объектному файлу и линкер извлекал функции прямо из библиотек и вставлял их в финальный файл. Этот процесс называется статической линковкой. Хорошим примером являются стандартные библиотеки в C. У этого метода есть изъян ― то, что в каждой программе у вас находятся абсолютно одинаковые копии функций. Впрочем, для ДОС-овских программ это не очень большой недостаток, так как только одна программа могла быть активной в памяти, поэтому не происходила трата драгоценной памяти.

Под Windows ситуация стала более критичной, так как у вас может быть несколько программ, выполняющихся одновременно. Память будет быстро пожираться, если ваша программа достаточно велика. У Windows есть решение этой проблемы: динамические библиотеки (dynamic link librariesdll).
Динамическая библиотека ― это что-то вроде сборника общих функций. Windows не будет загружать несколько копий DLL в память; даже если одновременно выполняются несколько экземпляров вашей программы, будет только одна копия DLL в памяти. Здесь я должен остановиться и разъяснить чуть поподробнее. В реальности, у всех процессов, использующих одну и ту же dll есть своя копия этой библиотеки, однако Windows делает так, чтобы все процессы разделяли один и тот же код этой dll. Впрочем, секция данных копируется для каждого процесса.

Программа линкуется к DLL во время выполнения в отличии от того, как это осуществлялось в старых статических библиотеках. Вы также можете выгрузить DLL во время выполнения, если она вам больше не нужна. Если программа одна использует эту DLL, тогда та будет выгружена немедленно. Hо если ее еще используют какие-то другие программы, DLL останется в памяти, пока ее не выгрузит последняя из использующих ее программ.

Как бы то ни было, перед линкером стоит сложная задача, когда он проводит фиксирование адресов в конечном исполняемом файле. Так как он не может "извлечь" функции и вставить их в финальный исполняемый файл, он должен каким-то образом сохранить достаточно информации о DLL и используемых функциях в выходном файле, чтобы тот смог найти и загрузить верную DLL во время выполнения.

И тут в дело вступают библиотеки импорта. Библиотека импорта содержит информацию о DLL, которую она представляет. Линкер может получить из нее необходимую информацию и вставить ее в исполняемый файл.

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

Вы можете загрузить DLL самостоятельно, не полагаясь на Windows-загрузчик.
  • В этом случае вам не потребуется библиотека импорта, поэтому вы сможете загружать и использовать любую DLL, даже если к ней не прилагается библиотеки импорта. Тем не менее, вам все равно нужно знать какие функции находятся внутри нее, сколько параметров они принимают и тому подобную информацию.
  • Когда вы поручаете Windows загружать DLL, если та отсутствует, Windows выдаст сообщение "Требуемый .DLL-файл, xxxxx.dll отсутствует" и все! Ваша программ не может сделать ничего, что изменить это, даже если ваша dll не является необходимой. Если же вы будете загружать DLL самостоятельно и библиотека не будет найдена, ваша программа может выдать пользователю сообщение, уведомляющее об этом, и продолжить работу.
  • Вы можете вызывать недокументированные функции, которые не включены в библиотеки импорта, главное, чтобы у вас было достаточно информации об этих функциях.
  • Если вы используете LoadLibrary, вам придется вызывать GetprocAddress для каждой функции, которую вы заходите вызвать. GetprocAddress получает адрес входной точки функции в определенной DLL. Поэтому ваш код будет чуть-чуть больше и медленнее, но не намного.
Теперь, рассмотрев преимущества и недостатки использования LoadLibrary, мы подробно рассмотрим как создать DLL.

Следующий код является каркасом DLL.
Содержимое файла DLLSkeleton.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
.386
   .model flat,stdcall
 
   option casemap:none
   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
   .code
   DllEntry proc hInstDLL:HINSTANCE, reason:DWORD, reserved1:DWORD
           mov  eax,TRUE
      
           ret
   DllEntry Endp
 
   ;----------------------------------------------------------------------------
   ; Это функция-пустышка - она ничего не делает. Я поместил ее сюда, чтобы
   ; показать, как вставляют функции в DLL.
   ;----------------------------------------------------------------------------
   TestFunction proc
       ret
   TestFunction endp
 
 
   End DllEntry
Содержимое файла DLLSkeleton.def
Кликните здесь для просмотра всего текста
Код
  LIBRARY   DLLSkeleton
   EXPORTS   TestFunction
Вышеприведенная программа ― это каркас DLL. Каждая DLL должна иметь стартовую функцию. Windows вызывает эту функцию каждый pаз, когда:
  • DLL загружена в первый раз
  • DLL выгружена
  • Создается тред в том же процессе
  • Тред разрушен в том же процессе
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
   DllEntry proc hInstDLL:HINSTANCE, reason:DWORD, reserved1:DWORD
 
           mov  eax,TRUE
           ret
   DllEntry Endp
Вы можете назвать стартовую функцию как пожелаете, главное чтобы был END. Эта функция получает три параметра, только первые два из них важны.
  • hInstDLL ― это хэндл модуля DLL. Это не тоже самое, что хэндл процесса. Вам следует сохранить это значение, так как оно понадобится вам позже. Вы не сможете ее получить в дальнейшем легко.
  • reason ― может иметь одно из следующих четырех значений:
    • DLL_PROCESS_ATTACH ― DLL получает это значение, когда впервые загружается в адресное пространство процесса. Вы можете использовать эту возможность для того, чтобы осуществить инициализацию.
    • DLL_PROCESS_DETACK ― DLL получает это значение, когда выгружается из адресного пространства процесса. Вы можете использовать эту возможность для того, чтобы "почистить" за собой: освободить память и так далее.
    • DLL_THREAD_ATTACK ― DLL получает это значение, когда процесс создает новую ветвь.
    • DLL_THREAD_DETACK ― DLL получает это значение, когда ветвь в процессе уничтожена.
Вы возвращаете TRUE в eax, если вы хотите, чтобы DLL продолжала выполняться. Если вы возвратите FALSE, DLL не будет загружена. Например, если ваш инициализационный код должен зарезервировать память и он не может это сделать, стартовой функции следует возвратить FALSE, чтобы показать, что DLL не может запуститься.

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

DLL требуется DEF-файл на стадии разработки. Мы сейчас посмотрим, что это такое.
Кликните здесь для просмотра всего текста
Код
   LIBRARY   DLLSkeleton
   EXPORTS   TestFunction
Обычно у вас должна быть первая строка. Ключевое слово LIBRARY определяет внутреннее имя модуля DLL. Желательно, чтобы оно совпадало с именем файла.

EXPORTS говорит линкеру, какие функции в DLL экспортируются, то есть, могут вызываться из других программ. В прилагающемся примере нам нужно, чтобы другие модули могли вызывать TestFunction, поэтому мы указываем здесь ее имя.

Другое отличие заключается в параметрах, передаваемых линкеру. Вы должны указать ключи /DLL и /DEF:.

Код
   link/DLL /SUBSYSTEM:WINDOWS/DEF:DLLSkeleton.def/LIBpATH:c:\masm32\lib DLLSkeleton.obj
Параметры ассемблера те же самые, обычно /c /coff /Cp. После компиляции вы получите .dll и .lib. Последний файл ― это библиотека импорта, которую вы можете использовать, чтобы прилинковать к другим программам функции из соответствующей .dll.
ПРАКТИКА ― СЕСТРА ШИЗОФРЕНИИ

Далее я покажу вам как использовать LoadLibrary, чтобы загрузить DLL.
Содержимое файла UseDLL.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
.386
   .model flat,stdcall
   option casemap:none
   include \masm32\include\windows.inc
   include \masm32\include\user32.inc
   include \masm32\include\kernel32.inc
   includelib \masm32\lib\kernel32.lib
   includelib \masm32\lib\user32.lib
 
   .data
   LibName db "DLLSkeleton.dll",0
   FunctionName db "TestHello",0
   DllNotFound db "Cannot load library",0
   AppName db "Load Library",0
   FunctionNotFound db "TestHello function not found",0
 
   .data?
   hLib dd ?                                         ; хэндл библиотеки (DLL)
   TestHelloAddr dd ?                        ; адрес функции TestHello
 
   .code
start:        invoke LoadLibrary,addr LibName
   ;-------------------------------------------------------------------------------
   ; Вызываем LoadLibrary и передаем имя желаемой DLL. Если вызов проходит успешно,
   ; будет возвращен хэндл библиотеки (DLL). Если нет, то будет возвращен NULL.
   ; Вы можете передать хэндл библиотеки функции GetрrocAddress или любой другой
   ; функции, которая требует его в качестве одного из параметров.
   ;-------------------------------------------------------------------------------
           .if eax==NULL
                   invoke MessageBox,NULL,addr DllNotFound,addr AppName,MB_OK
           .else
                   mov hLib,eax
                   invoke GetprocAddress,hLib,addr FunctionName
   ;------------------------------------------------------------------------------
   ; Когда вы получаете хэндл библиотеки, вы передаете его GetрrocAddress вместе
   ; с именем функции в этой dll, которую вы хотите вызвать. Она возвратит адрес
   ; функции, если вызов пройдет успешно. В противном случае, она возвратит NULL.
   ; Адреса функций не изменятся, пока вы не перезагрузите библиотеку. Поэтому
   ; их можно поместить в глобальные переменные для будущего использования.
   ;------------------------------------------------------------------------------
                   .if eax==NULL
                           invoke MessageBox,NULL,addr FunctionNotFound,addr AppName,MB_OK
                   .else
                           mov TestHelloAddr,eax
                           call [TestHelloAddr]
   ;----------------------------------------------------------------------------
   ; Затем мы вызываем функцию с помощью call и переменной, содержащей адрес
   ; функции в качестве операнда.
   ;----------------------------------------------------------------------------
                   .endif
                   invoke FreeLibrary,hLib
   ;------------------------------------------------------------------------------
   ; Когда вам больше не требуется библиотека, выгружате ее с помощью FreeLibrary.
   ;------------------------------------------------------------------------------
           .endif
           invoke Exitprocess,NULL
   end start
Как вы можете видеть, использование LoadLibrary чуть сложнее, но гораздо гибче.
________________________________________
© Iczelion, пер. Aquila.
5
Вложения
Тип файла: zip tut17.zip (5.1 Кб, 94 просмотров)
Mikl___
Заблокирован
Автор FAQ
16.01.2013, 05:36  [ТС] #52
Win32 API. Урок 17a. Динамические библиотеки

В этом туториале мы узнаем о dll, что это такое и как их создавать.

Вы можете скачать пример здесь.
ТЕОРИЯ ― МАТЬ СКЛЕРОЗА
батник, которым собираются DLL
Кликните здесь для просмотра всего текста
Код
@echo off
cls
set filename=%1
if exist %filename%.exe del %filename%.exe
ml /AT /c /Cp /Gz /I\masm32\include %filename%.asm
Link16 /t %filename%.obj ,%filename%.dll;
del %filename%.obj
Заголовочный файл для DLL назовем его «capito_dll.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
OPTION NOKEYWORD: <invoke>
invoke MACRO Fn,args:VARARG
LOCAL txt,arg
    txt TEXTEQU <>
    IRP arg,<args>
    txt CATSTR <arg>, <!,>, txt
    ENDM 
%   IRP arg,<txt>
    push arg
    ENDM
    call Fn+dllbase
ENDM
;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_DLL 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 dllbase
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 export
export_size         dd end_export-export
import_RVA          dd import
import_size         dd end_import-import
;------------------------------------------------
section_table       dd 'xet.','t'
virtual_size        dd 0
virtual_address     dd start
Physical_size       dd end_import-start
Physical_offset     dd start
Relocations         dd 0
Linenumbers     dd 0
Relocations_and_Linenumbers_count dd 0
Attributes              dd 0
Заголовочный файл для DLL основные отличия файла «capito_dll.asm» от «capito.asm» и «capito_res.asm»:
  1. появляется значения в секциях export_RVA и export_size
    Кликните здесь для просмотра всего текста
    Assembler
    1
    2
    3
    4
    5
    6
    
    loader_flag     dd 0
    number_of_dirs      dd (section_table-export_RVA)/8
    export_RVA          dd export
    export_size         dd end_export-export
    import_RVA          dd import
    import_size         dd end_import-import
  2. меняется поле Characteristics
    Кликните здесь для просмотра всего текста
    тип приложениязначение в поле Characteristicsчисловое значение
    exeIMAGE_FILE_32BIT_MACHINE or IMAGE_FILE_RELOCS_STRIPPED or IMAGE_FILE_EXECUTABLE_IMAGE or IMAGE_FILE_LINE_NUMS_STRIPPED or IMAGE_FILE_LOCAL_SYMS_STRIPPED10Fh
    dllIMAGE_FILE_32BIT_MACHINE or IMAGE_FILE_DLL or IMAGE_FILE_EXECUTABLE_IMAGE or IMAGE_FILE_LINE_NUMS_STRIPPED or IMAGE_FILE_LOCAL_SYMS_STRIPPED210Eh
  3. Image base меняется с exebase=400000h на dllbase=100000000h
Кликните здесь для просмотра всего текста
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
; masm dos com #
.586p
.model tiny
;for WinXP - 298 bytes
include windows.inc
.code
dllbase equ 10000000h
main:
include capito_dll.asm
;----------------------------------
start:
hInstDLL    equ dword ptr [esp+4]
;----------------------------------------------------------------------
;hInstDLL - это хэндл модуля DLL. Это не тоже самое, что хэндл пpоцесса. 
;----------------------------------------------------------------------
reason      equ dword ptr [esp+8]
;----------------------------------------------------------------------
;reason может иметь одно из следующих четыpех значений: 
;DLL_PROCESS_ATTACH - DLL получает это значение, когда впеpвые загpужается 
;в адpесное пpостpанство пpоцесса. Вы можете использовать эту возможность 
;для того, чтобы осуществить инициализацию. 
;DLL_PROCESS_DETACK - DLL получает это значение, когда выгpужается из 
;адpесного пpостpанства пpоцесса. Вы можете использовать эту возможность 
;для того, чтобы "почистить" за собой: освободить память и так далее. 
;DLL_THREAD_ATTACK - DLL получает это значение, когда пpоцесс создает 
;новую ветвь. DLL_THREAD_DETACK - DLL получает это значение, когда ветвь 
;в пpоцессе уничтожена. 
;----------------------------------------------------------------------------
res1        equ dword ptr [esp+0Ch]
    mov eax,reason
    invoke MessageBox,NULL,[handle_message+eax*4+dllbase],\
    offset AppName+dllbase,MB_OK
    push TRUE
        pop eax
;------------------------------------------------------------------------
;Вы возвpащаете TRUE в eax, если хотите, чтобы DLL пpодолжала выполняться
;Если возвpатить FALSE, DLL не будет загpужена.
;-------------------------------------------------------------------------
        retn 0Ch
TestHello: invoke MessageBox, NULL,offset HelloMsg+dllbase,\
    offset AppName+dllbase,MB_OK
    push TRUE
        pop eax
        retn
AppName     db "DLL Skeleton",0
HelloMsg    db "Hello, you're calling a function in this DLL",0
LoadMsg     db "The DLL is loaded",0
UnloadMsg   db "The DLL is unloaded",0
ThreadCreated   db "A thread is created in this process",0
ThreadDestroyed db "A thread is destroyed in this process",0
handle_message  dd UnloadMsg+dllbase,LoadMsg+dllbase,ThreadCreated+dllbase,\
        ThreadDestroyed+dllbase
;----------------------------------------------------------
align 2
export:
dd 0,0,0,DLLNAME1
dd 1,1,1,functions
dd names,ordinals
functions dd TestHello
names     dd _TestHello
_TestHello db 'TestHello',0
ordinals   dw 0
DLLNAME1   db 'tut17a',0
end_export:
;------------------------------------------------------------
align 2
import:
dd 0,0,0,user32_dll
dd user32_table
dd 0,0
user32_table:
MessageBox  dd _MessageBox
        dw 0
_MessageBox db 0,0,'MessageBoxA',0
user32_dll  db 'user32' 
end_import:
end main
Динамическую библиотеку можно вызывать двумя способами ― явным и неявным связыванием. Неявное связывание ― наиболее широко используемый способ. Здесь с самого начала компилятору строго говорится, что вот так-то и так-то, вот это вот считать dll и поступать с ней вот так, а не иначе. Явное связывание ― это вызов подпрограммы LoadLibrary для загрузки конкретной библиотеки (например, kernel32.dll), а потом подпрограмма GetProcAddress возвращает адрес конкретной функции (например, IsDebuggerPresent), а далее что-то вроде call [адрес, возвращенный GetProcAddress]. Неявное связывание оставляет следы в таблице импорта, которые легко обнаружить. Явное связывание в таблице импорта не отображается.
Два варианта EXE-файлов, которые будут работать с tut17a.dll
Вариант первый ― загрузка адреса функции TestHello из tut17a.dll операционной системой в секцию импорта.
Кликните здесь для просмотра всего текста
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
; masm dos com #
.586p
.model tiny
;for WinXP - 239 bytes
include windows.inc
.code
exebase equ 400000h
main:
include capito.asm
;----------------------------------
start:  invoke TestHello 
    retn                        
;----------------------------------
import: 
dd 0,0,0
dd tut17a_dll
dd tut17a_table,0,0
tut17a_table:            
TestHello       dd _TestHello
        dw 0          
_TestHello  db 0,0,'TestHello',0
tut17a_dll  db 'tut17a'                      
end_import:
end main
Вариант второй ― «динамическая загрузка» tut17a.dll при помощи функции LoadLibrary. Далее определяется адрес процедуры TestHello с помощью GetProcessAddress, после чего осуществляется вызов TestHello. После того как tut17a.dll стала ненужной ― ее выгружают из памяти функцией FreeLibrary (если вы этого не сделаете ― система выгрузит dll автоматически после закрытия приложения ― использование FreeLibrary это программисткая традиция с целью освободить оперативную память от «лишних» приложений )
Кликните здесь для просмотра всего текста
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
; masm dos com #
.586p
.model tiny
;for WinXP - 498 bytes
include windows.inc
.code
exebase equ 400000h
main:
include capito.asm
;----------------------------------
start:  xor ebx,ebx
    mov ebp,ebx
    invoke LoadLibrary,offset tut17a_dll+exebase 
;-------------------------------------------------------------------------
; Вызываем LoadLibrary и пеpедаем имя нашей DLL. Если вызов пpошел успешно,
; будет возвpащен хэндл библиотеки (DLL). Если нет, тогда будет возвpащен NULL.
; Вы можете пеpедать хэндл библиотеки функции GetProcAddress или любой дpугой
; функции, котоpая тpебует его в качестве одного из паpаметpов.
;---------------------------------------------------------------------
        xchg eax,ecx
    jecxz a1
    mov edi,ecx  ; хэндл библиотеки (DLL)
        invoke GetProcAddress,ecx,offset aTestHello+exebase
;---------------------------------------------------------------------
; После получения хэндла библиотеки, пеpедаем его GetProcAddress вместе с именем
; функции в этой dll, котоpую хотим вызвать. Функция GetProcAddress возвpащает 
; адpес функции, если вызов пpошел успешно. В пpотивном случае, она возвpащает 
; NULL. Адpеса функций не изменятся, пока мы не пеpезагpузим библиотеку. Поэтому 
; их можно поместить в глобальные пеpеменные для будущего использования.
;------------------------------------------------------------------------------
    inc ebp
        xchg eax,ecx
    jecxz a1
    mov TestHelloAddr+exebase,ecx
        call dword ptr TestHelloAddr+exebase
;----------------------------------------------------------------------------
; Затем мы вызываем функцию с помощью call и пеpеменной, содеpжащей адpес
; функции в качестве опеpанда.
;----------------------------------------------------------------------------
    jmp a3
;---------------------------------------------------------------------------
; универсальная функция сообщения об ошибках. В зависимости от значения в ebp
; сообщения будут разными - если ebp=0 "Cannot load library", если ebp=1
; "TestHello function not found"
;---------------------------------------------------------------------------
a1:     invoke MessageBox,ebx,[handle_message+ebp*4+exebase],\
    offset aLoadLibrary+exebase,0
    dec ebp
    jnz a2
;---------------------------------------------------------------------------
; если было ebp равно 1 - нам придется выгрузить dll
;---------------------------------------------------------------------------
a3:     invoke FreeLibrary,edi
;------------------------------------------------------------------------------
; Когда нам больше не тpебуется библиотека, выгpужаем ее с помощью FreeLibrary.
;------------------------------------------------------------------------------
a2:     retn                        
;----------------------------------
aLoadLibrary db "Load Library",0
DllNotFound db "Cannot load library",0
FunctionNotFound db "TestHello function not found",0
handle_message dd DllNotFound+exebase,FunctionNotFound+exebase
tut17a_dll db "tut17a.dll",0
aTestHello db "TestHello",0
TestHelloAddr dd ?  ; адpес функции TestHello   
;----------------------------------
import: 
dd 0,0,0,user32_dll
dd user32_table
dd 0,0,0,kernel32_dll
dd kernel32_table
dd 0,0
user32_table:            
MessageBox      dd _MessageBox,0
kernel32_table:
LoadLibrary dd _LoadLibrary
GetProcAddress  dd _GetProcAddress
FreeLibrary dd _FreeLibrary
        dw 0
_MessageBox db 0,0,'MessageBoxA',0
user32_dll  db 'user32'
_FreeLibrary    db 0,0,'FreeLibrary'
_GetProcAddress db 0,0,'GetProcAddress'
_LoadLibrary    db 0,0,'LoadLibraryA',0
kernel32_dll    db 'kernel32'          
end_import:
end main
DLL может содержать ресурсы, которые в основную программу можно подгружать по мере необходимости.

содержимое файла «capito_dll_res.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
OPTION NOKEYWORD: <invoke>
invoke MACRO Fn,args:VARARG
LOCAL txt,arg
    txt TEXTEQU <>
    IRP arg,<args>
    txt CATSTR <arg>, <!,>, txt
    ENDM 
%   IRP arg,<txt>
    push arg
    ENDM
    call Fn+dllbase
ENDM
;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_DLL 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 dllbase
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 export
export_size         dd end_export-export
import_RVA          dd import
import_size         dd end_import-import
resource_RVA        dd resource
resource_size       dd end_resource-resource
;------------------------------------------------
section_table       dd 'xet.','t'
virtual_size        dd 0
virtual_address     dd start
Physical_size       dd end_import-start
Physical_offset     dd start
Relocations         dd 0
Linenumbers     dd 0
Relocations_and_Linenumbers_count dd 0
Attributes              dd 0
Содержимое файла tut17a-01.asm из которого будет создан tut17a-01.dll
Кликните здесь для просмотра всего текста
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
; masm dos com #
.586p
.model tiny
;for WinXP - 298 bytes
include windows.inc
.code
dllbase equ 10000000h
IDC_ICON1 equ 2 
IDC_ICON2 equ 3
lang      equ 419h
main:
include capito_dll_res.asm
;----------------------------------
start:
hInstDLL    equ dword ptr [ebp+8]
reason      equ dword ptr [ebp+0Ch]
res1        equ dword ptr [ebp+10h]
    push TRUE
        pop eax
        retn 0Ch
SETIC:
    push ebp
    mov ebp,esp
    xor ICON+dllbase,1
    invoke LoadIcon,reason,ICON+dllbase
    invoke PostMessage,hInstDLL,WM_SETICON,0,eax
    pop ebp
        retn 8
ICON dd IDC_ICON1
;--------------------------------------------------------------------------------
align 2
export:
dd 0,0,0,DLLNAME1
dd 1,1,1,functions
dd names,ordinals
functions   dd SETIC
names       dd _SETIC
_SETIC      db 'SETIC',0
ordinals   dw 0
DLLNAME1   db 'tut17a-01',0
end_export:
;----------------------------------------
align 4
resource:
Characteristics0    dd 0
TimeDateStamp0      dd 0
MajorVersion0       dw 0
MinorVersion0       dw 0
;------------------------------------------------------
NumberOfNamedEntries0   dw 0;количество ресурсов с именами
NumberOfIdEntries0  dw 2;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является типом ресурса
dw RT_ICON,0,i1-resource,8000h
dw RT_GROUP_ICON,0,g1-resource,8000h
i1:
Characteristics1    dd 0
TimeDateStamp1      dd 0
MajorVersion1       dw 0
MinorVersion1       dw 0
NumberOfNamedEntries1   dw 0;количество ресурсов с именами
NumberOfIdEntries1  dw 2;количество ресурсов с идентификаторами
dw 1,0
dw i21-resource,8000h
dw 2,0
dw i22-resource,8000h
g1:
Characteristics2    dd 0
TimeDateStamp2      dd 0
MajorVersion2       dw 0
MinorVersion2       dw 0
NumberOfNamedEntries2   dw 0;количество ресурсов с именами
NumberOfIdEntries2  dw 2;количество ресурсов с идентификаторами
dw IDC_ICON1,0,g21-resource,8000h
dw IDC_ICON2,0,g22-resource,8000h
i21:
Characteristics3    dd 0
TimeDateStamp3      dd 0
MajorVersion3       dw 0
MinorVersion3       dw 0
NumberOfNamedEntries3   dw 0;количество ресурсов с именами
NumberOfIdEntries3  dw 1;количество ресурсов с идентификаторами
dw lang,0,i31-resource,0
i22:
Characteristics4    dd 0
TimeDateStamp4      dd 0
MajorVersion4       dw 0
MinorVersion4       dw 0
NumberOfNamedEntries4   dw 0;количество ресурсов с именами
NumberOfIdEntries4  dw 1;количество ресурсов с идентификаторами
dw lang,0,i32-resource,0;0A0
g21:
Characteristics5    dd 0
TimeDateStamp5      dd 0
MajorVersion5       dw 0
MinorVersion5       dw 0
NumberOfNamedEntries5   dw 0;количество ресурсов с именами
NumberOfIdEntries5  dw 1;количество ресурсов с идентификаторами
dw lang,0,g31-resource,0;D0
g22:
Characteristics6    dd 0
TimeDateStamp6      dd 0
MajorVersion6       dw 0
MinorVersion6       dw 0
NumberOfNamedEntries6   dw 0;количество ресурсов с именами
NumberOfIdEntries6  dw 1;количество ресурсов с идентификаторами
dw lang,0,g32-resource,0
i31:
OffsetToData0   dd icon1
Size0       dd end_icon1-icon1 
CodePage0   dd 0            
Reserved0   dd 0
i32:
OffsetToData1   dd icon2
Size1       dd end_icon2-icon2
CodePage1   dd 0
Reserved1   dd 0
g31: 
OffsetToData2   dd group1
Size2       dd end_group1-group1
CodePage2   dd 0
Reserved2   dd 0 
g32: 
OffsetToData3   dd group2
Size3       dd end_group2-group2
CodePage3   dd 0
Reserved3   dd 0
icon1:
;  incbin "Images\1.ico",22;пропуск первых 22 байт размер иконки 766-22=744=2E8
db 40,0,0,0,16,0,0,0,32,0,0,0,1,0,4,0,0,0,0,0,192,25 dup (0)
db 2 dup(128,0,0),0,128,2 dup(128,0),0,3 dup(0,128),128,0,0,3 dup(192)
db 0,3 dup (128),0,2 dup(0,0,255),0,0,0,255,2 dup (255,0),0,3 dup(0,255)
db 255,0,0,3 dup (255),17 dup (0),7,7 dup (0),7,6 dup (136)
db 2 dup (0,7,136,140,136,136,200,136),0,7,136,140,204,204,200,136
db 3 dup (0,7,136,136,200,140,136,136),0,7,2 dup (136,136,140,200,136,136,0,7)
db 2 dup (6 dup (136),0,7),6 dup (119)
db 9 dup (0),2 dup (255,255,0,0),13 dup (128,1,0,0),255,255,0,0
end_icon1: 
icon2:;  incbin "Images\2.ico",22
db 40,0,0,0,16,0,0,0,32,0,0,0,1,0,4,0,0,0,0,0,192,25 dup(0)
db 2 dup(128,0,0),0,128,2 dup(128,0),0,3 dup(0,128),128,0,0
db 3 dup(192),0,3 dup(128),0,2 dup(0,0,255),0,0,0,255,2 dup (255,0)
db 0,3 dup(0,255),255,0,0,3 dup (255),41 dup (0),8 dup(204),200,136 
db 140,136,136,200,136,140,200,189,140,139,216,200,189,140,200,219
db 140,141,184,200,219,140,200,189,140,139,216,200,189,140,200,136
db 140,136,136,200,136,140,8 dup(204),32 dup(0),2 dup(255,255,0,0)
db 0,0,0,0,85,85,38 dup (0),85,85,6 dup(0),255,255,0,0
end_icon2:
;16 x 16 (16 colors)  - Ordinal name: 1
group1: 
dw 0,1,1,1010h,10h,1,4,end_icon1-icon1,0,1
end_group1:
;16 x 16 (16 colors)  - Ordinal name: 2
group2:
dw 0,1,1,1010h,10h,1,4,end_icon2-icon2,0,2 
end_group2:
end_resource:
;---------------------------------------------------
align 2
import:
dd 0,0,0,user32_dll
dd user32_table
dd 0,0,0,0
user32_table:
PostMessage dd _PostMessage
LoadIcon    dd _LoadIcon
                dw 0
_PostMessage    db 0,0,'PostMessageA'
_LoadIcon   db 0,0,'LoadIconA',0
user32_dll  db 'user32' 
end_import:
end main
Программа будет загружать иконки из ресурсов dll и устанавливать их на окно при помощи LoadIcon

С каждым левым кликом по окошку будет циклически меняться иконка с на
Кликните здесь для просмотра всего текста

чтобы поместить содержимое 1.ico и 2.ico в ресурсы DLL пришлось воспользоваться программой bintodb.exe расположенной в папке \masm32\tools
Кликните здесь для просмотра всего текста
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
; masm dos com #
.586p
.model tiny
;for WinXP - 750 bytes
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
include windows.inc
.code
exebase equ 400000h
IDC_ICON1   equ 2
IDC_ICON2   equ 3
IDC_DIALOG  equ 20
main:
include capito_res.asm
;----------------------------------
start:  invoke DialogBoxParam,exebase,IDC_DIALOG,eax,\
    offset DlgProc+exebase,eax 
    retn                        
;----------------------------------
DlgProc:
hDlg    equ dword ptr [esp+4]
uMsg    equ dword ptr [esp+8]
    mov eax,uMsg
    mov edi,hDlg
        sub eax,WM_CLOSE
    je short wmCLOSE
        sub eax,WM_INITDIALOG-WM_CLOSE
    je short wmINITDIALOG
        sub eax,WM_LBUTTONDOWN-WM_INITDIALOG
    jne short FINISH
;получить адрес процедуры из динамической библиотеки
wmLBUTTONDOWN: invoke GetProcAddress,hLib+exebase,\
    offset NameProc+exebase
;вызвать  процедуру с двумя параметрами
    push hLib+exebase
    push edi;hWnd
    CALL eax
    jmp short FINISH
;закрыть библиотеку. библиотека закрывается также при выходе из программы
wmCLOSE: invoke FreeLibrary,hLib+exebase
    invoke EndDialog,edi,0
    jmp short FINISH
;загрузить библиотеку
wmINITDIALOG: invoke LoadLibrary,offset LIBR+exebase
        mov hLib+exebase,eax
;загрузить пиктограмму
    invoke LoadIcon,eax,IDC_ICON1
;установить пиктограмму
    invoke PostMessage,edi,WM_SETICON,0,eax
FINISH: xor eax,eax
    retn 10h
;-----------------------------------------------
LIBR db 'tut17a-01.dll',0
hLib dd 0
NameProc db 'SETIC',0
;---------------------------------------------
align 4
resource:
Characteristics0    dd 0
TimeDateStamp0      dd 0
MajorVersion0       dw 0
MinorVersion0       dw 0
;------------------------------------------
NumberOfNamedEntries0   dw 0;количество ресурсов с именами
NumberOfIdEntries0  dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является типом ресурса
dw RT_DIALOG,0,d1-resource,8000h
d1:
Characteristics1    dd 0
TimeDateStamp1      dd 0
MajorVersion1       dw 0
MinorVersion1       dw 0
NumberOfNamedEntries1   dw 0;количество ресурсов с именами
NumberOfIdEntries1  dw 1;количество ресурсов с идентификаторами
dw IDC_DIALOG,0
dw d2-resource,8000h
d2:
Characteristics2    dd 0
TimeDateStamp2      dd 0
MajorVersion2       dw 0
MinorVersion2       dw 0
NumberOfNamedEntries2   dw 0;количество ресурсов с именами
NumberOfIdEntries2  dw 1;количество ресурсов с идентификаторами
dw 409h,0,d3-resource,0
d3:
OffsetToData    dd dialog
Size0       dd end_dialog-dialog
CodePage    dd 0
Reserved0   dd 0
; --------------------------------------------
dialog: 
style dd DS_MODALFRAME or DS_3DLOOK or DS_CONTEXTHELP or WS_POPUP \
    or WS_VISIBLE or WS_CAPTION or WS_SYSMENU or DS_SETFONT
dwExtendedStyle dd 0
cdit dw 0;число элементов управления, входящих в состав диалогового окна 
x    dw 0;отступ левой границы окна от левой границы экрана
y    dw 0;отступ верхней границы окна от верхней границы экрана
cx0   dw 240;ширина окна
cy   dw 120;высота окна
      dw 0;есть меню у диалога (=FFFF) или меню нет (=0)
      dw 0;терминатор   
CAPTION: du &lt;Диалог с иконками из DLL&gt; ;заголовок окна
font dw 8
du <MS Sans Serif>
end_dialog:
end_resource:
import:         
dd 0,0,0,user32_dll
dd user32_table
dd 0,0,0,kernel32_dll
dd kernel32_table
dd 0,0,0,0
user32_table:
EndDialog       dd _EndDialog
PostMessage     dd _PostMessage
LoadIcon        dd _LoadIcon
DialogBoxParam      dd _DialogBoxParam,0
kernel32_table:
GetProcAddress      dd _GetProcAddress
FreeLibrary     dd _FreeLibrary
LoadLibrary     dd _LoadLibrary,0
 
_EndDialog      db 0,0,'EndDialog'
_PostMessage        db 0,0,'PostMessageA'
_LoadIcon       db 0,0,'LoadIconA'
_DialogBoxParam     db 0,0,'DialogBoxParamA',0
user32_dll      db 'user32'
_GetProcAddress     db 0,0,'GetProcAddress'
_FreeLibrary        db 0,0,'FreeLibrary'
_LoadLibrary        db 0,0,'LoadLibraryA',0
kernel32_dll        db 'kernel32'
end_import:
end main
______________________________________________________
© Mikl___ 2013
5
Миниатюры
Сам себе Iczelion   Сам себе Iczelion  
Изображения
  
Вложения
Тип файла: zip tut17a.zip (12.0 Кб, 66 просмотров)
Mikl___
Заблокирован
Автор FAQ
16.01.2013, 09:20  [ТС] #53
Win32 API. Урок 18. Common Control'ы
Мы узнаем, что такое common control'ы и как их использовать. Этот туториал является не более, чем поверхностным введением в данную тему.

Скачайте код примера здесь.
ТЕОРИЯ ― МАТЬ СКЛЕРОЗА
Windows 95 принесла несколько новых элементов пользовательского интерфейса, сделавших GUI более разнообразным. Некоторые из них широко использовались и в Windows 3.1, но программисты должны были программировать их самостоятельно. Теперь Microsoft включил их в Windows 9x и NT. Мы изучим их в этом туториале.

Вот список новых элементов управления:
  • Toolbar
  • Tooltip
  • Status bar
  • property sheet
  • property page
  • Tree view
  • List view
  • Animation
  • Drag list
  • Header
  • Hot-key
  • Image list
  • progress bar
  • Right edit
  • Tab
  • Trackbar
  • Up-down
Так как новых элементов управления довольно много, их загрузка в память и регистрация была бы бессмысленной тратой ресурсов. Все эти элементы управления, за исключением rich edit'а, находятся в comctl32.dll, чтобы приложения могли загружать их, когда они им нужны. Rich edit находится в своей собственной dll, richedXX.dll, так как он слишком сложен и поэтому больше, чем остальные.

Вы можете вызвать comctl32.dll, поместив вызов функции IntiCommonControls в вашу программу. InitCommonControls ― это функция в comctl32.dll, поэтому ее вызов в любом месте вашего кода заставит PE-загрузчик загрузить comctl32.dll, когда ваша программ запустится. Вам не нужно выполнять эту функцию, просто поместите ее где-нибудь. Эта функция ничего не делает! Ее единственной инструкцией является "ret". Ее главная цель ― это создание ссылки на comctl32.dll в секции импорта, чтобы PE-загрузчик загружал ее всегда, когда будет загружаться программа. Главным следствием будет являться то, что стартовая функция DLL зарегистрирует все классы элементов управления при загрузке dll. Элементы управления создаются на основе этих классов, как и другие дочерние элементы окон, например, edit control, listbox и так далее.

С rich edit'ом дел обстоит совершенно по другому. Если вы хотите использовать его, вы должны вызвать LoadLibrary, чтобы загрузить его и FreeLibrary, чтобы выгрузить.
Теперь давайте научимся создавать элементы управления. Вы можете использовать редактор ресурсов, чтобы внедрить их в диалоговое окно, или создать их самостоятельно. Почти все элементы управления создаются с помощью вызова CreateWindowEx или CreateWindow, путем передачи имени класса элемента управления. У некоторых элементов управления есть специальные функции для создания, хотя, на самом деле, они являются функциями-обертками вокруг CreateWindowEx, чтобы сделать создание элемента управления легче. Такие функции перечислены ниже:
  • CreateToolbarEx
  • CreateStatusWindow
  • CreatepropertySheetpage
  • propertySheet
  • ImageList_Create
Чтобы создавать элементы управления, вы должны знать их имена. Они перечислены ниже:
Кликните здесь для просмотра всего текста
Имя классаCommon Control
ToolbarWindow32 Toolbar
tooltips_class32 Tooltip
msctls_statusbar32 Status bar
SysTreeView32 Tree view
SysListView32 List view
SysAnimate32 Animation
SysHeader32 Header
msctls_hotkey32 Hot-key
msctls_progress32 progress bar
RICHEDIT Rich edit
msctls_updown32 Up-down
SysTabControl32Tab
рroрerty sheet'ы и рroрerty рage'ы и контрол image list имеют собственные функции создания. Drag list control ― это усовершенствованный listbox, поэтому у него нет своего собственного класса. Вышеприведенные имена проверены путем проверки скриптов ресурсов, генерируемых редактором ресурсов, входящего в Visual C++. Они отличаются от имен, приведенных в справочнике по Win32 API от Borland'а и тех, что указаны в книге Charles Petzold's "Programming Windows 95". Вышеприведенный список является точной версией.

Эти common control'ы могут использовать общие стили окна, такие как WS_CHILD и т.п. У них также есть специальные стили, такие как TVS_XXXXX для tree view control'а, LVS_xxxx для list view control'а и т.д. Справочник по Win32 API ваше лучшее руководство в данном случае.

Теперь, когда мы знаем, как создать common control'ы, мы можем перейти к тому, как взаимодействуют common control'ы и их родители. В отличие от дочерних элементов управления, common control'ы не взаимодействую с родительским окно через WM_COMMAND. Вместо этого они используют сообщение WM_NOTIFY, посылаемое родительскому окну, когда происходит какое-то интересное событие. "родитель" может контролировать "детей", посылая им определенные сообщения, которые введено достаточно много. Вам следует обратиться к справочнику по Win32 API за конкретными деталями.

Давайте посмотрим, как создать рrogress bar и status bar.
ПРАКТИКА ― СЕСТРА ШИЗОФРЕНИИ
Кликните здесь для просмотра всего текста
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
   include \masm32\include\windows.inc
   include \masm32\include\user32.inc
   include \masm32\include\kernel32.inc
   include \masm32\include\comctl32.inc
   includelib \masm32\lib\comctl32.lib
   includelib \masm32\lib\user32.lib
   includelib \masm32\lib\kernel32.lib
 
   WinMain pROTO :DWORD,:DWORD,:DWORD,:DWORD
 
   .const
   IDC_pROGRESS equ 1            ; control IDs
   IDC_STATUS equ 2
   IDC_TIMER  equ 3
 
   .data
   ClassName  db "CommonControlWinClass",0
   AppName    db "Common Control Demo",0
   progressClass  db "msctls_progress32",0       ; the class name of the progress bar
   Message  db "Finished!",0
   TimerID  dd 0
 
   .data?
   hInstance  HINSTANCE ?
   hwndprogress dd ?
   hwndStatus dd ?
   CurrentStep dd ?
 
   .code
start: invoke GetModuleHandle, NULL
       mov    hInstance,eax
       invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
       invoke Exitprocess,eax
       invoke InitCommonControls
WinMain proc hInst:HINSTANCE,hprevInst:HINSTANCE,CmdLine: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_AppWORKSPACE
       mov   wc.lpszMenuName,NULL
       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_OVERLAppED+\
   WS_CApTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT, \
   CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInst,NULL
       mov   hwnd,eax
       .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_CREATE
            invoke CreateWindowEx,NULL,ADDR progressClass,NULL,WS_CHILD+WS_VISIBLE,\
               100,200,300,20,hWnd,IDC_pROGRESS,hInstance,NULL
           mov hwndprogress,eax
           mov eax,1000               ; the lparam of pBM_SETRANGE message contains the range
           mov CurrentStep,eax
           shl eax,16                   ; the high range is in the high word
           invoke SendMessage,hwndprogress,pBM_SETRANGE,0,eax
           invoke SendMessage,hwndprogress,pBM_SETSTEp,10,0
           invoke CreateStatusWindow,WS_CHILD+WS_VISIBLE,NULL,hWnd,IDC_STATUS
           mov hwndStatus,eax
           invoke SetTimer,hWnd,IDC_TIMER,100,NULL        ; create a timer
           mov TimerID,eax
       .elseif uMsg==WM_DESTROY
           invoke postQuitMessage,NULL
           .if TimerID!=0
               invoke KillTimer,hWnd,TimerID
           .endif
       .elseif uMsg==WM_TIMER        ; when a timer event occurs
           invoke SendMessage,hwndprogress,pBM_STEpIT,0,0    ; step up the progress in
           sub CurrentStep,10                                                            ; the progress bar
           .if CurrentStep==0
               invoke KillTimer,hWnd,TimerID
               mov TimerID,0
               invoke SendMessage,hwndStatus,SB_SETTEXT,0,addr Message
               invoke MessageBox,hWnd,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
               invoke SendMessage,hwndStatus,SB_SETTEXT,0,0
               invoke SendMessage,hwndprogress,pBM_SETpOS,0,0
           .endif
       .else
           invoke DefWindowproc,hWnd,uMsg,wparam,lparam
           ret
       .endif
       xor eax,eax
       ret
   Wndproc endp
   end start
Разбор полетов
Кликните здесь для просмотра всего текста
Assembler
1
2
3
           invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
           invoke ExitProcess,eax
           invoke InitCommonControls
Я специально поместил InitCommonControls после ExitProcess, чтобы продемонстрировать то, что эта функция необходима только для создания ссылки на comctl32.dll в секции импорта. Как вы можете видеть, common control'ы работают, даже если функция InitCommonControls не запускалась.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
           .if uMsg==WM_CREATE
                invoke CreateWindowEx,NULL,ADDR ProgressClass,NULL,\
                   WS_CHILD+WS_VISIBLE,100,200,300,20,hWnd,IDC_PROGRESS,\
                   hInstance,NULL
               mov hwndprogress,eax
Здесь мы создаем common control. Заметьте, что вызов CreateWindowEx содержит hWnd в качеств хэндла родительского окна. Он также задает ID контрола, для идентификации последнего. Тем не менее, так как у нас есть хэндл окна контрола, этот ID не используется. Все дочерние окна должны иметь стиль WS_CHILD.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
               mov eax,1000
               mov CurrentStep,eax
               shl eax,16
               invoke SendMessage,hwndprogress,pBM_SETRANGE,0,eax
               invoke SendMessage,hwndprogress,pBM_SETSTEp,10,0
После того, как создан progress bar, мы можем установить его диапазон. Диапазон по умолчанию равен от 0 до 100. Если это вас не устраивает, вы можете указать ваш собственный диапазон с помощью сообщения PBM_SETRANGE. lрaram этого сообщения содержит диапазон, максимальное значение в верхнем слове и минимальное в нижнем. Вы также можете указать шаг, используя сообщение рBM_SETSTEр. Этот пример устанавливает его в 10, что означает то, что когда вы посылаете сообщение рBM_STEрIT прогресс бару, индикатор прогресса будет повышаться на 10. Вы также можете установить положение индикатора, послав сообщение PBM_SETPOS. Это сообщение дает вам полный контроль над рrogress bar'ом.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
               invoke CreateStatusWindow,WS_CHILD+WS_VISIBLE,NULL,hWnd,IDC_STATUS
               mov hwndStatus,eax
               invoke SetTimer,hWnd,IDC_TIMER,100,NULL        ; create a timer
               mov TimerID,eax
Затем мы создаем status bar, вызывая CreateStatusWindow. Этот вызов легко понять, поэтому я не буду комментировать его. После того, как status window создан, мы создаем таймер. В этом примере мы будем обновлять progress bar каждые 100 ms, поэтому нам нужно создать таймеp.
Кликните здесь для просмотра всего текста
Assembler
1
       SetTimer pROTO hWnd:DWORD, TimerID:DWORD, TimeInterval:DWORD, lpTimerproc:DWORD
  • hWnd : хэндл родительского окна
  • TimerID : не равный нулю идентификатор таймера. Вы можете создать свой собственный идентификатор.
  • TimerInteral : временной интервал в миллисекундах, который должен пройти, прежде чем таймер вызовет процедуру таймер или пошлет сообщение WM_TIMER.
  • lpTimeproc : адрес функции таймера, которая будет вызываться при истечении временного интервала. Если параметр равен нулю, таймер вместо этого будет посылать родительскому окну сообщение WM_TIMER.
Если вызов прошел успешно, функция возвратит TimerID. В противном случае, будет возвращен ноль. Вот почему идентификатор таймера не должен быть равен нулю.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
           .elseif uMsg==WM_TIMER
               invoke SendMessage,hwndprogress,pBM_STEpIT,0,0
               sub CurrentStep,10
               .if CurrentStep==0
                   invoke KillTimer,hWnd,TimerID
                   mov TimerID,0
                   invoke SendMessage,hwndStatus,SB_SETTEXT,0,addr Message
                   invoke MessageBox,hWnd,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
                   invoke SendMessage,hwndStatus,SB_SETTEXT,0,0
                   invoke SendMessage,hwndprogress,PBM_SETPOS,0,0
               .endif
Когда истекает указанный временной интервал, таймер посылает сообщение WM_TIMER. Вы можете поместить здесь свой код, который будет выполнен.
В данном пример, мы обновляем рrogress bar, а затем проверяем, было ли достигнуто максимальное значение. Если это так, мы убиваем таймеp, после чего устанавливаем текст статус-окна с помощью сообщения SB_SETTEXT. Отображается message box, и когда юзер кликает OK, мы очищаем текст в status bar'е и progress bar'е.
____________________________________
© Iczelion, пер. Aquila.
3
Вложения
Тип файла: zip tut18.zip (3.1 Кб, 59 просмотров)
Mikl___
Заблокирован
Автор FAQ
16.01.2013, 09:30  [ТС] #54
Win32 API. Урок 18a. Common Control'ы

Мы узнаем, что такое common control'ы и как их использовать. Этот туториал является не более, чем поверхностным введением в данную тему.
Кликните здесь для просмотра всего текста
Скачайте код примера здесь.
Кликните здесь для просмотра всего текста
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
; masm dos com #
.586p
.model tiny
;for WinXP - 1817 bytes
exebase     equ 400000h
pb1      equ 1
sb1      equ 2
tm1      equ 3
btn1         equ 4
PBS_SMOOTH   equ 1
PBS_VERTICAL     equ 4
include windows.inc
.code
exebase equ 400000h
main:
include capito.asm
;----------------------------------
start:  xchg ebx,eax
    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_BTNFACE+1,ebx,edi
;--------------------------+
; creating the main window |
;--------------------------+
    push ebx
    push esi
    shl esi,9
    invoke CreateWindowEx,ebx,edi,edi,\
        WS_CAPTION or WS_SYSMENU or WS_MINIMIZEBOX or WS_VISIBLE,\
        esi,esi,410,240,ebx,ebx
        pop esi
        mov  edi,eax
    invoke CreateWindowEx,WS_EX_DLGMODALFRAME,offset progressclass+exebase,ebx,\
    WS_CHILD  or  WS_VISIBLE,10,10,367,22,eax,pb1,esi,ebx
        mov  [pb1H+exebase],eax
    invoke SendMessage,eax,PBM_SETSTEP,2,ebx
 
    invoke CreateWindowEx,WS_EX_CLIENTEDGE or WS_EX_STATICEDGE,\
    offset progressclass+exebase,ebx,WS_CHILD or WS_VISIBLE or PBS_SMOOTH,\
        10,40,367,22,edi,pb1,esi,ebx
        mov  [pb2H+exebase],eax
    invoke SendMessage,eax,PBM_SETSTEP,2,ebx
 
    invoke CreateWindowEx,ebx,offset progressclass+exebase,ebx,WS_CHILD or WS_VISIBLE or \
    PBS_VERTICAL,305,80,25,100,edi,pb1,esi,ebx
        mov  [pb3H+exebase],eax
    invoke SendMessage,eax,PBM_SETSTEP,2,ebx
 
    invoke CreateWindowEx,ebx,offset progressclass+exebase,ebx,WS_CHILD or WS_VISIBLE or \
    PBS_SMOOTH or PBS_VERTICAL,350,80,25,100,edi,pb1,esi,ebx
        mov [pb4H+exebase],eax
    invoke SendMessage,eax,PBM_SETSTEP,2,ebx
 
    invoke CreateStatusWindow,WS_CHILD or WS_VISIBLE,ebx,edi,sb1
        mov  [sb1H+exebase],eax
    invoke CreateWindowEx,ebx,offset aButton+exebase,offset aClick+exebase,WS_CHILD  or  \
    WS_VISIBLE or BS_PUSHBUTTON,10,90,100,30,edi,btn1,esi,ebx
        mov  [btn1H+exebase],eax
    invoke CreateSolidBrush,0FF0000h
    mov [blueBrush+exebase],eax
    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 dword ptr [ebp+8]
uMsg        equ dword ptr [ebp+0Ch]
wParam      equ dword ptr [ebp+10h]
;lParam     equ ebp+14h
buffer      equ dword ptr [ebp-14h]
oldBrush    equ dword ptr [buffer - 4]
oldPen      equ dword ptr [oldBrush - 4]
    enter sizeof(PAINTSTRUCT) + 1Ch,0
    xor ebx,ebx
    mov eax,uMsg
    mov edi,hWnd
    dec eax
    dec eax;cmp [uMsg],WM_DESTROY=2
    je wmDESTROY
    sub eax,WM_PAINT-WM_DESTROY;cmp [uMsg],WM_PAINT=0Fh
    je wmPAINT
    sub eax,WM_COMMAND-WM_PAINT;cmp [uMsg],WM_COMMAND=111h
    je wmCOMMAND
    dec eax
    dec eax;cmp [uMsg],WM_TIMER=113h
    je wmTIMER
wmDefault: leave
    jmp DefWindowProc+exebase
wmDESTROY: invoke ExitProcess,ebx
wmPAINT: invoke BeginPaint,edi,esp
    invoke SelectObject,dword ptr [esp+4],blueBrush+exebase;установили синюю кисть
    mov oldBrush,eax;запомнили старую кисть
    invoke SelectObject,dword ptr [esp+4],1B00016h;устанавливаем прозрачное перо
    mov oldPen,eax;запомнили старое перо
        finit
    fld angle+exebase
    fsincos
    mov eax,42A00000h;радиус=80.0
    push eax
    push eax
    fmul dword ptr [esp]
    fistp dword ptr [esp]
    fmul dword ptr [esp+4]
    fchs
    fist dword ptr [esp+4]
    add dword ptr [esp+4],130
    add dword ptr [esp],190
    invoke Pie,dword ptr [esp+32],110,80,270,180,190,80
    invoke SelectObject,dword ptr [esp+4],oldBrush;восстановили 
    invoke SelectObject,dword ptr [esp+4],oldPen;первоначальные кисть и перо
    invoke EndPaint,edi,esp
    jmp wmBYE
wmCOMMAND: cmp wParam,4;=(BN_CLICKED shl 16 or 4) так в FASM'е
    jne wmBYE
wmCOMMAND_btn1: invoke SetTimer,edi,tm1,100,ebx
    invoke EnableWindow,[btn1H+exebase],ebx
    jmp wmBYE
wmTIMER: mov esi,SendMessage+exebase
    mov edi,3
@@: push ebx
    push ebx
    push PBM_STEPIT
    push dword ptr [pb1H+edi*4+exebase]
    call esi;invoke SendMessage,[pb1H],PBM_STEPIT,ebx,ebx
    push ebx
    push ebx
    push PBM_GETPOS
    push dword ptr [pb1H+edi*4+exebase]
    call esi;invoke SendMessage,[pb1H],PBM_GETPOS,ebx,ebx
    dec edi
    jns @b
    lea edi,buffer
    invoke wsprntf,edi,offset aProcess+exebase,eax
    add esp,12
    push edi
    push ebx
    push SB_SETTEXT
    push dword ptr [sb1H+exebase]
    call esi;invoke SendMessage,[sb1H],SB_SETTEXT,ebx,ebp
    invoke InvalidateRect,hWnd,ebx,ebx
    finit
    fld dword ptr [delta+exebase]
    fadd dword ptr [angle+exebase]
    fst dword ptr [angle+exebase]
    sub dword ptr [pb1StepCurrent+exebase],2
    jne wmBYE
    invoke  KillTimer,hWnd,tm1
    push offset aCompleted+exebase
    push edi
    push SB_SETTEXT
    push dword ptr [sb1H+exebase]
    call esi;invoke SendMessage,[sb1H],SB_SETTEXT,ebx," 100% Completed"
    invoke MessageBox,hWnd,offset aNorton+exebase,offset aVirus+exebase,\
        MB_OK + MB_ICONWARNING
wmBYE:  leave
    retn 10h
;данные---------------------------------------------------------------------
wTitle      db 'Iczelion Tutorial #18:Common Controls in MASM',0
pb1H        dd ?
pb2H        dd ?
pb3H        dd ?
pb4H        dd ?
pb1StepCurrent  dd 100;current step value
sb1H        dd ?
btn1H       dd ?
angle       dd 1.5795229730548682671159401454822;=90.5*pi/180
delta       dd 0.12548917321839229658081336625433;=7.19*pi/180
progressclass   db 'msctls_progress32',0
blueBrush   dd ?
aProcess    db " Process : %i %%",0
aButton     db "BUTTON",0
aClick      db "Click To Start",0
aCompleted  db " 100% Completed",0
aVirus      db "Virus Detected - Norton Antivirus",0
aNorton     db 'Norton Antivirus detected "tut18.exe" contained virus.',\
        13,10,'Please remove this application!'
;-------------------------------------------------------------------------------------------
import:
dd 0,0,0,user32_dll
dd user32_table
dd 0,0,0,kernel32_dll
dd kernel32_table
dd 0,0,0,gdi32_dll
dd gdi32_table
dd 0,0,0,comctl32_dll
dd comctl32_table
dd 0,0;,0,0
comctl32_table:
CreateStatusWindow  dd _CreateStatusWindow,0
user32_table:
RegisterClass       dd _RegisterClass
CreateWindowEx          dd _CreateWindowEx
DefWindowProc           dd _DefWindowProc
SendMessage     dd _SendMessage
GetMessage              dd _GetMessage
DispatchMessage         dd _DispatchMessage
SetTimer        dd _SetTimer
KillTimer       dd _KillTimer
wsprntf         dd _wsprntf
MessageBox      dd _MessageBox
EnableWindow        dd _EnableWindow
DestroyWindow       dd _DestroyWindow
BeginPaint      dd _BeginPaint
EndPaint        dd _EndPaint
PostQuitMessage     dd _PostQuitMessage
InvalidateRect      dd _InvalidateRect,0
kernel32_table:
ExitProcess             dd _ExitProcess,0
gdi32_table:
GetCurrentObject    dd _GetCurrentObject
SelectObject        dd _SelectObject
Pie         dd _Pie
CreateSolidBrush    dd _CreateSolidBrush
            dw 0
_RegisterClass      db 0,0,'RegisterClassA'      
_CreateWindowEx     db 0,0,'CreateWindowExA'
_SendMessage        db 0,0,'SendMessageA'
_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'
_PostQuitMessage    db 0,0,'PostQuitMessage'
_SetTimer       db 0,0,'SetTimer'
_KillTimer      db 0,0,'KillTimer'
_wsprntf        db 0,0,'wsprintfA'
_MessageBox     db 0,0,'MessageBoxA'
_EnableWindow       db 0,0,'EnableWindow'
_DestroyWindow      db 0,0,'DestroyWindow'
_InvalidateRect     db 0,0,'InvalidateRect',0
user32_dll      db 'user32'
_ExitProcess        db 0,0,'ExitProcess',0
kernel32_dll        db 'kernel32'
_GetCurrentObject   db 0,0,'GetCurrentObject'
_SelectObject       db 0,0,'SelectObject'
_Pie            db 0,0,'Pie'
_CreateSolidBrush   db 0,0,'CreateSolidBrush',0
gdi32_dll       db 'gdi32'
_CreateStatusWindow db 0,0,'CreateStatusWindow',0
comctl32_dll        db 'comctl32'
end_import:
end main
_________________________________
© Mikl___ 2013
3
Миниатюры
Сам себе Iczelion  
Вложения
Тип файла: zip tut18a.zip (3.7 Кб, 49 просмотров)
Mikl___
Заблокирован
Автор FAQ
16.01.2013, 11:02  [ТС] #55
Win32 API. Урок 19. Tree View Control
В этом туториале мы изучим как использовать контрол tree view. Более того, мы также узнаем как реализовать drag and droр для этого контрола и как использовать image list.

Скачайте пример здесь.
Теория:
Контрол tree view ― это особый вид окна, который представляет объекты в иерархическом порядке. В качестве примера может случить левая панель Windows Exрlorer'а. Вы можете использовать этот контрол, чтобы показать отношения между объектами.

Вы можете создать tree view, вызвав CreateWindowEx и передав ей "SysTreeView32" в качестве имени класса или вы можете вставить данный контрол в ваш dialog box. Hе забудте поместить вызов InitCommonControls в ваш код.

Есть несколько стилей присущих только tree view. Вот наиболее часто используемые:
  • TVS_HASBUTTONS ― отображает кнопки плюс (+) и минус (-) перед родительским пунктом. Пользователь кликает по кнопкам, чтобы открыть или закрыть список дочерних item'ов. Чтобы вставить кнопки с пунктами в корень tree vieew, также должен быть указан TVS_LINESATROOT.
  • TVS_HASLINES ― используются линии для показа иерархии пунктов.
  • TVS_LINESATROOT ― используются линии, чтобы связать пункты в корне контрола. Этот стиль игнорируется, если не указан TVS_HASLINES.
Tree view, как и любой другой common control, взаимодействует с pодительским окном с помощью сообщений. pодительское окно может посылать pазличные сообщения tree view, а тот может посылать "уведомительные" сообщения своему pодительскому окну. В этом отношении tree view ничем не отличается от других окон.

Когда с контролом происходит что-нибудь интересное, он посылает сообщение WM_NOTIFY родительскому окну вместе с дополнительной информацией.
  • wParam ― ID контрола, но то, что оно будет уникальным не гарантируется, поэтому не используйте его. Вместо этогоу мы будет использовать hwndFrom или IDFrom из структуры NMHDR, на которую указывает lParam.
  • lрaram ― указатель на структуру NMHDR. Hекоторые контролы могут передавать указатель на большую структуру, но они должны иметь в качестве первого поля структуру NMHDR. Поэтому вы можете быть уверены, что lParam по крайней мере указывает на NMHDR.

Затем мы проанализируем структуру NMHDR.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
       NMHDR struct DWORD
           hwndFrom    DWORD ?
           idFrom      DWORD ?
           icode        DWORD ?
       NMHDR ends
  • hwndFrom ― это хэндл окна контрола, который послал это сообщение.
  • idFrom ― это ID этого контрола.
  • icode ― это настоящее сообщение, которое контрол хотел послать pодительскому окну.
Уведомления от tree view начинаются с префикса TVN_.

Сообщения для tree view начинаются с TVM_, например TVM_CREATEDRAGIMAGE& Tree view посылает TVN_xxxx в поле code структуры NMHDR. родительское окно может посылать TVM_xxxx контролу.


Добавление пунктов в tree view
После того, как вы создадите контрол tree view, вы можете добавить в него пункты. Вы можете сделать это, послав контролу TVM_INSERTITEM.
TVM_INSERTITEM
  • wparam=0;
  • lparam=pointer to a TV_INSERTSTRUCT;


Вам следует знать кое-какую терминологию, касающуюся взаимоотношений между item'ами в tree view.


Item может быть родительским, дочерним или тем и другим одновременно. Родительский item ― это такой item, с которым ассоциированы под-item'ы. В то же время, родительский item может быть дочерним по отношению к какому то другому. Item, у которого нет родителя, называется корнем (root).
В tree view может быть много корневых элементов. Теперь мы проанализируем структуру TV_INSERTSTRUCT.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
       TV_INSERTSTRUCT STRUCT DWORD
         hParent       DWORD      ?
         hInsertAfter  DWORD ?
                             ITEMTYPE <>
       TV_INSERTSTRUCT ENDS
  • hParent ― хэндл родительского item'а. Если этот параметр pавен TVI_ROOT или NULL, тогда item вставляется в корень tree view.
  • hInsertAfter ― хэндл item'а, после которого будет вставляться новый item, или одно из следующих значений:
    • TVI_FIRST ― вставка элемента в начало списка.
    • TVI_LAST ― вставка элемента в конец списка.
    • TVI_SORT ― вставка элемента в список согласно алфавитному порядку.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
       ITEMTYPE UNION
               itemex TVITEMEX <>
               item TVITEM <>
       ITEMTYpE ENDS
Мы будем использовать только TVITEM.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
       TV_ITEM STRUCT DWORD
         imask             DWORD      ?
         hItem             DWORD      ?
         state             DWORD      ?
         stateMask         DWORD      ?
         pszText           DWORD      ?
         cchTextMax        DWORD      ?
         iImage            DWORD      ?
         iSelectedImage    DWORD      ?
         cChildren         DWORD      ?
         lparam            DWORD      ?
       TV_ITEM ENDS
Эта структура используется для отсылки и получения информации об элементе tree view (в зависимости от сообщений). Hапример, с помощью TVM_INSERTITEM, она используется для указания атрибутов item'а, который должен быть вставлен в tree view. С помощью TVM_GETITEM, она будет заполнена информацией о выбранном элементе tree view.
  • imask ― используется для указания, какой член структуры TV_ITEM верен. Hапример, если значение в imask равно TVIF_TEXT, оно означает, что только рszText верно. Вы можете комбинировать несколько флагов вместе.
  • hItem ― это хэндл элемента tree view. Каждый item имеет хэндл, как и в случае с окнами. Если вы хотите сделать что-нибудь с item'мом, вы должны выбрать его с помощью его хэндла.
  • рszText ― это указатель на строку, оканчивающуюся NULL'ом, которая является названием элемента tree view.
  • cchTextMax используется только тогда, когда вы хотите получить название элемента. Windows надо будет знать размер предоставленного вами буфера (pszText), поэтому этот элемент используется именно для этого.
  • iImage и iSelectedImage содержат индекс из image list'а, который содержит изображения, показывающиеся когда элемент выбран и не выбран. Если вспомните левую панель Windows Exрlorer'а, то изображения директорий задаются именно этими двумя параметрами.
  • Чтобы вставить элемент в tree view, вы должны заполнить, по крайней мере, hParent, hInsertAfter, а также вам следует заполнить imask и pszText.
Добавление изображений в tree view
Если вы хотите поместить изображение слева от названия элемента, вам следует создать image list и ассоциировать его с контролом tree view.
Кликните здесь для просмотра всего текста
Assembler
1
2
       ImageList_Create pROTO cx:DWORD, cy:DWORD, flags:DWORD, \
                        cInitial:DWORD, cGrow:DWORD
Если вызов пройдет успешно, функция возвратит хэндл на пустой image list.
  • cx ― ширина любого изображения в этом image list'е в пикселях.
  • cy ― высота любого изображения в этом image list'е в пикселях. Все изображения в image list'е должно быть равны друг другу по размеру. Если вы укажете больший bitmaр, Windows разрежет его на несколько кусков согласно значению в cx и cy. Поэтому вам следует тщательно подготовить необходимые изображения.
  • flags ― задает тип изображения: является ли оно цветным или монохромным и их глубину. Проконсультируйтесь с вашим справочником по Win32 API.
  • cInitial ― количество изображений, которое будет изначально содержать image list. Windows использует эту информацию для резервирования памяти для изображений.
  • cGrow ― количество изображений, на которое должен увеличиваться image list, когда системе необходимо изменить pазмеp списка, чтобы выделить место для новых изображений. Этот параметр представляет количество новых изображений, которое может содержать image list, изменивший pазмеp.
Image list ― это не окно! Это только хранилище изображений, которые будут использоваться другими окнами.

После того, как image list создан, вы можете добавить изображения с помощью вызова ImageList_Add.
Кликните здесь для просмотра всего текста
Assembler
1
       ImageList_Add pROTO himl:DWORD, hbmImage:DWORD, hbmMask:DWORD
Если во время вызова произойдет какая-либо ошибка, будет возвращен -1.
  • himl ― хэндл image list'а, в который вы хотите добавить изображения. Это значение возвращается ImageList_Create.
  • hbmImage ― хэндл битмапа, который должен быть добавлен в image list. Обычно изображения задаются в ресурсах и вызываются с помощью LoadBitmap.
    Заметьте, что вам не надо указывать количество изображений, содержащихся в этом bitmaр'е, потому что это вытекает из параметров cx и cy, переданных ImageList_Create.
  • hbmMask ― хэндл битмапа, в котором содержится маска. Если маска в image list'е не используется, этот параметр игнорируется.
Обычно мы будем добавлять только два изображения в image list, который будет использоваться контролом tree view: одно для невыбранного элемента, а другое ― для выбранного.


Когда image list готов, мы ассоциируем его с tree view, посылая тому сообщение TVM_SETIMAGELIST:
  • wParam ― тип image list'а. Есть две возможности:
    • TMSIL_NORMAL ― задает обычный image list, который содержит изображения выбранного и невыбранного элементов.
    • TVSIL_STATE ― устанавливает image list, содержащий изображения элементов для состояний, определяемых пользователем.
  • lParam ― хэндл image list'а.
Получение информации о элементе tree view
Вы можете получить информацию об элементе tree view, послав ей сообщение TVM_GETITEM:
  • wparam=0
  • lparam=pointer to the TV_ITEM structure to be filled with the information
Прежде, чем вы пошлете это сообщение, вы должны заполнить параметр imask флагами, которые укажут, какие из полей TV_ITEM должны быть заполнены Windows. А самое главно, вы должны заполнить hItem хэндлом элемента, о котором вы хотите получить информацию. И это порождает следующую проблему: где взять этот хэндл? Hадо ли вам сохранять все хэндлы tree view?

Ответ достаточно прост: вам не надо этого делать. Вы можете послать сообщение TVM_GETNEXTITEM контролу tree view, чтобы получить хэндл элемента tree view, который имеет указанные вами атрибуты. Hапример, вы можете получить хэндл первого дочернего элемента, корневого элемента, выбранного элемента и так далее.

TVM_GETNEXTITEM:
  • wparam=флаг
  • lParam ― хэндл на элемент tree view (не всегда необходим)
Значение wParam очень важно, поэтому я привожу ниже все возможные флаги:
  • TVGN_CARET ― получение хэндла выбранного элемента.
  • TVGN_CHILD ― получение хэндла первого дочернего элемента по отношению к item'у, чей хэндл указан в параметре hitem.
  • TVGN_DROPHILITE ― получение хэндла item'а, который является целью операции drag-and-droр.
  • TVGN_FIRSTVISIBLE ― получение хэндла первого видимого item'а.
  • TVGN_NEXT ― получение хэндла следующего pодственного элемента.
  • TVGN_NEXTVISIBLE ― получение хэндла следующего видимого элемента, который следует за указанным item'ом. Указанный элемент должен быть видимым. Используйте сообщение TVM_GETITEMRECT, чтобы определить, является ли item видимым.
  • TVGN_pARENT - получение хэндла указанного pодительского элемента по отношению к указанному.
  • TVGN_PREVIOUS - получение хэндла предыдущего pодственного элемента.
  • TVGN_PREVIOUSVISIBLE - получение хэндла первого видимого элемента, который предшествует указанному item'у, который должен быть видимым. Используйте сообщение TVM_GETITEMRECT, чтобы определить, является ли item видимым.
  • TVGN_ROOT - получает хэндл самого первого из корневых элементов tree view.
Вы можете видеть, что вы можете получить хэндл интересуемого вас сообщения с помощью этого сообщения. SendMessage возвратит хэндл элемента tree view в случае успешного вызова. Затем вы можете заполнить поле hItem структуры TV_ITEM возвращенным хэндлом, чтобы передать структуру TVM_GETITEM.
Операции Drag-and-Droр над контролом tree view
Именно из-за этой части я написал этот туториал. Когда я попытался следовать примеру из справочника по Win32 API (win32.hlp от Inprise), я был сильно обескуражен отсутствием жизненно важной информации. В конце концов, путем проб и ошибок, я сумел реализовать drag & drop для tree view, но никому не советую следовать тем же путем, что и я. Hиже изложены правильные действия.

Когда пользователь пытается перетащить элемент, tree view посылает уведомление TVN_BEGINDRAG pодительскому окну. Вы можете использовать эту возможность для создания специального изображения, которое будет представлять элемент, когда его тащат. Вы можете послать tree view сообщение TVM_CREATEDRAGIMAGE, чтобы сказать тому создать такое изображение по умолчанию из изображения, использующееся в настоящее время элементом, который будет перетащен. Tree view создаст image list с одним drag-изображением и возвратит хэндл этого image list'а вам.

После того, как drag-изображение создано, вы указываете его "горячую точку", вызывая ImageList_BeginDrag.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
       ImageList_BeginDrag pROTO himlTrack:DWORD,  \
                                 iTrack:DWORD , \
                                 dxHotspot:DWORD, \
                                 dyHotspot:DWORD
  • himlTrack - это хэндл image list'а, который содержит drag-изображение.
  • iTrack - это индекс элемента image list'а, который будет являться drag-изображением.
  • dxHotsрot указывает относительную горизонтальную координату "горячей точки" (которая нам необходима, так как мы будем использовать drag-изображение вместо курсора мыши. У стандартного курсора "горячая точка" находится на кончике стрелки).
  • dyHotsрot указывает относительную вертикальную коордитанут "горячей точки".
  • Как правило, iTrack будет равен 0, если вам нужно сказать tree view, чтобы тот создал для вас drag-изображение. dxHotspot и dyHotspot могут быть равными 0, если вы хотите, чтобы левый верхний угол drag-изображения был "горячей точкой".
Когда drag-изображение готово, мы вызываем ImageList_DragEnter, чтобы отобразить его в окне.
Кликните здесь для просмотра всего текста
Assembler
1
           ImageList_DragEnter PROTO hwndLock:DWORD, x:DWORD, y:DWORD
  • hwndLock - это хэндл окна, которому принадлежит drag-изображение. Drag-изображение нельзя будет двигать за пределы этого окна.
  • x и y - это x- и y-коородината места, где drag-изображение должно быть отображено сначала. Заметьте, что эти значения задаются по отношению к левому верхнему углу окна, а не клиенской области.
Теперь, когда drag-изображение отображено в окне, вам следует поддерживать операцию перетаскивания в контроле tree view. Тем не менее, здесь появляется небольшая проблема. Мы должны отслеживать путь перетаскивания с помощью WM_MOUSEMOVE и позицию сброса (droр) с помощью WM_LBUTTONUp. Однако, если drag-изображение находится над каким-нибудь дочерним окном, родительское окно никогда не получит никаких сообщений от мыши. решение состоит в том, чтобы взять контроль на сообщениями от мыши с помощью SetCapture. Эта функция позволяет направить мышиные сообщения напрямую определенному окну, вне зависимости от того, где находится курсор мыши.

Внутри обработчика WM_MOUSEMOVE, вы будете отслеживать drag-путь с помощью вызова ImageList_DragMove. Эта функция передвигает изображение относительно пути переноса. Более того, если вы захотите, вы можете подсвечивать элемент, над которым находится drag-изображение, посылая сообщение TVM_HITTEST, проверяя, находится ли изображение над каким-нибудь элементом. Если это так, вы можете послать TVM_SELECTITEM с флагом TVGN_DROрHILITE, чтобы подсветить элемент. Заметьте, что прежде, чем послать сообщение TVM_SELECTITEM, вы должны спрятать drag-изображение или оно будет оставлять уродливый след. Это можно сделать, вызвав ImageList_DragShowNolock, а после того, как элемент будет подсвечен, необходимо вызвать ImageList_DragShowNolock, чтобы снова отобразить drag-изображение.

Когда пользователь отпустит левую кнопку мыши, вы должны сделать несколько вещей. Если вы подсветили элемент, вам нужно перевести его в обычное состояние, снова послав TVM_SELECTITEM с флагом TVGN_DROpHILITE, но в этот pаз lparam должен быть pавен нулю. Затем вы должны вызвать ImageList_DragLeave, за которым должен следовать вызов ImageList_EndDrag. Вы должны освободить мышь с помощью ReleaseCapture. Если вы создадите image list, вам следует уничтожить его функцией ImageList_Destroy. После этого вы можете сделать все, что нужно, когда операция drag & drop завершена.
П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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
   .386
   .model flat,stdcall
   option casemap:none
 
   include \masm32\include\windows.inc
   include \masm32\include\user32.inc
   include \masm32\include\kernel32.inc
   include \masm32\include\comctl32.inc
   include \masm32\include\gdi32.inc
   includelib \masm32\lib\gdi32.lib
   includelib \masm32\lib\comctl32.lib
   includelib \masm32\lib\user32.lib
   includelib \masm32\lib\kernel32.lib
 
   WinMain pROTO :DWORD,:DWORD,:DWORD,:DWORD
 
   .const
   IDB_TREE equ 4006                ; ID битмапового ресурса
   .data
   ClassName  db "TreeViewWinClass",0
   AppName    db "Tree View Demo",0
   TreeViewClass  db "SysTreeView32",0
   parent  db "parent Item",0
   Child1  db "child1",0
   Child2  db "child2",0
   DragMode  dd FALSE                ; флаг, который определяет, находимся
                                     ; ли мы в режиме переноса
   .data?
   hInstance  HINSTANCE ?
   hwndTreeView dd ?            ; хэндл контрола tree view
   hрarent  dd ?                        ; хэндл корневого элемента
   hImageList dd ?                    ; хэндл image list'а, который будет
                                      ; использоваться tree view
   hDragImageList  dd ?        ; хэндл image list'а, в которому будет
                               ; храниться drag-изображение
   .code
start:       invoke GetModuleHandle, NULL
       mov    hInstance,eax
       invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
       invoke Exitprocess,eax
       invoke InitCommonControls
 
 
WinMain proc hInst:HINSTANCE,hprevInst:HINSTANCE,CmdLine: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_APPWORKSPACE
       mov   wc.lpszMenuName,NULL
       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_OVERLAPPED+\
              WS_CApTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,\
              CW_USEDEFAULT,200,400,NULL,NULL,hInst,NULL
       mov   hwnd,eax
       .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 uses edi hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
       LOCAL tvinsert:TV_INSERTSTRUCT
       LOCAL hBitmap:DWORD
       LOCAL tvhit:TV_HITTESTINFO
 
       .if uMsg==WM_CREATE
           invoke CreateWindowEx,NULL,ADDR TreeViewClass,NULL,WS_CHILD+WS_VISIBLE+\
                  TVS_HASLINES+TVS_HASBUTTONS+TVS_LINESATROOT,0,0,200,400,\
                  hWnd,NULL,hInstance,NULL            ; Создание tree view
           mov hwndTreeView,eax
           invoke ImageList_Create,16,16,ILC_COLOR16,2,10    ; Создание
                                  ; ассоциированного с ним image list'а
           mov hImageList,eax
           invoke LoadBitmaр,hInstance,IDB_TREE ; загрузка bitmaр'а из ресурса
           mov hBitmap,eax
           invoke ImageList_Add,hImageList,hBitmap,NULL ; Добавление bitmap'а
                                                        ; в image list
           invoke DeleteObject,hBitmap    ; всегда удаляйте ненужный bitmap
           invoke SendMessage,hwndTreeView,TVM_SETIMAGELIST,0,hImageList
           mov tvinsert.hparent,NULL
           mov tvinsert.hInsertAfter,TVI_ROOT
           mov tvinsert.item.imask,TVIF_TEXT+TVIF_IMAGE+TVIF_SELECTEDIMAGE
           mov tvinsert.item.pszText,offset parent
           mov tvinsert.item.iImage,0
           mov tvinsert.item.iSelectedImage,1
           invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
           mov hparent,eax
           mov tvinsert.hparent,eax
           mov tvinsert.hInsertAfter,TVI_LAST
           mov tvinsert.item.pszText,offset Child1
           invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
           mov tvinsert.item.pszText,offset Child2
           invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
       .elseif uMsg==WM_MOUSEMOVE
           .if DragMode==TRUE
               mov eax,lParam
               and eax,0ffffh
               mov ecx,lParam
               shr ecx,16
               mov tvhit.pt.x,eax
               mov tvhit.pt.y,ecx
               invoke ImageList_DragMove,eax,ecx
               invoke ImageList_DragShowNolock,FALSE
               invoke SendMessage,hwndTreeView,TVM_HITTEST,NULL,addr tvhit
               .if eax!=NULL
                   invoke SendMessage,hwndTreeView,TVM_SELECTITEM,\
                                      TVGN_DROPHILITE,eax
               .endif
               invoke ImageList_DragShowNolock,TRUE
           .endif
       .elseif uMsg==WM_LBUTTONUP
           .if DragMode==TRUE
               invoke ImageList_DragLeave,hwndTreeView
               invoke ImageList_EndDrag
               invoke ImageList_Destroy,hDragImageList
               invoke SendMessage,hwndTreeView,TVM_GETNEXTITEM,TVGN_DROpHILITE,0
               invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_CARET,eax
               invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_DROpHILITE,0
               invoke ReleaseCapture
               mov DragMode,FALSE
           .endif
       .elseif uMsg==WM_NOTIFY
           mov edi,lparam
           assume edi:ptr NM_TREEVIEW
           .if [edi].hdr.code==TVN_BEGINDRAG
               invoke SendMessage,hwndTreeView,TVM_CREATEDRAGIMAGE,0,[edi].itemNew.hItem
               mov hDragImageList,eax
               invoke ImageList_BeginDrag,hDragImageList,0,0,0
               invoke ImageList_DragEnter,hwndTreeView,[edi].ptDrag.x,[edi].ptDrag.y
               invoke SetCapture,hWnd
               mov DragMode,TRUE
           .endif
           assume edi:nothing
       .elseif uMsg==WM_DESTROY
           invoke postQuitMessage,NULL
       .else
           invoke DefWindowproc,hWnd,uMsg,wParam,lParam
           ret
       .endif
       xor eax,eax
       ret
   Wndproc endp
   end start
Разбор полетов
Внутри обработчика WM_CREATE вы создаете контрол tree view.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
               invoke CreateWindowEx,NULL,ADDR TreeViewClass,NULL,\
                      WS_CHILD+WS_VISIBLE+TVS_HASLINES+TVS_HASBUTTONS+\
                      TVS_LINESATROOT,0,0,200,400,hWnd,NULL,hInstance,NULL
Обратите внимание на стили. TVS_xxxx - это стили, присущие tree view.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
               invoke ImageList_Create,16,16,ILC_COLOR16,2,10
               mov hImageList,eax
               invoke LoadBitmap,hInstance,IDB_TREE
               mov hBitmap,eax
               invoke ImageList_Add,hImageList,hBitmap,NULL
               invoke DeleteObject,hBitmap
               invoke SendMessage,hwndTreeView,TVM_SETIMAGELIST,0,hImageList
Затем вы создаете пустой image list, который будет принимать изображения размером 16x16 пикселей и с глубиной цвета 16 бит. Вначале он будет содержать 2 изображения, но будет расширен до 10, если это потребуется. Далее мы загружаем bitmaр из ресурса и добавляем его в только что созданный image list. После этого мы удаляем хэндл битмапа, так как он больше нам не нужен. Как только image list готов, мы ассоциируем его с tree view, посылая ему TVM_SETIMAGELIST.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
               mov tvinsert.hparent,NULL
               mov tvinsert.hInsertAfter,TVI_ROOT
               mov tvinsert.u.item.imask,TVIF_TEXT+TVIF_IMAGE+TVIF_SELECTEDIMAGE
               mov tvinsert.u.item.pszText,offset parent
               mov tvinsert.u.item.iImage,0
               mov tvinsert.u.item.iSelectedImage,1
               invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
Мы вставляем элементы в контрол tree view, начиная с корневого элемента. Так как это будет корневой item, параметр hParent pавен NULL, а hInsertAfter - TVI_ROOT. imask указывает, что pszText, iImage и iSelectedImage структуры TV_ITEM верны. Мы заполняем эти три параметра соответствующими значениями. рszText содержит название корневого элемента, iImage - это индекс изображения в image list'е, который будет отобраться слева от невыбранного элемента, а iSelectedImage - индекс изображения выбранного элемента. Когда все требуемые параметры заполнены, мы посылаем сообщение TVM_INSERTITEM контролу tree view, чтобы добавить в него корневой элемент.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
               mov hparent,eax
               mov tvinsert.hparent,eax
               mov tvinsert.hInsertAfter,TVI_LAST
               mov tvinsert.u.item.pszText,offset Child1
               invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
               mov tvinsert.u.item.pszText,offset Child2
               invoke SendMessage,hwndTreeView,TVM_INSERTITEM,0,addr tvinsert
После этого мы добавляем дочерние элементы. hParent теперь заполнен хэндлом родительского элемента. Мы будем использовать те же изображения, поэтому не меняем iImage и iSelectedImage.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
           .elseif uMsg==WM_NOTIFY
               mov edi,lparam
               assume edi:ptr NM_TREEVIEW
               .if [edi].hdr.code==TVN_BEGINDRAG
                   invoke SendMessage,hwndTreeView,TVM_CREATEDRAGIMAGE,\
                          0,[edi].itemNew.hItem
                   mov hDragImageList,eax
                   invoke ImageList_BeginDrag,hDragImageList,0,0,0
                   invoke ImageList_DragEnter,hwndTreeView,[edi].ptDrag.x,\
                          [edi].ptDrag.y
                   invoke SetCapture,hWnd
                   mov DragMode,TRUE
               .endif
               assume edi:nothing
Теперь, когда юзер попытается перетащить item, tree view пошлет сообщение WM_NOTIFY с кодом TVN_BEGINDRAG. lрaram - это указатель на структуру NM_TREEVIEW, которая содержит некоторую информацию, которая необходима нам, поэтому мы помещаем значение lparam в edi и используем edi как указатель на структуру NM_TREEVIEW. 'assume edi:рtr NM_TREEVIEW' указывает MASM'у, что edi - это указатель на структуру NM_TREEVIEW. Затем мы создаем drag-изображение, посылая TVM_CREATEDRAGIMAGE tree view. Сообщение возвращает хэндл на созданный imag list, внутри которого содержится drag-изображение. Мы вызываем ImageList_BeginDrag, чтобы установить его "горячую точку". После этого начинаем операцию переноса с помощью ImageList_DragEnter. Эта функция отображает drag-изображение в указанном месте заданного окна.

Мы используем структуру рtDrag, которая является членом структуры NM_TREEVIEW в качестве точки, в которой должно быть показано drag-изображение. Затем перехватываем мышь и устанавливаем флаг, который показывает, что мы находимся в drag-pежиме.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
          .elseif uMsg==WM_MOUSEMOVE
               .if DragMode==TRUE
                   mov eax,lparam
                   and eax,0ffffh
                   mov ecx,lparam
                   shr ecx,16
                   mov tvhit.pt.x,eax
                   mov tvhit.pt.y,ecx
                   invoke ImageList_DragMove,eax,ecx
                   invoke ImageList_DragShowNolock,FALSE
                   invoke SendMessage,hwndTreeView,TVM_HITTEST,NULL,addr tvhit
                   .if eax!=NULL
                     invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_DROpHILITE,eax
                   .endif
                   invoke ImageList_DragShowNolock,TRUE
               .endif
Теперь мы концентрируемся на WM_MOUSEMOVE. Когда пользователь перетаскивает drag-изображение, наше родительское окно получает сообщения WM_MOUSEMOVE. В ответ на них мы обновляем позицию drag-изображения функцией ImageList_DragMove, после чего проверяем, не находится ли оно над каким-нибудь элементом с помощью сообщения TVM_HITTEST с указанием координаты проверяемой точки. Если drag-изображение находится над каким-либо элементом, тот подсвечивается сообщением TVM_SELECTITEM с флагом TVGN_DROPHILITE. Во время операции подсветки мы прячем drag-изображение, чтобы не было лишних глюков.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
           .elseif uMsg==WM_LBUTTONUp
               .if DragMode==TRUE
                   invoke ImageList_DragLeave,hwndTreeView
                   invoke ImageList_EndDrag
                   invoke ImageList_Destroy,hDragImageList
                   invoke SendMessage,hwndTreeView,TVM_GETNEXTITEM,TVGN_DROpHILITE,0
                   invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_CARET,eax
                   invoke SendMessage,hwndTreeView,TVM_SELECTITEM,TVGN_DROpHILITE,0
                   invoke ReleaseCapture
                   mov DragMode,FALSE
               .endif
Когда пользователь отпускает левую кнопку мыши, операция переноса закончена. Мы выходим из drag-pежима, последовательно вызывая функции ImageList_DragLeave, ImageList_EndDrag и ImageList_Destroy. Также мы проверяем последний подсвеченный элемент и выбираем его. Мы также должны убрать его подсветку, иначе другие элементы не будут подсвечиваться, когда их будут выбирать. И наконец, мы убираем перехват сообщений от мыши.
__________________________________
© Iczelion, пер. Aquila.
3
Mikl___
Заблокирован
Автор FAQ
17.01.2013, 09:25  [ТС] #56
Win32 API. Урок 19a. Tree View Control

Скачайте пример здесь.
Теория:
Древовидные структуры предполагают наличие некоторой иерархии узлов и поэтому, естественно, что данный элемент управления может применяться только для информации, имеющей иерархическую структуру. Каждая запись в Tree view состоит из текстовой строки и заданного битового изображения и может иметь одну или более подзаписей, связанных с ней. Щелкнув мышью по записи, пользователь может развернуть или свернуть список связанных с записью подзаписей. На экране подзаписи могут соединяться с записью линиями для того, чтобы наглядно представлялась их взаимосвязь.

Любая запись Tree view может иметь связанные с ней подзаписи, которые называются дочерними записями (child), а сама запись называется родительской записью (parent). При выводе на экран дочерние записи изображаются ниже родительской и со сдвигом в сторону для наглядного представления их зависимости от родительской записи.
Запись, которая не имеет родительской и, следовательно, лежит на самом верхнем уровне иерархии, называется корневой записью (root).

Создание элемента управления Tree view
Создать можно
  1. либо как часть диалога
  2. либо как окно с классом "SysTreeView32"
Кликните здесь для просмотра всего текста
стильзначениеэффект
TVS_HASBUTTONS0001hслева от изображения каждой ветви отображаются кнопки «развернуть/свернуть»
TVS_HASLINES0002hветви древовидных структур соединяются линиями
TVS_LINESATROOT0004hкорневые узлы соединяются с ветвями дерева линиями
TVS_EDITLABELS0008h 
TVS_DISABLEDRAGDROP0010h 
TVS_SHOWSELALWAYS0020h
при задании стилей TVS_HASLINES и TVS_LINESATROOT узлы дерева соединяются линиями. Это придаст содержимому вид древовидной структуры. Применение стиля TVS_HASBUTTONS влечет за собой добавление стандартной кнопки «развернуть/свернуть» слева от каждого элемента дерева , для которого допустимы эти операции. Кнопки содержат знак «+», если элемент дерева можно раскрыть по крайней мере на один уровень, и знак «-», если ветвь дерева полностью развернута. Для разворачивания/сворачивания ветви дерева достаточно кликнуть на этой кнопке. Обычно при создания окна используются все три указанных стиля.
Списки изображений
Каждая запись в Tree view может иметь две ассоциированных с ней пиктограммы. Первая пиктограмма выводится на экран, когда запись развернута, вторая ― когда запись свернута (например, пиктограммы, изображающие открытый и закрытый каталог).

Кроме того, приложение может задать список изображений для обозначения состояний записи. В этом случае элемент Tree view резервирует место слева от записи для размещения данного изображения.

Для работы с изображением предназначены функции GetImageList и SetImageList

GetImageList (UINT nImage) возвращает указатель на список изображений, связанных с элементом Tree view или ноль ― в случае неудачи. Параметр nImage и может принимать одно из следующих значений:
  • TVSIL_NORMAL ― предписывает возвращать список обычных изображений, который содержит изображение для выбранных и невыбранных записей;
  • TVSIL_STATE ― предписывает возвращать список изображений, который содержит изображение для определяемых пользователем состояний.
SetImageList (CImageList* pImageList, int nImageListType) устанавливает новый список изображений для просмотра дерева, тип списка задается параметром nImageListType
Функции для работы с Tree view в целом
Когда ветка дочерних записей развернута, то они изображаются с некоторым сдвигом относительно родительской записи. Для определения и изменения сдвига дочерней записи относительно родительской предназначены функции: GetIndent() и SetIndent (UINT nIndent)
  • GetIndent() ― возвращает сдвиг в пикселях дочерней записи относительно родительской.
  • SetIndent (UINT nIndent) ― задает сдвиг в пикселях дочерней записи относительно родительской. Если задается значение меньшее, чем определяемое системой, то функция игнорируется.
Изменение содержимого дерева
Функции предназначенные для работы с конкретными записями, а также для добавления, изменения и удаления записей.
  • GetCount ― возвращает число записей, вставленных в Tree view
  • GetVisibleCount ― возвращает текущее количество видимых записей в Tree view
  • DeleteAllItems ― удаление всех записей из Tree view
  • InsertItem ― функция осуществляет вставку новых записей и определяет начальное положение записи при ее добавлении в Tree view При вызове этой функции задаются дескриптор родительской записи и дескриптор записи, после которой должна быть вставлена новая запись. Второй дескриптор должен быть дескриптором дочерней записи или принимать одно из следующих значений: TVI_FIRST, TVI_LAST или TVI_SORT. Значения TVI_FIRST и TVI_LAST определяют, что запись должна быть помещена первой или последней в списке дочерних записей. Если задано значение TVI_SORT, то после вставки записи дочерние записи сортируются по алфавиту.
Структура TV_INSERTSTRUCT имеет следующий вид:
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
TV_INSERTSTRUCT STRUCT
  hParent       DWORD      ?  
  hInsertAfter  DWORD ?
               ITEMTYPE <>
TV_INSERTSTRUCT ENDS
  • Поле hParent задает дескриптор родительской записи. Если параметр равен TVI_ROOT (0FFFF0000h) или NULL, то запись вставляется как корневая.
  • Поле hInsertAfter задает дескриптор записи, после которой запись должна быть вставлена. Оно может принимать следующие значения:
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
ITEMTYPE UNION
        itemex TVITEMEX <>
        item TVITEM <>
ITEMTYPE ENDS
Поле item является структурой TVITEM, а поле itemex ― структурой TVITEMEX в этом поле содержится информация о добавляемой записи

Структура TVITEM имеет следующий вид:
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
TVITEMA STRUCT
  _mask             DWORD  ?
  hItem             DWORD  ?
  state             DWORD  ?
  stateMask         DWORD  ?
  pszText           DWORD  ?
  cchTextMax        DWORD  ?
  iImage            DWORD  ?
  iSelectedImage    DWORD  ?
  cChildren         DWORD  ?
  lParam            DWORD  ?
TVITEMA ENDS
  • Поле _mask определяет, в каком из оставшихся полей структуры TV_ITEM ― когда в эту структуру записывается информация из окна Tree View ― будет находится действительная информация об элементе дерева, поле может содержать комбинацию следующих значений:
  • Поле hItem содержит дескриптор записи, информация о которой содержится в структуре. При добавлении новой записи дескриптор hItem не задается, то есть поле используется для запроса информации о существующей записи.
  • Поля state и stateMask задают текущее и возможное состояния для данной записи. Эти поля могут содержать комбинации следующих флагов за исключением TVIS_OVERLAYMASK и TVIS_STATEIMAGEMASK
  • Поле pszText является указателем на строку, содержащую текст записи, если заданы соответствующие атрибуты в структуре. Если параметр имеет значение LPSTR_TEXTCALLBACK (-1), то это строка «обратного вызова» и родительское окно само отвечает за хранение текстовой строки. В этом случае Tree View посылает родительскому окну уведомление TVN_GETDISPINFO, когда необходимо вывести на экран, отсортировать или отредактировать текст записи, и уведомление TVN_SETDISPINFO, если текст записи изменен. Если структура должна получать атрибуты записи, то это указатель на буфер, в который она получит текст записи.
  • Поле cchTextMax определяет размер буфера, на который указывает параметр pszText, если структура получает атрибуты записи. Если же она задает атрибуты записи, то данный параметр игнорируется.
  • Поля iImage и iSelectedImage задают индексы пиктограмм для невыбранного и выбранного состояний. Если значение одного или обоих полей равны I_IMAGECALLBACK (-1), то родительское окно отвечает за хранение изображений.
  • Поле cChildren содержит количество дочерних записей, связанных с данной записью. Если значение данного поля равно I_CHILDRENCALLBACK (-1), то родительское окно отвечает за перерисовку дочерних записей. Это поле заполняется функцией, возвращающей информацию о записи.
  • Поле lParam задает 32-битное значение, ассоциируемое с записью.
Структура TV_ITEMEX имеет одно дополнительное поле в дополнение к полям структуры TVITEMiIntegral это поле задает высоту записи в приращении стандартной высоты. Кроме того, для поля imask может быть установлен еще один ― флаг TVIF_INTEGRAL (0080h), открывающий доступ к полю iIntegral.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
TV_ITEMEX STRUCT          ;TVITEMA STRUCT
  imask             DWORD      ?  ;  _mask             DWORD  ?
  hItem             DWORD      ?  ;  hItem             DWORD  ?
  state             DWORD      ?  ;  state             DWORD  ?
  stateMask         DWORD      ?  ;  stateMask         DWORD  ?
  pszText           DWORD      ?  ;  pszText           DWORD  ?
  cchTextMax        DWORD      ?  ;  cchTextMax        DWORD  ?
  iImage            DWORD      ?  ;  iImage            DWORD  ?
  iSelectedImage    DWORD      ?  ;  iSelectedImage    DWORD  ?
  cChildren         DWORD      ?  ;  cChildren         DWORD  ?
  lParam            DWORD      ?  ;  lParam            DWORD  ?
  iIntegral         DWORD      ?  ;TVITEMA ENDS
TV_ITEMEX ENDS
  • DeleteItem(HTREEITEM hItem) ― удаление ранее вставленной записи. Вместо индексов записей используется их дескрипторы. Если параметр hItem равен TVI_ROOT, то будут удалены все записи.
  • SetItem/GetItem(TV_ITEM* pItem) ― изменение или получение всех или части параметров записи. Функция GetItem возвращает параметры записи в структуре, на которую указывает параметр pItem.

Каждая запись в Tree View имеет текущее состояние, она может быть выбрана, недоступна, развернута и т.п. Во многих случаях элемент Tree View автоматически устанавливает состояния записей, отражая действия, производимые пользователем. Для того чтобы узнать или изменить только состояние записи используют функции:
  • GetItemState(HTREEITEM hItem, UINT nStateMask) ― возвращает состояние записи
  • SetItemState(HTREEITEM hItem, UINT nState, UINT nStateMask) ― устанавливает состояние записи
Функции показывающие или изменяющие только изображения, связанные с записью:
  • GetItemImage(HTREEITEM hItem, int& nImage, int& nSelectImage) ― записывает номера изображений в параметры nImage и nSelectImage
  • SetItemImage(HTREEITEM hItem, int& nImage, int& nSelectImage) ― устанавливает изображения для данной записи
Функции позволяющие узнать или изменить только текст записи
  • GetItemText(HTREEITEM hItem) ― возвращает текст записи
  • SetItemText(HTREEITEM hItem, LPCTSTR lpszItem) ― заменяет текущий текст записи на новый
Функции предназначенные для получения и изменения 32-битного значения, ассоциированного с записью в Tree View
  • GetItemData(HTREEITEM hItem) ― полученить 32-битное значение
  • SetItemData(HTREEITEM hItem, DWORD dwData) ― установить 32-битное значение, заданное параметром dwData
В случае, когда необходимо получить размеры прямоугольника, ограничивающего запись в Tree View и узнать при этом, видима запись или нет, используется функция
  • GetItemRect(HTREEITEM hItem, LPRECT lpRect, BOOL bTextOnly) ― возвращает TRUE, если запись hItem видима, в этом случае размеры прямоугольника содержатся в переменной lpRect. Координаты задаются относительно левого верхнего угла окна Tree View. Если значение bTextOnly равно TRUE, то прямоугольник содержит только размеры надписи для данной записи. Иначе он определяет размеры прямоугольника, который содержит запись целиком
Функции для работы с дочерними записями:
  • ItemHasChildren (HTREEITEM hItem) ― возвращает значение TRUE, если указанная запись имеет дочерние записи
  • GetChildItem(HTREEITEM hItem) ― возвращает дескриптор первой дочерней записи
Записи могут быть свернуты или развернуты щелчком мыши. Если дочерние записи некоторой записи развернуты, то они выводятся ниже ее. Если они свернуты, то на экран не выводятся. Переключение из одного состояния в другое происходит автоматически при двойном клике на запись или нажатием на кнопку «свернуть/развернуть», расположенную слева от родительской записи (если запись имеет стиль TVS_HASBUTTONS). Приложение также может сворачивать и разворачивать ветви дерева путем вызова функции
  • Expand(HTREEITEM hItem, UINT nCode) ― параметр nCode определяет, что нужно сделать с веткой дочерней записи, для родительской записи, заданной параметром hItem
следующая функция выбирает запись
  • Select(HTREEITEM hItem, UINT nCode) ― осуществляет выбор записи с одновременной прокруткой элемента управления так, чтобы запись стала видимой. Параметр nCode определяет действия. которые следует произвести помимо собственно выбора записи. Возможны следующие значения:
выполняемые данной функцией операции могут также выполняться следующей парой функций:
  • SelectItem(HTREEITEM hItem) ― выделяет заданную параметром hItem запись просмотра дерева
  • SelectDropTarget(HTREEITEM hItem) ― перерисовывает заданную запись как запись назначения операции перетаскивания
Поиск и сортировка записей дерева
Функции этой группы предназначены для поиска записей по различным критериям, но в отличие от последовательных списков для Tree View поиск осуществляется по тому, как искомая запись соотносится с другими
  • GetNextItem(HTREEITEM hItem, UINT nCode) ― возвращает дескриптор следующей записи, подходящей по заданным параметрам, или NULL, если таковая не найдена. Параметр hItem определяет дескриптор записи, относительно которой производится поиск. Параметр nCode задает критерии поиска и может содержать один из следующих параметров:
Если нужно получить записи, лежащие на том же уровне иерархии, что и данная запись, то используют функции
  • GetNextSibilingItem(HTREEITEM hItem) ― возвращает дескриптор следующей записи
  • GetPrevSibilingItem(HTREEITEM hItem) ― возвращает дескриптор предыдущей записи
функция возвращающая дескриптор родительской записи, заданной параметром hItem
  • GetParentItem(HTREEITEM hItem)
Tree View позволяет определить запись по координатам и, если необходимо, узнать, на какой записи находится курсор мыши. Для этого используют в обработчике нажатия клавиши мыши функцию
  • HitTest (TV_HITTESTINFO* pHitTestInfo) ― возвращает дескриптор записи, которой принадлежит указанная точка или NULL, если точка не принадлежит ни одной записи. Параметр pFlag первой версии функции ― указатель на целочисленную переменную, принимающую результаты проверки. Может иметь одно или более значений, указанных в описании параметра flags структуры TV_HITTESTINFO. Параметр pHitTestInfo ― указатель на структуру TV_HITTESTINFO, содержащую координаты точки для проверки и принимающую результаты проверки.


    Описание структуры TV_HITTESTINFO:
    Кликните здесь для просмотра всего текста
    Assembler
    1
    2
    3
    4
    5
    
    TV_HITTESTINFO STRUCT
      pt        POINT      <>
      flags     DWORD      ?
      hItem     DWORD      ?
    TV_HITTESTINFO ENDS
    Поле flags используется для хранения результатов проверки и может содержать комбинацию следующих значений:
Функции осуществляющие сортировку записей в Tree View
  • SortChildren(HTREEITEM hItem) ― сортирует по алфавиту дочерние записи родительской записи, указанной в hItem. Определяет уровни дочерних записей, которые должны быть отсортированы. Если значение параметра hItem равно NULL, то будет отсортирован весь Tree View
  • SortChildrenCB(LPTV_SORTCB pSort) ― функция используется если необходимо задать собственный критерий сортировки записей, использует функцию сортировки определяемую приложением. pSort ― указатель на структуру TV_SORTCB
    Кликните здесь для просмотра всего текста
    Assembler
    1
    2
    3
    4
    5
    
    TV_SORTCB STRUCT
      hParent       DWORD      ?
      lpfnCompare   DWORD      ?
      lParam        DWORD      ?
    TV_SORTCB ENDS
    Поле lpfnCompare указатель на определяемую приложением функцию CompareFunc для сортировки записей. Она вызывается всякий раз, когда необходимо сравнить две записи
  • CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) ― возвращает отрицательное значение, если первая запись должна предшествовать второй, положительное, если первая запись должна следовать за второй, ноль если записи равны. Параметры функции сортировки соответствуют параметрам функции сортировки для List View


Обработка уведомлений
При воздействии на окно Tree view генерируется сообщение WM_NOTIFY. В этом сообщении содержатся нотификационные коды собранные в таблице

параметр lParam сообщения WM_NOTIFY содержит указатель на структуру NM_TREEVIEW, которая определяется следующим образом:
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
NM_TREEVIEW STRUCT
  hdr       NMHDR      <>
  action    DWORD      ?
  itemOld   TVITEM     <>
  itemNew   TVITEM     <>
  ptDrag    POINT      <>
NM_TREEVIEW ENDS
  • первое поле структуры NM_TREEVIEW представляет собой структуру NMHDR.
    • Нотификационный код содержится в поле icode структуры NMHDR.
    • Дескриптор окна Tree View, направившего сообщение, содержится в поле hwndFROM структуры NMHDR
      Кликните здесь для просмотра всего текста
      Assembler
      1
      2
      3
      4
      5
      
      NMHDR STRUCT
          hwndFrom    DWORD ?
          idFrom      DWORD ?
          icode       DWORD ?
      NMHDR ends
  • поле action содержит информацию, которая зависит от нотификационного кода
  • структуры itemOld и itemNew ― информацию о ранее выбранном элементе (если такой был) и о вновь выбранном элементе (если он есть)
  • информация о позиции курсора мыши в момент передачи сообщения содержится в поле ptDrag
при передаче нотификационных кодов TVN_SELCHANGED и TVN_SELCHANGING структуры itemOld и itemNew содержат информацию о ранее выбранном и о вновь выбранном элементах соответственно. Для кодов TVN_ITEMEXPANDED и TVN_ITEMEXPANDING поле itemNew описывает элемент, являющийся родительским для раскрываемой или свертываемой ветви. Для кода TVN_DELETEITEM поле itemOld описывает удаленный элемент.

Механизм обработки такой же как и для List View

Функции поддержки редактирования текст записи GetEditControl и EditLabel, функцию CreateDragImage, создающую временный список изображений для реализации механизма перемещения записей при просмотре дерева, функции GetBkColor, SetBkColor, GetTextColor, SetTextColor, GetInsertMarkColor и SetInsertMarkColor для работы с цветами фона, текста и вставляемой записи, функции GetToolTips и SetToolTips для работы с всплывающими подсказками, функцию EnsureVisible, гарантирующую видимость конкретного элемента дерева и другие...


Два примера реализации Tree view
Кликните здесь для просмотра всего текста
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
; masm dos com #
.586p
.model tiny
;for WinXP - 1995 bytes
include windows.inc
.code
exebase equ 400000h
IDB_TREE equ 31
main:
include capito_res.asm
;----------------------------------
start:  xor ebx,ebx
    mov edi,offset wTitle+exebase
    mov esi,exebase
;------------------------------+
; registering the window class |
;------------------------------+
    invoke RegisterClass,esp,ebx,window_procedure+exebase,ebx,\
    ebx,esi,ebx,10011h,COLOR_BTNFACE+1,ebx,edi
;--------------------------+
; creating the main window |
;--------------------------+
    push ebx
    push esi
    shl esi,9
    invoke CreateWindowEx,WS_EX_CLIENTEDGE,edi,edi,\
    WS_CAPTION or WS_SYSMENU or WS_MINIMIZEBOX or WS_VISIBLE,\
    esi,esi,415,200,ebx,ebx
    mov ebp,esp
;---------------------------+
; entering the message loop |
;---------------------------+
window_message_loop_start:
    invoke GetMessage,ebp,ebx,ebx,ebx
    invoke DispatchMessage,ebp
    jmp window_message_loop_start
;----------------------+
; the window procedure |
;----------------------+
window_procedure:
hWnd        equ dword ptr [ebp+8]
uMsg        equ dword ptr [ebp+0Ch]
wParam      equ dword ptr [ebp+10h]
lParam      equ dword ptr [ebp+14h]
tv1Insert   equ dword ptr [ebp - sizeof(TV_INSERTSTRUCT)]; - 4]
tv1HitInfo  equ dword ptr [tv1Insert - sizeof(TV_HITTESTINFO)]
imgl1DragH  equ dword ptr [tv1HitInfo-4]
;-----------------------------------
        enter sizeof(TV_INSERTSTRUCT)+sizeof(TV_HITTESTINFO)+4,0
        mov esi,tv1H+exebase
        mov eax,uMsg
        dec eax
        je   wmCREATE
        dec eax;cmp  [uMsg],WM_DESTROY
        je   wmDESTROY
        sub eax,WM_NOTIFY-WM_DESTROY;cmp  [uMsg],WM_NOTIFY
        je   wmNOTIFY
        sub eax,WM_MOUSEMOVE-WM_NOTIFY;cmp  [uMsg],WM_MOUSEMOVE
        je   wmMOUSEMOVE
        dec eax
        dec eax;cmp  [uMsg],WM_LBUTTONUP
        je   wmLBUTTONUP
wmDEFAULT:  leave
        jmp DefWindowProc+exebase
;--------------------------------------------------
wmMOUSEMOVE:    cmp  dword ptr imgl1Drag+exebase,ebx
        je  wmDEFAULT
        movzx  eax,word ptr lParam
        movzx  ecx,word ptr lParam+2 
        lea edi,tv1HitInfo
        mov  [edi+TV_HITTESTINFO.pt+POINT.x],eax
        mov  [edi+TV_HITTESTINFO.pt+POINT.y],ecx
        invoke  ImageList_DragMove,eax,ecx
        invoke  ImageList_DragShowNolock,ebx
        invoke  SendMessage,esi,TVM_HITTEST,ebx,edi
        xchg eax,ecx
        jecxz @f
        invoke  SendMessage,esi,TVM_SELECTITEM,TVGN_DROPHILITE,ecx
@@:     invoke  ImageList_DragShowNolock,TRUE
        jmp  wmBYE
wmNOTIFY:   mov  edi,lParam
        cmp  dword ptr [edi+NM_TREEVIEW.hdr+NMHDR.icode],TVN_BEGINDRAG
        jne  wmBYE
        invoke  SendMessage,esi,TVM_CREATEDRAGIMAGE,ebx,[edi+NM_TREEVIEW.itemNew+TV_ITEM.hItem]
        mov  imgl1DragH,eax
        invoke  ImageList_BeginDrag,eax,ebx,ebx,ebx;dword[imgl1DragH],ebx,ebx,ebx
        invoke  ImageList_DragEnter,esi,[edi+NM_TREEVIEW.ptDrag+POINT.x],[edi+NM_TREEVIEW.ptDrag+POINT.y]
        invoke  SetCapture,hWnd;eax
        or dword ptr imgl1Drag+exebase,TRUE
        jmp  wmBYE
wmLBUTTONUP:    cmp imgl1Drag+exebase,ebx
        jz  wmBYE
        invoke  ImageList_DragLeave,esi
        invoke  ImageList_EndDrag
        invoke  ImageList_Destroy,imgl1DragH
        invoke  SendMessage,esi,TVM_GETNEXTITEM,TVGN_DROPHILITE,ebx
        invoke  SendMessage,esi,TVM_SELECTITEM,TVGN_CARET,eax
        invoke  SendMessage,esi,TVM_SELECTITEM,TVGN_DROPHILITE,ebx
        invoke  ReleaseCapture
        and imgl1Drag+exebase,ebx
        jmp  wmBYE
wmCREATE:   invoke  CreateWindowEx,ebx,offset ctlClsNameTv+exebase,ebx,\
        WS_VISIBLE or WS_CHILD or WS_BORDER or TVS_HASBUTTONS or TVS_LINESATROOT or TVS_HASLINES,\
        ebx,ebx,412,200,hWnd,ebx,exebase,ebx
        mov tv1H+exebase,eax
        xchg esi,eax
        invoke  ImageList_Create,16,16,ILC_COLOR16,2,10      ;with mask
        xchg edi,eax
        invoke  LoadBitmap,exebase,IDB_TREE
        invoke  ImageList_Add,edi,eax,ebx,eax         ;with mask
        invoke  DeleteObject
        invoke  SendMessage,esi,TVM_SETIMAGELIST,ebx,edi
        lea edi,tv1Insert
        mov [edi+TV_INSERTSTRUCT.hParent],ebx
        mov dword ptr [edi+TV_INSERTSTRUCT.hInsertAfter],TVI_ROOT
        mov dword ptr [edi+TV_INSERTSTRUCT.item+TV_ITEM.imask],TVIF_TEXT+TVIF_IMAGE+TVIF_SELECTEDIMAGE
        mov dword ptr [edi+TV_INSERTSTRUCT.item+TV_ITEM.pszText],offset tv1Txt1+exebase
        mov [edi+TV_INSERTSTRUCT.item+TV_ITEM.iImage],ebx
        or dword ptr [edi+TV_INSERTSTRUCT.item+TV_ITEM.iSelectedImage],1
        invoke  SendMessage,tv1H+exebase,TVM_INSERTITEM,ebx,edi
        mov  [edi+TV_INSERTSTRUCT.hParent],eax
        mov  dword ptr [edi+TV_INSERTSTRUCT.hInsertAfter],TVI_LAST
        mov  dword ptr [edi+TV_INSERTSTRUCT.item+TV_ITEM.pszText],offset tv1Txt2+exebase
        invoke  SendMessage,esi,TVM_INSERTITEM,ebx,edi
        mov  dword ptr [edi+TV_INSERTSTRUCT.item+TV_ITEM.pszText],offset tv1Txt3+exebase
        invoke  SendMessage,esi,TVM_INSERTITEM,ebx,edi
        and imgl1Drag+exebase,ebx
wmBYE:      leave
        retn 10h
wmDESTROY:  invoke  ExitProcess,ebx                         
;----------------------------------
wTitle  db 'Iczelion Tutorial #19-1:Tree View Control in MASM',0
ctlClsNameTv    db 'SysTreeView32',0
tv1Txt1     db 'Node - Parent',0
tv1Txt2     db 'Node - Child 1',0
tv1Txt3     db 'Node - Child 2'
;-------------------------------------------------------------------------------------------
resource:
dd 0,0,0
NumberOfNamedEntries0   dw 0;количество ресурсов с именами
NumberOfIdEntries0  dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является типом ресурса
dw RT_BITMAP,0
dw bmp1-resource,8000h
bmp1:
dd 0,0,0
NumberOfNamedEntries1   dw 0;количество ресурсов с именами
NumberOfIdEntries1  dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является идентификатором ресурса 
dw IDB_TREE,0
dw bmp2-resource,8000h
bmp2:
dd 0,0,0
NumberOfNamedEntries2   dw 0;количество ресурсов с именами
NumberOfIdEntries2  dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является идентификатором ресурса 
dw 409h,0
dd bmp3-resource;
bmp3: ;struct _IMAGE_RESOURCE_DATA_ENTRY
dd bmp,end_bmp-bmp,0,0
bmp:
;incbin "Images\list1.bmp",14 ; пропуск первых 14 байт размер курсора 374-14=360
db 40,0,0,0,32,0,0,0,16,0,0,0,1,0,4,6 dup(0)
db 1,0,0,206,14,0,0,196,14,16 dup (0),128,0,0,128
db 0,0,0,128,128,0,128,0,0,0,128,0,128,0,128,128
db 0,0,128,128,128,0,192,192,192,0,0,0,255,0,0,255
db 0,0,0,255,255,0,255,0,0,0,255,0,255,0,255,255
db 0,0,255,255,255,0,17 dup(255),7 dup(0),255,240,6 dup(0)
db 247,6 dup(119),112,255,6 dup(119),0,247,251,5 dup(139)
db 112,255,127,4 dup(184),183,0,247,248,5 dup(184),112,247,251
db 4 dup(139),128,112,247,251,5 dup(139),112,247,248,4 dup(184)
db 112,112,247,248,5 dup(184),112,127,5 dup(139),7,112,247,251
db 5 dup(139),112,127,4 dup(255),247,8,112,247,248,5 dup(184)
db 112,6 dup(119),123,112,247,251,5 dup(139),112,247,248
db 5 dup(184),112,247,6 dup(255),112,247,251,139,139,143
db 255,255,112,247,139,139,139,135,119,119,127,247,248,184,184
db 247,119,119,127,255,120,184,184,127,4 dup(255),127,255,255
db 127,4 dup (255),247,119,119,5 dup(255),247,119,119,36 dup (255)
end_bmp:
;--------------------------------------------------
end_resource:
import: 
imgl1Drag   dd FALSE
tv1H        dd 0
dd 0,user32_dll
dd user32_table
dd 0,0,0,kernel32_dll
dd kernel32_table
dd 0,0,0,gdi32_dll
dd gdi32_table
dd 0,0,0,comctl32_dll
dd comctl32_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
LoadBitmap      dd _LoadBitmap
SendMessage     dd _SendMessage
SetCapture      dd _SetCapture
ReleaseCapture      dd _ReleaseCapture
GetParent       dd _GetParent
DestroyWindow       dd _DestroyWindow,0
comctl32_table:
ImageList_Create    dd _ImageList_Create
ImageList_Add       dd _ImageList_Add
ImageList_GetImageCount dd _ImageList_GetImageCount
ImageList_BeginDrag dd _ImageList_BeginDrag
ImageList_EndDrag   dd _ImageList_EndDrag
ImageList_DragEnter dd _ImageList_DragEnter
ImageList_DragMove  dd _ImageList_DragMove
ImageList_DragLeave dd _ImageList_DragLeave
ImageList_DragShowNolock dd _ImageList_DragShowNolock
ImageList_Destroy   dd _ImageList_Destroy,0
gdi32_table:
DeleteObject        dd _DeleteObject
                        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'
_SendMessage        db 0,0,'SendMessageA'
_SetCapture     db 0,0,'SetCapture'
_ReleaseCapture     db 0,0,'ReleaseCapture'
_GetParent      db 0,0,'GetParent'
_DestroyWindow      db 0,0,'DestroyWindow'
_LoadBitmap     db 0,0,'LoadBitmapA',0
user32_dll      db 'user32'
_ExitProcess        db 0,0,'ExitProcess',0
kernel32_dll        db 'kernel32'
_DeleteObject       db 0,0,'DeleteObject',0
gdi32_dll       db 'gdi32'
_ImageList_Create   db 0,0,'ImageList_Create'
_ImageList_Add      db 0,0,'ImageList_Add'
_ImageList_GetImageCount db 0,0,'ImageList_GetImageCount'
_ImageList_BeginDrag    db 0,0,'ImageList_BeginDrag'
_ImageList_EndDrag  db 0,0,'ImageList_EndDrag'
_ImageList_DragEnter    db 0,0,'ImageList_DragEnter'
_ImageList_DragMove db 0,0,'ImageList_DragMove'
_ImageList_DragLeave    db 0,0,'ImageList_DragLeave'
_ImageList_DragShowNolock db 0,0,'ImageList_DragShowNolock'
_ImageList_Destroy  db 0,0,'ImageList_Destroy',0
comctl32_dll            db 'comctl32'
end_import:
end main
Кликните здесь для просмотра всего текста
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
; masm dos com #
.586p
.model tiny
;for WinXP - 3221 bytes
include windows.inc
.code
exebase equ 400000h
IDB_TREE equ 31
main:
include capito_res.asm
;----------------------------------
start: xor ebx,ebx
    mov edi,offset wTitle+exebase
    mov esi,exebase
; +------------------------------+
; | registering the window class |
; +------------------------------+
    invoke RegisterClass,esp,ebx,window_procedure+exebase,ebx,\
    ebx,esi,ebx,10011h,COLOR_BTNFACE+1,ebx,edi
;--------------------------+
; creating the main window |
;--------------------------+
    push ebx
    push esi
    shl esi,9
    invoke CreateWindowEx,WS_EX_CLIENTEDGE,edi,edi,\
    WS_CAPTION or WS_SYSMENU or WS_MINIMIZEBOX or WS_VISIBLE,\
    esi,esi,415,200,ebx,ebx
    mov ebp,esp
;---------------------------+
; entering the message loop |
;---------------------------+
window_message_loop_start:
    invoke  GetMessage,ebp,ebx,ebx,ebx
    invoke  DispatchMessage,ebp
    jmp window_message_loop_start
;----------------------+
; the window procedure |
;----------------------+
window_procedure:
hWnd        equ dword ptr [ebp+8]
uMsg        equ dword ptr [ebp+0Ch]
wParam      equ dword ptr [ebp+10h]
lParam      equ dword ptr [ebp+14h]
tv1Insert   equ dword ptr [ebp - sizeof(TV_INSERTSTRUCT)]; - 4]
tv1HitInfo  equ dword ptr [tv1Insert - sizeof(TV_HITTESTINFO)]
imgl1DragH  equ dword ptr [tv1HitInfo-4]
;-----------------------------------
        enter sizeof(TV_INSERTSTRUCT)+sizeof(TV_HITTESTINFO)+4,0
        mov esi,tv1H+exebase
        mov eax,uMsg
        dec eax
        je   wmCREATE
        dec eax;cmp  [uMsg],WM_DESTROY
        je   wmDESTROY
        sub eax,WM_NOTIFY-WM_DESTROY;cmp  [uMsg],WM_NOTIFY
        je   wmNOTIFY
        sub eax,WM_MOUSEMOVE-WM_NOTIFY;cmp  [uMsg],WM_MOUSEMOVE
        je   wmMOUSEMOVE
        dec eax
        dec eax;cmp  [uMsg],WM_LBUTTONUP
        je   wmLBUTTONUP
 
wmDEFAULT:  leave
        jmp DefWindowProc+exebase
 
wmMOUSEMOVE:    cmp  dword ptr imgl1Drag+exebase,ebx
        je  wmDEFAULT
        movzx  eax,word ptr lParam
        movzx  ecx,word ptr lParam+2 
        lea edi,tv1HitInfo
        mov  [edi+TV_HITTESTINFO.pt+POINT.x],eax
        mov  [edi+TV_HITTESTINFO.pt+POINT.y],ecx
        invoke  ImageList_DragMove,eax,ecx
        invoke  ImageList_DragShowNolock,ebx
        invoke  SendMessage,esi,TVM_HITTEST,ebx,edi
        xchg eax,ecx
        jecxz @f
        invoke  SendMessage,esi,TVM_SELECTITEM,TVGN_DROPHILITE,ecx
@@:     invoke  ImageList_DragShowNolock,TRUE
        jmp  wmBYE
 
wmNOTIFY:   mov  edi,lParam
        cmp  dword ptr [edi+NM_TREEVIEW.hdr+NMHDR.icode],TVN_BEGINDRAG
        jne  wmBYE
        invoke  SendMessage,esi,TVM_CREATEDRAGIMAGE,ebx,[edi+NM_TREEVIEW.itemNew+TV_ITEM.hItem]
        mov  imgl1DragH,eax
        invoke  ImageList_BeginDrag,eax,ebx,ebx,ebx;dword[imgl1DragH],ebx,ebx,ebx
        invoke  ImageList_DragEnter,esi,[edi+NM_TREEVIEW.ptDrag+POINT.x],[edi+NM_TREEVIEW.ptDrag+POINT.y]
        invoke  SetCapture,hWnd;eax
        or dword ptr imgl1Drag+exebase,TRUE
        jmp  wmBYE
 
wmLBUTTONUP:    cmp imgl1Drag+exebase,ebx
        jz  wmBYE
        invoke  ImageList_DragLeave,esi
        invoke  ImageList_EndDrag
        invoke  ImageList_Destroy,imgl1DragH
        invoke  SendMessage,esi,TVM_GETNEXTITEM,TVGN_DROPHILITE,ebx
        invoke  SendMessage,esi,TVM_SELECTITEM,TVGN_CARET,eax
        invoke  SendMessage,esi,TVM_SELECTITEM,TVGN_DROPHILITE,ebx
        invoke  ReleaseCapture
        and imgl1Drag+exebase,ebx
        jmp  wmBYE
 
wmCREATE:
        invoke  CreateWindowEx,ebx,offset ctlClsNameTv+exebase,ebx,\
        WS_VISIBLE or WS_CHILD or WS_BORDER or TVS_HASBUTTONS or TVS_LINESATROOT or TVS_HASLINES,\
        ebx,ebx,412,200,hWnd,ebx,exebase,ebx
        mov tv1H+exebase,eax
        xchg esi,eax
        invoke  ImageList_Create,16,16,ILC_COLOR16+ILC_MASK,2,10      ;with mask
        xchg edi,eax
        invoke  LoadBitmap,400000h,IDB_TREE
        invoke  ImageList_AddMasked,edi,eax,0FF00h,eax        ;with mask
        invoke  DeleteObject
        invoke  SendMessage,esi,TVM_SETIMAGELIST,ebx,edi
        lea edi,tv1Insert
        mov [edi+TV_INSERTSTRUCT.hParent],ebx
        mov dword ptr [edi+TV_INSERTSTRUCT.hInsertAfter],TVI_ROOT
        mov dword ptr [edi+TV_INSERTSTRUCT.item+TV_ITEM.imask],TVIF_TEXT+TVIF_IMAGE+TVIF_SELECTEDIMAGE
        mov dword ptr [edi+TV_INSERTSTRUCT.item+TV_ITEM.pszText],offset tv1Txt1+exebase
            mov [edi+TV_INSERTSTRUCT.item+TV_ITEM.iImage],ebx
            or dword ptr [edi+TV_INSERTSTRUCT.item+TV_ITEM.iSelectedImage],1
        invoke  SendMessage,tv1H+exebase,TVM_INSERTITEM,ebx,edi
        mov  [edi+TV_INSERTSTRUCT.hParent],eax
        mov  dword ptr [edi+TV_INSERTSTRUCT.hInsertAfter],TVI_LAST
        mov  dword ptr [edi+TV_INSERTSTRUCT.item+TV_ITEM.pszText],offset tv1Txt2+exebase
        invoke  SendMessage,esi,TVM_INSERTITEM,ebx,edi
        mov  dword ptr [edi+TV_INSERTSTRUCT.item+TV_ITEM.pszText],offset tv1Txt3+exebase
        invoke  SendMessage,esi,TVM_INSERTITEM,ebx,edi
        and imgl1Drag+exebase,ebx
wmBYE:      leave
        retn 10h
wmDESTROY:  invoke  ExitProcess,ebx                         
;----------------------------------
wTitle  db 'Iczelion Tutorial #19-1:Tree View Control in MASM',0
ctlClsNameTv    db 'SysTreeView32',0
tv1Txt1     db 'Node - Parent',0
tv1Txt2     db 'Node - Child 1',0
tv1Txt3     db 'Node - Child 2';,0
;-------------------------------------------------------------------------------------------
;align 4
resource:
Characteristics0    dd 0
TimeDateStamp0      dd 0
MajorVersion0       dw 0
MinorVersion0       dw 0
NumberOfNamedEntries0   dw 0;количество ресурсов с именами
NumberOfIdEntries0  dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является типом ресурса
dw RT_BITMAP,0
dw bmp1-resource,8000h
bmp1:
Characteristics1    dd 0
TimeDateStamp1      dd 0
MajorVersion1       dw 0
MinorVersion1       dw 0
NumberOfNamedEntries1   dw 0;количество ресурсов с именами
NumberOfIdEntries1  dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является идентификатором ресурса 
dw IDB_TREE,0
dw bmp2-resource,8000h;04-строка
bmp2:
Characteristics2    dd 0
TimeDateStamp2      dd 0
MajorVersion2       dw 0
MinorVersion2       dw 0
NumberOfNamedEntries2   dw 0;количество ресурсов с именами
NumberOfIdEntries2  dw 1;количество ресурсов с идентификаторами
;на этом уровне идентификатор ресурсов является идентификатором ресурса 
dw 409h,0
dd bmp3-resource;
bmp3: ;struct _IMAGE_RESOURCE_DATA_ENTRY
OffsetToData0   dd bmp
Size0       dd end_bmp-bmp
CodePage0   dd 0
Reserved0   dd 0
bmp:
;incbin "Images\list2.bmp",14 ; пропуск первых 14 байт размер курсора 374-14=360
db 40,0,0,0,32,0,0,0,16,0,0,0,1,0,8,6 dup(0)
db 2,11 dup(0),1,0,0,0,1,8 dup(0),128,0,0,128
db 0,0,0,128,128,0,128,0,0,0,128,0,128,0,128,128
db 0,0,192,192,192,0,192,220,192,0,240,202,166,0,255,255
db 255,0,99,99,99,0,16,123,181,0,107,214,255,0,123,222
db 255,0,132,231,255,0,140,239,255,0,148,247,255,0,156,255
db 56 dup(255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255)
db 255,0,255,255,255,0,255,255,255,0,255,255,255,0,240,251
db 255,0,164,160,160,0,128,128,128,0,0,0,255,0,0,255,0,0,0
db 255,255,0,255,0,0,0,255,0,255,0,255,255,0,0,255,255,255
db 0,34 dup(250),14 dup(11),250,250,250,13 dup(11),250,14 dup(12)
db 11,250,250,12 dup(12),11,11,250,12,10,11 dup(13),12,11,250,250
db 12,10,9 dup (13),12,11,11,250,12,10,11 dup(14),12,11,250,12,10
db 9 dup(15),12,11,12,11,250,12,10,11 dup(15),12,11,250,12,10
db 9 dup(17),12,11,12,11,250,12,10,11 dup(16),12,11,12,10
db 9 dup(18),12,11,13,12,11,250,12,10,11 dup(17),12,11,12
db 10 dup(10),12,11,13,12,11,250,12,10,11 dup(18),12,11
db 12 dup(12),15,15,12,11,250,12,10,11 dup(18),12,11,250,12,10
db 11 dup(16),12,11,250,12,12 dup(10),12,11,250,12,10,6 dup(17)
db 5 dup(10),12,11,250,12,13,5 dup(18),13,6 dup(12),250,250,12,10
db 5 dup(18),10,6 dup(12),3 dup(250),12,13,18,18,18,13,12,9 dup(250)
db 12,5 dup(10),12,10 dup(250),5 dup(12),11 dup(250),5 dup(12),72 dup(250)
end_bmp:
;--------------------------------------------------
end_resource:
import: 
imgl1Drag   dd FALSE
tv1H        dd 0
dd 0,user32_dll
dd user32_table
dd 0,0,0,kernel32_dll
dd kernel32_table
dd 0,0,0,gdi32_dll
dd gdi32_table
dd 0,0,0,comctl32_dll
dd comctl32_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
LoadBitmap      dd _LoadBitmap
SendMessage     dd _SendMessage
SetCapture      dd _SetCapture
ReleaseCapture      dd _ReleaseCapture
GetParent       dd _GetParent
DestroyWindow       dd _DestroyWindow,0
comctl32_table:
ImageList_Create    dd _ImageList_Create
ImageList_AddMasked dd _ImageList_AddMasked
ImageList_GetImageCount dd _ImageList_GetImageCount
ImageList_BeginDrag dd _ImageList_BeginDrag
ImageList_EndDrag   dd _ImageList_EndDrag
ImageList_DragEnter dd _ImageList_DragEnter
ImageList_DragMove  dd _ImageList_DragMove
ImageList_DragLeave dd _ImageList_DragLeave
ImageList_DragShowNolock dd _ImageList_DragShowNolock
ImageList_Destroy   dd _ImageList_Destroy,0
gdi32_table:
DeleteObject        dd _DeleteObject
                        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'
_SendMessage        db 0,0,'SendMessageA'
_SetCapture     db 0,0,'SetCapture'
_ReleaseCapture     db 0,0,'ReleaseCapture'
_GetParent      db 0,0,'GetParent'
_DestroyWindow      db 0,0,'DestroyWindow'
_LoadBitmap     db 0,0,'LoadBitmapA',0
user32_dll      db 'user32'
_ExitProcess        db 0,0,'ExitProcess',0
kernel32_dll        db 'kernel32'
_DeleteObject       db 0,0,'DeleteObject',0
gdi32_dll       db 'gdi32'
_ImageList_Create   db 0,0,'ImageList_Create'
_ImageList_AddMasked    db 0,0,'ImageList_AddMasked'
_ImageList_GetImageCount db 0,0,'ImageList_GetImageCount'
_ImageList_BeginDrag    db 0,0,'ImageList_BeginDrag'
_ImageList_EndDrag  db 0,0,'ImageList_EndDrag'
_ImageList_DragEnter    db 0,0,'ImageList_DragEnter'
_ImageList_DragMove db 0,0,'ImageList_DragMove'
_ImageList_DragLeave    db 0,0,'ImageList_DragLeave'
_ImageList_DragShowNolock db 0,0,'ImageList_DragShowNolock'
_ImageList_Destroy  db 0,0,'ImageList_Destroy',0
comctl32_dll            db 'comctl32'
end_import:
end main
______________________________
© Mikl___ 2013
3
Изображения
 
Вложения
Тип файла: zip tut19a.zip (10.0 Кб, 48 просмотров)
Mikl___
Заблокирован
Автор FAQ
18.01.2013, 11:08  [ТС] #57
Win32 API. Урок 20. Сабклассинг окна
В этом туториале мы изучим сабклассинг окна, что это такое, и как это использовать нам на пользу.

Скачайте пример здесь.
Теория:
Если вы уже некоторое время программируете в Windows, вы уже могли столкнуться с ситуацией, когда окно имеет почти все атрибуты, которые вам нужны, но не все. Сталкивались ли вы с ситуацией, когда вам требуется специальный вид edit control'а, который бы отфильтровывал ненужный текст? Первое, что может придти в голову, это написать свое собственное окно. Hо это действительно тяжелая работа, требующая значительного времени. Выходом является сабклассинг окна.

Вкратце, сабклассинг окна позволяет получить контроль над сабклассированным окном. У вас будет абсолютный контроль над ним. Давайте рассмотрим пример, что прояснить данное утверждение. Предположите, что вам нужен text box, в котором можно вводить только шестнадцатиричные числа. Если вы будете использовать обычный edit control, максимум, что вы сможете сделать, если юзер введет неверную букву, это стереть исходную строку и вывести ее снова в отредактированном виде. По меньшей мере, это непрофессионально. Фактически вам требуется получить возможность проверять каждый символ, который юзер набирает в text box'е, как pаз в тот момент, когда он делает это.

Теперь мы изучим как это сделать. Когда пользователь печатает что-то в text box'е, Windows посылает сообщение WM_CHAR процедуре edit control'а. Эта процедура окна находится внутри Windows, поэтому мы не можем модифицировать ее. Hо мы можем перенаправить поток сообщений к нашей оконной процедуре. Поэтому наша процедура окна первой получит возможность обработать сообщение, которое Windows пошлет edit control'у. Если наша процедура решит обработать сообщение, она так и сделает. Hо если она не захочет его обрабатывать, она может передать его оригинальной оконной процедуре. Таким образом, наша функция будет стоять между Windows и edit control'ом. Посмотрите на условную схему внизу.

До сабклассинга: Windows http://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrow процедура edit control'а
После сабклассинга: Windows http://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrow наша оконная процедура http://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrow процедура edit control'а

Теперь мы можем рассмотреть то, каким образом происходит сабклассинг окна. Заметьте, что сабклассинг не ограничивается контролами, он может использоваться с любым окном. Давайте подумаем о том, как Windows узнает, где находится процедура edit box'а. Hу?.. Поле lрfnWndрroc в структуре WNDCLASSEX. Если мы сможем поменять значение этого поля на адрес собственной структуры, Windows пошлет сообщение нашей процедуре окна вместо этого. Мы можем сделать это, вызвав SetWindowLong.
Кликните здесь для просмотра всего текста
Assembler
1
SetWindowLong PROTO hWnd:DWORD, nIndex:DWORD, dwNewLong:DWORD
  • hWnd - хэндл окна, чьи свойства мы хотим поменять.
  • nIndex - значение, которое нужно изменить.
  • dwNewLong новое значение.У каждого окна есть ассоциированное с ним 32-битное значение, предназначенное для использования приложением в своих целях.
GWL_EXSTYLE Установка нового расширенного стиля окна.
GWL_STYLE Установка нового стиля окна.
GWL_WNDPROC Установка нового адреса для процедуры окна.
GWL_HINSTANCE Установка нового хэндла приложения.
GWL_ID Установка нового идентификатора окна.
GWL_USERDATA Установка 32-битного значения, ассоциирующегося с окном.
Таким образом, наша работа проста: мы создаем процедуру окна, которая будет обрабатывать сообщения для edit control'а и затем вызывать SetWindowLong с флагом GWL_WNDPROC, которому передается адрес нашего окна в качестве третьего параметра. В случае, если вызов функции прошел нормально, возвращаемым значением является прежнее значение замещаемого параметра, в нашем случае - это адрес оригинальной процедуры окна. Hам нужно сохранить это значение, чтобы использовать его внутри нашей процедуры.

Помните, что есть сообщения, которые нам не нужно будет обрабатывать. Их мы будем передавать оригинальной процедуре. Мы можем сделать это с помощью вызова функции CallWindowproc.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
  CallWindowproc PROTO lpPrevWndFunc:DWORD, \
                                                   hWnd:DWORD,\
                                                   Msg:DWORD,\
                                                   wparam:DWORD,\
                                                   lparam:DWORD
  • lpPrevWndFunc - адрес оригинальной процедуры окна.
  • Остальные четыре значения - это те, что передаются нашей процедуре окна. Мы передаем их CallWindowproc.
П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
   .386
   .model flat,stdcall
   option casemap:none
   include \masm32\include\windows.inc
   include \masm32\include\user32.inc
   include \masm32\include\kernel32.inc
   include \masm32\include\comctl32.inc
   includelib \masm32\lib\comctl32.lib
   includelib \masm32\lib\user32.lib
   includelib \masm32\lib\kernel32.lib
 
   WinMain pROTO :DWORD,:DWORD,:DWORD,:DWORD
   EditWndproc pROTO :DWORD,:DWORD,:DWORD,:DWORD
 
   .data
   ClassName  db "SubclassWinClass",0
   AppName    db "Subclassing Demo",0
   EditClass  db "EDIT",0
   Message  db "You pressed Enter in the text box!",0
 
   .data?
   hInstance  HINSTANCE ?
   hwndEdit dd ?
   OldWndproc dd ?
 
   .code
start: invoke GetModuleHandle, NULL
       mov    hInstance,eax
       invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
       invoke Exitprocess,eax
 
WinMain proc hInst:HINSTANCE,hprevInst:HINSTANCE,CmdLine: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_AppWORKSpACE
       mov   wc.lpszMenuName,NULL
       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_OVERLAppED+WS_CApTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+\
       WS_VISIBLE,CW_USEDEFAULT,350,200,NULL,NULL,hInst,NULL
       mov   hwnd,eax
       .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_CREATE
 
           invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR EditClass,NULL,WS_CHILD+\
               WS_VISIBLE+WS_BORDER,20,20,300,25,hWnd,NULL,hInstance,NULL
 
           mov hwndEdit,eax
           invoke SetFocus,eax
           ;-----------------------------------------
           ; Subclass it!
           ;-----------------------------------------
           invoke SetWindowLong,hwndEdit,GWL_WNDpROC,addr EditWndproc
           mov OldWndproc,eax
       .elseif uMsg==WM_DESTROY
           invoke postQuitMessage,NULL
       .else
           invoke DefWindowproc,hWnd,uMsg,wparam,lparam
           ret
       .endif
       xor eax,eax
       ret
   Wndproc endp
 
EditWndproc pROC hEdit:DWORD,uMsg:DWORD,wparam:DWORD,lparam:DWORD
       .if uMsg==WM_CHAR
           mov eax,wparam
           .if (al>="0" && al<="9") || (al>="A" && al<="F") || (al>="a" && al<="f") || al==VK_BACK
               .if al>="a" && al<="f"
                   sub al,20h
               .endif
               invoke CallWindowproc,OldWndproc,hEdit,uMsg,eax,lparam
               ret
           .endif
       .elseif uMsg==WM_KEYDOWN
           mov eax,wparam
           .if al==VK_RETURN
               invoke MessageBox,hEdit,addr Message,addr
   AppName,MB_OK+MB_ICONINFORMATION
               invoke SetFocus,hEdit
           .else
               invoke CallWindowproc,OldWndproc,hEdit,uMsg,wparam,lparam
               ret
           .endif
       .else
           invoke CallWindowproc,OldWndproc,hEdit,uMsg,wparam,lparam
           ret
       .endif
       xor eax,eax
       ret
   EditWndproc endp
   end start
Анализ:
Кликните здесь для просмотра всего текста
Assembler
1
2
               invoke SetWindowLong,hwndEdit,GWL_WNDpROC,addr EditWndproc
               mov OldWndproc,eax
После того, как edit control создан, мы сабклассим его, вызывая SetWindowLong и замещая адрес оригинальной процедуры окна нашим собственным адресом. Заметьте, что мы сохраняем значение адреса оригинальной процедуры, чтобы впоследствии использовать его при вызове CallWindowproc. Заметьте, что EditWndрroc - это обычная оконная процедура.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
         .if uMsg==WM_CHAR
               mov eax,wparam
               .if (al>="0" && al<="9") || (al>="A" && al<="F") || (al>="a" && al<="f") || al==VK_BACK
                   .if al>="a" && al<="f"
                       sub al,20h
                   .endif
                   invoke CallWindowproc,OldWndproc,hEdit,uMsg,eax,lparam
                   ret
               .endif
Внутри EditWndрroc, мы фильтруем сообщения WM_CHAR. Если введен символ в диапазоне 0-9 или a-f, мы передаем его оригинальной процедуре окна. Если это символ нижнего регистра, мы конвертируем его в верхний, добавляя 20h. Заметьте, что если символ не тот, который мы ожидали, мы пропускаем его. Мы не передаем его оригинальной процедуре окна. Поэтому, когда пользователь печатат что-нибудь отличное от 0-9 или a-f, символ не появляется в edit control'е.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
           .elseif uMsg==WM_KEYDOWN
               mov eax,wparam
               .if al==VK_RETURN
                   invoke MessageBox,hEdit,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
                   invoke SetFocus,hEdit
               .else
                   invoke CallWindowproc,OldWndproc,hEdit,uMsg,wparam,lparam
                   ret
               .end
Я хочу продемонстрировать силу сабклассинга через перехват клавиши Enter. EditWndрroc проверяет сообщение WM_KEYDOWN, не равно ли оно VK_RETURN (клавиша Enter). Если это так, она отображает окно с сообщением "You pressed the Enter key in the text box!". Если это не клавиша Enter, она передает сообщение оригинальной процедуре.

Вы можете использовать сабклассинг окна, чтобы получить контроль над другими окнами. Эту мощную технику вам следует иметь в своем арсенале.
_______________________________
© Iczelion, пер. Aquila.
3
Вложения
Тип файла: zip tut20.zip (3.1 Кб, 53 просмотров)
Mikl___
Заблокирован
Автор FAQ
18.01.2013, 11:19  [ТС] #58
Win32 API. Урок 20a. Сабклассинг окна
В этом туториале мы изучим сабклассинг окна, что это такое, и как это использовать нам на пользу.

Скачайте пример здесь.
Кликните здесь для просмотра всего текста
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
.586
.model tiny,stdcall
;for WinXP - 837 bytes
include windows.inc
exebase equ 400000h
.code
main:
include capito.asm
;---------------------------------------------------------
start:  xchg ebx,eax
    mov esi,exebase;400000h
    mov edi,offset wTitle+exebase
;------------------------------
; registering the window class 
;------------------------------
    invoke RegisterClass,esp,ebx,offset window_procedure+exebase,\
        ebx,ebx,esi,ebx,10011h,COLOR_BTNFACE+1,ebx,edi,esi
;--------------------------+
; creating the main window |
;--------------------------+
    push ebx
    push esi
    shl esi,9
    invoke CreateWindowEx,WS_EX_CLIENTEDGE,edi,\
    edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE,esi,esi,430,240,ebx,ebx
    pop esi
        invoke  CreateWindowEx,WS_EX_CLIENTEDGE,offset aEdit+exebase,\
    ebx,WS_CHILD + WS_VISIBLE + WS_BORDER,20,20,300,24,eax,ebx,esi,ebx
    xchg edi,eax
        invoke  SetFocus,edi
    invoke  SetWindowLong,edi,GWL_WNDPROC,offset edit1_procedure+exebase
    mov wndProcAddr+exebase,eax
    mov ebp,esp
;---------------------------+
; entering the message loop |
;---------------------------+
message_loop: invoke GetMessage,ebp,ebx,ebx,ebx
        invoke  TranslateMessage,ebp
    invoke DispatchMessage,ebp
    jmp message_loop
;----------------------+
; the window procedure |
;----------------------+
window_procedure: cmp dword ptr [esp+08],WM_DESTROY
    je short wmDESTROY
    jmp DefWindowProc+exebase
wmDESTROY: invoke ExitProcess,ebx
;---------------------------------------------
edit1_procedure:
hWnd    equ dword ptr [esp+4]
umsg    equ dword ptr [esp+8]
wParam  equ dword ptr [esp+0Ch]
lParam  equ dword ptr [esp+10h]
    mov edi,hWnd
    mov eax,umsg
        mov edx,wParam
    sub eax,WM_KEYDOWN
    je edit1_wmKEYDOWN
    dec eax
    dec eax;cmp  eax,WM_CHAR
    jne @f
 
edit1_wmCHAR: cmp dl,VK_BACK    ;compare with virtual key BACKSPACE
    je @f
    cmp dl,'0'      ;compare with ascii 0
    jb edit1_wmBYE
    cmp dl,'9'      ;compare with ascii 9
    jbe @f
        and edx,-33     ;so our DL become big letter
    cmp dl,'A'      ;compare with ascii A
    jb edit1_wmBYE
    cmp dl,'F'      ;compare with ascii F
    ja edit1_wmBYE     ;something else
                
@@: invoke  CallWindowProc,wndProcAddr+exebase,edi,[umsg+8],edx,lParam
    jmp  edit1_wmBYE
 
edit1_wmKEYDOWN: cmp dl,VK_RETURN   ;compare with virtual key RETURN
    jne  @b
    invoke MessageBox,edi,offset Asimple+exebase,offset wTitle+exebase,ebx;MB_OK
    invoke SetFocus,edi
edit1_wmBYE: retn 10h
Asimple db "A simple HEX edit control!",0
aEdit db "Edit",0
wTitle db 'Iczelion Tutorial #20:Window Subclassing in MASM'
;-------------------------------------------------------
import: 
wndH dd 0
wndProcAddr 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
MessageBox      dd _MessageBox
SetFocus        dd _SetFocus
CallWindowProc      dd _CallWindowProc
TranslateMessage    dd _TranslateMessage
SetWindowLong       dd _SetWindowLong
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'
_SetFocus       db 0,0,'SetFocus'
_CallWindowProc     db 0,0,'CallWindowProcA'
_TranslateMessage   db 0,0,'TranslateMessage'
_SetWindowLong      db 0,0,'SetWindowLongA'
_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
3
Вложения
Тип файла: zip tut20a.zip (2.9 Кб, 43 просмотров)
Mikl___
Заблокирован
Автор FAQ
21.01.2013, 06:45  [ТС] #59
Win32 API. Урок 21. Пайп
В этом туториале мы исследуем пайп (pipe) , что это такое и для чего мы можем использовать его. Чтобы сделать этот процесс более интересным, я покажу, как можно изменить бэкграунд и цвет текста edit control'а.

Скачайте пример здесь.
Теория:
Пайп - канал или дорога с двумя концами. Вы можете использовать пайп, чтобы обмениваться данными между двумя различными процессами или внутри одного процесса. Это что-то вроде "уоки-токи". Вы даете другому участнику конец канала и он может использовать его для того, чтобы взаимодействовать с вами.

Есть два типа пайпов: анонимные и именованные. Анонимный пайп анонимен - вы можете использовать его не зная его имени. Для того, чтобы использовать именованный пайп, вам обязательно нужно знать его имя.

Вы можете разделить пайп по их свойствам: однонаправленные и двунаправленные. В однонаправленном пайпе данные могут течь только в одном направлении: от одного конца к другому, в то время как в двунаправленном данные могут передаваться между обоими концами.

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

В этом туториале мы подробно рассмотрим анонимные пайпы. Главная цель таких пайпов - служить каналом между родительским и дочерним процессом или между дочерними процессами.

Анонимный пайп действительно полезен, когда вы взаимодействуете с консольным приложением. Консольное приложение - это вид win32-программ, которые используют консоль для своего ввода и вывода. Консоль - это вроде DOS-box'а. Тем не менее, консольное приложение - это полноценное 32-битное приложение. Оно может использовать любую GUI-функцию, так же как и другие GUI-программы. Она отличается только тем, что у нее есть консоль.

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


Консольное приложение может получить эти три стандартных значения, вызвав функцию GetStdHandle, указав хэндл, который она хочет получить. GUI-приложение не имеет консоли. Если вы вызывает GetStdHandle, она возвратит ошибку. Если вы действительно хотите использовать консоль, вы можете вызвать AllocConsole, чтобы зарезервировать новую консоль. Тем не менее, не забудьте вызвать FreeConsole, когда вы уже не будете в ней нуждаться.


Анонимный пайп очень часто используется для перенаправления ввода и/или вывода дочернего консольного приложения. родительский процесс может быть консоль или GUI-приложение, но дочернее приложение должно быть консольным, чтобы это сработало. Как вы знаете, консольное приложение использует стандартные хэндлы для ввода и вывода. Если мы хотите перенаправить ввод/вывод консольного приложения, мы можем заменить один хэндл другим хэндлом одного конца пайпа. Консольное приложение не будет знать, что оно использует один конец пайпа. Оно будет считать, что это стандартный хэндл. Это вид полиморфизма на ООП-жаргоне. Это мощный подход, так как нам не нужно модифицировать родительский процесс никаким образом.

Другая вещь, которую вы должны знать о консольном приложение - это откуда оно берет стандартный хэндл. Когда консольное приложение создано, у родительского приложения есть следующий выбор: оно может создать новую консоль для дочернего приложения или позволить тому наследовать собственную консоль. Чтобы второй метод работал, родительский процесс должен быть консольным, либо, если он GUI'евый, создать консоль с помощью AllocConsole.

Давайте начнем работу. Чтобы создать анонимный пайп, вам требуется вызывать CreatePipe. Эта функция имеет следующий прототип:
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
       CreatePipe proto pReadHandle:DWORD, \
              pWriteHandle:DWORD,\
              ppipeAttributes:DWORD,\
              nBufferSize:DWORD
  • рReadHandle - это указатель на переменную типа dword, которая получит хэндл конца чтения пайпа.
  • рWriteHandle - это указатель на переменную типа dword, которая получить хэндл на конец записи пайпа.
  • PipeAttributes указывает на структуру SECURITY_ATTRIBUTES, которая определяет, наследуется ли каждый из концов дочерним процессом.
  • nBufferSize - это предполагаемый размер буфера, который пайп зарезервирует для использования. Это всего лишь предполагаемый pазмеp. Вы можете передать NULL, чтобы указать функции использовать pазмеp по умолчанию.
Если вызов прошел успешно, возвращаемое значение не равно нулю, иначе оно будет нулевым.

После успешного вызова CreatePipe вы получите два хэндла, один к концу чтения, а другой к концу записи. Теперь я вкратце изложу шаги, необходимые для перенаправления стандартного вывода дочерней консольной программы в ваш процесс. Заметьте, что мой метод отличается от того, который изложен в справочнике по WinAPI от Borland. Тот метод предполагает, что родительский процесс - это консольное приложение, поэтому дочерний процесс должен наследовать стандартные хэндлы от него. Hо большую часть времени нам будет требоваться перенаправить вывод из консольного приложения в GUI'евое.
  • Создаем анонимный пайп с помощью CreatePipe. Hе забудьте установить параметр bInheritable структуры SECURITY_ATTRIBUTES в TRUE, чтобы хэндлы могли наследоваться.
  • Теперь мы должны подготовить параметры, которые передадим CreateProcess (мы используем эту функцию для загрузки консольного приложения). Среди аргументов этой функции есть важная структура STARTUPINFO. Эта структура определяет появление основного окна дочернего процесса, когда он запускается. Эта структура жизненно важна для нас. Вы можете спрятать основное окно и передать хэндл пайпа дочерней консоли вместе с этой структурой.
  • Hиже находятся поля, которые вы должны заполнить:
    • cb : размер структуры STARTUPINFO
    • dwFlags : двоичные битовые флаги, которые определяют, какие члены структуры будут использоваться, также она управляет состоянием основного окна. Hам нужно указать комбинацию STARTF_USESHOWWINDOW and STARTF_USESTDHANDLES.
    • hStdOutрut и hStdError : хэндлы, которые будут использоваться в дочернем процессе в качестве хэндлов стандартного ввода/вывода. Для наших целей мы передадим хэндл пайпа в качестве стандартного вывода и вывода ошибок. Поэтому когда дочерний процесс выведет что-нибудь туда, он фактически передаст информацию через пайп родительскому процессу.
    • wShowWindow управляет тем, как будет отображаться основное окно. Hам не нужно, что окно консоли отображалось на экран, поэтому мы приравняем этот параметр к SW_HIDE.
  • Вызов Createрrocess, чтобы загрузить дочернее приложение. После того, как вызов прошел успешно, дочерний процесс все еще находится в спящем состоянии. Он загружается в память, но не запускается немедленно.
  • Закройте конец хэндл конца записи пайпа. Это необходимо, так как родительский процессу нет нужды использовать этот хэндл, а пайп не будет работать, если открыть более чем один конец записи. Следовательно, мы должны закрыть его прежде, чем считывать данные из пайпа. тем не менее, не закрывайте этот конец до вызова CreateProcess, иначе ваш пайп будет сломан. Вам следует закрыть конец записи после того, как будет и вызвана функция CreateProcess, и до того, как вы считаете данные из конца чтения пайпа.
  • Теперь вы можете читать данные из конца чтения с помощью ReadFile. С ее помощью вы запускаете дочерний процесс, который начнет выполняться, а когда он запишет что-нибудь в стандартный хэндл вывода, данные будут посланы на конец чтения пайпа. Вы должны последовательно вызывать ReadFile, пока она не возвратит ноль, что будет означать, что больше данных нет. С полученной информацией вы можете делать все, что хотите, в нашем случае я вывожу их в edit control.
  • Закроем хэндл чтения пайпа.
П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
143
144
145
146
147
148
149
150
       .386
       .model flat,stdcall
       option casemap:none
       include \masm32\include\windows.inc
       include \masm32\include\user32.inc
       include \masm32\include\kernel32.inc
       include \masm32\include\gdi32.inc
       includelib \masm32\lib\gdi32.lib
       includelib \masm32\lib\user32.lib
       includelib \masm32\lib\kernel32.lib
 
       WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
 
       .const
       IDR_MAINMENU equ 101         ; the ID of the main menu
       IDM_ASSEMBLE equ 40001
 
       .data
       ClassName            db "PipeWinClass",0
       AppName              db "One-way pipe Example",0 
       EditClass        db  "EDIT",0
       CreatePipeError     db "Error during pipe creation",0
       CreateProcessError     db "Error during process creation",0
       CommandLine     db "ml /c /coff /Cp test.asm",0
 
 
       .data?
       hInstance HINSTANCE ?
       hwndEdit dd ?
 
       .code
start:           invoke GetModuleHandle, NULL
           mov hInstance,eax
           invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
           invoke Exitprocess,eax
 
       WinMain proc hInst:DWORD,hprevInst:DWORD,CmdLine:DWORD,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_AppWORKSPACE
           mov wc.lpszMenuName,IDR_MAINMENU
           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+WS_VISIBLE,CW_USEDEFAULT,\
       CW_USEDEFAULT,400,200,NULL,NULL,hInst,NULL
 
           mov hwnd,eax
           .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
           LOCAL rect:RECT
           LOCAL hRead:DWORD
           LOCAL hWrite:DWORD
           LOCAL startupinfo:STARTUpINFO
           LOCAL pinfo:pROCESS_INFORMATION
           LOCAL buffer[1024]:byte
           LOCAL bytesRead:DWORD
           LOCAL hdc:DWORD
           LOCAL sat:SECURITY_ATTRIBUTES
 
           .if uMsg==WM_CREATE
               invoke CreateWindowEx,NULL,addr EditClass, NULL, WS_CHILD+\
       WS_VISIBLE+ ES_MULTILINE+ ES_AUTOHSCROLL+ ES_AUTOVSCROLL, 0, 0, 0, 0,
       hWnd, NULL, hInstance, NULL
               mov hwndEdit,eax
           .elseif uMsg==WM_CTLCOLOREDIT
               invoke SetTextColor,wParam,Yellow
               invoke SetBkColor,wParam,Black
              invoke GetStockObject,BLACK_BRUSH
               ret
           .elseif uMsg==WM_SIZE
               mov edx,lParam
               mov ecx,edx
               shr ecx,16
               and edx,0ffffh
               invoke MoveWindow,hwndEdit,0,0,edx,ecx,TRUE
           .elseif uMsg==WM_COMMAND
              .if lparam==0
                   mov eax,wParam
                   .if ax==IDM_ASSEMBLE
                       mov sat.niLength,sizeof SECURITY_ATTRIBUTES
                       mov sat.lpSecurityDescriptor,NULL
                       mov sat.bInheritHandle,TRUE
                       invoke Createpipe,addr hRead,addr hWrite,addr sat,NULL
                       .if eax==NULL
                           invoke MessageBox, hWnd, addr CreatePipeError, \
                           addr AppName, MB_ICONERROR+ MB_OK
                       .else
                           mov startupinfo.cb,sizeof STARTUPINFO
                           invoke GetStartupInfo,addr startupinfo
                           mov eax, hWrite
                           mov startupinfo.hStdOutput,eax
                           mov startupinfo.hStdError,eax
                           mov startupinfo.dwFlags, STARTF_USESHOWWINDOW+\
                           STARTF_USESTDHANDLES
                           mov startupinfo.wShowWindow,SW_HIDE
                           invoke Createprocess, NULL, addr CommandLine, \
                           NULL, NULL, TRUE, NULL, NULL, NULL, addr startupinfo, \
                           addr pinfo
                           .if eax==NULL
                               invoke MessageBox,hWnd,addr CreateProcessError,\
                               addr AppName,MB_ICONERROR+MB_OK
                           .else
                               invoke CloseHandle,hWrite
                               .while TRUE
                                   invoke RtlZeroMemory,addr buffer,1024
                                   invoke ReadFile,hRead,addr buffer,1023,addr bytesRead,NULL
                                   .if eax==NULL
                                       .break
                                   .endif
                                   invoke SendMessage,hwndEdit,EM_SETSEL,-1,0
                                   invoke SendMessage,hwndEdit,EM_REPLACESEL,\
                                   FALSE,addr buffer
                               .endw
                           .endif
                           invoke CloseHandle,hRead
                       .endif
                   .endif
               .endif
           .elseif uMsg==WM_DESTROY
               invoke postQuitMessage,NULL
           .else
               invoke DefWindowproc,hWnd,uMsg,wParam,lParam
               ret
           .endif
           xor eax,eax
           ret
       Wndproc endp
       end start
Анализ:
Пример вызовет ml.exe, чтобы скомпилировать файл под названием test.asm, и перенаправит вывод в edit control. Когда программа загружена, она регистрирует класс окна и создает, как обычно, основное окно.

Теперь наступает самая интересная часть. Мы изменим цвет текста и бэкграунда edit control'а. Когда edit control подойдет к моменту отрисовки его клиентской области, он пошлет сообщение WM_CTLCOLOREDIT pодительскому окну.

wParam содержит хэндл device context'а, который edit control будет использовать для отрисовки его клиентской области. Мы можем использовать эту возможность для изменения характеристик HDC.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
           .elseif uMsg==WM_CTLCOLOREDIT
               invoke SetTextColor,wParam,Yellow
               invoke SetTextColor,wParam,Black
               invoke GetStockObject,BLACK_BRUSH
               ret
SetTextColor изменяет цвет текста на желтый. SetTextColor изменяет цвет фона текста на черный. И, наконец, мы получаем хэндл черной кисти, которую мы возвратим Windows. Обрабатывая сообщение WM_CTLCOLOREDIT, мы должны возвратить хэндл кисти, которую Windows использует для отрисовки бэкграунда edit control'а. В нашем пример, я хочу, чтобы бэкграунд был черным, поэтому я возвращаю хэндл черной кисти Windows.

Когда пользователь выберет пункт меню 'Assemble', программа создаст анонимный пайп.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
               .if ax==IDM_ASSEMBLE
                   mov sat.niLength,sizeof SECURITY_ATTRIBUTES
                   mov sat.lpSecurityDescriptor,NULL
                   mov sat.bInheritHandle,TRUE
Перед вызовом CreatePiрe мы должны заполнить структуру SECURITY_ATTRIBUTES. Заметьте, что мы можем передать NULL, если нас не интересуют настройки безопасности. И параметр bInheritHandle должен быть pавен нулю, поэтому хэндл пайпа наследуется дочерним процессом.
Assembler
1
                  invoke CreatePipe,addr hRead,addr hWrite,addr sat,NULL
После этого мы вызываем CreatePipe, которая заполнить переменные hRead и hWrite хэндлами концов чтения и записи.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
                       mov startupinfo.cb,sizeof STARTUPINFO
                       invoke GetStartupInfo,addr startupinfo
                       mov eax, hWrite
                       mov startupinfo.hStdOutput,eax
                       mov startupinfo.hStdError,eax
                       mov startupinfo.dwFlags, STARTF_USESHOWWINDOW+STARTF_USESTDHANDLES
                       mov startupinfo.wShowWindow,SW_HIDE
Затем мы заполним структуру STARTUPINFO. Мы вызовем GetStartupInfo, чтобы заполнить ее значениями родительского процесса. Вы должны заполнить эту структуру, если хотите, чтобы ваш код работал и под win9x и под NT. После вы модифицирует члены структуры. Мы копируем хэндл конца записи в hStdOutput и hStdError, так как мы хотим, чтобы дочерний процесс использовал их вместо соответствующих стандартных хэндлов. Мы также хотим спрятать консольное окно дочернего процесса, поэтому в wShowWindow мы помещаем значение SW_HIDE. И, наконец, мы должны подтвердить, что модифицированные нами поля нужно использовать, поэтому мы указываем флаги STARTF_USESHOWWINDOW и STARTF_USESTDHANDLES.
Кликните здесь для просмотра всего текста
Assembler
1
2
       invoke CreateProcess, NULL, addr CommandLine, NULL, NULL, TRUE, NULL, NULL, NULL,\
           addr startupinfo, addr pinfo
Теперь мы создаем дочерний процесс функцией CreateProcess. Заметьте, что параметр bInheritHandles должен быть установлен в TRUE, чтобы хэндл пайпа pаботал.
Кликните здесь для просмотра всего текста
Assembler
1
       invoke CloseHandle,hWrite
После успешного создания дочернего процесса мы закрываем конец записи пайпа. Помните, что мы передали хэндл записи дочернему процессу через структуру STURTUPINFO. Если мы не закроем конец записи с нашей стороны, будет два конца записи, и тогда пайп не будет работать. Мы должны закрыть конец записи после Createprocess, но до того, как начнем считывание данных.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
                       .while TRUE
                          invoke RtlZeroMemory,addr buffer,1024
                          invoke ReadFile,hRead,addr buffer,1023,addr bytesRead,NULL
                            .if eax==NULL
                              .break
                              .endif
                              invoke SendMessage,hwndEdit,EM_SETSEL,-1,0
                              invoke SendMessage,hwndEdit,EM_REpLACESEL,FALSE,addr buffer
                       .endw
Теперь мы готовы читать данные. Мы входим в бесконечный цикл, пока все данные не будут считаны. Мы вызываем RtlZeroMemory, чтобы заполнить буфер нулями, потом вызываем ReadFile и вместо хэндла файла передаем хэндл пайпа. Заметьте, что мы считываем максимум 1023 байта, так данные, которые мы получим, должны быть ASCIIZ-строкой, которую можно будет передать edit control'у.

Когда ReadFile вернет данные в буфере, мы выведем их в edit control. Тем не менее, здесь есть несколько проблем. Если мы используем SetWindowText, чтобы поместить данные в edit control, новые данные перезапишут уже считанные! Hам нужно, чтобы новые данные присоединялись к старым.

Для достижения цели мы сначала двигаем курсор к концу текста edit control'а, послав сообщение EM_SETSEL с wParam'ом равным -1. Затем мы присоединяем данные с помощью сообщения EM_REPLACESEL.
Кликните здесь для просмотра всего текста
Assembler
1
                      invoke CloseHandle,hRead
Когда ReadFile возвращает NULL, мы выходим из цикла и закрываем конец чтения.
________________________________
© Iczelion, пер. Aquila.
3
Вложения
Тип файла: zip tut21.zip (5.6 Кб, 48 просмотров)
Mikl___
Заблокирован
Автор FAQ
21.01.2013, 09:53  [ТС] #60
Win32 API. Урок 22. Суперклассинг
В этом туториале мы изучим суперклассинг, что это такое и для чего он служит. Вы также узнаете, как pеализовать навигацию с помощью клавиши 'Tab' в вашем окне.

Скачайте пример здесь.
Теория:
Во время вашей программной карьеры, вы наверняка встретитесь с ситуацией, когда вам потребуется несколько контролов с *несколько* отличным поведением. Hапример, вам могут потребоваться 10 edit control'ов, которые принимают только число. Есть несколько путей достигнуть цели:
  • Создать собственный класс и написать контролы с нуля
  • Создать эти edit control'ы и сабклассировать каждый из них
  • Суперклассировать edit control
Первый метод слишком сложен. Вам придется с нуля воплощать всю функциональность edit control'ов. Слишком трудоемкая задача, чтобы ее можно было быстро выполнить. Второй метод лучше, чем первый, но, тем не менее, также требует немало работы. Все нормально, пока вам надо сабклассировать несколько контролов, но сабклассинг дюжины или еще большего количества контролов может превратиться в аде. Суперклассинг - это техника, которой вы должны владеть.

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

Hиже приведены шаги для суперклассинга:
  • вызвать функцию GetClassInfoEx, чтобы получить информацию о классе окна, который вы хотите суперклассировать. GetClassInfoEx требует указатель на структуру WNDCLASSEX, которая будет заполнена информацией, если вызов пройдет успешно.
  • Изменяйте требуемые параметры WNDCLASSEX. Тем не менее, если два члена, которые вы должны обязательно изменить:
    • hInstance - Вы должны поместить в это поле хэндл программы.
    • lpszClassName - вы должны поместить сюда указатель на новое имя класса.
    • Вы не обязаны изменять параметр lрfnWndProc, но обычно вам будет это нужно делать. Главное не забудьте сохранить старое значение lpfnWndproc, если вам надо будет его вызывать с помощью CallWindowProc.
  • Зарегистрирует измененную структуру WNDCLASSEX. У вас будет новый класс окна, который будет обладать некоторыми характеристиками старого класса.
  • Создайте окна с помощью нового класса.
Суперклассинг лучше, чем сабклассинг, если вы хотите создать много контролов с одинаковыми характеристиками.
П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
143
144
   .386
   .model flat,stdcall
   option casemap:none
   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
 
   WM_SUPERCLASS equ WM_USER+5
   WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
   EditWndproc PROTO :DWORD,:DWORD,:DWORD,:DWORD
 
   .data
   ClassName  db "SuperclassWinClass",0
   AppName    db "Superclassing Demo",0
   EditClass  db "EDIT",0
   OurClass db "SUPEREDITCLASS",0
   Message  db "You pressed the Enter key in the text box!",0
 
   .data?
   hInstance dd ?
   hwndEdit dd 6 dup(?)
   OldWndproc dd ?
 
   .code
start:       invoke GetModuleHandle, NULL
       mov    hInstance,eax
       invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
       invoke Exitprocess,eax
 
   WinMain proc hInst:HINSTANCE,hprevInst:HINSTANCE,CmdLine: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_AppWORKSpACE
       mov wc.lpszMenuName,NULL
       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+WS_EX_CONTROLpARENT,ADDR ClassName,\
       ADDR AppName,WS_OVERLAppED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+ \
       WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,350,220,NULL,NULL,hInst,NULL
       mov hwnd,eax
       .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 uses ebx edi hWnd:HWND, uMsg:UINT, wparam:WpARAM, lparam:LpARAM
       LOCAL wc:WNDCLASSEX
 
       .if uMsg==WM_CREATE
           mov wc.cbSize,sizeof WNDCLASSEX
           invoke GetClassInfoEx,NULL,addr EditClass,addr wc
           push wc.lpfnWndproc
           pop OldWndproc
           mov wc.lpfnWndproc, OFFSET EditWndproc
           push hInstance
           pop wc.hInstance
           mov wc.lpszClassName,OFFSET OurClass
           invoke RegisterClassEx, addr wc
           xor ebx,ebx
           mov edi,20
           .while ebx<6
               invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR OurClass,NULL,WS_CHILD+\
                    WS_VISIBLE+WS_BORDER,20,edi,300,25,hWnd,ebx,hInstance,NULL
               mov dword ptr [hwndEdit+4*ebx],eax
               add edi,25
               inc ebx
           .endw
           invoke SetFocus,hwndEdit
       .elseif uMsg==WM_DESTROY
           invoke postQuitMessage,NULL
       .else
           invoke DefWindowproc,hWnd,uMsg,wParam,lParam
           ret
       .endif
       xor eax,eax
       ret
   Wndproc endp
 
   EditWndproc pROC hEdit:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
 
       .if uMsg==WM_CHAR
           mov eax,wparam
           .if (al>="0" && al<="9") || (al>="A" && al<="F") || (al>="a" && al<="f") || al==VK_BACK
               .if al>="a" && al<="f"
                  sub al,20h
               .endif
               invoke CallWindowproc,OldWndproc,hEdit,uMsg,eax,lParam
               ret
           .endif
       .elseif uMsg==WM_KEYDOWN
           mov eax,wParam
           .if al==VK_RETURN
               invoke MessageBox,hEdit,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
               invoke SetFocus,hEdit
           .elseif al==VK_TAB
               invoke GetKeyState,VK_SHIFT
               test eax,80000000h
               .if ZERO?
                   invoke GetWindow,hEdit,GW_HWNDNEXT
                   .if eax==NULL
                       invoke GetWindow,hEdit,GW_HWNDFIRST
                   .endif
               .else
                   invoke GetWindow,hEdit,GW_HWNDpREV
                   .if eax==NULL
                       invoke GetWindow,hEdit,GW_HWNDLAST
                   .endif
               .endif
               invoke SetFocus,eax
               xor eax,eax
               ret
           .else
               invoke CallWindowproc,OldWndproc,hEdit,uMsg,wParam,lParam
               ret
           .endif
       .else
           invoke CallWindowproc,OldWndproc,hEdit,uMsg,wParam,lParam
           ret
       .endif
       xor eax,eax
       ret
   EditWndproc endp
   end start
Анализ:
Программа создаст простое окно с "измененными" edit control'ами в своей клиентской области. Edit control'ы будут принимать только шестнадцатиричные числа. Фактически, я адаптировал пример с сабклассингом. Программа стартует как обычно, а самое интересное происходит, когда создается основное окно:
Кликните здесь для просмотра всего текста
Assembler
1
2
3
       .if uMsg==WM_CREATE
            mov wc.cbSize,sizeof WNDCLASSEX
           invoke GetClassInfoEx,NULL,addr EditClass,addr wc
Сначала мы заполним данными класса, который мы хотим суперклассировать, в нашем случае это класс edit'а. Помните, что вы должны установить параметр структуры WNDCLASSEX, перед тем, как вызвать GetClassInfoEx, в противном случае она будет заполнена неверно. После вызова GetClassInfoEx у нас будет иметься вся необходимая для создания нового класса информация.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
           push wc.lpfnWndproc
           pop OldWndproc
           mov wc.lpfnWndproc, OFFSET EditWndproc
           push hInstance
           pop wc.hInstance
           mov wc.lpszClassName,OFFSET OurClass
Теперь мы можем изменить некоторые члены wc. Первый из них - это указатель на процедуру окна. Так как нам нужно будет соединить вызовы новой и старой процедуры в цепь, нам необходимо сохранить старое значение в переменную, чтобы потом воспользоваться функцией CallWindowProc. Эта техника идентична с сабклассингом, не считая того, что вы напрямую изменяете структуру WNDCLASSEX не вызывая SetWindowLong. Следующие два поля должны быть изменены, иначе вам не удастся зарегистрировать ваш новый класс окна, hInstance и lрszClassName. Вы должны заменить старое значение hInstance на хэндл вашей програмы, а также выбрать имя для нового класса.
Кликните здесь для просмотра всего текста
Assembler
1
           invoke RegisterClassEx, addr wc
Когда все готово, регистрируйте новый класс. Вы получите новый класс, обладающий некоторыми характеристиками старого.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
           xor ebx,ebx
           mov edi,20
           .while ebx<6
               invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR OurClass,NULL,WS_CHILD+\
                    WS_VISIBLE+WS_BORDER,20,edi,300,25,hWnd,ebx,hInstance,NULL
               mov dword ptr [hwndEdit+4*ebx],eax
               add edi,25
               inc ebx
           .endw
           invoke SetFocus,hwndEdit
Теперь, когда мы зарегистрировали класс, мы можем создать основанные на нем окна. Вы вышеприведенном куске кода, я использовал ebx в качестве счетчика созданных окон. edi используется как y-координата левого верхнего угла окна. Когда окно создано, его хэндл сохраняется в массиве dword'ов. Когда все окна созданы, устанавливаем фокус на первое окно. К этому моменту у вас есть 6 edit control'ов, которые принимают только шестнадцатиричные числа. Hовая процедура окна, заменившая старую, выполняет роль фильтра. Фактически, это работает точно также, как и в примере с сабклассингом, только вам не нужно выполнять лишнюю pаботу.

Я вставил кусок кода, который обрабатывает нажатия на Tab, чтобы сделать пример более полезным для вас. Обычно, если вы помещаете контролы на диалоговое окно, его внутренний менеджер сам обрабатывает нажатия на клавиши навигации. Увы, но это недоступно, когда вы помещаете контролы на обычное окно. Вам следует сабклассировать их, чтобы нажатия на Tab обрабатывались. В нашем примере нам нет нужны сабклассировать контролы по одному, так как мы уже суперклассировали, поэтому можем pеализовать "центральный менеджер навигации контролов".
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
           .elseif al==VK_TAB
               invoke GetKeyState,VK_SHIFT
               test eax,80000000
               .if ZERO?
                   invoke GetWindow,hEdit,GW_HWNDNEXT
                   .if eax==NULL
                       invoke GetWindow,hEdit,GW_HWNDFIRST
                   .endif
               .else
                   invoke GetWindow,hEdit,GW_HWNDpREV
                   .if eax==NULL
                       invoke GetWindow,hEdit,GW_HWNDLAST
                   .endif
               .endif
               invoke SetFocus,eax
               xor eax,eax
               ret
Вышеприведенный код взят из процедуры EditWndClass. Он проверяет, нажал ли пользователь клавишу tab, если да, он вызывает GetKeyStat, чтобы узнать, нажата ли также клавиша Shift. GetKeyState возвращает значение в eax, которое определяет, нажата ли указанная клавиша или нет. Если клавиша нажата, верхний бит eax будет установлен. Если нет, он будет очищен. Поэтому мы тестируем полученное значение 80000000h. Если верхний бит установлен, это будет означать, что пользователь нажал shift и tab одновременно, и должны обработать это отдельно.

Если пользователь нажал клавишу Tab, мы вызываем GetWindow, чтобы получить хэндл следующего контрола. Мы используем флаг GW_HWNDNEXT, чтобы указать GetWindow получить хэндл следующего окна относительно текущего hEdit. Если эта функция возвращает NULL, то такого окна нет и мы устанавливаем фокус на первое окно, вызвав GetWindow с флагом GW_HWNDFIRST. Shift-Tab работает так же, как и обычно нажатие на Tab, только передвигает фокус окна назад.
___________________________________
© Iczelion, пер. Aquila.
4
Вложения
Тип файла: zip tut22.zip (3.5 Кб, 43 просмотров)
21.01.2013, 09:53
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
21.01.2013, 09:53
Привет! Вот еще темы с ответами:

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

Комп включается сам по себе - Windows
проблема такая - после выключения компьютера, он самопроизвольно включается каждый раз с разным промежутком времени от 2-3 секунд до 1-2...

Монитор гаснет сам по себе - Мониторы
Купил монитор , подключил его в ноуту. Работают оба монитора вместе (новый и монитор ноута), новый монитор гаснет иногда, всего три раза...

Компьютер сам по себе выключается. - Материнские платы
Комп работает, работат, а потом сам по себе выключается... Иногда долго работает, а инода часто отключается...


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

Или воспользуйтесь поиском по форуму:
60
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru