Theory:
In this example present implementation of adding linenumbers subclass of standard EDIT control.
linenumbers are sent to NC area of control, creation of NC area reached with handling WM_NCCALCSIZE message and adding 40 to left field of first RECT pointed by lparam of that message, that reserves a rectangle 40 pix width from left of client area.
Process of drawing linenumbers itself passed to WM_NCPAINT handler. Because this drawing goes to our special added area, after that drawing execution moved to standart EDIT handler of WM_NCPAINT to draw borders and scrolls.
In addition to WM_NCCALCSIZE & WM_NCPAINT handling added handlers of WM_PAINT & WM_KEYDOWN (I guess last two would be enought to catch all cases that change content of linenumbers).
handling of WM_PAINT (happend when something appear infront of client area - another window (for ex. context menu), or when client visible area have to be scrolled).
When adding or removing char or line dont causing scrolling of visible area - repeant (in form of passing WM_PAINT message) is not happend, there goes trick of filling rects with background color for lines or characters that are removed or drawing text for chars or lines that are added. (I guessed for this cases would be perfect to catch WM_KEYDOWN and in addition to standard processing make InvalidateRect in it). This approach avoid any flickering usualy specific to linenumbers located in left margin of text of client area. My override of WM_PAINT & WM_KEYDOWN handling in some manner breach standard processing (at winXP and below for ex.) after parts of window stoped to be covered by another window they rarely have some grey graphic artefacts (narrow horisontal rectangle or wide vertical rectangle or both) that lay on border, scroll bar, client area (that never happend for nonsubclassed EDIT). Could it be related that I pass "invoke BaseEditProc,[hwnd],WM_PAINT,0,0" to standard message processing, and wparam & lparam have not only zero values(like in official documentation) and I should pass "invoke BaseEditProc,[hwnd],WM_PAINT,[wparam],[lparam]". I can
t understand where I trick myself. In windows 10 I could not reproduce problem, so attached screen holds application look, not artifacts over it. I will attach them later. Critic & suggestions are very appreciated.
; Simple text editor - fasm example program
format PE GUI 4.0
entry start
include 'win32a.inc'
IDR_ICON = 17
IDR_MENU = 37
IDM_NEW = 101
IDM_EXIT = 102
IDM_ABOUT = 901
section '.text' code readable executable
start: RTL_C
invoke GetModuleHandle,0
mov [wc.hInstance],eax
invoke LoadIcon,eax,IDR_ICON
mov [wc.hIcon],eax
invoke LoadCursor,0,IDC_ARROW
mov [wc.hCursor],eax
invoke RegisterClass,wc
test eax,eax
jz error
invoke LoadMenu,[wc.hInstance],IDR_MENU
invoke CreateWindowEx,0,_class,_title,WS_VISIBLE+WS_OVERLAPPEDWINDOW,144,128,256,256,NULL,eax,[wc.hInstance],NULL
test eax,eax
jz error
msg_loop:
invoke GetMessage,msg,NULL,0,0
cmp eax,1
jb end_loop
jne msg_loop
invoke TranslateMessage,msg
invoke DispatchMessage,msg
jmp msg_loop
error:
invoke MessageBox,NULL,_error,NULL,MB_ICONERROR+MB_OK
end_loop:
invoke ExitProcess,[msg.wParam]
flush_locals
WindowProc: RTL_C hwnd,wmsg,wparam,lparam
frame ebp
push ebx esi edi
mov eax,[wmsg]
cmp eax,WM_CREATE
je .wmcreate
cmp eax,WM_SIZE
je .wmsize
cmp eax,WM_SETFOCUS
je .wmsetfocus
cmp eax,WM_COMMAND
je .wmcommand
cmp eax,WM_DESTROY
je .wmdestroy
.defwndproc:
invoke DefWindowProc,[hwnd],[wmsg],[wparam],[lparam]
jmp .finish
.wmcreate:
invoke CreateWindowEx,WS_EX_CLIENTEDGE,_edit,0,WS_VISIBLE+WS_CHILD+WS_HSCROLL+WS_VSCROLL+ES_AUTOHSCROLL+ES_AUTOVSCROLL+ES_MULTILINE,0,0,0,0,[hwnd],0,[wc.hInstance],NULL
or eax,eax
jz .failed
mov [edithwnd],eax
invoke SetWindowLong,eax,GWL_WNDPROC,NewEditProc
mov [BaseEditProc],eax
invoke CreateFont,16,0,0,0,0,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_RASTER_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FIXED_PITCH+FF_DONTCARE,NULL
or eax,eax
jz .failed
mov [editfont],eax
invoke SendMessage,[edithwnd],WM_SETFONT,eax,FALSE
;xor eax,eax
jmp .wmsize
.failed:
or eax,-1
jmp .finish
.wmsize:
invoke GetClientRect,[hwnd],client
invoke MoveWindow,[edithwnd],[client.left],[client.top],[client.right],[client.bottom],TRUE
xor eax,eax
jmp .finish
.wmsetfocus:
invoke SetFocus,[edithwnd]
xor eax,eax
jmp .finish
.wmcommand:
mov eax,[wparam]
and eax,0FFFFh
cmp eax,IDM_NEW
je .new
cmp eax,IDM_ABOUT
je .about
cmp eax,IDM_EXIT
je .wmdestroy
jmp .defwndproc
.new:
invoke SendMessage,[edithwnd],WM_SETTEXT,0,0
jmp .finish
.about:
invoke MessageBox,[hwnd],_about_text,_about_title,MB_OK
jmp .finish
.wmdestroy:
invoke DeleteObject,[editfont]
invoke PostQuitMessage,0
xor eax,eax
.finish:
pop edi esi ebx
exitf
stack_cleanup callee
flush_locals
NewEditProc: RTL_C hwnd,wmsg,wparam,lparam
;frame esp
mov eax,[wmsg]
cmp eax,WM_NCCALCSIZE
je .make_ncarea
cmp eax,WM_NCPAINT
je .wmncpaint
cmp eax,WM_PAINT
je .wmpaint
cmp eax,WM_KEYDOWN
jnz .default
invoke InvalidateRect,[hwnd],0,FALSE
.default:
jmp [BaseEditProc]
.make_ncarea:
mov eax,[lparam]
add dword[eax+RECT.left],40
jmp .default
;exitf
flush_locals
NewEditProc.wmpaint: RTL_C hwnd,wmsg,wparam,lparam
frame ebp
invoke BaseEditProc,[hwnd],WM_PAINT,0,0
invoke GetWindowRgn,[hwnd],[wparam]
mov [wmsg],WM_NCPAINT
exitf
flush_locals
NewEditProc.wmncpaint: RTL_C hwnd,wmsg,wparam,lparam
frame ebp
invoke SendMessage,[hwnd], EM_GETLINECOUNT, 0, 0
push esi
mov esi,eax
invoke SendMessage,[hwnd], EM_GETFIRSTVISIBLELINE, 0, 0
sub esi,eax
inc eax
mov ecx,10
xor edx,edx
div ecx
add edx,'0'
mov byte[ntext+3],dl
xor edx,edx
div ecx
add edx,'0'
mov byte[ntext+2],dl
xor edx,edx
div ecx
add edx,'0'
add eax,'0'
mov byte[ntext+1],dl
mov byte[ntext+0],al
invoke GetWindowDC,[hwnd]
mov [NC_DC],eax
invoke GetTextExtentPoint32,[NC_DC],ntext,1,size
invoke GetClientRect,[hwnd],client
invoke GetSystemMetrics,SM_CYEDGE
add [client.left],eax
add [client.top],eax
mov [client.right],40
sub [client.right],eax
add [client.bottom],eax
invoke GetSystemMetrics,SM_CYHSCROLL
add [client.bottom],eax
invoke SelectObject,[NC_DC], [editfont]
invoke FillRect,[NC_DC],client,HOLLOW_BRUSH
invoke SetBkMode,[NC_DC], TRANSPARENT
invoke GetSystemMetrics,SM_CYEDGE
invoke TextOut,[NC_DC], eax, eax, ntext, 4
label loop_TextOut at $-6
sub esp,5*4
inc byte[ntext+3]
cmp byte[ntext+3],'9'
jbe @F
sub byte[ntext+3],10
inc byte[ntext+2]
cmp byte[ntext+2],'9'
jbe @F
sub byte[ntext+2],10
inc byte[ntext+1]
cmp byte[ntext+1],'9'
jbe @F
sub byte[ntext+1],10
inc byte[ntext+0]
@@:
mov eax,[size.Y]
add dword[esp+8],eax
mov eax,dword[esp+8]
sub eax,[size.Y]
dec esi
jz @F
cmp eax,[client.bottom]
jb loop_TextOut
@@:
add esp,5*4
pop esi
invoke ReleaseDC,[hwnd],[NC_DC]
xor eax,eax
exitf
jmp [BaseEditProc]
flush_locals
section '.data' data readable writeable
_title TCHAR 'MiniPad',0
_about_title TCHAR 'About MiniPad',0
_about_text TCHAR 'This is Win32 example program created with flat assembler.',0
_error TCHAR 'Startup failed.',0
_class TCHAR 'MINIPAD32',0
_edit TCHAR 'EDIT',0
ntext TCHAR '0001',0
wc WNDCLASS 0,WindowProc,0,0,NULL,NULL,NULL,COLOR_BTNFACE+1,NULL,_class
edithwnd dd ?
editfont dd ?
BaseEditProc dd ?
NC_DC dd ?
msg MSG
client RECT
size:
.X dd ?
.Y dd ?
section '.idata' import data readable writeable
library kernel,'KERNEL32.DLL',\
user,'USER32.DLL',\
gdi,'GDI32.DLL'
import kernel,\
GetModuleHandle,'GetModuleHandleA',\
ExitProcess,'ExitProcess'
import user,\
RegisterClass,'RegisterClassA',\
CreateWindowEx,'CreateWindowExA',\
DefWindowProc,'DefWindowProcA',\
SetWindowLong,'SetWindowLongA',\
RedrawWindow,'RedrawWindow',\
GetMessage,'GetMessageA',\
TranslateMessage,'TranslateMessage',\
DispatchMessage,'DispatchMessageA',\
SendMessage,'SendMessageA',\
LoadCursor,'LoadCursorA',\
LoadIcon,'LoadIconA',\
LoadMenu,'LoadMenuA',\
GetClientRect,'GetClientRect',\
DrawText,'DrawTextA',\
MoveWindow,'MoveWindow',\
SetFocus,'SetFocus',\
MessageBox,'MessageBoxA',\
GetDCEx,'GetDCEx',\
GetWindowDC,'GetWindowDC',\
GetWindowRgn,'GetWindowRgn',\
ReleaseDC,'ReleaseDC',\
FillRect,'FillRect',\
InvalidateRect,'InvalidateRect',\
GetSystemMetrics,'GetSystemMetrics',\
PostQuitMessage,'PostQuitMessage'
import gdi,\
CreateFont,'CreateFontA',\
GetTextExtentPoint32,'GetTextExtentPoint32A',\
TextOut,'TextOutA',\
SetTextColor,'SetTextColor',\
SetBkColor,'SetBkColor',\
SetTextAlign,'SetTextAlign',\
SetBkMode,'SetBkMode',\
SelectObject,'SelectObject',\
DeleteObject,'DeleteObject'
section '.rsrc' resource data readable
; resource directory
directory RT_MENU,menus,\
RT_ICON,icons,\
RT_GROUP_ICON,group_icons,\
RT_VERSION,versions
; resource subdirectories
resource menus,\
IDR_MENU,LANG_ENGLISH+SUBLANG_DEFAULT,main_menu
resource icons,\
1,LANG_NEUTRAL,icon_data
resource group_icons,\
IDR_ICON,LANG_NEUTRAL,main_icon
resource versions,\
1,LANG_NEUTRAL,version
menu main_menu
menuitem '&File',0,MFR_POPUP
menuitem '&New',IDM_NEW
menuseparator
menuitem 'E&xit',IDM_EXIT,MFR_END
menuitem '&Help',0,MFR_POPUP + MFR_END
menuitem '&About...',IDM_ABOUT,MFR_END
icon main_icon,icon_data,'minipad.ico'
versioninfo version,VOS__WINDOWS32,VFT_APP,VFT2_UNKNOWN,LANG_ENGLISH+SUBLANG_DEFAULT,0,\
'FileDescription','MiniPad - example program',\
'LegalCopyright','No rights reserved.',\
'FileVersion','1.0',\
'ProductVersion','1.0',\
'OriginalFilename','MINIPAD.EXE'