C# > Interop and Unsafe Code > Interop with Native Code > P/Invoke Basics
Accessing Native Structures with P/Invoke
This snippet demonstrates how to define and use a native structure in C# using P/Invoke. Correctly mapping structures is crucial for passing data effectively between managed and unmanaged code.
Defining the Native Structure
The `StructLayout` attribute with `LayoutKind.Sequential` is crucial. It ensures that the fields in the C# structure are laid out in memory in the same order as the fields in the native structure. Incorrect layout can lead to data corruption. The `SYSTEM_INFO` structure is a common Windows structure that provides information about the system's hardware. We use `IntPtr` for pointer types.
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public struct SYSTEM_INFO
{
public ushort wProcessorArchitecture;
public ushort wReserved;
public uint dwPageSize;
public IntPtr lpMinimumApplicationAddress;
public IntPtr lpMaximumApplicationAddress;
public IntPtr dwActiveProcessorMask;
public uint dwNumberOfProcessors;
public uint dwProcessorType;
public uint dwAllocationGranularity;
public ushort wProcessorLevel;
public ushort wProcessorRevision;
}
Calling a Native Function that Uses the Structure
Here, we define the `GetSystemInfo` function from `kernel32.dll` using P/Invoke. The `out` keyword indicates that the function will populate the `SYSTEM_INFO` structure. The `DllImport` attribute specifies the DLL and the function name.
public class NativeMethods
{
[DllImport("kernel32.dll")]
public static extern void GetSystemInfo(out SYSTEM_INFO lpSystemInfo);
}
Using the Structure in C#
In the `Main` method, we create an instance of the `SYSTEM_INFO` structure, call the `GetSystemInfo` function, and then access the fields of the populated structure. The `out` keyword passes a reference to the structure, allowing the native function to modify it.
public class Program
{
public static void Main(string[] args)
{
SYSTEM_INFO sysInfo = new SYSTEM_INFO();
NativeMethods.GetSystemInfo(out sysInfo);
Console.WriteLine("Number of processors: " + sysInfo.dwNumberOfProcessors);
Console.WriteLine("Page size: " + sysInfo.dwPageSize);
}
}
Memory footprint
The memory footprint of the C# structure directly corresponds to the size of the native structure it represents. The `StructLayout` attribute helps maintain the correct size and alignment. Incorrect structure definition can lead to memory corruption.
Important Considerations
When to use them
Use this approach when you need to interact with native functions that require structures as input or output parameters. Common scenarios include accessing system information, working with device drivers, or using legacy libraries.
Error Handling
Check the return values of native functions to determine if the structure was populated correctly. If the function fails, handle the error appropriately.
FAQ
-
What happens if the structure layout is incorrect?
Data will be misinterpreted, leading to incorrect results or crashes. It's crucial to match the structure layout exactly. -
How do I handle pointers within structures?
Use `IntPtr` to represent pointers. You might need to use `Marshal.PtrToStructure` to convert the data pointed to by the pointer into a C# object. -
How do I handle structures with variable-length arrays?
This is more complex. You might need to use unsafe code or create a wrapper class to handle the memory management and data access.