he whole set of COM+ services are madeavailable in .NET via a set of classes gathered into theSystem.EnterpriceServices namespace. Such classes are implemented in the sharedassembly named System.EnterpriseServices.dll.
Almost all COM+ settings have their .NETattribute counterpart. Using .NET attributes at the proper level (method,interface, class, assembly) it’s possible to control any aspect of theexporting process of a .NET assembly to a COM+ application (read the regdb).There are just a few settings, like the process identity (the user/passwordpair) of a server COM+ application that can’t be, reasonably, set in the .NETassembly code.
Note that, in some cases, the .NET runtime reads the assembly metadata and not the COM+ catalog to apply COM+ related settings. It’s not quite clear if this is a beta bug or a desired behavior (maybe for performance reasons).In any case, it’s better to check that altering manually COM+ settings on a .NET derived COM+ application using the Component services snap-in has the expected results.
COM and COM+Attributes
You typically deploy a class that wants totake advantage of COM+ services (known as Enterprise Services in the .NET world)into a Class library assembly. The basic requirement is to derive the class fromthe ServicedComponent class (note that such class derives fromMarshalbyRefObject); of course, you do that after you’ve added a reference toSystem.EnterpriseServices.dll in to the project, as shown below.
In the next step you can go directly todefine and implement the methods your class exposes or (better) define aninterface first and then have the class implement it. In both cases .NETrequires you to specify how the class public methods or the interface should beexposed to the COM+ world. This means that you have to specify if the interfaceand/or the public class methods must be exposed as pure IUnknown, Dual or pureIDispatch interfaces. This can be done specifying, respectively, the InterfaceTypeAttributeand ClassInterface at the proper level as shown incode snipped below
_Public Interface Imyinterface Function mymethod(ByVal a As Long, ByRef x As String) As LongEnd Interface _Public Class class1 Inherits ServicedComponent ...End Class
The ComInterfaceType enumexposes the following entries
While theClassInterfaceType exposes
Note that None isthe default in beta2, hence you’ll have to specify a ClassInterface attributeto make the class methods available to COM. There is one, most time, undesiredside-effect in publishing class public methods directly: public methods ofclasses your class derives from are exposed as well; as a consequence, you’llget, at least, the public methods of System.Object exposed by your class “forfree”.
Defining explicitly the interface, and then having the class implementing it, isthe recommend solution; if you do so, you can omit the ClassInterfaceTypeon the class.
There are other COM related settings youcan declare explicitly like the interface and class UUID and the class progid;again these settings are applied via attributes
_Public Class class1 Inherits ServicedComponentEnd Class
COM+ related attributes are declared in asimilar way. If you omit an attribute the .NET framework make a default choice(as the COM+ runtime does when importing a class into the COM+ catalog); The.NET framework is clever enough in this situation to adapt its default choicenot to be inconsistent with explicitly declared attribute, e.g., when a classdeclares to require transaction, its synchronization attribute is set torequired.
COM+ application level settings are setvia attributes declared at the assembly level, that is in the Assemblyinfo.vbfile. In this sample the assembly will be deployed into a library COM+application whose name is NetComponent
Any Serviced component must be hosted intoa strongly signed assembly; to do this you must first run the sn.exe utilitywith the -k switch to generate a file containing the public/private key pair.You then sign the assembly with the key pair adding the following line in theassemblyinfo.vb file
It’s highly recommended that you deploythe assembly into the GAC, if you fail to do so, just remember that the .NETruntime binding policy applies to such assembly as well: having the assemblyregistered in a COM+ application is not what a com developer tends to think.From a COM+ perspective all registered serviced components are implemented inmscoree.dll (more on it later).
Assembly containing serviced classes mustgo through a registration process to insert the required info into the COM+catalog and in the registry in order to make them visible to the COM+ runtime.Assemblies are registered using the Regsvcs.exe utility whose basic command linesyntax is:
In order to provide XCOPY deploymentfeatures for serviced components, Microsoft has tried to automate thisoperation: while binding to a serviced component, the .NET runtime registers theassembly transparently if the registration info can’t be found. Unfortunatelythis process, that requires Administrator rights, is run under the identity ofthe caller; hence it will likely fail in a real world scenario.
It’s very important to stress the factthat a .NET serviced component still run into the .NET runtime environment; allsuch settings just declare the properties of the its associated COM+ contextthat will run into the unmanaged world. The picture below gives you a clue ofthe binding process to a serviced .NET component.
As you can see any creation of a servicedcomponent implies a call into the unmanaged world (CoCreateInstance). All suchcalls are dispatched to the mscoree.dll since all serviced components areimplemented into this dll, as far as COM+ is concerned.
Having setup the proper context,mscoree.dll calls into the managed world to create the class and pass it backthe obtained proxy to the .NET caller.
This is the VB6 pseudo-code themscorlib.dll uses to create the managed object while running into the unmanagedworld.
Dim crs As New mscoree.CorRuntimeHostcrs.StartDim domain As AppDomaincrs.CurrentDomain domainDim objh As ObjectHandleSet objh = domain.CreateInstanceFrom("c:myassembly.dll", "vblibrary.Class1")Dim obj As ObjectSet obj = objh.Unwrap
Once you’ve registered the assemblycontaining the serviced class, a local .NET client can bind to it as if it wasdealing with a standard .NET assembly. In case a serviced class holds expensiveresources, you can explicitly release it, instead of relying on the Garbagecollector, calling the DisposeObject method that’s inherited from theServicedComponent class (calling the Dispose method would sort the same effectbut requiring an additional context switch).
If you mark your class with the [JustInTimeActivation]attribute you can avoid having the client calling DisposeObject: the .NETruntime automatically release the object when, within a method call, ContextUtil.SetComplete/SetAbortis called (or the method is marked as [AutoComplete])
The same apply when dealing with pooledobject: If JITA is used, the object is returned to the pool when the done bit isset to true (Setcomplete/Setabort is called or the method is marked as [AutoComplete]);if it’s not the case you must call DisposeObject explicitly, if you fail to doso, the object is not returned to the pool.
Invoking ServicedComponents Remotely
It’s likely that, in most scenarios, aserviced component must be invoked remotely. You have two options to do so:resort to .NET remoting or DCOM.
No special steps are required to invokethe object via .NET remoting: you set up a listening process, declare yourpreferences in the protocol and formatter to be used and you’re done. Thereare just a couple of bugs of beta2 you’ve to be aware:
- You cannot invoke a serviced component via .NET remoting if the component is deployed in a server COM+ application
- Calling DisposeObject on the object wrapper hold by the client has no effect: you must resort to JITA if you want the object to be deterministically released (or returned to the pool)
Invoking the serviced component via DCOMimplies referencing it as if it was a COM component (thus involving .NETinterop). You can do this provided you’ve run the COM+ application proxy onthe client machine. Unfortunately there is another beta2 bug that prevents youfrom doing this on Windows 2000, still the process works on Windows XP beta2.
One last consideration: know thattransaction flow cannot propagate across .NET remoting calls, consider this factwhen you must choose which remoting mechanism adopt.
It’s pretty likely that .NET willprovide built-in enterprise services in the future; still, for the while,Microsoft has resorted completely to the good old COM+ framework to provide suchservices. Provided that the bugs that affect the b2 build will be removed in theV1 release, everyone will certainly appreciate the attribute-oriented approachexposed by the .NET framework