News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Custom message box

Started by Vortex, May 25, 2007, 06:41:44 PM

Previous topic - Next topic

Vortex

Based on an article in catch22.net, I coded a message box having a customized OK button.

The article with the original demo coded with C :

http://www.catch22.net/tuts/msgbox.asp


.386
.model  flat, stdcall
option  casemap:none

include \GeneSys\include\windows.inc
include \GeneSys\include\kernel32.inc
include \GeneSys\include\user32.inc

includelib \GeneSys\lib\kernel32.lib
includelib \GeneSys\lib\user32.lib

CustomMsgBox PROTO :DWORD,:DWORD,:DWORD,:DWORD

.data
msg1 db 'This is a customized message box',0
capt db 'Hello!',0
btext db 'Click here',0

.data?
hMboxHook dd ?
pButtonText dd ?

.code

start:

invoke CustomMsgBox,0,ADDR msg1,ADDR capt,ADDR btext
invoke ExitProcess,0

mboxCBTProc PROC nCode:DWORD,wParam:DWORD,lParam:DWORD

cmp nCode,0
jb @f
cmp nCode,HCBT_ACTIVATE
jne @f
invoke GetDlgItem,wParam,IDOK
invoke SetWindowText,eax,pButtonText
xor eax,eax
ret
@@:
invoke CallNextHookEx,hMboxHook,nCode,wParam,lParam
ret

mboxCBTProc ENDP

CustomMsgBox PROC hWnd:DWORD,message:DWORD,caption:DWORD,buttontext:DWORD

mov eax,buttontext
mov pButtonText,eax
invoke GetCurrentThreadId
invoke SetWindowsHookEx,WH_CBT,ADDR mboxCBTProc,NULL,eax
mov hMboxHook,eax
invoke MessageBox,0,message,caption,MB_OK
invoke UnhookWindowsHookEx,hMboxHook
ret

CustomMsgBox ENDP

END start

[attachment deleted by admin]

Vortex

#1
Here is another version of the customized message box displaying a menu.

[attachment deleted by admin]

BlackVortex

Haha, nice stuff, this goes in my "cool stuff to check out" folder


--> SetWindowsHookEx <--
Don't you just love that API ? I use it for keyboard hooks in trainers.


Vortex

Thanks for your kind words. SetWindowsHookEx, it's this API doing the job.

jj2007

Very cute indeed. Do you need the first branch?

   cmp      nCode,0
   jb      GetOut

   cmp      nCode,HCBT_ACTIVATE
   jne      GetOut

jdoe

Quote from: jj2007 on October 03, 2008, 12:25:11 AM
Very cute indeed. Do you need the first branch?

   cmp      nCode,0
   jb      GetOut

   cmp      nCode,HCBT_ACTIVATE
   jne      GetOut



In this case probably needed because of the WindowProc. Without it nCode never gets a negative value.

One problem using this hook with a MessageBox is that it is not thread-safe because hMboxHook could be changed by another call somewhere else (if the function is in a library for example).


jj2007

Quote from: jdoe on October 03, 2008, 01:05:18 AM
Quote from: jj2007 on October 03, 2008, 12:25:11 AM
Very cute indeed. Do you need the first branch?

   cmp      nCode,0
   jb      GetOut

   cmp      nCode,HCBT_ACTIVATE
   jne      GetOut



In this case probably needed because of the WindowProc. Without it nCode never gets a negative value.

I am a bit confused here. First, nCode seems to be unsigned:
mboxCBTProc PROC nCode:DWORD,wParam:DWORD,lParam:DWORD
Since jb is a test for an unsigned value, I don't understand for which values of nCode the jump will be taken.
Second, HCBT_ACTIVATE equals +5, and this is tested in the second half. If nCode is not +5, there will be a jump to GetOut. That holds true for all negative values, too.

jdoe


jj2007,

You're absolutely right... Should be jl instead of jb. CBTProc is "supposed" to return the returned value of CallNextHookEx when nCode is less than zero.

:clap:


Vortex

jj,

Thanks for the heads-up. Bug fixed. Updated GSmbox.zip at the top.

jdoe,

You would probably create a thread-safe dialog box which is more easy to handle. It was interesting to combine hooking with subclassing as regular message boxes are not providing much resources for customizations.

jj2007

Quote from: Vortex on October 03, 2008, 10:24:54 AM
jj,

Thanks for the heads-up. Bug fixed. Updated GSmbox.zip at the top.


Not really a bug, but the first comparison is simply not needed - the jne is enough.

   cmp      nCode,0
   jl      exit
   cmp      nCode,HCBT_ACTIVATE
   jne      exit   ; implies that it will also jump to exit for all negative values... :wink

Vortex

Hi jj,

Your comment is OK. That's fine, thanks.

Another interesting article by MS :

How To Position a MsgBox Using a Windows Hook Procedure

jdoe

Quote from: Vortex on October 03, 2008, 10:24:54 AM
jdoe,

You would probably create a thread-safe dialog box which is more easy to handle. It was interesting to combine hooking with subclassing as regular message boxes are not providing much resources for customizations.

Vortex,

I did made thread-safe dialogs for the one having their own callback procedure like BrowseForFolder, GetOpenFileName or GetSaveFileName, but in the case of a MessageBox, I tried hard but never been able to do it.

In other words, we are on the same boat for this one   :P

Regards

jdoe


jj2007

I have a suggestion and a question:
- Suggestion: reset flag on exit for multiple uses:

.elseif nCode==HCBT_DESTROYWND
   xor eax, eax
   mov flag, eax

EDIT: No good here, as it causes the "internal" About Messagebox to adopt the same design. Put it here:
   invoke UnhookWindowsHookEx, hMboxHook
   m2m flag, 0   ; needed for multiple uses

- Question: Would it be good practice to proceed with CallNextHookEx after handling HCBT_ACTIVATE, instead of returning 0? It works, but I am not sure if there are drawbacks.

mboxCBTProc proc uses esi nCode:DWORD, wParam:DWORD, lParam:DWORD
LOCAL rc:RECT
  .if nCode==HCBT_ACTIVATE ; HCBT_CREATEWND is too early
.if flag==0
lea esi, rc
invoke GetWindowRect, wParam, esi
mov eax, RECT.right[esi] ; same as mov eax, [esi.RECT.right]
sub eax, RECT.left[esi]
mov ecx, RECT.bottom[esi]
sub ecx, RECT.top[esi]
add ecx, 24
invoke MoveWindow, wParam, RECT.left[esi], RECT.top[esi], eax, ecx, TRUE

invoke GetDlgItem, wParam, IDOK
invoke SetWindowText, eax, pButtonText
invoke GetModuleHandle, 0
invoke LoadMenu, eax, 100
invoke SetMenu, wParam, eax

invoke SetWindowLong, wParam, GWL_WNDPROC, addr MsgboxProc
mov pOldProc, eax
mov flag, 1
.endif
; xor eax, eax ; needed, or can we proceed
; ret ; with CallNextHookEx?

  ; .elseif nCode==HCBT_DESTROYWND ********** see comment above
  ; xor eax, eax
  ; mov flag, eax ; needed for multiple uses
; ret ; can we pass on to CallNextHookEx?
  .endif

  invoke CallNextHookEx, hMboxHook, nCode, wParam, lParam
  ret
mboxCBTProc endp

Vortex

m2m flag, 0

That's a nice idea and that was exactly what I added to the SolAsm translation of this demo. About the usage of CallNextHookEx, terminating the procedure with this function instead of xor eax,eax can work as you suggested but care must be taken when dealing with multiple hook setups. From win32.hlp :

QuoteChaining to the next hook procedure (that is, calling the CallNextHookEx function) is optional. An application or library can call the next hook procedure either before or after any processing in its own hook procedure. Although chaining to the next hook is optional, it is highly recommended; otherwise, the other applications that have installed hooks will not receive hook notifications and may behave incorrectly as a result.

Vortex

QuoteI did made thread-safe dialogs for the one having their own callback procedure like BrowseForFolder, GetOpenFileName or GetSaveFileName, but in the case of a MessageBox, I tried hard but never been able to do it.

In other words, we are on the same boat for this one

Jdoe,

What I mean is that you can create a dialog box simulating a message box. It's always easier to customize a dialog box.