Managed/Unmanaged Code Interoperability

Filed Under (Blog) by fakhrul on 23-12-2009

I just read the article here regarding to the managed and unmanaged code interoperability. Below is the summary.

 

Call unmanaged APIs from managed code

This can be done for both flat APIs (eg. kernel32.dll and user32.dll) and COM APIs (eg. Microsoft® Word, Excel).

Expose managed APIs to unmanaged code

Examples of doing this include creating an add-in for a COM-based application like Windows Media® Player or embedding a managed Windows Forms control on an MFC form.

 

1. Guidelines

 

Call unmanaged APIs from managed code

Calling Unmanaged Flat APIs

There are two mechanisms for calling into unmanaged flat APIs from managed code: through Platform Invoke (available in all managed languages) or through C++ interop (available in C++).

1. For calling just a few unmanaged methods or for calling simple flat APIs, the suggestion is to use platform invoke.

2. For wrapping complex unmanaged flat APIs, or for wrapping unmanaged flat APIs that are changing while managed code is under development, the suggestion is to use C++ interop.

 

Calling COM APIs

There are two ways to call COM components from managed code: through COM interop (available in all managed languages) or through C++ interop (available in C++).

1. For calling OLE Automation-compatible COM components, the suggestion is to use COM interop.

2. For calling COM components based on Interface Definition Language (IDL), the suggestion is to use C++ interop.

 

Exposing Managed APIs to Unmanaged Code

Directly Accessing a Managed API

If an unmanaged client is written in C++, it can be compiled with the Visual Studio .NET C++ compiler as a "mixed mode image." After this is done, the unmanaged client can directly access any managed API. However, some coding rules do apply to accessing managed objects from unmanaged code; check the C++ documentation for more details.

 

Exposing a Managed API as a COM API

Every public managed class can be exposed to unmanaged clients through COM interop.

 

Exposing a Managed API as a Flat API

Expose your managed API as a flat API only if absolutely necessary. If you have no choice, be sure to check the C++ documentation and be fully aware of all the limitations.

 

2. Performance

 

With every transition from managed code to unmanaged code (and vice versa), there is some performance overhead. The amount of overhead depends on the types of parameters used. The CLR interop layer uses three levels of interop call optimization based on transition type and parameter types: just-in-time (JIT) inlining, compiled assembly stubs, and interpreted marshaling stubs (in order of fastest to slowest type of call).

Approximate overhead for a platform invoke call: 10 machine instructions (on an x86 processor)

Approximate overhead for a COM interop call: 50 machine instructions (on an x86 processor)

 

1. If you control the interface between managed and unmanaged code, make it "chunky" rather than "chatty," to reduce the total number of transitions made.

     Chatty interfaces are interfaces that make a lot of transitions without performing any significant work.

     Chunky interfaces are interfaces that make only a few transitions, and the amount of work done on the other side of the boundary is significant.

 

2.  Avoid Unicode/ANSI conversions if possible.

 

3. For high-performance scenarios, declaring parameters and fields as IntPtr can boost performance, albeit at the expense of ease-of-use and maintainability.

 

4. Sometimes it is faster to do manual marshaling using methods available on the Marshal class, rather than rely on default interop marshaling. For example, if large arrays of strings must be passed across an interop boundary, but only a few elements will be needed, declaring the array as an IntPtr and accessing only those few elements manually will be much faster.

 

5. Use InAttribute and OutAttribute wisely to reduce unnecessary marshaling.

 

6. The interop marshaler uses default rules when deciding if a certain parameter needs to be marshaled in before the call and marshaled out after the call. These rules are based on the level of indirection and the parameter type. Some of these operations might not be necessary depending on the method’s semantics.

 

7. Use SetLastError=false on platform invoke signatures only if you will call Marshal.GetLastWin32Error afterwards.

 

8. Setting SetLastError=true on platform invoke signatures requires additional work from the interop layer to preserve the last error code. Use this feature only when you rely on this information and will use it after you make the call.

 

9. If, and only if, unmanaged calls are exposed in a non-exploitable fashion, use SuppressUnmanagedCodeSecurityAttribute to reduce the number of security checks.

 

References:

http://msdn.microsoft.com/en-us/library/ms973872.aspx

http://msdn.microsoft.com/en-us/library/aa720203%28VS.71%29.aspx

http://msdn.microsoft.com/en-us/library/bb985764.aspx

http://www.codeproject.com/KB/COM/comintro.aspx

Leave a Reply