Queued components allow you to call object functions that
will be recorded, forwarded through Microsoft Message Queue Server (MSMQ), and
then replayed at the server where the queued component is configured. In some
cases, the parameters of the functions being recorded may include objects. You
can do this with queued components, but you must follow certain rules in order
to have the argument object recreated when the method call is played back at
- The object being passed as an argument must support
IPersistStream. In Visual Basic, it must be marked as "persistable" and must
implement the Class_ReadProperties and Class_WriteProperties functions
- The object class being passed as an argument must be
correctly registered on both the client computer and the server
- Independently of the above, a component instance created as
queued may be passed as a parameter to a second queued component.
How It Works
What goes on when a call is made to a queued component, and an
object is passed as an argument?
Here's an example: Suppose you have
an object you use to manage customer information called Customer.CCustomer.
This object is implemented in the Customer.dll file and has the following code.
Note that it is marked as persistable, and note its implementation of the
'CCustomer Class. Marked as 'Persistable'
Public Name As String
Public Age As Integer
Public Address As String
Private Sub Class_ReadProperties(PropBag As PropertyBag)
Name = PropBag.ReadProperty("Name")
Age = PropBag.ReadProperty("Age")
Address = PropBag.ReadProperty("Address")
MsgBox "Person read!!"
Private Sub Class_WriteProperties(PropBag As PropertyBag)
PropBag.WriteProperty "Name", Name
PropBag.WriteProperty "Age", Age
PropBag.WriteProperty "Address", Address
MsgBox "Person Written!!"
Private Sub Class_Initialize()
MsgBox "CCustomer Class_Initialize"
Private Sub Class_Terminate()
MsgBox "CCustomer Class_Terminate"
This sample class does nothing except cache information. In a real
world scenario, however, you would probably have other methods (and no message
boxes in Class_Initialize!).
Now imagine that there is an object that
is designed for server use. This object is called Orders.COrder, is in a
separate file called Orders.dll, and has a function with the following code:
Public Sub AddOrder(ByVal ItemID As Long, ByVal Amount As Double, ByVal Customer As Customer.CCustomer)
MsgBox "Customer order for item '" & ItemID & "' for " & Customer.Name
Note that the customer information for the order is directly specified
as an object of type Customer.CCustomer. In the real world, you would probably
engage in some database activity in this method. However, in this sample all
you do is raise a message box.
And last but not least, there is the
client EXE application. All this has is a form and a command button in which
you type the following code:
Dim oOrder As COrder
Dim oCust As CCustomer
Set oCust = New CCustomer
oCust.Name = "Eduardo A. Jezierski"
oCust.Age = 23
oCust.Address = "One Microsoft Way, Redmond, WA"
Set oOrder = New COrder
oOrder.AddOrder 123, 100, oCust
Set oOrder = Nothing
Set oCust = Nothing
Say you have configured your orders component inside of COM+, adding it
to a COM+ application. When a user clicks the button on the client, the
- A CCustomer object is created (raising the CCustomer Class_Initialize message box) and populated by the client.
- A COrder object is created.
- The AddOrder method is executed in the COM+
- A message box saying "Customer order for item '123' for
Eduardo A. Jezierski" appears.
- The COrder instance is destroyed.
- The CCustomer instance is destroyed, bringing up the CCustomer Class_Terminate message box.
Bring in the Queues
So what happens now if you bring on Queued Components?
To test this, first configure your COrder component as queued. See the
following Microsoft Knowledge Base article for instructions:
How To Configuring a COM+ application for Queued Components
Next, modify the client application so that the
components created are queued. To do so, change the line where the COrder is
created to be as follows:
Set o = GetObject("queue:/new:Orders.COrder")
This will effectively give you a "stand-in" COrder object that will
record the method calls and will dispatch them to the COrder object sitting in
your COM+ application.
The rest of the application remains the same.
When you test it, however, a very different set of events occurs:
- First, a customer object is created and populated by the
- A COrder queued recorder instance is created using the
GetObject function with the specified moniker.
- The AddOrder method is invoked. The QC recorder, behaving
as a COrder, takes in the Customer object, which gets its Class_WriteProperties
function called so that its state is persisted, together with all other
parameters (ItemID and Amount).
- The COrder-recorder instance is set to nothing. At this
point, all the calls invoked on it (just the AddOrder in this case) are packed
together with the client security token and other information, wrapped in a
Microsoft Message Queue Server (MSMQ) message, and sent off to the
- Having finished its job, the client destroys the customer
instance and finishes its work.
- Once the message is sent, you assume that it eventually
reaches the intended application's queue.
- Because the application was set to "listen" for messages,
it detects the MSMQ message arrival and figures out that it belongs to a COrder
class used by user X. After doing access checks, it creates a COrder instance on the
- Now the "playback" part of the story begins. The QC player
starts invoking the methods the recorder stored in the message.
- In this case, that is just the AddOrder method. However,
because the parameters include a Customer object, first a CCustomer instance is
created, and it is directed to "load" itself through the Class_ReadProperties
- Once the instance is loaded, the player actually invokes
the AddOrder method on the real COrder object, so that it does its chores. It
passes the new Customer instance as the call argument, and once the call is
done, it destroys this temporary instance. It then invokes any other recorded
calls in a similar fashion, and then releases the COrder object and temporary
Your COrder code executed without realizing that the customer
instance it was using was really a local and different instance of the customer
object that stored itself in the client, maybe minutes or hours ago. In this
way, the whole asynchronous model is maintained.
Article ID: 246627 - Last Review: August 15, 2005 - Revision: 2.7
- Microsoft Visual Studio 6.0 Enterprise Edition
- Microsoft Visual Basic 6.0 Professional Edition
- Microsoft Visual Basic Enterprise Edition for Windows 6.0
|kbcomplusqc kbhowto KB246627|