Exporting Metadata to Type Libraries

team lib

This section describes how .NET entities, including interfaces, classes, and methods , are converted when exported to a type library.

Exporting Assemblies

A .NET assembly is converted into a single type library. It is a one-to-one mapping, and you cannot split an assembly over more than one type library. You will always get the same type library regardless of the way it has been produced from the assembly (for example, using the TlbExp.exe tool or the ConvertAssemblyToTypeLib class).

Note 

Assembly names often contain periods (for example, System.Runtime.InteropServices ). Because the assembly name is often used as the base name for the type library and periods are not allowed in type library names, any periods in the assembly name will be converted to underscores during export.

Assemblies are fully identified by a strong name, which consists of the assembly name, version number, public key, and locale information. Type libraries, on the other hand, are identified by three items:

  • A GUID, called the library ID (or LIBID)

  • An optional locale identifier (or LCID)

  • A version number

The LIBID is constructed from the assembly name and public key information. You must use the public key because there might be two assemblies with the same name, signed with different keys, that need to be mapped onto different type libraries. A given assembly name and public key always yields the same LIBID. If you want to use a specific LIBID, you can override the default by using the Guid attribute on the assembly.

Version information is passed from the assembly to the type library, but because COM supports only a two-part version number (in contrast to an assemblys four-part version number), only the major and minor parts of the version are preserved. For example, an assembly with version number 1.22.5.123 will produce a type library with version 1.22.

The assembly locale information is converted to an LCID; if there is no locale information, an LCID of zero will be used in the type library.

If an assembly has the AssemblyDescription attribute, the description will be used to provide a HelpString for the type library.

Exporting Namespaces

.NET types will lose their namespace information when exported. For example, the class MyNamespace.MyClass will be exported simply as MyClass . This can lead to problems if two classes have the same name but live in different namespaces, as you see here:

 namespaceOuter{ //EnuminOuternamespace publicenumSeasons { Spring,Summer,Autumn,Winter } namespaceInner{ //EnuminOuter.Innernamespace publicenumSeasons { Spring,Summer,Autumn,Winter } } } 

In this case, the export process will prefix the generated typenames with namespace information so that the two enums will be called Outer_Seasons and Outer_Inner_Seasons . Although this transformation will make the code work correctly when called from COM, the type names wont match those in the original .NET code, thus leading to possible confusion for COM programmers.

Note 

Namespace prefixes are not used if there is no name clash .

Exporting Classes

By default, every class in an assembly is converted into a coclass. The ComVisible attribute can be used to prevent .NET classes from being exported to the type library, as shown in the following C# sample code:

 //Classesareexportedbydefault publicclassExportedToCOM { ... } //Thisclasswillnotbeexported [ComVisible(false)] publicclassNotExportedToCom { ... } 

The generated coclass will be given an automatically generated GUID unless you specify one using the Guid attribute:

 //SpecifyaGUIDforthecoclass [Guid(4a79399a-bf0f-4208-9512-aa3a438bd67a)] publicclassMyComClass { ... } 

Abstract classes and interfaces will have their definitions in the type library marked with the noncreatable attribute. Concrete classes that dont have a default constructor will also be marked noncreatable because COM needs to be able to call a default constructor to create instances.

Inheritance disappears when the type library is generated because there is no inheritance of coclasses . A coclass will expose its own interfaces, plus all the interfaces exposed by its base classes, as shown in the following Visual C# example:

 //BaseinheritsfromObjectbydefault,andimplementsinterfaceIOne classBase:IOne { } //DerivedderivesfromBase,andexposesadual //classinterface [ClassInterface(ClassInterfaceType.AutoDual)] publicclassDerived:Base { } 

The coclass for the Derived class will look like this:

 coclassDerived { interfaceIOne; interface_Object; } 

The coclass exposes the IOne interface from the base class. The _Object interface represents an interface to the Object class; this interface will be generated for all .NET types, since everything inherits from Object eventually.

Exporting Interfaces

COM interfaces derived from .NET interfaces will contain the same methods and properties as the original .NET interface.

As with classes, a GUID will be generated automatically to represent the COM interface ID (IID) during the export process. You can override the default IID generation by using the Guid attribute, as the following C# code fragment shows:

 [Guid(db568ac0-0c40-40ca-b8dc-9badf68588fc)] interfaceIMyInterface { ... } 
Note 

The automatically generated interface ID is derived from the interface name and the complete signatures of all the methods defined by the interface. If you alter any method signatures, or reorder the methods in the interface definition, youll get a different IID generated. The names of individual methods are not used, so you can change method names without affecting the IID.

Choosing Interface Types

When exporting .NET interfaces, COM dual interfaces are generated by default, as they provide the greatest flexibility for COM clients . You can control the type of interface produced by using the InterfaceType attribute. This attribute takes one of the members of the ComInterfaceType enumeration, listed in Table 4-6, as a parameter:

Table 4-6: Members of the ComInterfaceType Enumeration

Member

Description

InterfaceIsDual

The .NET interface is exposed to COM as a dual interface. This is the default value.

InterfaceIsIDispatch

The .NET interface is exposed to COM as a dispinterface . Such interfaces can be used only via late binding.

InterfaceIsIUnknown

The .NET interface is exposed to COM as a custom interface. Such interfaces can be used only via early binding.

The following Visual C# example shows how to use the InterfaceType attribute:

 'ACOMdispinterfacewillbegeneratedwhenthisinterfaceis 'exportedtoatypelibrary [ InterfaceType(ComInterfaceType.InterfaceIsIDispatch), Guid("ACDA141C-A24C-4499-B16E-A95A48A24484") ] publicinterfaceIBase { voidBaseMethod(); } 

Interfaces and Inheritance

Interfaces can, and often do, form an inheritance hierarchy, as shown in the following C# example, where IDerived inherits from IBase . Any class that implements IDerived will have to implement the cumulative interface defined by both IDerived and IBase :

 namespaceInterfaceInherit { //Abaseinterface,whichwillbeexportedasadualinterface //bydefault.AGUIDhasbeenspecifiedusingtheGuidattribute. [Guid("ACDA141C-A24C-4499-B16E-A95A48A24484")] publicinterfaceIBase { voidBaseMethod(); } //IDerivedderivesfromIBase [Guid("5DE99C74-D1F1-451d-A509-F8ACCD54BB2E")] publicinterfaceIDerived:IBase { voidDerivedMethod(); longSquare(intval); } //TheclasshastoimplementthemethodsofbothIBaseand //IDerived.SinceallthemethodsIwanttoexposetoCOMare //implementedviainterfaces,aclassinterfaceisnotneeded [ ClassInterface(ClassInterfaceType.None), Guid("682C25B2-5FC8-4b50-903B-2F8B2972F1DC") ] publicclassTheClass:IDerived { publicTheClass() { } //IDerivedMembers publicvoidDerivedMethod() { } publiclongSquare(intval) { returnval*val; } //IBaseMembers publicvoidBaseMethod() { } } } 

When such a hierarchy is exported to a type library, the inheritance hierarchy is necessarily flattened because COM isnt object oriented and doesnt support interface inheritance at run time. A dual interface might appear to be derived from IDispatch in Interface Definition Language (IDL), but this means only that it contains the IDispatch methods in its vtable. It doesnt mean a dual interface is IDispatch in the object-oriented sense.

For the example above, you end up with the following items when the interfaces and class are exported to a type library:

  • An IBase interface that derives from IDispatch

  • An IDerived interface that derives from IDispatch

  • A coclass that implements both interfaces

The IDL that follows shows how this appears in the type library. You can see how the relationship between the two interfaces has disappeared, and how they are regarded as completely separate:

 [ odl,uuid(ACDA141C-A24C-4499-B16E-A95A48A24484), version(1.0),dual,oleautomation, custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, InterfaceInherit.IBase) ] interfaceIBase:IDispatch{ [id(0x60020000)]HRESULTBaseMethod(); }; [ odl,uuid(5DE99C74-D1F1-451D-A509-F8ACCD54BB2E), version(1.0),dual,oleautomation, custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, InterfaceInherit.IDerived) ] interfaceIDerived:IDispatch{ [id(0x60020000)]HRESULTDerivedMethod(); [id(0x60020001)]HRESULTSquare([in]longval, [out,retval]int64*pRetVal); }; [ uuid(682C25B2-5FC8-4B50-903B-2F8B2972F1DC),version(1.0), custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, InterfaceInherit.TheClass) ] coclassTheClass{ interface_Object; [default]interfaceIDerived; interfaceIBase; }; 

If an exported class has a class interface, it will be made the coclasss default interface. If a class doesnt have a class interface, the default interface will be the first one implemented by the class. In this case, the default interface is IDerived .

Exporting Methods

COM interface method parameters have directional attributes that inform the marshaling code in which direction parameters need to be marshaled. The following bullet points summarize how parameters of .NET methods are represented in COM interface methods:

  • Reference types that are passed by value are marked as [in] parameters.

  • Types that are passed by reference are marked as [in,out] parameters.

  • Pointers (as used in C# unsafe code and managed C++ code) are also marked as [in,out] parameters.

You can apply the In and Out attributes to .NET parameters, and this will affect how they are represented when the type is exported. As you might expect, .NET In parameters are marshaled as COM [in] parameters, Out parameters are marshaled as [out] , and parameters that have both In and Out attributes are marshaled as [in,out] .

COM methods return HRESULT s, so .NET methods are converted in such a manner that function return values are represented as [out, retval] parameters, and the return type is converted to an HRESULT . The following example shows how a Visual C# function would be represented in a type library:

 //Functionthatreturnsalong publiclongSquare(intn) //ResultingIDL HRESULTSquare([in]longn,[out,retval]int64*pRetVal); 
Note 

The PreserveSig attribute can be used to prevent this transformation from taking place so that the signature of the COM method matches that of the .NET method.

If .NET methods dont use HRESULTs , what values get returned when .NET methods are executed by COM clients? If no error occurs, the runtime will return S_OK . If an exception is thrown by the .NET code, the runtime will return an HRESULT that depends on the exception thrown. A large number of possible HRESULT s can be returned (over 60 in total), and the most common ones are summarized in Table 4-7. For a full list, consult the .NET Framework Developers Guide in the online help, under the topic HRESULTs and Exceptions. Note that some HRESULT values can be mapped onto more than one .NET exception class; the table shows only the most common .NET exception classes for each HRESULT .

Table 4-7: Conversion Between .NET Exceptions and COM HRESULT s

Exception Type

HRESULT

Numeric Value

ArgumentException ,
InvalidEnumArgumentException

COR_E_ARGUMENT
or E_INVALIDARG

0x80070057

ArgumentNullException

COR_E_NULLREFERENCE
or E_POINTER

0x80004003

ArgumentOutOfRangeException

COR_E_ARGUMENTOUTOFRANGE

0x80131502

ArithmeticException

COR_E_ARITHMETIC or ERROR_ARITHMETIC_OVERFLOW

0x80070216

ArrayTypeMismatchException

COR_E_ARRAYTYPEMISMATCH

0x80131503

DivideByZeroException

COR_E_DIVIDEBYZERO

0x80020012

Exception

COR_E_EXCEPTION

0x80131500

FileNotFoundException

COR_E_FILENOTFOUND or ERROR_FILE_NOT_FOUND

0x80070002

FormatException ,
CookieException ,
UriFormatException

COR_E_FORMAT

0x80131537

IndexOutOfRangeException

COR_E_INDEXOUTOFRANGE

0x80131508

InvalidCastException

COR_E_INVALIDCAST
or E_NOINTERFACE

0x80004002

IOException

COR_E_IO

0x80131620

MissingMemberException

COR_E_MISSINGMEMBER

0x80131512

NotImplementedException

E_NOTIMPL

0x80004001

NotSupportedException

COR_E_NOTSUPPORTED

0x80131515

OutOfMemoryException

COR_E_OUTOFMEMORY or E_OUTOFMEMORY

0x8007000E

SecurityException

COR_E_SECURITY

0x8013150A

SystemException ,
InvalidPrinterException ,
SoapException , SqlTypeException

COR_E_SYSTEM

0x80131501

Overloaded methods in .NET classes pose a problem because COM doesnt support the concept of overloading of interface methods. To overcome this, the exporter will generate unique method names by adding a suffix to each method name, consisting of an underscore and a numeral. The numeral starts at two and is incremented by one for each additional overloaded method, as shown in the following Visual C# example:

 //C#overloadedmethods intMyMethod(intval); intMyMethod(intval1,intval2); intMyMethod(doubled); //COMinterfacemethods HRESULTMyMethod([in]longval,[out,retval]long*pRetVal); HRESULTMyMethod_2([in]longval1,[in]longval2, [out,retval]long*pRetVal); HRESULTMyMethod_3([in]doubleval,[out,retval]long*pRetVal); 

Note that these names are generated automatically when the type is exported, and methods arent guaranteed to retain the same numerical suffixes in subsequent type library generations.

Exporting Properties

Properties, with their get and set methods, are a fundamental feature of .NET languages and can be defined in both classes and interfaces. COM also supports properties, using the [propget] and [propput] attributes on interface methods. Heres how the type library exporter handles exporting properties:

  • Property get methods become interface methods with the [propget] attribute.

  • Property set methods become interface methods with the [propput] attribute.

  • Properties without a get or a set method are ignored.

  • If the property type is a class or interface, the property set method will become an interface method with the [propputref] attribute, giving it an added level of indirection.

To show you how this works, here is a Visual C# class that implements two properties. One of them is of type double , while the other is of class type:

 namespaceprops { [ClassInterface(ClassInterfaceType.AutoDual)] publicclassWorker { publicWorker(Workerbs) { theBoss=bs; } privateWorkertheBoss; privatedoublethePittance; publicWorkerBoss { get { returntheBoss; } set { theBoss=value; } } publicdoubleSalary { get { returnthePittance; } set { thePittance=value; } } } } 

When exported to a type library, the resulting interface looks like this:

 [ odl,uuid(2A33C1A5-B38A-36DB-B370-5C3F7AAA0E5F), hidden,dual,nonextensible,oleautomation, custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9,props.Worker) ] interface_Worker:IDispatch{ //MethodsinheritedfromObjecthavebeenomitted [id(0x60020004),propget] HRESULTBoss([out,retval]_Worker**pRetVal); [id(0x60020004),propputref] HRESULTBoss([in]_Worker*pRetVal); [id(0x60020006),propget] HRESULTSalary([out,retval]double*pRetVal); [id(0x60020006),propput] HRESULTSalary([in]doublepRetVal); }; 

The get methods have been converted into [propget] methods, the set method for the double property has been converted to a [propput] method, and the set method for the Worker property has been converted to a [propputref] property.

Any public fields exposed by .NET types are also converted into [propput] and [propget] methods when the type is exported. Read-only fields are represented by a [propget] method.

Exporting Data Types

The type library exporter will convert .NET types to suitable unmanaged types during the export process. This is more complex than importing data types when using COM components in .NET because.NET components can use a greater range of data types than are available for COM interfaces.

Table 4-8 shows the corresponding .NET, COM IDL, and Visual Basic types for some of the most common data types.

Table 4-8: Conversion Between .NET, COM IDL, and Visual Basic 6 Types

.NET Type

IDL Type

VB6 Type

Remarks

System.Boolean (when used as a parameter)

VARIANT_BOOL

Boolean

 

System.Boolean (when used as a field in a structure)

long

Long

 

System.Byte

unsigned char

Byte

 

System.Int16

short

Integer

 

System.Int32

long

Long

Can also be marshaled as an IDL HRESULT by using the MarshalAsAttribute .

System.Int64

int64

n/a

 

System.IntPtr

long

Long

 

System.UInt16

unsigned short

n/a

Unsigned types arent
supported by Visual Basic.

System.UInt32

unsigned long

n/a

Can also be marshaled as an IDL HRESULT by using the MarshalAs attribute.

System.UInt64

uint64

n/a

 

System.Single

single

Single

 

System.Double

double

Double

 

System.String (used as a parameter)

BSTR

String

 

System.String (used as a field in a structure)

LPSTR

String

 

System.DateTime

DATE

Date

 

System.Guid

GUID

n/a

 

System.Decimal

DECIMAL

n/a

Can be marshaled to Visual Basic as a Currency by using the MarshalAs attribute.

System.Object (used as a parameter)

VARIANT

Variant

 

System.Object (used as a field in a structure)

IUnknown*

n/a

 

Arrays of a given type

SAFEARRAY(type)

type()

 

Note that for some types the marshaled type will be different depending on whether theyre used as parameters or as fields in structs. You can also vary the way some types are marshaled by using the MarshalAs attribute. This advanced interop feature is discussed in Chapter 13, Advanced Interaction.

Exporting Value Types

.NET value types are exported as IDL structs. Youll need to use the StructLayout attribute with the LayoutKind.Sequential argument to fix the layout of the members so that it isnt altered by .NET. Here is an example struct coded in C#:

 [StructLayout(LayoutKind.Sequential)] publicstructMyStruct { publicintvalOne; publicintvalTwo; publicvoidMethod1(inta) { valOne=valTwo=a; } } 

The corresponding COM type in the type library looks like this:

 typedef[uuid(4D469648-1406-3683-BADA-580CE600EE2E),version(1.0), custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9,ExportTest.MyStruct) ] structtagMyStruct{ longvalOne; longvalTwo; }MyStruct; 

You can see how the value type has been represented by an IDL struct. Note also that the generated struct contains only data members; methods will not be exported.

Note 

The custom IDL attribute was introduced in Chapter 3. It provides a way to specify the namespace that will be used for this COM type if it is imported back into .NET using the TlbImp.exe tool.

Exporting Enumerations

Enumerations are converted into COM enumerations in the generated type library. Since it is a COM requirement that names of enumeration members be unique, the exporter will generate unique names by adding the name of each member of the enumeration as a prefix to the enumeration name itself. For example, consider the following .NET enumeration coded in C#:

 publicenumCompassPoint { North=0, East=90, South=180, West=270 } 

The generated COM enum will look like this:

 typedef[uuid(9371DAC5-C4FA-3B40-8822-90CE107AD8F9),version(1.0), custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9,ExportTest.CompassPoint) ] enum{ CompassPoint_North=0, CompassPoint_East=90, CompassPoint_South=180, CompassPoint_West=270 }CompassPoint; 

You can see that each name has been prefixed with CompassPoint_ to generate a unique identifier.

 
team lib


COM Programming with Microsoft .NET
COM Programming with Microsoft .NET
ISBN: 0735618755
EAN: 2147483647
Year: 2006
Pages: 140

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