Krzysztof Mossakowski
For Students
  • Creating a serviced component
    • Create a new project of Class Library type, name it MySuperComponent.
    • Add a reference to the System.EnterpriseServices.
    • Modify the class.
      • Change the name of the class to MySuperClass.
      • Inherit the class from ServicedComponent class.
      • Create a public method to do something, e.g.:
        public string UpperCase(string s)
        {
            string s1 = s.ToUpper();
            return s1;
        }
        
    • Make a strong name for the project.
      • Create a key pair (in the project's folder): sn.exe -k key.snk
      • Sign the assembly using Signing / Sign the assembly from project's properties (check the checkbox and choose created key.snk file as a strong name key file).
    • Compile the project.
    • Install it into the Global Assembly Cache: gacutil.exe /i MySuperComponent.dll
    • Try to register the component as a COM+ component: regsvcs.exe /fc MySuperComponent.dll
      You will see an error - we need to change some attributes:
      • Remove the ComVisible(false) attribute (in the AssemblyInfo.cs file).
      • Add 3 new attributes in the AssemblyInfo.cs file (the GUID can be generated using the Create GUID tool, available in Tools menu in Visual Studio):
        [assembly: ApplicationAccessControl(true)]
        [assembly: ApplicationName("MySuperApplication")]
        [assembly: ApplicationID("392587A7-5AEC-42e8-AF01-F6BCACA3ED1E")]
        
      • Try to register the component again:
        D:\PW\exercises\complus\MySuperComponent\bin\Debug>regsvcs.exe /fc MySuperComponent.dll
        Microsoft (R) .NET Framework Services Installation Utility Version 2.0.50727.42
        Copyright (c) Microsoft Corporation.  All rights reserved.
        
        Installed Assembly:
                Assembly: D:\PW\exercises\complus\MySuperComponent\bin\Debug\MySuperComponent.dll
                Application: MySuperApplication
                TypeLib: D:\PW\exercises\complus\MySuperComponent\bin\Debug\MySuperComponent.tlb
        
      • If there are still errors, try to remove the AssemblyVersion and the AssemblyFileVersion attributes (from the AssemblyInfo.cs file).
    • Open the Component Services explorer C:\WINDOWS\system32\Com\comexp.msc (you can find it also in the group of Administrative Tools in the Control Panel).
    • Add an interface with declaration of the UpperCase method and implement the interface by the class:
      public interface IMySuperInterface
      {
          string UpperCase(string s);
      }
      
      [Description("This is my super class")]
      public class MySuperClass : ServicedComponent, IMySuperInterface
      {
          [Description("Very useful method")]
          public string UpperCase(string s)
          {
              string s1 = s.ToUpper();
              return s1;
          }
      }
      
    • Compile and register again.
    • Refresh the view in the Component Services explorer and note that the method is visible now.


  • Using a serviced component
    • Add a new project (Console Application for simplicity) to the solution with the name MyTester.
    • In the second project add a reference to the first one.
    • Add also a reference to the System.EnterpriseServices.
    • Add a code testing the UpperCase method to the Main method:
      MySuperComponent.MySuperClass c = new MySuperComponent.MySuperClass();
      string s = Console.ReadLine();
      Console.WriteLine(c.UpperCase(s));
      
    • Compile and run the project, note the great result (our text in upper case).


  • Using Loosely Coupled Events
    • In this point we will create two completely separated application, and will send events from one application to the second one. The COM+ component will make it possible.
    • Create a new Serviced Component.
      • Add a new project of Class Library type (name it 'MySuperEvent') to the existing solution.
      • Change the name of the class to MyEventClass.
      • Remove the ComVisible(false) attribute from the AssemblyInfo.cs file.
      • Copy the following attributes from the MySuperComponent (the same name and ID of the application must be used, because we want to store both components in the same COM+ application - as you can see, the COM+ application is just a set of COM+ components):
        [assembly: ApplicationAccessControl(true)]
        [assembly: ApplicationName("MySuperApplication")]
        [assembly: ApplicationID("392587A7-5AEC-42e8-AF01-F6BCACA3ED1E")]
        
      • Sign the project with a strong name key.
      • Add a reference to the System.EnterpriseServices.
      • Derive the class from the ServicedComponent class.
      • Register the component: regsvcs.exe /fc MySuperEvent.dll
      • Check in the Component Services window if both components are registered in the same application
      • Replace the code with the following:
        using System.EnterpriseServices;
        using System.Runtime.InteropServices;
        
        namespace MySuperEvent 
        {
            [Guid("BD40FDD7-42A3-44A4-8A17-8748123E3E5D")]
            public interface IMyEvent
            {
                void TheEvent(string s);
            }
        
            [EventClass]
            [Guid("203AF033-C4ED-403D-8E6B-12C7F2310916")]
            public class MyEventClass : ServicedComponent, IMyEvent
            {
                public static string FiringInterfaceID = "{BD40FDD7-42A3-44A4-8A17-8748123E3E5D}";
                public static string EventClassCLSID = "{203AF033-C4ED-403D-8E6B-12C7F2310916}";
        
                public void TheEvent(string s) 
                { 
                }
            }
        }
        
    • Modify the MyTester project.
      • Add a reference to the MySuperEvent project.
      • Modify the Main method:
        MySuperComponent.MySuperClass component = new MySuperComponent.MySuperClass();
        MySuperEvent.IMyEvent ev = new MySuperEvent.MyEventClass() as MySuperEvent.IMyEvent;
        string s;
        do {
            s = Console.ReadLine();
            ev.TheEvent(s);
            Console.WriteLine(component.UpperCase(s));
        } while (s != "END");
        
    • Add a new Windows Forms project to the solution with MyWatcher name.
      • Add two buttons to the form: 'Subscribe' and 'Unsubscribe'.
      • Add a reference to the MySuperEvent project.
      • Implement the MySuperEvent.IMyEvent interface by the form class:
        public partial class Form1 : Form, MySuperEvent.IMyEvent
        
      • Add a reference to the 'COM+ 1.0 Admin Type Library' (from the COM group in the Add Reference window).
      • Add the COMAdminWrapper.cs file to the project.
      • Add a reference to the System.EnterpriseServices.
      • Add the following code to the form class:
        private Guid subscriptionID;
        
        private void subscribeButton_Click(object sender, EventArgs e)
        {
            subscriptionID = Guid.NewGuid();
            MySuperEvent.IMyEvent subscriberInterface = this as MySuperEvent.IMyEvent;
            COMAdmin.ICOMAdminCatalog pICat = new COMAdmin.COMAdminCatalogClass() as COMAdmin.ICOMAdminCatalog;
            COMAdminWrapper.Subscriptions.AddTransientSubscription(
                pICat,                                      //ICOMAdminCatalog
                subscriptionID.ToString(),                  // Subscription name
                MySuperEvent.MyEventClass.EventClassCLSID,  // EventClass to subscribe to
                null,                                       // PublisherID
                MySuperEvent.MyEventClass.FiringInterfaceID,// FiringInterfaceID
                subscriberInterface,                        // the transient interface pointer on this form
                null,                                       // methodName
                null,                                       // FilterCriteria
                null,                                       // Publisher property name
                null);                                      // publisher property value
        }
        
        private void unsubscribeButton_Click(object sender, EventArgs e)
        {
            COMAdmin.ICOMAdminCatalog pICat = new COMAdmin.COMAdminCatalogClass() as COMAdmin.ICOMAdminCatalog;
            COMAdminWrapper.Subscriptions.RemoveTransientSubscription(pICat, subscriptionID.ToString());
        }
        
        public void TheEvent(string s)
        {
            MessageBox.Show("TheEvent with: " + s);
        }
        
    • Run both MyWatcher and MyTester applications and test the events.