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: through functional calls to unmanaged libraries. The platform invoke (often abbreviated as PInvoke) feature of .NET enables .NET code to call functions from unmanaged libraries such as the Windows API.
To call unmanaged functions from your .NET code, you need to provide the declarations of the function in your .NET code. There are two ways to do this. First, you can use a Declare statement, just as you could in previous versions of Visual Basic:
Declare Auto Function GetComputerName Lib "kernel32" (_ ByVal lpBuffer As String, _ ByRef nSize As Integer) As Integer
The Declare statement specifies that a particular function is contained in the named library. You must declare the parameters and return type of the function to be compatable with those in the library. But Declare doesn't offer you much control over the way that .NET handles an API call. For finer-grained control, you'll want to use the DllImport attribute instead.
Along with declaring the method, you can 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 method by specifying the name of the unmanaged library. After the method is declared, you can use it in Visual Basic .NET just as you use any other method. Functions imported using DllImport must always be declared as Shared . In addition to the name of the library, the DllImport attribute also accepts other parameters (see Table 8.2).
Parameter | Description |
---|---|
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 EntryPoint field is not specified, the name of the method is used as the entry point. If EntryPoint field is passed, 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 function 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 | whether the last error of the Win32 function should be preserved. By default the value is false . |
Step by Step 8.4 shows how to call GetComputerName function from kernel32.dll library by using DllImport attribute.
STEP BY STEP8.4 Using Platform Invoke with the Windows API
|
EXAM TIP
StringBuilder Necessary In Visual Basic .NET, you should use StringBuilder object for a Windows API call that expects a string buffer to be modified by the function.
In Step by Step 8.4, note the use of the CharSet.Auto parameter in the DllImport attribute of the GetComputerName() method declaration.
You might 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 modifier instructs the .NET Framework to use the appropriate version of the API call for the platform where the code is running.
Platform invoke can also handle API calls that require structures as parameters. For example, a call to the GetSystemTime API 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 how to pass the structure to the GetSystemTime method in order to fill it with the Web server's time.
STEP BY STEP8.5 Using Platform Invoke with a Structure Parameter
|
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 Basic .NET compiler that the location of the individual fields is sequential within the class. By using the StructLayout attribute, you can ensure that the .NET Framework constructs the same structure that the API function 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 Basic .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 Structure Rect <FieldOffset(0)> Public left As Integer <FieldOffset(4)> Public top As Integer <FieldOffset(8)> Public right As Integer <FieldOffset(12)> Public bottom As Integer End Structure
In this case, the StructLayout attribute tells the Visual Basic .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.
REVIEW BREAK
|
Top |