Announcement

Collapse
No announcement yet.

How to return a string from a CALLBACK FUNCTION?

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • How to return a string from a CALLBACK FUNCTION?

    I wanted a "standalone" routine that would let the user select a printer, BUT NOT actually do an ATTACH at the same time. I just want the name (string).
    XPRINT ATTACH CHOOSE performs the Attach automatically, and for some drivers, displays one or two startup dialogs, which I don't want.
    So, after looking around and not finding anything similar (which surprised me, there are so many other printer functions in the forums), I figured I would have to "roll my own".

    Seemed that a simple listbox would suffice, so I swiped and modified one of the code postings in the recent thread on "slow SAPI".
    The program displays a list of installed printers, lets the user select one, and reports the one selected. (For fun, I retained the speaking function.)


    To refresh my knowledge on CALLBACK functions, I read the Help for Callbacks, but it only mentions return values that are numerical.
    I reviewed dozens of programs, and observed that the bulk of CALLBACK functions perform an action, like loading a textbox or other control, or changing a setting (eg, dialog size or color, etc), or display a message. Few return a numerical value (FUNCTION = x, where x > 0), and I could not find any that return a string to a variable in the caller (the way a normal FUNCTION returns a string value)... Most likely, code exists, but I just didn't find any.

    Without an obvious way to return the listbox selection, I established a Global that receives the array index of the selection, and the caller uses that to return the selected item's text string.

    Knowing that some people claim to write complex programs without any Globals, it seems to me that this technique must be a "kludge".

    Are there other ways besides Globals to return a string from a CALLBACK?

    Thanks,
    -John



    Code:
    'TEST GetUserPrinterSelection.bas  
    '=============================================
    'Alternative to XPRINT ATTACH CHOOSE, which I'm finding tough
    ' to get a printer string from, without having the driver startup.
    '---------------------------------------------
    
    #Compile Exe
    #Dim All
    
    #Include "win32api.inc"
    #Include "SApi.inc"
    
    
    %Listbox01 = 101
    
    Global gsUserSelectedPrinter As String
    Global glUserSelectedPrinter As Long
    
    
    Function PBMain () As Long
       Local sRet As String
       sRet = GetUserPrinterSelection()
       ? "GetUserPrinterSelection = " & sRet ,, "GetUserPrinterSelection"
    End Function
    
    Function GetUserPrinterSelection() As String
       Local sUserSelPrinter As String
       Local i, lNumPrinters, lNumMatching As Long
       Local sPrinterNames(), sPrinter As String
    
       lNumPrinters = PrinterCount
       ReDim sPrinterNames(1 To lNumPrinters)
    
       For i = 1 To lNumPrinters
          sPrinterNames(i) = Printer$(Name, i)
       Next i
    
       'Create a dialog...
       Local hDlg As Dword
       Dialog New %HWND_Desktop, "Speaking list", , , 250, 110, %WS_Caption Or %WS_MinimizeBox Or %WS_SysMenu To hDlg
       Control Add ListBox, hDlg, %Listbox01, sPrinterNames(), 5, 5, 170, 105, %LBS_Notify Or %WS_VScroll, %WS_Ex_Left Or %WS_Ex_StaticEdge
       Dialog Show Modal hDlg Call DlgProc
    
       sUserSelPrinter = sPrinterNames(glUserSelectedPrinter)  ' remember what their choice  *** (Is this the right way?) ***
       Function = sUserSelPrinter
    End Function
    
    
    CallBack Function DlgProc
       Static pISpVoice As ISpVoice
       Static wsText    As WStringZ * 64
       Local  index     As Long
    
       Select Case Cb.Msg
       Case %WM_InitDialog
          pISpVoice = NewCom "SAPI.SpVoice"
    
       Case %WM_Command
          If GetAsyncKeyState(%VK_ESCAPE) Then 'And &H8000 then     'jhm mod to allow closing via ESC key
             Function = 1
             Dialog End Cb.Hndl
          End If
    
          Select Case Cb.Ctl
          Case Cb.WParam           ' jhm mod; probably not the best, but it works! allows selection via ENTER key.
             ListBox Get Select Cb.Hndl, %Listbox01 To index
             ListBox Get Text Cb.Hndl, %Listbox01, index To wsText
             If (GetAsyncKeyState(%VK_RETURN) And &H8000) Then      
                pISpVoice.Speak(wsText, %SPF_ASYNC Or %SPF_PURGEBEFORESPEAK, ByVal %NULL)
             End If
    
          Case %Listbox01
             Select Case Cb.CtlMsg
             Case %LBN_SelChange
                ListBox Get Select Cb.Hndl, %Listbox01 To index
                ListBox Get Text Cb.Hndl, %Listbox01, index To wsText
                If (GetAsyncKeyState(%VK_DOWN) And &H8000) Or _
                   (GetAsyncKeyState(%VK_UP)   And &H8000) Then  'Arrow key down
                   'WinBeep(2500, 100)
                   ' BEEP '  see post #26 where Pierre used PlaySound  'this is Pierre's code, not mine.
                   wsText = "<silence msec='500'/>" & wsText
                End If
                pISpVoice.Speak(wsText, %SPF_ASYNC Or %SPF_PURGEBEFORESPEAK, ByVal %NULL)
             End Select
          End Select
          gsUserSelectedPrinter = wsText     '
          glUserSelectedPrinter = index         'jhm added... but is it the right/best way?
       End Select
    End Function




  • #2
    PrinterCount and Printer$ perhaps to a Control Add ComboBox or listbox.
    https://duckduckgo.com instead of google

    Comment


    • #3
      John,

      Globals have their place, if you cannot use a LOCAL due to scope issues and there is no way to pass a value back from the callback, a GLOBAL is the right choice.
      hutch at movsd dot com
      The MASM Forum

      www.masm32.com

      Comment


      • #4
        John, in the code you posted you could move
        Code:
        sRet = GetUserPrinterSelection()
        ? "GetUserPrinterSelection = " & sRet ,, "GetUserPrinterSelection"
        to %WM_INITDIALOG but you would have to create your Window in PBMAIN and start the Callback from there as well. A couple of variable declarations may have to switch as well.
        Rod
        "To every unsung hero in the universe
        To those who roam the skies and those who roam the earth
        To all good men of reason may they never thirst " - from "Heaven Help the Devil" by G. Lightfoot

        Comment


        • #5
          Originally posted by John Montenigro View Post
          Are there other ways besides Globals to return a string from a CALLBACK?
          Code:
          FUNCTION StringPocket(OPTIONAL strdata AS STRING) AS STRING
              STATIC rememberthis AS STRING
              IF ISMISSING(strdata) = 0 THEN rememberthis = strdata
              FUNCTION = rememberthis
          END FUNCTION
          The world is strange and wonderful.*
          I reserve the right to be horrifically wrong.
          Please maintain a safe following distance.
          *wonderful sold separately.

          Comment


          • #6
            @Mike,
            Thanks - am I doing it wrong? A couple of lines below "'Create a dialog..." I'm creating a listbox and loading it via an array...?

            @Steve,
            OK, that's reassuring to know. I was concerned I was "breaking a rule".

            @Rod,
            Hmmm, I understand your post. However, I am hoping to have the routine "modularized" so that I can add it to an INC file as part of my "library". I hope I'm not twisting things up.

            @Kurt,
            That's a cool technique, and I intend to borrow it profusely! I don't often use Statics, or Optional params, so this is a good thought to hold on to. I didn't think CALLBACK FUNCTIONs could take parameters, so I'll go back and re-read the Help tonight.

            Thanks to all of you for your feedback!!!!
            -John

            Comment


            • #7
              If only Enter was wanted for a selection this would work.
              How to trap left click on a listbox without modeless or subclass?
              Talk can be put back in.
              Code:
              #INCLUDE "win32api.inc"
              %Listbox01 = 101
              GLOBAL gsUserSelectedPrinter AS STRING
              
              FUNCTION PBMAIN AS LONG   'speaking list
                 LOCAL i, lNumPrinters AS LONG
                 LOCAL sPrinterNames() AS STRING
                 lNumPrinters = PRINTERCOUNT
                 REDIM sPrinterNames(1 TO lNumPrinters)
                 FOR i = 1 TO lNumPrinters
                    sPrinterNames(i) = PRINTER$(NAME, i)
                 NEXT i
                 LOCAL hDlg AS DWORD     'Create a dialog...
                 DIALOG NEW %HWND_DESKTOP, "Select Printer", , , 250, 110, %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_SYSMENU TO hDlg
                 CONTROL ADD LISTBOX, hDlg, %Listbox01, sPrinterNames(), 5, 5, 170, 105, %LBS_NOTIFY OR %WS_VSCROLL, %WS_EX_LEFT OR %WS_EX_STATICEDGE
                 DIALOG SHOW MODAL hDlg CALL DlgProc
                 IF LEN(gsUserSelectedPrinter) THEN ? gsUserSelectedPrinter,,"Selected" ELSE BEEP
              END FUNCTION
              
              CALLBACK FUNCTION DlgProc
              
                 LOCAL  index     AS LONG
              
                 SELECT CASE AS LONG CB.MSG
              
                  CASE %WM_INITDIALOG
                   index = 1
                   LISTBOX SELECT CB.HNDL,%Listbox01,index
              
                  CASE %WM_COMMAND
                    IF GetAsyncKeyState(%VK_ESCAPE) THEN
                       FUNCTION = 1
                       DIALOG END CB.HNDL
                    END IF
              
                    SELECT CASE CB.CTL
                     CASE %ListBox01
                      CASE CB.WPARAM
                       IF (GetAsyncKeyState(%VK_RETURN) AND &H8000) THEN
                        LISTBOX GET SELECT CB.HNDL, %Listbox01 TO index
                        LISTBOX GET TEXT CB.HNDL, %Listbox01, index TO gsUserSelectedPrinter
                        DIALOG END CB.HNDL
                       END IF
                     END SELECT
              
                 END SELECT
              
              END FUNCTION
              https://duckduckgo.com instead of google

              Comment


              • #8
                One with no global variable used for demo purpose...

                Click image for larger version

Name:	print2.png
Views:	106
Size:	13.0 KB
ID:	781978

                Code:
                #COMPILE EXE '#Win 9.07#
                #DIM ALL
                #INCLUDE "Win32Api.inc"
                
                'No global variable used for demo purpose
                
                %ButtonSelect = 101
                %ButtonCancel = 102
                %PrinterList  = 201
                '_____________________________________________________________________________
                
                CALLBACK FUNCTION DlgProc
                 LOCAL sPrinter AS STRING
                 LOCAL index    AS LONG
                
                 SELECT CASE CBMSG
                
                   CASE %WM_COMMAND
                     SELECT CASE CBCTL
                
                       CASE %ButtonSelect
                         IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                           LISTBOX GET SELECT CBHNDL, %PrinterList TO index
                           LISTBOX GET TEXT CBHNDL, %PrinterList, index TO sPrinter
                           DIALOG END CBHNDL, STRPTR(sPrinter) 'Return a pointer to sPrinter
                         END IF
                
                       CASE %IDOK 'IDOK = enter key
                         IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                           IF GetFocus() = GetDlgItem(CBHNDL, %ButtonCancel) THEN 'Enter key on cancel button
                             PostMessage(CBHNDL, %WM_COMMAND, MAK(DWORD, %ButtonCancel, %BN_CLICKED), 0)
                           ELSE 'Enter key on listbox or select button
                             PostMessage(CBHNDL, %WM_COMMAND, MAK(DWORD, %ButtonSelect, %BN_CLICKED), 0)
                           END IF
                         END IF
                
                       CASE %ButtonCancel, %IDCANCEL 'IDCANCEL = ESC key
                         IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                           sPrinter = "Esc or cancel button used"
                           DIALOG END CBHNDL, STRPTR(sPrinter) 'Return a pointer to sPrinter
                         END IF
                
                     END SELECT
                
                   CASE %WM_SYSCOMMAND
                     IF (CBWPARAM AND &HFFF0) = %SC_CLOSE THEN '[x] button or Alt-F4 used
                       sPrinter = "Alt-F4 or [x] button used"
                       DIALOG END CBHNDL, STRPTR(sPrinter) 'Return a pointer to sPrinter
                     END IF
                
                  END SELECT
                
                END FUNCTION
                '_____________________________________________________________________________
                
                FUNCTION PBMAIN()
                 LOCAL zPrinterDefault AS ASCIIZ * %MAX_FNAME 'File name component is 256 chars max
                 LOCAL zPrinter        AS ASCIIZ * %MAX_FNAME
                 LOCAL pz              AS ASCIIZ POINTER
                 LOCAL hDlg            AS DWORD
                 LOCAL hIcon           AS DWORD
                 LOCAL index           AS LONG
                
                 DIALOG NEW %HWND_DESKTOP, "Select printer", , , 160, 105, %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_SYSMENU, %WS_EX_LEFT TO hDlg
                
                 CONTROL ADD LISTBOX, hDlg, %PrinterList, , 5, 5, 150, 80
                
                 FOR index = 1 TO PRINTERCOUNT
                   LISTBOX ADD hDlg, %PrinterList, PRINTER$(NAME, index)
                 NEXT
                 LISTBOX SELECT hDlg, %PrinterList, 1
                
                 GetDefaultPrinter(zPrinterDefault, SIZEOF(zPrinterDefault))
                 FOR index = 1 TO PRINTERCOUNT 'If any, select default printer in list
                   LISTBOX GET TEXT hDlg, %PrinterList, index TO zPrinter
                   IF zPrinter = zPrinterDefault THEN LISTBOX SELECT hDlg, %PrinterList, index
                 NEXT
                
                 CONTROL ADD BUTTON, hDlg, %ButtonSelect, "&Select", 20, 85, 50, 15
                
                 CONTROL ADD BUTTON, hDlg, %ButtonCancel, "&Cancel", 90, 85, 50, 15
                
                 hIcon = ExtractIcon(GetModuleHandle(""), "Shell32.dll", 16)
                 SetClassLong(hDlg, %GCL_HICON, hIcon)
                
                 DIALOG SHOW MODAL hDlg CALL DlgProc TO pz
                 zPrinter = @pz
                 MSGBOX zPrinter, , "Printer choice"
                
                 DestroyIcon(hIcon)
                
                END FUNCTION
                '_____________________________________________________________________________
                '
                Last edited by Pierre Bellisle; 11 Jun 2019, 08:22 PM.

                Comment


                • #9
                  Why bother with STATIC when all you need is GLOBAL.
                  "Declare static variables inside a Sub, Function, Method, or Property. Static variables retain their values as long as the program is running." "The STATIC statement is valid only inside a procedure. Static variables retain their values even after the procedure ends. A static variable is local to its procedure, and can have the same name as other variables in other parts of the program without conflict."

                  If you can track thousands of IDs then you can track thousands of GLOBALs. I'm just saying...

                  Comment


                  • #10
                    Or..
                    Code:
                    'TEST GetUserPrinterSelection.bas
                    '=============================================
                    'Alternative to XPRINT ATTACH CHOOSE, which I'm finding tough
                    ' to get a printer string from, without having the driver startup.
                    '---------------------------------------------
                    
                    #Compile Exe
                    #Dim All
                    #Include "win32api.inc"
                    
                    DECLARE FUNCTION GetDefaultPrinter LIB "WINSPOOL.DRV" ALIAS "GetDefaultPrinterA" (pszBuffer AS ASCIIZ, pcchBuffer AS DWORD) AS LONG
                    
                    %Listbox01 = 101
                    '------------------/
                    
                    Function PBMain () As Long
                       Local sRet As String
                       sRet = GetUserPrinterSelection()
                       If Len(sRet) Then
                          ? "GetUserPrinterSelection = " & sRet ,, "GetUserPrinterSelection"
                       else
                          Local pszBuffer As Asciiz * %MAX_FNAME
                          Local pcchBuffer As Dword  :  pcchBuffer = %MAX_FNAME
                          GetDefaultPrinter(pszBuffer, pcchBuffer)
                          ? "GetUserPrinterSelection = " & Trim$(pszBuffer) ,, "Default Printer"
                        End If
                    End Function
                    '------------------/
                    
                    Function GetUserPrinterSelection() As String
                       Local i, lNumPrinters, Index As Long
                       Local sPrinterNames() As String
                    
                       lNumPrinters = PrinterCount
                       ReDim sPrinterNames(1 To lNumPrinters)
                    
                       For i = 1 To lNumPrinters
                          sPrinterNames(i) = Printer$(Name, i)
                       Next i
                    
                       'Create a dialog...
                       Local hDlg As Dword
                       Dialog New %HWND_Desktop, "Speaking list", , , 250, 110, %WS_Caption Or %WS_MinimizeBox Or %WS_SysMenu To hDlg
                       Control Add ListBox, hDlg, %Listbox01, sPrinterNames(), 5, 5, 170, 105, %LBS_Notify Or %WS_VScroll, %WS_Ex_Left Or %WS_Ex_StaticEdge
                       Dialog Show Modal hDlg Call DlgProc To Index ' index returned by Dialog End in Callback
                    
                      If Index Then Function = sPrinterNames(Index)
                    End Function
                    '------------------/
                    
                    CallBack Function DlgProc() As Long
                       Local  index As Long
                    
                       Select Case Cb.Msg
                       Case %WM_InitDialog
                       Case %WM_SysCommand
                          If (CbWParam AND &HFFF0) = %SC_CLOSE Then                   ' User clicked [X] close btn
                             ListBox Get Select Cb.Hndl, %Listbox01 To index
                             Dialog End Cb.Hndl, index
                          End If
                    
                       Case %WM_Command
                          Select Case Cb.Ctl
                          Case %IDOK, %IDCANCEL                                       ' User pressed enter or escape key
                             ListBox Get Select Cb.Hndl, %Listbox01 To index
                             Dialog End Cb.Hndl, index
                    
                          Case %Listbox01
                             Select Case Cb.CtlMsg
                             Case %LBN_SelChange                                       ' Arrow key or click selection
                                ListBox Get Select Cb.Hndl, %Listbox01 To index
                             End Select
                          End Select
                       End Select
                    End Function
                    '------------------/
                    Rgds, Dave

                    Comment


                    • #11
                      Modeless.
                      Mouse clicks or keystrokes can be processed before or after the listbox.
                      This example calls Success if an item is selected with a left click or the Enter key is pressed.
                      It doesn't check for the control id in the message loop because there is only 1-control on the form.
                      Code:
                      #INCLUDE "win32api.inc"
                      %Listbox01 = 1001
                      GLOBAL gsUserSelectedPrinter AS STRING
                      
                      FUNCTION PBMAIN AS LONG
                         LOCAL msg AS TagMsg    'for message pump
                         LOCAL i, lNumPrinters AS LONG
                         LOCAL sPrinterNames() AS STRING
                         lNumPrinters = PRINTERCOUNT
                         REDIM sPrinterNames(1 TO lNumPrinters)
                         FOR i = 1 TO lNumPrinters
                            sPrinterNames(i) = PRINTER$(NAME, i)
                         NEXT i
                         LOCAL hDlg AS DWORD     'Create a dialog...
                         DIALOG NEW %HWND_DESKTOP, "Click printer or use keys", , , 250, 110, %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_SYSMENU TO hDlg
                         CONTROL ADD LISTBOX, hDlg, %Listbox01, sPrinterNames(), 5, 5, 170, 105, %LBS_NOTIFY OR %WS_VSCROLL, %WS_EX_LEFT OR %WS_EX_STATICEDGE
                      
                         DIALOG SHOW MODELESS hDlg CALL DlgProc
                         WHILE GetMessage(Msg, %NULL, 0, 0)
                          IF IsDialogMessage(hDlg, Msg) = %FALSE THEN
                           TranslateMessage Msg
                           DispatchMessage Msg
                          END IF
                         IF Msg.message = %WM_LBUTTONDOWN THEN Success hDlg
                         LOOP WHILE ISWIN(hDlg)
                      
                         IF LEN(gsUserSelectedPrinter) THEN ? gsUserSelectedPrinter,,"Thank you"
                      END FUNCTION
                      
                      SUB Success(hDlg AS DWORD)
                       LOCAL index AS LONG
                       LISTBOX GET SELECT hDlg, %Listbox01 TO index
                       LISTBOX GET TEXT hDlg, %Listbox01, index TO gsUserSelectedPrinter
                       DIALOG END hDlg
                      END SUB
                      
                      CALLBACK FUNCTION DlgProc
                       LOCAL index AS LONG
                         SELECT CASE AS LONG CB.MSG
                          CASE %WM_INITDIALOG
                           index = 1
                           LISTBOX SELECT CB.HNDL,%Listbox01,index
                          CASE %WM_COMMAND
                            IF GetAsyncKeyState(%VK_ESCAPE) THEN DIALOG END CB.HNDL
                            SELECT CASE CB.CTL
                             CASE %ListBox01
                              CASE CB.WPARAM
                              IF (GetAsyncKeyState(%VK_RETURN) AND &H8000) THEN Success(CB.HNDL)
                             END SELECT
                         END SELECT
                      END FUNCTION
                      https://duckduckgo.com instead of google

                      Comment


                      • #12
                        Pierre,
                        Got it! I see how you put in the CALLBACK:
                        Code:
                        DIALOG END CBHNDL, STRPTR(sPrinter) 'Return a pointer to sPrinter
                        and in the caller:
                        Code:
                        DIALOG SHOW MODAL hDlg CALL DlgProc TO pz
                        
                        MSGBOX @pz, , "Printer choice"
                        That makes sense to me, although I would never have known how to create that scheme myself. But after re-reading the Help on CALLBACK Return Values, I begin to grasp it.
                        Very tidy!
                        Thanks!

                        Jim, I appreciate the affirmation that it's OK to use Globals. Now I feel more comfortable with 2 ways of doing it.

                        Dave, in concert with Pierre, returning a pointer to the string.

                        LOL, Mike. wheee, what a ride! What a scheme!! Modeless with some very interesting handling! OK, I grok what you're doing...but I'm not sure why. What are the advantages of this scheme?

                        Geez, you guys are fantastic! Thanks for all the follow-up. I'm learning A LOT through this!!!

                        Thanks,
                        -John

                        Comment


                        • #13
                          The advantage using modal is the user can click on a line without having to click another button to say that is the item they want.
                          https://duckduckgo.com instead of google

                          Comment


                          • #14
                            No CALLBACK or GLOBAL
                            Code:
                            #INCLUDE "win32api.inc"
                            FUNCTION PBMAIN AS LONG
                               LOCAL msg                       AS TagMsg
                               LOCAL i,lNumPrinters,hDlg,index AS LONG
                               LOCAL sPrinternames(),sprinter  AS STRING
                               lNumPrinters = PRINTERCOUNT
                               REDIM sPrinterNames(1 TO lNumPrinters)
                               FOR i = 1 TO lNumPrinters
                                  sPrinterNames(i) = PRINTER$(NAME, i)
                               NEXT i
                               DIALOG NEW %HWND_DESKTOP, "Click printer or use keys", , , 250, 110, %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_SYSMENU TO hDlg
                               CONTROL ADD LISTBOX, hDlg, 1001, sPrinterNames(), 5, 5, 170, 105, %LBS_NOTIFY OR %WS_VSCROLL, %WS_EX_LEFT OR %WS_EX_STATICEDGE
                               LISTBOX SELECT hdlg,1001,1
                               DIALOG SHOW MODELESS hDlg
                               WHILE GetMessage(Msg, %NULL, 0, 0)
                                IF IsDialogMessage(hDlg, Msg) = %FALSE THEN
                                 TranslateMessage Msg
                                 DispatchMessage Msg
                                END IF
                                IF Msg.message = %WM_LBUTTONDOWN OR msg.wparam = 13 THEN
                                 LISTBOX GET SELECT hDlg, 1001 TO index
                                 LISTBOX GET TEXT hDlg, 1001, index TO sPrinter
                                 DIALOG END hDlg
                                ELSEIF msg.wparam = 27 THEN
                                  DIALOG END hDlg
                                END IF
                               LOOP WHILE ISWIN(hDlg)
                               ? sPrinter,,"Thank you"
                            END FUNCTION
                            https://duckduckgo.com instead of google

                            Comment


                            • #15


                              That is REALLY cool code, Mike! Thanks for putting this alternative method on the table, very enlightening.

                              -John

                              Comment


                              • #16
                                Originally posted by Jim Fritts View Post
                                Why bother with STATIC when all you need is GLOBAL.
                                "Declare static variables inside a Sub, Function, Method, or Property. Static variables retain their values as long as the program is running." "The STATIC statement is valid only inside a procedure. Static variables retain their values even after the procedure ends. A static variable is local to its procedure, and can have the same name as other variables in other parts of the program without conflict."

                                If you can track thousands of IDs then you can track thousands of GLOBALs. I'm just saying...
                                Very true.
                                But using a STATIC allows you to do other things as well, moving functionality connected with
                                a variable to the module in which the variable resides.
                                This was how we did object oriented programming before there were objects.
                                Storing, retrieving and acting on data could be accomplished with functions.
                                Further arguments to the function could be processed to activate code segments.
                                The world is strange and wonderful.*
                                I reserve the right to be horrifically wrong.
                                Please maintain a safe following distance.
                                *wonderful sold separately.

                                Comment


                                • #17
                                  moving functionality connected with a variable to the module in which the variable resides.
                                  Kurt,

                                  This comment made me want to understand more, so I went back to post #5, where you posted code without comment.

                                  Initially, I found that code curious and intriguing, but after re-reading the Help, I couldn't see how to apply it to a CALLBACK FUNCTION...

                                  But as a standalone technique, this comment I just quoted helps to clarify more of that code's usefulness.

                                  Thanks!
                                  -John

                                  Comment


                                  • #18
                                    Functions can be called from the CALLBACK function instead of being executed within the CALLBACK function using a STATIC variable.
                                    This makes the code more reusuable in my opinion by placing in a separate function with the STATIC in the separate function.
                                    I think that is the point being made.
                                    Code:
                                    #DIM ALL
                                    
                                    %BTN_LOGIT = 1001
                                    '-----------------------------------------------------------------
                                    SUB LogIt(sMsg AS STRING)
                                     STATIC sAll  AS STRING
                                     sAll+=sMsg + $CR
                                     ? sAll,%MB_SYSTEMMODAL,FUNCNAME$
                                    END SUB
                                    '-----------------------------------------------------------------
                                    FUNCTION PBMAIN()
                                      MyDialog %HWND_DESKTOP
                                    END FUNCTION
                                    '-----------------------------------------------------------------
                                    CALLBACK FUNCTION MyCallBack()
                                      SELECT CASE AS LONG CB.MSG
                                        CASE %WM_INITDIALOG
                                        CASE %WM_COMMAND
                                          SELECT CASE AS LONG CB.CTL
                                            CASE %BTN_LOGIT
                                              IF CB.CTLMSG = %BN_CLICKED OR CB.CTLMSG = 1 THEN
                                             LogIt TIME$
                                              END IF
                                          END SELECT
                                      END SELECT
                                    END FUNCTION
                                    '-----------------------------------------------------------------
                                    FUNCTION MyDialog(BYVAL hParent AS DWORD) AS LONG
                                      LOCAL lRslt AS LONG
                                      LOCAL hDlg  AS DWORD
                                      DIALOG NEW hParent, "Dialog1", 442, 228, 258, 201, %WS_POPUP OR %WS_BORDER _
                                        OR %WS_DLGFRAME OR %WS_SYSMENU OR %WS_MINIMIZEBOX OR %WS_MAXIMIZEBOX OR _
                                        %WS_CLIPSIBLINGS OR %WS_VISIBLE OR %DS_MODALFRAME OR %DS_3DLOOK OR _
                                        %DS_NOFAILCREATE OR %DS_SETFONT, %WS_EX_CONTROLPARENT OR %WS_EX_LEFT OR _
                                        %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR, TO hDlg
                                        CONTROL ADD BUTTON, hDlg, %BTN_LOGIT, "Button1", 5, 5, 50, 15
                                      DIALOG SHOW MODAL hDlg, CALL MyCallBack TO lRslt
                                      FUNCTION = lRslt
                                    END FUNCTION
                                    https://duckduckgo.com instead of google

                                    Comment


                                    • #19
                                      Some of what I've learned (so far) from this thread:

                                      1) It pays to step back and look at what you're REALLY trying to accomplish (for the user) before coding. Yeah, everyone knows this, but I still try to express my initial ideas in code because, well, because it's a kind of shortcut, quicker than writing out notes, exploring alternatives, and coding afterward.
                                      But as Mike showed in post #14, I hadn't thought through from the user side what I was making them do. Based on the fact that all I needed was a user selection (or escape), the original dialog did not need to require additional clicks or keystrokes, and MODAL with message processing provides a cleaner user experience than MODELESS.
                                      May seem like small potatoes in this case, but it reminds me to pay attention to the big, user picture, earlier in the process.

                                      2) I am constantly being reminded to go back and refresh the basics. I was completely blind to the capability demonstrated by Pierre and Dave to use the CALLBACK return values as pointer to the string. I know - Duh... But I came to this with tunnel-vision - I needed to return a string, right? So how could I have missed this obvious solution? Because I have always struggled with pointers in Basic. Before Basic, I had used pointers in assembly language on the Signetics 2650, the MOS 6502, the RCA/Motorola 1802, and the Zilog Z80... But my first Basic was on the TRS-80, and "pointers" were poorly imitated via PEEK and POKE. When I moved to PowerBasic for DOS, my projects didn't need them, and then when I moved to PB-DLL, I struggled just to understand Windows ways. So I have let my skills with pointers decay, and here obviously missed a perfect and easy implementation. I should have recognized it.
                                      When I was programming fulltime, I used to sit and just read the PB manuals, but it's been years, and I see that I tend to fall back on the simplest stuff. Since I'm doing more programming once again, I recognize that I cannot revert to simplistic procedures. I have to take advantage of all that the OS and PB have to offer.
                                      So, back to the books!

                                      3) The PB community continues to demonstrate incredible diversity of skills, at high levels, and contribute the highest quality of thought, and generously share really smart coding techniques and examples. There just aren't words to convey how valuable these are to me, who will probably always be a junior coder... Everyone who has posted here has upheld these high standards, and as the beneficiary, I am greatly appreciative!

                                      Thank you all! You're just the best techs I know!
                                      -John



                                      Comment


                                      • #20
                                        The user interface can be exactly the same using MODAL or MODELESS.
                                        It is just a different message pump.
                                        The modeless method allowed trapping a left mouse click in the modeless message pump.
                                        The CALLBACK function did not need to be removed.
                                        https://duckduckgo.com instead of google

                                        Comment

                                        Working...
                                        X