Page #78 (Chapter 11 - Using ActiveX DLLs from WebClasses)

Chapter 11 - Using ActiveX DLLs from WebClasses

Visual Basic Developers Guide to ASP and IIS
A. Russell Jones
  Copyright 1999 SYBEX Inc.

Persisting ActiveX Objects
I'll start this section with a warning: Don't persist ActiveX objects unless you have to. I've warned you throughout this book that persisting ActiveX objects is a bad idea because of the threading problems associated with storing apartment-threaded objects in Session variables. But now I'm going to show you a way to circumvent the problem. If you don't store the object, just the data required to re-create the object, the threading issue disappears.
Store Object Data, Not Objects
You've already seen some examples that persist CStoredRecordset and CStoredDictionary objects, but those examples transform the ActiveX object rather than storing it with its properties and methods intact. The remainder of this section builds on the idea of transformation for persistence. When you create your own ActiveX objects, you can store them in Session variables as Variants containing byte arrays. You use VB's built-in PropertyBag object to do this. You'll see how to make the whole process of persisting and depersisting objects almost automatic.
In VB 6, you begin the process of persisting ActiveX objects by setting the design-time property called Persistable to 1-Persistable. When you do this, VB adds three events to the class: InitProperties, ReadProperties, and WriteProperties. Those of you who have been building ActiveX controls are probably familiar with these events.
The InitProperties event fires only when you instantiate the object for the first time. You can, and should, use this event to initialize the values of properties instead of the standard Initialize event. The Initialize event fires before the InitProperties event. If you use the Initialize event to initialize property values, the ReadProperties event will overwrite those values when you re-instantiate the object. Additionally, you can use the InitProperties event to determine whether the object is a "new" instance or a re-created instance.
For example, if you want to count the number of times a new copy of an object is instantiated, you can open a file, read a counter value, increment the value, and assign the result in the InitProperties event:
Private Sub Class_InitProperties()
    Dim fnum As Integer
    Dim counter As Long
    fnum = FreeFile
    Open "counter.dat" For Binary Lock Read Write As #fnum
    Get #fnum, 1, counter
    counter = counter + 1
    Put #fnum, 1, counter
    Close #fnum
    mCounter = counter
End Sub
To see this process in action, create a new IIS project and rename it WCPersistence. Rename the default WebClass WCPersistTest. Place this code into the WebClass_BeginRequest event:
Private Sub WebClass_BeginRequest()
    Dim cpc As TestPersistence.CPersistCounter
    Set cpc = New CPersistCounter
    Response.Write "You have instantiated a " _
    & " CPersistCounter object " & cpc.Counter & _
    " times."
    Set cpc = Nothing
End Sub
You must also create the CPersistCounter object. Add a new ActiveX DLL project. Rename the project TestPersistence. Rename the default class module CPersistCounter. Add this code to the CPersistCounter class:
Private mCounter As Long
Public Property Get Counter() As Long
    Counter = mCounter
End Property
Private Property Let Counter(l As Long)
    mCounter = l
End Property
Private Sub Class_InitProperties()
    Dim fnum As Integer
    Dim lCount As Long
    fnum = FreeFile
    Open "hellocounter.dat" For Binary Lock Read _
       Write As #fnum
    If LOF(fnum) > 0 Then
        Get #fnum, 1, lCount
    Else
        lCount = 0
    End If
    lCount = lCount + 1
    Put #fnum, 1, lCount
    Close #fnum
    Counter = lCount
End Sub
Private Sub Class_ReadProperties(PropBag As PropertyBag)
    Counter = PropBag.ReadProperty("Counter", 0)
End Sub
Private Sub Class_WriteProperties(PropBag As PropertyBag)
    PropBag.WriteProperty "Counter", Counter, 0
End Sub
Add a project reference in the WCPersistence project to the TestPersistence project and save both projects. When you run the WCPersistence project, you'll see that the count increments each time you refresh the browsers (see Figure 11.1).
The incrementing count means you're creating a brand new instance of the CPersistCounter object each time you run the WebClass_Start method. It's a new instance because persistence isn't automatic. Although the class contains all the code needed to persist and depersist the object, you have to supply the PropBag argument to the ReadProperties and WriteProperties events from outside the class. To do that, you'll need to create a PropertyBag object and call the CPersistCounter's WriteProperties and ReadProperties methods when you want to save and restore the object.
Add the declaration for the CpersistCounter object to the Declarations section of the WebClass:
Dim cpc As TestPersistence.CPersistCounter
Replace the WebClass_BeginRequest event code so it looks like this:
Private Sub WebClass_BeginRequest()
Dim pb As PropertyBag
    Dim b() As Byte
    If IsEmpty(Session("cpc")) Then
        Set cpc = New CPersistCounter
    Else
        Set pb = New PropertyBag
        b = Session("cpc")
        pb.Contents = b
        Set cpc = pb.ReadProperty("CPC")
    End If
    Response.Write "You have instantiated a " & _
      "CPersistCounter object " & cpc.Counter & " times."
End Sub
The code you just added tests a Session variable called cpc. If the variable is empty, the WebClass creates a new CPersistCounter object; otherwise, it creates a PropertyBag object, assigns the value of Session("cpc") to the Contents property of the PropertyBag object, then re-instantiates the object and assigns it to the variable cpc by reading the PropertyBag property called CPC. When you re-create the object this way, it fires the ReadProperties method in the newly re-instantiated object, not the InitProperties method; therefore, the Counter property will acquire its previous value and will not increment on the browser screen.
Of course, you must save the object's data so that you can re-create it. Place this code into the WebClass_EndRequest event:
Private Sub WebClass_EndRequest()
Dim pb As PropertyBag
    Dim b() As Byte
    If Not cpc Is Nothing Then
        Set pb = New PropertyBag
        pb.WriteProperty "CPC", cpc
        b = pb.Contents
        Session("cpc") = b
        Set cpc = Nothing
    End If
End Sub
The WebClass_EndRequest code saves the data to re-create the object in the Session("cpc") variable. Save and run the project. Be sure to refresh the WebClass several times. I urge you to step through the code with the debugger until you're comfortable with the steps required to save and re-create objects.
Note that you must transfer the contents of the Session variable to a byte array before assigning it to the PropertyBag.Contents property. The PropertyBag will not accept a Variant, and all Session variables are Variants.
Automate Object Persistence and Retrieval
Although the preceding code works well for individual objects, it's just too much code to write for each object that you might want to save. A much better method would be to create an object that can then save and re-instantiate any other object. To make such an object, add a new ActiveX DLL project to your workspace. Rename the project Persistor and rename the default class CPersistor.
The CPersistor class contains two public methods: Persist and Depersist, which store and save objects. You are responsible for providing a name for each object that you want to persist, and for providing the same name and the correct data when you want to depersist the object. The class returns a Variant containing a byte array when you persist an object, thus enabling you to write the value to disk, to a database, or to store the value in a variable. Place the following code into the CPersistor class:
Public Function Persist(aName As String, anObject As Object) As Variant
    Dim pb As PropertyBag
    Dim b() As Byte
    Set pb = New PropertyBag
    pb.WriteProperty aName, anObject
    b = pb.Contents
    Persist = b
End Function
Public Function Depersist(aName As String, varData As Variant) As Object
    Dim pb As PropertyBag
    Dim b() As Byte
    Set pb = New PropertyBag
    b = varData
    pb.Contents = b
    Set Depersist = pb.ReadProperty(aName)
End Function
To test the class, change the WebClass_BeginRequest and the WebClass_End-Request methods as follows:
Private Sub WebClass_BeginRequest()
    Dim CP As CPersistor
    If IsEmpty(Session("cpc")) Then
        Set cpc = New CPersistCounter
    Else
        Set CP = New CPersistor
        Set cpc = CP.Depersist("CPC", Session("cpc"))
    End If
    Response.Write "You have instantiated a CPersistCounter object " & cpc.Counter & " times."
End Sub
Private Sub WebClass_EndRequest()
    Dim CP As CPersistor
    If Not cpc Is Nothing Then
        Set CP = New CPersistor
        Session("cpc") = CP.persist("CPC", cpc)
    End If
End Sub
Maintain State via Object Persistence
You now have another method for storing state. You can create ActiveX objects and use them to store state by using either files, database tables, or Session variables. Re-creating the objects from files or databases requires exactly the same steps as those shown in this chapter to save the objects in Session variables—instead, just write the Variant data to disk or to a database.
You should notice several things about this method of storing objects. First, you can store objects that hold references to other objects. For example, you can store an entire collection of objects in a single Variant—as long as each object implements the ReadProperties and WriteProperties events, and the names are unique. To do so, you take the PropertyBag argument passed to the top-level object and pass it on to each referenced object. You must reverse the process to re-instantiate the objects. Also, you must devise a way to store the names of the objects in their proper order; otherwise, you will not be able to restore the object to exactly the same state.
Second, once you know that you can store referenced objects along with their "owners," it becomes obvious that you can store multiple objects in the same PropertyBag. If you want to do this, you'll need to alter the CPersistor class so that it accepts a PropertyBag object as an argument rather than creating a new one each time. You should also know that the names of the objects must be unique. Otherwise, you'll simply overwrite objects whose data has already been stored.
Finally, you can combine this technique with other state maintenance options—for example, you can store the object names on the client, as a cookie, or you can use database key values as the names. Although I hesitate to say so, you can also store disconnected recordsets by making the recordset a property of an object. You can then persist the recordset along with any other property. Note that this is not nearly as efficient as the StoredRecordset class, because persisting the object means you have to copy the data three times: once to the PropertyBag, once to the byte array, and once to the Variant. However, when you re-instantiate the recordset, you have a true Recordset object, with all its properties and methods. In contrast, the StoredRecordset object has only some of the properties and methods of a Recordset object, and only copies data when you call the StoreRecordset method; saving the data in a Session variable is just a pointer assignment.
I'll end this section with a warning as well: As with all state maintenance techniques, use persistence sparingly and judiciously. As I stated in Chapter 8, you can easily bog down your server by maintaining more data than you need. For larger sites, you're much better off putting the data in a database rather than using the Session object. Even though databases are slower for a small number of users, they're faster when the server is under load.



Visual Basic Developer[ap]s Guide to ASP and IIS
Visual Basic Developer[ap]s Guide to ASP and IIS
ISBN: 782125573
EAN: N/A
Year: 2005
Pages: 98

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net