Using Platform Invoke
Access unmanaged code from a Windows service, a serviced component, a .NET Remoting object, and an XML Web service.
So far in this chapter, you've seen interoperability between managed code and unmanaged code by way of method calls to classes in COM libraries. There's a second way that the .NET Framework can interoperate with unmanaged code, though: by calling methods from unmanaged libraries. The Platform Invoke (often abbreviated as PInvoke) feature of .NET allows .NET code to call methods from unmanaged libraries such as the Windows API.
To call unmanaged methods from your .NET code, you need to provide the declarations of the method in your .NET code. Because the methods are implemented externally in unmanaged libraries, you should declare the methods in the .NET code with extern and static keywords.
Along with declaring the method, you should also apply a DllImport attribute of the System.Runtime.InteropServices namespace to the methods. The DllImport attribute tells the CLR where to find the implementation of the extern method by specifying the name of the unmanaged library. After the method is declared, you can use it in Visual C# .NET just like you use any other method. In addition to the name of the library, the DllImport attribute also accepts other parameters (see Figure).
Figure Parameters for the DllImport Attribute|
CallingConvention | Defines the calling convention to use. The values are specified by the CallingConvention enumeration—Cdecl, FastCall, StdCall (default value), ThisCall, and Winapi. | CharSet | Specifies the character set to use. By default it uses CharSet.Ansi. The other possible values are CharSet.Auto, CharSet.Unicode, and CharSet.None (which is obsolete and behaves as CharSet.Ansi). | EntryPoint | Represents the name of the entry point in the DLL. If the EntryPoint field is not specified, the name of the method is used as the entry point. If the EntryPoint field is passed, then you can provide a custom name for the method. | ExactSpelling | Specifies whether the name of the entry point should exactly match the name of the method in the unmanaged DLL. By default the value is false. | PreserveSig | Indicates whether the method signature should be preserved or can be changed. By default the value is true. | SetLastError | Specifies whether the last error of the Win32 method should be preserved. By default the value is false. |
Step-by-Step 8.4 shows you how to call the GetComputerName() method from the kernel32.dll library by using the DllImport attribute.
|
8.4 Using Platform Invoke with the Windows API
Add a new Web service named StepByStep8_4.asmx to your Visual C# .NET application. Switch to the code view. Enter the following using directives:
using System.Text;
using System.Runtime.InteropServices;
Add the following lines of code in the class definition, which indicates that the GetComputerName() method is implemented in kernel32.dll:
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
public static extern int GetComputerName(
StringBuilder buffer, ref uint size);
NOTE
Reference Parameters
In Visual C# .NET, the ref and out keywords are used to pass the reference parameters to Windows 32 API methods. Add the following Web method definition in the StepByStep8_4.asmx file:
[WebMethod]
public string WebServerName()
{
StringBuilder sbBuf = new StringBuilder(128);
UInt32 intLen = (uint) sbBuf.Capacity;
Int32 intRet=0;
// Call the Win API method
intRet = GetComputerName(sbBuf, ref intLen);
return sbBuf.ToString();
}
Set the Web service as the start page for the project. Run the project. You should see that a browser is launched showing the test page. Click on the WebServerName method link and click the Invoke button in the method test page. The code displays the name of the Web server where the Web service is run, as shown in Figure.

|
In Step-by-Step 8.6, note the use of the CharSet.Auto parameter in the DllImport attribute of the GetComputerName() method declaration. You may know that many Windows API calls come in two versions, depending on the character set that you're using. For example, GetComputerName() really exists as GetComputerNameA() (for ANSI characters) and GetComputerNameW() (for Unicode characters). The Auto parameter instructs the .NET Framework to use the appropriate version of the API call for the platform where the code is running.
EXAM TIP
StringBuilder Necessary
In Visual C# .NET, you should use the StringBuilder object for a Windows API call that expects a string buffer to be modified by the method.
Platform Invoke can also handle API calls that require structures as parameters. For example, a call to the GetSystemTime() method fills in a structure that consists of eight members that together indicate the system time. Step-by-Step 8.5 shows how to represent this structure in .NET code and pass the structure to the GetSystemTime() method to fill it with the Web server's time.
|
8.5 Using Platform Invoke with a struct Parameter
Add a new Web service named StepByStep8_5.asmx to your Visual C# .NET application. Switch to the code view. Enter the following using directives:
using System.Runtime.InteropServices;
Add the following lines of code in the class definition:
[StructLayout(LayoutKind.Sequential)]
public struct SystemTime
{
public ushort year;
public ushort month;
public ushort dayOfWeek;
public ushort day;
public ushort hour;
public ushort minute;
public ushort second;
public ushort millisecond;
}
[DllImport("Kernel32.dll")]
public static extern void GetSystemTime(
out SystemTime time);
Add the following Web method definition to the Web service:
[WebMethod]
public SystemTime WebServerTime()
{
SystemTime time;
// call the Win32 API method
GetSystemTime(out time);
return time;
}
Set the Web service as the start page for the project. Run the project. You should see that a browser is launched showing the test page. Click on the WebServerTime method link and click the Invoke button in the method test page. The method returns the time of the Web server in a structure as defined by the Web service, as shown in Figure.

|
The tricky part of the code in Step-by-Step 8.5 lies in the declaration of the structure. In this case, the StructLayout attribute tells the Visual C# .NET compiler that the location of the individual fields is sequential within the structure. By using the StructLayout attribute, you can ensure that the .NET Framework constructs the same structure that the API method is expecting to receive.
Many API calls require a Rect structure, which consists of four members that are filled in with the coordinates of a rectangle. In Visual C# .NET, you can declare a structure with explicit byte offsets for each member, which lets you define any structure that the Windows API requires:
[StructLayout(LayoutKind.Explicit)]
public struct Rect
{
[FieldOffset(0)] public Int32 left;
[FieldOffset(4)] public Int32 top;
[FieldOffset(8)] public Int32 right;
[FieldOffset(12)] public Int32 bottom;
}
In this case, the StructLayout attribute tells the Visual C# .NET compiler that you'll explicitly specify the location of the individual fields within the structure. The FieldOffset attribute specifies the starting byte of each field within the structure.
You can use the .NET Framework Platform Invoke facility to call methods from Windows libraries, including the Windows API. You should use the StringBuilder object for a Windows API call that expects a string buffer to be modified by the method.
|
|