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';
      }
    }
  }
}
C# 12 in a Nutshell
Buy from amazon.com Buy print or Kindle edition
Buy from ebooks.com Buy PDF edition
Buy from O'Reilly Read via O'Reilly subscription