Author Topic: InputBox  (Read 8210 times)

0 Members and 1 Guest are viewing this topic.

This topic contains a post which is marked as Best Answer. Press here if you would like to see it.

Offline mpgcan

  • Newbie
  • Posts: 26
    • View Profile
InputBox
« on: August 12, 2021, 09:58:40 am »
I am trying to create an InputBox for QB64 similar to that used in VB. I have a working prototype see file (mpg_input_box_1.bas) .  I would like to trap characters  entered into the edit control  before they are displayed for example (enter key, valid characters etc)  To intercept these characters and perform pre-processing it seems I require subclassing!

It all looked relatively easy however soon got stuck in the quick sand of coding. I added subclassing code to the above file, resulting file (mpg_input_box_1b.bas).  This almost works, OK it fails. 
If you comment lines 359,360 and 361 subclassing is disabled and the code works as mpg_input_box_1.bas, with the lines un-commented  subclassing is enabled. The new SubEdit function gets called however with a return value of (SubEdit = 0) the edit control is not drawn.  Comment (SubEdit = 0) and uncomment ( SubEdit = CallWindowProc(PreviousWinProc.......)  fails with program stopped working.

If anyone can point me in the right direction to get subclassing working it would be most appreciated.

WIN.h
Code: [Select]
ptrszint FUNC_WINDOWPROC(ptrszint*_FUNC_WINDOWPROC_OFFSET_HWND,uint32*_FUNC_WINDOWPROC_ULONG_UMSG,uptrszint*_FUNC_WINDOWPROC_UOFFSET_WPARAM,ptrszint*_FUNC_WINDOWPROC_OFFSET_LPARAM);

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
 return FUNC_WINDOWPROC((ptrszint *) (& hwnd), & uMsg, & wParam, (ptrszint *) (& lParam));
}

void * GetWindowProc() {
 return (void *) WindowProc;
}

mpg_input_box_1.bas

Code: [Select]
Option _Explicit

' A Simple Input box for QB64 32 & 64 bit
' mpg_input_box_1.bas
' MPG 12-8-2021
'===================================================================
' Displays an inputbox popup window. Gets a line of text from user.
'
'There are three InputBox function:
'InputBox("prompt, title)  - Accepts any character input
'InputBoxN("prompt, title) - Only numeric characters allowed
'InputBoxP("prompt, title) - Password entry. Display asterisk
'                            for each character entered
'
'prompt - text displayed by the InputBox e.g. "Please enter a number:".
'title  - text displayed in the title bar of the InputBox.
'
'Note:  Cancel button returns a null string ("")
'
'----------------------------------------------------------------------
' Code based on ideas from the following references:
' 1) Windows buttons for 32 and 64 bit IDE
'    https://www.qb64.org/forum/index.php?topic=3217.msg124966#msg124966
' 2) Base64 Encoding/Decoding with Windows, Mac, and Linux
'    https://www.qb64.org/forum/index.php?topic=3214.msg124919#msg124919
' 3) Threading
'    https://www.qb64.org/forum/index.php?topic=3865.msg132124#msg132124
'=======================================================================
'
'---------------------------------------------------------------
'NOTE:
'Create a new file WIN.h with the following content (Remove the comments '):
'---Start of file contents:---

'ptrszint FUNC_WINDOWPROC(ptrszint*_FUNC_WINDOWPROC_OFFSET_HWND,uint32*_FUNC_WINDOWPROC_ULONG_UMSG,uptrszint*_FUNC_WINDOWPROC_UOFFSET_WPARAM,ptrszint*_FUNC_WINDOWPROC_OFFSET_LPARAM);
'LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
' return FUNC_WINDOWPROC((ptrszint *) (& hwnd), & uMsg, & wParam, (ptrszint *) (& lParam));
'}
'void * GetWindowProc() {
' return (void *) WindowProc;
'}

'---End of file contents---
'Input box uses the WIN.h file to get the WindowProc. Note: Place above file in your working folder (The QB64 folder).
'----------------------------------------------------------------

'==Note: InputBox requires this section to be added to your program.=========
' Creates global variable ipb. Its corresponding elements are global.
Dim Shared ipb As input_box_udt ' InputBox user defined type
inputbox_init '                   Define ipb structure and initialize elements
'=============================================================================

'===Your program START ==========
Screen _NewImage(400, 300, 32) 'Set main screen size
Color _RGB(0, 0, 0), _RGB32(205, 238, 205) ' Set colors
Cls
_Title "Main Program" ' Set your main program title


'Examples:

Print "===" + InputBox("Please enter some text:", "InputBox") + "==="
Print "===" + InputBoxN("Please enter port number:", "Port Number") + "==="
Print "===" + InputBoxP("Please enter password:", "Password Required") + "==="

Print "End"
End
System
'===Your program END ============


'######--INPUT BOX FUNCTIONS and SUBS--######################################

Function InputBox$ (prompt As String, title As String)
    Const FALSE = 0
    Const TRUE = Not FALSE
    ipb.number = FALSE '   Disable numeric input
    ipb.password = FALSE ' Disable password input

    ipb.caption_text = title 'Pop-up window title
    ipb.prompt_text = prompt 'Label text user instrutions

    ipb_UserInput ' Run UserInput, data entered saved in buffer ipb.buf
    InputBox = ipb_get_string 'Extract data from ipb.buf return string
End Function

Function InputBoxN$ (prompt As String, title As String)
    Const FALSE = 0
    Const TRUE = Not FALSE

    ipb.number = TRUE '        Enable numeric input
    ipb.password = FALSE '     Disable password input
    ipb.caption_text = title ' Pop-up window title
    ipb.prompt_text = prompt ' Label text user instrutions

    ipb_UserInput ' Run UserInput, data entered saved in buffer ipb.buf
    InputBoxN = ipb_get_string 'Extract data from ipb.buf return string
End Function

Function InputBoxP$ (prompt As String, title As String)
    Const FALSE = 0
    Const TRUE = Not FALSE

    ipb.number = FALSE '       Disable numeric input
    ipb.password = TRUE '      Enable password input
    ipb.caption_text = title ' Pop-up window title
    ipb.prompt_text = prompt ' Label text user instrutions

    ipb_UserInput ' Run UserInput, data entered saved in buffer ipb.buf
    InputBoxP = ipb_get_string 'Extract data from ipb.buf return string
End Function

'-- inputbox_init: Creates a user defined type and sets initial values.
Sub inputbox_init
    Const FALSE = 0
    Const TRUE = Not FALSE

    Type input_box_udt
        buf As String * 64 '  Buffer to store entered characters
        hw As _Offset '       Pointer to inputbox window
        hwLabel As _Offset '  Pointer to label
        hwb0 As _Offset '     Pointer button 0 - OK
        hwb1 As _Offset '     Pointer button 1 - Cancel
        hwe As _Offset '      Pointer to single line edit control
        number As Integer '   Edit control numbers only
        password As Integer ' asterisk displayed

        caption_text As String ' Window title
        prompt_text As String '  Instruction to user
        button0_text As String ' Left button
        button1_text As String ' Right button
        x As Long 'Position of input box
        y As Long 'Position of input box
    End Type

    'Set default values
    ipb.caption_text = "InputBox"
    ipb.prompt_text = "Enter some text:"
    ipb.button0_text = "OK"
    ipb.button1_text = "Cancel"
    ipb.number = FALSE
    ipb.password = FALSE

    'Center InputBox window
    Dim As Long userwidth, userheight
    userwidth = _DesktopWidth: userheight = _DesktopHeight 'get current screen resolution

    ipb.x = (userwidth \ 2 - 358 \ 2) - 3
    ipb.y = (userheight \ 2 - 136 \ 2) - 29
End Sub


Function ipb_get_string$ ()
    Dim As String a

    'Extract string from buf
    a = _Trim$(ipb.buf) '                  Remove spaces
    a = Left$(a, InStr(a, Chr$(0)) - 1) '  Buffer contains a null terminated string. Find position of null,
    ipb.buf = "" '                         extract characters upto this null character. Clear buffer

    ipb_get_string = a 'Return clean string
End Function

'===Main Sub ===========================
Sub ipb_UserInput
    '--Constants
    Const FALSE = 0
    Const TRUE = Not FALSE

    Const IDC_ARROW = &H7F00
    Const COLOR_WINDOW = 5

    Const WS_OVERLAPPED = 0
    Const WS_CAPTION = &H00C00000
    Const WS_SYSMENU = &H00080000
    Const WS_VISIBLE = &H10000000
    Const WS_CHILD = &H40000000
    Const WS_TABSTOP = &H00010000
    Const WS_EX_CLIENTEDGE = &H00000200
    Const BS_PUSHBUTTON = 0
    Const CW_USEDEFAULT = &H80000000
    Const SW_SHOWDEFAULT = &HA

    Const ES_LEFT = 0
    Const ES_NUMBER = &H2000
    Const ES_PASSWORD = &H0020

    Const WS_OVERLAPPEDWINDOW = WS_OVERLAPPED Or WS_CAPTION

    'Select style before control is created.
    'No need to dynamically change. Controls are built every time they are used
    Dim ipb_style As Long
    ipb_style = 0
    If ipb.password Then
        ipb_style = ES_PASSWORD
    End If
    If ipb.number Then
        ipb_style = ES_NUMBER
    End If

    '--Types
    Type POINT
        As Long x
        As Long y
    End Type

    Type MSG
        As _Offset hwnd
        As _Unsigned Long message
        As _Unsigned _Offset wParam 'unsigned pointer sized integer
        As _Offset lParam '          pointer sized integer
        As _Unsigned Long time
        As POINT pt
    End Type

    Type WNDCLASSA
        As _Unsigned Long style
        $If 64BIT Then
        As String * 4 padding
        $End If
        As _Offset lpfnWndProc
        As Long cbClsExtra, cbWndExtra
        As _Offset hInstance, hIcon, hCursor, hbrBackground, lpszMenuName, lpszClassName
    End Type

    '--Libaries

    Declare Library "WIN"
        Function GetWindowProc%& () 'Windows procedure
    End Declare

    Declare Dynamic Library "user32"
        Function SendMessageA%& (ByVal hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
        Function DefWindowProcA%& (ByVal hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
        Sub PostQuitMessage (ByVal nExitCode As Long)
        Function LoadCursorW%& (ByVal hInstance As _Offset, Byval lpCursorName As _Offset)
        Function RegisterClassA~% (ByVal lpWndClass As _Offset)
        Function CreateWindowExA%& (ByVal dwExStyle As Long, Byval lpClassName As _Offset, Byval lpWindowName As _Offset, Byval dwStyle As Long, Byval X As Long, Byval Y As Long, Byval nWidth As Long, Byval nHeight As Long, Byval hWndParent As _Offset, Byval hMenu As _Offset, Byval hInstance As _Offset, Byval lpParam As _Offset)
        Function ShowWindow& (ByVal hWnd As _Offset, Byval nCmdShow As Long)
        Function UpdateWindow& (ByVal hWnd As _Offset)
        Function GetMessageA% (ByVal lpMsg As _Offset, Byval hWnd As _Offset, Byval wMsgFilterMin As _Unsigned Long, Byval wMsgFilterMax As _Unsigned Long)
        Function TranslateMessage& (ByVal lpMsg As _Offset)
        Function DispatchMessageA%& (ByVal lpmsg As _Offset)
        Sub DestroyWindow (ByVal hWnd As _Offset)
        Function PostMessageA%& (ByVal hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
        Function SetFocus& (ByVal hWnd As _Offset)
    End Declare

    Declare Dynamic Library "kernel32"
        Function GetModuleHandleW%& (ByVal lpModuleName%&)
        Function GetLastError~& ()
    End Declare
    '--End Libaries


    '--Variables
    Static registered As Integer 'Variables initialized to 0 (false) hence not registered. Value retained between functtion calls

    Dim hi As _Offset '   Handle to application instance
    Dim wc As WNDCLASSA ' define wc as WNDCLASSEX structure
    Dim msg As MSG

    Dim discardb As Long '   Dummy variable
    Dim discardp As _Offset 'Dummy variable
    Dim t0 As String '       Type of control
    Dim t1 As String '       Title or controls text

    Dim MainClassName As String * 5
    MainClassName = "main" + Chr$(0)

    Dim CrLf As String * 2 '     define as 2 byte STRING
    CrLf = Chr$(13) + Chr$(10) ' carriage return and line feed

    Dim As String className '               Variable className stores name of our window class
    className = "myWindowClass" + Chr$(0) ' Used in wc. which in turn is used to register window class with the system.

    hi = GetModuleHandleW(0) 'Handle to application instance

    '---Step 1: Registering the Window Class
    'Fill out the members of WNDCLASSEX structure (wc) and call RegisterClassA

    wc.style = 0 '                            Class Styles (CS_*), not Window Styles (WS_*) This is usually be set to 0.
    wc.lpfnWndProc = GetWindowProc '          Pointer to the window procedure for this window class. (see WIN.h)
    wc.cbClsExtra = 0 '                       Amount of extra data allocated for this class in memory. Usually 0.
    wc.cbWndExtra = 0 '                       Amount of extra data allocated in memory per window of this type. Usually 0.
    wc.hInstance = hi '                       Handle to application instance .
    wc.hIcon = 0 '                            Large (usually 32x32) icon shown when the user presses Alt+Tab. Set to 0
    wc.hCursor = LoadCursorW(0, IDC_ARROW) '  Cursor that will be displayed over our window.
    wc.hbrBackground = COLOR_WINDOW 'was +1   Background Brush to set the color of our window. '
    wc.lpszMenuName = 0 '                     Name of a menu resource to use for the windows with this class.
    wc.lpszClassName = _Offset(className) '   Name to identify the class with.

    If Not registered Then '   First time in funcion OK to register
        If RegisterClassA(_Offset(wc)) = 0 Then
            Print "RegisterClassA failed:"; GetLastError
            End
        End If
        registered = TRUE ' Class was registered
    End If

    '--Step 2: Creating the Windows
    'After registering the class, create a window with it using CreateWindowExA.

    'Note: A visible un-owned window gets a taskbar button. To hide the inputbox window taskbar button
    'make the inputbox owned by our main applicationusing using  _WindowHandle instead of 0'
    t1 = ipb.caption_text + Chr$(0) 'Window title
    ipb.hw = CreateWindowExA(0, _Offset(className), _Offset(t1), WS_OVERLAPPEDWINDOW, ipb.x, ipb.y, 358, 130, _WindowHandle, 0, hi, 0): If 0 = ipb.hw Then System

    'Controls are just child windows. They have a procedure, a class etc... that is registered by the system.

    'Label
    t0 = "STATIC" + Chr$(0) '        Window control is STATIC predefined class
    t1 = ipb.prompt_text + Chr$(0) ' Label text
    ipb.hwLabel = CreateWindowExA(0, _Offset(t0), _Offset(t1), WS_VISIBLE Or WS_CHILD, 9, 5, 372, 16, ipb.hw, 0, hi, 0): If 0 = ipb.hwLabel Then System


    'OK Button 0
    t0 = "BUTTON" + Chr$(0) '   Window control is BUTTON predefined class
    t1 = ipb.button0_text + Chr$(0)
    ipb.hwb0 = CreateWindowExA(0, _Offset(t0), _Offset(t1), WS_TABSTOP Or WS_VISIBLE Or WS_CHILD Or BS_PUSHBUTTON, 175, 65, 80, 23, ipb.hw, 0, hi, 0): If 0 = ipb.hwb0 Then System

    'Cancel button 1
    t1 = ipb.button1_text + Chr$(0)
    ipb.hwb1 = CreateWindowExA(0, _Offset(t0), _Offset(t1), WS_TABSTOP Or WS_VISIBLE Or WS_CHILD Or BS_PUSHBUTTON, 262, 65, 80, 23, ipb.hw, 0, hi, 0): If 0 = ipb.hwb1 Then System

    'Edit control
    t0 = "EDIT" + Chr$(0)
    '    t1 = "This is a edit control." + Chr$(0)
    t1 = "" + Chr$(0)
    ipb.hwe = CreateWindowExA(WS_EX_CLIENTEDGE, _Offset(t0), _Offset(t1), WS_VISIBLE Or WS_CHILD Or ES_LEFT Or ipb_style, 12, 30, 330, 26, ipb.hw, 0, hi, 0): If 0 = ipb.hwe Then System

    'Display and Update window to ensure it has properly redrawn itself on the screen.
    discardb = SetFocus(ipb.hwe) ' and start typing
    discardb = ShowWindow(ipb.hw, SW_SHOWDEFAULT)
    discardb = UpdateWindow(ipb.hw)

    '-- Step 3: The Message Loop
    While GetMessageA(_Offset(msg), 0, 0, 0) > 0 '   gets a message from your application's message queue.
        discardb = TranslateMessage(_Offset(msg)) '  performs some additional processing on keyboard events
        discardp = DispatchMessageA(_Offset(msg)) '  sends the message out to the window that the message was sent to
    Wend

End Sub
'===End main function ===========================

'-- Step 4: the Window Procedure
Function WindowProc%& (hWnd As _Offset, uMsg As _Unsigned Long, wParam As _Unsigned _Offset, lParam As _Offset)
    Const WM_CREATE = &H0001
    Const WM_CLOSE = &H0010
    Const WM_DESTROY = 2
    Const WM_COMMAND = &H0111
    Const BN_CLICKED = 0
    Const WM_GETTEXT = &H000D
    Const WM_SETFOCUS = &H0007
    Dim discardp As _Offset 'Dummy variable
    Dim discardb As Long '   Dummy variable
    Select Case uMsg

        '   Case WM_CREATE
        '     WindowProc = 0

        Case WM_CLOSE
            DestroyWindow (hWnd) 'Destroy window and child windows
            WindowProc = 0

        Case WM_DESTROY
            PostQuitMessage 0 ' Want to exit the program
            WindowProc = 0

        Case WM_SETFOCUS
            discardb = SetFocus(ipb.hwe) 'Set Edit control focus
            WindowProc = 0

        Case WM_COMMAND
            '==============
            If wParam = BN_CLICKED Then
                Select Case lParam
                    'A button was clicked test each one
                    '---Sandard buttons---
                    Case ipb.hwb0 'OK button
                        'Print "Button 0 pressed OK"
                        'Get input text and copy to buffer (buf)
                        discardp = SendMessageA(ipb.hwe, WM_GETTEXT, Len(ipb.buf), _Offset(ipb.buf))
                        'Print Len(buf)
                        discardp = PostMessageA(hWnd, WM_CLOSE, 0, 0) 'Close
                        WindowProc = 0

                    Case ipb.hwb1 'Cancel button
                        'Print "Button 1 pressed Cancel"
                        ipb.buf = "" 'reset zero-length string ("").
                        discardp = PostMessageA(hWnd, WM_CLOSE, 0, 0) 'Close
                        WindowProc = 0
                        '---End standard buttons---
                End Select
            Else
                'Not our message send back to system for processing
                WindowProc = DefWindowProcA(hWnd, uMsg, wParam, lParam)
            End If
            '=================
            WindowProc = 0

        Case Else
            'Not our message send back to system for processing
            WindowProc = DefWindowProcA(hWnd, uMsg, wParam, lParam)
    End Select
End Function
'######--END INPUT BOX FUNCTIONS and SUBS--##################################

WIN2.h

Code: [Select]
ptrszint FUNC_WINDOWPROC(ptrszint*_FUNC_WINDOWPROC_OFFSET_HWND,uint32*_FUNC_WINDOWPROC_ULONG_UMSG,uptrszint*_FUNC_WINDOWPROC_UOFFSET_WPARAM,ptrszint*_FUNC_WINDOWPROC_OFFSET_LPARAM);

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
 return FUNC_WINDOWPROC((ptrszint *) (& hwnd), & uMsg, & wParam, (ptrszint *) (& lParam));
}

void * GetWindowProc() {
 return (void *) WindowProc;
}


ptrszint FUNC_SUBEDIT(ptrszint*_FUNC_SUBEDIT_OFFSET_HWND,uint32*_FUNC_SUBEDIT_ULONG_UMSG,uptrszint*_FUNC_SUBEDIT_UOFFSET_WPARAM,ptrszint*_FUNC_SUBEDIT_OFFSET_LPARAM);

LRESULT CALLBACK SubEdit(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
 return FUNC_SUBEDIT((ptrszint *) (& hwnd), & uMsg, & wParam, (ptrszint *) (& lParam));
}

void * GetSubEdit() {
 return (void *) SubEdit;
}

mpg_input_box_1b.bas

Code: [Select]
Option _Explicit

' A Simple Input box for QB64 32 & 64 bit
' mpg_input_box_1b.bas
' MPG 12-8-2021
'===================================================================
' Displays an inputbox popup window. Gets a line of text from user.
'
'There are three InputBox function:
'InputBox("prompt, title)  - Accepts any character input
'InputBoxN("prompt, title) - Only numeric characters allowed
'InputBoxP("prompt, title) - Password entry. Display asterisk
'                            for each character entered
'
'prompt - text displayed by the InputBox e.g. "Please enter a number:".
'title  - text displayed in the title bar of the InputBox.
'
'Note:  Cancel button returns a null string ("")
'
'----------------------------------------------------------------------
' Code based on ideas from the following references:
' 1) Windows buttons for 32 and 64 bit IDE
'    https://www.qb64.org/forum/index.php?topic=3217.msg124966#msg124966
' 2) Base64 Encoding/Decoding with Windows, Mac, and Linux
'    https://www.qb64.org/forum/index.php?topic=3214.msg124919#msg124919
' 3) Threading
'    https://www.qb64.org/forum/index.php?topic=3865.msg132124#msg132124
'=======================================================================
'
'---------------------------------------------------------------
'NOTE:
'Create a new file WIN2.h with the following content (Remove the comments '):
'---Start of file contents:---

'ptrszint FUNC_WINDOWPROC(ptrszint*_FUNC_WINDOWPROC_OFFSET_HWND,uint32*_FUNC_WINDOWPROC_ULONG_UMSG,uptrszint*_FUNC_WINDOWPROC_UOFFSET_WPARAM,ptrszint*_FUNC_WINDOWPROC_OFFSET_LPARAM);
'LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
' return FUNC_WINDOWPROC((ptrszint *) (& hwnd), & uMsg, & wParam, (ptrszint *) (& lParam));
'}
'void * GetWindowProc() {
' return (void *) WindowProc;
'}

'ptrszint FUNC_SUBEDIT(ptrszint*_FUNC_SUBEDIT_OFFSET_HWND,uint32*_FUNC_SUBEDIT_ULONG_UMSG,uptrszint*_FUNC_SUBEDIT_UOFFSET_WPARAM,ptrszint*_FUNC_SUBEDIT_OFFSET_LPARAM);
'LRESULT CALLBACK SubEdit(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
' return FUNC_SUBEDIT((ptrszint *) (& hwnd), & uMsg, & wParam, (ptrszint *) (& lParam));
'}
'void * GetSubEdit() {
' return (void *) SubEdit;
'}

'---End of file contents---
'Input box uses the WIN2.h file to get the WindowProc and SubEdit . Note: Place above file in your working folder (The QB64 folder).
'----------------------------------------------------------------

'==Note: InputBox requires this section to be added to your program.=========
' Creates global variable ipb. Its corresponding elements are global.
Dim Shared ipb As input_box_udt ' InputBox user defined type
inputbox_init '                   Define ipb structure and initialize elements
'=============================================================================

'===Subclassing
Dim Shared PreviousWinProc As Long
'===End Subclassing

'===Your program START ==========

Screen _NewImage(400, 300, 32) 'Set main screen size
Color _RGB(0, 0, 0), _RGB32(205, 238, 205) ' Set colors
Cls
_Title "Main Program" ' Set your main program title


'Examples:

Print "===" + InputBox("Please enter some text:", "InputBox") + "==="
Print "===" + InputBoxN("Please enter port number:", "Port Number") + "==="
Print "===" + InputBoxP("Please enter password:", "Password Required") + "==="

Print "End"
End
System
'===Your program END ============


'######--INPUT BOX FUNCTIONS and SUBS--######################################

Function InputBox$ (prompt As String, title As String)
    Const FALSE = 0
    Const TRUE = Not FALSE
    ipb.number = FALSE '   Disable numeric input
    ipb.password = FALSE ' Disable password input

    ipb.caption_text = title 'Pop-up window title
    ipb.prompt_text = prompt 'Label text user instrutions

    ipb_UserInput ' Run UserInput, data entered saved in buffer ipb.buf
    InputBox = ipb_get_string 'Extract data from ipb.buf return string
End Function

Function InputBoxN$ (prompt As String, title As String)
    Const FALSE = 0
    Const TRUE = Not FALSE

    ipb.number = TRUE '        Enable numeric input
    ipb.password = FALSE '     Disable password input
    ipb.caption_text = title ' Pop-up window title
    ipb.prompt_text = prompt ' Label text user instrutions

    ipb_UserInput ' Run UserInput, data entered saved in buffer ipb.buf
    InputBoxN = ipb_get_string 'Extract data from ipb.buf return string
End Function

Function InputBoxP$ (prompt As String, title As String)
    Const FALSE = 0
    Const TRUE = Not FALSE

    ipb.number = FALSE '       Disable numeric input
    ipb.password = TRUE '      Enable password input
    ipb.caption_text = title ' Pop-up window title
    ipb.prompt_text = prompt ' Label text user instrutions

    ipb_UserInput ' Run UserInput, data entered saved in buffer ipb.buf
    InputBoxP = ipb_get_string 'Extract data from ipb.buf return string
End Function

'-- inputbox_init: Creates a user defined type and sets initial values.
Sub inputbox_init
    Const FALSE = 0
    Const TRUE = Not FALSE

    Type input_box_udt
        buf As String * 64 '  Buffer to store entered characters
        hw As _Offset '       Pointer to inputbox window
        hwLabel As _Offset '  Pointer to label
        hwb0 As _Offset '     Pointer button 0 - OK
        hwb1 As _Offset '     Pointer button 1 - Cancel
        hwe As _Offset '      Pointer to single line edit control
        number As Integer '   Edit control numbers only
        password As Integer ' asterisk displayed

        caption_text As String ' Window title
        prompt_text As String '  Instruction to user
        button0_text As String ' Left button
        button1_text As String ' Right button
        x As Long 'Position of input box
        y As Long 'Position of input box
    End Type

    'Set default values
    ipb.caption_text = "InputBox"
    ipb.prompt_text = "Enter some text:"
    ipb.button0_text = "OK"
    ipb.button1_text = "Cancel"
    ipb.number = FALSE
    ipb.password = FALSE

    'Center InputBox window
    Dim As Long userwidth, userheight
    userwidth = _DesktopWidth: userheight = _DesktopHeight 'get current screen resolution

    ipb.x = (userwidth \ 2 - 358 \ 2) - 3
    ipb.y = (userheight \ 2 - 136 \ 2) - 29
End Sub


Function ipb_get_string$ ()
    Dim As String a

    'Extract string from buf
    a = _Trim$(ipb.buf) '                  Remove spaces
    a = Left$(a, InStr(a, Chr$(0)) - 1) '  Buffer contains a null terminated string. Find position of null,
    ipb.buf = "" '                         extract characters upto this null character. Clear buffer

    ipb_get_string = a 'Return clean string
End Function

'===Main Sub ===========================
Sub ipb_UserInput
    '--Constants
    Const FALSE = 0
    Const TRUE = Not FALSE

    Const IDC_ARROW = &H7F00
    Const COLOR_WINDOW = 5

    Const WS_OVERLAPPED = 0
    Const WS_CAPTION = &H00C00000
    Const WS_SYSMENU = &H00080000
    Const WS_VISIBLE = &H10000000
    Const WS_CHILD = &H40000000
    Const WS_TABSTOP = &H00010000
    Const WS_EX_CLIENTEDGE = &H00000200
    Const BS_PUSHBUTTON = 0
    Const CW_USEDEFAULT = &H80000000
    Const SW_SHOWDEFAULT = &HA

    Const ES_LEFT = 0
    Const ES_NUMBER = &H2000
    Const ES_PASSWORD = &H0020

    Const WS_OVERLAPPEDWINDOW = WS_OVERLAPPED Or WS_CAPTION

    'Select style before control is created.
    'No need to dynamically change. Controls are built every time they are used
    Dim ipb_style As Long
    ipb_style = 0
    If ipb.password Then
        ipb_style = ES_PASSWORD
    End If
    If ipb.number Then
        ipb_style = ES_NUMBER
    End If

    '--Types
    Type POINT
        As Long x
        As Long y
    End Type

    Type MSG
        As _Offset hwnd
        As _Unsigned Long message
        As _Unsigned _Offset wParam 'unsigned pointer sized integer
        As _Offset lParam '          pointer sized integer
        As _Unsigned Long time
        As POINT pt
    End Type

    Type WNDCLASSA
        As _Unsigned Long style
        $If 64BIT Then
        As String * 4 padding
        $End If
        As _Offset lpfnWndProc
        As Long cbClsExtra, cbWndExtra
        As _Offset hInstance, hIcon, hCursor, hbrBackground, lpszMenuName, lpszClassName
    End Type

    '--Libaries

    '===Subclassing
    Declare Library "WIN2"
        Function GetWindowProc%& () 'Windows procedure
        Function GetSubEdit%& () 'Edit procedure
    End Declare

    Declare CustomType Library
        Function SetWindowLongPtr& Alias "SetWindowLongPtrA" (ByVal hwnd As _Offset, Byval nIndex As Long, Byval dwNewLong As Long)
        Function GetWindowLongPtr& (ByVal hwnd As _Offset, Byval nIndex As Long)
        Function CallWindowProc& Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, Byval hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
    End Declare
    '===END Subclassing


    Declare Dynamic Library "user32"
        Function SendMessageA%& (ByVal hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
        Function DefWindowProcA%& (ByVal hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
        Sub PostQuitMessage (ByVal nExitCode As Long)
        Function LoadCursorW%& (ByVal hInstance As _Offset, Byval lpCursorName As _Offset)
        Function RegisterClassA~% (ByVal lpWndClass As _Offset)
        Function CreateWindowExA%& (ByVal dwExStyle As Long, Byval lpClassName As _Offset, Byval lpWindowName As _Offset, Byval dwStyle As Long, Byval X As Long, Byval Y As Long, Byval nWidth As Long, Byval nHeight As Long, Byval hWndParent As _Offset, Byval hMenu As _Offset, Byval hInstance As _Offset, Byval lpParam As _Offset)
        Function ShowWindow& (ByVal hWnd As _Offset, Byval nCmdShow As Long)
        Function UpdateWindow& (ByVal hWnd As _Offset)
        Function GetMessageA% (ByVal lpMsg As _Offset, Byval hWnd As _Offset, Byval wMsgFilterMin As _Unsigned Long, Byval wMsgFilterMax As _Unsigned Long)
        Function TranslateMessage& (ByVal lpMsg As _Offset)
        Function DispatchMessageA%& (ByVal lpmsg As _Offset)
        Sub DestroyWindow (ByVal hWnd As _Offset)
        Function PostMessageA%& (ByVal hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
        Function SetFocus& (ByVal hWnd As _Offset)
        Function GetWindowLongA& (ByVal hwnd As _Offset, Byval nIndex As Long)
        Function SetWindowLongA& (ByVal hwnd As _Offset, Byval nIndex As Long, Byval dwNewLong As Long)
    End Declare

    Declare Dynamic Library "kernel32"
        Function GetModuleHandleW%& (ByVal lpModuleName%&)
        Function GetLastError~& ()
    End Declare
    '--End Libaries


    '--Variables
    Static registered As Integer 'Variables initialized to 0 (false) hence not registered. Value retained between functtion calls

    Dim hi As _Offset '   Handle to application instance
    Dim wc As WNDCLASSA ' define wc as WNDCLASSEX structure
    Dim msg As MSG

    Dim discardb As Long '   Dummy variable
    Dim discardp As _Offset 'Dummy variable
    Dim t0 As String '       Type of control
    Dim t1 As String '       Title or controls text

    Dim MainClassName As String * 5
    MainClassName = "main" + Chr$(0)

    Dim CrLf As String * 2 '     define as 2 byte STRING
    CrLf = Chr$(13) + Chr$(10) ' carriage return and line feed

    Dim As String className '               Variable className stores name of our window class
    className = "myWindowClass" + Chr$(0) ' Used in wc. which in turn is used to register window class with the system.

    hi = GetModuleHandleW(0) 'Handle to application instance

    '---Step 1: Registering the Window Class
    'Fill out the members of WNDCLASSEX structure (wc) and call RegisterClassA

    wc.style = 0 '                            Class Styles (CS_*), not Window Styles (WS_*) This is usually be set to 0.
    wc.lpfnWndProc = GetWindowProc '          Pointer to the window procedure for this window class. (see WIN.h)
    wc.cbClsExtra = 0 '                       Amount of extra data allocated for this class in memory. Usually 0.
    wc.cbWndExtra = 0 '                       Amount of extra data allocated in memory per window of this type. Usually 0.
    wc.hInstance = hi '                       Handle to application instance .
    wc.hIcon = 0 '                            Large (usually 32x32) icon shown when the user presses Alt+Tab. Set to 0
    wc.hCursor = LoadCursorW(0, IDC_ARROW) '  Cursor that will be displayed over our window.
    wc.hbrBackground = COLOR_WINDOW 'was +1   Background Brush to set the color of our window. '
    wc.lpszMenuName = 0 '                     Name of a menu resource to use for the windows with this class.
    wc.lpszClassName = _Offset(className) '   Name to identify the class with.

    If Not registered Then '   First time in funcion OK to register
        If RegisterClassA(_Offset(wc)) = 0 Then
            Print "RegisterClassA failed:"; GetLastError
            End
        End If
        registered = TRUE ' Class was registered
    End If

    '--Step 2: Creating the Windows
    'After registering the class, create a window with it using CreateWindowExA.

    'Note: A visible un-owned window gets a taskbar button. To hide the inputbox window taskbar button
    'make the inputbox owned by our main applicationusing using  _WindowHandle instead of 0'
    t1 = ipb.caption_text + Chr$(0) 'Window title
    ipb.hw = CreateWindowExA(0, _Offset(className), _Offset(t1), WS_OVERLAPPEDWINDOW, ipb.x, ipb.y, 358, 130, _WindowHandle, 0, hi, 0): If 0 = ipb.hw Then System

    'Controls are just child windows. They have a procedure, a class etc... that is registered by the system.

    'Label
    t0 = "STATIC" + Chr$(0) '        Window control is STATIC predefined class
    t1 = ipb.prompt_text + Chr$(0) ' Label text
    ipb.hwLabel = CreateWindowExA(0, _Offset(t0), _Offset(t1), WS_VISIBLE Or WS_CHILD, 9, 5, 372, 16, ipb.hw, 0, hi, 0): If 0 = ipb.hwLabel Then System


    'OK Button 0
    t0 = "BUTTON" + Chr$(0) '   Window control is BUTTON predefined class
    t1 = ipb.button0_text + Chr$(0)
    ipb.hwb0 = CreateWindowExA(0, _Offset(t0), _Offset(t1), WS_TABSTOP Or WS_VISIBLE Or WS_CHILD Or BS_PUSHBUTTON, 175, 65, 80, 23, ipb.hw, 0, hi, 0): If 0 = ipb.hwb0 Then System

    'Cancel button 1
    t1 = ipb.button1_text + Chr$(0)
    ipb.hwb1 = CreateWindowExA(0, _Offset(t0), _Offset(t1), WS_TABSTOP Or WS_VISIBLE Or WS_CHILD Or BS_PUSHBUTTON, 262, 65, 80, 23, ipb.hw, 0, hi, 0): If 0 = ipb.hwb1 Then System

    'Edit control
    t0 = "EDIT" + Chr$(0)
    '    t1 = "This is a edit control." + Chr$(0)
    t1 = "" + Chr$(0)
    ipb.hwe = CreateWindowExA(WS_EX_CLIENTEDGE, _Offset(t0), _Offset(t1), WS_VISIBLE Or WS_CHILD Or ES_LEFT Or ipb_style, 12, 30, 330, 26, ipb.hw, 0, hi, 0): If 0 = ipb.hwe Then System

    '====Subclassing
    Const GWLP_WNDPROC = -4 ' Sets a new address for the window procedure
    PreviousWinProc = GetWindowLongPtr(ipb.hwe, GWLP_WNDPROC) '                     get the default procedure
    discardb = SetWindowLongPtr(ipb.hwe, GWLP_WNDPROC, ConvertOffset(GetSubEdit)) ' set your custom procedure
    discardb = SetFocus(ipb.hwe) ' and start typing
    '===End Subclassing


    'Display and Update window to ensure it has properly redrawn itself on the screen.
    discardb = SetFocus(ipb.hwe) ' and start typing
    discardb = ShowWindow(ipb.hw, SW_SHOWDEFAULT)
    discardb = UpdateWindow(ipb.hw)

    '-- Step 3: The Message Loop
    While GetMessageA(_Offset(msg), 0, 0, 0) > 0 '   gets a message from your application's message queue.
        discardb = TranslateMessage(_Offset(msg)) '  performs some additional processing on keyboard events
        discardp = DispatchMessageA(_Offset(msg)) '  sends the message out to the window that the message was sent to
    Wend

End Sub
'===End main function ===========================


'-- Step 4: the Window Procedure
Function WindowProc%& (hWnd As _Offset, uMsg As _Unsigned Long, wParam As _Unsigned _Offset, lParam As _Offset)
    Const WM_CREATE = &H0001
    Const WM_CLOSE = &H0010
    Const WM_DESTROY = 2
    Const WM_COMMAND = &H0111
    Const BN_CLICKED = 0
    Const WM_GETTEXT = &H000D
    Const WM_SETFOCUS = &H0007
    Dim discardp As _Offset 'Dummy variable
    Dim discardb As Long '   Dummy variable
    Select Case uMsg

        '   Case WM_CREATE
        '     WindowProc = 0

        Case WM_CLOSE
            DestroyWindow (hWnd) 'Destroy window and child windows
            WindowProc = 0

        Case WM_DESTROY
            PostQuitMessage 0 ' Want to exit the program
            WindowProc = 0

        Case WM_SETFOCUS
            discardb = SetFocus(ipb.hwe) 'Set Edit control focus
            WindowProc = 0

        Case WM_COMMAND
            '==============
            If wParam = BN_CLICKED Then
                Select Case lParam
                    'A button was clicked test each one
                    '---Sandard buttons---
                    Case ipb.hwb0 'OK button
                        'Print "Button 0 pressed OK"
                        'Get input text and copy to buffer (buf)
                        discardp = SendMessageA(ipb.hwe, WM_GETTEXT, Len(ipb.buf), _Offset(ipb.buf))
                        'Print Len(buf)
                        discardp = PostMessageA(hWnd, WM_CLOSE, 0, 0) 'Close
                        WindowProc = 0

                    Case ipb.hwb1 'Cancel button
                        'Print "Button 1 pressed Cancel"
                        ipb.buf = "" 'reset zero-length string ("").
                        discardp = PostMessageA(hWnd, WM_CLOSE, 0, 0) 'Close
                        WindowProc = 0
                        '---End standard buttons---
                End Select
            Else
                'Not our message send back to system for processing
                WindowProc = DefWindowProcA(hWnd, uMsg, wParam, lParam)
            End If
            '=================
            WindowProc = 0

        Case Else
            'Not our message send back to system for processing
            WindowProc = DefWindowProcA(hWnd, uMsg, wParam, lParam)
    End Select
End Function
'######--END INPUT BOX FUNCTIONS and SUBS--##################################

Function ConvertOffset&& (value As _Offset)
    Dim temp As Long
    $Checking:Off
    Dim m As _MEM 'Define a memblock
    m = _Mem(value) 'Point it to use value
    $If 64BIT Then
        'On 64 bit OSes, an OFFSET is 8 bytes in size.  We can put it directly into an Integer64
        _MEMGET m, m.OFFSET, ConvertOffset&& 'Get the contents of the memblock and put the values there directly into ConvertOffset&&
    $Else
        'However, on 32 bit OSes, an OFFSET is only 4 bytes.  We need to put it into a LONG variable first
        _MemGet m, m.OFFSET, temp 'Like this
        ConvertOffset&& = temp 'And then assign that long value to ConvertOffset&&
    $End If
    _MemFree m 'Free the memblock
    $Checking:On
End Function

Function SubEdit%& (hWnd As _Offset, uMsg As _Unsigned Long, wParam As _Unsigned _Offset, lParam As _Offset)
    Const WM_CHAR = 258
    Print "xxxxxx"
    Select Case uMsg
        Case WM_CHAR '           only interested in the WM_CHAR message
            If wParam > 57 Then
                Print "Test >57"
                '   SubEdit = 0 '    Return 0 character processed
            Else
                Print "Not >57"
            End If
    End Select
    ' No matter what happened, use the old window procedure to finish processing the message.
    'SubEdit = CallWindowProc(PreviousWinProc, hWnd, uMsg, wParam, lParam) ' perform default processing
    SubEdit = 0
End Function






Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: InputBox
« Reply #1 on: August 12, 2021, 11:29:13 am »
Here's one with about 170 lines of code (with the demo code) no library or Window's calls:
https://www.qb64.org/forum/index.php?topic=1511.msg107684#msg107684
You can drag it around your QB64 app window in case it is overlapping a section you need to see.

Fellippe and Ashish have nice ones too.

Offline mpgcan

  • Newbie
  • Posts: 26
    • View Profile
Re: InputBox
« Reply #2 on: August 13, 2021, 05:17:24 am »
Thanks for your reply b+,  alternatives you pointed are excellent however do not address my primary question, how to get subclassing working. 

Offline euklides

  • Forum Regular
  • Posts: 128
    • View Profile
Re: InputBox
« Reply #3 on: August 13, 2021, 05:52:15 am »
What do you exactly mean with " subclassing" ?
Why not yes ?

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: InputBox
« Reply #4 on: August 13, 2021, 08:14:47 am »
Thanks for your reply b+,  alternatives you pointed are excellent however do not address my primary question, how to get subclassing working.

I think you would need to start with an OOP PL.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile

Offline mpgcan

  • Newbie
  • Posts: 26
    • View Profile
Re: InputBox
« Reply #6 on: September 09, 2021, 05:38:13 am »
I decided to revisit my attempt at subclassing a control.
Note: Subclassing Controls background:  https://docs.microsoft.com/en-us/windows/win32/controls/subclassing-overview

I have managed to get subclassing to compile/run on QQ64 32bit . Code see file mpg_input_box_1c.bas uses  the function "SetWindowLongA" :

mpg_input_box_1c.bas
Code: QB64: [Select]
  1.  
  2. ' A Simple Input box for QB64 32 & 64 bit
  3. ' mpg_input_box_1c.bas
  4. ' MPG 9-9-2021
  5. '===================================================================
  6. ' Displays an inputbox popup window. Gets a line of text from user.
  7. '
  8. 'There are three InputBox function:
  9. 'InputBox("prompt, title)  - Accepts any character input
  10. 'InputBoxN("prompt, title) - Only numeric characters allowed
  11. 'InputBoxP("prompt, title) - Password entry. Display asterisk
  12. '                            for each character entered
  13. '
  14. 'prompt - text displayed by the InputBox e.g. "Please enter a number:".
  15. 'title  - text displayed in the title bar of the InputBox.
  16. '
  17. 'Note:  Cancel button returns a null string ("")
  18. '
  19. '----------------------------------------------------------------------
  20. ' Code based on ideas from the following references:
  21. ' 1) Windows buttons for 32 and 64 bit IDE
  22. '    https://www.qb64.org/forum/index.php?topic=3217.msg124966#msg124966
  23. ' 2) Base64 Encoding/Decoding with Windows, Mac, and Linux
  24. '    https://www.qb64.org/forum/index.php?topic=3214.msg124919#msg124919
  25. ' 3) Threading
  26. '    https://www.qb64.org/forum/index.php?topic=3865.msg132124#msg132124
  27. '=======================================================================
  28. '
  29. '---------------------------------------------------------------
  30. 'NOTE:
  31. 'Create a new file WIN2.h with the following content (Remove the comments '):
  32. '---Start of file contents:---
  33.  
  34. 'ptrszint FUNC_WINDOWPROC(ptrszint*_FUNC_WINDOWPROC_OFFSET_HWND,uint32*_FUNC_WINDOWPROC_ULONG_UMSG,uptrszint*_FUNC_WINDOWPROC_UOFFSET_WPARAM,ptrszint*_FUNC_WINDOWPROC_OFFSET_LPARAM);
  35. 'LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  36. ' return FUNC_WINDOWPROC((ptrszint *) (& hwnd), & uMsg, & wParam, (ptrszint *) (& lParam));
  37. '}
  38. 'void * GetWindowProc() {
  39. ' return (void *) WindowProc;
  40. '}
  41.  
  42. 'ptrszint FUNC_SUBEDIT(ptrszint*_FUNC_SUBEDIT_OFFSET_HWND,uint32*_FUNC_SUBEDIT_ULONG_UMSG,uptrszint*_FUNC_SUBEDIT_UOFFSET_WPARAM,ptrszint*_FUNC_SUBEDIT_OFFSET_LPARAM);
  43. 'LRESULT CALLBACK SubEdit(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  44. ' return FUNC_SUBEDIT((ptrszint *) (& hwnd), & uMsg, & wParam, (ptrszint *) (& lParam));
  45. '}
  46. 'void * GetSubEdit() {
  47. ' return (void *) SubEdit;
  48. '}
  49.  
  50. '---End of file contents---
  51. 'Input box uses the WIN2.h file to get the WindowProc and SubEdit . Note: Place above file in your working folder (The QB64 folder).
  52. '----------------------------------------------------------------
  53.  
  54. '==Note: InputBox requires this section to be added to your program.=========
  55. ' Creates global variable ipb. Its corresponding elements are global.
  56. Dim Shared ipb As input_box_udt ' InputBox user defined type
  57. inputbox_init '                   Define ipb structure and initialize elements
  58. '=============================================================================
  59.  
  60. '===Subclassing
  61. Dim Shared OldWindowProc As Long
  62. '===End Subclassing
  63.  
  64. '===Your program START ==========
  65.  
  66. Screen _NewImage(400, 300, 32) 'Set main screen size
  67. Color _RGB(0, 0, 0), _RGB32(205, 238, 205) ' Set colors
  68. _Title "Main Program" ' Set your main program title
  69.  
  70.  
  71. 'Examples:
  72.  
  73. Print "===" + InputBox("Please enter some text:", "InputBox") + "==="
  74. Print "===" + InputBoxN("Please enter port number:", "Port Number") + "==="
  75. Print "===" + InputBoxP("Please enter password:", "Password Required") + "==="
  76.  
  77. Print "End"
  78. '===Your program END ============
  79.  
  80.  
  81. '######--INPUT BOX FUNCTIONS and SUBS--######################################
  82.  
  83. Function InputBox$ (prompt As String, title As String)
  84.     Const FALSE = 0
  85.     Const TRUE = Not FALSE
  86.     ipb.number = FALSE '   Disable numeric input
  87.     ipb.password = FALSE ' Disable password input
  88.  
  89.     ipb.caption_text = title 'Pop-up window title
  90.     ipb.prompt_text = prompt 'Label text user instrutions
  91.  
  92.     ipb_UserInput ' Run UserInput, data entered saved in buffer ipb.buf
  93.     InputBox = ipb_get_string 'Extract data from ipb.buf return string
  94.  
  95. Function InputBoxN$ (prompt As String, title As String)
  96.     Const FALSE = 0
  97.     Const TRUE = Not FALSE
  98.  
  99.     ipb.number = TRUE '        Enable numeric input
  100.     ipb.password = FALSE '     Disable password input
  101.     ipb.caption_text = title ' Pop-up window title
  102.     ipb.prompt_text = prompt ' Label text user instrutions
  103.  
  104.     ipb_UserInput ' Run UserInput, data entered saved in buffer ipb.buf
  105.     InputBoxN = ipb_get_string 'Extract data from ipb.buf return string
  106.  
  107. Function InputBoxP$ (prompt As String, title As String)
  108.     Const FALSE = 0
  109.     Const TRUE = Not FALSE
  110.  
  111.     ipb.number = FALSE '       Disable numeric input
  112.     ipb.password = TRUE '      Enable password input
  113.     ipb.caption_text = title ' Pop-up window title
  114.     ipb.prompt_text = prompt ' Label text user instrutions
  115.  
  116.     ipb_UserInput ' Run UserInput, data entered saved in buffer ipb.buf
  117.     InputBoxP = ipb_get_string 'Extract data from ipb.buf return string
  118.  
  119. '-- inputbox_init: Creates a user defined type and sets initial values.
  120. Sub inputbox_init
  121.     Const FALSE = 0
  122.     Const TRUE = Not FALSE
  123.  
  124.     Type input_box_udt
  125.         buf As String * 64 '  Buffer to store entered characters
  126.         hw As _Offset '       Pointer to inputbox window
  127.         hwLabel As _Offset '  Pointer to label
  128.         hwb0 As _Offset '     Pointer button 0 - OK
  129.         hwb1 As _Offset '     Pointer button 1 - Cancel
  130.         hwe As _Offset '      Pointer to single line edit control
  131.         number As Integer '   Edit control numbers only
  132.         password As Integer ' asterisk displayed
  133.  
  134.         caption_text As String ' Window title
  135.         prompt_text As String '  Instruction to user
  136.         button0_text As String ' Left button
  137.         button1_text As String ' Right button
  138.         x As Long 'Position of input box
  139.         y As Long 'Position of input box
  140.     End Type
  141.  
  142.     'Set default values
  143.     ipb.caption_text = "InputBox"
  144.     ipb.prompt_text = "Enter some text:"
  145.     ipb.button0_text = "OK"
  146.     ipb.button1_text = "Cancel"
  147.     ipb.number = FALSE
  148.     ipb.password = FALSE
  149.  
  150.     'Center InputBox window
  151.     Dim As Long userwidth, userheight
  152.     userwidth = _DesktopWidth: userheight = _DesktopHeight 'get current screen resolution
  153.  
  154.     ipb.x = (userwidth \ 2 - 358 \ 2) - 3
  155.     ipb.y = (userheight \ 2 - 136 \ 2) - 29
  156.  
  157.  
  158. Function ipb_get_string$ ()
  159.     Dim As String a
  160.  
  161.     'Extract string from buf
  162.     a = _Trim$(ipb.buf) '                  Remove spaces
  163.     a = Left$(a, InStr(a, Chr$(0)) - 1) '  Buffer contains a null terminated string. Find position of null,
  164.     ipb.buf = "" '                         extract characters upto this null character. Clear buffer
  165.  
  166.     ipb_get_string = a 'Return clean string
  167.  
  168. '===Main Sub ===========================
  169. Sub ipb_UserInput
  170.     '--Constants
  171.     Const FALSE = 0
  172.     Const TRUE = Not FALSE
  173.  
  174.     Const IDC_ARROW = &H7F00
  175.     Const COLOR_WINDOW = 5
  176.  
  177.     Const WS_OVERLAPPED = 0
  178.     Const WS_CAPTION = &H00C00000
  179.     Const WS_SYSMENU = &H00080000
  180.     Const WS_VISIBLE = &H10000000
  181.     Const WS_CHILD = &H40000000
  182.     Const WS_TABSTOP = &H00010000
  183.     Const WS_EX_CLIENTEDGE = &H00000200
  184.     Const BS_PUSHBUTTON = 0
  185.     Const CW_USEDEFAULT = &H80000000
  186.     Const SW_SHOWDEFAULT = &HA
  187.  
  188.     Const ES_LEFT = 0
  189.     Const ES_NUMBER = &H2000
  190.     Const ES_PASSWORD = &H0020
  191.  
  192.     Const WS_OVERLAPPEDWINDOW = WS_OVERLAPPED Or WS_CAPTION
  193.  
  194.     'Select style before control is created.
  195.     'No need to dynamically change. Controls are built every time they are used
  196.     Dim ipb_style As Long
  197.     ipb_style = 0
  198.     If ipb.password Then
  199.         ipb_style = ES_PASSWORD
  200.     End If
  201.     If ipb.number Then
  202.         ipb_style = ES_NUMBER
  203.     End If
  204.  
  205.     '--Types
  206.     Type POINT
  207.         As Long x
  208.         As Long y
  209.     End Type
  210.  
  211.     Type MSG
  212.         As _Offset hwnd
  213.         As _Unsigned Long message
  214.         As _Unsigned _Offset wParam 'unsigned pointer sized integer
  215.         As _Offset lParam '          pointer sized integer
  216.         As _Unsigned Long time
  217.         As POINT pt
  218.     End Type
  219.  
  220.     Type WNDCLASSA
  221.         As _Unsigned Long style
  222.         $If 64BIT Then
  223.         As String * 4 padding
  224.         $End If
  225.         As _Offset lpfnWndProc
  226.         As Long cbClsExtra, cbWndExtra
  227.         As _Offset hInstance, hIcon, hCursor, hbrBackground, lpszMenuName, lpszClassName
  228.     End Type
  229.  
  230.     '--Libaries
  231.  
  232.     '===Subclassing
  233.    Declare Library "WIN2"
  234.         Function GetWindowProc%& () 'Windows procedure
  235.         Function GetSubEdit%& () 'Edit procedure
  236.     End Declare
  237.  
  238.         Function SetWindowLongPtr& Alias "SetWindowLongPtrA" (ByVal hwnd As _Offset, Byval nIndex As Long, Byval dwNewLong As Long)
  239.         Function GetWindowLongPtr& (ByVal hwnd As _Offset, Byval nIndex As Long)
  240.         Function CallWindowProc& Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, Byval hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
  241.     End Declare
  242.     '===END Subclassing
  243.  
  244.     Declare Dynamic Library "user32"
  245.         Function SendMessageA%& (ByVal hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
  246.         Function DefWindowProcA%& (ByVal hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
  247.         Sub PostQuitMessage (ByVal nExitCode As Long)
  248.         Function LoadCursorW%& (ByVal hInstance As _Offset, Byval lpCursorName As _Offset)
  249.         Function RegisterClassA~% (ByVal lpWndClass As _Offset)
  250.         Function CreateWindowExA%& (ByVal dwExStyle As Long, Byval lpClassName As _Offset, Byval lpWindowName As _Offset, Byval dwStyle As Long, Byval X As Long, Byval Y As Long, Byval nWidth As Long, Byval nHeight As Long, Byval hWndParent As _Offset, Byval hMenu As _Offset, Byval hInstance As _Offset, Byval lpParam As _Offset)
  251.         Function ShowWindow& (ByVal hWnd As _Offset, Byval nCmdShow As Long)
  252.         Function UpdateWindow& (ByVal hWnd As _Offset)
  253.         Function GetMessageA% (ByVal lpMsg As _Offset, Byval hWnd As _Offset, Byval wMsgFilterMin As _Unsigned Long, Byval wMsgFilterMax As _Unsigned Long)
  254.         Function TranslateMessage& (ByVal lpMsg As _Offset)
  255.         Function DispatchMessageA%& (ByVal lpmsg As _Offset)
  256.         Sub DestroyWindow (ByVal hWnd As _Offset)
  257.         Function PostMessageA%& (ByVal hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
  258.         Function SetFocus& (ByVal hWnd As _Offset)
  259.         Function GetWindowLongA& (ByVal hwnd As _Offset, Byval nIndex As Long)
  260.         Function SetWindowLongA& (ByVal hwnd As _Offset, Byval nIndex As Long, Byval dwNewLong As Long)
  261.     End Declare
  262.  
  263.     Declare Dynamic Library "kernel32"
  264.         Function GetModuleHandleW%& (ByVal lpModuleName%&)
  265.         Function GetLastError~& ()
  266.     End Declare
  267.     '--End Libaries
  268.  
  269.  
  270.     '--Variables
  271.     Static registered As Integer 'Variables initialized to 0 (false) hence not registered. Value retained between functtion calls
  272.  
  273.     Dim hi As _Offset '   Handle to application instance
  274.     Dim wc As WNDCLASSA ' define wc as WNDCLASSEX structure
  275.     Dim msg As MSG
  276.  
  277.     Dim discardb As Long '   Dummy variable
  278.     Dim discardp As _Offset 'Dummy variable
  279.     Dim t0 As String '       Type of control
  280.     Dim t1 As String '       Title or controls text
  281.  
  282.     Dim MainClassName As String * 5
  283.     MainClassName = "main" + Chr$(0)
  284.  
  285.     Dim CrLf As String * 2 '     define as 2 byte STRING
  286.     CrLf = Chr$(13) + Chr$(10) ' carriage return and line feed
  287.  
  288.     Dim As String className '               Variable className stores name of our window class
  289.     className = "myWindowClass" + Chr$(0) ' Used in wc. which in turn is used to register window class with the system.
  290.  
  291.     hi = GetModuleHandleW(0) 'Handle to application instance
  292.  
  293.     '---Step 1: Registering the Window Class
  294.     'Fill out the members of WNDCLASSEX structure (wc) and call RegisterClassA
  295.  
  296.     wc.style = 0 '                            Class Styles (CS_*), not Window Styles (WS_*) This is usually be set to 0.
  297.     wc.lpfnWndProc = GetWindowProc '          Pointer to the window procedure for this window class. (see WIN.h)
  298.     wc.cbClsExtra = 0 '                       Amount of extra data allocated for this class in memory. Usually 0.
  299.     wc.cbWndExtra = 0 '                       Amount of extra data allocated in memory per window of this type. Usually 0.
  300.     wc.hInstance = hi '                       Handle to application instance .
  301.     wc.hIcon = 0 '                            Large (usually 32x32) icon shown when the user presses Alt+Tab. Set to 0
  302.     wc.hCursor = LoadCursorW(0, IDC_ARROW) '  Cursor that will be displayed over our window.
  303.     wc.hbrBackground = COLOR_WINDOW 'was +1   Background Brush to set the color of our window. '
  304.     wc.lpszMenuName = 0 '                     Name of a menu resource to use for the windows with this class.
  305.     wc.lpszClassName = _Offset(className) '   Name to identify the class with.
  306.  
  307.     If Not registered Then '   First time in funcion OK to register
  308.         If RegisterClassA(_Offset(wc)) = 0 Then
  309.             Print "RegisterClassA failed:"; GetLastError
  310.             End
  311.         End If
  312.         registered = TRUE ' Class was registered
  313.     End If
  314.  
  315.     '--Step 2: Creating the Windows
  316.     'After registering the class, create a window with it using CreateWindowExA.
  317.  
  318.     'Note: A visible un-owned window gets a taskbar button. To hide the inputbox window taskbar button
  319.     'make the inputbox owned by our main applicationusing using  _WindowHandle instead of 0'
  320.     t1 = ipb.caption_text + Chr$(0) 'Window title
  321.     ipb.hw = CreateWindowExA(0, _Offset(className), _Offset(t1), WS_OVERLAPPEDWINDOW, ipb.x, ipb.y, 358, 130, _WindowHandle, 0, hi, 0): If 0 = ipb.hw Then System
  322.  
  323.     'Controls are just child windows. They have a procedure, a class etc... that is registered by the system.
  324.  
  325.     'Label
  326.     t0 = "STATIC" + Chr$(0) '        Window control is STATIC predefined class
  327.     t1 = ipb.prompt_text + Chr$(0) ' Label text
  328.     ipb.hwLabel = CreateWindowExA(0, _Offset(t0), _Offset(t1), WS_VISIBLE Or WS_CHILD, 9, 5, 372, 16, ipb.hw, 0, hi, 0): If 0 = ipb.hwLabel Then System
  329.  
  330.  
  331.     'OK Button 0
  332.     t0 = "BUTTON" + Chr$(0) '   Window control is BUTTON predefined class
  333.     t1 = ipb.button0_text + Chr$(0)
  334.     ipb.hwb0 = CreateWindowExA(0, _Offset(t0), _Offset(t1), WS_TABSTOP Or WS_VISIBLE Or WS_CHILD Or BS_PUSHBUTTON, 175, 65, 80, 23, ipb.hw, 0, hi, 0): If 0 = ipb.hwb0 Then System
  335.  
  336.     'Cancel button 1
  337.     t1 = ipb.button1_text + Chr$(0)
  338.     ipb.hwb1 = CreateWindowExA(0, _Offset(t0), _Offset(t1), WS_TABSTOP Or WS_VISIBLE Or WS_CHILD Or BS_PUSHBUTTON, 262, 65, 80, 23, ipb.hw, 0, hi, 0): If 0 = ipb.hwb1 Then System
  339.  
  340.     'Edit control
  341.     t0 = "EDIT" + Chr$(0)
  342.     '    t1 = "This is a edit control." + Chr$(0)
  343.     t1 = "" + Chr$(0)
  344.     ipb.hwe = CreateWindowExA(WS_EX_CLIENTEDGE, _Offset(t0), _Offset(t1), WS_VISIBLE Or WS_CHILD Or ES_LEFT Or ipb_style, 12, 30, 330, 26, ipb.hw, 0, hi, 0): If 0 = ipb.hwe Then System
  345.  
  346.     '====Subclassing
  347.     Const GWLP_WNDPROC = -4 ' Sets a new address for the window procedure
  348.     OldWindowProc = SetWindowLongA(ipb.hwe, GWLP_WNDPROC, ConvertOffset(GetSubEdit)) ' set your custom procedure  
  349.     '===End Subclassing
  350.  
  351.  
  352.     'Display and Update window to ensure it has properly redrawn itself on the screen.
  353.     discardb = SetFocus(ipb.hwe) ' and start typing
  354.     discardb = ShowWindow(ipb.hw, SW_SHOWDEFAULT)
  355.     discardb = UpdateWindow(ipb.hw)
  356.  
  357.     '-- Step 3: The Message Loop
  358.     While GetMessageA(_Offset(msg), 0, 0, 0) > 0 '   gets a message from your application's message queue.
  359.         discardb = TranslateMessage(_Offset(msg)) '  performs some additional processing on keyboard events
  360.         discardp = DispatchMessageA(_Offset(msg)) '  sends the message out to the window that the message was sent to
  361.     Wend
  362.  
  363. '===End main function ===========================
  364.  
  365.  
  366. '-- Step 4: the Window Procedure
  367. Function WindowProc%& (hWnd As _Offset, uMsg As _Unsigned Long, wParam As _Unsigned _Offset, lParam As _Offset)
  368.     Const WM_CREATE = &H0001
  369.     Const WM_CLOSE = &H0010
  370.     Const WM_DESTROY = 2
  371.     Const WM_COMMAND = &H0111
  372.     Const BN_CLICKED = 0
  373.     Const WM_GETTEXT = &H000D
  374.     Const WM_SETFOCUS = &H0007
  375.     Dim discardp As _Offset 'Dummy variable
  376.     Dim discardb As Long '   Dummy variable
  377.     Select Case uMsg
  378.  
  379.         '   Case WM_CREATE
  380.         '     WindowProc = 0
  381.  
  382.         Case WM_CLOSE
  383.             DestroyWindow (hWnd) 'Destroy window and child windows
  384.             WindowProc = 0
  385.  
  386.         Case WM_DESTROY
  387.             PostQuitMessage 0 ' Want to exit the program
  388.             WindowProc = 0
  389.  
  390.         Case WM_SETFOCUS
  391.             discardb = SetFocus(ipb.hwe) 'Set Edit control focus
  392.             WindowProc = 0
  393.  
  394.         Case WM_COMMAND
  395.             '==============
  396.             If wParam = BN_CLICKED Then
  397.                 Select Case lParam
  398.                     'A button was clicked test each one
  399.                     '---Sandard buttons---
  400.                     Case ipb.hwb0 'OK button
  401.                         'Print "Button 0 pressed OK"
  402.                         'Get input text and copy to buffer (buf)
  403.                         discardp = SendMessageA(ipb.hwe, WM_GETTEXT, Len(ipb.buf), _Offset(ipb.buf))
  404.                         'Print Len(buf)
  405.                         discardp = PostMessageA(hWnd, WM_CLOSE, 0, 0) 'Close
  406.                         WindowProc = 0
  407.  
  408.                     Case ipb.hwb1 'Cancel button
  409.                         'Print "Button 1 pressed Cancel"
  410.                         ipb.buf = "" 'reset zero-length string ("").
  411.                         discardp = PostMessageA(hWnd, WM_CLOSE, 0, 0) 'Close
  412.                         WindowProc = 0
  413.                         '---End standard buttons---
  414.                 End Select
  415.             Else
  416.                 'Not our message send back to system for processing
  417.                 WindowProc = DefWindowProcA(hWnd, uMsg, wParam, lParam)
  418.             End If
  419.             '=================
  420.             WindowProc = 0
  421.  
  422.         Case Else
  423.             'Not our message send back to system for processing
  424.             WindowProc = DefWindowProcA(hWnd, uMsg, wParam, lParam)
  425.     End Select
  426. '######--END INPUT BOX FUNCTIONS and SUBS--##################################
  427.  
  428. 'Ref https://www.qb64.org/forum/index.php?topic=1553.msg108409#msg108409
  429. 'Ref https://www.qb64.org/forum/index.php?topic=2905.msg121660#msg121660
  430. Function ConvertOffset&& (value As _Offset)
  431.     Dim temp As Long
  432.     Dim m As _MEM 'Define a memblock
  433.     m = _Mem(value) 'Point it to use value
  434.     $If 64BIT Then
  435.         'On 64 bit OSes, an OFFSET is 8 bytes in size.  We can put it directly into an Integer64
  436.         _MEMGET m, m.OFFSET, ConvertOffset&& 'Get the contents of the memblock and put the values there directly into ConvertOffset&&
  437.     $Else
  438.         'However, on 32 bit OSes, an OFFSET is only 4 bytes.  We need to put it into a LONG variable first
  439.         _MemGet m, m.OFFSET, temp 'Like this
  440.         ConvertOffset&& = temp 'And then assign that long value to ConvertOffset&&
  441.     $End If
  442.     _MemFree m 'Free the memblock
  443.  
  444. Function SubEdit%& (hWnd As _Offset, uMsg As _Unsigned Long, wParam As _Unsigned _Offset, lParam As _Offset)
  445.     Const WM_CHAR = 258
  446.     Const WM_NCDESTROY = &H82
  447.     Dim discardb As Long '   Dummy variable
  448.     Const GWLP_WNDPROC = -4 ' Sets a new address for the window procedure
  449.  
  450.     ' If we're being destroyed, restore the original WindowProc.
  451.     If uMsg = WM_NCDESTROY Then
  452.         discardb = SetWindowLongA(hWnd, GWLP_WNDPROC, OldWindowProc)
  453.     End If
  454.  
  455.      '    Print "xxxxxx"
  456.     Select Case uMsg
  457.         Case WM_CHAR '           only interested in the WM_CHAR message
  458.             If wParam > 57 Then
  459.                 Print "Test >57"
  460.                 SubEdit = 0 '    Return 0 character processed
  461.             Else
  462.                 Print "Not >57" 'Call default processing
  463.                 SubEdit = CallWindowProc(OldWindowProc, hWnd, uMsg, wParam, lParam)
  464.             End If
  465.  
  466.         Case Else
  467.      ' No matter what happened, use the old window procedure to finish processing the message.      
  468.             SubEdit = CallWindowProc(OldWindowProc, hWnd, uMsg, wParam, lParam)
  469.     End Select
  470.  
  471.  

However the above code fails to compile/run on QB64 64bit.  I have tried using the alternative function "SetWindowLongPtr" which is supposed tobe compatible for both 32 and 64 bit.
In trying to get this working I have discovered the endpoint of the mythical oozlum bird.

How do I get the above code to compile and run in both  QB64 32 and 64 bit.
Any help would be most appreciated.

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: InputBox
« Reply #7 on: September 09, 2021, 09:41:48 am »
@mpgcan Hi! Glad to see you are working with some Win32 controls! This compiled and ran perfectly fine on 64 bit for me!
Shuwatch!

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: InputBox
« Reply #8 on: September 09, 2021, 09:48:32 am »
By the way, did you know that if you use any $VERSIONINFO key (on the latest dev build (Thanks, Fellippe))then you will get a more modern style input box? I made a change a few months ago to use Common Controls v6 when someone uses the $VERSIONINFO metacommand. Here's how it looks on Windows 10 64 bit using $VERSIONINFO. It will look even better on Windows 11. P.S.: I know how to change the font for the box ;)

  [ You are not allowed to view this attachment ]  
« Last Edit: September 09, 2021, 09:53:58 am by SpriggsySpriggs »
Shuwatch!

FellippeHeitor

  • Guest
Re: InputBox
« Reply #9 on: September 09, 2021, 09:51:41 am »
** on the latest dev build 😉

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: InputBox
« Reply #10 on: September 09, 2021, 10:06:19 am »
I've also made some minor changes to your code. I'm too busy to do a lot with it but you actually don't need any DLL declarations! All of the functions from the APIs you are using can be put under Declare CustomType Library. See below for the change. Code still compiles and executes the same! Also, I changed GetModuleHandleW to GetModuleHandleA because you won't be using the Unicode functions with what you have so far.
Code: QB64: [Select]
  1. $VersionInfo:Comments=This uses Subclassing Controls!
  2.  
  3. ' A Simple Input box for QB64 32 & 64 bit
  4. ' mpg_input_box_1c.bas
  5. ' MPG 9-9-2021
  6. '===================================================================
  7. ' Displays an inputbox popup window. Gets a line of text from user.
  8. '
  9. 'There are three InputBox function:
  10. 'InputBox("prompt, title)  - Accepts any character input
  11. 'InputBoxN("prompt, title) - Only numeric characters allowed
  12. 'InputBoxP("prompt, title) - Password entry. Display asterisk
  13. '                            for each character entered
  14. '
  15. 'prompt - text displayed by the InputBox e.g. "Please enter a number:".
  16. 'title  - text displayed in the title bar of the InputBox.
  17. '
  18. 'Note:  Cancel button returns a null string ("")
  19. '
  20. '----------------------------------------------------------------------
  21. ' Code based on ideas from the following references:
  22. ' 1) Windows buttons for 32 and 64 bit IDE
  23. '    https://www.qb64.org/forum/index.php?topic=3217.msg124966#msg124966
  24. ' 2) Base64 Encoding/Decoding with Windows, Mac, and Linux
  25. '    https://www.qb64.org/forum/index.php?topic=3214.msg124919#msg124919
  26. ' 3) Threading
  27. '    https://www.qb64.org/forum/index.php?topic=3865.msg132124#msg132124
  28. '=======================================================================
  29. '
  30. '---------------------------------------------------------------
  31. 'NOTE:
  32. 'Create a new file WIN2.h with the following content (Remove the comments '):
  33. '---Start of file contents:---
  34.  
  35. 'ptrszint FUNC_WINDOWPROC(ptrszint*_FUNC_WINDOWPROC_OFFSET_HWND,uint32*_FUNC_WINDOWPROC_ULONG_UMSG,uptrszint*_FUNC_WINDOWPROC_UOFFSET_WPARAM,ptrszint*_FUNC_WINDOWPROC_OFFSET_LPARAM);
  36. 'LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  37. ' return FUNC_WINDOWPROC((ptrszint *) (& hwnd), & uMsg, & wParam, (ptrszint *) (& lParam));
  38. '}
  39. 'void * GetWindowProc() {
  40. ' return (void *) WindowProc;
  41. '}
  42.  
  43. 'ptrszint FUNC_SUBEDIT(ptrszint*_FUNC_SUBEDIT_OFFSET_HWND,uint32*_FUNC_SUBEDIT_ULONG_UMSG,uptrszint*_FUNC_SUBEDIT_UOFFSET_WPARAM,ptrszint*_FUNC_SUBEDIT_OFFSET_LPARAM);
  44. 'LRESULT CALLBACK SubEdit(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  45. ' return FUNC_SUBEDIT((ptrszint *) (& hwnd), & uMsg, & wParam, (ptrszint *) (& lParam));
  46. '}
  47. 'void * GetSubEdit() {
  48. ' return (void *) SubEdit;
  49. '}
  50.  
  51. '---End of file contents---
  52. 'Input box uses the WIN2.h file to get the WindowProc and SubEdit . Note: Place above file in your working folder (The QB64 folder).
  53. '----------------------------------------------------------------
  54.  
  55. '==Note: InputBox requires this section to be added to your program.=========
  56. ' Creates global variable ipb. Its corresponding elements are global.
  57. Dim Shared ipb As input_box_udt ' InputBox user defined type
  58. inputbox_init '                   Define ipb structure and initialize elements
  59. '=============================================================================
  60.  
  61. '===Subclassing
  62. Dim Shared OldWindowProc As Long
  63. '===End Subclassing
  64.  
  65. '===Your program START ==========
  66.  
  67. Screen _NewImage(400, 300, 32) 'Set main screen size
  68. Color _RGB(0, 0, 0), _RGB32(205, 238, 205) ' Set colors
  69. _Title "Main Program" ' Set your main program title
  70.  
  71.  
  72. 'Examples:
  73.  
  74. Print "===" + InputBox("Please enter some text:", "InputBox") + "==="
  75. Print "===" + InputBoxN("Please enter port number:", "Port Number") + "==="
  76. Print "===" + InputBoxP("Please enter password:", "Password Required") + "==="
  77.  
  78. Print "End"
  79. '===Your program END ============
  80.  
  81.  
  82. '######--INPUT BOX FUNCTIONS and SUBS--######################################
  83.  
  84. Function InputBox$ (prompt As String, title As String)
  85.     Const FALSE = 0
  86.     Const TRUE = Not FALSE
  87.     ipb.number = FALSE '   Disable numeric input
  88.     ipb.password = FALSE ' Disable password input
  89.  
  90.     ipb.caption_text = title 'Pop-up window title
  91.     ipb.prompt_text = prompt 'Label text user instrutions
  92.  
  93.     ipb_UserInput ' Run UserInput, data entered saved in buffer ipb.buf
  94.     InputBox = ipb_get_string 'Extract data from ipb.buf return string
  95.  
  96. Function InputBoxN$ (prompt As String, title As String)
  97.     Const FALSE = 0
  98.     Const TRUE = Not FALSE
  99.  
  100.     ipb.number = TRUE '        Enable numeric input
  101.     ipb.password = FALSE '     Disable password input
  102.     ipb.caption_text = title ' Pop-up window title
  103.     ipb.prompt_text = prompt ' Label text user instrutions
  104.  
  105.     ipb_UserInput ' Run UserInput, data entered saved in buffer ipb.buf
  106.     InputBoxN = ipb_get_string 'Extract data from ipb.buf return string
  107.  
  108. Function InputBoxP$ (prompt As String, title As String)
  109.     Const FALSE = 0
  110.     Const TRUE = Not FALSE
  111.  
  112.     ipb.number = FALSE '       Disable numeric input
  113.     ipb.password = TRUE '      Enable password input
  114.     ipb.caption_text = title ' Pop-up window title
  115.     ipb.prompt_text = prompt ' Label text user instrutions
  116.  
  117.     ipb_UserInput ' Run UserInput, data entered saved in buffer ipb.buf
  118.     InputBoxP = ipb_get_string 'Extract data from ipb.buf return string
  119.  
  120. '-- inputbox_init: Creates a user defined type and sets initial values.
  121. Sub inputbox_init
  122.     Const FALSE = 0
  123.     Const TRUE = Not FALSE
  124.  
  125.     Type input_box_udt
  126.         buf As String * 64 '  Buffer to store entered characters
  127.         hw As _Offset '       Pointer to inputbox window
  128.         hwLabel As _Offset '  Pointer to label
  129.         hwb0 As _Offset '     Pointer button 0 - OK
  130.         hwb1 As _Offset '     Pointer button 1 - Cancel
  131.         hwe As _Offset '      Pointer to single line edit control
  132.         number As Integer '   Edit control numbers only
  133.         password As Integer ' asterisk displayed
  134.  
  135.         caption_text As String ' Window title
  136.         prompt_text As String '  Instruction to user
  137.         button0_text As String ' Left button
  138.         button1_text As String ' Right button
  139.         x As Long 'Position of input box
  140.         y As Long 'Position of input box
  141.     End Type
  142.  
  143.     'Set default values
  144.     ipb.caption_text = "InputBox"
  145.     ipb.prompt_text = "Enter some text:"
  146.     ipb.button0_text = "OK"
  147.     ipb.button1_text = "Cancel"
  148.     ipb.number = FALSE
  149.     ipb.password = FALSE
  150.  
  151.     'Center InputBox window
  152.     Dim As Long userwidth, userheight
  153.     userwidth = _DesktopWidth: userheight = _DesktopHeight 'get current screen resolution
  154.  
  155.     ipb.x = (userwidth \ 2 - 358 \ 2) - 3
  156.     ipb.y = (userheight \ 2 - 136 \ 2) - 29
  157.  
  158.  
  159. Function ipb_get_string$ ()
  160.     Dim As String a
  161.  
  162.     'Extract string from buf
  163.     a = _Trim$(ipb.buf) '                  Remove spaces
  164.     a = Left$(a, InStr(a, Chr$(0)) - 1) '  Buffer contains a null terminated string. Find position of null,
  165.     ipb.buf = "" '                         extract characters upto this null character. Clear buffer
  166.  
  167.     ipb_get_string = a 'Return clean string
  168.  
  169. '===Main Sub ===========================
  170. Sub ipb_UserInput
  171.     '--Constants
  172.     Const FALSE = 0
  173.     Const TRUE = Not FALSE
  174.  
  175.     Const IDC_ARROW = &H7F00
  176.     Const COLOR_WINDOW = 5
  177.  
  178.     Const WS_OVERLAPPED = 0
  179.     Const WS_CAPTION = &H00C00000
  180.     Const WS_SYSMENU = &H00080000
  181.     Const WS_VISIBLE = &H10000000
  182.     Const WS_CHILD = &H40000000
  183.     Const WS_TABSTOP = &H00010000
  184.     Const WS_EX_CLIENTEDGE = &H00000200
  185.     Const BS_PUSHBUTTON = 0
  186.     Const CW_USEDEFAULT = &H80000000
  187.     Const SW_SHOWDEFAULT = &HA
  188.  
  189.     Const ES_LEFT = 0
  190.     Const ES_NUMBER = &H2000
  191.     Const ES_PASSWORD = &H0020
  192.  
  193.     Const WS_OVERLAPPEDWINDOW = WS_OVERLAPPED Or WS_CAPTION
  194.  
  195.     'Select style before control is created.
  196.     'No need to dynamically change. Controls are built every time they are used
  197.     Dim ipb_style As Long
  198.     ipb_style = 0
  199.     If ipb.password Then
  200.         ipb_style = ES_PASSWORD
  201.     End If
  202.     If ipb.number Then
  203.         ipb_style = ES_NUMBER
  204.     End If
  205.  
  206.     '--Types
  207.     Type POINT
  208.         As Long x
  209.         As Long y
  210.     End Type
  211.  
  212.     Type MSG
  213.         As _Offset hwnd
  214.         As _Unsigned Long message
  215.         As _Unsigned _Offset wParam 'unsigned pointer sized integer
  216.         As _Offset lParam '          pointer sized integer
  217.         As _Unsigned Long time
  218.         As POINT pt
  219.     End Type
  220.  
  221.     Type WNDCLASSA
  222.         As _Unsigned Long style
  223.         $If 64BIT Then
  224.             As String * 4 padding
  225.         $End If
  226.         As _Offset lpfnWndProc
  227.         As Long cbClsExtra, cbWndExtra
  228.         As _Offset hInstance, hIcon, hCursor, hbrBackground, lpszMenuName, lpszClassName
  229.     End Type
  230.  
  231.     '--Libaries
  232.  
  233.     '===Subclassing
  234.     Declare Library "WIN2"
  235.         Function GetWindowProc%& () 'Windows procedure
  236.         Function GetSubEdit%& () 'Edit procedure
  237.     End Declare
  238.  
  239.         Function SetWindowLongPtr& Alias "SetWindowLongPtrA" (ByVal hwnd As _Offset, Byval nIndex As Long, Byval dwNewLong As Long)
  240.         Function GetWindowLongPtr& (ByVal hwnd As _Offset, Byval nIndex As Long)
  241.         Function CallWindowProc& Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, Byval hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
  242.         Function SendMessageA%& (ByVal hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
  243.         Function DefWindowProcA%& (ByVal hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
  244.         Sub PostQuitMessage (ByVal nExitCode As Long)
  245.         Function LoadCursorW%& (ByVal hInstance As _Offset, Byval lpCursorName As _Offset)
  246.         Function RegisterClassA~% (ByVal lpWndClass As _Offset)
  247.         Function CreateWindowExA%& (ByVal dwExStyle As Long, Byval lpClassName As _Offset, Byval lpWindowName As _Offset, Byval dwStyle As Long, Byval X As Long, Byval Y As Long, Byval nWidth As Long, Byval nHeight As Long, Byval hWndParent As _Offset, Byval hMenu As _Offset, Byval hInstance As _Offset, Byval lpParam As _Offset)
  248.         Function ShowWindow& (ByVal hWnd As _Offset, Byval nCmdShow As Long)
  249.         Function UpdateWindow& (ByVal hWnd As _Offset)
  250.         Function GetMessageA% (ByVal lpMsg As _Offset, Byval hWnd As _Offset, Byval wMsgFilterMin As _Unsigned Long, Byval wMsgFilterMax As _Unsigned Long)
  251.         Function TranslateMessage& (ByVal lpMsg As _Offset)
  252.         Function DispatchMessageA%& (ByVal lpmsg As _Offset)
  253.         Sub DestroyWindow (ByVal hWnd As _Offset)
  254.         Function PostMessageA%& (ByVal hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
  255.         Function SetFocus& (ByVal hWnd As _Offset)
  256.         Function GetWindowLongA& (ByVal hwnd As _Offset, Byval nIndex As Long)
  257.         Function SetWindowLongA& (ByVal hwnd As _Offset, Byval nIndex As Long, Byval dwNewLong As Long)
  258.         Function GetModuleHandleA%& (ByVal lpModuleName%&)
  259.         Function GetLastError~& ()
  260.     End Declare
  261.     '--End Libaries
  262.  
  263.  
  264.     '--Variables
  265.     Static registered As Integer 'Variables initialized to 0 (false) hence not registered. Value retained between functtion calls
  266.  
  267.     Dim hi As _Offset '   Handle to application instance
  268.     Dim wc As WNDCLASSA ' define wc as WNDCLASSEX structure
  269.     Dim msg As MSG
  270.  
  271.     Dim discardb As Long '   Dummy variable
  272.     Dim discardp As _Offset 'Dummy variable
  273.     Dim t0 As String '       Type of control
  274.     Dim t1 As String '       Title or controls text
  275.  
  276.     Dim MainClassName As String * 5
  277.     MainClassName = "main" + Chr$(0)
  278.  
  279.     Dim CrLf As String * 2 '     define as 2 byte STRING
  280.     CrLf = Chr$(13) + Chr$(10) ' carriage return and line feed
  281.  
  282.     Dim As String className '               Variable className stores name of our window class
  283.     className = "myWindowClass" + Chr$(0) ' Used in wc. which in turn is used to register window class with the system.
  284.  
  285.     hi = GetModuleHandleA(0) 'Handle to application instance
  286.  
  287.     '---Step 1: Registering the Window Class
  288.     'Fill out the members of WNDCLASSEX structure (wc) and call RegisterClassA
  289.  
  290.     wc.style = 0 '                            Class Styles (CS_*), not Window Styles (WS_*) This is usually be set to 0.
  291.     wc.lpfnWndProc = GetWindowProc '          Pointer to the window procedure for this window class. (see WIN.h)
  292.     wc.cbClsExtra = 0 '                       Amount of extra data allocated for this class in memory. Usually 0.
  293.     wc.cbWndExtra = 0 '                       Amount of extra data allocated in memory per window of this type. Usually 0.
  294.     wc.hInstance = hi '                       Handle to application instance .
  295.     wc.hIcon = 0 '                            Large (usually 32x32) icon shown when the user presses Alt+Tab. Set to 0
  296.     wc.hCursor = LoadCursorW(0, IDC_ARROW) '  Cursor that will be displayed over our window.
  297.     wc.hbrBackground = COLOR_WINDOW 'was +1   Background Brush to set the color of our window. '
  298.     wc.lpszMenuName = 0 '                     Name of a menu resource to use for the windows with this class.
  299.     wc.lpszClassName = _Offset(className) '   Name to identify the class with.
  300.  
  301.     If Not registered Then '   First time in funcion OK to register
  302.         If RegisterClassA(_Offset(wc)) = 0 Then
  303.             Print "RegisterClassA failed:"; GetLastError
  304.             End
  305.         End If
  306.         registered = TRUE ' Class was registered
  307.     End If
  308.  
  309.     '--Step 2: Creating the Windows
  310.     'After registering the class, create a window with it using CreateWindowExA.
  311.  
  312.     'Note: A visible un-owned window gets a taskbar button. To hide the inputbox window taskbar button
  313.     'make the inputbox owned by our main applicationusing using  _WindowHandle instead of 0'
  314.     t1 = ipb.caption_text + Chr$(0) 'Window title
  315.     ipb.hw = CreateWindowExA(0, _Offset(className), _Offset(t1), WS_OVERLAPPEDWINDOW, ipb.x, ipb.y, 358, 130, _WindowHandle, 0, hi, 0): If 0 = ipb.hw Then System
  316.  
  317.     'Controls are just child windows. They have a procedure, a class etc... that is registered by the system.
  318.  
  319.     'Label
  320.     t0 = "STATIC" + Chr$(0) '        Window control is STATIC predefined class
  321.     t1 = ipb.prompt_text + Chr$(0) ' Label text
  322.     ipb.hwLabel = CreateWindowExA(0, _Offset(t0), _Offset(t1), WS_VISIBLE Or WS_CHILD, 9, 5, 372, 16, ipb.hw, 0, hi, 0): If 0 = ipb.hwLabel Then System
  323.  
  324.  
  325.     'OK Button 0
  326.     t0 = "BUTTON" + Chr$(0) '   Window control is BUTTON predefined class
  327.     t1 = ipb.button0_text + Chr$(0)
  328.     ipb.hwb0 = CreateWindowExA(0, _Offset(t0), _Offset(t1), WS_TABSTOP Or WS_VISIBLE Or WS_CHILD Or BS_PUSHBUTTON, 175, 65, 80, 23, ipb.hw, 0, hi, 0): If 0 = ipb.hwb0 Then System
  329.  
  330.     'Cancel button 1
  331.     t1 = ipb.button1_text + Chr$(0)
  332.     ipb.hwb1 = CreateWindowExA(0, _Offset(t0), _Offset(t1), WS_TABSTOP Or WS_VISIBLE Or WS_CHILD Or BS_PUSHBUTTON, 262, 65, 80, 23, ipb.hw, 0, hi, 0): If 0 = ipb.hwb1 Then System
  333.  
  334.     'Edit control
  335.     t0 = "EDIT" + Chr$(0)
  336.     '    t1 = "This is a edit control." + Chr$(0)
  337.     t1 = "" + Chr$(0)
  338.     ipb.hwe = CreateWindowExA(WS_EX_CLIENTEDGE, _Offset(t0), _Offset(t1), WS_VISIBLE Or WS_CHILD Or ES_LEFT Or ipb_style, 12, 30, 330, 26, ipb.hw, 0, hi, 0): If 0 = ipb.hwe Then System
  339.  
  340.     '====Subclassing
  341.     Const GWLP_WNDPROC = -4 ' Sets a new address for the window procedure
  342.     OldWindowProc = SetWindowLongA(ipb.hwe, GWLP_WNDPROC, ConvertOffset(GetSubEdit)) ' set your custom procedure
  343.     '===End Subclassing
  344.  
  345.  
  346.     'Display and Update window to ensure it has properly redrawn itself on the screen.
  347.     discardb = SetFocus(ipb.hwe) ' and start typing
  348.     discardb = ShowWindow(ipb.hw, SW_SHOWDEFAULT)
  349.     discardb = UpdateWindow(ipb.hw)
  350.  
  351.     '-- Step 3: The Message Loop
  352.     While GetMessageA(_Offset(msg), 0, 0, 0) > 0 '   gets a message from your application's message queue.
  353.         discardb = TranslateMessage(_Offset(msg)) '  performs some additional processing on keyboard events
  354.         discardp = DispatchMessageA(_Offset(msg)) '  sends the message out to the window that the message was sent to
  355.     Wend
  356.  
  357. '===End main function ===========================
  358.  
  359.  
  360. '-- Step 4: the Window Procedure
  361. Function WindowProc%& (hWnd As _Offset, uMsg As _Unsigned Long, wParam As _Unsigned _Offset, lParam As _Offset)
  362.     Const WM_CREATE = &H0001
  363.     Const WM_CLOSE = &H0010
  364.     Const WM_DESTROY = 2
  365.     Const WM_COMMAND = &H0111
  366.     Const BN_CLICKED = 0
  367.     Const WM_GETTEXT = &H000D
  368.     Const WM_SETFOCUS = &H0007
  369.     Dim discardp As _Offset 'Dummy variable
  370.     Dim discardb As Long '   Dummy variable
  371.     Select Case uMsg
  372.  
  373.         '   Case WM_CREATE
  374.         '     WindowProc = 0
  375.  
  376.         Case WM_CLOSE
  377.             DestroyWindow (hWnd) 'Destroy window and child windows
  378.             WindowProc = 0
  379.  
  380.         Case WM_DESTROY
  381.             PostQuitMessage 0 ' Want to exit the program
  382.             WindowProc = 0
  383.  
  384.         Case WM_SETFOCUS
  385.             discardb = SetFocus(ipb.hwe) 'Set Edit control focus
  386.             WindowProc = 0
  387.  
  388.         Case WM_COMMAND
  389.             '==============
  390.             If wParam = BN_CLICKED Then
  391.                 Select Case lParam
  392.                     'A button was clicked test each one
  393.                     '---Sandard buttons---
  394.                     Case ipb.hwb0 'OK button
  395.                         'Print "Button 0 pressed OK"
  396.                         'Get input text and copy to buffer (buf)
  397.                         discardp = SendMessageA(ipb.hwe, WM_GETTEXT, Len(ipb.buf), _Offset(ipb.buf))
  398.                         'Print Len(buf)
  399.                         discardp = PostMessageA(hWnd, WM_CLOSE, 0, 0) 'Close
  400.                         WindowProc = 0
  401.  
  402.                     Case ipb.hwb1 'Cancel button
  403.                         'Print "Button 1 pressed Cancel"
  404.                         ipb.buf = "" 'reset zero-length string ("").
  405.                         discardp = PostMessageA(hWnd, WM_CLOSE, 0, 0) 'Close
  406.                         WindowProc = 0
  407.                         '---End standard buttons---
  408.                 End Select
  409.             Else
  410.                 'Not our message send back to system for processing
  411.                 WindowProc = DefWindowProcA(hWnd, uMsg, wParam, lParam)
  412.             End If
  413.             '=================
  414.             WindowProc = 0
  415.  
  416.         Case Else
  417.             'Not our message send back to system for processing
  418.             WindowProc = DefWindowProcA(hWnd, uMsg, wParam, lParam)
  419.     End Select
  420. '######--END INPUT BOX FUNCTIONS and SUBS--##################################
  421.  
  422. 'Ref https://www.qb64.org/forum/index.php?topic=1553.msg108409#msg108409
  423. 'Ref https://www.qb64.org/forum/index.php?topic=2905.msg121660#msg121660
  424. Function ConvertOffset&& (value As _Offset)
  425.     Dim temp As Long
  426.     Dim m As _MEM 'Define a memblock
  427.     m = _Mem(value) 'Point it to use value
  428.     $If 64BIT Then
  429.         'On 64 bit OSes, an OFFSET is 8 bytes in size.  We can put it directly into an Integer64
  430.         _MemGet m, m.OFFSET, ConvertOffset&& 'Get the contents of the memblock and put the values there directly into ConvertOffset&&
  431.     $Else
  432.         'However, on 32 bit OSes, an OFFSET is only 4 bytes.  We need to put it into a LONG variable first
  433.         _MEMGET m, m.OFFSET, temp 'Like this
  434.         ConvertOffset&& = temp 'And then assign that long value to ConvertOffset&&
  435.     $End If
  436.     _MemFree m 'Free the memblock
  437.  
  438. Function SubEdit%& (hWnd As _Offset, uMsg As _Unsigned Long, wParam As _Unsigned _Offset, lParam As _Offset)
  439.     Const WM_CHAR = 258
  440.     Const WM_NCDESTROY = &H82
  441.     Dim discardb As Long '   Dummy variable
  442.     Const GWLP_WNDPROC = -4 ' Sets a new address for the window procedure
  443.  
  444.     ' If we're being destroyed, restore the original WindowProc.
  445.     If uMsg = WM_NCDESTROY Then
  446.         discardb = SetWindowLongA(hWnd, GWLP_WNDPROC, OldWindowProc)
  447.     End If
  448.  
  449.     '    Print "xxxxxx"
  450.     Select Case uMsg
  451.         Case WM_CHAR '           only interested in the WM_CHAR message
  452.             If wParam > 57 Then
  453.                 Print "Test >57"
  454.                 SubEdit = 0 '    Return 0 character processed
  455.             Else
  456.                 Print "Not >57" 'Call default processing
  457.                 SubEdit = CallWindowProc(OldWindowProc, hWnd, uMsg, wParam, lParam)
  458.             End If
  459.  
  460.         Case Else
  461.             ' No matter what happened, use the old window procedure to finish processing the message.
  462.             SubEdit = CallWindowProc(OldWindowProc, hWnd, uMsg, wParam, lParam)
  463.     End Select
  464.  

There are some other changes I would make as well just for consistency and matching of variable typing but right now your code works quite well and I am impressed. If I am ever in a spot where I have some more time (after the ODBC, SQLite, and MySQL libraries) then I will definitely look into this. It could find a nice slot in my Win32 GUI library using Common Controls v6!

P.S., As a bonus, I've put the $VERSIONINFO comment in there to use v6 of Common Controls (if you are on the latest dev build!)
« Last Edit: September 09, 2021, 10:08:52 am by SpriggsySpriggs »
Shuwatch!

Offline mpgcan

  • Newbie
  • Posts: 26
    • View Profile
Re: InputBox
« Reply #11 on: September 11, 2021, 05:12:13 am »
Hi  Spriggsy thank you for a quick reply.  I ran your modified code in both QB64 32 and 64 bit
With the following interesting results:

1a) QB64x32 Version 1.5 Stable Release From git 3043116
After compiling there is a flicker (attempts to run and closes) .

1b) QB64x32 Version 1.6 Development Build From git ca32adf
After compiling there is a flicker (attempts to run and closes) .

2a) QB64x64 Version 1.5 Stable Release From git 3043116
The program compiles and runs however the subclass function "SubEdit" does not get called.

2b) QB64x64 Version 1.6 Development Build From git ca32adf
The program compiles and runs however the subclass function "SubEdit" does not get called.

In the pass I have seen issues when declaring libraries in QB64x32.  With my PC it seems some libraries need a specific declaration.
I regressed  library definitions as follows:
Code: QB64: [Select]
  1.  Declare Library "WIN2"
  2.         Function GetWindowProc%& () 'Windows procedure
  3.         Function GetSubEdit%& () 'Edit procedure
  4.     End Declare
  5.  
  6.     '   Declare CustomType Library
  7.     '       Function SetWindowLongPtr& Alias "SetWindowLongPtrA" (ByVal hwnd As _Offset, Byval nIndex As Long, Byval dwNewLong As Long)
  8.     '       Function GetWindowLongPtr& (ByVal hwnd As _Offset, Byval nIndex As Long)
  9.     '     Function CallWindowProc& Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, Byval hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
  10.     '   End Declare
  11.  
  12.     Declare Dynamic Library "user32"
  13.         Function CallWindowProc& Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, Byval hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
  14.         Function SendMessageA%& (ByVal hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
  15.         Function DefWindowProcA%& (ByVal hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
  16.         Sub PostQuitMessage (ByVal nExitCode As Long)
  17.         Function LoadCursorW%& (ByVal hInstance As _Offset, Byval lpCursorName As _Offset)
  18.         Function RegisterClassA~% (ByVal lpWndClass As _Offset)
  19.         Function CreateWindowExA%& (ByVal dwExStyle As Long, Byval lpClassName As _Offset, Byval lpWindowName As _Offset, Byval dwStyle As Long, Byval X As Long, Byval Y As Long, Byval nWidth As Long, Byval nHeight As Long, Byval hWndParent As _Offset, Byval hMenu As _Offset, Byval hInstance As _Offset, Byval lpParam As _Offset)
  20.         Function ShowWindow& (ByVal hWnd As _Offset, Byval nCmdShow As Long)
  21.         Function UpdateWindow& (ByVal hWnd As _Offset)
  22.         Function GetMessageA% (ByVal lpMsg As _Offset, Byval hWnd As _Offset, Byval wMsgFilterMin As _Unsigned Long, Byval wMsgFilterMax As _Unsigned Long)
  23.         Function TranslateMessage& (ByVal lpMsg As _Offset)
  24.         Function DispatchMessageA%& (ByVal lpmsg As _Offset)
  25.         Sub DestroyWindow (ByVal hWnd As _Offset)
  26.         Function PostMessageA%& (ByVal hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
  27.         Function SetFocus& (ByVal hWnd As _Offset)
  28.         Function GetWindowLongA& (ByVal hwnd As _Offset, Byval nIndex As Long)
  29.         Function SetWindowLongA& (ByVal hwnd As _Offset, Byval nIndex As Long, Byval dwNewLong As Long)
  30.     End Declare
  31.  
  32.     Declare Dynamic Library "kernel32"
  33.         Function GetModuleHandleA%& (ByVal lpModuleName%&)
  34.         Function GetLastError~& ()
  35.     End Declare
  36.  

Ran the new code with the following results:

1an) QB64x32 Version 1.5 Stable Release From git 3043116
Works as expected.
Test: At the first input popup enter 123qwer
Result: 123 displayed

1bn) QB64x32 Version 1.6 Development Build From git ca32adf
Works as expected.

2an) QB64x64 Version 1.5 Stable Release From git 3043116
The program compiles and runs however the subclass function "SubEdit" does not get called.
Test: At the first input popup enter 123qwer
Result: 123qwer displayed - function "SubEdit" not called.

2bn) QB64x64 Version 1.6 Development Build From git ca32adf
The program compiles and runs however the subclass function "SubEdit" does not get called.

I just found this https://www.qb64.org/wiki/DECLARE_DYNAMIC_LIBRARY
"Note: SUB procedures using CUSTOMTYPE LIBRARY API procedures inside may error. Try DYNAMIC with "KERNEL32"."
May explain what I observed with the above QB64x32

Back to the original problem how to run the  subclass function "SubEdit" on QB64x64

Note: My PC spec as follows:
 Windows 8.1
 Processor Intel(R) Core(TM) i3-3217U CPU 1.8 GHz
 Installed memory RAM 4GB
 System type: 64-bit Operating System, x64-based processor

P.S. I liked the bonus, $VERSIONINFO comment. Works fine in the dev builds.


Marked as best answer by mpgcan on October 11, 2021, 12:55:28 am

Offline mpgcan

  • Newbie
  • Posts: 26
    • View Profile
Re: InputBox
« Reply #12 on: October 11, 2021, 04:49:46 am »
With the release of QB64 v2.0  I have updated the input box code.   
I finally managed to get subclassing to compile on both 32 and 64 bit versions of QB64. Solution in part was to update appropriate functions. However real solution came with the beta releases of QB64 v2.0 allowing the 64bit input box version to compile. 

The following provides a quick overview of subclassing:
"Subclassing is a technique that allows an application to intercept and process messages sent or posted to a particular window before the window has a chance to process them. By subclassing a window, an application can augment, modify, or monitor the behavior of the window."

"An application subclasses a window by replacing the address of the window's original window procedure with the address of a new window procedure, called the subclass procedure. Thereafter, the subclass procedure receives any messages sent or posted to the window."

Save the following files and place them in your QB64 folder:

input_box_example.bas
Code: QB64: [Select]
  1. $VersionInfo:Comments=This uses Subclassing Controls!
  2.  
  3. '$Include:'input_box.BI'
  4.  
  5. '===Your program START ==========
  6. Screen _NewImage(400, 300, 32) '             Set main screen size
  7. Color _RGB(0, 0, 0), _RGB32(205, 238, 205) ' Set colors
  8. _Title "Main Program" ' Set your main program title
  9.  
  10. 'Examples:
  11.  
  12. 'Accepts alpha-numeric characters
  13. Print "=1a= " + InputBoxM("", "", "", "", "")
  14. Print "=1b= " + InputBox
  15.  
  16.  
  17. 'Allows alpha-numeric and extra valid characters.
  18. Print "=2a= " + InputBoxM("Any character:", "All A+", "", "A+", "")
  19. Print "=2b= " + InputBoxA("Any character:", "All A+")
  20.  
  21. 'Only numeric characters allowed
  22. Print "=3a= " + InputBoxM("Digits 0-9 only", "NUMERIC", "", "N", "")
  23. Print "=3b= " + InputBoxN("Digits 0-9 only", "NUMERIC")
  24.  
  25. 'Password entry. Display asterisk
  26. Print "=4a= " + InputBoxM("Asterisk mask:", "PASSWORD 1", "", "", "A")
  27. Print "=4b= " + InputBoxP("Asterisk mask:", "PASSWORD 1")
  28.  
  29. 'Password entry. Display bullet
  30. Print "=5a= " + InputBoxM("Bullet mask:", "PASSWORD 2", "", "", "B")
  31. Print "=5b= " + InputBoxPB("Bullet mask:", "PASSWORD 2")
  32.  
  33.  
  34. Print "=7= " + InputBoxM("Limit 4 characters:", "LIMIT", "4", "", "") '
  35. Print "=8= " + InputBoxM("Digits 0-9 and associated characters", "NUMERIC+", "", "N+", "")
  36.  
  37. Print "End"
  38. '===Your program END ============
  39.  
  40. '$Include:'input_box.BM'
  41.  

input_box.BI
Code: QB64: [Select]
  1. '###_start_BI_inputbox
  2. Do: Loop Until _ScreenExists 'run before using _WindowHandle or _title
  3. $If 64BIT Then
  4.     $If VERSION < 1.6 Then
  5.         $ERROR Requires Windows QB64 version 1.6 or above
  6.     $End If
  7.  
  8. $If 32BIT Then
  9.     $If VERSION < 1.5 Then
  10.         $ERROR Requires Windows QB64 version 1.5 or above
  11.     $End If
  12.  
  13. '===================================================================
  14. 'Input box for QB64 32 & 64 bit.
  15. 'The QB64 InputBoxM function displays a pop-up dialog box,
  16. 'prompting the user for input. It has OK and Cancel buttons.
  17. 'Function returns a text string containing the user's input
  18. 'when OK button clicked. Returns an empty text string when
  19. 'Cancel button is clicked.
  20. '
  21. ' mpg_input_box_1.bas
  22. ' MPG 9-10-2021
  23. '====================================================================
  24. 'InputBoxM function:
  25. 'InputBoxM (prompt$,title$,length$,numeric$,pwMask$)
  26. '
  27. 'prompt$  - text displayed above the edit control to prompt user e.g. "Please enter a number:".
  28. 'title$   - text displayed in the title bar of the InputBoxM.
  29. 'length$  - maximum number of characters a user allowed to enter.
  30. 'numeri$c - select numeric input. Options are:
  31. '            ""   Default, allows alpha-numeric characters.
  32. '            "A+" Allows alpha-numeric and extra valid characters.
  33. '            "N"   Numeric digits 0-9 only
  34. '            "N+"  Numeric digits 0-9 and associated characters.
  35. 'pwMask$  - select a password mask' Options are:
  36. '            ""  Default no mask
  37. '            "A" Password mask Asterisk
  38. '            "B" Password mask Bullet
  39. '
  40. ' Note1: All variables are optional. Unused variables use a null string.
  41. ' Note2: Pressing cancel button returns a null string e.g ""
  42. ' Note3: You can change default values e.g valid characters, length etc
  43. '        Defaults, located in "Sub inputbox_init" or "Function InputBoxM"
  44. '----------------------------------------------------------------------
  45. 'Predefined functions:
  46. 'Based on the above, five predefined InputBox functions are
  47. 'included as follows:
  48. '
  49. 'InputBox                    - Accepts alpha-numeric characters
  50. 'InputBoxA(prompt$, title$)  - Accepts any character input
  51. 'InputBoxN(prompt$, title$)  - Only numeric characters allowed
  52. 'InputBoxP(prompt$, title$)  - Password entry. Display asterisk
  53. 'InputBoxPB(prompt$, title$) - Password entry. Display bullet
  54. '
  55. ' Note1: Variables are optional. Unused variables use a null string
  56. '----------------------------------------------------------------------
  57. ' VB Inputbox has some limitations as does the edit control. This QB64
  58. ' input box addresses these limitation.
  59. ' 1) Default character length 0x7FFE to 0x7FFFFFFE os dependent.
  60. '    Added option to limit number of characters.
  61. ' 2) Default password mask was an asterisk. Added option to select
  62. '    asterisk or bullet.
  63. ' 3) Numeric characters limited to digits 0-9 added option to have
  64. '    extra associated characters e.g calculator +-%= etc.
  65. '
  66. ' Using subclassing added the following:
  67. ' 4) Disabled context menu (Mouse Right click) copy and paste .
  68. ' 5) Disabled copy and paste using  Ctrl+c Ctrl+v.
  69. ' 6) Disabled copy and paste using Ctrl+Shift+c Ctrl+Shift+v.
  70. ' 7) Disabled annoying beep when pressing enter key.
  71. ' 8) Character validation.
  72. ' 9) Invalid character user alert. Uses MessageBeep.
  73. '
  74. '----------------------------------------------------------------------
  75. ' Code based on ideas from the following references:
  76. ' 1) Windows buttons for 32 and 64 bit IDE
  77. '    https://www.qb64.org/forum/index.php?topic=3217.msg124966#msg124966
  78. ' 2) Base64 Encoding/Decoding with Windows, Mac, and Linux
  79. '    https://www.qb64.org/forum/index.php?topic=3214.msg124919#msg124919
  80. ' 3) Threading
  81. '    https://www.qb64.org/forum/index.php?topic=3865.msg132124#msg132124
  82. '=======================================================================
  83. '
  84. '-----------------------------------------------------------------------
  85. 'NOTE:
  86. 'Create a new file input_box_WIN.h with the following content (Remove the comments '):
  87. '---Start of file contents:---
  88.      
  89. 'ptrszint FUNC_WINDOWPROC(ptrszint*_FUNC_WINDOWPROC_OFFSET_HWND,uint32*_FUNC_WINDOWPROC_ULONG_UMSG,uptrszint*_FUNC_WINDOWPROC_UOFFSET_WPARAM,ptrszint*_FUNC_WINDOWPROC_OFFSET_LPARAM);
  90. 'LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  91. ' return FUNC_WINDOWPROC((ptrszint *) (& hwnd), & uMsg, & wParam, (ptrszint *) (& lParam));
  92. '}
  93. 'void * GetWindowProc() {
  94. ' return (void *) WindowProc;
  95. '}
  96.      
  97. 'ptrszint FUNC_SUBEDIT(ptrszint*_FUNC_SUBEDIT_OFFSET_HWND,uint32*_FUNC_SUBEDIT_ULONG_UMSG,uptrszint*_FUNC_SUBEDIT_UOFFSET_WPARAM,ptrszint*_FUNC_SUBEDIT_OFFSET_LPARAM);
  98. 'LRESULT CALLBACK SubEdit(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  99. ' return FUNC_SUBEDIT((ptrszint *) (& hwnd), & uMsg, & wParam, (ptrszint *) (& lParam));
  100. '}
  101. 'void * GetSubEdit() {
  102. ' return (void *) SubEdit;
  103. '}
  104.      
  105. '---End of file contents---
  106. 'Input box uses the input_box_WIN.h file to get the WindowProc and SubEdit . Note: Place above file in your working folder (The QB64 folder).
  107. '----------------------------------------------------------------
  108.      
  109. '==Note: InputBox requires this section to be added to your program.==========
  110. 'Creates a global variable ipb. Its corresponding elements, are also global.
  111. Dim Shared ipb As input_box_udt 'The input_box_udt user defined type
  112. inputbox_init '                  Define ipb structure and initialize elements
  113.      
  114. '*****Subclassing
  115. Dim Shared OldWindowProc As Long 'Global variable store old windows process
  116. '*****End Subclassing
  117.  
  118. '###_end_BI_inputbox
  119.  

input_box.BM
Code: QB64: [Select]
  1. '###_start_BM_inputbox
  2. '######--INPUT BOX FUNCTIONS and SUBS--######################################
  3.  
  4. '--inputbox_init:  Creates a user defined type. This type is used to create
  5. '                  global variable ipb. Its corresponding elements are
  6. '                  global. Some variables are initialised in this function.
  7.  
  8. Sub inputbox_init
  9.     Const FALSE = 0
  10.     Const TRUE = Not FALSE
  11.  
  12.     Type input_box_udt
  13.         buf As String * 64 '  Buffer to store entered characters
  14.         hw As _Offset '       Pointer to inputbox window
  15.         hwLabel As _Offset '  Pointer to label
  16.         hwb0 As _Offset '     Pointer button 0 - OK
  17.         hwb1 As _Offset '     Pointer button 1 - Cancel
  18.         hwe As _Offset '      Pointer to single line edit control
  19.  
  20.         pw_mask As Integer '   Enable password mask flag
  21.         pw_bullet As Integer ' Password mask bullet flag
  22.         max_length As Integer 'Maximum allowed characters
  23.  
  24.         caption_text As String 'Window title
  25.         prompt_text As String ' Instructions to user
  26.         valid_str As String '   All allowed characters
  27.  
  28.         button0_text As String ' Left button
  29.         button1_text As String ' Right button
  30.  
  31.         x As Long 'Position of inputbox
  32.         y As Long 'Position of inputbox
  33.         max_char_allowed As Long ' Maximum number of input characters allowed
  34.     End Type
  35.  
  36.     '---Set default values
  37.     ipb.button0_text = "OK"
  38.     ipb.button1_text = "Cancel"
  39.  
  40.     '---Center InputBox window
  41.     Dim As Long userwidth, userheight
  42.     userwidth = _DesktopWidth: userheight = _DesktopHeight 'get current screen resolution
  43.  
  44.     ipb.x = (userwidth \ 2 - 358 \ 2) - 3
  45.     ipb.y = (userheight \ 2 - 136 \ 2) - 29
  46. '-- END inputbox_init:
  47.  
  48. 'Predefined function 1: - Allow numeric and alpha characters
  49. Function InputBox$ ()
  50.     InputBox = InputBoxM("", "", "", "", "")
  51.  
  52. 'Predefined function 2: - Allows alpha-numeric and extra valid characters.
  53. Function InputBoxA$ (prompt As String, title As String)
  54.     InputBoxA = InputBoxM(prompt, title, "", "A+", "")
  55.  
  56. 'Predefined function 3: - Only numeric characters allowed.
  57. Function InputBoxN$ (prompt As String, title As String)
  58.     InputBoxN = InputBoxM(prompt, title, "", "N", "")
  59.  
  60. 'Predefined function 4: - Password entry. Display asterisk.
  61. Function InputBoxP$ (prompt As String, title As String)
  62.     InputBoxP = InputBoxM(prompt, title, "", "", "A")
  63.  
  64. 'Predefined function 5: - Password entry. Display bullet.
  65. Function InputBoxPB$ (prompt As String, title As String)
  66.     InputBoxPB = InputBoxM(prompt, title, "", "", "B")
  67.  
  68.  
  69.  
  70. '---Main input box
  71. Function InputBoxM$ (prompt As String, title As String, maxLength As String, numeric As String, pwMask As String)
  72.     Const FALSE = 0
  73.     Const TRUE = Not FALSE
  74.     Dim As String str1, str2, str3, str4
  75.  
  76.     '---Set prompt text
  77.     ipb.prompt_text = "Enter some text:" 'Default value
  78.     If prompt <> "" Then
  79.         ipb.prompt_text = prompt 'Label text. User instrutions
  80.     End If
  81.  
  82.     '---Set pop-up window title (caption) text
  83.     ipb.caption_text = "InputBox" 'Default value
  84.     If title <> "" Then
  85.         ipb.caption_text = title 'User pop-up window title
  86.     End If
  87.  
  88.     '--Set maximum number of characters allowed
  89.     ipb.max_length = 40 'Default allowed characters 40
  90.     If maxLength <> "" Then
  91.         ipb.max_length = Val(maxLength) 'Required length
  92.     End If
  93.  
  94.     '---Set allowed characters.
  95.     'numeric: Options are
  96.     '  ""    Default, allows alpha-numeric characters.
  97.     '  "A+"  Allows alpha-numeric and extra valid characters.
  98.     '  "N"   Numeric digits 0-9 only
  99.     '  "N+"  Numeric digits 0-9 and associated characters.
  100.  
  101.     str1 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 'Alpha
  102.     str2 = "0123456789 " ' numeric
  103.     str3 = Chr$(34) + "!$%^&()+{}[]:@#<>?" ' Extra associated alpha/numeric characters
  104.     str4 = "%^.+-*/=" '      Associated numeric characters e.g basic calculator
  105.  
  106.     ipb.valid_str = ""
  107.  
  108.     If numeric = "" Then '                  Set default alpha numeric characters
  109.         ipb.valid_str = str1 + str2 '       Valid alpha/numeric characters
  110.     End If
  111.     If numeric = "A+" Then '                Set allowed alpha numeric and extra characters
  112.         ipb.valid_str = str1 + str2 + str3 'Valid alpha/numeric and extra characters
  113.     End If
  114.     If numeric = "N" Then '    Set allowed numeric characters
  115.         ipb.valid_str = str2 ' Valid characters 0-9
  116.     End If
  117.     If numeric = "N+" Then '          Set numeric and associated characters.
  118.         ipb.valid_str = str2 + str4 ' Valid numeric and associated characters
  119.     End If
  120.  
  121.     '---Set mask option
  122.     ' ""  Default no mask
  123.     ' "A" Password mask asterisk
  124.     ' "B" Password mask Bullet
  125.     ipb.pw_mask = FALSE '                  Reset flag, disable password mask
  126.     ipb.pw_bullet = FALSE '                Reset bullet flag
  127.  
  128.     If pwMask = "A" Or pwMask = "B" Then ' Mask required
  129.         ipb.pw_mask = TRUE '               Set flag, mask required
  130.         If pwMask = "B" Then '             Bullet required
  131.             ipb.pw_bullet = TRUE '         Set bullet flag
  132.         End If
  133.     End If
  134.  
  135.     '---Get user input string
  136.     ipb_UserInput '              Run UserInput, data entered saved in buffer ipb.buf
  137.     InputBoxM = ipb_get_string ' Extract data from ipb.buf return string
  138.  
  139. '---Function Extract data from ipb.buf return input string
  140. Function ipb_get_string$ ()
  141.     Dim As String a
  142.  
  143.     'Extract string from buf
  144.     a = _Trim$(ipb.buf) '                  Remove spaces. Buffer contains a null terminated
  145.     a = Left$(a, InStr(a, Chr$(0)) - 1) '  string. Find position of null and extract
  146.     ipb.buf = "" '                         characters upto this null character. Clear buffer
  147.  
  148.     ipb_get_string = a 'Return clean string
  149.      
  150. '===Main Sub ===========================
  151. Sub ipb_UserInput
  152.     '--Constants
  153.     Const FALSE = 0
  154.     Const TRUE = Not FALSE
  155.  
  156.     Const IDC_ARROW = &H7F00
  157.     Const COLOR_WINDOW = 5
  158.      
  159.     Const WS_OVERLAPPED = 0
  160.     Const WS_CAPTION = &H00C00000
  161.     Const WS_SYSMENU = &H00080000
  162.     Const WS_VISIBLE = &H10000000
  163.     Const WS_CHILD = &H40000000
  164.     Const WS_TABSTOP = &H00010000
  165.     Const WS_EX_CLIENTEDGE = &H00000200
  166.     Const BS_PUSHBUTTON = 0
  167.     Const CW_USEDEFAULT = &H80000000
  168.     Const SW_SHOWDEFAULT = &HA
  169.  
  170.     Const ES_LEFT = 0
  171.     Const ES_NUMBER = &H2000
  172.  
  173.     Const WS_OVERLAPPEDWINDOW = WS_OVERLAPPED Or WS_CAPTION
  174.  
  175.     '--Types
  176.     Type POINT
  177.         As Long x
  178.         As Long y
  179.     End Type
  180.  
  181.     Type MSG
  182.         As _Offset hwnd
  183.         As _Unsigned Long message
  184.         As _Unsigned _Offset wParam 'unsigned pointer sized integer
  185.         As _Offset lParam '          pointer sized integer
  186.         As _Unsigned Long time
  187.         As POINT pt
  188.     End Type
  189.  
  190.     Type WNDCLASSA
  191.         As _Unsigned Long style
  192.         $If 64BIT Then
  193.             As String * 4 padding
  194.         $End If
  195.         As _Offset lpfnWndProc
  196.         As Long cbClsExtra, cbWndExtra
  197.         As _Offset hInstance, hIcon, hCursor, hbrBackground, lpszMenuName, lpszClassName
  198.     End Type
  199.  
  200.     '--Libaries
  201.  
  202.    
  203.     Declare Library "input_box_WIN"
  204.         Function GetWindowProc%& () 'Windows procedure address
  205.         '***Subclassing
  206.         Function GetSubEdit%& () '   Edit procedure address
  207.         '***End Subclassing
  208.     End Declare
  209.  
  210.     '***Subclassing
  211.         Function SetWindowLongPtr& Alias "SetWindowLongPtrA" (ByVal hwnd As _Offset, Byval nIndex As Long, Byval dwNewLong As Long)
  212.     End Declare
  213.     '***End Subclassing
  214.  
  215.     '  Declare CustomType Library
  216.     Declare Dynamic Library "user32"
  217.         Function CallWindowProc& Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, Byval hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
  218.         Function SendMessageA%& (ByVal hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
  219.         Function DefWindowProcA%& (ByVal hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
  220.         Sub PostQuitMessage (ByVal nExitCode As Long)
  221.         Function LoadCursorW%& (ByVal hInstance As _Offset, Byval lpCursorName As _Offset)
  222.         Function RegisterClassA~% (ByVal lpWndClass As _Offset)
  223.         Function CreateWindowExA%& (ByVal dwExStyle As Long, Byval lpClassName As _Offset, Byval lpWindowName As _Offset, Byval dwStyle As Long, Byval X As Long, Byval Y As Long, Byval nWidth As Long, Byval nHeight As Long, Byval hWndParent As _Offset, Byval hMenu As _Offset, Byval hInstance As _Offset, Byval lpParam As _Offset)
  224.         Function ShowWindow& (ByVal hWnd As _Offset, Byval nCmdShow As Long)
  225.         Function UpdateWindow& (ByVal hWnd As _Offset)
  226.         Function GetMessageA% (ByVal lpMsg As _Offset, Byval hWnd As _Offset, Byval wMsgFilterMin As _Unsigned Long, Byval wMsgFilterMax As _Unsigned Long)
  227.         Function TranslateMessage& (ByVal lpMsg As _Offset)
  228.         Function DispatchMessageA%& (ByVal lpmsg As _Offset)
  229.         Sub DestroyWindow (ByVal hWnd As _Offset)
  230.         Function PostMessageA%& (ByVal hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
  231.         Function SetFocus& (ByVal hWnd As _Offset)
  232.         Function GetWindowLongA& (ByVal hwnd As _Offset, Byval nIndex As Long)
  233.         Function SetWindowPos& (ByVal hWnd As _Offset, Byval hWndInsertAfter As _Offset, Byval X As Integer, Byval Y As Integer, Byval cx As Integer, Byval cy As Integer, Byval uFlags As _Offset)
  234.         Function SendMessageW%& (ByVal hWnd As _Offset, Byval Msg As _Unsigned Long, Byval wParam As _Unsigned _Offset, Byval lParam As _Offset)
  235.         '     Function SetWindowLongA& (ByVal hwnd As _Offset, Byval nIndex As Long, Byval dwNewLong As Long)
  236.         Sub MessageBeep (ByVal alert As _Unsigned Long)
  237.     End Declare
  238.  
  239.     Declare Dynamic Library "kernel32"
  240.         Function GetModuleHandleA%& (ByVal lpModuleName%&)
  241.         Function GetLastError~& ()
  242.     End Declare
  243.     '--End Libaries
  244.  
  245.  
  246.     '--Variables
  247.     Static registered As Integer 'Variables initialized to 0 (false) hence not registered. Value retained between functtion calls
  248.  
  249.     Dim hi As _Offset '   Handle to application instance
  250.     Dim wc As WNDCLASSA ' define wc as WNDCLASSEX structure
  251.     Dim msg As MSG
  252.  
  253.     Dim dummyL As Long '   Dummy variable
  254.     Dim dummyO As _Offset 'Dummy variable
  255.     Dim t0 As String '       Type of control
  256.     Dim t1 As String '       Title or controls text
  257.  
  258.     Dim MainClassName As String * 5
  259.     MainClassName = "main" + Chr$(0)
  260.  
  261.     Dim CrLf As String * 2 '     define as 2 byte STRING
  262.     CrLf = Chr$(13) + Chr$(10) ' carriage return and line feed
  263.  
  264.     Dim As String className '               Variable className stores name of our window class
  265.     className = "myWindowClass" + Chr$(0) ' Used in wc. which in turn is used to register window class with the system.
  266.  
  267.     hi = GetModuleHandleA(0) 'Handle to application instance
  268.  
  269.     '---Step 1: Registering the Window Class
  270.     'Fill out the members of WNDCLASSEX structure (wc) and call RegisterClassA
  271.  
  272.     wc.style = 0 '                            Class Styles (CS_*), not Window Styles (WS_*) This is usually be set to 0.
  273.     wc.lpfnWndProc = GetWindowProc '          Pointer to the window procedure for this window class. (see WIN.h)
  274.     wc.cbClsExtra = 0 '                       Amount of extra data allocated for this class in memory. Usually 0.
  275.     wc.cbWndExtra = 0 '                       Amount of extra data allocated in memory per window of this type. Usually 0.
  276.     wc.hInstance = hi '                       Handle to application instance .
  277.     wc.hIcon = 0 '                            Large (usually 32x32) icon shown when the user presses Alt+Tab. Set to 0
  278.     wc.hCursor = LoadCursorW(0, IDC_ARROW) '  Cursor that will be displayed over our window.
  279.     wc.hbrBackground = COLOR_WINDOW 'was +1   Background Brush to set the color of our window. '
  280.     wc.lpszMenuName = 0 '                     Name of a menu resource to use for the windows with this class.
  281.     wc.lpszClassName = _Offset(className) '   Name to identify the class with.
  282.  
  283.     If Not registered Then '   First time in funcion OK to register.
  284.         If RegisterClassA(_Offset(wc)) = 0 Then
  285.             Print "RegisterClassA failed:"; GetLastError
  286.             End
  287.         End If
  288.         registered = TRUE ' Class was registered
  289.     End If
  290.  
  291.     '--Step 2: Creating the Windows
  292.     'After registering the class, create a window with it using CreateWindowExA.
  293.  
  294.     'Note: A visible un-owned window gets a taskbar button. To hide the inputbox window taskbar button
  295.     'make the inputbox owned by our main applicationusing using  _WindowHandle instead of 0'
  296.     t1 = ipb.caption_text + Chr$(0) 'Window title
  297.     ipb.hw = CreateWindowExA(0, _Offset(className), _Offset(t1), WS_OVERLAPPEDWINDOW, ipb.x, ipb.y, 358, 130, _WindowHandle, 0, hi, 0): If 0 = ipb.hw Then System
  298.  
  299.     'Controls are just child windows. They have a procedure, a class etc... that is registered by the system.
  300.  
  301.     'Label
  302.     t0 = "STATIC" + Chr$(0) '        Window control is STATIC predefined class
  303.     t1 = ipb.prompt_text + Chr$(0) ' Label text
  304.     ipb.hwLabel = CreateWindowExA(0, _Offset(t0), _Offset(t1), WS_VISIBLE Or WS_CHILD, 9, 5, 372, 16, ipb.hw, 0, hi, 0): If 0 = ipb.hwLabel Then System
  305.  
  306.  
  307.     'OK Button 0
  308.     t0 = "BUTTON" + Chr$(0) '   Window control is BUTTON predefined class
  309.     t1 = ipb.button0_text + Chr$(0)
  310.     ipb.hwb0 = CreateWindowExA(0, _Offset(t0), _Offset(t1), WS_TABSTOP Or WS_VISIBLE Or WS_CHILD Or BS_PUSHBUTTON, 175, 65, 80, 23, ipb.hw, 0, hi, 0): If 0 = ipb.hwb0 Then System
  311.  
  312.     'Cancel button 1
  313.     t1 = ipb.button1_text + Chr$(0)
  314.     ipb.hwb1 = CreateWindowExA(0, _Offset(t0), _Offset(t1), WS_TABSTOP Or WS_VISIBLE Or WS_CHILD Or BS_PUSHBUTTON, 262, 65, 80, 23, ipb.hw, 0, hi, 0): If 0 = ipb.hwb1 Then System
  315.  
  316.     'Edit control
  317.     t0 = "EDIT" + Chr$(0)
  318.     '    t1 = "This is a edit control." + Chr$(0)
  319.     t1 = "" + Chr$(0)
  320.     ipb.hwe = CreateWindowExA(WS_EX_CLIENTEDGE, _Offset(t0), _Offset(t1), WS_VISIBLE Or WS_CHILD Or ES_LEFT, 12, 30, 330, 26, ipb.hw, 0, hi, 0): If 0 = ipb.hwe Then System
  321.  
  322.     '---Set character length
  323.     Const EM_LIMITTEXT = &H00C5
  324.     dummyO = SendMessageA(ipb.hwe, EM_LIMITTEXT, ipb.max_length, 0) 'Send message maxcharacters 8
  325.  
  326.     '----Enable and configure password mask.
  327.     'Note: The bullet is Unicode (9679 or 0x25CF ) use  SendMessageW. Use SendMessageA for old Asterisk (42)
  328.     Const EM_SETPASSWORDCHAR = &HCC
  329.     If ipb.pw_mask Then 'Input Mask required
  330.         If ipb.pw_bullet Then 'Set bullet
  331.             dummyO = SendMessageW(ipb.hwe, EM_SETPASSWORDCHAR, 9679, 0) 'Set bullet password mask
  332.         Else 'Standard *
  333.             dummyO = SendMessageA(ipb.hwe, EM_SETPASSWORDCHAR, 42, 0) 'Set Asterisk default mask
  334.         End If
  335.     Else 'Mask not required
  336.         dummyO = SendMessageA(ipb.hwe, EM_SETPASSWORDCHAR, 0, 0) '  Turn password mask off
  337.         ' dummyO = SendMessageW(ipb.hwe, EM_SETPASSWORDCHAR, 0, 0) '   Turn password mask off
  338.     End If
  339.  
  340.     '*****Subclassing
  341.     Const GWLP_WNDPROC = -4 ' Sets a new address for the window procedure
  342.     OldWindowProc = SetWindowLongPtr(ipb.hwe, GWLP_WNDPROC, ConvertOffset(GetSubEdit)) ' set your custom procedure
  343.     '*****End Subclassing
  344.  
  345.     'Display and Update window to ensure it has properly redrawn itself on the screen.
  346.     dummyL = ShowWindow(ipb.hw, SW_SHOWDEFAULT)
  347.     dummyL = UpdateWindow(ipb.hw)
  348.  
  349.     ' Force window to top and select with screen click
  350.     Const HWND_TOPMOST = -1 '      window above all others no focus unless active
  351.     Const SWP_NOSIZE = &H0001 '    ignores cx and cy size parameters
  352.     Const SWP_NOACTIVATE = &H0010 'does not activate window
  353.     dummyL = SetWindowPos(ipb.hw, HWND_TOPMOST, ipb.x, ipb.y, 0, 0, SWP_NOSIZE Or SWP_NOACTIVATE) 'force to top
  354.     _ScreenClick ipb.x + 160, ipb.y + 96 'Force focus and move cursor to left of ok button
  355.  
  356.     '-- Step 3: The Message Loop
  357.     While GetMessageA(_Offset(msg), 0, 0, 0) > 0 ' gets a message from our application's message queue.
  358.         dummyL = TranslateMessage(_Offset(msg)) '  performs some additional processing on keyboard events
  359.         dummyO = DispatchMessageA(_Offset(msg)) '  sends the message out to the window that the message was sent to
  360.     Wend
  361. '===End main function ===========================
  362.      
  363.      
  364. '-- Step 4: the Window Procedure
  365. Function WindowProc%& (hWnd As _Offset, uMsg As _Unsigned Long, wParam As _Unsigned _Offset, lParam As _Offset)
  366.     Const WM_CREATE = &H0001
  367.     Const WM_CLOSE = &H0010
  368.     Const WM_DESTROY = 2
  369.     Const WM_COMMAND = &H0111
  370.     Const BN_CLICKED = 0
  371.     Const WM_GETTEXT = &H000D
  372.     Const WM_SETFOCUS = &H0007
  373.     Dim dummyO As _Offset 'Dummy variable
  374.     Dim dummyL As Long '   Dummy variable
  375.  
  376.     Select Case uMsg
  377.  
  378.         Case WM_CLOSE
  379.             DestroyWindow (hWnd) 'Destroy window and child windows
  380.             WindowProc = 0
  381.  
  382.         Case WM_DESTROY
  383.             PostQuitMessage 0 ' Want to exit the program
  384.             WindowProc = 0
  385.  
  386.         Case WM_SETFOCUS
  387.             dummyL = SetFocus(ipb.hwe) 'Set Edit control focus
  388.             WindowProc = 0
  389.  
  390.         Case WM_COMMAND
  391.             '==============
  392.             If wParam = BN_CLICKED Then
  393.                 Select Case lParam
  394.                     'A button was clicked test each one
  395.                     '---Sandard buttons---
  396.                     Case ipb.hwb0 'OK button
  397.                         'Print "Button 0 pressed OK"
  398.                         'Get input text and copy to buffer (buf)
  399.                         dummyO = SendMessageA(ipb.hwe, WM_GETTEXT, Len(ipb.buf), _Offset(ipb.buf))
  400.                         'Print Len(buf)
  401.                         dummyO = PostMessageA(hWnd, WM_CLOSE, 0, 0) 'Close
  402.                         WindowProc = 0
  403.  
  404.                     Case ipb.hwb1 'Cancel button
  405.                         'Print "Button 1 pressed Cancel"
  406.                         ipb.buf = "" 'reset zero-length string ("").
  407.                         dummyO = PostMessageA(hWnd, WM_CLOSE, 0, 0) 'Close
  408.                         WindowProc = 0
  409.                         '---End standard buttons---
  410.                 End Select
  411.             Else
  412.                 'Not our message send back to system for processing
  413.                 WindowProc = DefWindowProcA(hWnd, uMsg, wParam, lParam)
  414.             End If
  415.             '=================
  416.             WindowProc = 0
  417.  
  418.         Case Else
  419.             'Not our message send back to system for processing
  420.             WindowProc = DefWindowProcA(hWnd, uMsg, wParam, lParam)
  421.     End Select
  422. '######--END INPUT BOX FUNCTIONS and SUBS--##################################
  423.      
  424. 'Ref https://www.qb64.org/forum/index.php?topic=1553.msg108409#msg108409
  425. 'Ref https://www.qb64.org/forum/index.php?topic=2905.msg121660#msg121660
  426.  
  427. Function ConvertOffset&& (value As _Offset)
  428.     Dim m As _MEM 'Define a memblock
  429.     m = _Mem(value) 'Point it to use value
  430.     $If 64BIT Then
  431.         Dim temp As _Integer64
  432.         'On 64 bit OSes, an OFFSET is 8 bytes in size.  We can put it directly into an Integer64
  433.         _MemGet m, m.OFFSET, temp
  434.         ConvertOffset = temp
  435.     $Else
  436.         Dim temp As Long
  437.         'However, on 32 bit OSes, an OFFSET is only 4 bytes.  We need to put it into a LONG variable first
  438.         _MemGet m, m.OFFSET, temp 'Like this
  439.         ConvertOffset = temp 'And then assign that long value to ConvertOffset&&
  440.     $End If
  441.     _MemFree m 'Free the memblock
  442.  
  443. '*****Subclass function
  444. Function SubEdit%& (hWnd As _Offset, uMsg As _Unsigned Long, wParam As _Unsigned _Offset, lParam As _Offset)
  445.     Const FALSE = 0
  446.     Const TRUE = Not FALSE
  447.     Const WM_NCDESTROY = &H82
  448.     Const GWLP_WNDPROC = -4 ' Sets a new address for the window procedure
  449.     Const WM_CONTEXTMENU = 123
  450.     Const WM_CHAR = 258
  451.     Const VK_RETURN = &H000D
  452.     Const BM_CLICK = 245
  453.     Const MB_ICONWARNING = &H30
  454.  
  455.     Dim processed As Integer 'Key valid and processed
  456.     Dim dummyL As Long '      Dummy variable
  457.     Dim dummyO As _Offset '   Dummy variable
  458.     Dim ChrValid As Integer ' Character is valid
  459.     Dim i As Integer
  460.  
  461.     'Set initial condition
  462.     processed = FALSE 'Set initial value. No key processed
  463.  
  464.     'If we're being destroyed, restore the original WindowProc.
  465.     '*****Subclassing
  466.     If uMsg = WM_NCDESTROY Then
  467.         dummyL = SetWindowLongPtr(hWnd, GWLP_WNDPROC, OldWindowProc)
  468.     End If
  469.     '*****End Subclassing
  470.  
  471.     'Disable context menu popup. Prevents a paste operation
  472.     If uMsg = WM_CONTEXTMENU Then
  473.         processed = TRUE '   Key processed nothing elese to do
  474.     End If
  475.  
  476.     'Disable Ctrl+V   Prevents a paste operation
  477.     If uMsg = WM_CHAR And wParam = 22 Then
  478.         processed = TRUE '   Key processed nothing elese to do
  479.     End If
  480.  
  481.     'Disable Enter key. Prevents anoying beep. Force enter to produce OK button click
  482.     'Pressing the enter key with Edit Control  in focus.
  483.     'Default behavior is to produce an annoying beep this is
  484.     'because enter is an invalid key for this control.
  485.  
  486.     If uMsg = WM_CHAR And wParam = VK_RETURN Then 'Enter key pressed
  487.         dummyO = SendMessageA(ipb.hwb0, BM_CLICK, 0, 0) ' Send message (button click) to OK button
  488.         processed = TRUE '   Key processed nothing elese to do
  489.     End If
  490.  
  491.     'Valid character section all others invalid
  492.     If uMsg = WM_CHAR Then 'There is a character to process
  493.         'Print wParam
  494.  
  495.         ChrValid = FALSE '                Set initial value
  496.         For i = 1 To Len(ipb.valid_str) ' Scan valid characters and backspace=8
  497.             If wParam = Asc(Mid$(ipb.valid_str, i, 1)) Or wParam = 8 Then
  498.                 ChrValid = TRUE ' Key in valid range. Allow default processing
  499.             End If
  500.         Next
  501.  
  502.         If Not ChrValid Then ' Invalid character remove by setting processed
  503.             processed = TRUE ' true flag. Forceses return 0 for invalid keys'
  504.             'Alert user.
  505.             If Not (wParam = VK_RETURN) Then ' Note: Return key is valid.
  506.                 MessageBeep MB_ICONWARNING '   For invalid characters
  507.             End If '                           create a "ding" to alert user.
  508.         End If
  509.     End If
  510.     'End valid character
  511.  
  512.     'Set return value
  513.     If processed Then 'A key was processed
  514.         SubEdit = 0 '  Return 0 no further processing required
  515.     Else '             No key processed pass onto default  processing
  516.         '*****Subclassing
  517.         SubEdit = CallWindowProc(OldWindowProc, hWnd, uMsg, wParam, lParam)
  518.         '*****End Subclassing
  519.     End If
  520.  
  521. '*****End Subclass functionn
  522. '###_start_BM_inputbox
  523.  
input_box_WIN.h
Code: QB64: [Select]
  1. ptrszint FUNC_WINDOWPROC(ptrszint*_FUNC_WINDOWPROC_OFFSET_HWND,uint32*_FUNC_WINDOWPROC_ULONG_UMSG,uptrszint*_FUNC_WINDOWPROC_UOFFSET_WPARAM,ptrszint*_FUNC_WINDOWPROC_OFFSET_LPARAM);
  2.  
  3. LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  4.  return FUNC_WINDOWPROC((ptrszint *) (& hwnd), & uMsg, & wParam, (ptrszint *) (& lParam));
  5. }
  6.  
  7. void * GetWindowProc() {
  8.  return (void *) WindowProc;
  9. }
  10.  
  11.  
  12. ptrszint FUNC_SUBEDIT(ptrszint*_FUNC_SUBEDIT_OFFSET_HWND,uint32*_FUNC_SUBEDIT_ULONG_UMSG,uptrszint*_FUNC_SUBEDIT_UOFFSET_WPARAM,ptrszint*_FUNC_SUBEDIT_OFFSET_LPARAM);
  13.  
  14. LRESULT CALLBACK SubEdit(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  15.  return FUNC_SUBEDIT((ptrszint *) (& hwnd), & uMsg, & wParam, (ptrszint *) (& lParam));
  16. }
  17.  
  18. void * GetSubEdit() {
  19.  return (void *) SubEdit;
  20. }
  21.  

Compile input_box_example.bas


Offline Petr

  • Forum Resident
  • Posts: 1720
  • The best code is the DNA of the hops.
    • View Profile
Re: InputBox
« Reply #13 on: October 11, 2021, 10:46:23 am »
@mpgcan

Perfect work!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: InputBox
« Reply #14 on: October 11, 2021, 11:59:12 am »
Roger that now that I know what to fix.

Oh boy 12 noon here, the bewitching hour for forum. Will my post get through?