C# > Interop and Unsafe Code > Interop with Native Code > P/Invoke Basics
P/Invoke: Calling Native DLL Functions in C#
This example demonstrates how to use Platform Invoke (P/Invoke) to call functions in a native DLL from C#. This is crucial for interacting with legacy code, system APIs, or libraries written in other languages like C or C++.
Defining the Native Function Signature
The key is the `DllImport` attribute. It specifies the name of the DLL containing the function you want to call (`user32.dll` in this case). The `extern` keyword indicates that the function implementation is provided externally (in the DLL). `CharSet = CharSet.Auto` handles string marshalling automatically (ANSI or Unicode based on the platform). The `MessageBox` function declaration mimics the signature of the native Windows API function.
using System.Runtime.InteropServices;
public class NativeMethods
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int MessageBox(IntPtr hWnd, string text, string caption, uint type);
}
Calling the Native Function
This is a standard C# program. Inside the `Main` method, we simply call the `NativeMethods.MessageBox` function we declared earlier. The parameters are passed as if it were a regular C# function. `IntPtr.Zero` represents a null window handle (no parent window), "Hello from P/Invoke!" is the message, "C# Message" is the title, and `0` is a flag specifying the style of the message box.
public class Program
{
public static void Main(string[] args)
{
NativeMethods.MessageBox(IntPtr.Zero, "Hello from P/Invoke!", "C# Message", 0);
}
}
Concepts Behind the Snippet
P/Invoke is a technology that allows managed code (like C#) to call unmanaged functions implemented in DLLs. It bridges the gap between the .NET runtime and native operating system APIs. The .NET runtime handles the complexities of marshalling data between the managed and unmanaged worlds. `DllImport` is the attribute that enables this functionality. The `CharSet` property controls how strings are encoded and passed to the native function. Incorrect `CharSet` settings can cause errors or unexpected behavior.
Real-Life Use Case
A common real-life use case is accessing functionality that is not directly available in the .NET Framework. For example, you might need to interact with a specific hardware device that has a driver written in C. P/Invoke allows you to call the functions in that driver from your C# application. Another use case is to interact with existing C/C++ libraries or components, avoiding the need to rewrite them in C#.
Best Practices
Interview Tip
Be prepared to explain the concept of P/Invoke, the `DllImport` attribute, and the importance of marshalling data between managed and unmanaged code. Also, understand common issues like string encoding and memory management.
When to use them
Use P/Invoke when you need to access functionality that is not available in the .NET Framework, interact with existing C/C++ libraries, or access system APIs. However, consider alternatives like creating a C++/CLI wrapper if the interaction is complex or performance-critical.
Performance Implications
P/Invoke calls incur performance overhead due to the marshalling of data between the managed and unmanaged worlds. Frequent or complex P/Invoke calls can negatively impact performance. Consider alternatives like C++/CLI for performance-critical scenarios.
Alternatives
Pros
Cons
FAQ
-
What happens if the DLL is not found?
A `DllNotFoundException` will be thrown at runtime when the P/Invoke method is first called. -
What is marshalling?
Marshalling is the process of converting data between the managed and unmanaged environments. This involves handling data types, memory allocation, and character encoding differences. -
Why is it important to match the function signatures?
If the function signatures don't match, the program may crash, produce incorrect results, or corrupt memory.