Results 1 to 4 of 4

Thread: CALLBACKS, unofficial guide... (Trial and error mode)

  1. #1
    Member
    Join Date
    Sep 2008
    Age
    48
    Posts
    326
    Rep Power
    49

    CALLBACKS, unofficial guide... (Trial and error mode)

    CALLBACKS were created to help stop programs from hogging resources using endless loops to check for input and control states. Like a wake-up call, you tell windows to give you a ring when you need to wake-up and do something. (Respond to a mouse-move, button-click, window-resize, etc...)

    A side-trait of a CALLBACK, was the creation of a dynamic associative handle/class. This turns anything that you can create with a handle, into a potential clone. This works much like a TYPE string. Each item with a handle becomes a virtual new element, handled by windows assigning a second handle to every element.

    MyWindow (Handle: 12345) becomes...

    MyWindow.(CallHandle: 87654)
    MyWindow.(CallHandle: 19843)
    MyWindow.(CallHandle: 65430)

    Each window having the same set of code, and sharing the same controls. You don't have to define 1000 separate buttons for 1000 windows. Only ONE constant is needed for the single "Clone" window.

    MyWindow (Handle: 12345)
    - Button (%btnCONSTANT = 1)
    becomes...
    MyWindow.(CallHandle: 87654)
    - Button.(CallHandle: 87654)

    MyWindow.(CallHandle: 19843)
    - Button.(CallHandle: 19843)

    MyWindow.(CallHandle: 65430)
    - Button.(CallHandle: 65430)

    To see this in action, you can use a neat TOOL called a "Window-Analyzer" which shows the hWnd and the call-hWnd, child/parent levels, and even the ID constants. (Great for debugging if you are using constants that you "Think" are unique, but they are actually overlapping, causing "Issues".)

    The "ANALYZER" link is the one you want.
    http://allapi.mentalis.org/vbexample...?category=MISC

    The "ApiViewer" and "API-Guide" are good resources also. (The samples are VB6, but it is the order of use, and constants which are important to notice. CALLBACKS being a new feature, will undoubtedly keep you waiting for new features to be developed. This will allow you to develop your own features to add, without having to wait.)

    This comes in handy, for instance in a "Paint Program". Each new window holds an image, edit controls, menu's, more dialogs, buttons... You are unsure if a user will have 10 or 10,000 windows open. (That would make you mad, attempting to hand-code all those controls and constants and IF...THEN and WHILE...END and SELECT CASE statements.)

    This is where a little "New" thinking has to come into play.

    There is only ONE window/dialog which you NEED to code manually for full display. This MAIN APPLICATION window/dialog, MUST be created and SHOWN with a CALLBACK attached, before any other controls or dialogs are created. This MAIN APPLICATION will hold the critical (HANDLE) hWnd that your program needs for any CALLBACKS.

    You can define child elements for the MAIN APPLICATION before you SHOW the window/dialog, but ONLY if those elements will not have a CALLBACK themselves. Since they are children-of-a-parent, and the parent-handle has not been SHOWN, with the CALLBACK... there is no way to create a CALLBACK for a child of a parent which does not exist at the moment. (It exists as a HANDLE, but not as a CALLBACK HANDLE.)

    This is why they created the first element of a callback called a notification. There is an order to creating these CALLBACK clones.
    1. WM_PARENTNOTIFY: (Prior to creation, the parent is alerted of the creation.)
    This can be used to suppress, or make adjustments in memory/variables/code, prior to creation.
    2. WM_CREATE: (When you request to CREATE a window/dialog/button/element.)
    Creation notice comes post creation, and prior to display. Values can be set here, and display can be suppressed.
    3. WM_INITDIALOG: (Since these "Windows" are "Dialog Windows", the next event is this one.)
    This is where you create your controls/elements for display. The next notice will be "SHOW".
    4. WM_SHOWWINDOW: (Everything is now visible, ready for use, and you now have a MAIN WINDOW.)

    In this specific case, the PARENT of MAIN WINDOW is "WINDOWS 98/ME/NT/2K/XP/LH/VISTA".

    This window now has TWO handles. The TB handle for the "Original Control/Dialog", plus the "Call-Handle" for this "Virtual Window". TB only suggests what the window will look like. Windows adopts full control of this "Virtual Window" and all elements created for display from this point on, which are created as a child to this window. (This will not include any DIRECT displayed or created controls that are not a child of this hWnd. EG, Creating a new dialog who is a child of "0", will be a full TB control window, until you assign that element to a CALLBACK.)

    Following me here?

    Quick review...
    1. Create Dialog in TB, (Only the dialog), and send that info to a CALLBACK creation. "CALL MyCallbackProc()"
    2. That window becomes cloned, and the "Notices" in the callback are used to setup the clone.
    3. Each CALL HANDLE will step through an order of notices until the clone-dialog is displayed.
    4. At the WM_INITDIALOG notice, just before the WM_SHOWWINDOW notice, the controls should be setup.
    5. Use the program... Handle other calls as needed...
    6. Now we have to exit...

    Exit... Exit what? Well, there is no "Real Window" so you can't exit the normal way. You have to "Kill" the clone. Sounds like a neat game or movie, but it is just another function. At the moment, this can only be done with API code. Or with the SYSMENU-CLOSE button, if one exits on your window that you created.

    The procedure to close a window is similar to the way it opens.

    1. Something tells "Windows", your clone-creator, that you want to exit. "SendMessage(hWnd, %WM_SYSCOMMAND, %SC_CLOSE, 0)"
    2. This CALL triggers the CALLBACK notice, "%WM_SYSCOMMAND, %SC_CLOSE". There should be code here to handle YES/NO or SAVE, etc... This event is followed by "%WM_CLOSE", which is where you may want to destroy elements or free memory before you actually exit. (NOTE: Look below to see the API call to destroy.)
    3. Once that is finished, the window may trigger a %WM_SHOWWINDOW, as it disappears from the screen, but that is not a solid state notice, just a side-path that MAY happen.
    4. The last notice before complete destruction is, "%WM_DESTROY". This sends a message to all children, and tells them to destroy also. It should be assumed they all still exist at this point.
    5. Wait... There is one more notice... But this goes to the PARENT of this window, (Which is "Windows".) "%WM_NCDESTROY", tells the PARENT of this window, that all the children of the clone and the child-clone are all killed, and memory can be released.

    Sounds like a breeze!

    Here is the missing "Destroy" code. (Missing as of, 10/27/0
    [code=thinbasic]Declare Function DestroyWindow _
    Lib "user32.dll" _
    ALIAS "DestroyWindow" _
    (ByVal hWnd As Long) As Long
    ' -- In the "%WM_CLOSE" area where notices are handled... Add this line...
    DestroyWindow(CBHNDL)[/code]

    Full sample-code to follow...

  2. #2
    Member
    Join Date
    Sep 2008
    Age
    48
    Posts
    326
    Rep Power
    49

    Re: CALLBACKS, unofficial guide... (Trial and error mode)

    Sample code. (Novel-style. LOL.)
    UPDATED to #SCRIPTVERSION 1.0.0.1

    This setup is for CALLBACKS as window/form controller, with all controls being similar to each child window/form.

    Controls for each form are processed by each forms unique-ID, in the forms CALLBACK. All controls need only be unique within each form, to each other. They do not have to all be unique to all other controls through the project.

    Specific use for a setup like this...
    - Image/sound editor, which each child window holds an image/sound to be edited.
    - Network or system controller, where each child window may be a remote system.
    - Game editor, where each window is a unique element being edited.

    ' Created by by Jason DAngelo
    ' This is NOT a 100% complete list of all functions/calls/notices...
    ' This is just a wide-guide, which can be used as a generic template.
    ' Well, if you remove all the miles of comments, it might be a nice guide.
    
    ' NOTE: This is NOT an MDI-Child/Parent sample. These are parent/child by assignment.
    
    #MINVERSION 1.7.0.0
    #SCRIPTVERSION 1.0.0.1
    
    USES "UI"
    Declare Function DestroyWindow Lib "user32.dll" ALIAS "DestroyWindow"(ByVal hWnd As Long) As Long
    ' -- ID numbers of controls
    BEGIN CONST
     %menuFILE = 1000
     %menuCHILD_OP
     %menuEXIT_OP
     %btnCOMMAND ' NOTE: There are three buttons, +0, +1, +2. Keep that in mind if you add more stuff.
     ' -- The ID's for more functions must be %MyNewConstant = %btnCOMMAND + 3 + 1
     ' -- The +3 is 0-2 and the +1 is the incrament designated for the new constant.
    END CONST
    
    GLOBAL hWnd, hMenu, hMenuFile, hWndChild AS LONG
    
    ' -- Create dialog here
    FUNCTION TBMAIN()
      DIALOG NEW PIXELS, 0, "Parent Window",-1,-1, 300, 180, _
        %WS_CLIPCHILDREN OR %WS_CLIPSIBLINGS OR %WS_OVERLAPPEDWINDOW, _
        %WS_EX_APPWINDOW TO hWnd
      ' -- Place NON-CALLBACK controls here
      ' -- Simple Menu
      MENU NEW BAR TO hMenu
      ' -- FILE Menu
      MENU NEW POPUP TO hMenuFile
      MENU ADD POPUP, hMenu, "&File", hMenuFile, %MF_ENABLED
      MENU ADD STRING, hMenuFile, "&New CHILD Window", %menuCHILD_OP, %MF_ENABLED
      MENU ADD STRING, hMenuFile, "&Exit", %menuEXIT_OP, %MF_ENABLED
      MENU ATTACH hMenu, hWnd
      DIALOG SHOW MODAL hWnd, CALL mainProc()
    END FUNCTION
    
    ' -- Callback Function for hWnd (MAIN) dialog/window
    CALLBACK FUNCTION mainProc() AS LONG
      ' -- CBMSG is the primary "Notice" Message
      ' -- Followed by, CBCTL, which is a Control ID value (%MyConstant)
      ' -- Followed by, CBCTLMSG, which is the "Notice" Message of a control.
      ' -- NOTE: You can't have a "Message", without a control. Do not check for "Notice" first.
      ' -- SAMPLE: CBMSG=(%WM_COMMAND) and -> CBCTL=(%btnCOMMAND) and -> CBCTLMSG=(%BN_CLICKED)
      SELECT CASE CBMSG
      CASE %WM_CREATE
        ' -- Once the "CallBack Handle" has been assigned, and the window "Cloned"
        ' -- This is the first event notice that triggers.
        ' -- (After the %WM_PARENTNOTIFY to the parent of this child.)
      CASE %WM_INITDIALOG
        ' -- Put code to be executed after dialog creation here.
        ' -- Here is where you should place any controls that have a CALLBACK
        ' -- Understand that there will be TWO callbacks, this one for the
        ' -- parent of the control, and the callback for the actual control.
      CASE %WM_SHOWWINDOW
        ' -- This area is fired once the window is visible.
      CASE %WM_COMMAND
        ' -- You can handle ALL hWnd (MAIN) controls here.
        ' -- This message is sent when the user selects a command item from 
        ' -- a menu, when a control sends a notification message to its 
        ' -- parent window, or when an accelerator key-stroke is translated.
        SELECT CASE CBCTL
        CASE %menuCHILD_OP
          CreateChildWindow()
          RETURN(1)
        CASE %menuEXIT_OP
          ' -- Do not actually terminate from here.
          SendMessage(hWnd, %WM_SYSCOMMAND, %SC_CLOSE, 0)
          RETURN(1)
        END SELECT
      CASE %WM_SYSCOMMAND
        SELECT CASE CBCTL
        CASE %SC_MINIMIZE
        CASE %SC_MAXIMIZE
        CASE %SC_RESTORE
        CASE %SC_MOVE
        CASE %SC_SIZE
        CASE %SC_CLOSE
          IF MSGBOX(0, "Are you sure you want to exit the program?", %MB_YESNO OR %MB_ICONQUESTION, "CONFIRM EXIT") = %IDYES THEN
            ' -- This tells the program to call close, if you select YES.
            ' -- You can close immediately, without prompt, by sending a message
            ' -- directly to %WM_CLOSE, as opposed to capturing the SYSCOMMAND close.
            SendMessage(hWnd, %WM_CLOSE, 0, 0)
          END IF
          RETURN(1)
        END SELECT
      CASE %WM_CLOSE
        ' -- Put code to be executed before dialog end here
        ' -- This is the API call to kill this "Virtual Window" by the "Call Handle"
        ' -- This will be a TB function soon, I imagine.
        DestroyWindow(hWnd)
        RETURN(1)
      CASE %WM_DESTROY 
      END SELECT
    END FUNCTION
    
    SUB CreateChildWindow()
      ' -- NOTICE: This is NOT an MDI-Child. This is a CHILD by assignment. (Parent=hWnd to Child=hWndChild)
      DIALOG NEW PIXELS, hWnd, "Test CHILD Window", -1, -1, 260, 80, _
        %WS_CLIPCHILDREN OR %WS_CLIPSIBLINGS OR %WS_OVERLAPPEDWINDOW, _
        0 TO hWndChild
      ' -- These are Children by assignment, of hWndChild.
      ' -- All these controls will trigger the childProc() CALL
      ' -- If you want to assign a callback for these controls, they need to be moved
      ' -- to the childProc() callback notice "%WM_INITDIALOG" area.
      CONTROL ADD BUTTON, hWndChild, %btnCOMMAND+0, "BEEP", 45, 10, 170, 20
      CONTROL ADD BUTTON, hWndChild, %btnCOMMAND+1, "CLOSE THIS CHILD", 45, 30, 170, 20
      CONTROL ADD BUTTON, hWndChild, %btnCOMMAND+2, "CLOSE PARENT", 45, 50, 170, 20
      DIALOG SHOW MODELESS hWndChild, CALL childProc()
    END SUB
    
    ' -- Callback Function for hWndChild (CHILD) dialog/window
    CALLBACK FUNCTION childProc() AS LONG
      SELECT CASE CBMSG
      CASE %WM_CREATE
      CASE %WM_INITDIALOG
        DIALOG SET TEXT CBHNDL, "Child CB-Handle: " & CBHNDL
        RETURN(1)
      CASE %WM_SHOWWINDOW
      CASE %WM_COMMAND
        SELECT CASE CBCTL
          ' -- I have use three %btnCOMMAND constants.
          ' -- Each three have a virtual handle that mathes the parent they
          ' -- are attached to. (They could also have another handle, if they
          ' -- had thier own btnProc() CALLBACK. But that requires a complex
          ' -- setup, because all buttons would trigger that same callback.)
        CASE %btnCOMMAND+0
          IF CBCTLMSG = %BN_CLICKED THEN
            BEEP
          END IF
          RETURN(1)
        CASE %btnCOMMAND+1
          IF CBCTLMSG = %BN_CLICKED THEN
            ' -- Close this child by the "Virtual Handle", using the SYSCOMMAND close
            SendMessage(CBHNDL, %WM_SYSCOMMAND, %SC_CLOSE, 0)
          END IF
          RETURN(1)
        CASE %btnCOMMAND+2
          IF CBCTLMSG = %BN_CLICKED THEN
            ' -- Close parent (MAIN), using the SYSCOMMAND close
            ' -- This can be the real handle, because it is only one window
            ' -- If this were a virtual child, with others using the same mainProc()
            ' -- You would want to send the CBHNDL of the window when it was created.
            ' -- That would require additional tracking in an ARRAY, of virtual CBHNDL's
            SendMessage(hWnd, %WM_SYSCOMMAND, %SC_CLOSE, 0)
          END IF
          RETURN(1)
        END SELECT
      CASE %WM_SYSCOMMAND
        SELECT CASE CBCTL
        CASE %SC_MINIMIZE
        CASE %SC_MAXIMIZE
        CASE %SC_RESTORE
        CASE %SC_MOVE
        CASE %SC_SIZE
        CASE %SC_CLOSE
          SendMessage(CBHNDL, %WM_CLOSE, 0, 0)
          RETURN(1)
        END SELECT
      CASE %WM_CLOSE
        DestroyWindow(CBHNDL)
        RETURN(1)
      CASE %WM_DESTROY
      END SELECT
    END FUNCTION
    
    I have updated this code to use RETURN(1) values. (Seems the "Close" button likes to continue to CLOSE, even though you explicitly block it. The values of (1) are generic and only a temp-guide. See the note below.)

    When you deal with CALLBACKS, windows expects a return-value to let it know the CALLBACK was "Processed". I had originally left that RETURN value out, due to the fact that I have not tested it well enough, to even guess if I am doing it correctly. It is my understanding that 90% of the time, it expects a ONE value. However, there is a small group of calls that expects a ZERO or a specific VALUE greater than ONE.

    EG, I did not want to say... "Return(1)", is what you "Need" to send, when that is not the case. What it will come down to, is returning what the program expects for a return value. In short... You should research what "Return value" is expected from any element you add to a callback. Many elements don't expect anything, but sending back a "Processed" value will stop that control from sending repeated call-triggers.

    If your program hangs for an unexpected reason... Assume that the reason is related to a RETURN-VALUE desire.

    Other "Button" command "Notifications"...
    BCN_DROPDOWN
    BCN_HOTITEMCHANGE
    BN_CLICKED
    BN_DBLCLK
    BN_DISABLE
    BN_DOUBLECLICKED
    BN_HILITE
    BN_KILLFOCUS
    BN_PAINT
    BN_PUSHED
    BN_SETFOCUS
    BN_UNHILITE
    BN_UNPUSHED
    NM_CUSTOMDRAW (button)
    WM_CTLCOLORBTN
    Last edited by ErosOlmi; 07-10-2019 at 21:03.

  3. #3
    thinBasic author ErosOlmi's Avatar
    Join Date
    Sep 2004
    Location
    Milan - Italy
    Age
    57
    Posts
    8,817
    Rep Power
    10

    Re: CALLBACKS, unofficial guide... (Trial and error mode)

    Very interesting.
    Can I get some parts for thinBasic help file?

    Thanks
    Eros
    www.thinbasic.com | www.thinbasic.com/community/ | help.thinbasic.com
    Windows 10 Pro for Workstations 64bit - 32 GB - Intel(R) Xeon(R) W-10855M CPU @ 2.80GHz - NVIDIA Quadro RTX 3000

  4. #4
    Member
    Join Date
    Sep 2008
    Age
    48
    Posts
    326
    Rep Power
    49

    Re: CALLBACKS, unofficial guide... (Trial and error mode)

    Dissect as desired... I am working on a reverse version. Where it shows how to use one set of Proc() for all forms, and another set of Proc() for all other elements. This will bring the VB5 and VB6 users into a little more of a comfort zone. These are the controls we have grown to love, but no other programming language provides. It is also the same thing that has driven most of us away from all the ".NET" versions. They dropped focus of CALLBACKS and resorted to OBJECT-RELATION creation and response. Still a CALLBACK, but one which makes it more complex than it has to be, or is desired to be. (Buttons, Lists, Images, etc... With universal communication, and all having the ability to have a separate code-section for each response.)

    That setup in the first two posts is semi-specific to a parent/child window setup. Where it is expected to operate similar to an MDI, without being an MDI setup. As opposed to being a single WINDOW, with unique POPUP DIALOG WINDOWS as children.

    Both would have to be combined to simulate a program with both styles... Multiple windowProc() and elementProc() for complete control that will drive you mad in the process.

    The "Paint Program" which I had in mind, when talking about the setup, was "Gimp", found at http://www.gimp.org/

    Gimp uses a non-MDI style setup. It spans multiple monitors better that way. Using CALLBACKS is how each window communicates with the main window that holds the tools.

Similar Threads

  1. Does anyone know of a good guide on thinBASIC?
    By ErosOlmi in forum thinBasic where ...
    Replies: 2
    Last Post: 08-04-2010, 15:37

Members who have read this thread: 0

There are no members to list at the moment.

Posting Permissions

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