interopbook: introductions




chapter introductions

What follows are the introduction sections from each chapter of the book. The introduction provides a brief overview of what you'll learn from the chapter.

chapter 1: using c-style APIs

Platform Invocation Services (PInvoke or P/Invoke) is the part of the common language runtime (CLR) that enables managed .NET code to access unmanaged functions. The functions can be written in ordinary C or C++, and they are usually packaged as dynamic link libraries (DLLs). These are the same unmanaged functions that you know and love, as they are routinely used by your non-.NET applications to provide core business functionality. PInvoke allows you to reuse the functions you need now while you contemplate a future rewrite in a .NET language.

When boiled down to only the essential elements, the steps to using PInvoke to access unmanaged functions are as follows:

  1. Identify the function that you need to call.
  2. Declare the function in managed code and apply the attributes that PInvoke uses.
  3. Call the function.

This is the essence of PInvoke, but along the way there will likely be several twists and turns. The complexity of PInvoke is directly related to the complexity of the unmanaged function you need to call.

If you are using Visual C++, you have a second option available to you for accessing unmanaged functions, one that no other .NET language supports. It’s simply called C++ Interop. This C++ technology was previously called It Just Works, or IJW. It enables you to make calls to unmanaged code without the special function declarations or attributes required by PInvoke in other languages.

Interop marshaling (or just marshaling) is the process of passing data between managed and unmanaged memory during a call. Marshaling takes place when input parameters are passed to an unmanaged function, and when a result is returned from the call. Tasks performed by the marshaler include conversion of data between managed and unmanaged types, copying of the converted data between managed and unmanaged memory, and freeing memory at the conclusion of the call.

Most of the PInvoke classes and attributes can be found in the System.Runtime.InteropServices namespace. The documentation for this namespace is a good starting point when you are searching for just the right attribute or class for the task at hand.

This chapter focuses on the core topics related to the use of unmanaged functions from managed code. There are recipes that cover the process of identifying the function to call, then declaring and calling the function. Several recipes discuss the use of managed wrappers to encapsulate the unmanaged calls, with one version of a wrapper focusing on exception handling and another on security.

chapter 2: c-style APIs: structures, classes, and arrays

Classes and structures allow you to create your own custom data types. Whether they are C++ or C# structs and classes, or Visual Basic .NET structures and classes, you can define complex types and implement business logic that would otherwise be either impossible or cumbersome. Custom data types free you from the restrictions of simple integers, floats, and doubles. Without the ability to define custom types, a developer’s life would indeed be dull.

Likewise, arrays are an important tool for any developer. They allow you to operate on sets of like data rather than individual variables.

It is fitting then that this chapter is devoted to the handling of classes, structures, and arrays in Windows interop. As it does for built-in data types, .NET provides the facilities you need to pass classes, structures, and arrays between managed and unmanaged code.

Structures used by both managed and unmanaged code are defined twice, once for each environment. In order to allow structures to pass successfully between the two environments, the layout of the structures must agree. This doesn’t mean that all vailable fields must be defined in both places, since there are ways to define partial structures.

The first few recipes in this chapter show you how to use structures (C++/C# structs, Visual Basic .NET Structures) with PInvoke. Although structures are more limited than classes, and therefore not as widely used, they clearly illustrate all the necessary techniques for passing programmer-defined types. To complete the discussion, passing classes instead of structures is demonstrated in another recipe.

This chapter includes a number of recipes that demonstrate different ways to define and pass structures. Fields in a structure can use a sequential layout, or the position of each field can be individually controlled. Structures can be allocated in managed code and passed to unmanaged code, or vice versa.

One recipe demonstrates how to specify the way each field in a structure should be marshaled, while another discusses how to allocate and free memory for individual fields in a structure.

The chapter ends with recipes that focus on passing arrays of data between managed and unmanaged code. Simple arrays using built-in types are discussed, followed by arrays of strings and finally arrays of structures. Throughout the chapter, the use of the directional attributes (In and Out) is demonstrated.

chapter 3: win32 API

Calling a Win32 function is really no different from calling any other unmanaged function. You declare the function using DllImport and make the call. It really should be that simple.

What complicates things with Win32 is that it comes with its own set of baggage that must be brought along for the ride. The recipes in this chapter are designed to review and handle some of that baggage.

Many Win32 functions are implemented in ANSI and Unicode versions. The first recipe in the chapter describes the process of choosing the correct version to execute. Win32 has its own mechanism for retrieving error codes, and a recipe is devoted to this subject. Callbacks are a technique used by Win32 functions and are covered in another recipe. The Win32 API makes use of handles for just about everything (or so it seems), and two recipes cover how these handles should be used and passed.

An annoying aspect of Win32 is the use of its own set of data types. Most of the functions are declared in the API reference with Win32-specific types that must be translated into integral types. Similarly, Win32 functions use a seemingly endless list of constant values that are used as calling or return values. This chapter includes a recipe for each of these subjects.

The chapter concludes with a recipe on replacing Win32 calls with native .NET classes and methods. Interop with your own unmanaged functions may be necessary, but interop with Microsoft’s Win32 functions shouldn’t be. Moving away from Win32 has its benefits and should be considered when suitable .NET replacements are available.

chapter 4: using c++ interop

Like other .NET languages, C++ is capable of working with unmanaged code. C++ can use the same facilities available to other languages such as PInvoke. However, unlike other languages, C++ is in a unique position to interoperate with unmanaged C++ code. It does this at the source-code level by directly compiling and linking unmanaged C++ code. Other .NET languages cannot access C++ source directly, so they must use PInvoke to access unmanaged functions. This ability to directly interoperate with unmanaged C++ code is simply called C++ Interop. This C++ technology was previously called It Just Works, or IJW. C++ is the only .NET language that allows managed and unmanaged code to coexist within the same project.

C++ Interop uses a very simple mechanism to marshal data between managed and unmanaged code. Parameters are copied between the two environments without any data conversion. This provides the fastest possible marshaling of data, but it also requires that you perform your own data conversions when they are necessary.

In contrast, PInvoke provides robust parameter marshaling including data conversions, but this benefit comes at a performance and flexibility cost. In situations where you have access to the unmanaged C++ code, and performance is an important consideration, C++ Interop may be the right tool for the job.

Performing your own data marshaling is also a potential advantage. You have the opportunity to optimize the conversions based on your knowledge of how you use the data. You can cache parameters after they are converted because you know they will be used again in another method. You can implement shortcuts for the conversion process if they are available. And you can easily convert and marshal your own custom data types.

C++ Interop also provides better type safety than PInvoke. Since PInvoke relies upon your declarations of the unmanaged functions, there is always the possibility that you have incorrectly declared a function. C++ Interop compiles the C++ headers containing the declarations for the unmanaged classes, eliminating the possibility of mistyped parameters.

Since native C++ classes are capable of accessing COM components, it makes sense that C++ Interop also allows this. By accessing COM components directly from managed C++ wrappers, you can expose these components to other .NET languages. These wrappers, called Custom Runtime Callable Wrappers (CRCWs), eliminate the need to generate and reference COM interop libraries.

Why should you consider using C++ Interop instead of PInvoke? Generally, for the same reasons you would choose C++ instead of a higher-level language like C# or Visual Basic for any programming task. Native C++ provides a low-level, high-performance solution to every conceivable software problem. In a similar way, C++ Interop provides you with low-level, highperformance control of interop and data marshaling. It’s the ultimate interop tool since it can tackle the most difficult interop challenges. But it does require additional work on your part since you are in total control of the interop process.

Consider using C++ Interop when you have access to the unmanaged C++ source and any of the following situations apply:

The first recipe in this chapter covers the basics of accessing unmanaged C++ classes. The chapter continues with recipes describing how to mix managed and unmanaged code, and how to detect attributes of a class at compile time. Another recipe shows how to reference and use managed objects from unmanaged code.

The next set of recipes covers how to marshal strings to unmanaged code, and how to work with structures that contain embedded pointers. Handling callbacks from unmanaged code is also discussed in another recipe.

The final recipe demonstrates how to create a managed C++ wrapper for a COM component. PInvoke is not covered in this chapter, since it is addressed in other chapters. Only the features that are unique to C++ Interop are covered here.

chapter 5: using COM

Prior to the introduction of .NET, Component Object Model (COM) was the recommended framework for Windows development. COM encourages the development of discrete, reusable components rather than massive, monolithic applications. Untold numbers of COM components have been developed using Visual Basic 6.0 (VB6), Active Template Library (ATL) for Visual C++, Delphi, and other development tools. Many applications (e.g., Microsoft Office) also expose interfaces via COM that allow you to extend and control them with your own code.

Given the popularity of COM, it’s only natural that Microsoft provides good interoperability between COM and .NET. The focus of this chapter is using existing COM components from managed .NET code.

Some of the terminology used for COM development is confusing and not always intuitive, so a few definitions are in order. I’ve tried to use this terminology consistently throughout this chapter as well as in the other COM-related chapters of this book.

In general, using a COM component from .NET managed code involves these steps:

  1. Import a COM type library to produce an interop library. The interop library contains a managed proxy class called a Runtime Callable Wrapper (RCW) for each COM class and interface in the type library.
  2. Reference the interop library from the managed project.
  3. Create an instance of the RCW. This creates an active instance of the COM object.
  4. Use methods or properties of the RCW instance.
  5. When the RCW is garbage collected, the instance of the COM object is released.

This chapter starts by examining the basics of referencing and using COM components from .NET. Recipes describe how to reference components directly from Visual Studio .NET, or how to create interop libraries using the TlbImp utility. Handling of COM events in managed code is covered in another recipe. Several recipes are dedicated to COM data types and how they are marshaled, including handling of COM Variant and array data.

Extending a COM class in managed code is discussed in one recipe, while changing the apartment model is covered in another. Performance considerations and using late binding of COM objects each have a recipe. Creating a primary interop assembly and deploying an application that uses COM interop each have a recipe.

The chapter concludes with a series of recipes covering exception and error handling for COM interop.

chapter 6: exposing managed code to COM

COM interop works in both directions. Chapter 5 covered the ability to use existing COM components from managed code. COM interop also provides the ability to expose managed code to COM clients. That is the focus of this chapter.

In its simplest form, exposing a managed class to COM involves setting a few project options to prepare the assembly for COM interop. When you build the project, Visual Studio .NET exports all public members of your managed class for use by COM. A COM type library (typelib) file (.tlb) is generated, containing the definitions for the COM interface and COM class (a coclass) that were exported. The assembly is also registered as a COM component.

At this point, any COM client written in a language such as Visual Basic 6.0 (VB6) or C++ can reference the managed COM component. The managed classes and interfaces that are exposed look just like ordinary COM types to client code. Instances of the COM object are created using the normal syntax. Properties and methods are referenced in the normal way. Reference counting works as expected for a COM object.

The .NET runtime performs this magic through the use of a COM Callable Wrapper (CCW). Since the COM client is unable to access the managed object directly, the CCW acts as a proxy. When a COM client creates an instance of the COM object that is exposed from managed code, the .NET runtime creates a CCW. The CCW handles the creation of the underlying managed object. The CCW is allocated in unmanaged memory and the managed object resides in the garbage-collected heap.

One and only one CCW is created for each managed object. Multiple COM clients can access the managed object through this single CCW. The CCW in turn manages the reference counting for the managed object. When the CCW detects that there are no COM references to it, it releases its reference on the managed object, allowing it to be garbage collected.

Of course, that is COM interop in its simplest form. The mechanism just described exposes all public members of a class to COM using something called a class interface. This interface type has a number of drawbacks, and it is not the recommended approach, as discussed in the first two recipes of this chapter.

You can (and should) design your own interfaces to expose to COM. Doing this provides you with complete control of your public API. You can control the type of interface exported to COM, and in doing so determine whether the component supports early or late binding. The use of your own interfaces is covered in the third recipe.

In addition to these major topics, this chapter includes recipes on controlling the COM identity and selectively hiding members of an interface or class. Another recipe discusses the options available for generation of a typelib as well as for registration of the assembly for use by COM. The exposing of managed events for use by a COM client is covered in another recipe. The chapter concludes with two recipes that demonstrate ways to return HRESULT values to the calling code.

chapter 7: marshaling to COM clients

This chapter continues the discussion started in Chapter 6 on exposing managed code to COM clients. The focus of this chapter is on marshaling of parameters.

Since the managed code acts as the publisher of new COM components, you are in a position to control the look and feel of the API that you expose to COM clients. You can define and implement your managed types any way you see fit and use them from managed code. But the decisions you make may affect the way COM client code is able to use your components. If there are multiple ways to define a parameter, you want to make decisions that will make it easy for COM clients to use your code. You don’t want to unknowingly place obstacles in the way of these clients.

This chapter reviews some of the options available to you when defining your managed interfaces and classes. We will examine several categories of data types to see how we can control the way they are marshaled.

The chapter starts with a recipe discussing parameter direction and the things you can do to control it. Additional recipes cover the marshaling of strings, arrays, and Variants, all core data types that are used frequently in COM development. Marshaling of entire classes and structures is demonstrated in another recipe.

Several specialized subjects are discussed in other recipes. One recipe demonstrates how to marshal currency, while another looks at the handling of null strings. Finally, the chapter closes with a discussion of optional parameters.

chapter 8: COM+ enterprise services

COM+ is the Microsoft architecture that provides a centralized runtime environment for COM components. Designed to run components in a middle tier, it provides a number of services that are necessary when using COM components in an enterprise-scale application. .NET does not replace COM+ since it doesn’t directly address these services.

At its core, COM+ is a hosting environment. Instead of activating a COM component in the same process as the client application, the component can be hosted by COM+ in a separate process. This allows you to use other performance-oriented features of COM+ such as just-in-time (JIT) activation and object pooling.

COM+ organizes components into applications, with each application hosting one or more related components. This allows you to logically group components together according to how they are used. The Component Services management tool (available from the Administrative Tools folder in the Control Panel) is available to administer and configure the applications and components. This is also where COM+ role-based security is managed for each application.

The feature most often associated with COM+ is automatic transactions. Because this is a key component of COM+, it merits its own chapter. Please refer to Chapter 9, which provides in-depth coverage of transactions.

.NET provides good support for COM+ components. It does this directly without using COM or PInvoke to access COM+ services. The terminology has changed slightly, with .NET calling these serviced components instead of COM+ components. And the broad category of services known as COM+ is now called Enterprise Services. But the COM+ concepts have not changed. Using .NET, you can develop a component in managed code, register it as a COM+ (serviced) component, and use it from COM or managed client code.

That last point is an interesting one. Traditional COM+ components are available only to COM clients. However, when a managed component is registered as a serviced component, it is also available to managed client code. This allows managed code to take full advantage of the available COM+ services.

In general, the classes that are used for COM+ support are located in the System. EnterpriseServices namespace. This namespace contains the ServicedComponent base class that your managed component classes must derive from. It also contains a large number of attributes that are used to determine which COM+ features each assembly, class, and method use.

Just as COM has its own unique terminology, COM+ uses a number of terms that I’ll define here:

The chapter starts with coverage of the most basic way to expose a managed class to COM+. Following that, we discuss implementing a server application instead of a library application. Additional recipes cover the registration and installation of serviced components. An alternative to manual registration is described in the recipe on dynamic component registration.

Two important performance-oriented topics are discussed next. JIT activation and object pooling each have a recipe that demonstrates how to use these features.

Security-related topics are covered in the next set of recipes. One recipe demonstrates the use of private components, while two others cover role-based security.

The chapter concludes with a look at Queued Components and how to implement and use them from .NET.

chapter 9: COM+ enterprise services transactions

The feature most often associated with COM+ is automatic transactions, which removes the necessity for component code to manually manage a logical unit of work (a transaction). Gone is the need for code that calls BeginTransaction on an individual SqlConnection instance. Gone is the code to explicitly call Commit or Rollback on a SqlTransaction object.

Instead, the component is enlisted into (or added to) an automatic transaction. When a method of a COM+ component is called, it receives a transaction that has already been started. If the method returns normally, the work (such as database updates) is committed. If an exception is thrown, a Rollback is executed.

Automatic transactions seamlessly work with different types of resource managers (databases, queues, etc.). This allows you to enlist multiple resources, such as multiple databases, in a single transaction. All updates to the resources will be committed or rolled back as a single unit of work. Multiple components can also work together within a single transaction, perhaps with each one performing an update to a different resource.

The COM+ context is a key ingredient when using automatic transactions. A COM+ context is an environment that is created by COM+ where objects live after they are activated. Each context has a set of properties that determine the runtime characteristics of the component. Every COM+ component is activated into either a new or existing context.

A COM+ context supports two separate but related flags that are used during transaction processing. The consistent flag is used to cast a vote to commit or abort the transaction. The done flag indicates that the component’s vote is now final. Several recipes in this chapter demonstrate ways to indirectly set these context flags.

This chapter begins with a recipe that shows how to monitor and inspect the current state of the transaction. This is followed by a recipe that demonstrates how to enable transaction support for a managed class.

Each component that participates in a transaction is allowed to vote on the outcome of the transaction. Therefore, the ability to place a vote is critical and is covered in a series of three recipes. Automatic and manual voting is covered in the first two recipes. The third recipe demonstrates the effect that voting and transaction options have on a transaction when working with multiple components.

The transaction isolation level prevents a component from working with data that is in an intermediate state. Changing the transaction isolation level is covered in one recipe.

Two recipes cover the use of a new lightweight transactional model that was introduced in the .NET Framework 2.0. It provides an alternative to using ServicedComponent and COM+ when only transaction support is needed. The first of these recipes covers the use of transactional code blocks, while the other demonstrates how to write your own resource manager. The ability to develop a resource manager enables the use of transactions for tasks that traditionally do not use transactions.

The final recipe demonstrates the use of services without components. This feature allows you to use COM+ services such as transactions without the need to implement a COM+ component.