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

C# ПІДРУЧНИКИ / c# / Hungry Minds - C# Bible

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

// Create a handler for the event private __COMObject_COMEventEventHandler

COMEventHandlerInstance;

private void button1_Click(object sender, System.EventArgs e)

{

// create new instance of the COMObject class COMObject ObjectInstance;

short Num1; short Num2; short Sum;

ObjectInstance = new COMObjectClass();

Num1 = 5;

Num2 = 6;

//Call the SquareIt method

Sum = ObjectInstance.SquareIt(ref Num1, ref

Num2);

listBox1.Items.Add (Sum.ToString()); listBox1.Items.Add (ObjectInstance.Message);

// Set the value of message different than the

default

ObjectInstance.Message = "C# Rocks";

COMEventHandlerInstance = new __COMObject_COMEventEventHandler(COMEventHandler);

ObjectInstance.COMEvent += COMEventHandlerInstance;

ObjectInstance.FireCOMEvent();

}

void COMEventHandler(ref string Message)

{

listBox1.Items.Add(Message);

}

}

}

The output from this application looks similar to what is shown in Figure 34-6.

Figure 34-6: Output from the C# client using the COM component

Like any other object in .NET, you use the new operator to create a new instance of the COMObject class, as the following snippet demonstrates:

ObjectInstance = new COMObject();

Once the variable name ObjectInstance is instantiated, you use the object just as you would any other .NET object; nothing special needs to be done. The RCW handles all of the Interop, type conversions and object marshalling for the types, so you are completely hidden from any of the COM marshalling internals that are occurring.

If you have used COM Interop from VB .NET, you will notice something different about the way the parameters are passed to the methods in C#. If you look at the C# code for the SquareIt method, note the addition of the Ref keyword:

Num1 = 5;

Num2 = 6;

//Call the SquareIt method

Sum = ObjectInstance.SquareIt(ref Num1, ref Num2);

Visual Basic COM servers may pass values by value or by reference. Your C# code needs to use the appropriate keywords when passing parameters into COM method calls. You can use ILDASM to help you determine whether a parameter should be passed by value or by reference.

Open the assembly generated by Tlbimp using the ILDASM tool and look at the definition of the method that you want to call. In this case, you need to call the SquareIt() method. The SquareIt() method is listed in the assembly with the following signature:

SquareIt : int16(int16&,int16&)

The type of the return value returned by the method follows the colon. The signature of the SquareIt() method lists a return type of int16, which, in Intermediate Language parlance, denotes a 16-bit integer. The ampersands that follow the parameter types signify that the

parameter must be passed by reference. Parameters that need to be passed by reference must be adorned with the ref keyword in the C# client. Parameters that need to be passed by value are not shown with the ampersand in the assembly. The C# client doesn't need to use the ref keyword on the parameters in this case.

Directly referencing the COM DLL from C#

In the previous section, you learned how to use the Interop assembly created from Tlbimp.exe in a C# Windows Forms application. The main reason to use Tlbimp.exe to create the Interop assembly is because it can be given a strong name with the SN.exe utility, and then installed in the Global Application Cache using the GACUTIL utility. Once in the GAC, the assembly can be shared among many other .NET assemblies or projects. If you are writing an application that uses COM Interop and the assembly does not need to be shared, you can simply reference the COM DLL directly through Visual Studio .NET, which will create the RCW for you.

To add a reference to a COM DLL directly to your C# project, follow these steps:

1.Right-click the References folder in the Solution Explorer. The Add Reference dialog box, shown in Figure 34-7, opens. The second tab, COM, lists all of the COM objects registered on the local machine.

Figure 34-7: Adding a COM object reference directly

2.After you have selected the COM component that you need to consume, you can use the same code that you used to write the Windows Forms application, the only difference being the assembly that you are referencing, which in this case would be VB6ComServer, and not Cominterop.

3.using VB6COMServer;

ObjectInstance = new COMObjectClass();

As you can see, referencing a COM component directly from the IDE is even easier than using the Tlbimp utility, though you lose some flexibility in terms of what you can actually do with the component.

Handling Interop Errors

In the .NET Framework, the CLR reports errors by throwing exceptions when things go wrong. In COM, HRESULTs are the avenue in which errors are reported, so the RCW needs to be able the map the HRESULT for a given error to the equivalent .NET exception.

Table 34-2 maps the standard HRESULTs in COM to their counterparts as .NET exceptions.

Table 34-2: HRESULTs to .NET Exceptions

HRESULT

 

.NET Exception

 

 

 

MSEE_E_APPDOMAINUNLOADED

 

AppDomainUnloadedException

 

 

 

COR_E_APPLICATION

 

ApplicationException

 

 

 

COR_E_ARGUMENT or E_INVALIDARG

 

ArgumentException

 

 

 

COR_E_ARGUMENTOUTOFRANGE

 

ArgumentOutOfRangeException

 

 

 

COR_E_ARITHMETIC or

 

ArithmeticException

ERROR_ARITHMETIC_OVERFLOW

 

 

 

 

 

COR_E_ARRAYTYPEMISMATCH

 

ArrayTypeMismatchException

 

 

 

COR_E_BADIMAGEFORMAT or

 

BadImageFormatException

ERROR_BAD_FORMAT

 

 

 

 

 

COR_E_COMEMULATE_ERROR

 

COMEmulateException

 

 

 

COR_E_CONTEXTMARSHAL

 

ContextMarshalException

 

 

 

COR_E_CORE

 

CoreException

 

 

 

NTE_FAIL

 

CryptographicException

 

 

 

COR_E_DIRECTORYNOTFOUND or

 

DirectoryNotFoundException

ERROR_PATH_NOT_FOUND

 

 

 

 

 

COR_E_DIVIDEBYZERO

 

DivideByZeroException

 

 

 

COR_E_DUPLICATEWAITOBJECT

 

DuplicateWaitObjectException

 

 

 

COR_E_ENDOFSTREAM

 

EndOfStreamException

 

 

 

COR_E_TYPELOAD

 

EntryPointNotFoundException

 

 

 

COR_E_EXCEPTION

 

Exception

 

 

 

COR_E_EXECUTIONENGINE

 

ExecutionEngineException

 

 

 

COR_E_FIELDACCESS

 

FieldAccessException

 

 

 

COR_E_FILENOTFOUND or

 

FileNotFoundException

ERROR_FILE_NOT_FOUND

 

 

 

 

 

COR_E_FORMAT

 

FormatException

 

 

 

COR_E_INDEXOUTOFRANGE

 

IndexOutOfRangeException

 

 

 

COR_E_INVALIDCAST or

 

InvalidCastException

E_NOINTERFACE

 

 

 

 

 

COR_E_INVALIDCOMOBJECT

 

InvalidComObjectException

 

 

 

COR_E_INVALIDFILTERCRITERIA

 

InvalidFilterCriteriaException

 

 

 

COR_E_INVALIDOLEVARIANTTYPE

 

InvalidOleVariantTypeException

 

 

 

COR_E_INVALIDOPERATION

 

InvalidOperationException

Table 34-2: HRESULTs to .NET Exceptions

HRESULT

 

.NET Exception

 

 

 

COR_E_IO

 

IOException

 

 

 

COR_E_MEMBERACCESS

 

AccessException

 

 

 

COR_E_METHODACCESS

 

MethodAccessException

 

 

 

COR_E_MISSINGFIELD

 

MissingFieldException

 

 

 

COR_E_MISSINGMANIFESTRESOURCE

 

MissingManifestResourceException

 

 

 

COR_E_MISSINGMEMBER

 

MissingMemberException

 

 

 

COR_E_MISSINGMETHOD

 

MissingMethodException

 

 

 

COR_E_MULTICASTNOTSUPPORTED

 

MulticastNotSupportedException

 

 

 

COR_E_NOTFINITENUMBER

 

NotFiniteNumberException

 

 

 

E_NOTIMPL

 

NotImplementedException

 

 

 

COR_E_NOTSUPPORTED

 

NotSupportedException

 

 

 

COR_E_NULLREFERENCE or E_POINTER

 

NullReferenceException

 

 

 

COR_E_OUTOFMEMORY or

 

OutOfMemoryException

E_OUTOFMEMORY

 

 

 

 

 

COR_E_OVERFLOW

 

OverflowException

 

 

 

COR_E_PATHTOOLONG or

 

PathTooLongException

ERROR_FILENAME_EXCED_RANGE

 

 

 

 

 

COR_E_RANK

 

RankException

 

 

 

COR_E_REFLECTIONTYPELOAD

 

ReflectionTypeLoadException

 

 

 

COR_E_REMOTING

 

RemotingException

 

 

 

COR_E_SAFEARRAYTYPEMISMATCH

 

SafeArrayTypeMismatchException

 

 

 

COR_E_SECURITY

 

SecurityException

 

 

 

COR_E_SERIALIZATION

 

SerializationException

 

 

 

COR_E_STACKOVERFLOW or

 

StackOverflowException

ERROR_STACK_OVERFLOW

 

 

 

 

 

COR_E_SYNCHRONIZATIONLOCK

 

SynchronizationLockException

 

 

 

COR_E_SYSTEM

 

SystemException

 

 

 

COR_E_TARGET

 

TargetException

 

 

 

COR_E_TARGETINVOCATION

 

TargetInvocationException

 

 

 

COR_E_TARGETPARAMCOUNT

 

TargetParameterCountException

 

 

 

COR_E_THREADABORTED

 

ThreadAbortException

 

 

 

COR_E_THREADINTERRUPTED

 

ThreadInterruptedException

 

 

 

COR_E_THREADSTATE

 

ThreadStateException

 

 

 

COR_E_THREADSTOP

 

ThreadStopException

 

 

 

COR_E_TYPELOAD

 

TypeLoadException

 

 

 

COR_E_TYPEINITIALIZATION

 

TypeInitializationException

Table 34-2: HRESULTs to .NET Exceptions

HRESULT

 

.NET Exception

 

 

 

COR_E_VERIFICATION

 

VerificationException

 

 

 

COR_E_WEAKREFERENCE

 

WeakReferenceException

 

 

 

COR_E_VTABLECALLSNOTSUPPORTED

 

VTableCallsNotSupportedException

 

 

 

All other HRESULTs

 

COMException

If your application needs to get extended error information and the COM object supports the IErrorInfo interface, you can use the IErrorInfo object to get further information about the exception. Table 34-3 describes the additional error information.

 

Table 34-3: COM Interop Extended Error Information

 

 

 

 

Exception Field

 

 

COM Source Information

 

 

 

 

ErrorCode

 

 

HRESULT returned from the method call

 

 

 

 

HelpLink

 

 

If IErrorInfo->HelpContext is nonzero, the string is formed by

 

 

 

concatenating IErrorInfo->GetHelpFile and "#" and IErrorInfo-

 

 

 

>GetHelpContext. Otherwise, the string is returned from

 

 

 

IErrorInfo->GetHelpFile.

 

 

 

 

InnerException

 

 

Always null

 

 

 

 

Message

 

 

String returned from IErrorInfo->GetDescription

 

 

 

 

Source

 

 

String returned from IErrorInfo->GetSource

 

 

 

 

StackTrace

 

 

The .NET generated stack trace for this exception

 

 

 

 

TargetSite

 

 

The method name that caused the HRESULT to be passed back to

 

 

 

.NET

 

 

 

 

Obviously, you need to include error handling in your applications, even if you are using COM Interop. There is essentially no difference in the way that you code the COM components versus .NET assemblies, so the structured exception handling in .NET should be used whenever you are writing code that has the possibility of causing an exception.

Using Platform Invoke

If you are a Visual Basic 6 developer, the Win32 API has been the way to harness the true power of Windows development. In .NET, you can still access the Win32 API from C#, although most or all of the functionality that you will likely be using is already present in the

.NET Framework. Calling exported functions from C DLL's is accomplished by using the Platform Invoke service. Platform invoke is a service that enables managed code to call unmanaged functions COM DLL's. Using the DLLImportAttribute class, you can specify the name of the DLL and the DLL function that you need to use in your C# application. Just like accessing the Win32 API in VB6, you need to know the name of the DLL and the function in the DLL that you need to execute. Once you have accomplished this, you can simply call the function using the DLLImport attribute in a method marked with static and extern modifiers, as the following code demonstrates:

using System.Runtime.InteropServices;

[DllImport("user32.dll")]

public static extern int MessageBox(int hWnd, String text, String caption, uint type);

When using platform invoke, you may need to alter the default behavior of the interoperability between the managed and unmanaged code. You can do this by modify the fields of the DLLImportAttribute class. Table 34-4 describes the fields that can be customized for the DLLImportAttribute class.

 

 

Table 34-4: DLLImportAttribute Fields

Object Field

 

Description

 

 

 

EntryPoint

 

Specifies the DLL entry point to be called

 

 

 

CharSet

 

Controls the way that string arguments should be marshaled to the

 

 

function. The default is CharSet.Ansi.

 

 

 

ExactSpelling

 

Prevents an entry point from being modified to correspond to the

 

 

character set. The default value varies by programming language.

 

 

 

CallingConvention

 

Specifies the calling-convention values used in passing method

 

 

arguments. The default is WinAPI, which corresponds to __stdcall

 

 

for the 32-bit Intel-based platforms.

 

 

 

PreserveSig

 

Indicates that the managed method signature should not be

 

 

transformed into an unmanaged signature that returns an

 

 

HRESULT, and might have an additional [out, retval] argument

 

 

for the return value.

 

 

The default is True (the signature should not be transformed).

 

 

 

SetLastError

 

Enables the caller to use the Marshal.GetLastWin32Error API

 

 

function to determine whether an error occurred while executing

 

 

the method. In Visual Basic, the default is True; in C# and C++,

 

 

the default is False.

 

 

 

Calling DLL functions from C# is similar to calling them from Visual Basic 6. With the DLLImport attribute, however, you are simply passing the DLL's name and the method that you need to call.

Note It is recommended that your DLL function calls be grouped in separate classes. This simplifies coding, isolates the external function calls, and reduces overhead.

Summary

This chapter described how to use COM objects in .NET code and how to use the Tlbimp utility to generate .NET assemblies. You also took a brief look at how to interpret generated assemblies. In addition, you learned how to write COM client code in C#, including calling COM methods and working with COM properties. As you can see, the .NET Framework enables you to easily integrate existing COM code into your .NET applications. This easy integration gives you the opportunity to slowly move portions of an application to .NET, without having to rewrite all of the COM component logic in C#.

Chapter 35: Working with COM+ Services

In This Chapter

Microsoft has steadily enhanced the functionality of the COM subsystem since it was first released in 1993. One of the most significant enhancements to the COM programming model was introduced in 1997 with the release of Microsoft Transaction Server (MTS). MTS, first released as an add-on to Windows NT 4.0, enabled developers to develop components using an object broker that provided transaction, role-based security, and resource pooling services.

With the release of Windows 2000, Microsoft elevated the programming model offered by MTS to a first-class subsystem. COM+ is, in large part, a merging of the traditional COM programming model and the MTS programming model. For the first time, Windows provided support for both traditional COM (or unconfigured) components with attributed (or configured) MTS-style components directly from the operating system.

The .NET Framework offers both styles of components to developers writing componentbased software. This chapter examines how to develop C# classes that you can use as configured components with COM+.

Caution Although the .NET Framework is available on a variety of operating system platforms, COM+ is not available on the same set of platforms. Components written in C# that take advantage of COM+ services can only be used on platforms that support COM+. The COM+ class code built into the .NET Framework throws an exception of class PlatformNotSupported if your code attempts to access a feature that does not exist on the runtime platform.

Understanding the System.EnterpriseServices Namespace

Any C# class can be used by COM clients as a COM component, regardless of the class's inheritance tree. C# classes can be derived from nothing more than System.Object and still be used as COM components. Taking advantage of COM+ services within your C# classes, however, requires a more stringent inheritance policy.

The System.EnterpriseServices namespace provides the classes, enumerations, structures, delegates, and interfaces that you need to write applications that take advantage of COM+ and its enterprise-level services. If you have written components in C++ or Visual Basic 6 that ended up running inside of the COM+ Services Runtime, most of this chapter will seem familiar to you. From the standpoint of an experienced COM+ developer, the System.EnterpriseServices namespace wraps the functionality to which you previously had programmatic access. If you have written components in VB6, then you will be happy to see that previously unavailable features, such as object pooling, are now fully available to you through the Framework. Services, such as just-in-time (JIT) activation, object pooling, transaction processing, and shared property management, are all available as classes or attributes in the System.EnterpriseServices namespace.

Table 35-1 describes each of the classes available in the System.EnterpriseServices namespace.

 

Table 35-1: System.EnterpriseServices Classes

Class

 

 

Description

ApplicationAccessControlAttribute

Enables security configuration for the library or server application housing the application. This class cannot be inherited.

ApplicationActivationAttribute

Specifies whether components in the assembly run in the creator's process or in a system process.

ApplicationIDAttribute

ApplicationNameAttribute

Specifies the application ID (as a GUID) for this assembly. This class cannot be inherited.

Specifies the name of the COM+ application to be used for the install of the components in the assembly. This class cannot be inherited.

ApplicationQueuingAttribute

Enables queuing support for the marked assembly and enables the application to read method calls from Message Queuing queues. This class cannot be inherited.

AutoCompleteAttribute

Marks the attributed method as an AutoComplete object. This class cannot be inherited.

BYOT

Wraps the COM+ ByotServerEx class and the COM+ DTC interfaces ICreateWithTransactionEx and ICreateWithTipTransactionEx. This class cannot be inherited.

ComponentAccessControlAttribute

COMTIIntrinsicsAttribute

Enables security checking on calls to a component. This class cannot be inherited.

Enables you to pass context properties from the COM Transaction Integrator (COMTI) into the COM+ context.

ConstructionEnabledAttribute

ContextUtil

DescriptionAttribute

Enables COM+ object construction support. This class cannot be inherited.

Obtains information about the COM+ object context. This class cannot be inherited.

Sets the description on an assembly (application), component, method, or interface. This class cannot be inherited.

EventClassAttribute

 

Marks the attributed class as an event class.

 

 

This class cannot be inherited.

 

 

 

EventTrackingEnabledAttribute

 

Enables event tracking for a component. This

 

 

class cannot be inherited.

 

 

 

ExceptionClassAttribute

 

Sets the queuing exception class for the

 

 

queued class. This class cannot be inherited.

 

 

 

IISIntrinsicsAttribute

 

Enables access to ASP intrinsic values from

 

Table 35-1: System.EnterpriseServices Classes

Class

 

 

Description

InterfaceQueuingAttribute

JustInTimeActivationAttribute

LoadBalancingSupportedAttribute

ContextUtil.GetNamedProperty. This class cannot be inherited.

Enables queuing support for the marked interface. This class cannot be inherited.

Turns just-in-time (JIT) activation on or off. This class cannot be inherited.

Determines whether the component participates in load balancing, if the component load balancing service is installed and enabled on the server.

MustRunInClientContextAttribute

ObjectPoolingAttribute

PrivateComponentAttribute

Forces the attributed object to be created in the context of the creator, if possible. This class cannot be inherited.

Enables and configures object pooling for a component. This class cannot be inherited.

Identifies a component as a private component that is only seen and activated by components in the same application. This class cannot be inherited.

RegistrationErrorInfo

Retrieves extended error information about methods related to multiple COM+ objects. This also includes methods that install, import, and export COM+ applications and components. This class cannot be inherited.

RegistrationException

RegistrationHelper

ResourcePool

SecureMethodAttribute

The exception that is thrown when a registration error is detected.

Installs and configures assemblies in the COM+ catalog. This class cannot be inherited.

Stores objects in the current transaction. This class cannot be inherited.

Ensures that the infrastructure calls through an interface for a method or for each method in a class when using the security service.

Classes need to use interfaces to use security services. This class cannot be inherited.

SecurityCallContext

 

Describes the chain of callers leading up to

 

 

the current method call.

 

 

 

SecurityCallers

 

Provides an ordered collection of identities in

 

 

the current call chain.

 

 

 

SecurityIdentity

 

Contains information regarding an identity in

 

 

a COM+ call chain.

 

 

 

SecurityRoleAttribute

 

Configures a role for an application or

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