You are on page 1of 5

Chapitre 1- Introduction aux technologies J2EE

70-536 Chapitre 13

Plan

„ Lesson 1 : Using COM Objects


.NET Framework 2.0
Application Development
Foundation
„ Lesson 2 : Exposing .NET Components to COM
Chapitre 13
„ Lesson 3 : Using Unmanaged Code

Interoperation

Mohamed Romdhani
INSAT, Octobre 2007 2
M. Romdhani, Novembre 2007

70-536 Chapitre 13 70-536 Chapitre 13

Importing Type Libraries


„ The .NET Framework provides ample support for COM Interoperability, and the
ability to import type libraries is included.
„ The mechanism that serves as a proxy so that the .NET runtime can
communicate with a COM component is known as a Runtime Callable Wrapper
(RCW).
„ The RCW handles the majority of the work between .NET and COM for you, including
marshaling data types, handling events, and handling interfaces.

Lesson 1 : Using COM Objects „ Unlike pure .NET components, COM components must be registered before they
can be used. After they are registered, these components need to be imported
by using Visual Studio 2005 or the Type Library Importer tool (TlbImp.exe).

„ Perform the following steps to execute the Regsvr32 command to ensure that a
COM DLL you are using is registered:
„ Open a new Command window and Execute Regsvr32 yourdll.dll.

„ Now that the DLL has been registered, you have two ways to import it:
1. Visual Studio 2005
2. TlbImp.exe

„ Strictly speaking, you can also use some of the services available in the
System.Runtime.InteropServices namespace, but doing so is cumbersome
and error prone. For the sake of this lesson, we'll focus on the two approaches
M. Romdhani, Novembre 2007 3 just listed.
M. Romdhani, Novembre 2007 4

70-536 Chapitre 13 70-536 Chapitre 13


Using Visual Studio 2005 to Import a
Type
Using TlbImp.exe to Import a Type

„ With one minor exception, importing a COM „ Using the Type Library Importer utility (TlbImp.exe) is a little more
library is virtually indistinguishable from intricate but still quite straightforward. To import a library using the
importing any other type. TlbImp.exe, do the following:
„ The exception is that most of the DLLs you 1. Open the Visual Studio 2005 command prompt.
reference will be located on the COM tab of
the Add Reference dialog box, as illustrated 2. Navigate to the location of the DLL you want to import.
in front. 3. Type tlbimp <dllname>.dll
4. This will import the DLL and create a .NET assembly with its original
name. For example, Person.dll will be imported as Person.dll, MyObject
„ All registered COM components will be visible will be imported as MyObject.dll, and so forth.
there, so simply do the following to finalize the process:
5. If you want a name to be used other than the original DLL name, type
1. Create a new blank solution named COMDemos. tlbimp <dllname>.dll /out:<DesiredName>.dll.
2. Create a new Visual Basic 2005 or C# 2005 project, and name it
TypeDemoVB or TypeDemoCS, depending on the language you use. „ Now add a reference to the assembly name you chose just as you
3. Expand the Project list in Solution Explorer, and right-click the would for any other .NET assembly.
References node. „ The important thing to remember is that TlbImp.exe is creating a new
4. Choose the Add Reference option, and select the COM tab. (With most assembly for you from the COM library. So now you have a brand new
configurations, this will be the second tab of the dialog box, located next .NET assembly, and it will be visible only under the .NET tab of the Add
to the .NET tab.) Reference dialog box.
5. Find the component you want to register, and click OK.
M. Romdhani, Novembre 2007 5 M. Romdhani, Novembre 2007 6

1
Chapitre 1- Introduction aux technologies J2EE

70-536 Chapitre 13 70-536 Chapitre 13

Tools Used by COM Interop Using COM Objects in Code


„ Both the .NET Framework 2.0 and Visual Studio 2005 provide multiple
tools that are used by COM Interop. The .NET tools and their uses are „ After the steps in the "Importing Type Libraries" section have been
explained hereafter : performed, using an object contained in a given library is virtually
identical to using one created purely in .NET.
„ TlbImp.exe
„ In the following example, the Adobe Acrobat Reader 7.0 Browser
„ Type Library ImporterImports : a new .NET assembly based on the COM Document COM component will be used to open and read a .pdf file.
component
// C#
„ TlbExp.exe
axAcroPDF1.LoadFile(@"SamplePDFDocument.pdf");
„ Type Library Exporter : Creates a COM type library that can be consumed
by a .NET application axAcroPDF1.Print();
„ Regedit.exe
„ RegistryEditor : All COM components must have an entry in the Windows This might seem surprisingly easy.
registry. Although not exclusive to COM Interop, the Registry Editor allows However, think about what happens when a DLL is imported. A new .NET
you to search for and manage existing registry entries. assembly is created from it. From your code's perspective, there's no notion
„ Ildasm.exe of where the code originated, so it makes perfect sense that the behavior
would seem identical.
„ Intermediate Language Disassembler : Although not exclusive to COM
Interop, this tool enables you to view a visual representation of the
Intermediate Language (IL).
„ Regasm.exe
„ Assembly Registration Tool: Enables you to add .NET assemblies to and
remove .NET assemblies from the system registration database.

M. Romdhani, Novembre 2007 7 M. Romdhani, Novembre 2007 8

70-536 Chapitre 13 70-536 Chapitre 13

Handling Exceptions in COM Interop Limitations of COM Interop


„ Dealing with exceptions is one area that has changed drastically with
regard to consuming COM objects in .NET 2.0. „ Following is a list of these shortcomings:
„ In prior versions of the Framework, System.Exception sat at the top of „ Static/shared members
the Exception object hierarchy chain. This meant that that trapping a „ COM objects are fundamentally different from .NET types. One of the
System.Exception object would catch anything wrong in an application. differences is lack of support for static/shared members.
„ Because your COM errors won't be CLS compliant, they won't be „ Parameterized constructors
caught. Because so many developers mistakenly understood the „ COM types don't allow parameters to be passed into a constructor. This
behavior of trapping System.Exception, quite a bit of unstable code and limits the control you have over initialization and the use of overloaded
code with serious potential security implications was developed. constructors.
„ In version 2.0 of the .NET Framework, the RuntimeWrappedException „ Inheritance
class was introduced into the System.Runtime.CompilerServices „ One of the biggest issues is the limitations COM objects place on the
namespace. inheritance chain. Members that shadow members in a base class aren't
recognizable, and therefore, aren't callable or usable in any real sense.
„ RuntimeWrappedException Properties „ Portability
„ Message, Source, HyperLink, StackTrace, … „ Operating systems other than Windows don't have a registry. Reliance on
the Windows registry limits the number of environments a .NET application
can be ported to.
„ Exception Handling
„ In the current version both CLS and Non CLS-Compliant will be
caught by the class Exception.

M. Romdhani, Novembre 2007 9 M. Romdhani, Novembre 2007 10

70-536 Chapitre 13 70-536 Chapitre 13

Lesson 1 Summary

„ COM components can easily be consumed in a .NET application, although the


reasons for doing so need to be carefully considered.

„ You can use the COM tab of the Add Reference dialog box to add a reference to
registered COM components.

„ The TlbImp.exe tool is a command-line mechanism to import a COM component.


Lesson 2 : Exposing .NET Components to
„ The out switch of the TlbImp.exe tool can be used to change the name of the
assembly that will be imported from its original name to a preferred one.
COM
„ The default behavior when catching System.Exception objects now catches
both CLS-compliant and non-CLS-compliant exceptions.

„ The RuntimeCompatibilty attribute can be used to change whether non-CLS-


compliant exceptions will be trapped by default.

M. Romdhani, Novembre 2007 11 M. Romdhani, Novembre 2007 12

2
Chapitre 1- Introduction aux technologies J2EE

70-536 Chapitre 13 70-536 Chapitre 13


Building .NET Components for Use by
Hiding Public .NET Classes from COM
COM
„ Just as COM components can be consumed by .NET applications, the „ The first thing you need to be aware of when considering visibility is whether you want
everything to be visible or invisible (making everything invisible makes little sense) and the
reverse is true. level of granularity of this visibility.
„ To set COM Visibility to either On or Off by default, simply set the ComVisible Assembly attribute to
„ When .NET components are consumed by COM, a proxy known as a true or false, respectively.
COM Callable Wrapper (CCW) handles marshaling items between .NET // C#
and COM. [assembly: ComVisible(false)]
„ Next, for each class and each member that you want to have visible or invisible, simply use the
„ There's one additional step that needs to ComVisible attribute individually for each of them:
be performed for components that will be // C#
consumed by COM versus ones that won't. using System.Runtime.CompilerServices;
To accomplish this task from start to using System.Runtime.InteropServices;
finish, perform the following steps:
namespace NetForComDemoCS{
1. Create a .NET class library just like you [ComVisible(false)]
normally would.
class ComVisiblePerson {
2. Open the Project Properties dialog box private String firstName;
by right-clicking the project and selecting private String lastName;
Properties. private Int32 salary;
3. Click the Build tab, which is located on
the right side of the dialog box. [ComVisible(true)]
public String FirstName {
4. Select the Register For COM Interop get { return firstName; }
option in the Output section of the tab, as set { firstName = value; }
shown in Figure. }
5. Build the application. …

M. Romdhani, Novembre 2007 13 M. Romdhani, Novembre 2007 14

70-536 Chapitre 13 70-536 Chapitre 13

Deploying COM-Enabled Assemblies Lesson 2 Summary


„ Although an assembly can be created visible to COM, the MSDN documentation
provides the following guidelines to ensure that things work as planned: „ The Register For COM Interop option under the build configuration
„ All classes must use a default constructor with no parameters. automates the process of exposing .NET assemblies to COM
„ Any type that is to be exposed must be public. components.
„ Any member that is to be exposed must be public.
„ Abstract classes will not be able to be consumed.
„ The primary mechanism for exposing .NET assemblies to COM
„ After these criteria are met, the assembly is essentially ready to be exported. components is the ComVisible attribute.
There are two mechanisms for doing this. As with many other tasks, you can
use either Visual Studio 2005 or a command-line utility (TlbExp.exe). First, you
need to compile the type through Visual Studio's build mechanism or through „ The ComVisible attribute can be set to apply to an entire assembly, an
the command-line compiler. Here is an example: entire class, or individual members.
„ csc /t:library ComVisiblePerson.cs
„ The more granular the application of the ComVisible attribute, the more
„ Next you need to use the Type Library Exporter Utility. This should be done it takes precedence. A class, for example, can be marked with
from the Visual Studio 2005 command prompt:
„ tlbexp ComVisiblePerson.dll /out:ComVisiblePersonlib.tlb
ComVisible set to false and with a given member set to true. The
member that is set to true will be visible to COM components.
„ Next you need to create a resource script (ComVisiblePerson.res) with the
following Interface Definition Language (IDL) definition:
„ IDR_TYPELIB1 typelib "ComVisiblePersonlib.tlb"

„ Then you recompile the application with the new resource file added, as shown
here:
„ csc /t:library ComVisiblePerson.cs /win32res:ComVisiblePerson.res

M. Romdhani, Novembre 2007 15 M. Romdhani, Novembre 2007 16

70-536 Chapitre 13 70-536 Chapitre 13

Calling Platform Invoke

„ Platform Invoke, or P/Invoke as it's commonly called, is critical in


many instances where, for example, you need to call an unmanaged
Windows API. If the .NET Framework doesn't have an existent wrapper
and you don't have legacy code to do something, P/Invoke is often the
only viable solution.

„ You manage P/Invoke through the System.Runtime.InteropServices


Lesson 3 : Using Unmanaged Code namespace, just as you would manage other unmanaged code. To use
P/Invoke, you do the following:
1. Create a new static/shared external method with the name of the function you
want to call.
2. Decorate it with the DllImport attribute specifying the library that it should call.
3. Call the method from your code.

„ In the following example, we're going to use the GetWindowText Windows API.
„ To do this, we need to ensure that we are accurately referencing the active
window running on the operating system, which might not be our application.

M. Romdhani, Novembre 2007 17 M. Romdhani, Novembre 2007 18

3
Chapitre 1- Introduction aux technologies J2EE

70-536 Chapitre 13 70-536 Chapitre 13

Calling Platform Invoke Encapsulating DLL Functions


using System.Runtime.InteropServices;
„ Because P/Invoke calls can be less than elegant, at least in terms of what most
.NET developers are accustomed to doing, it's often beneficial to create a class
namespace OptionalCS{ that exposes them and wraps this functionality.
class WindowExample { „ After all, much of the .NET Framework is composed of precisely this
private const Int32 BufferSize = 256; methodology !!!! (Et Oui ! ça se passe comme ça chez MS)
[DllImport("user32.dll")] „ This approach has the following benefits:
private static extern IntPtr GetForegroundWindow(); „ Consumers of your class will not know this code from any other "normal" code they are
used to dealing with.
„ It relieves developers of having to remember the API call names and their respective
[DllImport("user32.dll")] parameters. You can create it once and then use it like any other .NET method.
private static extern Int32 GetWindowText(IntPtr hWnd, StringBuilder textValue, „ It will be less error prone. Even slight typing differences can cause P/Invoke calls to
Int32 counter); break. Even if you are a perfect typist and never forget anything, it's doubtful that
everyone you work with will have the same capability. And if they don't, they will
invariable type something incorrectly, miss a parameter, or forget the name of
public static void GetScreenDemo() { something.
StringBuilder DemoBuilder = new StringBuilder(BufferSize);
„ Assume that we are using the WindowsExample class shown in the previous
section. Wrapping it so that it can be used like a "normal" .NET class is already
IntPtr DemoHandle = GetForegroundWindow(); taken care of. Instead of repetitiously writing the code in the GetScreenDemo
if (GetWindowText(DemoHandle, DemoBuilder, BufferSize) > 0) { method each time you need that functionality, you can simply do the following:
Console.WriteLine(DemoBuilder.ToString()); „ // C#
} „ WindowExample.GetScreenDemo();
}
}
}

M. Romdhani, Novembre 2007 19 M. Romdhani, Novembre 2007 20

70-536 Chapitre 13 70-536 Chapitre 13

Converting Data Types Marshaling Structures


„ Chances are that if you've created even a rudimentary .NET application you've „ Structures are commonly used in many Windows APIs and methods
converted data from one type to another. that you will use through P/Invoke. So to understand how the
„ Perhaps the most common conversion is the ToString() method. Every time you unmanaged structures are marshaled, it's worth a brief discussion of
call ToString(), you are taking a given object and converting it to its String how managed types are handled as well.
representation.
„ In fully managed applications, you can specify conversion functionality by using „ By default, when a type is created, the CLR will decide how best to
the TypeConverter class. When using unmanaged code, however, you typically arrange the class's members.
need to take a different approach.
„ The first mechanism for converting data types is the MarshalAs attribute. MarshalAs „ To manually direct the CLR about how to handle (or not handle) the
can be applied to a property or a parameter. Either way, it works essentially the same. layout of a type, the
You simply create your property, decorate it with the MarshalAs attribute, and then System.Runtime.InteropServices.StructLayoutAttribute attribute is
specify the type it should be converted from, as shown here:
provided.
// C#
„ Arguably the most important aspect of using the StructLayoutAttribute
using System.Runtime.CompilerServices;
attribute is the constructor, which takes one of the following three
using System.Runtime.InteropServices; values:
namespace NetForComDemoCS{
„ LayoutKind.Auto Causes the developer to relinquish all control over the
class MarshalAsDemo { layout to the CLR
[MarshalAs(UnmanagedType.LPStr)]
„ LayoutKind.Sequential Causes the CLR to preserve the layout specified
public String FirstName; by the developer
public String LastName; „ LayoutKind.Explicit Causes the CLR to use the layout explicitly specified
[MarshalAs(UnmanagedType.Bool)] by the developer by using memory offsets
public Boolean IsCurrentlyWorking;
}}
M. Romdhani, Novembre 2007 21 M. Romdhani, Novembre 2007 22

70-536 Chapitre 13 70-536 Chapitre 13

Using a Callback with Unmanaged Code Using a Callback with Unmanaged Code
using System;
„ Callback functions are an extremely important tool in any developer's using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
library. namespace NetForComDemoCS{
„ Callbacks are used throughout the .NET Framework and Framework Class public class UnmanagedCallbackDemo {
Library extensively, and most nontrivial applications will employ them in public delegate Boolean DemoCallback(IntPtr hWnd, Int32 lParam);
some fashion or another. private const String UserReference = "user32.dll";
private const Int32 BufferSize = 100;
„ Just as callbacks are important in a totally managed environment, they are
also important in an unmanaged environment. [DllImport(UserReference)]
public static extern Int32 EnumWindows(DemoCallback callback,Int32 param);

„ Traditionally, callbacks were implemented with pointers. [DllImport(UserReference)]


public static extern Int32 GetWindowText(IntPtr hWnd, StringBuilder lpString, Int32 nMaxCount);
„ This approach afforded a tremendous amount of power to the programmer,
but it also had some inherent shortcomings, such as lack of type safety. public static Boolean DisplayWindowInfo(IntPtr hWnd, Int32 lParam) {
„ To address this issue, the .NET Framework provides Delegate objects, StringBuilder DemoBuilder = new StringBuilder(BufferSize);
which can be used to manage callbacks in a "type-safe" fashion. if (GetWindowText(hWnd, DemoBuilder, BufferSize) != 0) {
Console.WriteLine("Demo Output: " + DemoBuilder.ToString());
}
„ To use Delegate objects correctly, perform the following steps: return true;
}
1. Create a Delegate object with the same signature as the callback. public static void RunDemo() {
EnumWindows(DisplayWindowInfo, 0);
2. Substitute the Delegate for the callback, and make the call. The following Console.WriteLine("Beginning process...");
listing is an example of using an unmanaged callback: Console.ReadLine();
} }}

M. Romdhani, Novembre 2007 23 M. Romdhani, Novembre 2007 24

4
Chapitre 1- Introduction aux technologies J2EE

70-536 Chapitre 13 70-536 Chapitre 13

Exception Handling in Managed Code Limitations of Unmanaged Code

„ Exceptions in unmanaged code are markedly different from those „ Since the advent of .NET, there have been some shortcomings with using
thrown in managed code. unmanaged code. They are largely related to inherent differences between .NET
and previous development methodologies. Following is a list of those
„ In the earlier COM days, you could use the GetLastError function to get shortcomings:
the last error that was raised. This approach won't work in a managed „ Performance
application because the return value of GetLastError might not be the
correct one. „ Code that isn't managed by a runtime will typically have the ability to perform faster
than equivalent code that is managed. However, this benefit might not necessarily be
„ Why? Because the GetLastError method can be set by either a .NET realized. This is because of the overhead associated with marshaling information
Framework object or the common language runtime (CLR). between the unmanaged code and the .NET 2.0 runtime. It's important to remember
that unmanaged code can easily introduce issues such as memory leaks.
„ Because you can't use the GetLastError method, you need to do something „ Type safety
else. After all, the only thing more problematic than no exception handling is
bad exception handling. „ Unmanaged code is sometimes not type safe. This deficiency can have multiple
implications, including decreased readability and security issues.
„ Code security
„ The .NET Framework security model didn't exist previously. There's no way that code
written prior to this model can take advantage of it. Features such as declarative
security are not available in unmanaged code, which can mean your new .NET code
will be forced to accommodate this inconsistency.
„ Versioning
„ As is the case with security, versioning (which incidentally was a huge issue in prior
development environments) didn't exist in the form it does now. Therefore, side-by-side
execution might not be available when using unmanaged code.

M. Romdhani, Novembre 2007 25 M. Romdhani, Novembre 2007 26

70-536 Chapitre 13 70-536 Chapitre 13

Lesson 3 Summary Chapter 13 Summary

„ The .NET Framework provides a mechanism to call Windows API calls and „ Because of its dependence on the Windows registry, COM Interop limits the
unmanaged code through Platform Invoke. platforms an application can run on.

„ To use P/Invoke, you use the DllImport attribute and the name of the DLL you „ The TlbImp.exe tool is a command-line mechanism you can use to import a COM
are referencing. component.

„ You must use the private and static/shared attributes for P/Invoke calls. „ The default behavior when catching System.Exception objects now catches
both CLS-compliant and non-CLS-compliant exceptions.
„ To allow default positioning in a structure that's to be marshaled, you can use
the Layout.Sequential attribute. „ The ComVisible attribute can be set to apply to an entire assembly, an entire
class, or individual members.
„ To specify a value for the positioning in a structure, you can use the
Layout.Explicit attribute. „ To use P/Invoke, you use the DllImport attribute and the name of the DLL you
are referencing.
„ Error messages from unmanaged code behave differently from managed
exceptions. To trap them correctly, the Windows API can be used. „ You must use the private and static/shared attributes for P/Invoke calls.

M. Romdhani, Novembre 2007 27 M. Romdhani, Novembre 2007 28

You might also like