Results 1 to 17 of 17

Thread: [RESOLVED] can i create a class on a module?

  1. #1

    Thread Starter
    PowerPoster joaquim's Avatar
    Join Date
    Apr 2007
    Posts
    3,969

    Resolved [RESOLVED] can i create a class on a module?

    can i create a class on a module(not class module)?
    VB6 2D Sprite control

    To live is difficult, but we do it.

  2. #2
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,918

    Re: can i create a class on a module?

    I don't understand the question.

    Can you instantiate a class from within a standard (BAS) module? Yes, of course. You'd do that the same way you'd instantiate the class anywhere else:

    Code inside any procedure of a standard (BAS) module:
    Code:
    Dim o As Class1
    Set o = New Class1
    
     -- or --
    
    Dim o As New Class1
    Regards,
    Elroy
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  3. #3

    Thread Starter
    PowerPoster joaquim's Avatar
    Join Date
    Apr 2007
    Posts
    3,969

    Re: can i create a class on a module?

    i was asking if i can create class's on bas module
    VB6 2D Sprite control

    To live is difficult, but we do it.

  4. #4
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,918

    Re: can i create a class on a module?

    Ohhhh, wait. Are you trying to instantiate a standard (BAS) module? Noooo, you can't do that.

    I've been at this long enough to where I remember the old Basic PDS system that ran on MSDOS/PCDOC. In that system, you had to separately compile, and then link your executables. In the linker command, you had to specify what was "core" code and what was pulled in later as "overlays".

    The COM architecture is much more sophisticated than that, but, IMHO, the analogy still holds. In other words, standard BAS modules are your "core" code. You only get one copy of it and it's always all pulled into memory when you execute your EXE. All FRM, CLS, and CTL modules are COM objects though and are only "instantiated" upon your request, and then they're pulled from the EXE into memory (very similar to the overlays of days-gone-by).

    Regards,
    Elroy

    EDIT1: Before Olaf jumps on me, let me also say that you only get one copy of the COM object's code as well (FRM, CLS, CTL, etc). However, you get as many copies of the variables declared in those as you do instantiations of them. You only need one copy of the actual code (machine code or p-code) because it's re-entrant, and can be used repeatedly by several actual instantiations of the class/object. But it's nice that the COM object's code is pulled from the EXE only when you instantiate it for the first time, and not upon initial execution of the EXE.

    EDIT2: Also, the fact that you can have many BAS modules is really just an organizational convenience. Once compiled, it just all becomes your "core" code. However, that's far from true with respect to CLS, FRM, CTL, etc class modules. In that case, those modules become true COM objects, ready for instantiation.
    Last edited by Elroy; Nov 16th, 2016 at 04:07 PM.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  5. #5
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,918

    Re: [RESOLVED] can i create a class on a module?

    Say joaquim,

    Please take this in a helping light. I'd like to also point out some VB6 (and really, any COM program) language. The "class" is really the compiled code in an executable, waiting to be instantiated, and not yet in memory. Once you've instantiated it, it then becomes the "object". Therefore, an "object" is an instantiation (in memory) of a "class" (in the EXE).

    Also, just to make things more confusing, forms and user controls are also classes. They just also have a visual (Windows) component to them.

    Best Regards,
    Elroy
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  6. #6
    Frenzied Member
    Join Date
    Mar 2008
    Posts
    1,210

    Re: [RESOLVED] can i create a class on a module?

    You can make code in a module behave much like a single Public instance of a Class. A module can (weirdly) contain Properties and so these can be used to manipulate variables which are otherwise Local to the module. Actions can be triggered using Public Subs (Methods) just like a 'real' Class.

    The drawbacks are;
    You only have one Public instance - a 'real' Class allows use of multiple instances
    The code is always loaded into memory - the code for a 'real' Class is only loaded into memory when the first instance is created and that memory is retrieved (typically after a short time delay) when the last instance falls back to Nothing.

  7. #7
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,918

    Re: [RESOLVED] can i create a class on a module?

    Yeah, it's always been a bit strange that you can have BAS level properties, sort of global properties.

    Also, just to build on Magic's comment a bit, you can actually use your module names much like class names. For instance, if you have two BAS modules (Module1 & Module2). And let's say they both have a function named MyFunction. If you then try to use this MyFunction, you'll get an "Ambiguous Name" error upon compiling. However, if you use it by saying "Module1.MyFunction" or "Module2.MyFunction", then it'll work just fine.

    However, again, this is all just a convenience. Once things are compiled, all of that gets boiled down to memory addresses, and it's a mistake to ever think of those BAS modules as classes.

    Regards,
    Elroy
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  8. #8
    PowerPoster techgnome's Avatar
    Join Date
    May 2002
    Posts
    34,687

    Re: [RESOLVED] can i create a class on a module?

    it's a mistake to ever think of those BAS modules as classes.
    But that's essentially what it is - a shared class ... it happens to be shared at the application level, which is what makes it handy for global stuff. But essentially that's what it is... a shared class at the application level.

    -tg
    * I don't respond to private (PM) requests for help. It's not conducive to the general learning of others.*
    * I also don't respond to friend requests. Save a few bits and don't bother. I'll just end up rejecting anyways.*
    * How to get EFFECTIVE help: The Hitchhiker's Guide to Getting Help at VBF - Removing eels from your hovercraft *
    * How to Use Parameters * Create Disconnected ADO Recordset Clones * Set your VB6 ActiveX Compatibility * Get rid of those pesky VB Line Numbers * I swear I saved my data, where'd it run off to??? *

  9. #9
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,918

    Re: [RESOLVED] can i create a class on a module?

    Say techgnome, it's not worth a squabble, but I don't think that's right. Do you think that they're "instantiated" when the EXE fires up? I've mucked around in the EXE files quite a bit, and I've never been able to pull out a list of BAS module names, but I can pull out a list of class and form module names following a fairly well documented path.

    I'm not 100% certain, but I was always under the impression that BAS modules were totally compiled down to machine code (or p-code), with nothing left of them but op-codes and relocatable memory addresses. Whereas class and form modules preserve their name as well as the names of all the public (and friend) methods and properties.

    I'd certainly agree that they can "look" like classes when viewed from the source code, but I was always under the impression that all that was gone once they were compiled, i.e., no COM object in sight.

    If that's not the case, then, maybe through some trickery, they actually can have other copies instantiated.

    Regards,
    Elroy

    EDIT1: As further supporting evidence of this, there's no mechanism to do something like...

    Code:
    Dim m As Module1
    Set m = Module1
    ...thereby making aliased references to the BAS objects.
    Last edited by Elroy; Nov 16th, 2016 at 05:33 PM.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  10. #10
    PowerPoster
    Join Date
    Feb 2015
    Posts
    2,797

    Re: [RESOLVED] can i create a class on a module?

    You can create so-called Light-Weight COM objects. When you've had these objects you have full-control over them. For example you can create an object on the stack or initialize thousands objects very fast. The easiest approach is to create a type library with needed interfaces for light-weight objects. For example, before you create the type library with the test interface ILightWeightObject:
    Code:
    [uuid(4d12246f-7e51-444c-1234-e39b969a08d8),
     helpstring("LightWeight COM-object"),
     version(1.00)]
    
    library LightWeightCOM{
    
        importlib("stdole2.tlb");
    
    
        [odl,
         uuid(8adec83e-64b0-46cf-bae9-7d8adbb6629e)]
        
        interface ILightWeightObject: IUnknown {
    
            [propget]
            HRESULT TestProp([out, retval] BSTR *pRet);
    
            [propput]
            HRESULT TestProp([in] BSTR pValue);
            
            HRESULT ShowMsg([in] BSTR pPrompt);
    
        };
    
    }
    This interface just has the one property TestProp (for reading and writing) and single method ShowMsg. Let the ShowMsg method shows simple MsgBox, and TestProp property keeps the internal object variable which you can modify.
    Then you should create a module that contains the code of methods and creation subroutine:
    Code:
    Option Explicit
    
    Private Const E_NOINTERFACE As Long = &H80004002
    
    Private Type UUID
        Data1               As Long
        Data2               As Integer
        Data3               As Integer
        Data4(0 To 7)       As Byte
    End Type
    
    Private Type tILightWeightObject_vTable
        pQueryInterface     As Long
        pAddRef             As Long
        pRelease            As Long
        pTestProp_get       As Long
        pTestProp_put       As Long
        pShowMsg            As Long
    End Type
    
    Private Type tLightWeightObject
        pInterface          As Long
        lRefCount           As Long
        sProperty           As String
    End Type
    
    Private Declare Function IIDFromString Lib "ole32.dll" ( _
                             ByVal lpsz As String, _
                             ByVal lpiid As Long) As Long
    Private Declare Function IsEqualGUID Lib "ole32" ( _
                             ByRef rguid1 As UUID, _
                             ByRef rguid2 As UUID) As Boolean
    Private Declare Function CoTaskMemAlloc Lib "ole32" ( _
                             ByVal cb As Long) As Long
    Private Declare Function GetMem4 Lib "msvbvm60" ( _
                             ByRef src As Any, _
                             ByRef Dst As Any) As Long
    Private Declare Sub SysFreeString Lib "oleaut32.dll" ( _
                        ByVal bstr As Long)
    Private Declare Sub CopyMemory Lib "kernel32" _
                        Alias "RtlMoveMemory" ( _
                        ByRef Destination As Any, _
                        ByRef Source As Any, _
                        ByVal Length As Long)
    Private Declare Sub ZeroMemory Lib "kernel32" _
                        Alias "RtlZeroMemory" ( _
                        ByRef Destination As Any, _
                        ByVal Length As Long)
    Private Declare Sub CoTaskMemFree Lib "ole32" ( _
                        ByRef pv As Any)
    
    Private tIID_IUnknown           As UUID:    Private Const IID_IUnknown              As String = "{00000000-0000-0000-C000-000000000046}"
    Private tIID_ILightWeightObject As UUID:    Private Const IID_ILightWeightObject    As String = "{8adec83e-64b0-46cf-bae9-7d8adbb6629e}"
    Private tVTable                 As tILightWeightObject_vTable
    
    Public Function CreateLightWeightObject( _
                    ByRef sDefProp As String) As ILightWeightObject
        Dim tNewObj As tLightWeightObject
        Dim pNewObj As Long
        
        If tVTable.pQueryInterface = 0 Then
            
            tVTable.pQueryInterface = GetAddress(AddressOf ILightWeightObject_QueryInterface)
            tVTable.pAddRef = GetAddress(AddressOf ILightWeightObject_AddRef)
            tVTable.pRelease = GetAddress(AddressOf ILightWeightObject_Release)
            tVTable.pTestProp_get = GetAddress(AddressOf ILightWeightObject_TestProp_get)
            tVTable.pTestProp_put = GetAddress(AddressOf ILightWeightObject_TestProp_put)
            tVTable.pShowMsg = GetAddress(AddressOf ILightWeightObject_ShowMsg)
    
        End If
        
        tNewObj.lRefCount = 1
        tNewObj.pInterface = VarPtr(tVTable)
        tNewObj.sProperty = sDefProp
        
        pNewObj = CoTaskMemAlloc(Len(tNewObj))
        If pNewObj = 0 Then Exit Function
        
        CopyMemory ByVal pNewObj, ByVal VarPtr(tNewObj), Len(tNewObj)
        ZeroMemory ByVal VarPtr(tNewObj), Len(tNewObj)
        
        GetMem4 pNewObj, CreateLightWeightObject
    
    End Function
    
    Private Sub KillLightWeightObject( _
                ByRef tObj As tLightWeightObject)
                
        SysFreeString VarPtr(tObj.sProperty)
        CoTaskMemFree ByVal VarPtr(tObj.pInterface)
        
    End Sub
    
    Private Function ILightWeightObject_QueryInterface( _
                     ByRef tObj As tLightWeightObject, _
                     ByRef tIID As UUID, _
                     ByRef pOut As Long) As Long
        
        Select Case True
        Case IsEqualGUID(tIID, tIID_IUnknown), IsEqualGUID(tIID, tIID_ILightWeightObject)
            
            pOut = VarPtr(tObj.pInterface)
            
        Case Else
        
            ILightWeightObject_QueryInterface = E_NOINTERFACE
            pOut = 0
            
        End Select
        
    End Function
    
    Private Function ILightWeightObject_AddRef( _
                     ByRef tObj As tLightWeightObject) As Long
                     
        tObj.lRefCount = tObj.lRefCount + 1
        ILightWeightObject_AddRef = tObj.lRefCount
        
    End Function
    
    Private Function ILightWeightObject_Release( _
                     ByRef tObj As tLightWeightObject) As Long
                     
        tObj.lRefCount = tObj.lRefCount - 1
        ILightWeightObject_Release = tObj.lRefCount
        
        If tObj.lRefCount = 0 Then KillLightWeightObject tObj
        
    End Function
    
    Private Function ILightWeightObject_TestProp_get( _
                     ByRef tObj As tLightWeightObject, _
                     ByRef sRet As String) As Long
        sRet = tObj.sProperty
    End Function
    
    Private Function ILightWeightObject_TestProp_put( _
                     ByRef tObj As tLightWeightObject, _
                     ByVal sValue As String) As Long
        tObj.sProperty = sValue
    End Function
    
    Private Function ILightWeightObject_ShowMsg( _
                     ByRef tObj As tLightWeightObject, _
                     ByVal sValue As String) As Long
        MsgBox sValue
    End Function
    
    Private Function GetAddress( _
                     ByVal lValue As Long) As Long
        GetAddress = lValue
    End Function
    The CreateLightWeightObject routine creates an instance of Light-Weight object and returns the reference to ILightWeightObject interface that object supports. As bonus you can make the constructors and pass the parameters during creation time. It just fills the Virtual-Functions-Table and moves the object (that is represented as UDT) to arbitrary memory. It frees temporary variable by writing zeroes there as well in order to VB6 doesn't free our string member. Then you can test it:
    Code:
    Option Explicit
    
    Public Sub Main()
        Dim cObj    As ILightWeightObject
        
        Set cObj = CreateLightWeightObject("Def property")
        
        cObj.ShowMsg cObj.TestProp
        
        cObj.TestProp = "Test value"
        
        cObj.ShowMsg cObj.TestProp
        
        Set cObj = Nothing
        
    End Sub
    Everything work fine.
    Regards,
    Кривоус Анатолий.
    Attached Files Attached Files

  11. #11
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    10,918

    Re: [RESOLVED] can i create a class on a module?

    Ahhh, interesting Trick.

    You're turning a TypeLib into an instantiated object that can be used with VB6.

    *chuckles and shakes head* Not exactly instantiating a BAS module, but pretty darned close.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  12. #12
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,454

    Re: [RESOLVED] can i create a class on a module?

    Quote Originally Posted by Elroy View Post
    Ahhh, interesting Trick.

    You're turning a TypeLib into an instantiated object that can be used with VB6.

    *chuckles and shakes head* Not exactly instantiating a BAS module, but pretty darned close.
    I think Anatoli was posting the stuff only for better understanding, "what gets loaded" (with regards to multiple
    instances of a Class) - and what is so called "shared code" (loaded only once, along with the compiled binary).

    Here's a Test-question (meant without offense, only trying to establish "common ground in understanding",
    because I've seen quite a few comments from you - and also Magic Ink with regards to "loaded Class-Code"):

    When you define the following Class in VB6:

    Code:
    Option Explicit
    
    Private mSomePrivateLongVariable As Long
    '---- the above being the only Private Member in the Declaration-Section of the Class ----
    
    '---- now the section, which defines and implements a lot of Public Methods and Props ----
    Public Sub SomeMethod1(...) As SomeType
    ...
    End Sub
    
    Public Property Get SomeProp1(...) As SomeType
    ...
    End Property
    
    Public Sub SomeMethod100(...) As SomeType
    ...
    End Sub
    So, this class has the Private Member mSomePrivateLongVariable As Long -
    and in addition also a whole lot of Methods (over 100 of them).

    Now let's say, we load 1000 instances of the above Class - how much *more* memory
    do you think the running Apps process will take up in this case (after all 1000 instances were loaded)?
    (assuming - for ease of calculation - that any VB-Class-instance has a fixed overhead of 100Bytes per instance)

    Olaf

  13. #13

  14. #14
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,454

    Re: [RESOLVED] can i create a class on a module?

    Ah, umm - well, since Anatoli was revealing that already - I'll answer my previous question now...
    Now let's say, we load 1000 instances of the above Class - how much *more* memory
    do you think the running Apps process will take up in this case (after all 1000 instances were loaded)?
    (assuming - for ease of calculation - that any VB-Class-instance has a fixed overhead of 100Bytes per instance)
    1000 instances of the described class (one Private Long-Member and "over 100 methods") will take up:
    1000 * (OverheadOf100Bytes + 4BytesForTheLongMember) only.
    (1000 * 104 = 104,000 Bytes, to give an exact number).

    So, what I was aiming at was: ... the MethodCount (the "amount of code" in the class)
    is totally irrelevant, when we talk about "loading instances into memory".

    It's only the Private Members we define in the Declaration-Section of a Class, which
    will take up "memory per instance" - the Methods of a given class "remain where they are".

    And that is basically just another "static, hidden *.bas-module", where these Methods are compiled into -
    the VB6-compiler then "expanding" the Method-signatures in a way, that they follow the COM-ABI,
    which means:

    When we have e.g. the following Member - and only a single Method defined in a VB6-Class:
    Code:
    Private mSomeLongMember As Long
    
    Public Function SomeMethod(ByVal SomeParam As Long) As Long
      ...
    End Function
    It will be compiled/expanded into normal "module-code", which (expressed in VB-syntax) would then look like this:
    Code:
    Private Type udtThis '<- only the memory described in this UDT-definition is "instance-relevant"
      pVTable As Long
      RefCounter As Long
      '... some optional Extra-Members, each Class-instance might need
      mSomeLongMember As Long
    End Type
    
    Public Function SomeMethod(This As udtThis, ByVal SomeParam As Long, ByRef FunctionResult As Long) As HResult
      ...
    End Function

    Olaf
    Last edited by Schmidt; Nov 17th, 2016 at 05:32 AM.

  15. #15

  16. #16
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,454

    Re: [RESOLVED] can i create a class on a module?

    Quote Originally Posted by The trick View Post
    I would like to stress that when you use a standard module the compiler/linker can remove function if one is not used.
    BTW, the references counter stored at (int*)((char*)*(void**)((char*)pObj + 0x08) + 0x04):
    Function-removal by the VB6-compiler will (in practice) not happen, because each of
    the "COM-interface-related functions" will be "touched", when filling the VTable with the Func-Pointers.

    As for the RefCounter-Position in Standard-VB-Classes (within the Instance-UDT-def)...
    That's only a VB-Class-specific detail - the RefCounter-Member can be placed anywhere -
    only the pVTable-Member needs to be "fixed" at the first position.

    I was only trying to make the principle obvious, how a "Compiler compiles the Code of a COM-class" -
    and maybe also the fact, that COM-Class-instances *could* be compiled with as low an
    Overhead as 8Bytes only!
    Meaning, a lightweight COM-Class which defines only Methods (has no Private Members)
    would take up only half the memory of a VB-Variant-Variable per instance.

    Olaf
    Last edited by Schmidt; Nov 17th, 2016 at 05:29 AM.

  17. #17

Posting Permissions

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



Click Here to Expand Forum to Full Width