Jump to content

Recommended Posts

Posted

Hi all,

I've been having a play around today and thought someone might find it interesting.

Basically you can define a dialog template, and with that you can create modal windows outside of the ones provided by MsgBox & InputBox.  Theoretically these can also be added to compiled scripts as resources - but that'll be the next thing to sus out!

You'll see "Script Activity!" is continuously written to the console - but this stops with the "About" window open. (DialogBoxIndirectParam doesn't return until EndDialog is called).  
DialogBoxIndirectParam will also return different codes depending on which button you push ;)

Its also worth noting, the dialog coords are in "Dialog Units" - not pixels.  These base values apparently change depending on scaling and the typeface selected. So controls can be a bit squidgy placement wise, but it should help with legibility across a range of different setups. If you believe the internet anyway!

#AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7
;#AutoIt3Wrapper_UseX64=N

#include <WinAPISys.au3>
#include <WinAPISysWin.au3>
#include <GuiConstants.au3>
#include <WindowsConstants.au3>
#include <FontConstants.au3>
#include <GuiMenu.au3>

Global Const $tagDLGTEMPLATEEX_Base = "struct;" & _
            "word dlgVer;" & _
            "word signature;" & _
            "dword helpID;" & _
            "dword exStyle;" & _
            "dword style;" & _
            "word cDlgItems;" & _
            "short x;" & _
            "short y;" & _
            "short cx;" & _
            "short cy;" & _
            "word menu[%d];" & _
            "word windowClass[%d];" & _
            "wchar title[%d];" & _
            "word pointsize;" & _
            "word weight;" & _
            "byte italic;" & _
            "byte charset;" & _
            "wchar typeface[%d];" & _
            "endstruct;" & _
            "align 4;"

Global Const $tagDLGITEMTEMPLATEEX_Base = "struct;" & _
            "dword helpID;" & _
            "dword exStyle;" & _
            "dword style;" & _
            "short x;" & _
            "short y;" & _
            "short cx;" & _
            "short cy;" & _
            "dword id;" & _
            "word windowClass[%d];" & _
            "wchar title[%d];" & _
            "word extraCount;" & _
            "endstruct;"

Global Const $IDM_ABOUT = 104, $IDM_EXIT = 105
Global $hGUI, $hDlgProc, $pDlgProc

Example()

Func Example()
    $hDlgProc = DllCallbackRegister("DlgProc", "int_ptr", "hwnd;uint;wparam;lparam")
    $pDlgProc = DllCallbackGetPtr($hDlgProc)

    $hGUI = GUICreate("Example GUI", 400, 200)
    GUICtrlCreateLabel("Goto: Help > About to launch the dialog.", 10, 60, 380, 20, BitOR($SS_CENTERIMAGE, $SS_CENTER))
    GUIRegisterMsg($WM_COMMAND, "WM_COMMAND")

    Local $hMainMenu = _GUICtrlMenu_CreateMenu()
    _GUICtrlMenu_SetMenu($hGUI, $hMainMenu)

    Local $hFileMenu = _GUICtrlMenu_CreateMenu()
    _GUICtrlMenu_AppendMenu($hFileMenu, $MF_ENABLED, $IDM_EXIT, "E&xit")

    Local $hHelpMenu = _GUICtrlMenu_CreateMenu()
    _GUICtrlMenu_AppendMenu($hHelpMenu, $MF_ENABLED, $IDM_ABOUT, "&About")

    _GUICtrlMenu_InsertMenuItem($hMainMenu, 0, "&File", 0, $hFileMenu)
    _GUICtrlMenu_InsertMenuItem($hMainMenu, 1, "&Help", 0, $hHelpMenu)
    _GUICtrlMenu_DrawMenuBar($hGUI)
    GUISetState()

    Local $hTimer = TimerInit()
    Do
        If TimerDiff($hTimer) > 1000 Then
            ConsoleWrite("Scipt Activity!" & @CRLF)
            $hTimer = TimerInit()
        EndIf
    Until GUIGetMsg() = $GUI_EVENT_CLOSE

    GUIRegisterMsg($WM_COMMAND, "")
    GUIDelete($hGUI)
    DllCallbackFree($hDlgProc)
EndFunc   ;==>Example

Func WM_COMMAND($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam

    Switch _WinAPI_LoWord($wParam)
        Case $IDM_EXIT
            _WinAPI_PostMessage($hWnd, $WM_SYSCOMMAND, $SC_CLOSE, 0)

        Case $IDM_ABOUT
            Local $tDialog = CreateDlg("About", "Hello World!!")
            Local $aCall = DllCall("User32.dll", "int_ptr", "DialogBoxIndirectParamW", "handle", 0, "struct*", $tDialog, "hwnd", $hWnd, "ptr", $pDlgProc, "dword", 0)
            ConsoleWrite("Return: " & $aCall[0] & @CRLF)
            _WinAPI_SetActiveWindow($hWnd)

    EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_COMMAND

Func DlgProc($hWnd, $iMsg, $wParam, $lParam)
    Switch $iMsg
        Case $WM_SYSCOMMAND
            If BitAND(0xFFF0, $wParam) = $SC_CLOSE Then
                DllCall("User32.dll", "bool", "EndDialog", "hwnd", $hWnd, "int_ptr", $IDCANCEL)
                Return 0
            EndIf

        Case $WM_COMMAND
            Switch _WinAPI_LoWord($wParam)
                Case $IDOK, $IDCANCEL
                    DllCall("User32.dll", "bool", "EndDialog", "hwnd", $hWnd, "int_ptr", $wParam)
                    Return 0

            EndSwitch

    EndSwitch

    Return _WinAPI_DefWindowProcW($hWnd, $iMsg, $wParam, $lParam)
EndFunc

Func CreateDlg($sTitle, $sMessage)
    Local $sTypeFace = "MS Shell Dlg"

    Local $tagDLGTEMPLATEEX = StringFormat($tagDLGTEMPLATEEX_Base, _
        1, 1, _
        StringLen($sTitle) + 1, _
        StringLen($sTypeFace) + 1)

    Local $iItemCnt = 3
    Local $asTitles = [$sMessage, "OK", "Cancel"]

    Local $tDlgTemplateItem, $tagDLGTEMPLATEITEMEX
    For $i = 1 To $iItemCnt
        $tagDLGTEMPLATEITEMEX = StringFormat($tagDLGITEMTEMPLATEEX_Base, 2, StringLen($asTitles[$i - 1]) + 1)
        $tDlgTemplateItem = DllStructCreate($tagDLGTEMPLATEITEMEX)
        $tagDLGTEMPLATEEX &= StringFormat("byte item%d[%d];", $i, DllStructGetSize($tDlgTemplateItem))
    Next

    Local $tDlgTemplate = DllStructCreate($tagDLGTEMPLATEEX)

    ;Header
    $tDlgTemplate.dlgVer = 1
    $tDlgTemplate.signature = 0xFFFF
    $tDlgTemplate.helpID = 0
    $tDlgTemplate.exStyle = $WS_EX_DLGMODALFRAME
    $tDlgTemplate.Style = BitOR($WS_POPUP, $WS_CAPTION, $WS_SYSMENU, $DS_MODALFRAME, $DS_SETFONT)
    $tDlgTemplate.cDlgItems = $iItemCnt
    $tDlgTemplate.x = 0
    $tDlgTemplate.y = 0
    $tDlgTemplate.cx = 200
    $tDlgTemplate.cy = 100
    $tDlgTemplate.title = $sTitle
    $tDlgTemplate.pointsize = 8
    $tDlgTemplate.weight = $FW_NORMAL
    $tDlgTemplate.italic = False
    $tDlgTemplate.charset = $DEFAULT_CHARSET
    $tDlgTemplate.typeface = $sTypeFace

    For $i = 1 To $iItemCnt
        $tagDLGTEMPLATEITEMEX = StringFormat($tagDLGITEMTEMPLATEEX_Base, 2, StringLen($asTitles[$i - 1]) + 1)
        $tDlgTemplateItem = DllStructCreate($tagDLGTEMPLATEITEMEX, DllStructGetPtr($tDlgTemplate, "item" & $i))

        Switch $i
            Case 1 ;Label
                $tDlgTemplateItem.windowClass(1) = 0xFFFF ;Label
                $tDlgTemplateItem.windowClass(2) = 0x0082
                $tDlgTemplateItem.title = $asTitles[$i - 1]
                $tDlgTemplateItem.x = 10
                $tDlgTemplateItem.y = 20
                $tDlgTemplateItem.cx = 180
                $tDlgTemplateItem.cy = 20
                $tDlgTemplateItem.Style = BitOR($SS_CENTERIMAGE, $SS_LEFT, $WS_VISIBLE, $WS_CHILD)
                $tDlgTemplateItem.exStyle = $WS_EX_WINDOWEDGE

            Case 2 ;OK Button
                $tDlgTemplateItem.windowClass(1) = 0xFFFF
                $tDlgTemplateItem.windowClass(2) = 0x0080
                $tDlgTemplateItem.id = $IDOK
                $tDlgTemplateItem.title = $asTitles[$i - 1]
                $tDlgTemplateItem.x = 90
                $tDlgTemplateItem.y = 75
                $tDlgTemplateItem.cx = 50
                $tDlgTemplateItem.cy = 20
                $tDlgTemplateItem.Style = BitOR($BS_CENTER, $BS_VCENTER, $WS_TABSTOP, $WS_VISIBLE, $WS_CHILD)
                $tDlgTemplateItem.exStyle = $WS_EX_WINDOWEDGE

            Case 3 ;Cancel Button
                $tDlgTemplateItem.windowClass(1) = 0xFFFF
                $tDlgTemplateItem.windowClass(2) = 0x0080
                $tDlgTemplateItem.id = $IDCANCEL
                $tDlgTemplateItem.title = $asTitles[$i - 1]
                $tDlgTemplateItem.x = 145
                $tDlgTemplateItem.y = 75
                $tDlgTemplateItem.cx = 50
                $tDlgTemplateItem.cy = 20
                $tDlgTemplateItem.Style = BitOR($BS_CENTER, $BS_VCENTER, $WS_TABSTOP, $WS_VISIBLE, $WS_CHILD)
                $tDlgTemplateItem.exStyle = $WS_EX_WINDOWEDGE
        EndSwitch
    Next

    Return $tDlgTemplate
EndFunc
Posted

Actually, embedding the dialog isn't too difficult once we have it defined:

  • Compile ShowDlg.au3
  • Run AddDlgRes.au3 to add the dialog to ShowDlg.exe's resources 
    • (ShowDlg.exe must be in the same folder as  AddDlgRes.au3)
  • Run ShowDlg.exe > Help > About.

DialogRes.zip

Posted

This is really neat, Matty. Thanks for sharing it. I think that this could be incredibly useful to be able to have custom dialogs instead of the typical system MsgBox. This opens up quite a bit of possibilities. I've been messing around with this script for about an hour trying various things and it is tempting me to implement it in some of my older GUI projects now.

I do have a question and it's something that will likely come up at some point. How easy or difficult would it be to implement adding a background color to the dialog box?

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...