Introduction
It has been always difficult to distribute COM+ LCE over the network for the following two reasons:
- When COM+ runtime tries to push an event over to a remote machine, Microsoft solution developers mostly use COM+ Application export utility to generate a proxy and install it on the remote machine. There is simply no easy programming model to call a Remote Object through DCOM in pre-.NET world. This creates a daunting task of installation when subscribers are spread out to over several hundred machines.
- COM+ cannot handle high rate of publishing as mentioned in Juval Lowy's book "COM and .NET Component Services", page 256. This is because DCOM is not a reliable messaging protocol and has to push events constantly to COM+ until bloating memory results in crash.
This article attempts to enhance DCOM with MSMQ on .NET platform. The approach will introduce reliable messaging into COM+ LCE and suggest a clean-easy-to-follow programming model for distributing COM+ LCE.
Basic Platform Requirement:
The concept, programming model, and code sample require the following setup to work:
- MSMQ 3.0 is installed with Active Directory Integration. This basically requires that server OS must be Windows 2003 and workstation OS must be Windows XP.
- Visual Studio .NET 2003 and .NET Framework 1.1 are required for using the sample code.
- The underlying network has no firewall between publishers and subscribers.
Sample code may work under other configurations.
COM+ LCE:
Any COM+ Component (a.k.a. ServicedComponent
) can be attributed into an Event type:
public interface ILCEEvent
{
void Fire (string Message) ;
}
[EventClass()]
public class LCEEventSink : ServicedComponent, ILCEEvent
{
public void Fire(string MessageString)
{
throw new NotImplementedException("...");
}
}
Although the attribute [EventClass()]
does not affect Event type's COM+ attributes as seen in Component Services Explorer MMC, when its methods get called, this type does instruct CLR runtime to invoke code in other .NET types that implement the same interface (a.k.a. subscriber):
public class TestDLCESubscriber : ServicedComponent, ILCEEvent
{
public void Fire(string Message)
{
EventLog.WriteEntry("Application","...");
}
}
This COM+ Event model works perfectly on a single machine. But when a subscriber resides on a remote machine, COM+ will have to marshal the invocation on Event Type over to that machine. As described above, this particular marshalling using DCOM has many practical problems and we need to enhance it with MSMQ reliable messaging. This enhancement is called COM+ Queued Component and we will describe its usage now.
COM+ Queued Component:
Almost all COM+ applications and their components can be attributed into Queued Components using Component Services Explorer MMC. This can also be done programmatically:
[assembly : ApplicationName("DistributedLCEQComp")]
[assembly : ApplicationQueuing(Enabled=true, QueueListenerEnabled=true)]
namespace JQD
{
public interface ILCEEventQC
{
void Fire(string Message, string MachineNameList);
}
[InterfaceQueuing(Enabled=true,Interface="ILCEEventQC")]
public class LCEEventQC : ServicedComponent, ILCEEventQC
{
public void Fire(string MessageString, string MachineNameList)
{
if (MachineNameList.Length==0)
{
ILCEEvent lce = (ILCEEvent) new LCEEventSink();
lce.Fire(MessageString);
}
....
To invoke this Queued Component in .NET and MSMQ 3.0 implementation, we need to use COM API defined in System.Rutime.InteropServices
namespace and Queued Component Interface method ILCEEventQC::Fire
:
string moniker="queue:FormatName=Direct=OS:"+machineName+
@"\distributedlceqcomp/new:JQD.LCEEventQC";
ILCEEventQC lce = (ILCEEventQC) Marshal.BindToMoniker(moniker);
lce.Fire(MessageString, "");
Marshal.ReleaseComObject(lce);
As a result of this COM API call, MSMQ writes to Message Queue on the remote machine specified by machineName
. And you can observe the message by checking an entry in public queue "distributedlceqcomp
" on the remote machine for each publishing of event, although the entry will be gone quickly after local LCE processes it.
Subsequently, the queued component for this public queue picks up the message and calls COM+ LCE interface ILCEEvent::Fire
and therefore informs local LCE event subscribers. It is important to remember that we no longer depend on COM+ to inform remote subscribers. Instead, we explicitly invoke remote Queued Component. This programming model enables a more reliable and scalable network communication than pure DCOM protocol approach. In fact, to scale out COM+ LCE event, we just need to add a list of machine names to the moniker binding code above and let MSMQ infrastructure to do the rest.
Sample Code Installation and Test:
- Delete existing COM+ Application "DistributedLCEQComp" and "DistributedLCESink" from Component Services MMC. Also delete all public and private queue pre-fix with "distributedlce" from Computer Management MMC under Message Queuing section.
- Make sure MSMQ 3.0 is installed with Active Directory Integration, by checking if you can access public queue using Computer Management MMC.
- To install the LCE Event Sink on Windows 2003 Server, copy the deployment directory in the sample code on that server and run install.bat. This will install two DLLs in the GAC and onto COM+. You may need to modify install.bat to fit your OS installation.
- Open Component Services Explorer MMC and find COM+ application. You should see two newly created COM+ applications "DistributedLCEQComp" and "DistributedLCESink". Right click on the first COM+ app, select Properties, Activation, check "Run Application as NT Service".
- Following 3, click on "Setup New Service", choose "Automatic" on Startup Type and check "Message Queuing" on Dependencies. Then click "Create", "OK". Then start this COM+ app.
- Repeat 3 and 4 for the other COM+ application "DistributedLCESink".
- To install testing event Subscriber on Windows Server 2003, copy TestDLCESub.dll to the server and run the command:
C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\regsvcs.exe TestDLCESub.dll
This will create a COM+ app "TestDLCESub". You then need to set up a subscription by following through New Subscription creating wizard. Do remember to check "Enable this subscription immediately".
- On the Windows XP machine where publisher resides, copy LCEEventQComponent.dll to a directory and run command:
C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\gacutil.exe
/i LCEEventQComponent.dll /f
C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\regsvcs.exe LCEEventQComponent.dll.
- To run Test Publisher, copy TestPublisher.exe in the sample code to the Windows XP machine and run it.
Your test should result in entries created in EventLog on the remote Windows 2003 machine, and you will also see some entries on the XP machine. Note: for real production installation, you may need to write an installation class and utilize COM+ export feature to automate the steps specified here.
Walk through COM+ LCE Code Execution Path:
When the publisher on Windows XP machine executes code to invoke LCEEventQC::Fire
method, COM+ will write a MSMQ Message to "distributedlceqcomp" public queue on the remote Windows 2003 machine. This queue is linked to COM+ Queued application "DistributedLCEQComp". This COM+ application immediately picks up the message and invokes the same method on itself since it runs as NT services. Inside this method, Event Sink is contacted and LCEEventSink::Fire
method is called, resulting in the invocation of TestDLCESubscriber::Fire
method through COM+ LCE infrastructure on the Windows 2003 machine locally.
Conclusion:
Using COM+ Queued Component to replace DCOM when distributing COM+ LCE over intranet seems to be a more scalable and reliable solution.