Declare Statements

 <  Day Day Up  >  

Although the .NET Framework provides an extensive library of classes, there are still times when it is necessary to call methods that exist outside the .NET Framework, such as Win32 APIs. Because methods external to the Framework do not have metadata associated with them, the Framework does not automatically understand how to call them. Instead, when a developer wants to call an external method, the method must first be described using a Declare statement. A Declare statement looks very much like a normal method declaration, with a few additions and no method body (since the implementation of the method is external to the Framework).

The first difference between a Declare statement and a regular method is that the method declaration is prefixed by the keyword Declare . Then, the name of the method in a Declare statement is followed by a library clause that tells the .NET Framework the name of the DLL where it can find the external method. The library clause begins with the word Lib followed by the name of the DLL (for example, "user32.dll" ). If the extension is left off of the name in the library clause, ".DLL" is assumed. The following example shows a Declare statement for the external method GetWindowsDirectoryA , defined in the DLL kernel32.dll .

 Module Test   Declare Function GetWindowsDirectoryA Lib "kernel32" _     (ByVal Buffer As String, ByVal Size As Integer) As Integer   Sub Main()     Dim s As String     Dim Count As Integer     s = Space(256)    ' Fill the string with spaces     Count = GetWindowsDirectoryA(s, s.Length)     If Count < 256 Then       Console.WriteLine(s)     End If   End Sub End Module 

NOTE

Many external methods call the Win32 API SetLastError to give an error code if the method fails. This value can be retrieved using the System.Runtime.InteropServices.Marshal.GetLastWin32-Error method.


Sometimes the name of an external method may conflict with an existing method or a type member in a program, or it may be desirable to use a different name than the one used by the external method. In that case, the library clause may be followed by an alias clause that gives the true external method name. If you add an alias, the name of the Declare statement is not required to match the external method name. In the following example, the GetWindowsDirectoryA external method is renamed to GetWindowsDirectory .

 Module Test   Declare Function GetWindowsDirectory Lib "kernel32" _     Alias "GetWindowsDirectoryA" _     (ByVal Buffer As String, ByVal Size As Integer) As Integer   Sub Main()     Dim s As String     Dim Count As Integer     s = Space(256)    ' Fill the string with spaces     Count = GetWindowsDirectory(s, s.Length)     If Count < 256 Then       Console.WriteLine(s)     End If   End Sub End Module 

Advanced

The alias clause can be a DLL ordinal value instead of a name if the external method is not exposed by name. The ordinal value is represented with a leading @ (i.e., Alias "@10" ).


Advanced

Declare statements are equivalent to placing the System.Interop-Services.DllImportAttribute on a Shared method. In general, Declare statements are preferred for readability, but there are a few advanced settings available in the attribute that cannot be set through a Declare statement.


Character Translation

Although the .NET Framework always uses the Unicode standard when encoding text characters , many external methods use the older ANSI standard. When calling an external method that takes a string argument, the Framework needs to know whether it needs to translate the string from Unicode to ANSI and back. If the wrong kind of translation is done, the external method will not be able to correctly understand the passed-in string, and unexpected things may happen.

If the external method being called uses the Unicode standard for strings, the Declare keyword should be followed by the Unicode keyword. If the external method being called uses the ANSI standard for strings, the Declare keyword should be followed by the Ansi keyword. For example:

 Module Test   Declare Unicode Function GetWindowsDirectoryW Lib "kernel32" _     (ByVal Buffer As String, ByVal Size As Integer) As Integer   Declare Ansi Function GetWindowsDirectoryA Lib "kernel32" _     (ByVal Buffer As String, ByVal Size As Integer) As Integer   Sub Main()     ...   End Sub End Module 

The GetWindowsDirectory method, like many Win32 APIs, actually has two different versions: an ANSI version and a Unicode version. In the preceding example, the GetWindowsDirectoryW declaration will call the Unicode version, while the GetWindowsDirectoryA declaration will call the ANSI version.

Although sometimes it is necessary to explicitly specify the character set to use when calling an external method, commonly the .NET Framework can infer the correct character set based on context. This is called using the "auto" character set and is the default if Unicode or Ansi isn't specified (although the Auto keyword can be supplied for clarity). When the auto character set is used, the Framework first decides whether the operating system it is running on is a native ANSI system (for example, Windows 98) or a native Unicode system (for example, Windows XP). If the operating system is ANSI, the Framework first looks for the method using the given Declare name or alias. If it is unable to find the method, it then appends an "A" to the end of the name and tries again. If the operating system is Unicode, the Framework first appends a "W" to the end of the name and looks for the method. If it is unable to find the API, it then looks up the name as it was originally given. This algorithm tends to choose the correct character set for the correct platform.

String Parameters

Advanced

The mechanics of passing String values to external methods is an advanced topic, and you don't need to understand it to use Declare statements.


External methods and the .NET Framework treat String parameters differently. In the Framework, when a string variable is passed to a parameter declared as ByVal String , the original variable is not modified if the string is changed. In the following example, the Main method will still print the string " Original ".

 Module Test   Sub Modify(ByVal s As String)     s = "New"   End Sub   Sub Main()     Dim s As String = "Original"     Modify(s)     Console.WriteLine(s)   End Sub End Module 

In external methods, however, when a string variable is passed to the equivalent of a ByVal String (usually, char * ), the string variable is still modifiable. As a result, many external methods that take a ByVal String modify the string with the expectation that the caller will see the result.

To make this work, when passing a string variable to an external method parameter that is declared ByVal String , Visual Basic .NET treats the argument in a special way. Instead of just passing the value of the string variable in by value, the string variable will be passed by reference using copy-in/copy-out. For example:

 Module Test   Declare Function GetWindowsDirectoryA Lib "kernel32" _     (ByVal Buffer As String, ByVal Size As Integer) As Integer   Sub Main()     Dim s As String = Space(1024)     Dim ReturnedChars As Integer     ReturnedChars = GetWindowsDirectoryA(s, 1024)   End Sub End Module 

In this example, even though the Buffer parameter is defined as being a value parameter, the GetWindowsDirectoryA method can still modify the string. When the modified string is passed out of the external method, the new string will be copied back into the local variable s .

To suppress this behavior in situations where changes to String parameters do not need to be reflected back, the System.InteropServices.MarshalAsAttribute can be applied to the parameter with an argument of System.InteropServices.UnmanagedType.LPStr or System.InteropServices.UnmanagedType.LPWStr , depending on whether the method is ANSI or Unicode, respectively.

 Imports System.Runtime.InteropServices Module Test   Declare Function GetWindowsDirectoryA Lib "kernel32" _     (<MarshalAs(UnmanagedType.LPStr)> ByVal Buffer As String, _     ByVal Size As Integer) As Integer   Sub Main()     Dim s As String = Space(1024)     Dim ReturnedChars As Integer     ReturnedChars = GetWindowsDirectoryA(s, 1024)   End Sub End Module 

In this example, the value of s will never change because all changes made to the string in GetWindowsDirectoryA are thrown away upon returning from the method.

It is worth noting that when an external method is repeatedly called in this way, it may be more efficient to use an instance of System.Text.StringBuilder to make the call. The StringBuilder class is designed specifically to be modifiable, so there is no need to do any copying on the way in or the way out of the method. For example:

 Imports System.Text Module Test   Declare Function GetWindowsDirectoryA Lib "kernel32" _     (ByVal Buffer As StringBuilder, ByVal Size As Integer) As Integer   Sub Main()     Dim s As StringBuilder = New StringBuilder(1024)     Dim ReturnedChars As Integer     ReturnedChars = GetWindowsDirectoryA(s, 1024)   End Sub End Module 

In this case, Buffer is directly modifiable by GetWindowsDirectoryA , so no string copying is done, making the call more efficient.

 <  Day Day Up  >  


The Visual Basic .NET Programming Language
The Visual Basic .NET Programming Language
ISBN: 0321169514
EAN: 2147483647
Year: 2004
Pages: 173
Authors: Paul Vick

Similar book on Amazon

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net