Calling COM Components from .NET


Calling COM Components from .NET

Before I explain how to work with CDO or the other object models in Exchange, we need to look at Component Object Model (COM) interoperability in .NET because you will be calling objects through this interoperability layer. This section describes what COM interoperability is and how it works. We will look at using ADO against Exchange Server as an example of COM interoperability.

You will often call COM components such as CDO, Knowledge Management Collaboration Data Objects (PKMCDO), or ADO from Visual Studio .NET. Visual Studio .NET makes it easy to integrate COM components into your .NET applications by providing seamless interoperability between the managed code environment in .NET and the unmanaged code environment in COM. We will look at calling COM components from .NET and what really happens under the covers when you do this.

COM differs from the .NET Framework in a couple of ways. First, clients of COM objects must manage the lifetime of those objects. Meanwhile, the .NET Framework, through its garbage collection and memory allocation support, manages the lifetime of all objects in its environment.

Second, COM clients discover the services of the COM object they are working with by requesting an interface that provides the service and getting back a pointer to that interface if the service exists. .NET objects expose the description of the functionality of the object through reflection, which leverages assemblies that describe the object and its properties and methods .

Finally, COM uses pointers that assume that the object will remain in the same memory location. The .NET Framework can move objects around in memory at run time for performance reasons, and the framework will dynamically update all references to the object when it is moved.

So, to interoperate with COM, the .NET Framework must bridge the gaps between the .NET and COM runtime environments. To do this, .NET leverages runtime callable wrappers (RCWs) and COM callable wrappers (CCWs). All the wrappers do is expose the expected interfaces from .NET to COM and also marshal any calls and parameters between COM and the .NET runtime.

The RCW is what allows your .NET application to call a COM component. This wrapper abstracts and manages the calls and makes sure you do not have to code to the differences between COM and .NET.

The CCW reverses the process. It allows COM objects to call a method on a .NET object. As a result, the COM object will have no idea that its calls are actually going to a .NET component. The wrapper simulates all the necessary COM interfaces to interoperate between the COM and .NET environments.

To start taking advantage of calling COM components from your .NET applications, all you need to do is add a reference to the COM component. The easiest way to do this is by choosing Add Reference from the Project menu in Visual Studio .NET. In the Add Reference dialog box (shown in Figure 15-19), select your COM component and click Select.

click to expand
Figure 15-19: Adding a reference to a COM component from .NET

What really happens under the covers is that Visual Studio .NET scans the type library for the COM object and creates an interop .NET assembly for your COM component. If you look at the files included with your project, you will see the new files that Visual Studio .NET creates for working with the COM component.

If you want to manually import a COM component, you can use the type library importer tool included with the .NET Framework. This is a command-line tool that offers the same functionality as Visual Studio .NET. The tool has the filename tlbimp.exe. Most times, you will just use Visual Studio .NET to automatically import the type library.

Of course, you can go the other way and export your .NET object to COM. We covered this functionality when we built a COM add-in for .NET in Chapter 7.

Once you add your reference, you can call the COM interfaces for the COM component in the same way you would for any .NET component. The sample in Figure 15-20 shows an application that uses the COM version of ADO.

click to expand
Figure 15-20: The COM interoperability sample application

Note that the original ASP architecture used a single-threaded apartment (STA) model, whereas the new ASP.NET engine uses a multi-threaded apartment (MTA) architecture for better performance. As a result, before your ASP .NET applications can use legacy ADO code, you must tell ASP .NET to use the STA model. To do so, you use the ASPCOMPAT directive. You will want to use this directive with any COM interoperability objects that you use in ASP.NET, such as CDO 1.21 or CDO for Exchange.

 <%@ Page Language="VB" ASPCompat="True" %> 

Otherwise , you can call your ADO code just as you did in Visual Studio 6.0. The following is the relevant code from the sample application shown using Visual Basic .NET:

 Private Sub Submit1_ServerClick(ByVal sender As System.Object, _                                 ByVal e As System.EventArgs) _                                 Handles Submit1.ServerClick     'See what the user selected     If dropdownCommand.SelectedIndex = 0 Then         linkNext.Visible = False         'Retrieve properties using Record Object         Dim oRecord As New ADODB.Record()         'Figure out the type of connection to use         oConnection = GetConnection()         If Not (oConnection Is Nothing) Then             'Open the item             oRecord.Open(txtURL.Text, oConnection)             'Create a datasource to fill our datagrid             DataGrid1.DataSource = CreateDataSource(oRecord)             DataGrid1.DataBind()             oRecord.Close()             oConnection.Close()         Else             lblError.Text = "There was an error.  " & _                             "There is no valid Connection object."         End If     ElseIf dropdownCommand.SelectedIndex = 1 Then         'Perform Search         'Figure out the type of connection to use         oConnection = GetConnection()         If Not (oConnection Is Nothing) Then             'Create a simple search to use to show how it works             Dim strQuery As String = "SELECT * FROM SCOPE('SHALLOW " & _                                 "TRAVERSAL OF """ & txtURL.Text & """')"             oRS.Open(strQuery, oConnection)                  'Another way would be to populate one-way an OLEDB data             'adapter with an ADO RecordSet object as shown below             'Dim myDA As System.Data.OleDb.OleDbDataAdapter = _             '    New System.Data.OleDb.OleDbDataAdapter()             'Dim myDS As DataSet = New DataSet()             'myDA.Fill(myDS, oRS, "MyTable")             'DataGrid1.DataSource = myDS                  DataGrid1.DataSource = CreateDataSource(oRS)             DataGrid1.DataBind()             linkNext.Visible = True             Session("oRS") = oRS             Session("oConnection") = oConnection         Else             lblError.Text = "There was an error.  " & _                             "There is no valid Connection object."         End If     ElseIf dropdownCommand.SelectedIndex = 2 Then         'Create item using Record object         Try             linkNext.Visible = False             Dim oRecord As New ADODB.Record()             oConnection = GetConnection()             If Not (oConnection Is Nothing) Then                 'Open the item                 oRecord.Open(txtURL.Text, oConnection, _                     ADODB.ConnectModeEnum.adModeReadWrite, _                     ADODB.RecordCreateOptionsEnum.adCreateNonCollection)                      oRecord.Fields("urn:schemas:httpmail:subject").Value = _                     "Test Message"                 oRecord.Fields("urn:schemas:" & _                                "httpmail:textdescription").Value = _                                "This is a test message!"                 oRecord.Fields("http://schemas.microsoft.com/" & _                                "exchange/outlookmessageclass").Value = _                                "IPM.Post"                 oRecord.Fields("DAV:contentclass").Value = _                                "urn:content-classes:message"                 oRecord.Fields.Update()                 lblError.Text = "Successfully created item at " & _                                 oRecord.Fields("DAV:href").Value                 oRecord.Close()                 oConnection.Close()             Else                 lblError.Text = "There was an error.  " & _                                 "There is no valid Connection object."             End If         Catch             lblError.Text = "There was an error. Error#" & Err.Number & _                             " Description: " & Err.Description         End Try     End If End Sub      Private Function GetConnection() As ADODB.Connection     'Try to create the connection based off the type     'specified by the user and also using the URL     Dim oConnection As New ADODB.Connection()     Try         'Detect connection type         If radioMethod.SelectedIndex = 0 Then             'Use EXOLEDB             oConnection.Provider = "EXOLEDB.DataSource "         Else             'Use MSDAIPP             oConnection.Provider = "MSDAIPP.DSO"         End If              oConnection.Open(txtURL.Text)         GetConnection = oConnection     Catch         'Some error occured, maybe bad URL         'Return back an empty object         GetConnection = Nothing     End Try End Function      Function CreateDataSource(ByVal oRecord As Object) As ICollection     On Error Resume Next     Dim dt As New DataTable()     Dim dr As DataRow          dt.Columns.Add(New DataColumn("Name"))     dt.Columns.Add(New DataColumn("Value"))          Dim oField As ADODB.Field     For Each oField In oRecord.Fields         dr = dt.NewRow()         dr(0) = oField.Name         dr(1) = oField.Value         dt.Rows.Add(dr)     Next          Dim dv As New DataView(dt)     Return dv End Function           Private Sub linkNext_Click(ByVal sender As System.Object, _                            ByVal e As System.EventArgs) _                            Handles linkNext.Click     'Scroll to the next item     oRS = Session("oRS")     oConnection = Session("oConnection")     If Not (oRS.EOF) Then         oRS.MoveNext()         DataGrid1.DataSource = CreateDataSource(oRS)         DataGrid1.DataBind()     End If End Sub 

The ADO code in the sample is self-explanatory ”it is standard ADO code that you've seen before. The interesting part of the sample is the code that shows the returned data in a .NET DataGrid object and the code that shows the interoperability between a .NET DataSet and an ADO RecordSet or Record object.

Filling the DataGrid with ADO information is very straightforward. First, we need to get a DataView object that contains the data we want to display in our DataGrid . We do this by creating a DataTable object in the CreateDataSource function. A DataTable is a single table of in-memory data. The CreateDataSource function creates a new DataTable , manually adds new columns to the table, and then fills in the rows and columns with the information from our ADO RecordSet . The last step in the function is to create a new DataView on the DataTable and return that DataView to the caller of the function.

Once the main code gets back the DataView object, it sets the DataSource property of the DataGrid to the new DataView . The code then calls the DataBind method on the DataGrid object to bind the table to the grid control. The DataGrid is now populated with the data from our ADO RecordSet . Be sure to close all the connections to your databases when you use the COM version of ADO ”otherwise, those connections will remain open until the next garbage collection pass in the .NET Framework.

To fill a DataSet with an ADO RecordSet or Record is even easier. To allow this, the OLE DB .NET Data provider overloads the Fill method of the OleDbDataAdapter to allow this method to accept an ADO RecordSet or Record object. This is only a one-way operation, however. Your code must manually handle any updates to the DataSet after it has been filled. The OleDbDataAdapter will not handle making changes back through ADO to the source database.

Now that you've seen how to interoperate with ADO as a COM component, you can imagine how you can start using CDO, PKMCDO, or other COM object libraries in your .NET applications. To start taking advantage of these other libraries, you just need to add a reference to them from your Visual Studio .NET projects.




Programming Microsoft Outlook and Microsoft Exchange 2003
Programming MicrosoftВ® OutlookВ® and Microsoft Exchange 2003, Third Edition (Pro-Developer)
ISBN: 0735614644
EAN: 2147483647
Year: 2003
Pages: 227
Authors: Thomas Rizzo

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