Results 1 to 40 of 42

Thread: VB6 - Original InputBox function reloaded with full Unicode support & new Edit Styles

Threaded View

  1. #1

    Thread Starter
    PowerPoster VanGoghGaming's Avatar
    Join Date
    Jan 2020
    Location
    Eve Online - Mining, Missions & Market Trading!
    Posts
    2,621

    Thumbs up VB6 - Original InputBox function reloaded with full Unicode support & new Edit Styles

    Every time I've searched the internet on how to make the InputBox function support Unicode, the answers were to make your own InputBox using a form, a couple of buttons and a TextBox that supports Unicode. But this seems like a waste of a perfectly fine InputBox that you already have!

    The original InputBox from VB6 is actually a simple, run-of-the-mill Windows Dialog and as such it is perfectly capable of displaying Unicode characters on its own if it weren't for VB6 messing things up in the background. It turns out the solution is rather short and sweet so I've put together a small "cIB" class to wrap up the InputBox.

    As a bonus I've also included the possibility to use a "Password Char" to mask user input (same as a regular TextBox). Another improvement over the original InputBox is that now you can tell whether the user selected "OK" or "Cancel" without entering any text. Unicode characters can be used everywhere (in the Titlebar, Prompt and Edit box). Here's what it looks like:

    Name:  UnicodeInputBox.png
Views: 3941
Size:  36.5 KB

    UPDATE: The Unicode InputBox class has been enhanced with additional properties:

    • Centered - You can center the InputBox on the current monitor or on its owner window.
    • EnablePasswordChar - You can specify an optional character to mask the password with. If omitted then the standard asterisk will be displayed.
    • InputFontColor - Select a different text and/or background color for the "Input" text
    • OnlyNumericInput - Force the InputBox to accept only Numeric Input.
    • OnlyUppercase - Force the InputBox to display only "UPPERCASE" characters.
    • OnlyLowercase - Force the InputBox to display only "lowercase" characters.
    • MaxLength - Limit the InputBox text to the specified number of characters.
    • PromptFontColor - Select a different text and/or background color for the "Prompt" text
    • RequireInput - The user MUST type something in the InputBox but can still press "Cancel" if desired.

    Name:  UnicodeInputBoxUpdatedNew.png
Views: 1755
Size:  9.3 KB

    UPDATE: In this update I have added the SetFont method which allows the possibility to select a font for the InputBox prompt. You can specify either the Font object of an existing control as a parameter or you can manually specify a font name, size and a combination of bold, italic and underline attributes. Optionally you can preserve the selected font between successive calls of the InputBox:

    Name:  UnicodeInputBoxFont.png
Views: 3400
Size:  15.7 KB

    UPDATE: The InpuBox size is calculated automatically in case your "Prompt" text doesn't fit in the default size. Some new features have been added as well:

    Now you can customize the text and/or background color of the "Prompt" and "Input" text fields (in addition to their font and size attributes). The edit box has been enhanced with a "placeholder text" (also known as "cue banner") that provides visual cues in case the user is required to type something in the InputBox.

    The placeholder text goes away automatically when you click in the edit box or begin to type. Also now you can make the InputBox mandatory by removing the possibility to cancel or close it until the user types something (this option is not recommended though as it makes for an overall poor user experience):

    Name:  UnicodeInputBoxRequireInput.png
Views: 1758
Size:  16.6 KB

    The main "InputBoxW" property is marked as "Default" to keep the syntax as short as possible. The "cIB" class is also "Predeclared" so it can be used "as is" without declaring new instances:

    cIB.cls
    Code:
    Implements ISubclass
    
    Private sInputText As String, sPrompt As String, sTitle As String, wPasswordChar As Integer, m_bOkayClicked As Boolean, m_bEnablePasswordChar As Boolean, m_sCueBanner As String, _
            m_objFontStatic As IFont, m_objFontEdit As IFont, m_lMaxLength As Long, m_bOnlyNumericInput As Boolean, m_bOnlyUppercase As Boolean, m_bOnlyLowercase As Boolean, m_bPreserveFont As Boolean, _
            m_bPreserveStyle As Boolean, m_PromptFontColor, m_InputFontColor, m_PromptBackColor, m_InputBackColor, m_lInputBoxStyle As Long, m_bRequireInput As Boolean, m_bAllowCancel As Boolean, _
            m_bCentered As Boolean, hWndEdit As Long, hWndStatic As Long, rcInputBox As RECT, wpStatic As WINDOWPLACEMENT, wpEdit As WINDOWPLACEMENT, hBrushStatic As Long, hBrushEdit As Long, _
            rcOwner As RECT, lAdjustStaticHeight As Long, lAdjustEditHeight As Long, lScreenHeight As Long, lOwnerWidth As Long, lOwnerHeight As Long
    
    Public Property Get InputBoxW(Optional Prompt As String, Optional Title As String, Optional Default As String, Optional bInputCanceled As Boolean, Optional xPos, Optional yPos) As String
        If InputBoxHook(Me) Then ' Hook the DialogBoxParamA API function used to create the InputBox Dialog, subclass it and initialize the parameters
            sPrompt = Prompt: sTitle = Title: sInputText = Default
            InputBox sPrompt, , , xPos, yPos ' Display the classic VB6 InputBox Dialog
            bInputCanceled = Not m_bOkayClicked ' Return user's choice
            If m_bOkayClicked Then InputBoxW = sInputText
            If Not m_bPreserveFont Then Set m_objFontStatic = Nothing: Set m_objFontEdit = Nothing
            If Not m_bPreserveStyle Then m_lInputBoxStyle = 0
        End If
    End Property
    
    Public Property Let InputBoxW(Optional Prompt As String, Optional Title As String, Optional Default As String, Optional bInputCanceled As Boolean, Optional xPos, Optional yPos, InputBoxW As String)
        ' Property Let doesn't do anything but it's required to mark this property as "Default" for our class.
    End Property
    
    Friend Property Get Centered(Optional hWndOwner As Long) As Boolean
        Centered = m_bCentered
    End Property
    
    Friend Property Let Centered(Optional hWndOwner As Long, bCentered As Boolean)
        m_bCentered = bCentered
        If m_bCentered Then
            With rcOwner
                If hWndOwner Then
                    GetWindowRect hWndOwner, VarPtr(rcOwner)
                    lOwnerWidth = .Right - .Left: lOwnerHeight = .Bottom - .Top
                Else
                    .Left = 0: .Top = 0: .Right = 0: .Bottom = 0: lOwnerWidth = 0: lOwnerHeight = 0
                End If
            End With
        End If
    End Property
    
    Friend Property Get EnablePasswordChar(Optional PasswordChar As String) As Boolean
        PasswordChar = ChrW$(wPasswordChar): EnablePasswordChar = m_bEnablePasswordChar
    End Property
    
    Friend Property Let EnablePasswordChar(Optional PasswordChar As String = "*", bEnablePasswordChar As Boolean)
        m_bEnablePasswordChar = bEnablePasswordChar
        If m_bEnablePasswordChar Then wPasswordChar = AscW(PasswordChar) Else wPasswordChar = 0
    End Property
    
    Friend Property Get InputFontColor(Optional clrBackColor As ColorConstants) As ColorConstants
        clrBackColor = m_InputBackColor: InputFontColor = m_InputFontColor
    End Property
    
    Friend Property Let InputFontColor(Optional clrBackColor As ColorConstants = -1, clrFontColor As ColorConstants)
        If clrBackColor >= 0 Then m_InputBackColor = clrBackColor Else m_InputBackColor = Empty
        m_InputFontColor = clrFontColor
    End Property
    
    Friend Property Get PromptFontColor(Optional clrBackColor As ColorConstants) As ColorConstants
        clrBackColor = m_PromptBackColor: PromptFontColor = m_PromptFontColor
    End Property
    
    Friend Property Let PromptFontColor(Optional clrBackColor As ColorConstants = -1, clrFontColor As ColorConstants)
        If clrBackColor >= 0 Then m_PromptBackColor = clrBackColor Else m_PromptBackColor = Empty
        m_PromptFontColor = clrFontColor
    End Property
    
    Friend Property Get MaxLength() As Long
        MaxLength = m_lMaxLength
    End Property
    
    Friend Property Let MaxLength(lMaxLength As Long)
        m_lMaxLength = lMaxLength
    End Property
    
    Friend Property Get OnlyNumericInput(Optional bPreserveStyle As Boolean) As Boolean
         bPreserveStyle = m_bPreserveStyle: OnlyNumericInput = m_bOnlyNumericInput
    End Property
    
    Friend Property Let OnlyNumericInput(Optional bPreserveStyle As Boolean = True, bOnlyNumericInput As Boolean)
        m_bPreserveStyle = bPreserveStyle: m_bOnlyNumericInput = bOnlyNumericInput
        If m_bOnlyNumericInput Then m_lInputBoxStyle = m_lInputBoxStyle Or ES_NUMBER Else m_lInputBoxStyle = m_lInputBoxStyle And Not ES_NUMBER
    End Property
    
    Friend Property Get OnlyUppercase(Optional bPreserveStyle As Boolean) As Boolean
        bPreserveStyle = m_bPreserveStyle: OnlyUppercase = m_bOnlyUppercase
    End Property
    
    Friend Property Let OnlyUppercase(Optional bPreserveStyle As Boolean = True, bOnlyUppercase As Boolean)
        m_bPreserveStyle = bPreserveStyle: m_bOnlyUppercase = bOnlyUppercase
        If m_bOnlyUppercase Then m_lInputBoxStyle = m_lInputBoxStyle Or ES_UPPERCASE Else m_lInputBoxStyle = m_lInputBoxStyle And Not ES_UPPERCASE
    End Property
    
    Friend Property Get OnlyLowercase(Optional bPreserveStyle As Boolean) As Boolean
        bPreserveStyle = m_bPreserveStyle: OnlyLowercase = m_bOnlyLowercase
    End Property
    
    Friend Property Let OnlyLowercase(Optional bPreserveStyle As Boolean = True, bOnlyLowercase As Boolean)
        m_bPreserveStyle = bPreserveStyle: m_bOnlyLowercase = bOnlyLowercase
        If m_bOnlyLowercase Then m_lInputBoxStyle = m_lInputBoxStyle Or ES_LOWERCASE Else m_lInputBoxStyle = m_lInputBoxStyle And Not ES_LOWERCASE
    End Property
    
    Friend Property Get RequireInput(Optional sCueBanner As String, Optional bAllowCancel As Boolean) As Boolean
        sCueBanner = m_sCueBanner: bAllowCancel = m_bAllowCancel: RequireInput = m_bRequireInput
    End Property
    
    Friend Property Let RequireInput(Optional sCueBanner As String, Optional bAllowCancel As Boolean = True, bRequireInput As Boolean)
        m_bAllowCancel = bAllowCancel: m_bRequireInput = bRequireInput
        If m_bRequireInput Then m_sCueBanner = sCueBanner Else m_sCueBanner = vbNullString
    End Property
    
    Friend Sub SetFont(Optional eSelectFont As SELECT_FONTS = ePromptFont, Optional ByVal objFont As IFont, Optional sFontFace As String, Optional cyFontSize As Currency, Optional CFS_FONTSTYLE As CFS_FONTSTYLES = CFS_FONTSTYLENONE, Optional bPreserveFont As Boolean = True)
    Dim fdFontDesc As FONTDESC, IID_FONT(0 To 1) As Currency
        m_bPreserveFont = bPreserveFont
        If objFont Is Nothing Then
            With fdFontDesc
                .cbSizeofstruct = LenB(fdFontDesc): .lpstrName = StrPtr(sFontFace): .cySize = cyFontSize
                If (CFS_FONTSTYLE And CFE_BOLD) = CFE_BOLD Then .sWeight = FW_BOLD Else .sWeight = FW_REGULAR
                .fItalic = (CFS_FONTSTYLE And CFE_ITALIC) = CFE_ITALIC: .fUnderline = (CFS_FONTSTYLE And CFE_UNDERLINE) = CFE_UNDERLINE
            End With
            IID_FONT(1) = IID_IUnknown1: OleCreateFontIndirect VarPtr(fdFontDesc), VarPtr(IID_FONT(0)), objFont
        End If
        Select Case eSelectFont
            Case ePromptFont: Set m_objFontStatic = objFont
            Case eInputFont: Set m_objFontEdit = objFont
        End Select
    End Sub
    
    Private Sub Class_Initialize()
        wpStatic.Length = LenB(wpStatic): wpEdit.Length = LenB(wpEdit)
    End Sub
    
    Private Sub CenterInputBox(hWnd As Long)
        GetWindowRect hWnd, VarPtr(rcInputBox)
        With rcInputBox
            If lOwnerHeight Then
                SetWindowPos hWnd, 0, rcOwner.Left + (lOwnerWidth - .Right + .Left) \ 2, rcOwner.Top + (lOwnerHeight - .Bottom + .Top) \ 2, 0, 0, SWP_NOSIZE Or SWP_NOOWNERZORDER Or SWP_NOZORDER
            Else
                SetWindowPos hWnd, 0, .Left, (lScreenHeight - .Bottom + .Top) \ 2, 0, 0, SWP_NOSIZE Or SWP_NOOWNERZORDER Or SWP_NOZORDER
            End If
        End With
    End Sub
    
    Private Function GetTextHeight(hWnd As Long, hDC As Long) As Long
    Dim rcText As RECT, lpSize As POINTAPI, sText As String, lOldFont As Long
        Select Case hWnd
            Case hWndStatic
                If Len(sPrompt) Then sText = sPrompt Else sText = Space$(1)
                rcText = wpStatic.rcNormalPosition
                DrawTextW hDC, StrPtr(sText), Len(sText), VarPtr(rcText), DT_CALCRECT Or DT_EDITCONTROL Or DT_WORDBREAK Or DT_NOPREFIX ' Calculate the height of the "Prompt" text
                GetTextHeight = rcText.Bottom - rcText.Top
            Case hWndEdit
                If Len(sInputText) Then sText = sInputText Else sText = Space$(1)
                lOldFont = SelectObject(hDC, SendMessageW(hWnd, WM_GETFONT, 0&, 0&))
                GetTextExtentPoint32W hDC, StrPtr(sText), Len(sText), VarPtr(lpSize) ' Calculate the height of the "Input" text
                GetTextHeight = lpSize.Y: SelectObject hDC, lOldFont
        End Select
    End Function
    
    Private Function ISubclass_WndProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long, ByVal dwRefData As Long, bDiscardMessage As Boolean) As Long
    Dim lTextLen As Long, rcEdit As RECT, hDC As Long
        Select Case uMsg
            Case WM_INITDIALOG
                hDC = GetDC(hWnd): lScreenHeight = GetDeviceCaps(hDC, VERTRES): ReleaseDC hWnd, hDC
                hWndStatic = GetDlgItem(hWnd, ID_STATIC): hWndEdit = GetDlgItem(hWnd, ID_EDIT)
                If m_bRequireInput And Not m_bAllowCancel Then ShowWindow GetDlgItem(hWnd, IDCANCEL), SW_HIDE: SetWindowLongW hWnd, GWL_STYLE, GetWindowLongW(hWnd, GWL_STYLE) And Not WS_SYSMENU
                If m_lInputBoxStyle Then SetWindowLongW hWndEdit, GWL_STYLE, GetWindowLongW(hWndEdit, GWL_STYLE) Or m_lInputBoxStyle
            Case WM_SHOWWINDOW ' The InputBox Dialog is about to be displayed and ready to be modified
                GetWindowRect hWnd, VarPtr(rcInputBox): GetWindowPlacement hWndStatic, VarPtr(wpStatic): GetWindowPlacement hWndEdit, VarPtr(wpEdit)
                If Not (m_objFontStatic Is Nothing) Then
                    lAdjustStaticHeight = wpStatic.rcNormalPosition.Bottom - wpStatic.rcNormalPosition.Top: SendMessageW hWndStatic, WM_SETFONT, m_objFontStatic.hFont, APITRUE
                End If
                If Not (m_objFontEdit Is Nothing) Then
                    GetClientRect hWndEdit, VarPtr(rcEdit): lAdjustEditHeight = rcEdit.Bottom - rcEdit.Top: SendMessageW hWndEdit, WM_SETFONT, m_objFontEdit.hFont, APITRUE
                End If
                If Len(sTitle) Then SetWindowTextW hWnd, StrPtr(sTitle) ' Set the titlebar text
                If Len(sPrompt) Then SendMessageW hWndStatic, WM_SETTEXT, 0&, StrPtr(sPrompt) ' Set the "Prompt" text
                If wPasswordChar Then SendMessageW hWndEdit, EM_SETPASSWORDCHAR, CLng(wPasswordChar), 0& ' Optional password char to mask user input
                If Len(m_sCueBanner) Then SendMessageW hWndEdit, EM_SETCUEBANNER, APITRUE, StrPtr(m_sCueBanner) ' Optional cue banner to explain why input is required
                If m_lMaxLength > 0 Then SendMessageW hWndEdit, EM_SETLIMITTEXT, m_lMaxLength, 0&: sInputText = Left$(sInputText, m_lMaxLength)
                If Len(sInputText) Then SendMessageW hWndEdit, WM_SETTEXT, 0&, StrPtr(sInputText): SendMessageW hWndEdit, EM_SETSEL, 0&, -1& ' Set and select "Default" text
                If m_bCentered Then CenterInputBox hWnd
            Case WM_COMMAND
                Select Case wParam
                    Case IDOK ' User clicked the "OK" button or pressed Enter to close the InputBox Dialog
                        lTextLen = SendMessageW(hWndEdit, WM_GETTEXTLENGTH, 0&, 0&) ' Get the text length
                        sInputText = String$(lTextLen, vbNullChar)
                        lTextLen = GetWindowTextW(hWndEdit, StrPtr(sInputText), lTextLen + 1) ' Read the actual text entered by the user
                        If lTextLen Then
                            m_bOkayClicked = True
                        Else
                            If m_bRequireInput Then
                                m_bOkayClicked = False: bDiscardMessage = True ' If input is required then the user must type something in the InputBox Dialog but can still press Cancel if desired
                            Else
                                m_bOkayClicked = True
                            End If
                        End If
                    Case IDCANCEL
                        m_bOkayClicked = False
                        If m_bRequireInput And Not m_bAllowCancel Then bDiscardMessage = True ' The user cannot cancel the InputBox when this option is active (not recommended, poor user experience)
                End Select
            Case WM_CTLCOLORSTATIC
                If lAdjustStaticHeight > 0 Then
                    lAdjustStaticHeight = GetTextHeight(hWndStatic, wParam) - lAdjustStaticHeight
                    If lAdjustStaticHeight > 0 Then ' We need to enlarge the InputBox window to accommodate a larger size for the "Prompt" text
                        With rcInputBox
                            .Bottom = .Bottom + lAdjustStaticHeight
                            SetWindowPos hWnd, 0, 0, 0, .Right - .Left, .Bottom - .Top, SWP_NOMOVE Or SWP_NOOWNERZORDER Or SWP_NOZORDER
                        End With
                        With wpStatic.rcNormalPosition
                            SetWindowPos hWndStatic, 0, 0, 0, .Right - .Left, .Bottom - .Top + lAdjustStaticHeight, SWP_NOMOVE Or SWP_NOOWNERZORDER Or SWP_NOZORDER
                        End With
                        With wpEdit.rcNormalPosition
                            SetWindowPos hWndEdit, 0, .Left, .Top + lAdjustStaticHeight, 0, 0, SWP_NOSIZE Or SWP_NOOWNERZORDER Or SWP_NOZORDER
                        End With
                        lAdjustStaticHeight = 0
                    End If
                End If
                If Not IsEmpty(m_PromptFontColor) Or Not IsEmpty(m_PromptBackColor) Then
                    SetBkMode wParam, TRANSPARENT: SetTextColor wParam, m_PromptFontColor ' Set the text color of the "Prompt" text
                    If hBrushStatic = 0 Then hBrushStatic = CreateSolidBrush(IIf(IsEmpty(m_PromptBackColor), GetSysColor(COLOR_BTNFACE), m_PromptBackColor)) ' Set the background color of the "Prompt" text
                    bDiscardMessage = True: ISubclass_WndProc = hBrushStatic
                End If
            Case WM_CTLCOLOREDIT
                If lAdjustEditHeight > 0 Then
                    lAdjustEditHeight = GetTextHeight(hWndEdit, wParam) - lAdjustEditHeight
                    If lAdjustEditHeight > 0 Then ' We need to enlarge the "Edit" control to accommodate a larger font size for the "Input" text
                        With rcInputBox
                            SetWindowPos hWnd, 0, 0, 0, .Right - .Left, .Bottom - .Top + lAdjustEditHeight + 3, SWP_NOMOVE Or SWP_NOOWNERZORDER Or SWP_NOZORDER
                        End With
                        With wpEdit.rcNormalPosition
                            SetWindowPos hWndEdit, 0, 0, 0, .Right - .Left, .Bottom - .Top + lAdjustEditHeight + 3, SWP_NOMOVE Or SWP_NOOWNERZORDER Or SWP_NOZORDER
                        End With
                        lAdjustEditHeight = 0
                    End If
                End If
                If Not IsEmpty(m_InputFontColor) Or Not IsEmpty(m_InputBackColor) Then
                    SetBkMode wParam, TRANSPARENT: SetTextColor wParam, m_InputFontColor ' Set the text color of the "Input" text
                    If hBrushEdit = 0 Then hBrushEdit = CreateSolidBrush(IIf(IsEmpty(m_InputBackColor), vbWhite, m_InputBackColor)) ' Set the background color of the "Input" text
                    bDiscardMessage = True: ISubclass_WndProc = hBrushEdit
                End If
            Case WM_SIZE
                If m_bCentered Then CenterInputBox hWnd
            Case WM_DESTROY
                If hBrushStatic Then If DeleteObject(hBrushStatic) Then hBrushStatic = 0
                If hBrushEdit Then If DeleteObject(hBrushEdit) Then hBrushEdit = 0
                If lAdjustStaticHeight Then lAdjustStaticHeight = 0: If lAdjustEditHeight Then lAdjustEditHeight = 0
        End Select
    End Function
    Basically the whole idea is to hook the "DialogBoxParamA" API function used to create the InputBox Dialog and then subclass it:

    mdlInputBox.bas
    Code:
    Private m_lSubclassInputBox As Long, hWndInputBox As Long, lpDialogBoxParamA As Long, lpOriginalDlgProc As Long, baOriginalDialogBoxParamA(0 To 5) As Byte, baTrampoline(0 To 5) As Byte
    
    Public Function InputBoxHook(SubclassInputBox As ISubclass) As Boolean
        m_lSubclassInputBox = ObjPtr(SubclassInputBox): hWndInputBox = 0 ' Save a reference to our cIB object for subclassing and reset the InputBox window handle
        If lpDialogBoxParamA = 0 Then
            lpDialogBoxParamA = GetProcAddress(GetModuleHandleW(StrPtr("user32")), "DialogBoxParamA") ' Get the address of the "DialogBoxParamA" function
            ReadProcessMemory -1&, lpDialogBoxParamA, VarPtr(baOriginalDialogBoxParamA(0)), 6&, 0& ' Save the first 6 bytes (the size of the trampoline) to be restored later
            baTrampoline(0) = &H68 ' push
            PutMem4 baTrampoline(1), AddressOf HookedDialogBoxParamA ' Set up the trampoline jump to our custom "HookedDialogBoxParamA" function
            baTrampoline(5) = &HC3 ' ret
        End If
        InputBoxHook = WriteProcessMemory(-1&, lpDialogBoxParamA, VarPtr(baTrampoline(0)), 6&, 0&) ' Now the InputBox function will call our custom "HookedDialogBoxParamA" function instead
    End Function
    
    Private Function HookedDialogBoxParamA(ByVal hInstance As Long, ByVal lpTemplateName As Long, ByVal hWndParent As Long, ByVal lpDialogFunc As Long, ByVal dwInitParam As Long) As Long
        lpOriginalDlgProc = lpDialogFunc ' Save the address of the original DLGPROC callback function (we need to call this later)
        WriteProcessMemory -1&, lpDialogBoxParamA, VarPtr(baOriginalDialogBoxParamA(0)), 6&, 0& ' Restore the original "DialogBoxParamA" API function
        HookedDialogBoxParamA = DialogBoxParamW(hInstance, lpTemplateName, hWndParent, AddressOf DlgProc, dwInitParam) ' Call "DialogBoxParamW" using our custom "DlgProc" instead
    End Function
    
    Private Function DlgProc(ByVal hWndDlg As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
        If hWndInputBox = 0 Then hWndInputBox = hWndDlg: SubclassWnd hWndInputBox, m_lSubclassInputBox ' This is the hWnd of the InputBox Dialog window so we can finally subclass it!
        DlgProc = CallWindowProcW(lpOriginalDlgProc, hWndDlg, uMsg, wParam, lParam) ' Call the original DLGPROC callback function to process the rest of messages
    End Function
    
    Private Function IsWndSubclassed(hWnd As Long, uIdSubclass As Long, Optional dwRefData As Long) As Boolean
        IsWndSubclassed = GetWindowSubclass(hWnd, AddressOf WndProc, uIdSubclass, dwRefData)
    End Function
    
    Public Function SubclassWnd(hWnd As Long, uIdSubclass As Long, Optional dwRefData As Long, Optional bUpdateRefData As Boolean) As Boolean
    Dim lOldRefData As Long
        If uIdSubclass Then
            If Not IsWndSubclassed(hWnd, uIdSubclass, lOldRefData) Then
                SubclassWnd = SetWindowSubclass(hWnd, AddressOf WndProc, uIdSubclass, dwRefData)
            Else
                If bUpdateRefData Then If lOldRefData <> dwRefData Then SubclassWnd = SetWindowSubclass(hWnd, AddressOf WndProc, uIdSubclass, dwRefData)
            End If
        End If
    End Function
    
    Private Function UnSubclassWnd(hWnd As Long, uIdSubclass As Long) As Boolean
        If IsWndSubclassed(hWnd, uIdSubclass) Then UnSubclassWnd = RemoveWindowSubclass(hWnd, AddressOf WndProc, uIdSubclass)
    End Function
    
    Private Function WndProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long, ByVal Subclass As ISubclass, ByVal dwRefData As Long) As Long
    Dim bDiscardMessage As Boolean
        Select Case uMsg
            Case WM_NCDESTROY ' Remove subclassing as the window is about to be destroyed
                UnSubclassWnd hWnd, ObjPtr(Subclass)
            Case Else
                WndProc = Subclass.WndProc(hWnd, uMsg, wParam, lParam, dwRefData, bDiscardMessage)
        End Select
        If Not bDiscardMessage Then WndProc = DefSubclassProc(hWnd, uMsg, wParam, lParam)
    End Function
    And of course the ubiquitous ISubclass stub (to use with Implements):

    ISubclass.cls
    Code:
    Public Function WndProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long, ByVal dwRefData As Long, bDiscardMessage As Boolean) As Long
    
    End Function
    That's all there is to it. Here's a small demo program with the InputBox showing some Japanese haikus as well as the password char in action:

    UnicodeInputBoxNew.zip (Updated)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width