Platform Invoke C# Help

Not all the features of Windows API calls are available from the .NET Framework.

This is not only true for old Windows API calls but also for very new features from Windows Vista or Windows Server 2008, Maybe you’ve written some DLLs that export unmanaged methods, and you would like to use them from C# as well.

You can read about some Windows Vista – and Windows Server 2008-specific features in Appendix C, “Windows Vista and Windows Server 2008.”

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 CLRloads 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\kerne132.dll I more

lists all exported functions from the DLL kerne132 .dll.

In the example, you use the CreateHardLink() Windows API function to create a hard link to an existing file.

With this API tall, 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.5, so platform invoke must be used.

To caUa 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 unrnanaged method must have mapped types with
managed code.
The Windows API call CreateHardLink () has this definition in C++:

BOOLCreateHardLink(
LPCTSTRlpFileName,
LPCTSTRlpExistingFileName,
LPSECURITY_ATTRIBUTlpESSecurityAttributes);

Now, this definition must be mapped to .NET data types. The return type is a BOOLwith unrnanaged code; this simply maps to the bool data type.

LPCTSTRdefines a long pointer to a const string.

The Windows API uses the Hungarian naming convention for the data type. LPis a long pointer,-c a const, and STRis a null-terminated string. The Tmarks 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~NETtype String.

LPSECURITY_ATTRIBUTE which is a long pointer to a struct of type SECURITY_ATTRIBUTEBSe.cause you can pass NULLto this argument, mapping this type to IntPtr is okay.

The C# declaration of this method must be marked with the extern modifier, because there’s no implementation of this method within the C# code. Instead, the implementation of this-method is found
in the DLLkerne132.dIl, which is referenced with the attribute [DIlImportj; Because the return type of the .NET declaration CreateHardLink () is of type bool, and the native method CreateHardLink () returns a BOOL,some additional clarification is useful. Because there are different Boolean data types with C++, for example the native bool and the Windows-defined BOOL,which have different values, the attribute [MarshalAs) specifies to what native type the :NET type bool should map.

[DllImport (“kerne132 .dll”, ‘SetLastError= “true”,
EntryPoint=”CreateHardLink”, CharSet=CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool))
public static extern bool CreateHardLink(string newFileName,
“..string existingFilename, IntPtr securityAttributes);-

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

DllImport Property or Field                            Description
EntryPoint   You can give the C# declaration of the function a different name
than it has with the unman aged 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 unrnanaged 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 Windows API usually uses the StdCaIl calling convention
on the Windows operating system, and it uses the Cdecl calling
convention on Windows CEoSetting the value to CaIlingConvention.

Winapi works for the Windows API-both in the Windows and the Windows CE environments.

Dlllmport Property                                       Field Description
Charset             String parameters can be either ANSI or Unicode.

With the CharSet setting, you can define how strings are managed.

Possible values that are definedwith the CharSet enumeration are Ansi, Unicode, and Auto char see Auto uses Unicode on the Windows NT platform, and ANSI on Windows 98 and Windows ME.

SetLastError

If the unmanaged function sets in error by using the Windows API SetLastError you can set the SetLastError field to true.

This way, you can read the error number afterward by using
Marshal.GetLastWin32Error().

SetLastwin32error().

To make the CreateHardLink () method easier to use from a .NETenvironment, you should follow these guidelines:

  • Create an internal class named NativeMethods that wraps the platform invoke method calls
  • Create a public class to offer the native method functionality to .NET applications
  • Use security attributes to mark the required security

In the sample code, the public method CreateHardLink () in the class FileUtili ty is the method that can be used by .NET applications.

This method has the file name arguments reversed compared to the native Windows API method CreateHardLink ( ).

The first argument is the name of the existing file, and the second argument is the name of the new file. 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 [Dlllmport) 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.

The public method CreateHardLink () has the FileIOPermission attribute applied to check if the caller has the necessary permission.

You can read more information about .NET security

using System;
using System.Runtime.lnteropServices;
using System.CoroponentModel;
using System.IO;
namespace Wrox.ProCSharp.lnterop
{
internal static class NativeMethods
{
[Dlllmport{kernal132.dll‘, SetLastError=true,
EntryPoint= °CreateHardLink” , CharSet=CharSet.Unicode)]
(return: MarshalAs(UnmanagedType.Bool)]

private static extern bool CreateHardLink(
string newFileName. string existingFileName.
IntPtr securityAttributes);
internal static void CreateHardLink (string oldFileName.
string newFileName)
if (CreateHardLink(newFileName. oldFileName. IntPtr.Zero»
{
Win32Exception ex = new Win32Exception(
Marshal.GetLastWin32Error(»;
throw new IOException(ex.Message. ex);
public static class FileUtility
{
[FileIOPermission (SecurityAction. LinkDemand. Unrestricted=true»)
public static void CreateHardLink(string oldFileName.
. string newFileName)
NativeMethods.CreateHardLink(oldFileName. newFileName);

                }

         }

   }

This class can now be used to create hard links very easily.

If the file file 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()
{
try
{
FileUtility.CreateHardLink(“filel.txt”. “file2.txt”);
)
catch (IOException ex)
{
Console.WriteLine(ex.Message);

            }

}

With native method calls, often you have to use Window handles.

A Windows handle is a 32-bit value where depending on the handle types some values are not allowed.

With .NET 1.0 for handles, usually the IntPtr structure was used because you can set every possible 32-bit value with this structure.

However, with some handle types, this led to security problems and possible threading race conditions and leaked handles with the finalization phase. That’s why .NET 2.0 introduced the SafeHandle class.

The class SafeHandle is an abstract base class for every Windows handle.

Derived classes inside the Microsoft. Win32.

SafeHandles namespace are SafeHandleZeroOrMinusOneIslnvalid and SafeHandleMinusOneIslnvalid. As the name tells, these classes do not accept invalid 0 or -1values.
Further derived handle types are SafeFileHandle, SafeWaitHandle, SafeNCryptHandle, and SafefileHandle that can be used by the specific Wmdows API calls.

For example, to map the Windows API CreateFile (),you can use this declaration to return a SafeFileHandle.

Of course, usually you could use the .NET classes File and Filelnfo instead.

Summary

In this chapter, you have seen how the different generations of COM and .NET appJications can interact.

Instead of rewriting applications and components, a COM component can be used from a .NET application just like a .NET class. The tool that makes this possible is tlbimp, which creates a runtime

• callable wrapper (RCW) that hides the COM object behind a .NET facade,
Likewise, tlbexp creates a type library from a .NET component that is used by the COM callablev wrapper (CCW).

The CCW hides the .NET component behind a COM facade, Using .NET classes as COM components makes it necessary to use some attributes from the namespace System.

Runtime InteropServices to define specific COM characteristics that are needed by the COM client, With platform invoke, you’ve seen how native methods can be invoked using C#. Platform invoke requires redefining the native method with C# and .NET data types.

After defining the mapping, you can invoke the native method as if it would be a C# method. Another option for doing interop would be to use the technology It Just Works (IIW) with C++/CLI. You can read information about C++/Cll in Appendix B.

The next part of this book is all about data. The next chapter gives information on how to access the file system, followed by chapters on how to read and write from the database and manipulate XML.

Posted on October 28, 2015 in Interoperability

Share the Story

Back to Top