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");