Chapter 24 - Native and COM Interoperability

Calling into Native DLLs

MessageBox (IntPtr.Zero,
           "Please do not press this again.", "Attention", 0);

[DllImport ("user32.dll")]
static extern int MessageBox (IntPtr hWnd, string text, string caption, int type);

Type Marshaling - StringBuilder

StringBuilder s = new StringBuilder (256);
GetWindowsDirectory (s, 256);
Console.WriteLine (s.ToString());

[DllImport ("kernel32.dll")]
static extern int GetWindowsDirectory (StringBuilder sb, int maxChars);

Type Marshaling - ArrayPool

GetWindowsDirectory().Dump();

string GetWindowsDirectory()
{
  var array = ArrayPool<char>.Shared.Rent (256);
  try
  {
    int length = GetWindowsDirectory (array, 256);
    return new string (array, 0, length).ToString();
  }
  finally
  {
    ArrayPool<char>.Shared.Return (array);
  }
  
  [DllImport ("kernel32.dll", CharSet = CharSet.Unicode)]
  static extern int GetWindowsDirectory (char[] buffer, int maxChars);
}

Marshaling Classes and Structs

SystemTime t = new SystemTime();
GetSystemTime (t);
Console.WriteLine (t.Year);

[DllImport ("kernel32.dll")]
static extern void GetSystemTime (SystemTime t);

[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;
}

Callbacks from Unmanaged Code - function pointers

EnumWindows (&PrintWindow, IntPtr.Zero);

[DllImport ("user32.dll")]
static extern int EnumWindows (delegate*<IntPtr, IntPtr, bool> hWnd, IntPtr lParam);

static bool PrintWindow (IntPtr hWnd, IntPtr lParam)
{
  Console.WriteLine (hWnd.ToInt64());
  return true;
}

Callbacks from Unmanaged Code - unmanaged function pointers

EnumWindows (&PrintWindow, IntPtr.Zero);

[DllImport ("user32.dll")]
static extern int EnumWindows (
  delegate* unmanaged <IntPtr, IntPtr, byte> hWnd, IntPtr lParam);

[UnmanagedCallersOnly]
static byte PrintWindow (IntPtr hWnd, IntPtr lParam)
{
  Console.WriteLine (hWnd.ToInt64());
  return 1;
}

Callbacks from Unmanaged Code - calling conventions

EnumWindows (&PrintWindow, IntPtr.Zero);

[DllImport ("user32.dll")]
static extern int EnumWindows (
  delegate* unmanaged[Stdcall] <IntPtr, IntPtr, byte> hWnd, IntPtr lParam);

[UnmanagedCallersOnly (CallConvs = new[] { typeof (CallConvStdcall) })]
static byte PrintWindow (IntPtr hWnd, IntPtr lParam)
{
  Console.WriteLine (hWnd.ToInt64());
  return 1;
}

Callbacks from Unmanaged Code - delegates

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 readonly EnumWindowsCallback printWindowFunc = PrintWindow;

  static void Main() => EnumWindows (printWindowFunc, IntPtr.Zero);
}

Simulating a C Union

void Main()
{
  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
}

[DllImport ("winmm.dll")]
public static extern uint midiOutShortMsg (IntPtr handle, uint message);

[StructLayout (LayoutKind.Explicit)]
public struct NoteMessage
{
  [FieldOffset (0)] public uint PackedMsg;    // 4 bytes long

  [FieldOffset (0)] public byte Channel;      // FieldOffset also at 0
  [FieldOffset (1)] public byte Note;
  [FieldOffset (2)] public byte Velocity;
}

Shared Memory Client

static unsafe void Main()
{
  using (SharedMem sm = new SharedMem ("MyShare", true,
                              (uint)sizeof (MySharedData)))
  {
    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->Numbers [10]}");

    // Our turn to update values in shared memory!
    data->Value++;
    data->Letter = '!';
    data->Numbers [10] = 987.5f;
    Console.WriteLine ("Updated shared memory");
    Console.ReadLine();
  }
}

[StructLayout (LayoutKind.Sequential)]
unsafe struct MySharedData
{
  public int Value;
  public char Letter;
  public fixed float Numbers [50];
}

public sealed 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", SetLastError = true)]
  static extern bool UnmapViewOfFile (IntPtr map);

  [DllImport ("kernel32.dll", SetLastError = true)]
  static extern int CloseHandle (IntPtr hObject);

  IntPtr fileHandle, fileMap;

  public IntPtr Root => 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 Win32Exception();

    // Obtain a read/write map for the entire file
    fileMap = MapViewOfFile (fileHandle, FileRights.ReadWrite, 0, 0, 0);

    if (fileMap == IntPtr.Zero)
      throw new Win32Exception();
  }

  public void Dispose()
  {
    if (fileMap != IntPtr.Zero) UnmapViewOfFile (fileMap);
    if (fileHandle != IntPtr.Zero) CloseHandle (fileHandle);
    fileMap = fileHandle = IntPtr.Zero;
  }
}

Shared Memory Server

static unsafe void Main()
{
  using (SharedMem sm = new SharedMem ("MyShare", false, (uint)sizeof(MySharedData)))
  {
    void* root = sm.Root.ToPointer();
    MySharedData* data = (MySharedData*)root;

    Console.Write("Before this process writes to shared mem:");
    Console.WriteLine ($"Value is {data->Value}");
    Console.WriteLine ($"Letter is {data->Letter}");
    Console.WriteLine ($"11th Number is {data->Numbers [10]}");

    data->Value = 123;
    data->Letter = 'X';
    data->Numbers [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->Numbers [10]}");
    Console.ReadLine();
  }
}

[StructLayout (LayoutKind.Sequential)]
unsafe struct MySharedData
{
  public int Value;
  public char Letter;
  public fixed float Numbers [50];
}

public sealed 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", SetLastError = true)]
  static extern bool UnmapViewOfFile (IntPtr map);

  [DllImport ("kernel32.dll", SetLastError = true)]
  static extern int CloseHandle (IntPtr hObject);

  IntPtr fileHandle, fileMap;

  public IntPtr Root => 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 Win32Exception();

    // Obtain a read/write map for the entire file
    fileMap = MapViewOfFile (fileHandle, FileRights.ReadWrite, 0, 0, 0);

    if (fileMap == IntPtr.Zero)
      throw new Win32Exception();
  }

  public void Dispose()
  {
    if (fileMap != IntPtr.Zero) UnmapViewOfFile (fileMap);
    if (fileHandle != IntPtr.Zero) CloseHandle (fileHandle);
    fileMap = fileHandle = IntPtr.Zero;
  }
}

Calling a COM Component from C#

var excel = new Excel.Application();
excel.Visible = true;
Excel.Workbook workBook = excel.Workbooks.Add();
((Excel.Range)excel.Cells [1, 1]).Font.FontStyle = "Bold";
((Excel.Range)excel.Cells [1, 1]).Value2 = "Hello World";
workBook.SaveAs (Path.GetTempFileName() + ".xlsx");
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