Cross-Language Support


One of the best features of COM is its support for multiple languages. It's possible to create a COM component with Visual Basic and to make use of it from within a scripting client such as JScript. On the other hand, it's also possible to create a COM component using C++ that a Visual Basic program cannot make use of. A scripting client has different requirements than a Visual Basic client, and a C++ client is able to use many more COM features than any other client language.

When you write COM components, it's always necessary to have the client programming language in mind. The server must be developed for a specific client language or for a group of client languages. If you are designing a COM component for a scripting client, this component can also be used from within C++, but then the C++ client has some disadvantages. Many rules must be followed when different clients should be supported, and the compiler can't help with COM; the COM developer has to know the requirements of the client language and has to create the interfaces accordingly.

How does this compare with the .NET platform? With the Common Type System (CTS), .NET defines how value types and reference types can be defined from a .NET language and the memory layout of such types. But the CTS does not guarantee that a type which is defined from any language can be used from any other language. This is the role of the Common Language Specification (CLS). The CLS defines the minimum requirement of types that must be supported by a .NET language.

The CTS and CLS were briefly mentioned in Chapter 1, ".NET Architecture." This section goes deeper and explores the following:

  • Both CTS and CLS.

  • Language independence in action by creating a C++, Visual Basic, and a C# class that derive from each other. You look at the MSIL code that's generated from these compilers.

  • The requirements of the CLS.

The CTS and the CLS

All types are declared with the guidance of the CTS. The CTS defines a set of rules that language compilers must follow to define, reference, use, and store reference and value types. Therefore, by following the CTS, objects written in different languages can interact with each other.

However, not all types are available to all programming languages. To build components accessible from all .NET languages, use the CLS. With the CLS the compiler can check for valid code according to the CLS specification.

Any language that supports .NET isn't just restricted to the common subset of features that is defined with the CLS; even with .NET it's still possible to create components that cannot be used from different languages. Supporting all languages is much easier with .NET than it was with COM. If you do restrict yourself to the CLS, it's guaranteed that this component can be used from all languages. It is most likely that libraries written by third parties will restrict themselves to the CLS to make the library available to all languages.

The .NET Framework was designed from the ground up to support multiple languages. During the design phase of .NET, Microsoft invited many compiler vendors to build their own .NET languages. Microsoft itself delivers Visual Basic, C++/CLI, C#, J#, and JScript .NET. In addition, more than 50 languages from different vendors, such as COBOL, Smalltalk, Perl, and Eiffel, are available. Each of these languages has its specific advantages and many different features. The compilers of all these languages have been extended to support .NET.

Important

The CLS is the minimum specification of requirements that a language must support. This means that if you restrict your public methods to the CLS, all languages supporting .NET can use your classes!

Most, but not all, of the classes in the .NET Framework are CLS-compliant. The non-compliant classes and methods are specially marked as not compliant in the MSDN documentation. One example is the UInt32 structure in the System namespace. UInt32 represents a 32-bit unsigned integer. Not all languages(including Visual Basic and J#) support unsigned data types; such data types are not CLS-compliant.

Figure 15-11 shows the relation of the CLS to the CTS and how types of programming languages relate.

image from book
Figure 15-11

Language Independence in Action

This section shows CLS in action. The first assembly created includes a base class with Visual C++. The second assembly has a Visual Basic class that inherits from the C++ class. The third assembly is a C# Console Application with a class deriving from the Visual Basic code, and a Main function that's using the C# class. The implementation should just show how the languages make use of .NET classes, and how they handle numbers, so all these classes have a simple Hello() method where the System.Console class is used, and an Add() method where two numbers are added. Figure 15-12 shows the UML class diagram with these classes and methods.

image from book
Figure 15-12

Writing the C++/CLI class

The first project type you create for this example is a .NET Class Library, which is created from the Visual C++ Projects project type of Visual Studio, and named HelloCPP (see Figure 15-13). Name the solution LanguageIndependence.

image from book
Figure 15-13

The application wizard generates a class Class1 marked with ref to make the class a managed type. Without a special attribute, the class would be a normal unmanaged C++ class generating native code. This class is in the file HelloCPP.h:

 // HelloCPP.h #pragma once using namespace System; namespace HelloCPP { public ref class Class1 { // TODO: Add your methods for this class here. }; } 

For demonstration purposes, change the namespace and class name, and add three methods to the class. The virtual method Hello2() uses a C runtime function, printf(), that demonstrates the use of native code within a managed class. To make this method available the header file stdio.h must be included. Within the Hello() method you are using the Console managed class from the System namespace. The C++ using namespace statement is similar to the C# using statement. using namespace System opens the System namespace, so you don't have to write System::Console::WriteLine(). Mark the method Hello() with the virtual keyword, so that it can be overridden. Hello() will be overridden in the Visual Basic and C# classes. Similar to C#, C++ member functions are not virtual by default. Add a third method, Add(), which returns the sum of two int arguments, to the class so that you can compare the generated MSIL to the different languages to see how they handle numbers. All three examples use the same namespace: Wrox.ProCSharp.Assemblies.CrossLanguage.

// HelloMCPP.h #pragma once #include <stdio.h> using namespace System; namespace Wrox { namespace ProCSharp { namespace Assemblies { namespace CrossLanguage { public ref class HelloCPP { public: virtual void Hello() { Console::WriteLine("Hello, C++/CLI"); } virtual void Hello2() { printf("Hello, calling native code\n"); } int Add(int val1, int val2) { return val1 + val2; } }; } } } } 

To allow all .NET languages to use the class HelloCPP, the assembly should be marked CLS compliant. Add the assembly attribute [CLSCompliant] to the file AssemblyInfo.cpp. Because the class CLSCompliantAttribute is in the namespace System, the namespace must be imported.

//... using namespace System; //... [assembly:ComVisible(false)]; [assembly:CLSCompliant(true)]; 

To compare the programs with running code, this example uses the release build instead of the debug configuration. With debug configurations you can see some non-optimized IL code. Looking at the generated DLL using ildasm (see Figure 15-14), in addition to the class HelloCPP with its members you will also find the static method printf(). This method calls a native unmanaged function using pinvoke.

image from book
Figure 15-14

The Hello2() method (see Figure 15-15) pushes the address of the field $ArrayType$$$BY0BM@$$CBD, which keeps the string on the stack. In line IL_0005 a call to the static printf() method can be seen where a pointer to the string "Hello, calling native code" is passed.

image from book
Figure 15-15

printf() itself is called via the platform invoke mechanism (see Figure 15-16). With the platform invoke, you can call all native functions like the C runtime and Win32 API calls.

image from book
Figure 15-16

The Hello() method (see Figure 15-17) is completely made up of MSIL code; there's no native code. Also the string "Hello, C++/CLI" is a managed string written into the assembly and put onto the stack with ldstr. In line IL_0005 you are calling the WriteLine() method of the System.Console class using the string from the stack.

image from book
Figure 15-17

To demonstrate how numbers are used within C++/CLI, take a look at the MSIL code of the Add() method (see Figure 15-18). The passed arguments are put on the stack with ldarg.1 and ldarg.2, add adds the stack values and puts the result on the stack, and in line IL_0003 the result is returned.

image from book
Figure 15-18



Professional C# 2005
Pro Visual C++ 2005 for C# Developers
ISBN: 1590596080
EAN: 2147483647
Year: 2005
Pages: 351
Authors: Dean C. Wills

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