Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

C# ПІДРУЧНИКИ / c# / MS Press - Msdn Training Programming Net Framework With C#

.pdf
Скачиваний:
193
Добавлен:
12.02.2016
Размер:
16.87 Mб
Скачать

Module 15 (Optional): Interoperating Between Managed and Unmanaged Code

25

 

 

 

Demonstration: Using the Type Library Importer

Topic Objective

To demonstrate how to use the Type Library Importer to convert the type definitions found within a COM type library into equivalent definitions in a .NET assembly.

Lead-in

In this demonstration you will learn how to use the Type Library Importer to convert the type definitions found within a COM type library into equivalent definitions in a .NET assembly.

*****************************ILLEGAL FOR NON-TRAINER USE******************************

The Type Library Importer (Tlbimp.exe) can be used to convert the coclasses and interfaces found within a COM type library into equivalent definitions in a

.NET assembly. The output of the Type Library Importer is an assembly that contains metadata for the types defined within the original type library. The Type Library Importer performs the following tasks:

!Converts unmanaged COM coclasses to C# classes with a constructor (without parameters) and no other methods.

!Converts unmanaged COM vtable interfaces to C# interfaces.

!Converts unmanaged COM structures to C# structures with public fields.

The syntax for using the Type Library Importer is as follows:

tlbimp inputFile [options]

In the previous example, inputFile is the name of the file that contains the COM type library. This file can be either a stand-alone type library (.TLB) file or a DLL. The following command imports a type library file and generates a .NET assembly named myTest.dll:

tlbimp myTest.tlb

26

Module 15 (Optional): Interoperating Between Managed and Unmanaged Code

The /out: option specifies the name of the assembly that is created as a result of running the Type Library Importer. The following command creates myNewTest as a DLL:

tlbimp myTest.tlb /out:myNewTest.dll

If the output assembly’s file name is equivalent to the input type library’s file name, the Type Library Importer will generate an error, because the input file cannot be overwritten.

The /delaysign option delays signing of the assembly. The /delaysign option must be used with the /keycontainer:, /keyfile:, or /publickey: option. For a complete list of all the options that can be used with the Type Library Importer, see “Type Library Importer (Tlbimp.exe)” in the .NET Framework SDK documentation.

Module 15 (Optional): Interoperating Between Managed and Unmanaged Code

27

 

 

 

Threading Models

Topic Objective

To describe the threading models in managed and unmanaged code.

Lead-in

The threading models that are supported by COM components and the .NET Framework are different. While managed objects use synchronized regions…

!COM Components Use Apartments to Synchronize Access to Managed Resources

!Runtime Creates/Initializes Apartment When Calling a COM Object

using System.Threading; using System.Threading; using APTOBJLib;

using APTOBJLib;

Thread.CurrentThread.ApartmentState =

Thread.CurrentThread.ApartmentState =

ApartmentState.STA;

ApartmentState.STA;

AptSimple obj = new AptSimple (); AptSimple obj = new AptSimple (); obj.Counter = 1;

obj.Counter = 1;

! To ensure that the main thread of an application is STA

[STAThread]

[STAThread] static void Main()

static void Main()

*****************************ILLEGAL FOR NON-TRAINER USE******************************

COM components use apartments to synchronize access to managed resources. In contrast, managed objects use synchronized regions; synchronization primitives such as mutexes, locks, and completion ports; and synchronized contexts to ensure that all shared resources are used in a thread-safe manner.

For interoperability, the common language runtime creates and initializes an apartment when calling a COM object. A managed thread can create and enter a single-threaded apartment (STA) that contains only one thread, or a multithreaded apartment (MTA) that contains one or more threads. When a COM apartment and a thread-generated apartment are compatible, COM permits the calling thread to make calls directly to the COM object. If the apartments are incompatible, COM creates a compatible apartment and marshals all calls through a proxy in the new apartment.

On the first call to unmanaged code, the runtime calls CoInitializeEx to initialize the COM apartment as either an MTA or an STA. You can control the type of apartment created by setting the System.Threading.ApartmentState property on the thread to MTA, STA, or Unknown. As long as the proxy and stub are registered or the type library is registered, you do not have to set this property. If neither the proxy and stub nor the type library is registered, an InvalidCastException can occur when calling a COM object from managed code.

28

Module 15 (Optional): Interoperating Between Managed and Unmanaged Code

The following table lists the ApartmentState enumeration values and shows the comparable COM apartment initialization call.

ApartmentState

 

enumeration value

COM apartment initialization

 

 

MTA

CoInitializeEx(NULL, COINIT_MULTITHREADED)

STA

CoIntializeEx(NULL,

 

COINIT_APARTMENTTHREADED)

Unknown

CoInitializeEx(NULL, COINIT_MULTITHREADED)

Whenever the COM object and the managed thread are in incompatible apartments, all calls on the object are made through a COM created proxy. For example, calls between Windows Forms controls whose COM objects must reside in an STA and managed code whose thread’s ApartmentState is MTA.

The following example shows how to create an STA apartment-threaded COM object, AptSimple, from managed code:

using System.Threading; using APTOBJLib;

AptSimple obj = new AptSimple (); obj.Counter = 1;

To eliminate the use of proxies and stubs and significantly enhance performance, set the ApartmentState on the thread before creating the object, as shown in the following example:

using System.Threading; using APTOBJLib;

Thread.CurrentThread.ApartmentState = ApartmentState.STA; AptSimple obj = new AptSimple ();

obj.Counter = 1;

Module 15 (Optional): Interoperating Between Managed and Unmanaged Code

29

 

 

 

A thread can only initialize a COM apartment once. Since the runtime initializes the apartment before making the very first call to unmanaged code on that thread, you should set the ApartmentState as early as possible. Changing the ApartmentState after the apartment has been initialized has no effect. You can neither un-initialize nor re-initialize an apartment. In some situations, the thread may already have called into unmanaged code before the ApartmentState could be set. In such cases, you cannot change the apartment type after the thread is initialized. Your only option is to create a new thread.

As an alternative to setting the ApartmentState enumeration, you can apply the System.STAThreadAttribute or System.MTAThreadAttribute to the main entry point of the application. By applying these attributes you ensure that the main thread of an application is in the proper state. For example:

[STAThread]

static void Main()

After setting the apartment state, you can check the state programmatically, as in the following example:

Thread.CurrentThread.ApartmentState = ApartmentState.STA;

if (Thread.CurrentThread.ApartmentState == ApartmentState.STA) //STA apartment state

else

//incompatible apartment state

30

Module 15 (Optional): Interoperating Between Managed and Unmanaged Code

Signature Translation and Error Handling

Topic Objective

To describe how signature translation works.

Lead-in

Signature translation is the mechanism by which managed return values are converted into COM HRESULTs.

*****************************ILLEGAL FOR NON-TRAINER USE******************************

COM components accept COM data types and return HRESULTs. However, classes in the .NET Framework do not use HRESULTs. In order to satisfy both models, every method of a managed type has a .NET Framework signature and an implied COM signature.

When COM interoperability services imports a COM type, it produces a

.NET Framework method signature equivalent to the original COM method signature. This is referred to as signature translation. During signature translation, COM parameters, return values, and HRESULTs are mapped to corresponding entities in the .NET Framework method signature, as shown in the illustration on the slide.

When the COM object returns an error HRESULT, the runtime converts the HRESULT to an exception, which is thrown to the caller. The type of exception that is generated depends on the error returned from the COM object. Note that any non-error HRESULTs will be lost in this conversion. For example, if a COM object returns any success HRESULT other than S_OK, such as S_FALSE, no exception is thrown, but the managed caller does not receive the HRESULT. You must manually generate the runtime callable wrappers to avoid this issue.

A managed signature is converted to an unmanaged signature by changing the managed return value to an [out, retval] parameter and changing the type of the unmanaged return value to HRESULT. The COM signature returns an HRESULT and has an additional output parameter for the return value. The return value from the managed implementation always returns as an [out, retval] parameter added to the end of the unmanaged signature, whereas the unmanaged signature always returns an HRESULT. If the managed method has a void return, the runtime omits the [out, retval] parameter.

Module 15 (Optional): Interoperating Between Managed and Unmanaged Code

31

 

 

 

Under some circumstances, it is preferable to leave the managed signature unchanged. You can use the PreserveSig attribute to do this. The following examples show the translation from a managed signature to an unmanaged signature.

Managed signature:

[PreserveSig] short DoSomething(short i);

Unmanaged signature:

short DoSomething ([in] short i);

32

Module 15 (Optional): Interoperating Between Managed and Unmanaged Code

Marshaling

Topic Objective

To describe how COM types relate to .NET Framework types and classes.

Lead-in

Types used in COM have corresponding .NET Framework built-in value types or classes.

Direct students to the Student Notes and explain that the table that is referred to in the slide may be found in the Student Notes.

!Wrappers perform data marshaling

#Support for marshaling data to and from COM almost always provides the correct marshaling behavior

!Table shows corresponding COM and C# types

!MarshalAsAttribute to change the marshaling behavior

public void M1 public void M1

([MarshalAs(UnmanagedType.LPWStr)]String msg); ([MarshalAs(UnmanagedType.LPWStr)]String msg);

!Two ways to customize the RCW to handle types

#Edit the interop assembly

#Create a wrapper manually

*****************************ILLEGAL FOR NON-TRAINER USE******************************

When marshaling data between managed and unmanaged code, the interop marshaler must recognize the representations of the data being passed.

!For blittable types, managed and unmanaged representations are always the same: a 4-byte integer is always marshaled to a 4-byte integer. The interop marshaler uses the managed signature to determine the data representation.

!For non-blittable types, the interop marshaler recognizes the managed representation from its method signature, but is unable to do the same for the unmanaged representation. To marshal non-blittable types, you can use one of the following techniques:

Allow the marshaler to infer the representation from the managed representation.

Supply the unmanaged data representation explicitly.

Module 15 (Optional): Interoperating Between Managed and Unmanaged Code

33

 

 

 

The following table shows data types used in COM and their corresponding

.NET Framework built-in value types or classes. Any type not explicitly identified in this table is converted to an Int32 system type.

COM value type

COM reference type

C# Data Type

bool

bool *

int

char, small

char *, small *

System.SByte

short

short *

short

long, int

long *, int *

int

Hyper

hyper *

long

unsigned char, byte

unsigned char *, byte *

byte

wchar_t, unsigned short

wchar_t *, unsigned short *

ushort

unsigned long, unsigned int

unsigned long *, unsigned int *

uint

unsigned hyper

unsigned hyper *

ulong

float

float *

float

double

double *

double

VARIANT_BOOL

VARIANT_BOOL *

bool

void *

void **

System.IntPtr

HRESULT

HRESULT *

System.IntPtr

SCODE

SCODE *

int

BSTR

BSTR *

string

LPSTR or [string, …]

LPSTR *

string

char *

 

 

LPWSTR or [string, …]

LPWSTR *

string

wchar_t *

 

 

VARIANT

VARIANT *

object

DECIMAL

DECIMAL *

System.Decimal

DATE

DATE *

System.DateTime

GUID

GUID *

System.Guid

CURRENCY

CURRENCY *

System.Decimal

IUnknown *

IUnknown **

object

IDispatch *

IDispatch **

object

SAFEARRAY(type)

SAFEARRAY(type) *

type[ ]

34

Module 15 (Optional): Interoperating Between Managed and Unmanaged Code

The following table lists COM value and reference types that convert to corresponding element types. For example, a COM coclass automatically maps to a managed class with the same name.

COM value type

COM Reference type

Element type

typedef BaseType

ByRef BaseType

BaseType

MyType

 

 

MyStruct

ByRef VALUETYPE<MyStruct>

valuetype<MyStruct>

MyEnum

ByRef VALUETYPE<MyEnum>

valuetype<MyEnum>

MyInterface *

ByRef CLASS <MyInterface>

Class<MyInterface>

MyCoClass

ByRef CLASS <_Class>

class <_Class>

You can apply the System.Runtime.InteropServices.MarshalAsAttribute to a method parameter, class field, or return value to change the marshaling behavior. For example, a string is converted to a BSTR type when marshaled from managed to unmanaged code, unless you explicitly apply the MarshalAsAttribute to marshal the string to another type, such as LPWSTR. You can apply this attribute to a parameter, field, or return value within the source of the type definition, as shown in the following examples where it is specified that msg is to be marshaled as a null-terminated buffer of Unicode characters (LPWStr).

Apply the MarshalAsAttribute to a parameter:

public void M1 ([MarshalAs(UnmanagedType.LPWStr)]String msg);

Apply the MarshalAsAttribute to a field within a class:

class MsgText {

[MarshalAs(UnmanagedType.LPWStr)] Public String msg;

}

Apply the MarshalAsAttribute to a return value:

[return: MarshalAs(UnmanagedType.LPWStr)] public String GetMessage();

Соседние файлы в папке c#