Code Listings
Chapter 22: Integrating with Native DLLs
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'; } } } }
© 2007, O'Reilly Media, Inc. All rights reserved