How To Pass Objects as Parameters to COM+ Queued Components (246627)



The information in this article applies to:

  • Microsoft Visual Studio, Enterprise Edition 6.0
  • Microsoft Visual Basic Professional Edition for Windows 6.0
  • Microsoft Visual Basic Enterprise Edition for Windows 6.0

This article was previously published under Q246627

SUMMARY

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 server:
  • 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 correctly.
  • The object class being passed as an argument must be correctly registered on both the client computer and the server computer.
  • Independently of the above, a component instance created as queued may be passed as a parameter to a second queued component.

MORE INFORMATION

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 appropriate methods:
'CCustomer Class. Marked as 'Persistable'
Option Explicit

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!!"
End Sub

Private Sub Class_WriteProperties(PropBag As PropertyBag)
    PropBag.WriteProperty "Name", Name
    PropBag.WriteProperty "Age", Age
    PropBag.WriteProperty "Address", Address
    MsgBox "Person Written!!"
End Sub

Private Sub Class_Initialize()
    MsgBox "CCustomer Class_Initialize"
End Sub

Private Sub Class_Terminate()
    MsgBox "CCustomer Class_Terminate"
End Sub
				
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
    
End Sub
				
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 following happens:
  1. A CCustomer object is created (raising the CCustomer Class_Initialize message box) and populated by the client.
  2. A COrder object is created.
  3. The AddOrder method is executed in the COM+ application.
  4. A message box saying "Customer order for item '123' for Eduardo A. Jezierski" appears.
  5. The COrder instance is destroyed.
  6. 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:

246825 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:
  1. First, a customer object is created and populated by the client.
  2. A COrder queued recorder instance is created using the GetObject function with the specified moniker.
  3. 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).
  4. 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 destination.
  5. Having finished its job, the client destroys the customer instance and finishes its work.

    Meanwhile...

  6. Once the message is sent, you assume that it eventually reaches the intended application's queue.
  7. 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 server.
  8. Now the "playback" part of the story begins. The QC player starts invoking the methods the recorder stored in the message.
  9. 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 function.
  10. 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 CCustomer instances.
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.

REFERENCES

For more detailed information about COM+ 1.0 services, visit the following Microsoft Developer Network Web site:

Modification Type:MinorLast Reviewed:8/15/2005
Keywords:kbComPlusQC kbhowto KB246627