15.1 Call a Function in an Unmanaged DLL


Problem

You need to call a C function in a DLL. This function might be a part of the Win32 API or your own legacy code.

Solution

Declare a method in your C# code that you will use to access the unmanaged function. Declare this method as both extern and static , and apply the attribute System.Runtime.InteropServices.DllImportAttribute to specify the DLL file and the name of the unmanaged function.

Discussion

To use a C function from an external library, all you need to do is declare it appropriately. The CLR automatically handles the rest, including loading the DLL into memory when the function is called and marshalling the parameters from .NET data types to C data types. The .NET service that supports this cross- platform execution is named PInvoke (Platform Invoke), and the process is usually seamless. Occasionally, you'll need to do a little more work, such as when you need to support in-memory structures, callbacks, or mutable strings.

PInvoke is often used to access functionality in the Win32 API, particularly if it includes features that are not present in the set of managed classes that make up the .NET Framework. Throughout this book are examples that use PInvoke in this way. There are three core libraries that make up the Win32 API:

  • Kernel32.dll includes operating specific-functionality such as process loading, context switching, and file and memory I/O.

  • User32.dll includes functionality for manipulating windows , menus , dialog boxes, icons, and so on.

  • GDI32.dll includes graphical capabilities for drawing directly on windows, menus and control surfaces, as well as printing.

As an example, consider the Win32 API functions used for writing and reading INI files, such as GetPrivateProfileString and WritePrivateProfileString in Kernel32.dll. The .NET Framework doesn't include any classes that wrap this functionality. However, you can import these functions using the attribute DllImportAttribute , like this:

 [DllImport("kernel32.DLL", EntryPoint="WritePrivateProfileString")] private static extern bool WritePrivateProfileString(string lpAppName,   string lpKeyName, string lpString, string lpFileName); 

The arguments specified in the signature of the WritePrivateProfileString method must match the DLL method, or a runtime error will occur when you attempt to invoke it. Remember that you don't define any method body because the declaration refers to a method in the DLL. The EntryPoint portion of the attribute DllImportAttribute is optional in this example. There's no need to specify the EntryPoint when the declared function name matches the function name in the external library.

Here's an example of a custom IniFileWrapper class that declares these methods privately and then adds public methods that call them based on the current designated file:

 using System; using System.Text; using System.Runtime.InteropServices; using System.Windows.Forms; public class IniFileWrapper {     private string filename;     public string Filename {         get {return filename;}     }     public IniFileWrapper(string filename) {         this.filename = filename;     }     [DllImport("kernel32.dll", EntryPoint="GetPrivateProfileString")]     private static extern int GetPrivateProfileString(string lpAppName,       string lpKeyName, string lpDefault, StringBuilder lpReturnedString,       int nSize, string lpFileName);     [DllImport("kernel32.dll", EntryPoint="WritePrivateProfileString")]     private static extern bool WritePrivateProfileString(string lpAppName,        string lpKeyName, string lpString, string lpFileName);     public string GetIniValue(string section, string key) {         StringBuilder buffer = new StringBuilder();         string sDefault = "";         if (GetPrivateProfileString(section, key, sDefault,           buffer, buffer.Capacity, filename) != 0) {             return buffer.ToString();         } else {             return null;         }     }     public bool WriteIniValue(string section, string key, string value) {         return WritePrivateProfileString(section, key, value, filename);     } } 

There are several other Win32 API functions for getting INI file information, including methods that retrieve all the sections in an INI file. These aren't used in this simple example.

Tip  

The GetPrivateProfileString method is declared with one StringBuilder parameter ( lpReturnedString ). This is because this string must be mutable ”when the call completes, it will contain the returned INI file information. Whenever you need a mutable string, you must substitute StringBuilder in place of the String class. Often, you will need to create the StringBuilder with a character buffer of a set size and then pass the size of the buffer to the function as another parameter. You can specify the number of characters in the StringBuilder constructor. (See recipe 2.1 for more information on using the StringBuilder class.)

You can test this program quite easily. First create the INI file shown in this code.

 [SampleSection] Key1=Value1 Key2=Value2 Key3=Value3 

Now, execute the following code. This code demonstrates a console application that reads and writes an INI value.

 public class IniTest {     private static void Main() {         IniFileWrapper ini = new IniFileWrapper(           Application.StartupPath + "\initest.ini");         string val = ini.GetIniValue("SampleSection", "Key1");         Console.WriteLine("Value of Key1 in [SampleSection] is: " + val);         ini.WriteIniValue("SampleSection", "Key1", "New Value");         val = ini.GetIniValue("SampleSection", "Key1");         Console.WriteLine("Value of Key1 in [SampleSection] is now: " + val);         ini.WriteIniValue("SampleSection", "Key1", "Value1");         Console.ReadLine();     } } 



C# Programmer[ap]s Cookbook
C# Programmer[ap]s Cookbook
ISBN: 735619301
EAN: N/A
Year: 2006
Pages: 266

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