Building the .NET Component

Snoops

   

 
Migrating to .NET: A Pragmatic Path to Visual Basic .NET, Visual C++ .NET, and ASP.NET
By Dhananjay Katre, Prashant Halari, Narayana Rao Surapaneni, Manu Gupta, Meghana Deshpande

Table of Contents
Chapter 9.  Migrating to Visual C++ .NET


Now armed with the knowledge of tlbexp.exe , regasm.exe , and gacutil.exe , we are ready to build the .NET component. The steps involved in its creation follows :

  • Define an interface in which you declare the method that you want the COM client to access. The visibility specifier of this interface and its method should be public.

  • Define a managed class with a default constructor that you want to be exposed as a COM coclass. This managed class should be derived from the interface declared in the preceding . Both the class and the constructor should have the visibility specifier as public.

  • Create a shared key file using the shared name ( sn.exe ) utility. The following syntax should be given to create the shared key file at the command prompt:

     sn k Test.snk 
  • Add this shared key file in the AssemblyInfo.cpp file in the A ssemblyKeyFileAttribute as follows:

     [assembly:AssemblyKeyFileAttribute("Test.snk")]; 
  • Register the assembly using the assembly registration tool ( regasm.exe ) as follows:

     regasm Test.dll /tlb: Test.tlb 
  • Now either place the assembly in the path of the client or place it in the global assembly cache using gacutil.exe as follows:

     gacutil /i Test.dll 
  • Import the type library of the .NET component in the COM client. This is achieved by using the #import directive as follows:

     #import  "Test.tlb" 

Defining the COM Creatable Class

Because .NET Framework and the CLR take care of most of the peripheral details that one has to worry about in COM, it has become easier to define a class as a COM component by deriving it from an interface that has a public visibility specifier. All that is required is a public class with a public constructor and the class can be exposed as a COM coclass. Any nonstatic member method with a public visibility will be exposed as the COM's method. This method is declared in the interface. We will define a simple class to make things clearer to you:

 graphics/icon01.gif namespace TestCOM  {      public __gc __interface ITestCOM      {      public:            void HelloWorld();      };  public __gc class CTestCOM : public ITestCOM      {      public:            CTestCOM(){}            void HelloWorld()            {                  Console::WriteLine("Hello World!!!");            }      };  } 

When a COM client calls a .NET component, it results in the generation of a CCW . This wrapper is generated by the runtime. The CCW is compatible because it is generated by the runtime after reading the type information for the component from its assembly metadata. All the managed types, methods , properties, and events that you want to expose must be public. This wrapper acts as a proxy for the COM client. A default interface of the class will also be created by this wrapper; it consists of the name of the class prefixed with an underscore . For example consider the COM class declared previously; the default interface for this that will be generated by the CCW will be _CTestCOM.

The runtime generates a compatible CCW after reading the type information for the .NET component from its assembly metadata. One CCW is created for each .NET component regardless of the number of COM clients requesting the services. It also helps in translating and marshalling the data between the managed and unmanaged code. The CCW is an unmanaged COM object that is allocated on the unmanaged heap and maintains references of the .NET object in the managed environment. The CLR will work with the CCW to keep its references on the .NET object up to date. The CCW maintains the reference count like other COM objects do. Being an unmanaged COM object, the CCW must be destroyed when the COM client releases its reference because it cannot be garbage collected. However, destruction of the CCW marks the .NET component that it wraps for garbage collection.

By default the ProgID of the class will be composed of the namespace and the class name. If a GUID is not specified, one will be created based on the name of the class. This GUID changes only if the definition of the class changes. In our example the ProgID will be TestCOM.CTestCOM, the default class interface will be _CTestCOM, and the HelloWorld() method will be exposed as a method to the COM clients. See Figure 9-2.

Figure 9-2. Functioning of the CCW .

graphics/09fig02.gif

Code Sample

By now you must be familiar with how to call a .NET component from a COM client. The following code sample will make this clearer. In the following code sample we will declare a .NET component class that will be called by a COM client in Visual C++ 6.0. A detailed explanation of the steps to be taken will be given as you proceed through the example.

In this example we will first create a .NET component. This .NET component is made in the MyComServer namespace and has a public interface IEmployee that has a function called IsValidEmployee() . A public managed class CEmployee is derived from the managed interface IEmployee and it implements the function IsValidEmployee() . This function will connect to a database and will check whether the employee name that is passed to it exists in the database that it connects to. If it does not exist in the database, it returns False, otherwise it will return True. The code for the .NET component follows:

 graphics/icon01.gif namespace MyComServer{      public __gc __interface IEmployee{      public:  void IsValidEmployee(String* empName, bool &IsPresent);      };  public __gc class CEmployee : public IEmployee{      public:            CEmployee(){}  void IsValidEmployee(String * empName, bool &IsPresent){  SqlConnection *ConnectionObj = new  SqlConnection("server=  pc-p3793;uid=sa;pwd=;database=pubs");  ConnectionObj->Open();  SqlDataAdapter *EmployeeDataAdapter = new  SqlDataAdapter(String::Format("Select fname from employee  where fname='{0}'",empName),ConnectionObj);  DataSet *EmployeeDataSet1 = new  DataSet("Employee");  EmployeeDataAdapter->Fill  (EmployeeDataSet1,"Employee");        if(EmployeeDataSet1->Tables->  get_Item(0)->Rows->Count > 0){                        IsPresent = true;                  }                  else{                        IsPresent = false;                  }                  ConnectionObj->Close();      }      };  } 

Build this piece of code. The key pair is created using the command prompt as shown:

 sn -k MyComServer.snk 

Use the AssemblyKeyFileAttribute to add the public key information into the metadata for the assembly:

 [assembly:AssemblyKeyFileAttribute("MyComServer.snk")]; 

Build the .NET component again. Now we have to generate the type library for this component and register it in the system registry so that it can be used by the COM client. The command is given as follows:

 regasm MyComServer.dll /tlb:MyComServer.tlb 

Install the assembly in the global assembly cache:

 gacutil /i MyComServer.dll 

The .NET component is now ready to be used by a COM client. Now let us create the COM client that will call this .NET component. The client is created in Visual C++ 6.0. In the COM client you have to import that type library that has been created by the #using directive. Now to obtain the method, the .NET component class creates a smart pointer to the interface in which the method has been declared and passes the universal unique ID of the .NET component. The code sample of the client is as follows:

 graphics/icon01.gif #import "MyComServer.tlb"  using namespace MyComServer;  int main(int argc, char* argv[]){      CoInitialize(NULL);      try{            short retVal = false;            IEmployeePtr pServer(__uuidof(CEmployee));            pServer->IsValidEmployee("Anabela",&retVal);            if(retVal == true){                  cout << "Valid Employee";            }            else{                  cout << "Invalid Employee";            }      }      catch(...){            cout<<"Managed Exception"<<endl;      }      CoUninitialize();      return 0;  } 

Output:

 Valid Employee 

This code is in the directory NetComponent_ComClient .

We have already seen that .NET and COM components differ . To overcome these differences the CLR has provided certain wrapper classes that give both the managed and unmanaged client the feeling that they are calling objects within their own environments. Summarizing, we can say that the RCW is created whenever a managed client calls a COM object method, and the CCW is created whenever a .COM client calls a .NET component.


Snoops

   
Top


Migrating to. NET. A Pragmatic Path to Visual Basic. NET, Visual C++. NET, and ASP. NET
Migrating to. NET. A Pragmatic Path to Visual Basic. NET, Visual C++. NET, and ASP. NET
ISBN: 131009621
EAN: N/A
Year: 2001
Pages: 149

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net