Cross-Language Integration


Prior to .NET, interoperating with the code written in other languages was challenging. There were pretty much two options for reusing functionality developed in other languages: COM interfaces or DLLs with exported C functions. As for exposing functionality written in Visual Basic, the only option was to create COM interfaces.

Because Visual Basic is now built on top of the CLR, it’s able to interoperate with the code written in other .NET languages. It’s even able to derive from a class written in another language. To support this type of functionality, the CLR relies on a common way of representing types, as well as rich metadata that can describe these types.

The Common Type System

Each programming language seems to bring its own island of datatypes with it. For example, previous versions of Visual Basic represent strings using the BSTR structure, C++ offers char and wchar datatypes, and MFC offers the CString class. Moreover, the fact that the C++ int datatype is a 32-bit value, whereas the Visual Basic 6 Integer datatype is a 16-bit value, makes it difficult to pass parameters between applications written using different languages.

To help resolve this problem, C has become the lowest common denominator for interfacing between programs written in multiple languages. An exported function written in C that exposes simple C datatypes can be consumed by Visual Basic, Java, Delphi, and a variety of other programming languages. In fact, the Windows API is exposed as a set of C functions.

Unfortunately, to access a C interface, you must explicitly map C datatypes to a language’s native datatypes. For example, a Visual Basic 6 developer would use the following statement to map the GetUserNameA Win32 function (GetUserNameA is the ANSI version of the GetUserName function):

  ' Map GetUserName to the GetUserNameA exported function ' exported by advapi32.dll. '   BOOL GetUserName( '      LPTSTR lpBuffer, // name buffer '      LPDWORD nSize // size of name buffer ' ); Public Declare Function GetUserName Lib "advapi32.dll" _ Alias "GetUserNameA" (ByVal strBuffer As String, nSize As Long) As Long 

This code explicitly maps the lpBuffer C character array datatype to the Visual Basic 6 String parameter strBuffer. This is not only cumbersome, but also error prone. Accidentally mapping a variable declared as Long to lpBuffer wouldn’t generate any compilation errors, but calling the function would more than likely result in a difficult-to-diagnose, intermittent-access violation at runtime.

COM provides a more refined method of interoperation between languages. Visual Basic 6 introduced a common type system (CTS) for all applications that supported COM - that is, variant-compatible datatypes. However, variant datatypes are as cumbersome to work with for non-Visual Basic 6 developers as the underlying C data structures that make up the variant datatypes (such as BSTR and SAFEAR- RAY) were for Visual Basic developers. The result is that interfacing between unmanaged languages is still more complicated than it needs to be.

The CTS provides a set of common datatypes for use across all programming languages. The CTS provides every language running on top of the .NET platform with a base set of types, as well as mechanisms for extending those types. These types may be implemented as classes or as structs, but in either case they are derived from a common System.Object class definition.

Since every type supported by the CTS is derived from System.Object, every type supports a common set of methods.

Open table as spreadsheet

Method

Description

Boolean Equals(Object)

Used to test equality with another object. Reference types should return True if the Object parameter references the same object. Value types should return True if the Object parameter has the same value.

Int32 GetHashCode()

Generates a number corresponding to the value of an object. If two objects of the same type are equal, then they must return the same hash code.

Type GetType()

Gets a Type object that can be used to access metadata associated with the type. It also serves as a starting point for navigating the object hierarchy exposed by the Reflection API (discussed shortly).

String ToString()

The default implementation returns the fully qualified name of the object’s class. This method is often overridden to output data that is more meaningful to the type. For example, all base types return their value as a string.

Metadata

Metadata is the information that enables components to be self-describing. Metadata is used to describe many aspects of a .NET component, including classes, methods, and fields, and the assembly itself. Metadata is used by the CLR to facilitate all sorts of things, such as validating an assembly before it is executed or performing garbage collection while managed code is being executed. Visual Basic developers have used metadata for years when developing and using components within their applications.

  • Visual Basic developers use metadata to instruct the Visual Basic runtime how to behave. For example, you can set the Unattended Execution property to determine whether unhandled exceptions are shown on the screen in a message box or are written to the Event Log.

  • COM components referenced within Visual Basic applications have accompanying type libraries that contain metadata about the components, their methods, and their properties. You can use the Object Browser to view this information. (The information contained within the type library is what is used to drive IntelliSense.)

  • Additional metadata can be associated with a component by installing it within COM+. Metadata stored in COM+ is used to declare the support a component needs at runtime, including transactional support, serialization support, and object pooling.

Better Support for Metadata

Metadata associated with a Visual Basic 6 component was scattered in multiple locations and stored using multiple formats:

  • Metadata instructing the Visual Basic runtime how to behave (such as the Unattended Execution property) is compiled into the Visual Basicgenerated executable.

  • Basic COM attributes (such as the required threading model) are stored in the registry.

  • COM+ attributes (such as the transactional support required) are stored in the COM+ catalog.

.NET refines the use of metadata within applications in three significant ways:

  • .NET consolidates the metadata associated with a component.

  • Because a .NET component does not have to be registered, installing and upgrading the component is easier and less problematic.

  • .NET makes a much clearer distinction between attributes that should only be set at compile time and those that can be modified at runtime.

Tip 

All attributes associated with Visual Basic components are represented in a common format and consolidated within the files that make up the assembly.

Since much of a COM/COM+ component’s metadata is stored separately from the executable, installing and upgrading components can be problematic. COM/COM+ components must be registered to update the registry/COM+ catalog before they can be used, and the COM/COM+ component executable can be upgraded without upgrading its associated metadata.

The process of installing and upgrading a .NET component is greatly simplified. Since all metadata associated with a .NET component must reside within the file that contains the component, no registration is required. Once a new component is copied into an application’s directory, it can be used immediately. Since the component and its associated metadata cannot become out of sync, upgrading the component becomes much less of a problem.

Another problem with COM+ is that attributes that should only be set at compile time may be reconfigured at runtime. For example, COM+ can provide serialization support for neutral components. A component that does not require serialization must be designed to accommodate multiple requests from multiple clients simultaneously. You should know at compile time whether or not a component requires support for serialization from the runtime. However, under COM+, the attribute describing whether or not client requests should be serialized can be altered at runtime.

.NET makes a much better distinction between attributes that should be set at compile time and those that should be set at runtime. For example, whether a .NET component is serializable is determined at compile time. This setting cannot be overridden at runtime.

Attributes

Attributes are used to decorate entities such as assemblies, classes, methods, and properties with additional information. Attributes can be used for a variety of purposes. They can provide information, request a certain behavior at runtime, or even invoke a particular behavior from another application. An example of this can be shown by using the Demo class defined in the following code block:

  Module Module1   <Serializable()> Public Class Demo     <Obsolete("Use Method2 instead.")> Public Sub Method1()       ' Old implementation ...     End Sub     Public Sub Method2()       ' New implementation ...     End Sub   End Class   Public Sub Main()     Dim d As Demo = New Demo()     d.Method1()   End Sub End Module 

Create a new Console application for Visual Basic and then add a new class into the sample file. Note that a best practice is to place each class in its own source file, but in order to simplify this demonstration, the class Demo has been defined within the main module.

The first attribute on the Demo class marks the class with the Serializable attribute. The base class library will provide serialization support for instances of the Demo type. For example, the ResourceWriter type can be used to stream an instance of the Demo type to disk. The second attribute is associated with Method1. Method1 has been marked as obsolete, but has not been made unavailable. When a method is marked as obsolete, there are two options, one being that Visual Studio should prevent applications from compiling. However, a better strategy for large applications is to first mark a method or class as obsolete and then prevent its use in the next release. The preceding code causes Visual Studio to display an IntelliSense warning if Method1 is referenced within the application, as shown in Figure 5-1. Not only does the line with Method1 have a visual hint of the issue, but a task has also been automatically added to the task window.

image from book
Figure 5-1

If the developer leaves this code unchanged and then compiles it, the application will compile correctly. As shown in Figure 5-2, the compilation is complete. However, the developer receives a warning with a meaningful message that the code should be changed to use the correct method.

image from book
Figure 5-2

There are also times when you might need to associate multiple attributes with an entity. The following code shows an example of using both of the attributes from the previous code at the class level. Note that in this case the Obsolete attribute has been modified to cause a compilation error by setting its second parameter to True:

  <Serializable(), Obsolete("No longer used.", True)> Public Class Demo   ' Implementation ... End Class 

Attributes play an important role in the development of .NET applications, particularly XML Web Services. As you’ll see in Chapter 26, the declaration of a class as a Web Service and of particular methods as Web methods are all handled through the use of attributes.

The Reflection API

The .NET Framework provides the Reflection API for accessing metadata associated with managed code. You can use the Reflection API to examine the metadata associated with an assembly and its types, and even to examine the currently executing assembly.

The Assembly class in the System.Reflection namespace can be used to access the metadata in an assembly. The LoadFrom method can be used to load an assembly, and the GetExecutingAssembly method can be used to access the currently executing assembly. The GetTypes method can then be used to obtain the collection of types defined in the assembly.

It’s also possible to access the metadata of a type directly from an instance of that type. Since every object derives from System.Object, every object supports the GetType method, which returns a Type object that can be used to access the metadata associated with the type.

The Type object exposes many methods and properties for obtaining the metadata associated with a type. For example, you can obtain a collection of properties, methods, fields, and events exposed by the type by calling the GetMembers method. The Type object for the object’s base type can also be obtained by calling the DeclaringType property.

A good tool that demonstrates the power of reflection is Lutz Roeder’s Reflector for .NET. Check out www.aisto.com/roeder/dotnet.




Professional VB 2005 with. NET 3. 0
Professional VB 2005 with .NET 3.0 (Programmer to Programmer)
ISBN: 0470124709
EAN: 2147483647
Year: 2004
Pages: 267

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