| love to shop, get traffic conditions, compare products, and read product reviews online. However, I'm finding that there are many things I'd like to do on the Internet that are still not possible today. For example, not only do I want to find restaurants in my area that serve a specific type of cuisine, I'd like to know if a particular restaurant has seating available at 7 pm tonight. If I had my own business, I might like to know which vendor has a particular item in stock. If several vendors can supply the item, I would want to know which vendor has the best price and which one can deliver the item faster.|
Services like these don't yet exist because there are no standards in place for integrating all this proprietary information. After all, vendors have their own way of describing items they sell. Additionally, it is difficult to develop the code necessary to integrate such services.
It should be no surprise that Microsoft recognizes the need to integrate services like these. To that end, Microsoft is embracing the XML standard for describing data, and is providing a new development platform infrastructure that I believe will help developers create and deploy distributed applications on Internet time.
This platform is called the MicrosoftÂ® .NET Framework (.NET). .NET will allow developers to take better advantage of technologies than any earlier Microsoft platform. Specifically, .NET will really deliver on code reuse, code specialization, resource management, multilanguage development, security, deployment, and administration. While designing this new platform, Microsoft also improved some features of the current WindowsÂ® platform. I will address many of these improvements in this article.
Common Language Runtime and Class Library .NET represents a whole new way of developing software. You'll notice that I didn't say developing software for Windows. That's because .NET is not inextricably tied to the Windows operating system. In this article, I'll discuss the .NET architecture and describe how this new platform offers the type of efficiencies I mentioned earlier.
At the heart of the .NET platform is a common language runtime engine and a base framework. All programmers are familiar with these concepts. I'm sure many of you have at least dabbled with the C runtime library, the standard template library, the MFC library, the Active Template Library, the Visual BasicÂ® runtime library, or the Java virtual machine. In fact, the Windows operating system itself can be thought of as a runtime engine and library. Runtime engines and libraries offer services to applications, and programmers love them because they save time and facilitate code reuse.
The .NET base framework will allow developers to access the features of the common language runtime and also will offer many high-level services so that developers don't have to code the same services repeatedly. But more importantly, the .NET common language runtime engine sitting under this library will provide the technologies to support rapid software development. The following lists a small sampling of features to be provided by the .NET common language runtime engine.
Consistent programming model All application services are offered via a common object-oriented programming model, unlike today where some OS facilities are accessed via DLL functions and other facilities are accessed via COM objects.
Simplified programming model .NET seeks to greatly simplify the plumbing and arcane constructs required by Win32Â® and COM. Specifically, developers no longer need to gain an understanding of the registry, GUIDs, IUnknown, AddRef, Release, HRESULTS, and so on. It is important to note that .NET doesn't just abstract these concepts away from the developer; in the new .NET platform, these concepts simply do not exist at all.
Run once, run always All developers are familiar with DLL Hell. Since installing components for a new application can overwrite components of an old application, the old app can exhibit strange behavior or stop functioning altogether. The .NET architecture now separates application components so that an app always loads the components with which it was built and tested. If the application runs after installation, then the application should always run. This marks the end of DLL Hell.
Execute on many platforms Today, there are many different flavors of Windows: Windows 95, Windows 98, Windows 98 SE, Windows Me, Windows NTÂ® 4.0, Windows 2000 (with various service packs), Windows CE, and soon a 64-bit version of Windows 2000. Most of these systems run on x86 CPUs, but Windows CE and 64-bit Windows run on non-x86 CPUs. Once written and built, a managed .NET application (that consists entirely of managed code, as I'll explain shortly) can execute on any platform that supports the .NET common language runtime. It is even possible that a version of the common language runtime could be built for platforms other than Windows in the future. Users will immediately appreciate the value of this broad execution model when they need to support multiple computing hardware configurations or operating systems.
Language integration COM allows different programming languages to interoperate with one another. .NET allows languages to be integrated with one another. For example, it is possible to create a class in C++ that derives from a class implemented in Visual Basic. .NET can enable this because it defines and provides a type system common to all .NET languages. The Microsoft Common Language Spe-cification (discussed in Part 2 of this article) describes what compiler implementors must do in order for their languages to integrate well with other languages. Microsoft is providing several compilers that produce code targeting the .NET common language runtime: C++ with managed extensions, C# (pronounced "C sharp"), Visual Basic (which now subsumes VBScript and Visual Basic for Applications), and JScriptÂ®. In addition, companies other than Microsoft are producing compilers for languages that also target the .NET common language runtime.
Code reuse Using the mechanisms I just described, you can create your own classes that offer services to third-party applications. This, of course, makes it extremely simple to reuse code and broadens the market for component vendors.
Automatic resource management As you know, programming requires great skill and discipline. This is especially true when it comes to managing resources such as files, memory, screen space, network connections, database resources, and so on. One of the most common bugs occurs when an application neglects to free one of these resources, causing that application or others to perform improperly at some unpredictable time. The .NET common language runtime automatically tracks resource usage, guaranteeing that your application never leaks resources. In fact, there is no way to explicitly free a resource. In a future article, I'll explain exactly how this works.
Type safety The .NET common language runtime can verify that all your code is type safe. Type safety ensures that allocated objects are always accessed in compatible ways. Hence, if a method input parameter is declared as accepting a 4-byte value, the common language runtime will detect and trap attempts to access the parameter as an 8-byte value. Similarly, if an object occupies 10 bytes in memory, the application can't coerce this into a form that will allow more than 10 bytes to be read. Type safety also means that execution flow will only transfer to well-known locations (namely, method entry points). There is no way to construct an arbitrary reference to a memory location and cause code at that location to begin execution. Together, these eliminate many common programming errors and classic system attacks such as the exploitation of buffer overruns.
Rich debugging support Because the .NET common language runtime is used for many languages, it is now much easier to implement portions of your application using the language that's best suited for it. The .NET common language runtime fully supports debugging applications that cross language boundaries. The runtime also provides built-in stack-walking facilities, making it much easier to locate bugs and errors.
Consistent error-handling One of the most aggravating aspects of programming in Windows is the inconsistent ways errors are reported. Some functions return Win32 error codes, some return HRESULTS, and some raise exceptions. In .NET, all errors are reported via exceptionsâ"period. Exceptions allow the developer to isolate the error-handling code from the code required to get the work done. This greatly simplifies writing, reading, and maintaining code. In addition, exceptions work across module and language boundaries as well.
Deployment Today, Windows-based applications can be incredibly difficult to install and deploy. There are usually several files, registry settings, and shortcuts that need to be created. In addition, completely uninstalling an application is nearly impossible. With Windows 2000, Microsoft introduced a new installation engine that helps with all of these issues, but it is still possible that a company authoring a Microsoft Installer Package may fail to do everything correctly. .NET seeks to make these issues ancient history. .NET components are not referenced in the registry. In fact, installing most .NET-based applications will require no more than copying the files to a directory, and uninstalling an application will be as easy as deleting those files.
Security Traditional OS security provides isolation and access control based on user accounts. This has proven to be a useful model, but at its core it assumes that all code is equally trustworthy. This assumption was justified when all code was installed from physical media (such as CD-ROM) or trusted corporate servers. But with the increasing reliance on mobile code such as Web scripts, Internet application downloads, and e-mail attachments, there is a need for more granular control of application behavior. The .NET Code Access Security model, which I will discuss in Part 2, will deliver this control.
Microsoft also recognizes that many apps need to enforce behaviors based on a concept of roles as opposed to individual accounts. Microsoft initially delivered support for this concept with Microsoft Transaction Server (MTS), and provided further enhancements with COM+ 1.0. In .NET, Microsoft supports the deployment of application-defined roles and access control based on these roles. These mechanisms are extended in ways that are appropriate for the Internet and heterogeneous environments.
As you might imagine, addressing all these issues requires an architecture consisting of many pieces. To create a clearer picture of the .NET Framework, I'll walk through the process of developing and deploying an application. The first step is to determine what type of application I intend to build and what languages I will use to develop it. For this discussion, let's assume that I already know the application type, that the application is designed, the specs are written, and I'm ready to start development.
The Right Language for the Job My application consists of several different subcomponents. To make development easier, I realize that certain programming languages are best tailored to handle specific tasks. In addition, some of the developers on my team prefer working in C++, while others prefer Pascal. In .NET, the language you select for any given task is totally up to you; it is simply a matter of personal choice.
As far as the .NET common language runtime is concerned, all languages are equal. Now it's true that some languages offer certain features that others don't, but these are language features, not common language runtime features. For example, the .NET common language runtime supports the creation and manipulation of threads. Since this is a common language runtime feature, any language that targets the common language runtime will be able to create and manipulate threads.
On the other hand, Visual Basic is specifically designed to prevent programmers from shooting themselves in the foot. One way that Visual Basic does this is by treating all variables, function names, and so on as case-insensitive symbols. This feature of Visual Basic is purely a language issue and has no bearing on the Visual Basic runtime at all.
Microsoft is planning to create four language compilers that target the common language runtime: C++ with managed extensions, C#, Visual Basic, and JScript. By default, the Microsoft Visual C++Â® compiler builds an executable or DLL that does not target the common language runtime; specifying a new command-line switch will make the Visual C++ compiler target it. The term "managed code" describes code that requires the common language runtime. Unmanaged code describes code that does not require the common language runtime engine.
Of the four Microsoft compilers mentioned, Visual C++ is the only one capable of producing unmanaged code. Since the other compilers (C#, Visual Basic, and JScript) can only produce managed code, the code written in these languages absolutely requires the common language runtime engine in order to execute. In addition to Microsoft, several companies are producing compilers that generate managed code. I am aware of compilers for APL, CAML, Cobol, Haskell, Mercury, ML, Oberon, Oz, Pascal, Perl, Python, Scheme, and Smalltalk. In fact, Rational is planning to create a Java-language compiler that targets the .NET common language runtime.
Assemblies (Managed Components) After I create my source code files, I run them through the respective compilers, and the result is an EXE or DLL. These EXE or DLL files are very similar to the Portable Executable (PE) files that you've become familiar with over the yearsâ"in fact, they are valid PE filesâ"with just a few differences.
One difference is that the code in managed PE files generated by the language compilers is not x86 machine code or machine code targeted to any specific CPU platform. Instead, the code generated by the compiler is a Microsoft intermediate language (MSIL), which will be discussed in more detail later.
Another difference is that the file contains metadata emitted by the compiler that is used by the common language runtime to locate and load class types in the file, lay out object instances in memory, resolve method invocations and field references, translate MSIL to native code, enforce security, and perform a whole slew of additional features. Metadata will be discussed in more detail in Part 2 of this article.
Yet another difference is that the components you are producing are not just EXEs or DLLs. Rather, the unit of reuse and deployment in .NET is an assembly. Depending on the choices you make in your compiler or tool, you might be producing a single-file assembly or you might be producing a managed code module that is intended to be deployed as a part of a multi-file assembly. Think of an assembly as a logical EXE or DLL. In fact, because in the single-file assembly case the assembly carries an .exe or .dll extension, you might wonder why a new concept was introduced. The point, very precisely, is that by decoupling the logical and physical notions of a reusable component, you get to partition your code into multiple files as an implementation choice (for example, to get benefits of incremental and on-demand download), while still maintaining the collection as a single unit of versioning and deployment.
From an assembly consumer's perspective (the external view), an assembly is a named and versioned collection of exported types and resources. From an assembly developer's perspective (the internal view), an assembly is a collection of one or more filesâ"from a single PE file to a collection of PE files, resource files, HTML pages, GIFs, and so onâ"that implement types and resources.
The components of an assembly are described in a manifest. A manifest is a block of data that enumerates the assembly's files, and controls what types and resources are exposed outside of the assembly. For example, some types may be reserved for use only within the assembly, while others may be exported to consumers of the assembly. The manifest also governs how references to these types and resources are mapped onto the files that contain their declarations and implementations, and enumerates other assemblies on which this one is dependent. The existence of a manifest provides a level of indirection between consumers of the assembly and the implementation details of the assembly and makes assemblies self-describing.
Every module loaded at runtime is loaded in the context of an assembly. Every type that is loaded is scoped in an assembly scopeâ"that is, part of the identity of a type is its assembly identity. A type Foo loaded in the scope of one assembly is not the same type Foo loaded in the scope of another assembly even if a hash of their declarations and implementations are exactly the same.
As a developer, you will make explicit choices at development time. If you import a type from another assembly, your own file's metadata will contain references to that assemblyâ"the compiler will put them there. If you import a type from an individual module within a multi-file assembly, your own file will contain an explicit dependency on that moduleâ"again, the compiler will put it there. In the latter case, .NET will honor and enforce assembly boundaries at runtime, making sure that you and that module are, in fact, built as part of the same assembly.
The Microsoft languages and compilers make it easy to tackle these decisions by providing reasonable defaults for the typical case and by providing directives and toolsâ"such as the Assembly Linker utility, AL.exe, that aid in building multi-file assemblies.
Assemblies are the basis for the deployment and versioning features of .NET. Assemblies allow developers and administrators to express strict version dependencies between pieces of an application and, because they are self-describing, help enable the notion of zero-impact install. Assemblies also play a role in the .NET security system in that the assembly is the unit at which permissions are requested and granted. To properly enforce the versioning, deployment, and security features in .NET, the assembly acts as the resolution scope that is used to resolve references to types. .NET honors the rules expressed in the assembly manifest for resolving types and resources to their implementation files within an assembly and for binding to dependent assemblies.
System Services Included with .NET will be a base framework assembly that contains several class definitions, where each class exposes some feature of the underlying platform. Since Microsoft defines literally hundreds of classes in the base library, the library is divided into namespaces that group related classes together.
For example, the System namespace (which you should become most familiar with) contains the base class, Object, which all other classes ultimately derive from (I'll mention more about this later). In addition, the System namespace contains classes for exception handling, garbage collection, console I/O, as well as a bunch of utility classes that convert safely between data types, format data types, generate random numbers, and perform various math functions.
Figure 1 shows a small sampling of some of the other namespaces included in the base framework. Most of the namespaces listed in Figure 1 show "lower-level" classes. However, the base framework also includes namespaces for building rich user interface applications. For example, the System.Web.UI.WebControls and System.WinForms namespaces include classes that allow for very rich user interfaces in both Web-based and non-Web-based applications. .NET and the base framework allow developers to build the following application types: Web Services, Win32-based GUI applications, Win32 CUI-based applications, services (controlled by the Service Control Manager), compilers and tools, personal-tier applications, and components.
To access any of the platform's features, you need to work with these namespaces and their defined classes. If you want to customize the behavior of any class, you can simply derive your own class from the desired base library class.
.NET can present a consistent programming paradigm to software developers because of the object-oriented nature of the platform. Developers can create their own namespaces containing their own classes. These merge seamlessly into the programming paradigm, greatly simplifying software development when compared to classic Windows-based programming paradigms.
Intermediate Language, the Common Language Runtime, and JIT Compilers As stated earlier, compiling your source code causes the compiler to emit MSIL. MSIL is a CPU-independent intermediate language created by Microsoft after consultation with several external commercial and academic language/compiler writers. MSIL is much higher level than most CPU machine languages. It understands object types and has instructions that create and initialize objects, call virtual methods on objects, and manipulate array elements directly. It even has instructions that raise and catch exceptions for error handling.
Like any other machine language, MSIL can be written in assembly language. Microsoft provides an MSIL assembler, ILAsm.exe, as well as an MSIL disassembler, ILDasm.exe. The important thing to note about MSIL is that it is not tied to any specific CPU platform. This means that a PE file containing MSIL can run on any CPU platform as long as the operating system running on that CPU platform hosts the .NET common language runtime engine. Initially, Microsoft plans to offer the .NET common language runtime engine on the various flavors of Windows 95, Windows 98, Windows NT 4.0, Windows 2000 (both 32-bit and 64-bit), and Windows CE.
Another benefit of MSIL is that it provides a hardware abstraction layer. While the initial release of the common language runtime is planned to run only on 32-bit Windows platforms, developing an application using managed MSIL allows the application to be more independent of the underlying operating system. For example, MSIL will help make code portable to 64-bit versions of Windows when the common language runtime is available on that platform. Finally, MSIL is a very important aspect of the .NET security story since verification (discussed in Part 2) is capable of examining the code's intention regardless of what high-level language was used to generate the code.
Today, when you build a managed executable, the module imports a function (called _CorExeMain) from the .NET common language runtime (MSCorEE.dll). When the user invokes the executable, the operating system loader loads MSCorEE.dll and jumps to an unmanaged entry point inside the executable module. The code in the executable module is a small stub (6 bytes on x86) that simply jumps to the _CorExeMain function contained inside MSCorEE.dll. Once the common language runtime engine has initialized, the engine locates the executable module's managed entry point and then starts executing the executable module's managed MSIL code.
In the future, operating system loaders will be modified to look specifically for modules containing managed MSIL code. When the OS loader detects a module with managed code, the unmanaged stub entry point will no longer be necessary and the common language runtime will simply be able to jump to the executable's managed entry point.
Of course, the MSIL instructions cannot be executed directly by today's host CPUs. So, the common language runtime engine must first compile the managed MSIL instructions into native CPU instructions. This process is shown in Figure 2. (Note that other parts of the figure are described in Part 2 of this article.)
|Figure 2 Steps in Compiling Source Code |
The need to compile MSIL into native code raises several questions. For example, should the common language runtime convert all of the file's MSIL code to CPU instructions at load time? The advantage of doing this is that the program runs very fast. However, the disadvantage is that compiling the MSIL requires time, and this would significantly prolong the application's initialization time. In addition, very rarely does a user cause an application to execute all of its code. If the common language runtime compiles all of the MSIL to CPU instructions, it is likely that a lot of time and memory would be wasted.
The consensus is that it would be much more efficient to have the common language runtime compile the MSIL instructions as functions are being called. When the common language runtime loads a class type, it connects stub code to each method. When a method is called, the stub code directs program execution to the component of the common language runtime engine that is responsible for compiling the method's MSIL into native code. Since the MSIL is being compiled just-in-time (JIT), this component of the runtime is frequently referred to as a JIT compiler or JITter. Once the JIT compiler has compiled the MSIL, the method's stub is replaced with the address of the compiled code. Whenever this method is called in the future, the native code will just execute and the JIT compiler will not have to be involved in the process. As you can imagine, this boosts performance considerably.
The common language runtime is slated to ship with two JIT compilers, the normal compiler and an economy compiler. The normal compiler examines a method's MSIL and optimizes the resulting native code just like the back end of a normal, unmanaged C/C++ compiler. The economy JIT compiler is typically used on machines where the cost of using memory and CPU cycles is high (such as many Windows CE-powered devices). The economy compiler simply replaces each MSIL instruction with its native code counterpart. As you can imagine, the economy compiler compiles code much faster than the normal compiler; however, the native code produced by the economy compiler is significantly less efficient. Even so, the economy compiler will still produce code that executes much faster than interpreted code.
When the common language runtime is ported to a new CPU platform, Microsoft first creates the economy JITter for that platform. Since the economy JITter is relatively easy to implement, this allows .NET applications to run on the new CPU platform in a very short period of time. Once the economy JITter is complete, Microsoft then focuses its efforts on the normal JITter to improve application performance.
When Microsoft ships the .NET common language runtime, the normal JITter will be the default on most machines. However, on machines with small footprints, like Windows CE-powered devices, the economy JITter will be the default because it requires less memory to run.
In addition, the economy JITter supports code pitching. Code pitching is the ability for the common language runtime to discard a method's native code block, freeing up memory used by methods that haven't been executed in a while. Of course, when the common language runtime pitches a block of code, it replaces the method with a stub so that the JITter can regenerate the native code the next time the method is called.
For those of you who are used to developing in low-level languages like C or C++, you're probably thinking about the performance ramifications of all this. After all, unmanaged code is compiled for a specific CPU platform, and when invoked the code can simply execute. In this managed environment, compiling the code is accomplished in two phases. First, the compiler passes over the source code, doing as much work as possible in producing MSIL. But then, in order to actually execute the code, the MSIL itself must be compiled into native CPU instructions at runtime, requiring that more memory and additional CPU time be allocated to do the work.
Believe me, since I approached the runtime from a C/C++ background myself, I was quite skeptical and concerned about this additional overhead. The truth is, managed code does not execute as fast and does have more overhead than unmanaged code. However, Microsoft has done a lot of performance work to address these issues. In addition, I've spoken to many developers at Microsoft who truly believe that in the future managed code will actually offer better performance than unmanaged code. Here's why: when the JITter compiles the MSIL code at runtime, it knows more about the execution environment than the compiler knows. For example, the JITter can detect that the host CPU is a Pentium III and generate CPU instructions that take advantage of any performance enhancements that Intel has made to the Pentium III over the Pentium II or Pentium.
In addition, the JITter may generate code that uses CPU registers that a compiler would normally avoid using. Or, a JITter may be able to detect that a variable always contains a specific value and can generate small, fast code that works solely because the JITter can make a runtime assumption. Furthermore, memory allocations are significantly faster than allocating memory via the Win32 HeapAlloc function. I will address this issue more fully in a future article.
Microsoft plans to offer a tool, tentatively called PreJit.exe, that can compile an entire assembly to native code and save the result on disk. When the assembly is loaded the next time, this saved version is loaded and the application starts up faster as a result. Because this tool takes advantage of information about other assemblies that have already been loaded when PreJit is run, it is best used in a warm-up mode. You turn PreJit on, and as assemblies are loaded, they are compiled and saved. After your application has run for a sufficient length of time, you turn off PreJit, and from then on the application will start up more quickly. Microsoft uses a variation on this same technique to make the key assemblies (such as the base framework) that will ship with .NET start faster.
The Common Type System By now you know that developing an application for the .NET Framework requires the base library classes to access platform features and services. You also know how to compile source code and how to access the mechanisms required to get the code to execute. In this section, I'll discuss how to extend the base framework by defining your own class objects.
The formal specification of the type system implemented by the common language runtime is called the Common Type System (CTS). The CTS specifies how object classes (called types) are defined. For example, the CTS allows a class type to contain zero or more members. The following is the list of possible members:
Field A data variable that is part of the object's state. Fields are identified by their name and type.
Method A function that performs an operation on the object, usually changing the object's state. Methods have a name, signature, and modifiers. The signature specifies the calling convention, number of parameters (and their sequence), the types of the parameters, and the type of value returned by the method. The modifiers can include custom attributes, whether the method is public, private, static, and so on.
Property To the caller, this member looks like a field. But to the class implementor, this member looks like a method. Properties allow an implementor to calculate a value only when necessary and allow a class user to have simplified syntax. Properties also allow you to create read-only or write-only "fields."
Event Events provide a notification mechanism between an object and other interested objects. For example, a button could offer an event that notifies other objects when the button is clicked.
The CTS also specifies the rules for type visibility and for access to the members of a type. Types are either visible outside of the assembly, to clients of the assembly, or they are visible only to code within the same assembly. Marking a type as public enables it to be visible (exported) outside of the assembly. Thus, the CTS establishes the rules by which assemblies form a boundary of visibility for a type and its methods, and the common language runtime enforces the visibility rules. Regardless of whether a type is visible to a caller, the type gets to control whether the caller has access to its members. Figure 3 shows the valid options for controlling access to a method or field.
In addition, the CTS defines the rules governing type inheritance, virtual functions, object lifetime, and so on. These rules have been designed to accommodate the semantics expressible in the languages you use today. In fact, you won't even need to learn the CTS rules per se, as the language you choose will expose its own language syntax and type rules in the same way you are familiar with, and will map this language-specific syntax into the syntax of the common language runtime when it emits the PE file.
When I first started working with the common language runtime, I realized that it is best to think of the language and the behavior of your code as two separate and distinct things. Using Visual C++, you can define your own classes with their own members. Of course, you could have used C# or Visual Basic to define the same class with the same members. Sure, the syntax you use for defining this class will differ depending on the language you choose, but the behavior of the class will be absolutely identical because the common language runtime CTS defines the behavior of the class object.
To help make this clear, let me give you an example. The CTS supports single inheritance only. So while the C++ language supports classes that inherit from multiple base classes, the CTS cannot accept and operate on any such class. To help the developer, the Visual C++ compiler will report an error if it detects that you're attempting to create managed code that includes a class inherited from multiple base classes.
Here's another CTS rule: all class types must (ultimately) inherit from a predefined class type called System.Object. As you can see, Object is the name of a type defined in the System namespace. This Object is the root of all other class types and therefore guarantees every class type has a minimum set of behaviors. Specifically, the System.Object type allows two objects to be compared for equality, allows you to uniquely identify an object via a hash code, allows you to query the object's true class type, allows you to perform a shallow (bitwise) copy of the object, and allows you to obtain a string representation of the object's current state.
Conclusion There is much more to say about the objectives Microsoft has set for the .NET platform than I've been able to cover here. In Part 2 of this series I'll cover many more topics including the Common Language Specification that allows multiple languages to integrate seamlessly with the .NET services, metadata, application deployment, verification, security, and interoperability with existing unmanaged components. In the coming months, I'll be writing more articles that discuss features specific to the development of applications and components for .NET.