Code Listings
Chapter 25: Native Interop
Calling MessageBox:
using System;
using System.Runtime.InteropServices;
class MsgBoxTest
{
[DllImport("user32.dll")]
static extern int MessageBox (IntPtr hWnd, string text, string caption,
int type);
public static void Main()
{
MessageBox (IntPtr.Zero, "Please do not press this again.", "Attention", 0);
}
}
Overloading external methods:
[DllImport ("user32.dll")]
static extern int SendMessage (IntPtr hWnd, uint msg,
IntPtr wParam, IntPtr lParam);
[DllImport ("user32.dll")]
static extern int SendMessage (IntPtr hWnd, uint msg,
int wParam, int lParam);
String marshalling with StringBuilder:
using System;
using System.Text;
using System.Runtime.InteropServices;
class Test
{
[DllImport("kernel32.dll")]
static extern int GetWindowsDirectory (StringBuilder sb, int maxChars);
static void Main()
{
StringBuilder s = new StringBuilder (256);
GetWindowsDirectory (s, 256);
Console.WriteLine (s);
}
}
Marshalling structs:
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
class SystemTime
{
public ushort Year;
public ushort Month;
public ushort DayOfWeek;
public ushort Day;
public ushort Hour;
public ushort Minute;
public ushort Second;
public ushort Milliseconds;
}
[DllImport("kernel32.dll")]
static extern void GetSystemTime (SystemTime t);
static void Main()
{
SystemTime t = new SystemTime();
GetSystemTime (t);
Console.WriteLine (t.Year);
}
Unmanaged callbacks:
using System;
using System.Runtime.InteropServices;
class CallbackFun
{
delegate bool EnumWindowsCallback (IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
static extern int EnumWindows (EnumWindowsCallback hWnd, IntPtr lParam);
static bool PrintWindow (IntPtr hWnd, IntPtr lParam)
{
Console.WriteLine (hWnd.ToInt64());
return true;
}
static void Main()
{
EnumWindows (PrintWindow, IntPtr.Zero);
}
}
Simulating a C union:
[StructLayout (LayoutKind.Explicit)]
public struct NoteMessage
{
[FieldOffset(0)] public int PackedMsg; // 4 bytes long
[FieldOffset(0)] public byte Channel; // FieldOffset also at 0
[FieldOffset(1)] public byte Note;
[FieldOffset(2)] public byte Velocity;
}
NoteMessage n = new NoteMessage();
Console.WriteLine (n.PackedMsg); // 0
n.Channel = 10;
n.Note = 100;
n.Velocity = 50;
Console.WriteLine (n.PackedMsg); // 3302410
n.PackedMsg = 3328010;
Console.WriteLine (n.Note); // 200
Shared memory wrapper:
using System;
using System.Runtime.InteropServices;
public class SharedMem : IDisposable
{
// Here we're using enums because they're safer than constants
enum FileProtection : uint // constants from winnt.h
{
ReadOnly = 2,
ReadWrite = 4
}
enum FileRights : uint // constants from WinBASE.h
{
Read = 4,
Write = 2,
ReadWrite = Read + Write
}
static readonly IntPtr NoFileHandle = new IntPtr (-1);
[DllImport ("kernel32.dll", SetLastError = true)]
static extern IntPtr CreateFileMapping (IntPtr hFile,
int lpAttributes,
FileProtection flProtect,
uint dwMaximumSizeHigh,
uint dwMaximumSizeLow,
string lpName);
[DllImport ("kernel32.dll", SetLastError=true)]
static extern IntPtr OpenFileMapping (FileRights dwDesiredAccess,
bool bInheritHandle,
string lpName);
[DllImport ("kernel32.dll", SetLastError = true)]
static extern IntPtr MapViewOfFile (IntPtr hFileMappingObject,
FileRights dwDesiredAccess,
uint dwFileOffsetHigh,
uint dwFileOffsetLow,
uint dwNumberOfBytesToMap);
[DllImport ("Kernel32.dll")]
static extern bool UnmapViewOfFile (IntPtr map);
[DllImport ("kernel32.dll")]
static extern int CloseHandle (IntPtr hObject);
IntPtr fileHandle, fileMap;
public IntPtr Root { get { return fileMap; } }
public SharedMem (string name, bool existing, uint sizeInBytes)
{
if (existing)
fileHandle = OpenFileMapping (FileRights.ReadWrite, false, name);
else
fileHandle = CreateFileMapping (NoFileHandle, 0,
FileProtection.ReadWrite,
0, sizeInBytes, name);
if (fileHandle == IntPtr.Zero)
throw new Exception
("Open/create error: " + Marshal.GetLastWin32Error());
// Obtain a read/write map for the entire file
fileMap = MapViewOfFile (fileHandle, FileRights.ReadWrite, 0, 0, 0);
if (fileMap == IntPtr.Zero)
throw new Exception
("MapViewOfFile error: " + Marshal.GetLastWin32Error());
}
public void Dispose()
{
if (fileMap != IntPtr.Zero) UnmapViewOfFile (fileMap);
if (fileHandle != IntPtr.Zero) CloseHandle (fileHandle);
fileMap = fileHandle = IntPtr.Zero;
}
}
Mapping a struct to unmanaged memory:
[StructLayout (LayoutKind.Sequential)]
unsafe struct MySharedData
{
public int Value;
public char Letter;
public fixed float NumberArray [50];
}
// Application 1:
static unsafe void Main()
{
using (SharedMem sm = new SharedMem ("MyShare", false, 1000))
{
void* root = sm.Root.ToPointer();
MySharedData* data = (MySharedData*) root;
data->Value = 123;
data->Letter = 'X';
data->Number[10] = 1.45f;
Console.WriteLine ("Written to shared memory");
Console.ReadLine();
Console.WriteLine ("Value is " + data->Value);
Console.WriteLine ("Letter is " + data->Letter);
Console.WriteLine ("11th Number is " + data->Number[10]);
Console.ReadLine();
}
}
// Application 2:
static unsafe void Main()
{
using (SharedMem sm = new SharedMem ("MyShare", true, 1000))
{
void* root = sm.Root.ToPointer();
MySharedData* data = (MySharedData*) root;
Console.WriteLine ("Value is " + data->Value);
Console.WriteLine ("Letter is " + data->Letter);
Console.WriteLine ("11th Number is " + data->Number[10]);
// Our turn to update values in shared memory!
data->Value++;
data->Letter = '!';
data->Number[10] = 987.5f;
Console.WriteLine ("Updated shared memory");
Console.ReadLine();
}
}
fixed and fixed { ... }
[StructLayout (LayoutKind.Sequential)]
unsafe struct MySharedData
{
...
// Allocate space for 200 chars (i.e., 400 bytes).
fixed char message [200];
// One would most likely put this code into a helper class:
public string Message
{
get { fixed (char* cp = message) return new string (cp); }
set
{
fixed (char* cp = message)
{
int i = 0;
for (; i < value.Length && i < 199; i++)
cp [i] = value [i];
// Add the null terminator
cp [i] = '\0';
}
}
}
}