Extended Message Box for Win32

Tight_Coder_Ex 0 Tallied Votes 314 Views Share

There are numerous calls to Win32 API that return detailed information as to failure via GetLastError (). I've devised a method by which to get the textual information corresponding to error and the address of the call that caused the violation. If GetLastError () returns null, then it can serve as possibly a simpler way to use MessageBox (). I've only tested this on XP, but it failed miserably on a Sony Lapton running ME.

What you need to define is; style for message box, Caption & Text. If last error was not null, then you'll see

Error < 128> @ [ 40103A ]

Message from system

What you defined as message body text

and then dependant upon MessageBox style buttons for your response

; Message definition in .rdata
Errm01	dd	MB_YESNO | MB_ICONSTOP | MB_DEFBUTTON2 | MB_APPLMODAL
		db	'MESSAGE BOX DEMO', 0
		db	13, 10, 9, 'Do you want to continue', 0

	; Example of how you would call function
		xor	eax, eax		; hWnd, owners window handle
		mov	edx, Errm01		; Message definition
		call	MsgBox

; ============================================================================================ 
  ; 				      *** MSGBOX ***

  ;	ENTRY:	EAX = hWnd
  ;		EDX = Base pointer ot message parameters

  ;	LEAVE	EAX = Users response as defined in MessageBox

; .............................................................................................
  FMTM		EQU	FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM

  MsgBox	push	esi
		push	edi			; Save essential registers
		push	ebx

		enter	0x1c8, 0		; Frame for MessageBox parameters
		mov	ebx, esp		; Let EBX point to base of these params
		add	ebx, byte 24
		mov	esi, edx
		cld				; Assure auto increment of indices

	; Due to the two modes of operation of this procedure, default to simple message
	; box without extended information from GetLastError.

		mov	[ebx], eax		; hWnd, handle of owner window
		lodsd				; Get style
		mov	[ebx+12], eax		; uType, style of message box
		mov	[ebx+8], esi		; lpCaption, title of message box
		push	esi
		call	_lstrlenA@4		; Determine length of title text
		inc	eax			; Bump past strings terminating byte
		add	esi, eax
		mov	[ebx+4], esi		; lpText, text in message box

	; The single determining factor whether procedure operates in extended mode or not
	; is return value from GetLastError.  If it isn't null, then that means we want
	; to show error code, address of offending call, systems text associated with error
	; and message box text from parameter 2 of MessgeBox.

		call	_GetLastError@0		; Get error code if it exists.
		and	eax, eax
		jnz	.Ext_Mode

		; NOTE: I've choosen to use this method because Extended Mode code is
		; greater than 128 bytes which puts it past a near jump.

	; Display message box in either simple or extended mode, get response from
	; operator and let calling procedure determine appropriate action.

  .Done		lea	esp, [ebp - 0x1b0]	; Point to parameters intialized earlier.
		call	_MessageBoxA@16		
		leave

		pop	ebx
		pop	edi			; Restore essential registers
		pop	esi

		ret				; EAX = Return value of MessageBox call.

  ; -------------------------------------------------------------------------------------------
  ; Normally I would put this in .rdata, but sake of this demonstration I'll leave it in
  ; this segment

  .OutFmt	db	9, 'Error < %d > @ [ %X ] ', 13, 10, 13, 10, '%s%s', 0
  ; -------------------------------------------------------------------------------------------

	; To create the extended string contents for message box, establish the 6 params
	; that are required by wsprint being; OutputBuffer, FormatStr, ErrorCode,
	; CallAddress, ErrorCodeStr, MsgBoxText.
	
  .Ext_Mode	mov	edi, esp		; Establish pointer to wsprintf params
		push	eax			; Save error code from GetLastError
		mov	eax, esp
		add	eax, byte 0x3c		; Base of Destination Ascii buffer
		stosd				; 1st Param for wsprintf
		mov	eax, .OutFmt		; Formatting definition
		stosd				; 2nd Param for wsprintf
		pop	eax			; Restore Error code
		stosd				; 3rd Param for wsprintf

	; Procedure assumes that the first occurence of E8 before the call to MSGBOX was
	; the API function that generated this error.

		mov	esi, edi		; Save next pointer to wsprintf param
		mov	edi, [ebp+16]		; Get callers return
		sub	edi, byte 6		; Point to just before call to MSGBOX
		mov	 al, 0xE8		; Searching for call instruction
		xor	ecx, ecx
		dec	ecx			; Kind of large but no matter
		std				; We want to search backward
		repnz	scasb
		inc	edi			; Points to call to API
		cld				; Re-establish direction to default
		mov	eax, edi
		xchg	esi, edi
		stosd				; 4th Param for wsprintf (Offending call).

	; Use FormatMessage to determine text associated with this error from system

		xor	eax, eax
		push	eax			; Arguments = NULL (No insertion points)
		push	eax			; nSize, not required
		push	edi			; We'll use this location temporaritly
		push	dword SUBLANG_DEFAULT << 10 | LANG_NEUTRAL
		push	dword [edi - 8]		; Error code
		push	eax
		push	dword FMTM
		call	_FormatMessageA@28
		and	eax, eax
		jnz	$+4

		int	3			; In the unlikely event FormatMessage fails.

		mov	eax, [edi+12]
		add	edi, byte 4		; Jump over value just save by FormatMessage
		stosd				; Save address to string
		call	_wsprintfA		; Format entire message

		pop	eax			; Point to newly created string
		mov	[edi+4], eax		; and save for MessageBox API
		push	dword [edi-8]		; Recover memory allocated by FormatMessage
		call	_LocalFree@4
		jmp	.Done			; Complete display and operators response.