Announcement

Collapse
No announcement yet.

Creating Objects

Collapse
This is a sticky topic.
X
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Creating Objects

    Here's some notes I wrote up on various ways to create objects. You can download the original .DOCX, PDF, and project files file here. Please provide corrections or suggest missing content. Hope this helps someone.

    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 9, PBWin 10, PBCC 5, PBCC 6Table of Contents
    Basics. 3
    1. Create a class in my program.. 3
    Example.bas. 3
    2. Adding Properties and Data to our Class. 4
    Example.bas. 4
    3. Class Constructors. 5
    Example.bas. 5
    4. Class factory* routines. 6
    Example.bas. 6
    5. Class Initialization. 7
    Example.bas. 7
    6. Adding GUID’s. 8
    Example.bas. 8
    7. Using a class from an include file. 10
    Example.bas. 10
    Example.inc. 10
    8. Using a class from an SLL. 11
    Example.bas. 11
    ExampleSLL.bas. 11
    9. Using a class from an SLL in another SLL. 12
    Example.bas. 12
    Test.bas. 12
    Reuse.bas. 13
    10. Sharing a user inherited class between SLL’s.14
    Test.bas. 14
    Reuse.bas. 16
    Example.bas. 17
    11. Using a class from a DLL using a Factory. 17
    Example.bas. 17
    ExampleDLL.bas. 17
    12. Using NewCom.. 18
    Example.bas. 18
    ExampleDLL.bas. 19
    13. Registering a COM Server. 20
    Example.bas. 20
    13ExampleDll.bas. 21
    14. Automation Objects. 22
    Example.bas. 22
    15. IDispatch objects. 23
    Example.bas. 23
    Example.vbs. 24
    16. Binding Dispatch Classes. 24
    Example.bas. 24
    ExampleDLL.bas. 25
     
    Basics
    At their heart all PowerBASIC objects are COM objects.  Essentially their use involves programming to interfaces contained in each class.  Each class can have one or more interfaces.  Any two interfaces that share a name or interface id must have the same methods and properties in the same order.  Interfaces are contained in stand-alone classes.  What I mean by that is that the class that contains an interface has no external relationship with any other class.  A class is a class and can never be thought of as another class.  For those of you used to procedural programming with subs and functions what do classes offer you?  Essentially you can think of classes as a group of subs and functions along with the data they work on.  Another way to put that is each class could represent a program, but now you can put several programs into one larger program.  
    In your program when you create an instance of a class, you create an object.  You can have many instances of the same class.
    1. Create a class in my program
    
    Ok, let’s create a simple class that’s part of an application.  We’re going to keep this class fairly simple and re-use it as we look at various ways of creating objects.
    Example.bas
    
    
    
    Code:
    #Compile Exe
    [SIZE=2][FONT=Courier New]#Dim All[/SIZE] [FONT=Courier New][SIZE=2] [/SIZE][/FONT] [SIZE=2][FONT=Courier New]Function PBMain () As Long[/FONT][/SIZE] [B][SIZE=2][FONT=Courier New] Local obj As iTest: ' Declare a variable[/FONT][/SIZE][/B] [FONT=Courier New][SIZE=2] [/SIZE][/FONT] [B][SIZE=2][FONT=Courier New] obj = Class "cTest": ' Attempt to create an object or instance of our class[/FONT][/SIZE][/B] [B][SIZE=2][FONT=Courier New] If IsObject( obj ) Then[/FONT][/SIZE][/B] [SIZE=2][FONT=Courier New] ? Str$( obj.Add( 5, 4 ) ) ' Display the result of adding 5 and 4[/FONT][/SIZE] [SIZE=2][FONT=Courier New] Else[/FONT][/SIZE] [SIZE=2][FONT=Courier New] ? "Object was not created" ' Indicate the object was not created[/FONT][/SIZE] [SIZE=2][FONT=Courier New] End If[/FONT][/SIZE] [FONT=Courier New][SIZE=2] [/SIZE][/FONT] [SIZE=2][FONT=Courier New] ' The following lines make pbcc easier to use from the ide[/FONT][/SIZE] [SIZE=2][FONT=Courier New] #If %Def( %PB_CC32 )[/FONT][/SIZE] [SIZE=2][FONT=Courier New] ? "Press enter to exit"[/FONT][/SIZE] [SIZE=2][FONT=Courier New] Local a As String[/FONT][/SIZE] [SIZE=2][FONT=Courier New] Line Input a[/FONT][/SIZE] [SIZE=2][FONT=Courier New] #EndIf[/FONT][/SIZE] [SIZE=2][FONT=Courier New]End Function[/FONT][/SIZE] [FONT=Courier New][SIZE=2] [/SIZE][/FONT] [SIZE=2][FONT=Courier New]Class cTest ' Name of our class[/FONT][/SIZE] [SIZE=2][FONT=Courier New] Interface iTest ' Name of our interface[/FONT][/SIZE] [SIZE=2][FONT=Courier New] Inherit IUnknown ' What our interface inherits from[/FONT][/SIZE] [SIZE=2][FONT=Courier New] Method Add( ByVal a As Long _[/FONT][/SIZE] [SIZE=2][FONT=Courier New] , ByVal b As Long _[/FONT][/SIZE] [SIZE=2][FONT=Courier New] ) As Long ' Our method[/FONT][/SIZE] [B][SIZE=2][FONT=Courier New] Method = a + b ' Return the result of work[/FONT][/SIZE][/B] [SIZE=2][FONT=Courier New] End Method ' End of method[/FONT][/SIZE] [SIZE=2][FONT=Courier New] End Interface ' End of interface[/FONT][/SIZE] [SIZE=2][FONT=Courier New]End Class ' End of class[/FONT][/SIZE]

    Let’s look at a couple of things. First note that when we declared a variable, we declared it using the name of an interface (Local obj As iTest). Next when we created an object we used the name of the class (obj = Class “cTest”). That name is also referred to as the Program ID or ProgID. Next note that after we thought we had created an object, we tested that it was successful with IsObject. Two other ways of testing are IsNothing and IsInterface, be sure to check out the documentation on those. Last when we wanted to return a value we assigned the result to Method (Method = a + b). That’s it, pretty simple. There’s a few thing happening that are hidden from you, but don’t worry we’ll talk about those before long!
    2. Adding Properties and Data to our Class

    Often a class is most useful when it contains some data. Our next example will add an instance variable and introduce properties. Properties are very much like Methods with a couple of important distinctions. First they indicate we are getting or setting values as opposed to methods that indicate we are performing some action. Note that this is just a nomenclature. You could very well perform actions in properties and set or retrieve values with methods. Second, they allow access to other interfaces through dotted notation. Our example has both a get and a set property. You do not need to include both. If you only include a get property, it is often referred to as a read-only property. If you only include a set property, it is often referred to as a write-only property.
    Example.bas

    Code:
    #Compile Exe
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 9, PBWin 10, PBCC 5, PBCC 6[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Function PBMain () As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local obj As iTest[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  obj = Class "cTest"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsObject( obj ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 5 ):                       ' Add 5 to last result[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 4 ):                       ' Add 4 to last result[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? Str$( obj.LastResult ):           ' Get the last result[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Else[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? "Object was not created"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  #If %Def( %PB_CC32 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  ? "Press enter to exit"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local a As String[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Line Input a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  #EndIf[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Function[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Class cTest[/FONT][/SIZE]
    [B][SIZE=2][FONT=Courier New]  Instance lastResult_ As Long:         ' Last result[/FONT][/SIZE][/B]
    [SIZE=2][FONT=Courier New]  Interface iTest[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Inherit IUnknown[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [B][SIZE=2][FONT=Courier New]   Property Get LastResult() As Long   ' Get the last result[/FONT][/SIZE][/B]
    [SIZE=2][FONT=Courier New]     Property = lastResult_            ' Assign the last result to the property[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [B][SIZE=2][FONT=Courier New]   Property Set LastResult( ByVal value As Long )  ' Set the last result[/FONT][/SIZE][/B]
    [SIZE=2][FONT=Courier New]     lastResult_ = value               ' Set last result to value[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Method Add( ByVal a As Long _[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]            ) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ += a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Method = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Method[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End Interface[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Class[/FONT][/SIZE]


    In the example above, we added data to the class by creating an Instance variable (Instance lastResult_ As Long). This variable can only be seen inside the class. Next we added a “getter” property called LastResult. It “exposes” or retrieves the value of our instance variable. Finally we added a “setter” property called LastResult. This assigns a value to our instance variable. In our program so far we haven’t used the setter property. Our example program always returns 9, but how do we know it will always return 9? First we add 5, but to what did we add 5? It turns out that PowerBASIC helps us out by always initializing variables to zero, so we can always count on a result of 9. But what if we wanted to start with 20 or some other number instead of zero? That’s where the Set property comes in. We can assign a value to last result using the following syntax: obj.LastResult = 20.
    3. Class Constructors

    Ok, so we’ve created an object, added some data, done some work and retrieved a value. How could we start out with lastResult initialized to some other value? I’m glad you asked! We do that with a “Class Method” called Create.
    Example.bas

    Code:
    #Compile Exe
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 9, PBWin 10, PBCC 5, PBCC 6[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Function PBMain () As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local obj As iTest[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  obj = Class "cTest"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsObject( obj ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 5 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 4 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? Str$( obj.LastResult )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Else[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? "Object was not created"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  #If %Def( %PB_CC32 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  ? "Press enter to exit"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local a As String[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Line Input a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  #EndIf[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Function[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Class cTest[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Instance lastResult_ As Long[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [B][SIZE=2][FONT=Courier New]  Class Method Create()[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]   lastResult_ = 20[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  End Method[/FONT][/SIZE][/B]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Interface iTest[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Inherit IUnknown[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Property Get LastResult() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Property = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Property Set LastResult( ByVal value As Long )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ = value[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Method Add( ByVal a As Long _[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]              ) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ += a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Method = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Method[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End Interface[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Class[/FONT][/SIZE]

    In the example above we always get 29. That’s because when we create an instance of the object, we set the initial value of lastResult_ to 20. Note that our program doesn’t ever call the create method, PowerBASIC does that for you. The mirror of the class create method is the destroy method ( Class Method Destroy() ). You can use this method to clean up when the object is no longer needed, again just like create, there is no need to call it either, PowerBASIC will do that for you.
    Wait you say, 20 is a fairly arbitrary number, I really want to set it to a different value each time I create an object! In PowerBASIC there is no way to change the parameters to the Create or Destroy methods, or really any way to call them. Instead you can call the properties and methods of the class to set the values ( obj.LastResult = 30 ). There’s a variation on this technique that allows you to send parameters to your class when you create it. See the fourth example J
    4. Class factory* routines

    * Factory is a term from object oriented design patterns. Essentially they are routines responsible for producing a new instance of an object.
    Class factory routines provide a couple of benefits. First, they allow you to pass parameters when creating an object. Next, they allow you to isolate code for object construction, which can make maintenance and debugging easier. Last they provide a declarable interface that can easily be used from DLL’s and SLL’s.
    Example.bas

    Code:
    #Compile Exe
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 9, PBWin 10, PBCC 5, PBCC 6[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Function PBMain () As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local obj As iTest[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Call NewTest( obj, 30 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsObject( obj ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 5 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 4 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? Str$( obj.LastResult )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Else[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? "Object was not created"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  #If %Def( %PB_CC32 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  ? "Press enter to exit"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local a As String[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Line Input a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  #EndIf[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Function[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [B][SIZE=2][FONT=Courier New]Sub NewTest( obj As iTest, Optional initialValue As Long )[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  obj = Class "cTest"                 ' Create the object[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  If IsFalse IsMissing( initialValue ) Then[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]   obj.LastResult = initialValue     ' Assign initial value if given[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]End Sub[/FONT][/SIZE][/B]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Class cTest[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Instance lastResult_ As Long[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Interface iTest[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Inherit IUnknown[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Property Get LastResult() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Property = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Property Set LastResult( ByVal value As Long )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ = value[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Method Add( ByVal a As Long _[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]              ) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ += a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Method = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Method[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End Interface[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Class[/FONT][/SIZE]

    Now we can provide different values when creating the object. If we don’t provide an initial value, lastResult_ starts with 0. We can combine that with the class create method of the last example to use a different initial default than 0.
    5. Class Initialization

    In the last example we created a class factory and showed using exposed properties to initialize the object. In some cases you may wish to utilize functionality when you create the object that you don’t want exposed the rest of the time. For example suppose we didn’t want users to change the value of our object except during object creation. There are no perfect answers to this, but below is one way in which you might approach this.
    Example.bas

    Code:
    #Compile Exe
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 9, PBWin 10, PBCC 5, PBCC 6[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Function PBMain () As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local obj As iTest[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Call NewTest( obj, 33 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsObject( obj ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 5 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 4 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? Str$( obj.LastResult )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Else[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? "Object was not created"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  #If %Def( %PB_CC32 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  ? "Press enter to exit"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local a As String[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Line Input a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  #EndIf[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Function[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Sub NewTest( obj As iTest, Optional initialValue As Long )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  obj = Class "cTest"                 ' Create the object[/FONT][/SIZE]
    [B][SIZE=2][FONT=Courier New]  If IsMissing( initialValue ) Then[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]   obj.Initialize( 0 )               ' Default value[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  Else[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]   obj.Initialize( initialValue )    ' Initialized value[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE][/B]
    [SIZE=2][FONT=Courier New]End Sub[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Class cTest[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Instance lastResult_ As Long[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Interface iTest[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Inherit IUnknown[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [B][SIZE=2][FONT=Courier New]   Method Initialize( ByVal initialValue As Long )[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]     Static init As Long[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]     If init Then Exit Method[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]     init = -1[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]     lastResult_ = initialValue[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]   End Method[/FONT][/SIZE][/B]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Property Get LastResult() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Property = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Method Add( ByVal a As Long _[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]              ) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ += a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Method = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Method[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End Interface[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Class[/FONT][/SIZE]

    In this example we change the LastResult property to read-only by removing the Set property. We also added an Initialize method that can only be called once. Finally in the factory subroutine NewTest we ensured a call to Initialize was always done. Now after getting a new object by calling NewTest, a user by design can only add values and retrieve the last result.
    There’s still a small loophole, if a user were to create the object themselves, they could call Add or LastResult before calling Initialize. Still this method can go a long way’s toward ensuring code correctness by design.
    6. Adding GUID’s

    So far all the classes we’ve created have been part of the application that used them. As long as the classes are included in the source we’re compiling there’s no need for GUID’s, PowerBASIC is helpfully providing you with new random GUID’s each time you compile. When do you need GUID’s? The short answer is, any time you don’t want a random GUID. Common examples are when using a class between SLL’s, from a DLL, or from an external EXE. One last consideration is any time we wish to register classes with Windows COM services, we’ll want a stable GUID.
    Example.bas

    Code:
    #Compile Exe
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 9, PBWin 10, PBCC 5, PBCC 6[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Function PBMain () As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local obj As iTest[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Call NewTest( obj, 25 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsObject( obj ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 5 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 4 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? Str$( obj.LastResult )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Else[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? "Object was not created"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  #If %Def( %PB_CC32 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  ? "Press enter to exit"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local a As String[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Line Input a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  #EndIf[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Function[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Sub NewTest( obj As iTest, Optional initialValue As Long )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  obj = Class "cTest"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsFalse IsMissing( initialValue ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.LastResult = initialValue[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Sub[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [B][SIZE=2][FONT=Courier New]Class cTest Guid$("{B2146BCF-C8D4-481B-BA25-32300B573C0E}")  ' Add class id or CLSID[/FONT][/SIZE][/B]
    [SIZE=2][FONT=Courier New]  Instance lastResult_ As Long[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [B][SIZE=2][FONT=Courier New]  Interface iTest Guid$("{B35DAF5E-8EEB-4DE4-8E86-BC7741BB5B8B}") ' Add interface id or IID[/FONT][/SIZE][/B]
    [SIZE=2][FONT=Courier New]   Inherit IUnknown[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Property Get LastResult() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Property = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Property Set LastResult( ByVal value As Long )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ = value[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Method Add( ByVal a As Long _[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]              ) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ += a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Method = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Method[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End Interface[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Class[/FONT][/SIZE]


    Note how little change was needed to add guids that don’t change. In the PowerBASIC IDE you can have it create these guids by pressing the Control+Alt+G key combination. If you want to create a PowerBASIC utility you can create these guids with the following expression: "Guid$(" + $Dq + GuidTxt$( Guid$ ) + $Dq + ")".
    We’ve introduced three important terms used with object creation.
    1. Program id or PRGID which is “cTest” for the example.
    2. Class id or CLSID which is "{B2146BCF-C8D4-481B-BA25-32300B573C0E}" for the example.
    3. Interface id or IID which is "{B35DAF5E-8EEB-4DE4-8E86-BC7741BB5B8B}" for the example.
    7. Using a class from an include file

    In this example we’ll simply move the class to an include file. This is the first form of reuse in PowerBASIC. You can put code you wish to reuse in an include file and then simply include it in each program that wants to use it.
    Example.bas

    Code:
    #Compile Exe
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 9, PBWin 10, PBCC 5, PBCC 6[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [B][SIZE=2][FONT=Courier New]#Include "Example.inc"[/FONT][/SIZE][/B]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Function PBMain () As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local obj As iTest[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  obj = Class "cTest"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  obj.LastResult = 15[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsObject( obj ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 5 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 4 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? Str$( obj.LastResult )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Else[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? "Object was not created"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  #If %Def( %PB_CC32 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  ? "Press enter to exit"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local a As String[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Line Input a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  #EndIf[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Function[/FONT][/SIZE]

    Example.inc

    Code:
    Class cTest
    Code:
    [SIZE=2][FONT=Courier New]  Instance lastResult_ As Long[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Interface iTest[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Inherit IUnknown[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Property Get LastResult() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Property = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Property Set LastResult( ByVal value As Long )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ = value[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Method Add( ByVal a As Long _[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]              ) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ += a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Method = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Method[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End Interface[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Class[/FONT][/SIZE]

    Note that we simply added one line to our program ( #include "Example.inc" ) to include the class. Now we can use this class in many programs. Also by convention we gave the included file an .inc extension instead of a .bas extension. To be more specific, files that are intended to be the main file compiled we give a .bas extension and files that are intended to be included we give a .inc extension.
    We notice something of interest when reviewing the source code. The source contains several statements that start with a #. These compiler directives allow source files to act as a project management system by controlling which files will be compiled and what compiler options will be used.
    8. Using a class from an SLL

    The next form of reuse introduced with PBWin 10 and PBCC 6 is SLL’s or Static Link Libraries. Some of the reasons you may choose to use an SLL are: You want to use different sets of include files when compiling. You want to hide global variables. You want to provide functionality to others without giving up your source code.
    Example.bas

    Code:
    #Compile Exe
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 10, PBCC 6[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [B][SIZE=2][FONT=Courier New]#Link "ExampleSLL.sll"[/FONT][/SIZE][/B]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Function PBMain () As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local obj As iTest[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  obj = Class "cTest"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  obj.LastResult = 12[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsObject( obj ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 5 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 4 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? Str$( obj.LastResult )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Else[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? "Object was not created"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  #If %Def( %PB_CC32 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  ? "Press enter to exit"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local a As String[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Line Input a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  #EndIf[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Function[/FONT][/SIZE]

    ExampleSLL.bas

    Code:
    #Compile SLL
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 10, PBCC 6[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Class cTest Common[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Instance lastResult_ As Long[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Interface iTest[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Inherit IUnknown[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Property Get LastResult() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Property = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Property Set LastResult( ByVal value As Long )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ = value[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Method Add( ByVal a As Long _[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]              ) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ += a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Method = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Method[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End Interface[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Class[/FONT][/SIZE]

    Note there are only a couple of changes from the last example. We changed #include to #link. In the SLL file we changed the extension to .bas, since we’ll be compiling this file independently. Added the #Compile SLL at the top of the file and added Common to the class. To compile this you must first compile ExampleSLL.bas. This will create ExampleSLL.sll. Now that you have the .sll file you can compile the Example.bas file that has the #link "ExampleSLL.sll" directive.
    9. Using a class from an SLL in another SLL

    In this example we’ll temporarily add a new class in another SLL to demonstrate sharing classes between SLL’s. This new class will simply encapsulate the work currently done in the main program. I would like to note that sharing classes between SLL’s is not currently documented or as far as I know supported by PowerBASIC. As such it’s possible that these techniques will either be disallowed at some point or require different techniques.
    Example.bas

    Code:
    #Compile Exe
    Code:
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]#Link "Test.sll"[/FONT][/SIZE]
    [B][SIZE=2][FONT=Courier New]#Link "Reuse.sll"[/FONT][/SIZE][/B]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Function PBMain () As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local obj As iReuse[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  obj = Class "cReuse"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsObject( obj ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? Str$( obj.Result )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Else[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? "Object was not created"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  #If %Def( %PB_CC32 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  ? "Press enter to exit"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local a As String[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Line Input a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  #EndIf[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Function[/FONT][/SIZE]

    Test.bas

    Code:
    #Compile SLL
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 10, PBCC 6[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]' This is required to re-use this SLL in another SLL[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]Sub NewTest( obj As iTest, Optional initialValue As Long ) Common[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  obj = Class "cTest"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsFalse IsMissing( initialValue ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.LastResult = initialValue[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Sub[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]' We need the GUID's here to remain stable[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]Class cTest [B]Guid$("{75BB0CC2-2599-4669-B22A-7F9AA25038CB}") [/B]Common[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Instance lastResult_ As Long[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Interface iTest [B]Guid$("{02451FFA-8BBE-4DF0-B4D1-9B16B2D7595B}")[/B][/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Inherit IUnknown[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Property Get LastResult() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Property = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Property Set LastResult( ByVal value As Long )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ = value[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Method Add( ByVal a As Long _[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]              ) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ += a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Method = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Method[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End Interface[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Class[/FONT][/SIZE]

    Reuse.bas

    Code:
    #Compile SLL
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 10, PBCC 6[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]' Declare the routine that creates the object from the other SLL[/FONT][/SIZE]
    [B][SIZE=2][FONT=Courier New]Declare Sub NewTest( obj As iTest, Optional initialValue As Long ) Common[/FONT][/SIZE][/B]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]' The interface we're re-using must be declared here[/FONT][/SIZE]
    [B][SIZE=2][FONT=Courier New]Interface iTest Guid$("{02451FFA-8BBE-4DF0-B4D1-9B16B2D7595B}")[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  Inherit IUnknown[/FONT][/SIZE][/B]
    [B][FONT=Courier New][SIZE=2] [/SIZE][/FONT][/B]
    [B][SIZE=2][FONT=Courier New]  Property Get LastResult() As Long[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  Property Set LastResult( ByVal value As Long )[/FONT][/SIZE][/B]
    [B][FONT=Courier New][SIZE=2] [/SIZE][/FONT][/B]
    [B][SIZE=2][FONT=Courier New]  Method Add( ByVal a As Long ) As Long[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]End Interface[/FONT][/SIZE][/B]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Class cReuse Common[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Interface iReuse[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Inherit IUnknown[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Property Get Result() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Local obj As iTest[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]     Call NewTest( obj, 23 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     If IsObject( obj ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]       obj.Add( 5 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]       obj.Add( 4 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]       Property = obj.LastResult[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Else[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]       ? "cReuse.Result was unable to create a cTest object"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     End If[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End Interface[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Class[/FONT][/SIZE]

    In this example we link to two .SLL files. Our program uses the class (cReuse) from one SLL file which uses the class (cTest) from another SLL file. Note that the cReuse class did not require GUID’s to be used, we saw that also in example 8. The cTest class however did need to include GUID’s. This is because the random GUID’s assigned by PowerBASIC would change with each compile and link. To be more specific, if we did not include interface GUID’s, when Reuse.bas was compiled it would receive a different GUID than Test.bas actually had. Also note that cReuse did not require a factory sub, this is because the main program can always create classes from SLL’s. An SLL however cannot directly create a class from another SLL. As a result the cTest class did need a factory routine. This is because we needed a way to create the cTest object in Reuse.bas and we can’t “declare” a class, only interfaces. If we had tried obj = class "cTest", PowerBASIC would issue a 509 error: Interface mismatches class.

    But what if we had many SLL files? Would we have to #Link each one? The answer is, you can. You can also create one or more .PBLIB files and link those instead. We can create the library for these two SLL’s with the following command:
    plib Lib +Reuse.sll +Test.sll

    We could also include each sll on a separate line by issuing two plib commands as follows:
    plib Lib +Reuse.sll
    plib Lib +Test.sll

    Either of these methods will create Lib.pblib. We can then replace the two #link statements in Example.bas with
    #link "lib.pblib"

    10. Sharing a user inherited class between SLL’s.

    In the example above we inherited from IUnknown. This is the easiest example of sharing between SLL’s. What if we wanted to share a user inherited class between SLL’s? The process is the same, but there’s a bit more work. Let’s take this one step by step.

    Several examples 10+ use DLL’s. This feature is not available in the Console Compiler as such only example 14 will also work with CC 6+. If you have access to both all the non-DLL examples will work, but the pausing for the IDE has been removed.


    Some of the examples use the #Resource statement, this is a PBWin 10+ feature. This could be replaced by generating a resource file and compiling it. If you do this the projects are otherwise compatible with PBWin 9.
    Test.bas

    Code:
    #Compile SLL
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 10[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]' This is required to re-use this[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]Sub NewTest( obj As iTest, Optional initialValue As Long ) Common[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  obj = Class "cTest"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsFalse IsMissing( initialValue ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.LastResult = initialValue[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Sub[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Class cBase Guid$("{4E84985A-29DB-431F-A201-10860E65C11E}") Common As Com[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Instance lastResult_ As Long[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Interface iBase Guid$("{17D05713-F2AE-41C7-B954-1291D0A8A2CF}")[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Inherit IUnknown[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Property Get LastResult() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Property = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Property Set LastResult( ByVal value As Long )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ = value[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Method Add( ByVal a As Long ) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ += a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Method = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Method[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End Interface[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Class[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]' We need the GUID's here to remain stable[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]Class cTest Guid$("{43E72ACE-1985-40FD-B0B0-A66E8A0843A3}") Common As Com[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Instance obj As iBase[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Class Method Create()[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj = Class "cBase"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End Method[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Interface iTest Guid$("{13537912-8F20-4CE4-BAF6-198D0B90D1A5}")[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Inherit cBase, iBase[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ' We'll just use the definition in cBase[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End Interface[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Class[/FONT][/SIZE]

    To use this, let’s first change the first three lines to the following and compile:
    Code:
    #Compile [B]D[/B]LL
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 10[/FONT][/SIZE]
    [FONT=Courier New]#Dim All[/FONT]
    [B][FONT=Courier New]#Com TLib On[/FONT][/B]

    This will produce Test.tlb. Be sure to change the first three lines back after compiling. You can also delete the Test.dll file since we won’t be using it. We can now use PBRow.exe from PowerBASIC or TLB_501.exe from José Roca. PBRow.exe will produce the following code by opening 10Test.tlb.
    Code:
    ' Generated by: PowerBASIC COM Browser v.2.00.0086
    Code:
    [SIZE=2][FONT=Courier New]' Date & Time : 7/22/2012 at 7:13 AM[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Options:[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Always use an Interface Prefix : Off[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Interface Prefix               : Int_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Prefix ProgIDs, ClassIDs...    : On[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Use ANSI Strings               : On[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Use Singular Enumerations      : Off[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Generate Dispatch Interfaces   : Off[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Include Parameter Names        : On[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Use Property Get/Set statements: On[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' ------------------------------------------------[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Library Name: Test.dll[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Library File: C:\Prj\pb\TestCom\10\Test.tlb[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Description : COM Library[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' GUID : {89A346C0-AF64-4110-82F1-0FF1C777E7B8}[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' LCID : 0[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Version : 1.0[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]' Class Identifiers[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]$CLSID_Test_dll_CBASE = GUID$("{4E84985A-29DB-431F-A201-10860E65C11E}")[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]$CLSID_Test_dll_CTEST = GUID$("{43E72ACE-1985-40FD-B0B0-A66E8A0843A3}")[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]' Interface Identifiers[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]$IID_Test_dll_IBASE = GUID$("{17D05713-F2AE-41C7-B954-1291D0A8A2CF}")[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]$IID_Test_dll_ITEST = GUID$("{13537912-8F20-4CE4-BAF6-198D0B90D1A5}")[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]' Interface Name  : IBASE[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Description     : IBASE is a custom interface for Direct VTable access.[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Class Name      : CBASE[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' ClassID         : $CLSID_Test_dll_CBASE[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' This Interface cannot be created directly it can only[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' be returned by a Method or Property in this library. [/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]Interface IBASE $IID_Test_dll_IBASE [/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Inherit IUnknown[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Method Get_LASTRESULT() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Method Set_LASTRESULT(Byval Rhs As Long)[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Method ADD(Byval A As Long) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Interface[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]' Interface Name  : ITEST[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Description     : ITEST is a custom interface for Direct VTable access.[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Class Name      : CTEST[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' ClassID         : $CLSID_Test_dll_CTEST[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' This Interface cannot be created directly it can only[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' be returned by a Method or Property in this library. [/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]Interface ITEST $IID_Test_dll_ITEST [/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Inherit IUnknown[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Method Get_LASTRESULT() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Method Set_LASTRESULT(Byval Rhs As Long)[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Method ADD(Byval A As Long) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Interface[/FONT][/SIZE]

    Let’s extract the iTest interface definition for use in Reuse.bas. We’ll want to tweak it slightly to reflect our choice of using properties.
    Code:
    $IID_10Test_dll_ITEST = GUID$("{13537912-8F20-4CE4-BAF6-198D0B90D1A5}")
    Code:
    [FONT=Courier New]Interface ITEST $IID_10Test_dll_ITEST [/FONT]
    [FONT=Courier New]  Inherit IUnknown[/FONT]
    [FONT=Courier New] [/FONT]
    [FONT=Courier New]  Property Get LASTRESULT() As Long[/FONT]
    [FONT=Courier New]  Property Set LASTRESULT(Byval Rhs As Long)[/FONT]
    [FONT=Courier New]  Method ADD(Byval A As Long) As Long[/FONT]
    [FONT=Courier New]End Interface[/FONT]


    We’re now ready to write Reuse.bas.
    Reuse.bas

    Code:
    #Compile SLL
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 10[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]' Declare the routine that creates the object from the other SLL[/FONT][/SIZE]
    [B][SIZE=2][FONT=Courier New]Declare Sub NewTest( obj As iTest, Optional initialValue As Long ) Common[/FONT][/SIZE][/B]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]' The interface we're re-using must be declared here[/FONT][/SIZE]
    [B][SIZE=2][FONT=Courier New]$IID_Test_dll_ITEST = Guid$("{13537912-8F20-4CE4-BAF6-198D0B90D1A5}")[/FONT][/SIZE][/B]
    [B][FONT=Courier New][SIZE=2] [/SIZE][/FONT][/B]
    [B][SIZE=2][FONT=Courier New]Interface ITEST $IID_Test_dll_ITEST[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  Inherit IUnknown[/FONT][/SIZE][/B]
    [B][FONT=Courier New][SIZE=2] [/SIZE][/FONT][/B]
    [B][SIZE=2][FONT=Courier New]  Property Get LASTRESULT() As Long[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  Property Set LASTRESULT(ByVal Rhs As Long)[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  Method Add(ByVal A As Long) As Long[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]End Interface[/FONT][/SIZE][/B]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Class cReuse Common[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Interface iReuse[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Inherit IUnknown[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Property Get Result() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Local obj As iTest[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]     ' Note: We can't use NewCom here because the class is not registered with windows[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Call NewTest( obj, 23 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     If IsObject( obj ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]       obj.Add( 5 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]       obj.Add( 4 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]       Property = obj.LastResult[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Else[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]       ? "cReuse.Result was unable to create a cTest object"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     End If[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End Interface[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Class[/FONT][/SIZE]


    And finally our program to test using them.
    Example.bas

    Code:
    #Compile Exe
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 10[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]#Link "Test.sll"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Link "Reuse.sll"[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Function PBMain () As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local obj As iReuse[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  obj = Class "cReuse"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsObject( obj ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? Str$( obj.Result )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Else[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? "Object was not created"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Function[/FONT][/SIZE]

    11. Using a class from a DLL using a Factory

    Using a class from a DLL is similar to using a class from an SLL. We need to declare the class with GUID’s and we need a method of creating the class, such as a factory sub. Instead of declaring the sub’s common we need to export them and since the classes will be used externally we need to declare them AS COM.
    Example.bas

    Code:
    #Compile Exe
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 9, PBWin 10[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [B][SIZE=2][FONT=Courier New]Interface iTest Guid$("{85C36BA0-D637-4D9A-96A6-83C28868A789}")[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  Inherit IUnknown[/FONT][/SIZE][/B]
    [B][FONT=Courier New][SIZE=2] [/SIZE][/FONT][/B]
    [B][SIZE=2][FONT=Courier New]  Property Get LastResult() As Long[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  Property Set LastResult( ByVal value As Long )[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  Method Add( ByVal a As Long ) As Long[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]End Interface[/FONT][/SIZE][/B]
    [B][FONT=Courier New][SIZE=2] [/SIZE][/FONT][/B]
    [B][SIZE=2][FONT=Courier New]Declare Sub NewTest Lib "ExampleDLL.dll" Alias "NewTest" ( obj As iTest, Optional initialValue As Long )[/FONT][/SIZE][/B]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Function PBMain () As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local obj As iTest[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Call NewTest( obj, 5 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsObject( obj ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 5 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 4 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? Str$( obj.LastResult )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Else[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? "Object was not created"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Function[/FONT][/SIZE]

    ExampleDLL.bas

    Code:
    #Compile Dll
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 9, PBWin 10[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [B][SIZE=2][FONT=Courier New]Sub NewTest Alias "NewTest" ( obj As iTest, Optional initialValue As Long ) Export[/FONT][/SIZE][/B]
    [SIZE=2][FONT=Courier New]  obj = Class "cTest"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsNothing( obj ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   MsgBox "Failed to create cTest"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Exit Sub[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsFalse IsMissing( initialValue ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.LastResult = initialValue[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Sub[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [B][SIZE=2][FONT=Courier New]Class cTest Guid$("{912A7BD0-0C3B-4026-992B-D336F9E73649}") As Com[/FONT][/SIZE][/B]
    [SIZE=2][FONT=Courier New]  Instance lastResult_ As Long[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [B][SIZE=2][FONT=Courier New]  Interface iTest Guid$("{85C36BA0-D637-4D9A-96A6-83C28868A789}")[/FONT][/SIZE][/B]
    [SIZE=2][FONT=Courier New]   Inherit IUnknown[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Property Get LastResult() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Property = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Property Set LastResult( ByVal value As Long )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ = value[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Method Add( ByVal a As Long _[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]              ) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ += a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Method = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Method[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End Interface[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Class[/FONT][/SIZE]


    For the interface and class we have to have a stable GUID and since it’s used externally As Com. We also need to EXPORT the sub. In the application we have to declare the interface and Sub and that’s about it. If we did not intend to use the class methods and properties outside of the DLL, we could drop the AS COM. If you do this you would either use the class in other classes or provide exported Sub’s and Function’s to interact with the class.

    12. Using NewCom

    Do we really need a factory routine? Not really, with just a few changes we can create objects without the routine. In this example we’ll move the factory routine from the DLL back into the main example file. In this way you can use factory routines to create and initialize COM objects written by other people. We’ll also introduce a different way of declaring the classes.
    Example.bas

    Code:
    #Compile Exe
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 9, PBWin 10[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [B][SIZE=2][FONT=Courier New]$TEST_CLSID = Guid$("{912A7BD0-0C3B-4026-992B-D336F9E73649}")[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]$TEST_IID = Guid$("{85C36BA0-D637-4D9A-96A6-83C28868A789}")[/FONT][/SIZE][/B]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Interface iTest [B]$TEST_IID[/B][/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Inherit IUnknown[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Property Get LastResult() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Property Set LastResult( ByVal value As Long )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Method Add( ByVal a As Long ) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Interface[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Sub NewTest( obj As iTest, Optional initialValue As Long )[/FONT][/SIZE]
    [B][SIZE=2][FONT=Courier New]  obj = NewCom ClsId $TEST_CLSID Lib EXE.Path$ + "ExampleDLL.dll"[/FONT][/SIZE][/B]
    [SIZE=2][FONT=Courier New]  If IsNothing( obj ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   MsgBox "Failed to create cTest"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Exit Sub[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsFalse IsMissing( initialValue ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.LastResult = initialValue[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Sub[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Function PBMain () As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local obj As iTest[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Call NewTest( obj, 5 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsObject( obj ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 5 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 4 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? Str$( obj.LastResult )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Else[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? "Object was not created"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Function[/FONT][/SIZE]

    ExampleDLL.bas

    Code:
    #Compile Dll
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 9, PBWin 10[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [B][SIZE=2][FONT=Courier New]#Com Guid Guid$("{1B0C15E4-B9A9-4463-94E0-0F8ED722DD5F}")[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]#Com Name "Test", 1.00[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]#Com TLib On[/FONT][/SIZE][/B]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [B][SIZE=2][FONT=Courier New]$TEST_CLSID = Guid$("{912A7BD0-0C3B-4026-992B-D336F9E73649}")[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]$TEST_IID = Guid$("{85C36BA0-D637-4D9A-96A6-83C28868A789}")[/FONT][/SIZE][/B]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Class cTest [B]$TEST_CLSID As Com[/B][/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Instance lastResult_ As Long[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Interface iTest [B]$TEST_IID[/B][/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Inherit IUnknown[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Property Get LastResult() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Property = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Property Set LastResult( ByVal value As Long )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ = value[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Method Add( ByVal a As Long _[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]              ) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ += a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Method = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Method[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End Interface[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Class[/FONT][/SIZE]


    In the example above we used string constants to declare the GUID’s. This made moving them from the DLL source to the program using them easier. It also means that the GUIDs only exists in one place in the application which can make tracking down errors related to mismatched GUID’s much easier. We also used NewCom to create the object and specified the DLL the object lives in. In the DLL, the #Com Guid directive provides an application id or APPID for the DLL. #Com Name provides the name of the server and a version number. #Tlib On generate a .TLB file. The .TLB file can be used by PBRow.exe to generate the definition for the class. Below is what it generates for the example tlb:
    Code:
    ' Generated by: PowerBASIC COM Browser v.2.00.0086
    Code:
    [SIZE=2][FONT=Courier New]' Date & Time : 7/22/2012 at 8:12 AM[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Options:[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Always use an Interface Prefix : Off[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Interface Prefix               : Int_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Prefix ProgIDs, ClassIDs...    : On[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Use ANSI Strings               : On[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Use Singular Enumerations      : Off[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Generate Dispatch Interfaces   : Off[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Include Parameter Names        : On[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Use Property Get/Set statements: On[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' ------------------------------------------------[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Library Name: Test[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Library File: C:\Prj\pb\TestCom\12\ExampleDLL.tlb[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Description : COM Library[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' GUID : {1B0C15E4-B9A9-4463-94E0-0F8ED722DD5F}[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' LCID : 0[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Version : 1.0[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]' Class Identifiers[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]$CLSID_Test_CTEST = GUID$("{912A7BD0-0C3B-4026-992B-D336F9E73649}")[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]' Interface Identifiers[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]$IID_Test_ITEST = GUID$("{85C36BA0-D637-4D9A-96A6-83C28868A789}")[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]' Interface Name  : ITEST[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Description     : ITEST is a custom interface for Direct VTable access.[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' Class Name      : CTEST[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' ClassID         : $CLSID_Test_CTEST[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' This Interface cannot be created directly it can only[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]' be returned by a Method or Property in this library. [/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]Interface ITEST $IID_Test_ITEST [/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Inherit IUnknown[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Method Get_LASTRESULT() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Method Set_LASTRESULT(Byval Rhs As Long)[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Method ADD(Byval A As Long) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Interface[/FONT][/SIZE]

    José Roca also has a similar tool TLB_501.exe, you can find it here. Note the definition is a bit different than what we originally wrote, if you use tools such as these, you may want to tweak the output.
    13. Registering a COM Server

    At this point you’re probably wondering what the difference between a PowerBASIC class and a COM Server is and how much code you’ll need to write. You might also wonder if there are COM Servers are there COM clients? The good news is you’ve already seen a COM Server and a COM client. The previous example was an unregistered COM Server used by the example application or COM client. COM servers are simply EXE’s or DLL’s that expose COM Classes for use by other applications. COM clients are simply EXE’s or DLL’s that use COM classes. One other thing you may have noticed, but if you tried to open the .DLL file with PBRow or TLB_501, it complained that there were no type libs. You may also have noticed that all the routine names were uppercased. We’ll address both of these in this example.
    Example.bas

    Code:
    #Compile Exe
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 10[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]$TEST_CLSID = Guid$("{57BB8788-204C-4A6F-975A-1FD701FCA523}")[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]$TEST_IID = Guid$("{37A1AA6D-69D0-45E8-B44E-435D47319813}")[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Interface iTestExample $TEST_IID[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Inherit IUnknown[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Property Get LastResult() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Property Set LastResult( ByVal value As Long )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Method Add( ByVal a As Long ) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Interface[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Sub NewTest( obj As iTestExample, Optional initialValue As Long )[/FONT][/SIZE]
    [B][SIZE=2][FONT=Courier New]  obj = NewCom "cTestExample"[/FONT][/SIZE][/B]
    [SIZE=2][FONT=Courier New]  If IsNothing( obj ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   MsgBox "Failed to create cTest"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Exit Sub[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsFalse IsMissing( initialValue ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.LastResult = initialValue[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Sub[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Function PBMain () As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local obj As iTestExample[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Call NewTest( obj, 1 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsObject( obj ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 5 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 4 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? Str$( obj.LastResult )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Else[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? "Object was not created"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Function[/FONT][/SIZE]

    ExampleDll.bas

    Code:
    #Compile Dll
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 10[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Com Guid Guid$("{0DC00447-DA0D-4DB9-AE1C-F02511ADC4CC}")[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Com Name "Test", 1.00[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Com TLib On[/FONT][/SIZE]
    [B][SIZE=2][FONT=Courier New]#Resource TypeLib, 1, "ExampleDll.tlb"[/FONT][/SIZE][/B]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]$TEST_CLSID = Guid$("{57BB8788-204C-4A6F-975A-1FD701FCA523}")[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]$TEST_IID = Guid$("{37A1AA6D-69D0-45E8-B44E-435D47319813}")[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Class cTest $TEST_CLSID As Com[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Instance lastResult_ As Long[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Interface iTest $TEST_IID[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Inherit IUnknown[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Property Get LastResult [B]Alias "LastResult"[/B] () As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Property = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Property Set LastResult [B]Alias "LastResult"[/B] ( ByVal value As Long )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ = value[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Method Add [B]Alias "Add"[/B] ( ByVal a As Long _[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]              ) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ += a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Method = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Method[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End Interface[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Class[/FONT][/SIZE]

    Including the type lib was just a matter of adding a #resource statement and fixing the all upper case methods was just a matter of adding an alias. The property aliases might look a bit unusual at first glance since they both have the same alias. PowerBASIC will prepend get and set to these names for you. If you compile ExampleDLL.bas and then run Example.bas, you’ll notice that it fails to create the object. That’s because cTestExample is not registered yet. Run regsvr32 /v ExampleDLL.dll to register it.
    Note: On Windows Vista+, you’ll need to run regsvr32 as an administrator. One way to do this would be to launch a Command Prompt as an Administrator.

    Now when you run Example.bas it should work. We have just created what’s referred to as an in-process COM server.
    To unregister this use regsvr32 /u ExampleDLL.dll. It is a good practice to unregister any DLL’s you’re not using or to unregister them before creating new versions of them.

    14. Automation Objects

    Classes that derive from the IAutomation interface are very similar to classes derived from IUnknown. All automation classes return a hidden result code hResult. In PowerBASIC you access this value with OBJRESULT and OBJRESULT$. The following example modifies example 5 to show setting and using this value.
    Example.bas

    Code:
    #Compile Exe
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 9, PBWin 10, PBCC 5, PBCC 6[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Function PBMain () As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local obj As iTest[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Call NewTest( obj, 33 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsObject( obj ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 5 )[/FONT][/SIZE]
    [B][SIZE=2][FONT=Courier New]   ? Str$( ObjResult ) + ": " + ObjResult$[/FONT][/SIZE][/B]
    [SIZE=2][FONT=Courier New]   obj.Add( 4 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? Str$( obj.LastResult )[/FONT][/SIZE]
    [B][SIZE=2][FONT=Courier New]   ? Str$( ObjResult ) + ": " + ObjResult$[/FONT][/SIZE][/B]
    [SIZE=2][FONT=Courier New]  Else[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   ? "Object was not created"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  #If %Def( %PB_CC32 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  ? "Press enter to exit"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local a As String[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Line Input a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  #EndIf[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Function[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Sub NewTest( obj As iTest, Optional initialValue As Long )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  obj = Class "cTest"                 ' Create the object[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsMissing( initialValue ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Initialize( 0 )               ' Default value[/FONT][/SIZE]
    [B][SIZE=2][FONT=Courier New]   ? Str$( ObjResult ) + ": " + ObjResult$[/FONT][/SIZE][/B]
    [SIZE=2][FONT=Courier New]  Else[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Initialize( initialValue )    ' Initialized value[/FONT][/SIZE]
    [B][SIZE=2][FONT=Courier New]   ? Str$( ObjResult ) + ": " + ObjResult$[/FONT][/SIZE][/B]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Sub[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Class cTest[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Instance lastResult_ As Long[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Interface iTest[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Inherit IAutomation[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Method Initialize( ByVal initialValue As Long )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Static init As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     If init Then Exit Method[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     init = -1[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ = initialValue[/FONT][/SIZE]
    [B][SIZE=2][FONT=Courier New]     Method ObjResult = %E_InvalidArg[/FONT][/SIZE][/B]
    [SIZE=2][FONT=Courier New]   End Method[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Property Get LastResult() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Property = lastResult_[/FONT][/SIZE]
    [B][SIZE=2][FONT=Courier New]     Property ObjResult = %E_AccessDenied[/FONT][/SIZE][/B]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Method Add( ByVal a As Long _[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]              ) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ += a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Method = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Method[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End Interface[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Class[/FONT][/SIZE]

    We simply changed the class we inherit from to IAutomation and set the ObjResult at some point in each method. It’s worth noting that when we called Add, we didn’t explicitly set the ObjResult. By default PowerBASIC will set the value to %S_OK to indicate success.
    Automation objects are often used where the facilities of IDispatch are not needed. They would often be placed in DLL’s, use fixed GUID’s, and be registered with windows. Consider trying to modify the above example to do this.
    15. IDispatch objects

    So far all the objects have used what is referred to as direct binding. With direct binding, the methods and properties used can be validated and bound at compile time. Sometimes you would like to defer that binding until runtime. Common examples of this need are dynamically loading objects and scripting languages whose only option is to bind at runtime. In this example we’ll start with a simple version of a dispatch class and then extend it a bit in the next example. Also in a departure from earlier examples our client application will be VBScript.
    Example.bas

    Code:
    #Compile Dll
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 10[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Com Name "Test", 1.00[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Com TLib On[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Resource TypeLib, 1, "Example.tlb"[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]$TEST_CLSID = Guid$("{CC8BE75B-C96C-4552-A613-A52A567A0D4A}")[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]$TEST_IID = Guid$("{9D935EDE-8359-4DA6-A37A-8DCD83348C5C}")[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Class cTest $TEST_CLSID As Com[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Instance lastResult_ As Long[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Interface iTest $TEST_IID[/FONT][/SIZE]
    [B][SIZE=2][FONT=Courier New]   Inherit IDispatch[/FONT][/SIZE][/B]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Property Get LastResult Alias "LastResult"() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Property = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Property Set LastResult Alias "LastResult"( ByVal value As Long )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ = value[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Method Add Alias "Add"( ByVal a As Long ) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ += a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Method = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Method[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End Interface[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Class[/FONT][/SIZE]

    Example.vbs

    Code:
    Set obj = CreateObject("[B]cTest[/B]")
    Code:
    [SIZE=2][FONT=Courier New][B]x =[/B] obj.Add( 4 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New][B]x =[/B] obj.Add( 5 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]MsgBox "" & obj.LastResult[/FONT][/SIZE]

    After compiling Example.bas be sure to use RegSvr32.exe. Also remember that if you’re on Vista+ you’ll need to run this as an administrator. To run the example you can simply enter Example.vbs in a command prompt.
    If you’re on a 64bit system, you’ll need to use either c:\windows\syswow64\cscript.exe Example.vbs or c:\windows\syswow64\wscript.exe Example.vbs to run the example.
    16. Binding Dispatch Classes

    In this example we’ll switch back to PowerBASIC client and demonstrate both direct binding and late binding as well as using fixed direct binding id’s.
    Example.bas

    Code:
    #Compile Exe
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 10[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]$TEST_CLSID = Guid$("{3BEAFD25-6AC3-4AD4-B125-62CDAC00EE7C}")[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]$TEST_IID = Guid$("{C5226C20-83C0-4251-B529-3DBEEE664BA6}")[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Interface iTest $TEST_IID[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Inherit IDispatch[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Property Get LastResult [B]<1>[/B]() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Property Set LastResult [B]<1>[/B]( ByVal value As Long )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Method Add [B]<2>[/B]( ByVal a As Long ) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Interface[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Function PBMain () As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local lb As IDispatch[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local obj As iTest[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local result As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Local value As Long[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  obj = NewCom ClsId $TEST_CLSID Lib "ExampleDLL.dll"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  lb = obj[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  ' Direct binding[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  If IsObject( obj ) Then[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 4 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   obj.Add( 5 )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   MsgBox Str$( obj.LastResult )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Else[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   MsgBox "Object was not created"[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End If[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [B][SIZE=2][FONT=Courier New]  ' Late binding[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  value = 0[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  Object Set lb.LastResult = value[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  value = 4[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  Object Call lb.Add( value )[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  value = 5[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  Object Call lb.Add( value )[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  Object Get lb.LastResult To result[/FONT][/SIZE][/B]
    [B][SIZE=2][FONT=Courier New]  MsgBox Str$( result )[/FONT][/SIZE][/B]
    [SIZE=2][FONT=Courier New]End Function[/FONT][/SIZE]

    ExampleDLL.bas

    Code:
    #Compile Dll
    Code:
    [SIZE=2][FONT=Courier New]#Compiler PBWin 10[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Dim All[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Com Name "Test", 1.00[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Com TLib On[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]#Resource TypeLib, 1, "ExampleDLL.tlb"[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]$TEST_CLSID = Guid$("{3BEAFD25-6AC3-4AD4-B125-62CDAC00EE7C}")[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]$TEST_IID = Guid$("{C5226C20-83C0-4251-B529-3DBEEE664BA6}")[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]Class cTest $TEST_CLSID As Com[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  Instance lastResult_ As Long[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]  Interface iTest $TEST_IID[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Inherit IDispatch[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Property Get LastResult [B]<1>[/B] Alias "LastResult"() As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Property = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   Property Set LastResult [B]<1>[/B] Alias "LastResult"( ByVal value As Long )[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ = value[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Property[/FONT][/SIZE]
    [FONT=Courier New][SIZE=2] [/SIZE][/FONT]
    [SIZE=2][FONT=Courier New]   Method Add [B]<2>[/B] Alias "Add"( ByVal a As Long ) As Long[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     lastResult_ += a[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]     Method = lastResult_[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]   End Method[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]  End Interface[/FONT][/SIZE]
    [SIZE=2][FONT=Courier New]End Class[/FONT][/SIZE]


    To use fixed binding id’s we just added <#> after the method or property name. For late binding we used Object Set, Get, and Call. Also worth noting that when using late binding, literals are not allowed as parameters. Instead you must assign values to variables and pass those.[/FONT][/SIZE][/FONT]
    Last edited by Jim Bailey; 21 Feb 2013, 03:51 PM.
    LarryC
    Website
    Sometimes life's a dream, sometimes it's a scream

  • #2
    Very nicely done, Larry, and I very much appreciate it!!

    Hope it gets made a 'Sticky'!!
    Rod
    In some future era, dark matter and dark energy will only be found in Astronomy's Dark Ages.

    Comment


    • #3
      Yes, nice work Larry. I'm sure that it will be referred to as a debugging aid as well as a tutorial.

      Comment


      • #4
        Larry, thank you for this reference. Everything is well explained and organized; a very useful guide.
        Anthony W. J. Giambalvo
        Giami Network Services, LLC
        North Plainfield, NJ USA
        Email: anthony dot powerbasic at giami dot com

        Comment


        • #5
          Beautiful work Larry...

          Thank...

          Pierre

          Comment


          • #6
            Larry,

            This is a "must read" for object novices like me. It's fantastic!
            Good work!
            It's impossible to make anything foolproof because fools are so ingenious.

            Comment


            • #7
              Great work!

              Larry, what a great contribution.

              Brilliant work. A real time saver for anyone embarking on an OO journey with PB.

              Thank you so much.

              Comment


              • #8
                Nice work Larry, it'll be surely useful.

                Comment


                • #9
                  Originally posted by Rodney Hicks View Post
                  Hope it gets made a 'Sticky'!!
                  +1 Thanks Larry !!
                  Gary - W4GNS

                  Comment


                  • #10
                    Larry
                    Love your work!
                    Undoubtedly worthy of the suggested "sticky" status.
                    Thank you very much for it.
                    Last edited by Dean Gwilliam; 17 Feb 2013, 06:36 AM.

                    Comment


                    • #11
                      Larry,
                      Have you ever bought a book that you want to read, but haven't found time yet? But just having the book in your hands makes you feel good because you know it's there waiting for you whenever you get the time to read it?

                      Comment


                      • #12
                        Registering an Out-Of-Process COM Servers

                        Thank you for this tutorial, It's very helpful.

                        However I'm having trouble registering an Out-Of-Process COM server. I can register a DLL just fine but when I attempt to give a PROGID to an EXE I can't get it to register in Windows. I'm interfacing with QuickBooks and it requires calling back to an EXE, not a DLL. I did find information here to do this http://www.jose.it-berater.org/smffo...p?topic=3666.0 but from what I can see I don't think PBWin10 requires this low-level approach to EXE COM servers, but maybe I'm wrong. Any insight would be appreciated.

                        Comment


                        • #13
                          Unfortunately, native PowerBASIC COM is rather limited, it cannot be used to create
                          out-of-process servers. Actually, it cannot be used to create in-process servers
                          such as ActiveX controls and BHOs.

                          Fortunately, low-level COM is not difficult.
                          Dominic Mitchell
                          Phoenix Visual Designer
                          http://www.phnxthunder.com

                          Comment


                          • #14
                            Dominic,

                            Thanks for the information. I'll look into doing a low-level COM instead.

                            Bill

                            Comment

                            Working...
                            X