Page 1 of 1

MASM - Handling WM_PASTE in subclassed control

#1 GunnerInc  Icon User is offline

  • "Hurry up and wait"
  • member icon




Reputation: 856
  • View blog
  • Posts: 2,246
  • Joined: 28-March 11

Post icon  Posted 21 November 2012 - 06:08 PM

In the tutorial Restricting characters typed in edit control, it had a flaw. The flaw was, that if you subclassed the edit control and only allowed numbers to be entered, the user could still paste non numbers into the control.

In this tutorial, we will address that issue by handling the WM_PASTE message. This will intercept the WM_PASTE message, trim any leading and trailing spaces/tabs, check the length of the text to be pasted, and check for a valid IP address, then if all is good, write the modified text back to the clipboard and allow pasting.

This subclass will be a bit different as I chose to use a lookup table to check for valid characters typed.
the modified subclass proc:
SubClassNumbers proc uses esi edi ebx hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM

	mov		eax, uMsg
	cmp     eax, WM_CHAR
	je      _CHAR
	cmp     eax, WM_PASTE
	je      _PASTE
	
PassThrough:
   invoke 	CallWindowProc, lpNumberProc, hWin, uMsg, wParam, lParam 
   ret
	    
_CHAR:
    mov     eax, wParam
    mov     ecx, offset TableNums
    movzx   edx, byte ptr [ecx + eax]
    test    edx, edx
    jz      NotWanted    
    jmp     PassThrough
    
_PASTE:
    invoke  OpenClipboard, NULL
    test    eax, eax
    jz      PassThrough
    
    invoke  GetClipboardData, CF_TEXT
    test    eax, eax
    jz      PasteDone

    mov     esi, eax

    invoke  szTrim, esi
    invoke  szLen, esi
    test    eax, eax
    jnz     CheckIPLen
    
    invoke  CloseClipboard
    jmp     NotWanted
    
CheckIPLen:
    cmp     eax, 15
    jg      BadIP
    
CheckDots:
    invoke  szWcnt, esi, offset szDot
    cmp     eax, 3
    jne     BadIP

CheckMask0:
    invoke  Cmpi, esi, offset szIP0
    test    eax, eax
    jz      BadIP
    
CheckMask255:
    invoke  Cmpi, esi, offset szIP255
    jz      BadIP
    
IsValidIP:    
    invoke  inet_addr, esi
    cmp     eax, INADDR_NONE
    je      BadIP

CopyModified:
    invoke  szLen, esi
    inc     eax
    invoke  GlobalAlloc, GHND, eax
    mov     edi, eax
    invoke  GlobalLock, eax
    mov     ebx, eax
    invoke  szCopy, esi, ebx
    invoke  EmptyClipboard
    invoke  GlobalUnlock, ebx
    invoke  SetClipboardData, CF_TEXT, edi
    
MemDone:
    invoke  GlobalFree, edi
        
PasteDone:    
    invoke  CloseClipboard
    jmp     PassThrough

BadIP:
    invoke  GlobalUnlock, esi
    invoke  CloseClipboard
    invoke	MessageBox, hWin, offset szErrBadIPPasteText, offset szErrBadIPTitle, MB_IConerror
    
NotWanted:
    xor    eax, eax
	ret	

SubClassNumbers endp


This subclass we will only allow numbers and a period to be entered or pasted. A simple IPv4 edit control.
_CHAR:
    mov     eax, wParam
    mov     ecx, offset TableNums
    movzx   edx, byte ptr [ecx + eax]
    test    edx, edx
    jz      NotWanted    
    jmp     PassThrough

wParam will contain the character code of the key pressed, this is the same as the ASCII code for the character, so we will use this as our index into our lookup table.

This is a bit overkill for this, but it is to show a different way to do it. The lookup table is a 256 byte table covering the ASCII and Extended ASCII characters. It contains ones and zeros. Any character that you want to allow, it will be a 1 in the table, and any character you do not want, will be a 0. Here is our lookup table allowing 0 - 9 (ASCII code 48 - 57) and a period (ASCII code 46).
TableNums   BYTE 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
			BYTE 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
			BYTE 0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0
			BYTE 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0
			BYTE 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
			BYTE 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
			BYTE 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
			BYTE 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
			BYTE 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
			BYTE 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
			BYTE 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
			BYTE 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
			BYTE 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
			BYTE 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
			BYTE 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
			BYTE 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

Let's say the letter "a" was pressed, the ASCII code is 97. We then tell the CPU to take the address of our lookup table (in ecx) and add 97 to that address, and move that value into edx while zeroing out unused bits - movzx edx, byte ptr [ecx + eax], next we check to see if the value is 0 or 1 with test edx, edx, this is the same as and except it does not modify any flags. If the value is 0, we return 0 from the callback to prevent the character from being displayed, if it is 1, the key is wanted, so we pass the keypress on to windows to do it's thing with CallWindowProc

Now to handle WM_PASTE. We need to get the contents of the clipboard, trim any leading and trailing spaces and tabs, do some checks and write back to clipboard then allow paste. An IPv4 address can be 15 characters long, so we need to check the length, check to see if it is 0.0.0.0 or 255.255.255.255, then check to see if values are in range. (this is from a program, and the 2 IP masks are not valid for the program.)
    invoke  OpenClipboard, NULL
    test    eax, eax
    jz      PassThrough
    
    invoke  GetClipboardData, CF_TEXT
    test    eax, eax
    jz      PasteDone

    mov     esi, eax

We first open the clipboard and if that failed, we pass the message on silently, you could notify user if wanted. OpenClipboard returns 0 on failure.

Next we attempt to get the text from the clipboard, GetClipboardData returns 0 on failure or a handle to the clipboard object we requested which you seem to be able to treat this as a pointer. We will save this pointer to esi.
    invoke  szTrim, esi
    invoke  szLen, esi
    test    eax, eax
    jnz     CheckIPLen
    
    invoke  CloseClipboard
    jmp     NotWanted

Now we trim any leading and trailing spaces and tabs, then check the length, if only spaces or tabs were on the clipboard, the len will be zero and we don't want that, so we CloseClipboard and return 0. For every successful call to OpenClipboard, you must call CloseClipboard
CheckIPLen:
    cmp     eax, 15
    jg      BadIP
    
CheckDots:
    invoke  szWcnt, esi, offset szDot
    cmp     eax, 3
    jne     BadIP

CheckMask0:
    invoke  Cmpi, esi, offset szIP0
    test    eax, eax
    jz      BadIP
    
CheckMask255:
    invoke  Cmpi, esi, offset szIP255
    jz      BadIP
    
IsValidIP:    
    invoke  inet_addr, esi
    cmp     eax, INADDR_NONE
    je      BadIP

CheckIPLen checks to see if the text is more that 15 characters, and if it is; notify and return 0
CheckDots checks to see if we have 3 periods, if less or more; notify and return 0
CheckMask0 compares the text to 0.0.0.0 and if equal, it is not wanted; notify and return 0
CheckMask255 compares the text to 255.255.255.255 and if equal, it is not wanted; notify and return 0
IsValidIP passes the text to inet_addr and lets it check for validity, if bad notify and return 0.

Now that we have passed all checks, if we modified the text in any way, we need to copy it back to the clipboard and allow the paste to take place.
CopyModified:
    invoke  szLen, esi
    inc     eax
    invoke  GlobalAlloc, GHND, eax
    mov     edi, eax
    invoke  GlobalLock, eax
    mov     ebx, eax
    invoke  szCopy, esi, ebx
    invoke  EmptyClipboard
    invoke  GlobalUnlock, ebx
    invoke  SetClipboardData, CF_TEXT, edi
    
MemDone:
    invoke  GlobalFree, edi
        
PasteDone:    
    invoke  CloseClipboard
    jmp     PassThrough

This is straight forward - Get the length of the modified text, add 1 to the length for the NULL. Allocate memory with GlobalAlloc, lock the memory, copy modified text to the alloc'd memory, empty the clipboard, and put our modified text onto the clipboard. We don't return 0 but, pass WM_PASTE on to windows to allow it to do its thing.

Attached File(s)



Is This A Good Question/Topic? 0
  • +

Page 1 of 1