Announcement

Collapse
No announcement yet.

Using CALL DWORD with Property routines

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

  • Using CALL DWORD with Property routines

    I am trying to learn about PB9 objects and in working with the OBJPTR function, I have run into some problems. The code below utilizes the CALL DWORD command, and it seems to work nicely with METHODS but not with PROPERTY routines. I haven't found anything in the help that prohibits such use, so I was wondering if I am just using them incorrectly. By the way, I have learned a lot from Fred Harris' related code in this forum.

    Code:
    #COMPILE EXE
    #DIM ALL
    #INCLUDE "WIN32API.INC"
    
    DECLARE FUNCTION getX() AS LONG
    DECLARE SUB setX(BYVAL LONG)
    DECLARE SUB showX()
    
    CLASS cXX
       INSTANCE x AS LONG
       INTERFACE iXX '---------------------------------------------------------------------
          INHERIT IUNKNOWN
    
          PROPERTY GET getX() AS LONG  'VFT(3)
             PROPERTY = x 
          END PROPERTY
          
          PROPERTY SET setX(BYVAL n AS LONG) 'VFT(4)
             MSGBOX "setX()  " + STR$(n)
             x = n
          END PROPERTY
          
          METHOD showX()  'VFT(5)
             MSGBOX "showX():  " + STR$(x)
          END METHOD
       END INTERFACE
    END CLASS
    
    FUNCTION PBMAIN
       LOCAL m, n AS LONG
       LOCAL oX AS iXX
       LOCAL pVFT, VFT AS DWORD PTR
       ox = CLASS "cXX"
       ox.setX = 20
       ox.showX
       MSGBOX "getX()  " + STR$(ox.getX)
       
       pVFT = OBJPTR(ox)
       VFT = @pVFT 
       CALL DWORD @VFT[5] USING showX()
       CALL DWORD @VFT[4] USING setX(30)
       CALL DWORD @VFT[3] USING getX() TO n
       MSGBOX "returned from getX():  " + STR$(n)
    END FUNCTION

  • #2
    I haven't found anything in the help that prohibits such use,
    HELP FILE:
    Purpose
    Invoke a procedure (Sub or Function) indirectly.

    Syntax
    CALL DWORD dwpointer [{BDECL | CDECL | SDECL} ()]
    CALL DWORD dwpointer USING abc([arguments]) [TO result_var]
    ....
    The CALL DWORD statement has the following parts:

    dwpointer
    A Double-word, Long-integer, or pointer variable that contains the address of the entry point of a procedure (Sub or Function).
    "Address of an interface property or method" is not the entry point of a (PowerBASIC) procedure.

    I won't even ask what's wrong with....
    Code:
        myVar = objectvar.property
    or
    Code:
       objectvar.method (params)
    .. because if I can't see the reason for trying to abuse CALL DWORD I guess I'm just not smart enough to 'get it.'

    MCM
    Last edited by Michael Mattias; 19 Feb 2009, 05:18 PM.
    Michael Mattias
    Tal Systems Inc. (retired)
    Racine WI USA
    [email protected]
    http://www.talsystems.com

    Comment


    • #3
      You simply cannot ever, under any circumstaces, for any reason, unequivocally do such a thing. Did I mention Don't Do It?

      Call DWORD may be used only with Subs and Functions. Period. Only with Subs and Functions. Any other use is like begging to be beheaded.

      We can't list everything that is prohibited. So, if you don't find it documented, please Don't Do It!

      Did I mention "Don't Do It?"


      Warmest regards,

      Bob Zale
      PowerBASIC Inc.

      Comment


      • #4
        Hi Charles!

        If you are referring to my ObjPtr() demo where I dumped the VTables with function pointers at...

        http://www.powerbasic.com/support/pb...ghlight=ObjPtr

        ...check out my comments at the end of the 3rd post where I showed a C++ version of the VTable dump. What appears to be happening in your program (I just took a quick look at it) is that you are ending up with an unbalanced stack because you are failing to set up an adequate function pointer prototype. The class pointer (me) is always 'pushed' as the 1st parameter of an object function call. Here, try this...

        Code:
        #COMPILE EXE
        #DIM ALL
        #INCLUDE "WIN32API.INC"
        
        DECLARE FUNCTION getX(Byval Dword) AS LONG
        DECLARE SUB setX(Byval Dword, BYVAL LONG)
        DECLARE SUB showX(Byval Dword)
        
        CLASS cXX
           INSTANCE x AS LONG
           INTERFACE iXX '---------------------------------------------------------------------
              INHERIT IUNKNOWN
        
              PROPERTY GET getX() AS LONG  'VFT(3)
                 PROPERTY = x
              END PROPERTY
        
              PROPERTY SET setX(BYVAL n AS LONG) 'VFT(4)
                 MSGBOX "setX()  " + STR$(n)
                 x = n
              END PROPERTY
        
              METHOD showX()  'VFT(5)
                 MSGBOX "showX():  " + STR$(x)
              END METHOD
           END INTERFACE
        END CLASS
        
        FUNCTION PBMAIN
           LOCAL m, n AS LONG
           LOCAL oX AS iXX
           LOCAL pVFT, VFT AS DWORD PTR
           ox = CLASS "cXX"
           ox.setX = 20
           ox.showX
           MSGBOX "getX()  " + STR$(ox.getX)
        
           pVFT = OBJPTR(ox)
           VFT = @pVFT
           CALL DWORD @VFT[5] USING showX(pVFT)
           CALL DWORD @VFT[4] USING setX(pVFT,30)
           CALL DWORD @VFT[3] USING getX(pVFT) TO n
           MSGBOX "returned from getX():  " + STR$(n)
        END FUNCTION
        As Mr. Zale mentioned, this is a rather unusual thing to do, and its main merit consists in demonstrating for oneself that one's conception of memory layout is correct.
        Fred
        "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

        Comment


        • #5
          Wrong. Very wrong. There is no merit. Please do not post such examples again, as they all fail miserably. Reference counting. Memory leaks. Much more.

          Comment


          • #6
            Fred, that "may" work... however, it is a documented restriction of CALL DWORD that "dwPointer" be the address of a procedure.

            When you go off the reservation you are on your own.

            And just on a more "ideological" plane ... why should you care about the memory layout at all? If you have been working in Windows for a while, you should be comfortable dealing with the fact that everything is 'virtual' anyway... accessed via whatever these "handle" thingies are.

            Ok, I used to concern myself with it, too. It can be fun.

            However, I learned the hard way that Thou Shalt Not Count On It Working The Same Way In The Next Release.


            MCM
            Michael Mattias
            Tal Systems Inc. (retired)
            Racine WI USA
            [email protected]
            http://www.talsystems.com

            Comment


            • #7
              Anyone remember writing a List Of Lists TSR dependent program?
              :wavey:
              Furcadia, an interesting online MMORPG in which you can create and program your own content.

              Comment


              • #8
                I certainly have every respect for any person who wants to experiment a bit, use some ASM code to call other code, even some METHODS. The problem is this:

                1- CALL DWORD is a bit of a "black box"
                2- The DECLARES used with CALL DWORD are a bit of a "black box"
                3- Object Method Calls like: obj.method1(param) is a bit of a "black box".

                One is using a black box to be substituted for another black box using a third black box. The calling conventions are slightly different, reference counting is lost, there are memory leaks from differences in parameter cleanup, and more. This is guaranteed to fail miserably, even if not immediately, and even the black boxes are subject to change. If you must do this sort of thing, do it very carefully in ASM.

                Best regards,

                Bob Zale
                PowerBASIC Inc.

                Comment


                • #9
                  Am I on safe ground with the following implementation of OBJPTR used to pass an object?

                  Code:
                  #COMPILE EXE
                  #DIM ALL
                  #INCLUDE "WIN32API.INC"
                  
                  CLASS cXX
                     INSTANCE x AS LONG
                     INTERFACE iXX '---------------------------------------------------------------------
                        INHERIT IUNKNOWN
                  
                        PROPERTY GET getX() AS LONG  'VFT(3)
                           PROPERTY = x 
                        END PROPERTY
                        
                        PROPERTY SET setX(BYVAL n AS LONG) 'VFT(4)
                           MSGBOX "setX():  " + STR$(n)
                           x = n
                        END PROPERTY
                        
                        METHOD showX()  'VFT(5)
                           MSGBOX "showX():  " + STR$(x)
                        END METHOD
                     END INTERFACE
                  END CLASS
                  
                  FUNCTION PBMAIN
                     LOCAL m, n AS LONG
                     LOCAL oX AS iXX
                     LOCAL pAddr AS DWORD
                     ox = CLASS "cXX"
                     pAddr = OBJPTR(ox)
                     workWithObj pAddr
                     MSGBOX "x = " + STR$(ox.getX)
                  END FUNCTION
                  
                  SUB workWithObj(BYVAL pAddr AS DWORD)
                     LOCAL iobj AS iXX
                     POKE DWORD, VARPTR(iObj), pAddr
                     IF ISOBJECT(iObj) THEN
                        iObj.AddRef()
                        iObj.setX = 24
                        iObj.showX
                     END IF
                  END SUB

                  Comment


                  • #10
                    That kind of manipulation must be only used when there is no other choice. If used without concern of reference counting, will cause serious troubles.

                    Better use:

                    Code:
                    FUNCTION PBMAIN
                       LOCAL m, n AS LONG
                       LOCAL oX AS iXX
                       LOCAL pAddr AS DWORD
                       ox = CLASS "cXX"
                       workWithObj ox
                       MSGBOX "x = " + STR$(ox.getX)
                    END FUNCTION
                    
                    SUB workWithObj(BYVAL iobj AS iXX)
                       iObj.setX = 24
                       iObj.showX
                    END SUB
                    Forum: http://www.jose.it-berater.org/smfforum/index.php

                    Comment


                    • #11
                      How about if my function is actually a callback function (which is actually the case I have) where the callback function needs the object. I could only think of two ways to get the object... define a global object or pass an object ptr to the callback. A global object defeats the whole idea of encapsulation, so that just left me with passing a pointer thru CONTROL SET USER.

                      The object created in the callback gets an AddRef and is an automatic stack variable, so it should get released as the callback goes out of scope. So it doesn't seem like there should be a problem with reference counting, right?

                      Comment


                      • #12
                        Originally posted by Charles Dietz View Post
                        How about if my function is actually a callback function (which is actually the case I have) where the callback function needs the object. I could only think of two ways to get the object... define a global object or pass an object ptr to the callback. A global object defeats the whole idea of encapsulation, so that just left me with passing a pointer thru CONTROL SET USER.

                        The object created in the callback gets an AddRef and is an automatic stack variable, so it should get released as the callback goes out of scope. So it doesn't seem like there should be a problem with reference counting, right?
                        Charles,
                        I've used both methods. With the GLOBAL I only have one, gApp, which is the main application class. It allows to dig down and get just about anything you need. I also use the method you presented. Do a search for some of my code and you will see how I do it.

                        James

                        Comment


                        • #13
                          Hmm, i don't see the need for PB objects to be called via pointers but for all other occasions we have been doing this for years. so what's up?

                          @@pUnk[2] Using fRelease( pUnk )

                          The only misfit but not a big deal in the example above is not using a form of addref()
                          But then there must be a valid object already since it exists.

                          Maybe i missed the clue but if you do it right it should not harm.
                          But like i mentioned, why make it difficult with pointers anyway.
                          hellobasic

                          Comment


                          • #14
                            Oh and btw, i create custom controls and 'attach' an object pointer to the object created to the hWnd myself.
                            And of course an extra Addref()
                            The actual variable goes out of scope but the object remains until the window is destroyed (and Release() is called)

                            I *always* check the release of the objects via it's class destroy method.
                            Everybody should do that imo (during test/build).
                            hellobasic

                            Comment


                            • #15
                              Code:
                                pAddr = OBJPTR(ox)
                                 workWithObj pAddr
                                 MSGBOX "x = " + STR$(ox.getX)
                              END FUNCTION
                              
                              SUB workWithObj(BYVAL pAddr AS DWORD)
                               ...
                              ===>
                              Code:
                                pAddr = OBJPTR(ox)
                                 workWithObj   BYVAL pAddr 
                                MSGBOX "x = " + STR$(ox.getX)
                              END FUNCTION
                              
                              SUB workWithObj([BYREF] ox as  object_type)
                              ...
                              CALL "BYVAL ADDRESS" = CALL "BYREF"

                              Or far more simply .... (a big :doh: on me)

                              Code:
                              ' DELETE   pAddr = OBJPTR(ox)    << REMOVE 
                                 workWithObj   ox
                                 MSGBOX "x = " + STR$(ox.getX)
                              END FUNCTION
                              
                              SUB workWithObj([BYREF] ox as  object_type)
                              ...


                              MCM
                              Last edited by Michael Mattias; 21 Feb 2009, 10:31 AM.
                              Michael Mattias
                              Tal Systems Inc. (retired)
                              Racine WI USA
                              [email protected]
                              http://www.talsystems.com

                              Comment

                              Working...
                              X