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

Here is another trial. This time, the message box is moved to a thread and a loop to get the handle with EnumWindows and GetWindowText runs until the message box in the thread is drawn on the screen. GetWindowsText is inserted here to "wait" for the display of the message box.

.386
.model flat,stdcall
option casemap:none

include MBox.inc
   
.data
capt        db "Hello!",0
msg         db 'Customized message box',0
msg2        db 'Message box test',0
ButtonText  db 'Click here',0
bclass      db 'Button',0
   
.data?
   
pOldProc dd ?
   
.code
   
start:
   
    invoke  CustMsgBox,0,ADDR msg,ADDR capt,ADDR ButtonText
    invoke  ExitProcess,0
   
CustMsgBox  PROC uses esi handle:DWORD,message:DWORD,caption:DWORD,button:DWORD
   
LOCAL rc:RECT
LOCAL ThreadID:DWORD
LOCAL hWnd:DWORD
LOCAL hThread:DWORD
LOCAL buffer[100]
   
    lea     edx,[caption]
    invoke  CreateThread,0,0,ADDR ThreadProc,edx,0,ADDR ThreadID
    mov     hThread,eax
@@:
    invoke  EnumWindows,ADDR EnumWndProc,ADDR ThreadID
    invoke  GetWindowText,hWnd,ADDR buffer,100
    invoke  StrCmpA,ADDR buffer,caption
    test    eax,eax
    jz      @b
   
    invoke  FindWindowEx,hWnd,0,ADDR bclass,0
    invoke  SetWindowText,eax,button
   
    lea     esi,[rc]
    invoke  GetWindowRect,hWnd,esi
    mov     eax,RECT.right[esi]
    sub     eax,RECT.left[esi]
    mov     ecx,RECT.bottom[esi]
    sub     ecx,RECT.top[esi]
    add     ecx,20
    invoke  MoveWindow,hWnd,RECT.left[esi],RECT.top[esi],eax,ecx,TRUE
   
    invoke  GetModuleHandle,0
    invoke  LoadMenu,eax,100
    invoke  SetMenu,hWnd,eax
    invoke  SetWindowLong,hWnd,GWL_WNDPROC,ADDR MsgboxProc
    mov     pOldProc,eax
   
    invoke  WaitForSingleObject,hThread,INFINITE
    invoke  CloseHandle,hThread
    ret   
   
CustMsgBox ENDP
   
ThreadProc PROC pParams:DWORD
   
    mov     eax,pParams
    invoke  MessageBox,DWORD PTR [eax-8],DWORD PTR [eax-4],DWORD PTR [eax],MB_OK
    ret
   
ThreadProc ENDP
   
EnumWndProc PROC hWnd:DWORD,lParam:DWORD
   
    LOCAL pid:DWORD
   
    invoke  GetWindowThreadProcessId,hWnd,ADDR pid
    mov     edx,lParam
    cmp     eax,[edx]
    jne     @f
    push    hWnd
    pop     DWORD PTR [edx-4]  ; get the handle of the window
    xor     eax,eax            ; displaying the message box
    ret
@@:
    mov     eax,TRUE
    ret
   
EnumWndProc ENDP
   
MsgboxProc PROC hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
   
    .IF uMsg == WM_COMMAND
   
        .IF wParam == IDB_EXIT
            invoke  SendMessage,hWnd,WM_CLOSE,0,0
   
        .ELSEIF wParam == IDB_ABOUT
            invoke  MessageBox,0,ADDR msg2,ADDR capt,MB_OK
   
        .ENDIF
   
        invoke    CallWindowProc,pOldProc,hWnd,uMsg,wParam,lParam
        ret
   
    .ELSEIF uMsg == WM_CLOSE
   
    invoke    EndDialog,hWnd,0
   
    .ELSE
   
    invoke    CallWindowProc,pOldProc,hWnd,uMsg,wParam,lParam
    ret
   
    .ENDIF
   
    xor    eax,eax
    ret
   
    MsgboxProc    ENDP

END start



[attachment deleted by admin]

jj2007

Quote from: Vortex on October 03, 2008, 05:42:00 PM
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, ...

Is there any argument against unhooking immediately in the HCBT_ACTIVATE handler? It seems to work fine, see below...

.nolist
include \masm32\include\masm32rt.inc

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

IDB_EXIT equ 1001
IDB_ABOUT equ 1002

.data
msg1 db 'Customized message box with menu', 0
capt db 'Hello!', 0
btext db 'Click here', 0
msg2 db 'Message box test', 0
flagCmb dd 0

.data?
hMboxHook dd ?
pButtonText dd ?
pOldProc dd ?

.code

start: invoke CustomMsgBox, 0, addr msg1, addr capt, addr btext
invoke CustomMsgBox, 0, chr$("Another one"), addr capt, chr$("A button!")
exit

mboxCBTProc proc uses esi nCode:DWORD, wParam:DWORD, lParam:DWORD
LOCAL rc:RECT
.if nCode==HCBT_ACTIVATE && flagCmb==0
lea esi, rc
invoke GetWindowRect, wParam, esi
mov eax, RECT.right[esi]
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
mov flagCmb, 1

invoke SetWindowLong, wParam, GWL_WNDPROC, addr MsgboxProc
mov pOldProc, eax

invoke UnhookWindowsHookEx, hMboxHook ; new: unhook immediately ******************

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

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

push buttontext
pop pButtonText
invoke GetCurrentThreadId
invoke SetWindowsHookEx, WH_CBT, addr mboxCBTProc, NULL, eax
mov hMboxHook, eax
invoke MessageBox, 0, message, caption, MB_OKCANCEL

; invoke UnhookWindowsHookEx, hMboxHook ; moved upstairs

m2m flagCmb, 0 ; needed for multiple uses
ret

CustomMsgBox ENDP

MsgboxProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

.if uMsg==WM_COMMAND
.if wParam==IDB_EXIT
invoke SendMessage, hWnd, WM_CLOSE, 0, 0

.elseif wParam==IDB_ABOUT
invoke MessageBox, 0, addr msg2, addr capt, MB_OK

.endif

.elseif uMsg==WM_CLOSE
invoke EndDialog, hWnd, 0
xor eax, eax
ret
.endif

invoke CallWindowProc, pOldProc, hWnd, uMsg, wParam, lParam
ret

MsgboxProc endp

end start

Vortex

Hi jj2007,

Your example works fine on my system. Thanks for your contribution.

jj2007

Quote from: Vortex on October 06, 2008, 08:24:35 PM
Your example works fine on my system. Thanks for your contribution.

I tried with various flag settings to see what happens if another app displays a normal MessageBox while the hook is active - no effect. Apparently the hook is limited to the thread.

Do we need .elseif uMsg==WM_CLOSE ? It seems to work without this branch, and I also don't see a good reason why the default handler (pOldProc) should not deal with it.

Vortex

There is no need of WM_CLOSE. That's right. I was typing the code as if I would work on a full dialog box procedure. WM_CLOSE can be removed safely.

PBrennick

Vortex,

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

I understand your point here but remember what the purpose of a MessageBox is. It is to quickly and easiily send information to the user. It requires much less programming to get the job done so in most cases it is just best to leave well enough alone.

Everyone,
There was an example on this board that hooked  timer so that the MessageBox would only be displayed for a certain interval. The user could click OK or just let it go away by itself. It was used in conjunction with an Empth The Recyle Bin utility. I don't remember the author but I still have tyhe code if anyone wants it.

JJ,
Your point about ..

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


is a very good point. The second branch will only be tested if the first one is not taken. If the first one is not taken then the second one will not either, so it is redundant.

Paul
The GeneSys Project is available from:
The Repository or My crappy website

Vortex

Hi Paul,

QuoteI understand your point here but remember what the purpose of a MessageBox is. It is to quickly and easiily send information to the user. It requires much less programming to get the job done so in most cases it is just best to leave well enough alone.

True, the MessageBox function requires less coding but the customization options are limited as this function displays a modal dialog box. This means that you have to find a method to get the handle of the message box.

New version with simplified code :

.386
.model flat,stdcall
option casemap:none

include MBox.inc
   
.data
capt        db "Hello!",0
msg         db 'Customized message box',0
msg2        db 'Message box test',0
ButtonText  db 'Click here',0
bclass      db 'Button',0
   
.data?
   
pOldProc dd ?
   
.code
   
start:
   
    invoke  CustMsgBox,0,ADDR msg,ADDR capt,ADDR ButtonText
    invoke  ExitProcess,0
   
CustMsgBox  PROC uses esi handle:DWORD,message:DWORD,caption:DWORD,button:DWORD
   
LOCAL rc:RECT
LOCAL ThreadID:DWORD
LOCAL hWnd:DWORD
LOCAL hThread:DWORD
LOCAL buffer[100]
   
    invoke  CreateThread,0,0,ADDR ThreadProc,ADDR caption,0,ADDR ThreadID
    mov     hThread,eax
@@:
    invoke  EnumWindows,ADDR EnumWndProc,ADDR ThreadID
    invoke  GetWindowText,hWnd,ADDR buffer,100
    invoke  StrCmpA,ADDR buffer,caption
    test    eax,eax
    jz      @b
    invoke  FindWindowEx,hWnd,0,ADDR bclass,0
    invoke  SetWindowText,eax,button
    invoke  GetWindowRect,hWnd,ADDR rc
    mov     ecx,rc.right
    sub     ecx,rc.left
    mov     edx,rc.bottom
    sub     edx,rc.top
    add     edx,20
    invoke  SetWindowPos,hWnd,0,0,0,ecx,edx,SWP_NOACTIVATE or SWP_NOMOVE
    invoke  GetModuleHandle,0
    invoke  LoadMenu,eax,IDM_MENU
    invoke  SetMenu,hWnd,eax
    invoke  SetWindowLong,hWnd,GWL_WNDPROC,ADDR MsgboxProc
    mov     pOldProc,eax
    invoke  WaitForSingleObject,hThread,INFINITE
    invoke  CloseHandle,hThread
    ret   
   
CustMsgBox ENDP
   
ThreadProc PROC pParams:DWORD
   
    mov     eax,pParams
    invoke  MessageBox,DWORD PTR [eax-8],DWORD PTR [eax-4],DWORD PTR [eax],MB_OK
    ret
   
ThreadProc ENDP
   
EnumWndProc PROC hWnd:DWORD,lParam:DWORD
   
    LOCAL pid:DWORD
   
    invoke  GetWindowThreadProcessId,hWnd,ADDR pid
    mov     edx,lParam
    cmp     eax,[edx]
    jne     @f
    push    hWnd
    pop     DWORD PTR [edx-4]  ; get the handle of the window
    xor     eax,eax            ; displaying the message box
    ret
@@:
    mov     eax,TRUE
    ret
   
EnumWndProc ENDP
   
MsgboxProc  PROC hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
   
    .IF uMsg == WM_COMMAND
   
        .IF wParam == IDB_EXIT
            invoke  SendMessage,hWnd,WM_CLOSE,0,0
   
        .ELSEIF wParam == IDB_ABOUT
            invoke  MessageBox,0,ADDR msg2,ADDR capt,MB_OK
   
        .ENDIF
   
    .ENDIF
   
    invoke  CallWindowProc,pOldProc,hWnd,uMsg,wParam,lParam
    ret
   
    MsgboxProc    ENDP

END start




[attachment deleted by admin]

Vortex

The loop to "catch" the message box window is unnecessary now. WaitForInputIdle is a much more elegant solution :


.
.
    invoke  CreateThread,0,0,ADDR ThreadProc,ADDR caption,0,ADDR ThreadID
    mov     hThread,eax
    invoke  GetCurrentProcess
    invoke  WaitForInputIdle,eax,INFINITE
@@:
    invoke  EnumWindows,ADDR EnumWndProc,ADDR ThreadID
    invoke  GetWindowText,hWnd,ADDR buffer,100

;   invoke  StrCmpA,ADDR buffer,caption
;   test    eax,eax
;   jz      @b

    invoke  FindWindowEx,hWnd,0,ADDR bclass,0
    invoke  SetWindowText,eax,button
    invoke  GetWindowRect,hWnd,ADDR rc
.
.