[hwndEdit:
0 0 0 0 0 0
OldWndProc:
0
ClassName:
'SuperclassWinClass' 0
AppName:
'Superclassing Demo' 0
EditClass:
'EDIT' 0
OurClass:
'SUPEREDITCLASS' 0
EnterMessage:
'You pressed the Enter key in the text box!' 0]
[WindowClassEx:
cbSize: len
style:
3 lpfnWndProc: MainWindowProc cbClsExtra: 0
cbWndExtra: 0
hInstance:
0 hIcon: 0 hCursor: 0 hbrBackground: &COLOR_APPWORKSPACE
lpszMenuName:
0 lpszClassName: ClassName hIconSm: 0]
[WindowHandle: 0]
[FirstMessage: 0 #7]
_________________________________________________________________________________________
Main:
call 'Kernel32.GetModuleHandleA' 0 | mov D§hInstance eax
call 'User32.LoadIconA' 0 &IDI_APPLICATION | mov D§hIcon
eax, D§hIconSm eax
call 'User32.LoadCursorA' 0 &IDC_ARROW | mov D§hCursor
eax
call 'User32.RegisterClassExA' WindowClassEx
call 'User32.CreateWindowExA' &WS_EX_CLIENTEDGE+&WS_EX_CONTROLPARENT
ClassName AppName,
&WS_OVERLAPPEDWINDOW+&WS_VISIBLE,
&CW_USEDEFAULT &CW_USEDEFAULT 350 220,
0 0 D§hInstance 0
mov D§WindowHandle eax
call 'User32.ShowWindow' D§WindowHandle &SW_SHOW
call 'User32.UpdateWindow' D§WindowHandle
L1:
call 'User32.GetMessageA' FirstMessage 0 0 0 | cmp eax 0 | je L9>
call 'User32.TranslateMessage' FirstMessage
call 'User32.DispatchMessageA' FirstMessage
jmp L1<
L9: call 'Kernel32.ExitProcess' 0
_____________________________________________________________________________________
[OurWindowClass:
OWC_len: len
OWC_style:
3 OWC_lpfnWndProc: 0 OWC_scbClsExtra: 0 OWC_scbWndExtra:
0
OWC_shInstance:
0 OWC_hIcon: 0 OWC_hCursor: 0 OWC_shbrBackground: &COLOR_APPWORKSPACE
OWC_slpszMenuName:
0 OWC_slpszClassName: OurClass OWC_hIconSm: 0]
Proc
MainWindowProc:
Arguments @Adressee, @Message, @wPAram, @lParam
pushad
.If D@Message = &WM_CREATE
call 'USER32.GetClassInfoExA' &NULL EditClass OurWindowClass
move D§OldWndProc D§OWC_lpfnWndProc
mov D§OWC_lpfnWndProc EditWndProc
move D§OWC_shInstance D§hInstance
mov D§OWC_slpszClassName OurClass
call 'User32.RegisterClassExA' OurWindowClass
mov ebx 0, edi 20
While ebx < 6
push ebx, edi
call 'User32.CreateWindowExA' &WS_EX_CLIENTEDGE OurClass &NULL,
&WS_CHILD+&WS_VISIBLE+&WS_BORDER,
20 edi 300 25 D@Adressee ebx,
D§hInstance &NULL
pop edi, ebx
mov D§hwndEdit+4*ebx eax
add edi 25 | inc ebx
End_While
call 'User32.SetFocus' D§hwndEdit
.Else_If D@Message = &WM_DESTROY
call 'User32.PostQuitMessage' &NULL
.Else
popad
call 'USER32.DefWindowProcA' D@Adressee D@Message D@wParam
D@lParam
Exit
.End_If
popad | mov eax &FALSE
EndP
_________________________________________________________________________________________
Proc
EditWndProc:
Arguments @Adressee, @Message, @wPAram, @lParam
pushad
.If D@Message = &WM_CHAR
mov eax D@wParam
On al = &VK_BACK, jmp L8>>
On al < '0', jmp L9>>
On al <= '9', jmp L8>>
and al 0FF-32
On al < 'A', jmp L9>>
On al > 'F', jmp L9>>
mov D@wParam eax | jmp L8>>
.Else_If D@Message = &WM_KEYDOWN
mov eax D@wParam
..If al = &VK_RETURN
call 'USER32.MessageBoxA' D@Adressee EnterMessage AppName &MB_ICONINFORMATION
call 'USER32.SetFocus' D@Adressee
..Else_If al = &VK_TAB
call 'USER32.GetKeyState' &VK_SHIFT
test eax 080000000 | jnz L1>
call 'USER32.GetWindow' D@Adressee &GW_HWNDNEXT
On eax = 0, call 'USER32.GetWindow' D@Adressee &GW_HWNDFIRST
jmp L2>
L1:
call 'USER32.GetWindow' D@Adressee &GW_HWNDPREV
On eax = 0, call 'USER32.GetWindow' D@Adressee &GW_HWNDLAST
L2:
call 'USER32.SetFocus' eax
..Else
jmp L8>
..End_If
.Else ; Valid_Char:
L8:
popad
call 'USER32.CallWindowProcA' D§OldWndProc D@Adressee D@Message D@wParam
D@lParam
Exit
.End_If
; UnValid_Char:
L9: popad | mov eax &FALSE
EndP
.If D@Message = &WM_CREATE
call 'USER32.GetClassInfoExA' &NULL EditClass OurWindowClass
We must first fill the WNDCLASSEX structure with the data from the class which we want to superclass, in this case, it's EDIT class. Remember that you must set the cbSize member of the WNDCLASSEX structure before you call GetClassInfoEx else the WNDCLASSEX structure will not be filled properly. After GetClassInfoEx returns, wc is filled with all information we need to create a new window class.
move D§OldWndProc D§OWC_lpfnWndProc
mov D§OWC_lpfnWndProc EditWndProc
move D§OWC_shInstance D§hInstance
mov D§OWC_slpszClassName OurClass
Now we must modify some members of wc. The first one is the pointer to the window procedure. Since we need to chain our own window procedure with the original one, we have to save it into a variable so we can call it with CallWindowProc. This technique is identical to subclassing except that you modify the WNDCLASSEX structure directly without having to call SetWindowLong. The next two members must be changed else you will not be able to register your new window class, hInstance and lpsClassName. You must replace original hInstance value with hInstance of your own program. And you must choose a new name for the new class.
call 'User32.RegisterClassExA' OurWindowClass
When all is ready, register
the new class. You will get a new class with some characteristics of the
old class.
mov ebx 0, edi 20
While ebx < 6
push ebx, edi
call 'User32.CreateWindowExA' &WS_EX_CLIENTEDGE OurClass &NULL,
&WS_CHILD+&WS_VISIBLE+&WS_BORDER,
20 edi 300 25 D@Adressee ebx,
D§hInstance &NULL
pop edi, ebx
mov D§hwndEdit+4*ebx eax
add edi 25 | inc ebx
End_While
call 'User32.SetFocus' D§hwndEdit
Now that we registered the
class, we can create windows based on it. In the above snippet, I use ebx
as the counter of the number of windows created. edi is used as the y coordinate
of the left upper corner of the window. When a window is created, its handle
is stored in the array of dwords. When all windows are created, set input
focus to the first window.
At this point, you got 6
edit controls which accept only hex digits. The substituted window proc
handles the filter. Actually, it's identical to the window proc in subclassing
example. As you can see, you don't have to do extra work of subclassing
them.
I throw in a code snippet
to handle control navigation with tabs to make this example more juicy.
Normally, if you put controls on a dialog box, the dialog box manager handles
the navigation keys for you so you can tab to go to the next control or
shift-tab to go back to the previous control. Alas, such feature is not
available if you put your controls on a simple window. You have to subclass
them so you can handle the Tab keys yourself. In our example, we need not
subclass the controls one by one because we already superclassed them,
so we can provide a "central control navigation manager" for them.
..Else_If al = &VK_TAB
call 'USER32.GetKeyState' &VK_SHIFT
test eax 080000000 | jnz L1>
call 'USER32.GetWindow' D@Adressee &GW_HWNDNEXT
On eax = 0, call 'USER32.GetWindow' D@Adressee &GW_HWNDFIRST
jmp L2>
L1:
call 'USER32.GetWindow' D@Adressee &GW_HWNDPREV
On eax = 0, call 'USER32.GetWindow' D@Adressee &GW_HWNDLAST
L2:
call 'USER32.SetFocus' eax
..Else
jmp L8>
..End_If
The above code snippet is
from EditWndClass procedure. It checks if the user press Tab key, if so,
it call GetKeyState to check if the SHIFT key is also pressed. GetKeyState
returns a value in eax that determines whether the specified key is pressed
or not. If the key is pressed, the high bit of eax is set. If not, the
high bit is clear. So we test the return value against 80000000h. If the
high bit is set, it means the user pressed shift+tab which we must handle
separately.
If the user press Tab key
alone, we call GetWindow to retrieve the handle of the next control. We
use GW_HWNDNEXT flag to tell GetWindow to obtain the handle to the window
that is next in line to the current hEdit. If this function returns NULL,
we interpret it as no more handle to obtain so the current hEdit is the
last control in the line. We will "wrap around" to the first control by
calling GetWindow with GW_HWNDFIRST flag. Similar to the Tab case, shift-tab
just works in reverse.