Interoperability: System.Runtime.InteropServices

< BACK  NEXT >
[oR]

Windows DNA is a successful group of technologies. Lots of applications that were built using COM, Active Server Pages, COM+, and the rest of the DNA family exist. Many of these applications play an important role in running businesses, so they're certain to remain in use for at least the next few years. No matter how successful the .NET Framework is, the Windows DNA technologies that preceded it are not going away anytime soon.

Software using pre-.NET technologies will not just disappear

Given the huge investment Microsoft's customers have made in these applications, the .NET Framework must provide some way for new applications to connect with them. Just as important, the Framework must provide an effective way for managed code to access existing DLLs that weren't built using COM and to invoke the raw services provided by the underlying operating system. Solutions to all of these problems are provided by the classes in the System.Runtime.InteropServices namespace.

The types in System.Runtime.-InteropServices allow interoperability with existing software

Accessing COM Objects

The problem of interoperating with COM objects is in some ways similar to an issue we've seen earlier in this chapter: translating from one type system to another. Just as mapping between the CLR's type system and that defined by XML is problematic, so is mapping between the CLR type system and that defined by COM. You might expect mapping to COM to be a bit easier, since both the CLR and COM are Microsoft-owned technologies. Yet the truth is that even though Microsoft owns it, COM is frozen in stone. The millions of lines of existing COM-based code in the world won't change to accommodate the new type system of the CLR, so the .NET Framework's solution for COM interoperability must adapt itself to the reality of the installed base.

A key aspect of interoperability is mapping from COM types to CLR types

Rebuilding the World

By and large, the .NET Framework is a beautiful thing. It's well designed, consistent, and quite elegant. Microsoft has completely rethought what an application platform should look like and then redesigned the developer's world from the ground up. (What a once-in-a-lifetime treat for its designers: Build a new development environment, more or less from scratch, the way you think it should be.) The breadth of the company's ambition is breathtaking, and the degree to which it has succeeded is very, very impressive.

But it's not perfect. Nothing is perfect.

Rebuilding the world is a lot of work, and it's unreasonable to expect anybody to finish the job in the first release. Still, it is surprising that the implementation of COM+ services in the .NET Framework class library is just a wrapper around the existing code. COM+ is certainly a large piece of code, yet given the scale of the .NET effort, it's not really all that big. And there are some corollaries of this decision that are downright ugly, such as supporting remote access to serviced components only via DCOM.

Completely rewriting COM+ would have allowed Microsoft to rethink the way its services were provided rather than just exposing the existing programming model to managed code. While this rethinking may appear in the future, Microsoft has also committed itself to support this version indefinitely. The services provided by COM+ are important, and so leaving them in their initial form is unfortunate.

Yet while the Framework's current imperfections are sometimes annoying, they're hardly deadly, and they're likely to be fixed in a future release. Whatever its rough edges today, the .NET Framework is a remarkable piece of work. Its creators should be proud of what they've produced.

Doing this can be simple. In some cases, mapping from a COM interface to a CLR type is straightforward. It can also be quite difficult, however, especially when the COM interface involved uses complex types. While it's virtually always possible to map the two together in some way, it isn't always easy. To make even difficult mappings possible, the classes in System.Runtime.InteropServices provide very fine-grained control over how the mapping is done as well as many, many options. While most people won't use most of these options most of the time, it's still good to know that they're available.

Mapping between COM and managed objects can be simple, but it can also be complex

The fundamental model for interoperation between managed code and COM-based code is that each side sees the other in the form it expects: Managed code sees COM-based code as managed types, while COM-based code sees managed code as COM objects. How this looks is shown in Figure 5-10. To provide this illusion, the .NET Framework relies on two kinds of wrappers. One, known as a runtime callable wrapper (RCW), allows managed code to call a COM object. The other, a COM callable wrapper (CCW), allows COM code to access managed code.

Figure 5-10. The .NET Framework's COM interoperability services can make a COM object look like managed code and managed code look like a COM object.
graphics/05fig10.gif

Interoperability lets COM code and managed code each see the other as being like themselves

But where does the information needed to create these wrappers come from? Managed code sees the world in terms of assemblies, so to access a COM object as managed code, an assembly that mimics the COM class must exist. Furthermore, this assembly must contain metadata that accurately reflects the COM class's interfaces. To create this interoperability assembly, the .NET Framework provides a tool called Tlbimp, also known as the Type Library Importer. The input to this tool is a COM type library, and the output is an assembly that contains metadata for the CLR analogs of the COM types in the type library.

Tools create wrappers for COM objects

Once this assembly has been created, managed code can treat the library's COM classes just like any other managed code. When the managed code creates an instance of the class, the RCW actually creates the COM object. When the managed code invokes a method, the RCW makes a corresponding method invocation on the COM object. If an error occurs and the COM method returns an error HRESULT, as COM requires, the RCW automatically turns this into an exception that can be caught by the managed code. And when the managed code is finished using the object, it can behave just as it does when using any other managed object. The RCW will decrement the COM object's reference count before it is itself destroyed by the CLR's garbage collector.

The wrapper maps between COM's behavior and what managed code expects

When a COM client uses a managed class, the same kinds of things happen in the opposite direction. Rather than producing an assembly from a type library, the developer can now produce a type library from an assembly. The Type Library Exporter tool, known as Tlbexp, provides one way to do this. Also, because COM uses the registry to determine which code should be loaded for a particular class, assemblies that will be accessed by COM clients must have appropriate registry entries. The Assembly Registration Tool, Regasm, can be used to do this and optionally to register the assembly's generated type library as well. When a COM client creates and uses an instance of a managed class, translations between the two worlds are performed as before, but this time they're done by the CCW rather than the RCW.

Tools also create wrappers for managed objects

All of this sounds simple and straightforward, and it often is. Yet what's not been addressed so far is the process of converting between the CLR type system and the COM type system. Much as with .NET Remoting, data must be marshaled between the two environments by the wrappers. Default mappings are defined, and if those defaults work, using code from the other world is simple. Marshaling an integer, for example, is straightforward, since a value of this type is the same in both environments. If the default mappings aren't appropriate, however, a developer's life gets more complex. What should a CLR string map to in the COM world, for example? COM has more than one string format, and it's not always obvious which one should be used. To control this and other marshaling choices, a developer can use the MarshalAs attribute to indicate the choice she prefers. Figuring out the right thing to do isn't always easy, but the fine-grained control the types in this namespace provide at least makes it possible.

A developer can customize the mapping between these two environments

One last point worth noting is that making calls across the boundary between managed and unmanaged code is noticeably more expensive than making calls solely within either environment. Marshaling data between the two takes time; writing managed code that interoperates with unmanaged code has performance implications. It's a good idea to do as much work as possible on each call across this boundary. If each one does only a small amount of work, an application that makes a large number of calls between the two worlds is unlikely to perform especially well.

Calls between managed and unmanaged code are expensive

Accessing Non-COM DLLs

While much of the existing code a .NET Framework application needs to use is accessible as COM classes, much of it isn't. Plenty of useful DLLs that don't use COM have been created. One important example of this is the Win32 API, exposed as a set of DLLs that allow direct access to Windows services. To allow managed code to call functions in these DLLs, the .NET Framework provides what are called platform invoke services, a phrase that's commonly shortened to just PInvoke.

PInvoke allows managed code to call existing DLLs

To use these services, a developer must specify the name of some DLL he wishes to use, the entry point to be called, the parameter list, and possibly other information. How this is done varies with the language in use. In VB.NET, for example, the Declare statement is used, while C# relies on an attribute called DllImport. Whichever choice is made, once a DLL function has been appropriately specified, it can be invoked as if it were a function in a managed object. The platform invoke services provide the necessary translations, including marshaling of parameter types, to carry out the call.

System.Runtime.InteropServices is a critically important part of the .NET Framework class library. Although the notion of legacy software is sometimes viewed pejoratively, it has one enormous thing going for it: We know it works. If new code written on the .NET Framework had no way to communicate with the installed base, this new platform would have been much less attractive. The Framework's strong support for interoperability with existing code recognizes this reality, doing its part to smooth the transition to the brave new .NET world.

Interoperability is an essential feature of the .NET Framework

How Many Customers Will Microsoft Lose in the Transition to the .NET Framework?

Suppose you're an organization that has made a substantial investment in Windows DNA. Just as Microsoft wanted you to, you've trained your entire staff in COM and other Windows DNA technologies, and you've built many mission-critical applications on this platform. Microsoft now tells you that you should build new applications in the completely different environment provided by the .NET Framework. Are you happy about this?

Developers may well be happy, because they often like learning the latest technologies. But the people who pay those developers' salaries are likely to be less thrilled. While the .NET Framework offers the potential of better productivity and new kinds of applications, it also incurs some immediate costs, such as retraining an entire development staff. Might the pain of this transition make some existing Microsoft customers bolt to a competing technology?

I doubt it. The only really viable alternative is the Java world, and I don't expect the advent of the .NET Framework to push many organizations into this other camp. For one thing, retraining a Windows DNA developer for the Java environment is likely to be even more expensive then retraining him for the .NET Framework, since few of his current language and operating system skills will apply. Furthermore, Java-based products don't have the built-in support for interoperability with Windows DNA code that is provided by the .NET Framework. By adding strong support for communicating with existing DLLs and COM objects, Microsoft has made migration to its new platform more attractive.

Customers who are unhappy with Microsoft for other reasons, such as poor support, may take this opportunity to switch sides. But because the difficulty and expense of moving from Windows DNA to the .NET Framework are significantly lower than the cost of moving to Java, I doubt we'll see many defections caused purely by this transition in technologies.

< BACK  NEXT >


Understanding. NET. A Tutorial and Analysis
Understanding .NET: A Tutorial and Analysis (Independent Technology Guides)
ISBN: 0201741628
EAN: 2147483647
Year: 2002
Pages: 60

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