var Name : String var Age: Integer = 24 Functions also have the addition of data types: function foo : Boolean { return True; } Implicit Type Conversion Data types can be implicit
Trang 1Response.Write("Y=" & Y.ToString())
The output here will be 3, rather than 4 To correct this you need to change the procedure declaration to:
Sub Foo(ByRef X As Integer)
Default Properties
The use of default properties is not allowed in Visual Basic NET This doesn't affect NET components, since there's no way
to define a default property, but it has an impact when accessing COM objects For example, the following would not be allowed:
Dim rs As New ADODB.Recordset
Dim Name As String
Trang 2rs.Open(" ", " ")
Name = rs("Name")
The last line in NET would be:
Name = rs("Name").Value
While this does mean more typing, it makes the code much more explicit, and therefore less prone to potential errors
Set and Let
The Set and Let keywords are no longer required for object references For example, the following is now the accepted syntax:
Dim conn As SQLConnection
conn = SQLConnection
There's no need for the Set keyword before the object assignment
Single and Multiple Lines
In Visual Basic NET, all If statements must be constructed across multiple lines For example, in VBScript you could do this:
Trang 3var Name : String
var Age: Integer = 24
Functions also have the addition of data types:
function foo() : Boolean
{
return True;
}
Implicit Type Conversion
Data types can be implicitly converted using the following syntax:
TypeName(expression)
For example:
var d : double = 123.456
Trang 4var i : Integer = int(d)
Interoperability
No matter how much we'd like to do all our coding in NET, we have to face reality There is an enormous amount of traditional ASP and COM code being used in web applications, and businesses cannot afford to just throw that away The success of MTS/COM+ Services as a middle-tier business object layer has led to a large number of COM objects being used
as, among other things, data layers, abstracting the data management code from the ASP code With NET, Microsoft has provided good interoperability for several reasons:
To preserve existing investment Compatibility with existing applications means we can continue to use existing code, as well as preserve our existing investment
Incremental Migration There is no need to migrate everything at once if your new code can exist alongside other applications
Some code will never change There is probably plenty of code where the investment, time, or skill to migrate is not available
Although NET is independent from COM, Microsoft has realized the need for interoperability, and provided ways to use not only COM objects from within NET, but also NET components from within COM They've realized that there had to be a way to call down to the Windows API, for those that need to
This chapter just gives an introduction into the interoperability issues For a more detailed look consult the Wrox Press book Professional Visual Basic Interoperability - COM and VB6 to NET, ISBN 1-861005-65-2
This chapter uses the term COM as a generic term for COM and COM+ purely to improve legibility From the
interoperability point of view there is no difference
Crossing the Boundary
We know that NET code is managed by the CLR, and that COM code is not, so there has to be some way to cross the managed/unmanaged code boundary One of the major problems is the conversion of data types, but the CLR handles this for us:
Trang 5When crossing this boundary we have to think about the differences between the two systems Architecturally these are:
Unmanaged Code has … Managed Code has …
Binary standard Type standard Type libraries Meta data Immutable types Version binding DLL hell Versioned assemblies Interface based Object based
HResults Exceptions
Additionally, there are the programming differences:
Unmanaged Code has … Managed Code has …
CoCreateInstance new operator QueryInterface Cast operator Reference counting Memory management and garbage collection
Trang 6GetProcAddress Static methods The unmanaged way of doing things doesn't affect ASP or ASP.NET, but does affect those of us who also write COM and use components
Data Type Marshalling
When we cross the managed/unmanaged boundary, the wrappers automatically perform data type mapping for us So, although we don't need to know how this works, it's useful to see what language types map to in NET There are two kinds
of data types as far as marshalling goes:
Blittable types These are the same on both sides of the boundary, and therefore don't need any conversion
Non-Blittable types These are different on either side of the boundary, and therefore require conversion The table below details the pre-.NET data types, and what they map into in NET:
unsigned short Not supported UInt16 Yes
unsigned int64 Not supported UInt64 Yes
IUnknown object UnmanagedType.IUknown No
Simple arrays (single dimensional arrays of blittable types) are themselves defined as blittable types
Trang 7Custom Type Marshalling
For blittable types, the marshaller always knows both the managed and unmanaged type, but this isn't so for non-blittable types (such as strings or multi-dimensional arrays) By default the following conversion takes place:
Managed Type Unmanaged Type
Boolean A 2 or 4 byte value (VARIANT_BOOL or Win32 BOOL), with True being 1 or -1
Char A Unicode or ANSI char (Win32 CHAR or CHAR)
String A Unicode or ANSI char array (Win32 LPWSTR/LPSTR), or a BSTR
Object A Variant or an interface
Class A class interface
Value Type Structure with fixed memory layout
Array Interface or a SafeArray
For non-blittable types, we can specify how they are marshalled across the boundary This is really beyond the scope of this book, but is well detailed in the NET SDK help file, under "Programming with the NET Framework", "Interoperating with Unmanaged Code", and "Data Marshalling"
HRESULTS
In Windows, the standard method of handling errors is via the use of HRESULTS When crossing the boundary to NET these are automatically converted to exceptions, with the HRESULT details being stored as part of the exceptionobject This means that we can use COM objects without sacrificing the structured exception handling that NET brings us For this to work the COM object must support the ISupportErrorInfo and IErrorInfo interfaces
Using COM Objects from NET
Using COM components from NET is extremely simple, as there is a tool that takes a COM component or type library and creates a managed assembly (a callable wrapper) to manage the boundary transition for us The diagram below shows how this wrapper is used:
Trang 8From the programming perspective all we have to do is call methods and access properties as we would with the COM component The difference is that we'll be calling the wrapper class, which will take the NET types, convert them to COM types, and call the COM interface methods The CLR maintains the reference to the COM object, so COM reference counting works as expected, while also providing the simplicity of garbage collected references for the NET usage of the object
There are several ways in which we can generate the wrapper class:
Adding a reference in Visual Studio NET
Using the type library import tool
Using the type library convert class
Creating a custom wrapper
Of these, the first two are by far the easiest
Using Visual Studio NET
In Visual Studio NET all we have to do is create a reference to the COM object, and the wrapper class is created for us First select References from the Solution Explorer, and then pick Add Reference…
Trang 9Then, from the dialog that appears, select the COM tab, and pick your COM object:
Once you've clicked Select and then OK, the reference is added The wrapper class (in this case it would be ADODB.dll)
is placed in the bin directory of the application
The Type Library Import Tool
If you don't have Visual Studio NET (or are a die-hard Notepad user) then you can use the type library import tool to
Trang 10create the wrapper class for you The syntax is:
tlbimp TypeLibrary [Options]
where Options can be:
/out:FileName The filename of the wrapper assembly to create
/namespace:Namespace Namespace of the assembly to be produced
/asmversion:version Version number of the assembly to be produced
/reference:FileName Assembly filename used to resolve references This can be specified multiple times for
multiple references
/publickey:FileName Filename containing the strong name public key
/keyfile:FileName Filename containing the strong name key pair
/keycontainer:FileName Key container holding the strong name key pair
/delaysign Force strong name delay signing
/unsafe Produce an interface without runtime security checks
/nologo Don't display the logo
/silent Don't display output, except for errors
/sysarray Map COM SafeArray to the NET System.Array class
/verbose Display full information
/primary Produce a primary interop assembly
/strictref Only use assemblies specified with /reference.
By default the output name will be the same as the COM type library, not the filename For example:
tlbimp msado15.dll
will produce a wrapper assembly called ADODB.dll, not msado15.dll
The resulting assembly can then be copied into the application bin directory (or installed in the Global Assembly Cache), and referenced as with other NET assemblies:
<%@ Import Namespace="ADODB" %>
The Type Library Convert Class
Trang 11The System.Runtime.InteropServices namespace contains a class called TypeLibConverter, which provides methods to convert COM classes and interfaces into assembly meta data This is really only useful if you are building tools that examine COM type libraries at runtime, and is outside the scope of this book
Custom Wrappers
If your COM component doesn't have a type library then it's possible to create a custom wrapper that directly calls the COM component This is outside the scope of the book, but for more information, see the topics "Programming with the NET Framework", "Interoperating with Unmanaged Code", and "Customizing Standard Wrappers" in the SDK help file
Using the Wrapper Assembly
Using the wrapper assembly is simply a case of treating it like any other managed assembly For example, if you import the ADO namespace, you can use it in your ASP.NET pages like so:
<%@ Import Namespace="ADODB" %>
<html>
<script language="VB" runat="server">
Sub Page_Load(Sender As Object, e as EventArgs)
Dim rs As New ADODB.Recordset
rs.Open("publishers", "Provider=SQLOLEDB; Data Source=.; " & _
"Initial Catalog=pubs; User Id=sa")
While Not rs.EOF
Response.Write(rs.Fields("pub_name").Value & "<br/>")
rs.MoveNext()
Trang 12Deploying Applications that Use COM
However great the COM interoperability story is, it doesn't get around the fact that COM components need to be registered This is not a fault of NET, more an issue of the way COM works, and you don't need to do anything other than the standard
COM registration However, the big problem this causes is with the xcopy deployment model, for which it isn't suitable
You can still xcopy the deployment, but you'd need to provide some form of script to register the COM components before
the application is activated
Using NET Components from COM
The interoperability story doesn't end with using COM code in NET, as the reverse is also possible This allows the new
language and class features to be used, but without getting rid of old applications The workings of the wrapper class are
shown below - it marshals the COM calls through to the managed object
Trang 13The story is very similar to its opposite that we've just examined, as COM type libraries are created for the NET assemblies The difference is that there's slightly more work, as you have to explicitly decide which interfaces and methods you want exposed to COM This is a crucial point, because for NET components to be available in COM they have
to have an Interface (see Chapter 3 for more details on Interfaces), and there are two ways to have this exposed - manually or automatically
Manually Created Interfaces
Manually creating interfaces means you use the language features to explicitly declare the interface For example, consider a Person class with two properties (FirstName and LastName) and one method (FullName) The interface and class could be defined like this:
Visual Basic NET
Public Interface IPersonVB
Property FirstName() As String
Property LastName() As String
Function FullName() As String
End Interface
Public Class PersonVB
Implements IPersonVB
Private _firstName As String
Private _lastName As String
Public Sub New()
' default constructor - required for interop
End Sub
Trang 14Public Property FirstName() As String Implements IPersonVB.FirstName
Public Function FullName() As String Implements IPersonVB.FullName
Return _firstName & " " & _lastName
Trang 15string FirstName{get; set;}
string LastName{get; set;}
string FullName();
}
public class PersonCS : IPersonCS
{
private string _firstName;
private string _lastName;
// default constructor - required for interop
public PersonCS() {}
public string FirstName
{
Trang 16get { return _firstName; }
set { _firstName = value; }
}
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
Attribute Description
None No class interface is generated for the class Using COM QueryInterface for IDispatch will fail An
interface needs to be manually created
Trang 17AutoDispatch An interface that supports IDispatch is created for the class However, no type information is
produced, so DispIds cannot be cached
AutoDual A dual interface is created for the class Typeinfo is produced and made available in the type library The use of this attribute form is shown below:
Visual Basic NET
public class PersonCS
The attribute can also be applied to the assembly, whereby it affects all classes within it
Interop Attributes
As well as the ClassInterfaceAttribute, there are others that control how various parts of the assembly are exposed to COM For example, the attribute GuidAttribute allows you to specify the GUID of the exposed item (class, interface, or assembly), and ComVisibleAttribute can be used to hide NET types from COM
Attributes that relate to the marshalling of data are covered in the API Calls section, a little later in the chapter, while the others are fully covered in the SDK help, under "Programming with the NET Framework", "Interoperating with
Unmanaged Code", "Exposing NET Framework Components to COM", and "Applying Interop Attributes"
Which Interface Method to Use
Trang 18We've seen that there are three forms of interface creation, and each has its own advantages and disadvantages The real problem that arises is that of versioning - as COM interfaces are immutable, and NET has the ability to bind to version interfaces So, the type of interface you expose to COM depends on how your NET components are going to change over time For example, consider the following:
Visual Basic NET
Trang 19By and large the safest option is for the manual creation of interfaces - although it involves more work, you get complete control over the interface The pros and cons of each method are explained below:
ClassInterfaceType None & Manual Interface
Advantages
There are no versioning problems because users can only call through explicitly created interfaces
The class author has full control over versioning of the class
No versioning problems because classes only support late binding, but without caching DispIds
Does not require user to create separate interface for each class
Supports scripting
Disadvantages
Trang 20 More work for class user.
Less support in Visual Basic, as everything must be of type Object
Slower
ClassInterfaceType AutoDual
Advantages
No extra work for class author or user
Easy to use from all COM clients
Disadvantages
Does not support versioning at all Any class changes will break COM clients
Exporting the Type Library
Once the NET assembly has been created, you need to create a COM type library so that COM clients can set references
to the classes This is done using the Type Library Export tool, the syntax of which is:
tlbexp AssemblyName [Options]
where Options can be:
Option Description
/out:FileName The filename of the type library to create
/names:FileName Use the specified file to specify capitalization of names in the type library
/nologo Don't display the logo
/silent Don't display output, except for errors
/verbose Display full information
For example, if the Person class were compiled into a Person.dll assembly, we would use:
tlbexp Person.dll
By default this creates Person.tlb