How to Use an Unmanaged DLL from C

How to Use an Unmanaged DLL from C#

C# code does not have the luxury of IJW. The only way to access code in an unmanaged DLL from C# is using PInvoke . To demonstrate how it's done, create a C# console application called CSUseLegacyDLL .

Although you could set up the custom marshaling in C#, it would be quite a lot of work. Instead, the C# example will pass a SYSTEMTIME to Log() , and use the MakeSystemTimeFromDateTime() helper method. Listing 7.2 shows the code for the C# console application. There are several very significant differences from the equivalent C++ code in the "Using PInvoke to Control Marshaling" section. These include

  • In C++, the default visibility for elements of a structure is public . In C# it is not, and you must specify public for each element as you declare it.

  • The syntax for declaring a struct is simpler and does not require (or allow) a typedef.

  • Global functions are not allowed; all functions must be members of a class, so MakeSystemTimeFromDateTime() is in the Class1 class. Make it a static method so it can be called without creating an instance of Class1 .

  • C++ calls accessor methods of the DateTime class such as get_Year() directly. C# uses property syntax, for example dt.Year .

  • C# requires a cast when putting int values into short variables , because a C# int is always longer than a short .

  • The imported methods must be static extern members of a class; add them to Class1 .

  • The DllImport attribute in C++ specifies the DLL name without the .dll extension; in C# you must provide the extension.

  • The methods as declared in C++ took pointers: a char* and a SYSTEMTIME* . In C#, pointers do not exist. When working with a heap-allocated garbage-collected object such as a String , what C# considers a reference is equivalent to a C++ pointer. When working with a stack-allocated structure such as SYSTEMTIME , pass the structure by reference (use the ref keyword both in the definition of the function and in the calling code) to produce something equivalent to a C++ pointer.

  • C# uses . to separate a class name, such as Console , from the name of a static method, such as WriteLine() .

  • C# does not use the S macro to convert quoted strings to String objects; all quoted strings are C# String objects.

  • The C# Main() function does not return a value.

  • The sample code here uses a different message string to help you spot messages in log.txt from variations on this application; it's "Testing with a C# string" in this version.

Listing 7.2 Class1.cs CSUseLegacyDLL
 using System; using  System.Runtime.InteropServices; namespace CSUseLegacyDLL {     struct SYSTEMTIME     {         public short wYear;         public short wMonth;         public short wDayOfWeek;         public short wDay;         public short wHour;         public short wMinute;         public short wSecond;         public short wMilliseconds;     }     /// <summary>     /// Summary description for Class1.     /// </summary>     class Class1     {         static SYSTEMTIME MakeSystemTimeFromDateTime(DateTime dt)         {             SYSTEMTIME st;             st.wYear = (short) dt.Year;             st.wMonth = (short) dt.Month;             st.wDayOfWeek = (short) dt.DayOfWeek;             st.wDay = (short) dt.Day;             st.wHour = (short) dt.Hour;             st.wMinute = (short) dt.Minute;             st.wSecond = (short) dt.Second;             st.wMilliseconds = (short) dt.Millisecond;             return st;         }         [DllImport("legacy.dll", CharSet=CharSet.Ansi)]         static extern bool Log(String message, ref SYSTEMTIME time);         [DllImport("legacy.dll")]         static extern double Add(double num1, double num2);         /// <summary>         /// The main entry point for the application.         /// </summary>         [STAThread]         static void Main(string[] args)         {             System.Console.Write("1 + 2 is ");             System.Console.WriteLine( Add(1,2));             String s = "Testing with a C# string" ;             SYSTEMTIME st = MakeSystemTimeFromDateTime(DateTime.Now);             Log(s, ref st);             System.Console.WriteLine("log succeeded");         }     } } 

After entering the C# code, build the project. Copy legacy.dll into the bin/Debug folder under the CSUseLegacyDLL project folder. Run the application and you should see the usual output. Open log.txt and you should see the current date and time, along with the C# version of the text message. The legacy DLL is available to developers in any managed language.

What about the custom marshaling approach? It's quite a bit harder in C# than in C++. A better plan is to create a managed library in C++ that exposes a Log() method, and to have that method use PInvoke and custom marshaling to get to the legacy DLL. That code is out of the scope of this chapter, but would make an interesting exploration for anyone wanting to push a little deeper than usual into legacy DLL interop issues.

Microsoft Visual C++. NET 2003 Kick Start
Microsoft Visual C++ .NET 2003 Kick Start
ISBN: 0672326000
EAN: 2147483647
Year: 2002
Pages: 141
Authors: Kate Gregory © 2008-2017.
If you may any questions please contact us: