Lesson 2: Using Unmanaged Code
The purpose of this lesson is to highlight the backwards-compatibility features of the .NET Framework, it's ability to create static entry points into Win32 COM interfaces. This lesson is quite forthcoming about the limitations of these features.
- "The process of executing native code from within a .NET assembly is called platform invoke, or pinvoke for short."
The "native code" is usually in the form of a COM DLL (of "DLL Hell" infamy). These are called by declaring the System.Runtime.InteropServices.DllImportAttribute as in the lesson's code sample:
[DllImport("KERNEL32.DLL", ...)]...
When "invoking" the Win32 platform, data is exchanged between the .NET Framework and the Win32 API in the form of passing parameters and reading results. This data exchange is often in the form of custom types defined by data structures.
- "Structures in .NET are defined in much the same way that they are for the Win32 API. By default, .NET structures are arranged sequentially in memory in the order in which they are defined... You can also explicitly define how structures are ordered in memory using the StructLayout attribute."
This is the System.Runtime.InteropServices.StructLayoutAttribute with the form:
[StructLayout(LayoutKind.Sequential)]...
This lesson may suggest that the reason why struct (in C#) and Structure (in VB.NET) exist in .NET languages is to facilitate structured data exchange between the .NET Framework and the Win32 API. It definitely suggests in the code samples that Class definitions (with all public fields) can also be used instead of struct types.
- "Declaring a layout is required when you're passing objects to unmanaged code, because objects might be moved around in memory after they are created."
Discovering exactly what data needs to be structured and passed into the Win32 API is no trivial task. For more information, please see:
"Marshaling Data with Platform Invoke"
http://msdn.microsoft.com/library/en-us/cpguide/
html/cpconmarshalingdatawithplatforminvoke.asp
"P/Invoke (Platform Invoke) links and resources"
http://dotnetjunkies.com/WebLog/imranko/archive/2004/10/24/29558.aspx
"Platform Invoke Cheat Sheet"
http://www.gotdotnet.com/team/clr/bcl/TechArticles/
TechArticles/PInvokeHelp/FAQ.aspx
- "Unmanaged procedures typically return a value that indicates whether an exception occurred... Nonzero return values usually indicate success, and a zero return value indicates that an exception occurred."
The lesson's code sample used to illustrate the use of Marshal.GetLastWin32Error() does not recognize the existence of the System.ComponentModel.Win32Exception type. With this recognition the code can refactored as:
// MoveTo returns False if there is an error.
Win32Exception ex = new Win32Exception(Marshal.GetLastWin32Error());
string msg = ex.Message;
instead of this:
// MoveTo returns False if there is an error.
string msg ;
// Set a message depending on error code returned.
switch (Marshal.GetLastWin32Error())
{
case 2:
msg = "File not found.";
break;
case 3:
msg = "Path not found.";
break;
case 5:
msg = "Access denied.";
break;
case 15:
msg = "Drive not found.";
break;
default:
msg = "Unlisted error.";
break;
}
- "Limitations of Unmanaged Code... Although native code can perform some operations more quickly than equivalent code managed by the CLR, these benefits might be offset by the time it takes to marshal the data to pass between the unmanaged procedure and the .NET assembly."
This performance limitation seems to be most surprising limitation of p-invoke. The others, "type safety," "versioning" and "code security" are to be expected.