A client and an in-process component share the same address space, so calls to the methods of an in-process component can use the client’s stack to pass arguments. This is not possible for an out-of-process component; instead, the method arguments must be moved across the boundary between the two processes. This is called marshaling.
A client and an out-of-process component communicate via a proxy/stub mechanism, as shown in Figure 8.14. The proxy and stub handle the marshaling and unmarshaling of arguments passed to methods of the component; they are completely transparent to the client.
Figure 8.14 A client and an out-of-process component
Marshaling is slower than passing parameters within a process, especially when parameters are passed by reference. It’s not possible to pass a pointer to another address space, so a copy must be marshaled to the component’s address space. When the method is finished, the data must be copied back.
Because of marshaling considerations, certain method arguments should be declared ByVal for out-of-process components and ByRef for in-process components, as described below.
When you’re declaring methods for objects provided by an out-of-process component, always use ByVal to declare arguments that will contain object references.
The reason for this is that cross-process marshaling of object references requires significantly less overhead if it’s one-way. Declaring an argument ByRef means that the object reference must be marshaled to your component, and then back to the client when the method is finished.
The only reason to use ByRef in the declaration of an argument that’s going to contain an object reference is when the method will replace the client’s object reference with a reference to another object.
If the method you’re declaring requires a property value supplied by an object that belongs to the client, declare the argument with the data type of the property rather than the object’s class. Marshaling an object reference requires significantly more overhead than marshaling a simple data type.
It’s common practice to declare parameters in Visual Basic procedures ByRef if they will be used to pass large strings and Variant arrays, even if the procedure doesn’t make any changes to the parameter. It’s much faster to pass a four-byte pointer to the data than to make a copy of the entire string or array and pass a pointer to the copy.
This practice works within the address space of your own process — that is, within your own program or with an in-process component — because the method to which you pass the parameter can use the pointer to access the data directly.
Cross-process marshaling reverses this practice. Data for a ByRef method argument is copied into the component’s address space, and the method is passed a pointer to the local copy of the data.
The method uses the pointer to modify the copy. When the method ends, the data for any ByRef parameters is copied back into the client’s address space. Thus a parameter declared ByRef will be passed cross-process twice per method call.
If you expect users to pass large strings or Variant arrays to a method of your component, and the method does not modify the data, declare the parameter ByVal for an out-of-process component and ByRef for an in-process component.
Important If you declare a parameter of a method ByRef in an out-of-process component, developers using the component cannot avoid the effects of marshaling by putting parentheses around the parameter, or by using the ByVal keyword when they call the method. Visual Basic will create a copy of the data in this situation, but since Automation has no way of knowing the data is just a copy, it will marshal the copy back and forth between processes. The data is thus copied a total of three times.
Properties make objects easy to use, but setting properties for an out-of-process component can be slow. When a client is using an out-of-process component, the extra overhead of cross-process marshaling makes it faster to call one method with five parameters than to set five properties and then call a method.
In cases where users of your component will frequently set a group of properties before calling a particular method, you can add a group of optional parameters to the method, one for each property. If one of these parameters is supplied, the method sets the corresponding property before proceeding.
If you give the optional parameters the names of the properties they are used to set, they can be used as named parameters (also known as named arguments), as in the following code fragments:
' Code fragment from a hypothetical Widget class.
Public Load As Double
Public Torque As Double
' ...other property declarations...
Public Sub Spin(ByVal Iterations As Long, _
Optional Load As Variant, _
Optional Torque As Variant)
' Before spinning the Widget, set any property
' values that were supplied. The object self-
' reference Me distinguishes between parameter
' and property.
If Not IsMissing(Load) Then Me.Load = Load
If Not IsMissing(Torque) Then Me.Torque = Torque
' ...code to spin the Widget...
End Sub
' Code fragment from a client, calling the Spin method
' to spin the Widget 10000 times. Note the use of
' the named parameter to set the Torque.
Dim wdgNew As New Widget
wdgNew.Spin 10000, Torque:=27.6