Platform Invoke


Not all the features of Win32 API calls are available from the .NET Framework. This is not only true for old Win32 API calls but also for very new features from Windows Vista. Maybe you’ve written some DLLs that export unmanaged methods, and you would like to use them from C#.

Tip 

You can read about some Windows Vista–specific features in Chapter 44, “Windows Vista.”

To reuse an unmanaged library that doesn’t contain COM objects but just exported functions, platform invoke can be used. With platform invoke services, the CLR loads the DLL that includes the function that should be called and marshals the parameters.

To use the unmanaged function, first you have to find out the name of the function as it is exported. You can do this by using the dumpbin tool with the /exports option.

For example, the command

 dumpbin /exports c:\windows\system32\kernel32.dll | more

lists all exported functions from the DLL kernel32.dll. In the example, you use the CreateHardLink() Win32 API function to create a hard link to an existing file. With this API call, you can have several file names that reference the same file as long as the file names are on just one hard disk. This API call is not available from .NET Framework 3.0, so platform invoke must be used.

To call a native function, you have to define a C# external method with the same number of arguments, and the argument types that are defined with the unmanaged method must have mapped types with managed code.

The Win32 API call CreateHardLink() has this definition in C++:

  BOOL CreateHardLink(    LPCTSTR lpFileName,    LPCTSTR lpExistingFileName,    LPSECURITY_ATTRIBUTES lpSecurityAttributes); 

Now, this definition must be mapped to .NET data types. The return type is a BOOL with unmanaged code; this simply maps to the bool data type. LPCTSTR defines a long pointer to a const string. The Win32 API uses the Hungarian naming convention for the data type. LP is a long pointer, C a const, and STR is a null-terminated string. The T marks the type as a generic type, and the type is either resolved to LPCSTR (an ANSI string) or LPWSTR (a wide Unicode string), depending on compiler settings. C strings map to the .NET type String. LPSECURITY_ATTRIBUTES, which is a long pointer to a struct of type SECURITY_ATTRIBUTES. Because you can pass NULL to this argument, mapping this type to IntPtr is okay. The C# declaration of this method must be marked with the extern modifier, as there’s no implementation of this method within the C# code. Instead, the implementation of this method is found in the DLL kernel32.dll, which is referenced with the attribute [DllImport].

  [DllImport("kernel32.dll", SetLastError="true",       EntryPoint="CreateHardLink", CharSet=CharSet.Auto)] public static extern bool CreateHardLink(string fileName,       string existingFilename, IntPtr securityAttributes); 

The settings that you can specify with the attribute [DllImport] are listed in the following table.

Open table as spreadsheet

DllImport Property or Field

Description

EntryPoint

You can give the C# declaration of the function a different name than it has with the unmanaged library. The name of the method in the unmanaged library is defined in the field EntryPoint.

CallingConvention

Depending on the compiler or compiler settings that were used to compile the unmanaged function, different calling conventions can be used. The calling convention defines how the parameters are dealt with and where to put them on the stack. You can define the calling convention by setting an enumerable value.

The Win32 API usually uses the StdCall calling convention on the Windows operating system, and it uses the Cdecl calling convention on Windows CE. Setting the value to CallingConvention.Winapi works for the Win32 API both in the Windows and the Windows CE environments.

CharSet

String parameters can be either ANSI or Unicode. With the CharSet setting, you can define how strings are managed. Possible values that are defined with the CharSet enumeration are Ansi, Unicode, and Auto. CharSet.Auto uses Unicode on the Windows NT platform, and ANSI on Windows 98 and Windows ME.

SetLastError

If the unmanaged function sets an error by using the Win32 API SetLastError, you can set the SetLastError field to true. This way, you can read the error number afterward by using Marshal.GetLastWin32Error().

To make the CreateHardLink() method easier to use from a .NET environment, it is best to use the external method as a private method inside a class, and offer another method that has a better .NET look and feel. This is done with the class FileUtility. The public method CreateHardLink() has the file name arguments reversed. The first argument is the name of the existing file, and the second argument is the name of the new file name. This is similar to other classes in the Framework; for example, File.Copy(). Because the third argument to pass the security attributes for the new file name is not used with this implementation, the public method has just two parameters. The return type is changed as well. Instead of returning an error by returning the value false, an exception is thrown. In case of an error, the unmanaged method CreateHardLink() sets the error number with the unmanaged API SetLastError(). To read this value from .NET, the [Dllimport] field SetLastError is set to true. Within the managed method CreateHardLink(), the error number is read by calling Marshal .GetLastWin32Error(). To create an error message from this number, the Win32Exception class from the namespace System.ComponentModel is used. This class accepts an error number with the constructor, and returns a localized error message. In case of an error, an exception of type IOException is thrown, which has an inner exception of type Win32Exception:

  using System; using System.Runtime.InteropServices; using System.ComponentModel; using System.IO; namespace Wrox.ProCSharp.Interop {    public static class FileUtility    {       [DllImport("kernel32.dll", SetLastError=true,             EntryPoint="CreateHardLink", CharSet=CharSet.Auto)]       private static extern bool CreateHardLink(string newFilename,             string existingFilename, IntPtr securityAttributes);       public static void CreateHardLink(string existingFilename,             string newFilename)       {          if (!CreateHardLink(newfilename, existingFilename,                IntPtr.Zero))          {             Win32Exception ex = new Win32Exception(Marshal.GetLastWin32Error());             throw new IOException(ex.Message, ex);          }       }    } } 

This class can now be used to create hard links very easily. If the file file1.txt does not exist, you will get an exception with the message “The system cannot find the file specified.” If the file exists, you get a new file name referencing the original file. You can easily verify this by changing text in one file; it will show up in the other file as well.

  static void Main() {    FileUtility.CreateHardLink("file1.txt", "file2.txt"); } 




Professional C# 2005 with .NET 3.0
Professional C# 2005 with .NET 3.0
ISBN: 470124725
EAN: N/A
Year: 2007
Pages: 427

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