Page 1 of 1

Writing A Window Source File Editor In X64 MASM - Part II

#1 Martyn.Rae   User is offline

  • The programming dinosaur
  • member icon

Reputation: 553
  • View blog
  • Posts: 1,432
  • Joined: 22-August 09

Posted 14 November 2018 - 10:57 PM

Writing A Window Source File Editor In X64 MASM - Part II

Continuing with this series of tutorials, I have had to make a few changes to the existing code I posted in the previous tutorial. These changes are as a result of Microsoft updating MASM x64 Assembler in the Visual Studio 2017. The changes are to do with stack frames and unwinding (this should not have affected the code, but it did!).

Anyway continuing on, this tutorial posting concerns itself with using the code I produced in part I as the basis for a simple source file editor. The code produced and provided below permits you to select a source file for editing, creates a window and displays the content. The vertical scrollbar permits you to set the position in the text.

This code is far from complete as we have to address the matter of editing lines of source in the display window. All of that will come in the next tutorial!

						title			'Editor'
						public			Editor
						externdef		__imp_VirtualAlloc: proc
						externdef		__imp_VirtualFree: proc
						externdef		__imp_GetOpenFileName: proc
						externdef		__imp_GetDesktopWindow: proc
						externdef		__imp_GetModuleHandleW: proc
						externdef		__imp_CreateFileW: proc
						externdef		__imp_GetFileSizeEx: proc
						externdef		__imp_ReadFile: proc
						externdef		__imp_CloseHandle: proc
						externdef		__imp_GetStockObject: proc
						externdef		__imp_LoadCursorW: proc
						externdef		__imp_CreateFontW: proc
						externdef		__imp_UpdateWindow: proc
						externdef		__imp_SetForegroundWindow: proc
						externdef		__imp_GetOpenFileNameW: proc
						externdef		__imp_RegisterClassExW: proc
						externdef		__imp_CreateWindowExW: proc
						externdef		__imp_DefWindowProcW: proc
						externdef		__imp_GetSystemMetrics: proc
						externdef		__imp_GetMessageW: proc
						externdef		__imp_TranslateMessage: proc
						externdef		__imp_DispatchMessageW: proc
						externdef		__imp_CreateFontW: proc
						externdef		__imp_SelectObject: proc
						externdef		__imp_DrawTextA: proc
						externdef		__imp_CreateSolidBrush: proc
						externdef		__imp_GetClientRect: proc
						externdef		__imp_BeginPaint: proc
						externdef		__imp_EndPaint: proc
						externdef		__imp_GetClientRect: proc
						externdef		__imp_SetScrollRange: proc
						externdef		__imp_SetScrollPos: proc
						externdef		__imp_InvalidateRect: proc
						externdef		__imp_FillRect: proc
						externdef		__imp_SetBkColor: proc
						externdef		__imp_SetTextColor: proc
						externdef		__imp_GetWindowDC: proc
						externdef		__imp_ReleaseDC: proc
						externdef		__imp_GetDeviceCaps: proc
						externdef		__imp_MulDiv: proc
						externdef		CreateList: proc
						externdef		AddItem: proc
						externdef		GetItem: proc

GENERIC_READ			equ				80000000H
OPEN_EXISTING			equ				3
FILE_ATTRIBUTE_NORMAL	equ				80H
MEM_COMMIT				equ				1000H
MEM_RELEASE				equ				8000H
PAGE_READWRITE			equ				4
OFN_PATHMUSTEXIST       equ				800H
OFN_FILEMUSTEXIST		equ				1000H
CS_OWNDC				equ				20H
WS_OVERLAPPED			equ				0
WS_CAPTION				equ				0C00000H
WS_SYSMENU				equ				80000H
WS_THICKFRAME			equ				40000H
WS_MINIMIZEBOX			equ				20000H
WS_MAXIMIZEBOX			equ				10000H
WS_VISIBLE				equ				10000000H
WS_VSCROLL				equ				0200000H
WS_HSCROLL				equ				0100000H
WS_OVERLAPPEDWINDOW		equ				WS_CAPTION or WS_SYSMENU or WS_THICKFRAME or WS_MINIMIZEBOX or WS_MAXIMIZEBOX or WS_VISIBLE or WS_VSCROLL or WS_HSCROLL
CW_USEDEFAULT			equ				80000000H
WS_EX_WINDOWEDGE		equ				100H
WS_EX_CLIENTEDGE		equ				200H
BLACK_BRUSH				equ				4
FW_NORMAL				equ				400
ANSI_CHARSET			equ				0
OUT_TT_PRECIS			equ				4
CLIP_DEFAULT_PRECIS		equ				0
PROOF_QUALITY			equ				2
FIXED_PITCH				equ				1
WM_PAINT				equ				0FH
WM_HSCROLL              equ		        114H
WM_VSCROLL              equ		        115H
IDC_ARROW				equ				32512
CS_VREDRAW				equ				1
CS_HREDRAW				equ				2
DT_LEFT					equ				0
SB_HORZ					equ				0
SB_VERT					equ				1
SB_LINEUP				equ				0
SB_LINELEFT				equ				0
SB_LINEDOWN				equ				1
SB_LINERIGHT			equ				1
SB_PAGEUP				equ				2
SB_PAGELEFT				equ				2
SB_PAGEDOWN				equ				3
SB_PAGERIGHT			equ				3
SB_THUMBPOSITION		equ				4
SB_THUMBTRACK			equ				5
SB_TOP					equ				6
SB_LEFT					equ				6
SB_BOTTOM				equ				7
SB_RIGHT				equ				7
SB_ENDSCROLL			equ				8
FF_MODERN				equ				30H
LINE_HEIGHT				equ				12

OPENFILENAMEW			struc
	lStructSize			dd				? 
						align			8
	hwndOwner			dq				?
	hInstance			dq				?
	lpstrFilter			dq				?
	lpstrCustomFilter	dq				?
	nMaxCustFilter		dd				?
	nFilterIndex		dd				?
	lpstrFile			dq				?
	nMaxFile			dd				?
						align			8
	lpstrFileTitle		dq				?
	nMaxFileTitle		dd				?
						align			8
	lpstrInitialDir		dq				?
	lpstrTitle			dq				?
	Flags				dd				?
	nFileOffset			dw				?
	nFileExtension		dw				?
	lpstrDefExt			dq				?
	lCustData			dq				?
	lpfnHook			dq				?
	lpTemplateName		dq				?
	pvReserved			dq				?
	dwReserved			dd				?
	FlagsEx				dd				?
OPENFILENAMEW			ends

WNDCLASSEX				struc
	cbSize				dd				?   
	style				dd      		?
	lpfnWndProc			dq				?
	cbClsExtra			dd       		?
	cbWndExtra			dd       		?
	hInstance			dq				?
	hIcon				dq				?
	hCursor				dq				?
	hbrBackground		dq				?
	lpszMenuName		dq				?
	lpszClassName		dq				?
	hIconSm				dq				?
WNDCLASSEX				ends

MSG						struc
	hwnd				dq				?
	message				dd				?
	wParam				dd				?
	lParam				dd				?
	time				dq				?
	pt					dq				?
MSG						ends

PAINTSTRUCT				struc
	hdc					dq				?
	fErase				dd				?
	rcPaintLeftTop		dq				?
	rcPaintRightBottom	dq				?
	fRestore			dd				?
	fIncUpdate			dd				?
PAINTSTRUCT				ends

RECT					struc
	left				dd				?
	top					dd				?
	right				dd				?
	bottom				dd				?
RECT					ends

						.data

ListStruct				dq				0
WindowDC				dq				0
TotalLines				dq				0
TopLine					dq				0
FontHeight				dd				0
						align			8
OpenFileName			OPENFILENAMEW	<>
						align			8
OpenFileNameFilter		dw				'A', 'l', 'l', 0, '*', '.', '*', 0, 0
						align			8
FileName				dw				260 dup (0)
FileSize				dq				0
BytesRead				dq				0
						align			8
MainWindowClassName		dw				'M', 'a', 'i', 'n', 'W', 'i', 'n', 'd', 'o', 'w', 'C', 'l', 'a', 's', 's',  0
						align			8
MainWindowClass			WNDCLASSEX		<SIZE WNDCLASSEX, CS_VREDRAW or CS_HREDRAW, 0, 0, 0, 0>
						align			8				  
WindowTitle				dw				'E', 'd', 'i', 't', 'o', 'r', 0
						align			8				  
Message					MSG				<>
						align			8				  
MainWindow				dq				0
						align			8				  
EditorFont				dd				0
						align			8
SysFontName				dw				'C', 'o', 'n', 's', 'o', 'l', 'a', 's', 0
						align			8
paintStructure			PAINTSTRUCT		<>
						align			8
clientRect				RECT			<>

						.code

						;
						;	Editor is called with the name of the file as the first parameter (rcx)
						;

FetchSource:			sub			rsp, 128

						;
						;	Open the file whose name is specified by  the caller in rcx
						;

						mov			rdx, GENERIC_READ
						xor			r8, r8
						xor			r9, r9
						mov			48[rsp], r8
						mov			rax, OPEN_EXISTING
						mov			32[rsp], rax
						mov			rax, FILE_ATTRIBUTE_NORMAL
						mov			40[rsp], rax
						call		qword ptr [__imp_CreateFileW]
						cmp			rax, -1
						je			OpenFailed
						mov			r15, rax

						;
						;	Get the size of the file in bytes
						;

						mov			rcx, rax
						lea			rdx, FileSize
						call		qword ptr [__imp_GetFileSizeEx]

						;
						;	Allocate memory for the file using the size just retrieved from the
						;	system plus one byte so we can terminate the file
						;

						mov			rdx, FileSize
						inc			rdx
						xor			rcx, rcx
						mov			r8, MEM_COMMIT
						mov			r9, PAGE_READWRITE
						call		qword ptr [__imp_VirtualAlloc]
						mov			r14, rax

						;
						;	Read the file
						;

						mov			rcx, r15
						mov			rdx, r14
						mov			r8, FileSize
						lea			r9, BytesRead
						xor			rax, rax
						mov			32[rsp], rax
						call		qword ptr [__imp_ReadFile]

						;
						;	Close the open file handle
						;

						mov			rcx, r15
						call		qword ptr [__imp_CloseHandle]

						;	Allocate another chuck of memory so that we can place the massaged
						;	file content

						mov			rdx, FileSize
						inc			rdx
						xor			rcx, rcx
						mov			r8, MEM_COMMIT
						mov			r9, PAGE_READWRITE
						call		qword ptr [__imp_VirtualAlloc]
						mov			r15, rax

						;
						;	Examine each byte in the source file. Each CR (0DH) goes across
						;	to the destination buffer as a NULL (0) byte. LF (0AH) chars
						;	are not transferred. Teminate the loop when a NAK (015H) is
						;	encountered. During the process of complete lines.
						;

						mov			rax, FileSize
						mov			byte ptr [r14+rax], 015H
						push		rsi
						push		rdi
						mov			rsi, r14
						mov			rdi, r15
						xor			rcx, rcx
FindEOL:				mov			al, byte ptr [rsi]
						cmp			al, 0DH
						je			GotEOL
						cmp			al, 0AH
						je			SkipCharacter
						cmp			al, 015H
						je			GotLineCount
						mov			byte ptr [rdi], al
						inc			rdi
SkipCharacter:			inc			rsi
						jmp			FindEOL
GotEOL:					mov			byte ptr [rdi], 0
						inc			rcx
						inc			rdi
						jmp			SkipCharacter

						;
						;	Create the list using the number of lines we have counted
						;

GotLineCount:			dec			rcx
						mov			byte ptr [rdi], 15H
						mov			TotalLines, rcx
						shl			rcx, 1
						lea			rax, CreateList
						call		rax
						mov			r13, rax

						;
						;	Free the original source file
						;

						mov			rcx, r14
						xor			rdx, rdx
						mov			r8, MEM_RELEASE
						call		qword ptr [__imp_VirtualFree]

						;
						;	Scan the NULL terminated strings adding each line into the list
						;

						mov			rsi, r15
AddLines:				mov			rdx, rsi
						mov			rcx, r13
						lea			rax, AddItem
						call		rax
FindNextEOL:			mov			al, byte ptr [rsi]
						or			al, al
						je			FoundEOL
						cmp			al, 15H
						je			FoundEOF
						inc			rsi
						jmp			FindNextEOL
FoundEOL:				inc			rsi
						jmp			AddLines

						;
						;	Clean up and return to the caller
						;

FoundEOF:				pop			rdi
						pop			rsi
						add			rsp, 128
						mov			rax, r13
						ret
OpenFailed:				xor			rax, rax
						ret

PaintWindow				proc

						;
						;	Code for handling the WM_PAINT message so begin the paint
						;

						push		rbx
						push		rcx
						sub			rsp, 48
						mov			rdx, OFFSET paintStructure
						call		qword ptr [__imp_BeginPaint]
						mov			WindowDC, rax

						;
						;	Get the client rectangle
						;

						and			rax, 0FFFFFFFFH
						mov			rcx, qword ptr [rsp + 48]
						mov			rdx, OFFSET clientRect
						call		qword ptr [__imp_GetClientRect]

						;
						;	Fill the rectangle with the BLACK_BRUSH
						;

						mov			rcx, BLACK_BRUSH
						call		qword ptr [__imp_GetStockObject]
						mov			rcx, WindowDC
						mov			rdx, OFFSET paintStructure.rcPaintLeftTop
						mov			r8, rax
						call		qword ptr [__imp_FillRect]

						;
						;	Set the background color for DrawText as black and pen color as white
						;

						mov			rcx, WindowDC
						xor			rdx, rdx
						call		qword ptr [__imp_SetBkColor]
						mov			rcx, WindowDC
						mov			rdx, 0FFFFFFH
						call		qword ptr [__imp_SetTextColor]
						mov			rcx, WindowDC
						mov			edx, EditorFont
						call		qword ptr [__imp_SelectObject]

						;
						;	Compute the number of lines and set the left margin for the draw
						;

						xor			rax, rax
						xor			rdx, rdx
						mov			eax, 3
						mov			clientRect.left, eax
						mov			eax, clientRect.bottom
						sub			eax, clientRect.top
						mov			ebx, LINE_HEIGHT
						idiv		ebx
						mov			r13, rax
						mov			r12, TopLine

						;
						;	For each line in the list that we can display in the window
						;

ShowNextLine:			mov			rcx, ListStruct
						mov			rdx, r12
						call		GetItem

						;
						;	Draw line line
						;

						mov			rcx, WindowDC
						mov			rdx, rax
						mov			r8, -1
						mov			r9, OFFSET clientRect
						mov			rax, DT_LEFT
						mov			[rsp + 32], rax
						call		qword ptr [__imp_DrawTextA]

						;
						;	Check to see if we have displayed all the lines
						;

						inc			r12
						mov			eax, LINE_HEIGHT
						add			clientRect.top, eax
						mov			rax, r13
						add			rax, TopLine
						cmp			r12, rax
						jne			ShowNextLine

						;
						;	End the paint operation
						;

						add			rsp, 48
						pop			rcx
						pop			rbx
						mov			rdx, OFFSET paintStructure
						call		qword ptr [__imp_EndPaint]
						mov			rax, 1
						ret
PaintWindow				endp

ScrollWindow			proc
						push		rbx
						push		rcx
						sub			rsp, 48
						cmp			r8w, SB_THUMBPOSITION
						jne			@F
						shr			r8d, 16
						mov			TopLine, r8
						mov			r9, 1
						push		rcx
						mov			rdx, SB_VERT
						call		qword ptr [__imp_SetScrollPos]
						pop			rcx
						xor			rdx, rdx
						xor			r9, r9
						call		qword ptr [__imp_InvalidateRect]
@@:						add			rsp, 48
						pop			rcx
						pop			rbx
						mov			rax, 1
						ret
ScrollWindow			endp

MainWindowProc			proc
						cmp			rdx, WM_PAINT
						jne			CheckScrollMove

						;
						;	Process the paint message
						;

						call		PaintWindow
						ret

						;
						;	Process the vertical scroll message
						;

CheckScrollMove:		cmp			rdx, WM_VSCROLL
						jne			@F
						call		ScrollWindow
						ret

@@:						jmp			qword ptr [__imp_DefWindowProcW]

MainWindowProc			endp

;
;	This is the main entry point for the editor
;

Editor					proc
						sub			rsp, 8

						;
						;	Display the open file dialog so the user can select the file
						;

						mov			rcx, OFFSET OpenFileName
						mov			eax, SIZE OPENFILENAMEW
						mov			OPENFILENAMEW.lStructSize[rcx], eax
						mov			rax, OFFSET OpenFileNameFilter
						mov			OPENFILENAMEW.lpstrFilter[rcx], rax
						mov			OPENFILENAMEW.nFilterIndex[rcx], 1
						mov			rax, OFFSET FileName
						mov			OPENFILENAMEW.lpstrFile[rcx], rax
						mov			OPENFILENAMEW.nMaxFile[rcx], SIZE FileName
						mov			OPENFILENAMEW.Flags[rcx], OFN_PATHMUSTEXIST or OFN_FILEMUSTEXIST
						call		qword ptr [__imp_GetOpenFileNameW]
						add			rsp, 8

						;
						;	Fetch the source into memory and create the list
						;

						mov			rcx, OpenFileName.lpstrFile
						call		FetchSource
						mov			ListStruct, rax

						;
						;	Get the module handle (HINSTANCE)
						;

						xor			rcx, rcx
						call		qword ptr [__imp_GetModuleHandleW]
						mov			r14, OFFSET MainWindowClass
						mov			WNDCLASSEX.hInstance[r14], rax

						;
						;	Set the cursor to the standard arrow
						;

						xor			rcx, rcx
						mov			rdx, IDC_ARROW
						call		qword ptr [__imp_LoadCursorW]
						mov			WNDCLASSEX.hCursor[r14], rax

						;
						;	Set the background brush colour
						;

						xor			rcx, BLACK_BRUSH
						call		qword ptr [__imp_GetStockObject]
						mov			WNDCLASSEX.hbrBackground[r14], rax

						;
						;	Set the class name and the window procedure
						;

						lea			rax, MainWindowProc
						mov			WNDCLASSEX.lpfnWndProc[r14], rax
						mov			rax, OFFSET MainWindowClassName
						mov			WNDCLASSEX.lpszClassName[r14], rax

						;
						;	Register the class
						;

						sub			rsp, 8
						mov			rcx, r14
						call		qword ptr [__imp_RegisterClassExW]

						;
						;	Now create the window
						;

						sub			rsp, 96
						mov			rcx, 0
						mov			rdx, OFFSET MainWindowClassName
						mov			r8, OFFSET WindowTitle
						mov			r9, WS_OVERLAPPEDWINDOW
						mov			qword ptr 32[rsp], 100
						mov			qword ptr 40[rsp], 100
						mov			qword ptr 48[rsp], 1600
						mov			qword ptr 56[rsp], 800
						xor			rax, rax
						mov			qword ptr 64[rsp], rax
						mov			qword ptr 72[rsp], rax
						mov			qword ptr 88[rsp], rax
						mov			rax, MainWindowClass.hInstance
						mov			qword ptr 80[rsp], rax
						call		qword ptr [__imp_CreateWindowExW]
						add			rsp, 96
						mov			MainWindow, rax

						;
						;	Set the scroll range
						;

						sub			rsp, 48
						mov			rcx, rax
						mov			rdx, SB_VERT
						xor			r8, r8
						mov			r9, TotalLines
						mov			rax, 1
						mov			[rsp+32], rax
						call		qword ptr [__imp_SetScrollRange]

						;
						;	Set the scroll position
						;

						mov			rcx, MainWindow
						mov			rdx, SB_VERT
						xor			r8, r8
						mov			r9, 1
						call		qword ptr [__imp_SetScrollPos]
						add			rsp, 48

						;
						;	Set as the foreground window
						;

						mov			rcx, MainWindow
						call		qword ptr [__imp_SetForegroundWindow]

						;
						;	Compute height of the font
						;

						mov			rcx, MainWindow
						call		qword ptr [__imp_GetWindowDC]
						mov			WindowDC, rax
						mov         rcx, rax
						mov         edx, 5Ah  
						call        qword ptr [__imp_GetDeviceCaps]
						mov         ecx, 08H
						mov         edx, eax
						mov         r8, 72 
						call        qword ptr [__imp_MulDiv]
						neg			eax
						mov			FontHeight, eax
						mov			rcx, MainWindow
						mov			rdx, WindowDC
						call		qword ptr [__imp_ReleaseDC]

						;
						;	Create the font
						;

						lea         rax, SysFontName
						mov         qword ptr [rsp+68h], rax  
						mov         dword ptr [rsp+60h], 31h  
						mov         dword ptr [rsp+58h], 5  
						mov         dword ptr [rsp+50h], 0  
						mov         dword ptr [rsp+48h], 4  
						mov         dword ptr [rsp+40h], 0  
						mov         dword ptr [rsp+38h], 0  
						mov         dword ptr [rsp+30h], 0  
						mov         dword ptr [rsp+28h], 0  
						mov         dword ptr [rsp+20h], 190h  
						xor         r9d, r9d  
						xor         r8d, r8d  
						xor         edx, edx  
						mov         ecx, dword ptr [FontHeight]  
						call        qword ptr [__imp_CreateFontW]
						mov			EditorFont, eax

						;
						;	Process the windows messages
						;

MessageLoop:			xor         r9d, r9d  
						lea         rcx, message
						xor         r8d, r8d  
						xor         edx, edx  
						mov         rbx, rax  
						call		qword ptr [__imp_GetMessageW]
						test        eax,eax  
						je          @F  
						mov			rcx,  OFFSET Message
						call		qword ptr [__imp_TranslateMessage]
						mov			rcx,  OFFSET Message
						call		qword ptr [__imp_DispatchMessageW]
						jmp			MessageLoop
@@:						add			rsp, 512
						ret
Editor					endp
						end



EDIT: Fixed a small problem with the size of the font

This post has been edited by Martyn.Rae: 17 November 2018 - 10:37 PM


Is This A Good Question/Topic? 0
  • +

Page 1 of 1