// API class declaration public class Win32Api { [DllImport("user32.dll")] public static extern IntPtr MessageBox(IntPtr hWnd, String text, String caption, uint type); }
private void button1_Click(object sender, EventArgs e) { // Calling the API Messagebox Win32Api.MessageBox(this.Handle, "test Message", "caption", 64); }Some fields for DllImport attribute:
CallingConvention - (Winapi, Cdecl, StdCall, ThisCall, FastCall). Winapi is default por windows APIs and Cdecl for Windows CE.NET.
CharSet - How string arguments should be marshaled (Ansi, Unicode, None, Auto)
EntryPoint - DLL entry point, this allows us for use a different name for the API in our class. See bellow
PreserveSig - The signature should be transformed into an unmanaged signature that returns an HRESULT and has an additional [out, retval] argument for the return value. This mean, the function will return an HRESULT instead of thrown an exception when an error occurs. More information see MSDN.
SetLastError - Enable the caller to use the Marshal.GetLasWin32Error API function to determine whether an error ocurred while executing the method.
Samples:
[DllImport("msvcrt.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] public static extern int printf(String format, int i, double d); [DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "MessageBox")] public static extern int MyNewMessageBoxMethod(IntPtr hWnd, String text, String caption, uint type);More about PreserveSig: Unlike COM Interop scenarios, PInvoke signatures have PreserveSig semantics by default. For example, in the IOleClientSite interface, we need to preserve the signature:
[ComVisible(true), Guid("00000118-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] public interface IOleClientSite { [return: MarshalAs(UnmanagedType.I4)] [PreserveSig] int SaveObject(); [return: MarshalAs(UnmanagedType.I4)] [PreserveSig] int GetMoniker( [In, MarshalAs(UnmanagedType.U4)] int dwAssign, [In, MarshalAs(UnmanagedType.U4)] int dwWhichMoniker, [Out, MarshalAs(UnmanagedType.Interface)] out object ppmk); [return: MarshalAs(UnmanagedType.I4)] [PreserveSig] int GetContainer([MarshalAs(UnmanagedType.Interface)] out IOleContainer container); [return: MarshalAs(UnmanagedType.I4)] [PreserveSig] int ShowObject(); [return: MarshalAs(UnmanagedType.I4)] [PreserveSig] int OnShowWindow( [In, MarshalAs(UnmanagedType.I4)] int fShow); [return: MarshalAs(UnmanagedType.I4)] [PreserveSig] int RequestNewObjectLayout(); }More about PreserveSig in the Adam Nathan's Blog.
Passing structures:
When passing structures or classes to unmanaged code using platform invoke, you must provide additional information to preserve the original layout and alignment. Here some examples from MSDN:
[StructLayout(LayoutKind.Sequential)] public struct Point { public int x; public int y; } [StructLayout(LayoutKind.Explicit)] public struct Rect { [FieldOffset(0)] public int left; [FieldOffset(4)] public int top; [FieldOffset(8)] public int right; [FieldOffset(12)] public int bottom; } class Win32API { [DllImport("User32.dll")] public static extern bool PtInRect(ref Rect r, Point p); }Callback functions:
For invoking API functions that are expecting a callback function, we need to declare one delegate function and one method conforms the delegate declaration. Here an example from MSDN that enumerates every opened window in the system:
// The callback delegate declaration public delegate bool theCallBack(int hwnd, int lParam); // The API wrapper public class EnumReportApp { [DllImport("user32")] public static extern int EnumWindows(theCallBack x, int y); public static void Run() { // Here our callback theCallBack myCallBack = new theCallBack(EnumReportApp.Report); EnumWindows(myCallBack, 0); } // This is the callback that EnumWindows will call public static bool Report(int hwnd, int lParam) { Console.Write("Window handle is "); Console.WriteLine(hwnd); return true; } } // Main program class Program { public static void Main() { EnumReportApp.Run(); } }Marshaling:
For APIs returning string values, is a best practice use the StringBuilder instead of normal strings classes when they expect a reference string type.
public class Win32Api { [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] private static extern Int32 GetWindowText(IntPtr hWnd, StringBuilder textValue, Int32 counter); public static string GetForegroupWindowText() { // Our string builder prefixed to 256 characters StringBuilder sb = new StringBuilder(256); IntPtr hwnd = GetForegroundWindow(); GetWindowText(hwnd, sb, 256); return sb.ToString(); } }"For example, you can marshal a string to unmanaged code as either a LPStr, a LPWStr, a LPTStr, or a BStr. By default, the common language runtime marshals a string parameter as a BStr to COM methods. You can apply the MarshalAsAttribute attribute to an individual field or parameter to cause that particular string to be marshaled as a LPStr instead of a BStr." - from MSDN
[DllImport("user32.dll")] public static extern int MessageBoxW( IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] String text, [MarshalAs(UnmanagedType.LPWStr)] String caption, int type);More information about Marshaling with P/Invoke in MSDN.
0 comments:
Post a Comment