c# - PInvoke marshal and FunctionPtr

The native code has very unsafe types, such as you can pass the Ponter of a function around and there is no runtime to that will proactive to check before it is too late (suppose that you have a long ptr value which expect a windows handler methods, and you will cast that Long to a procedure pointer right before you try to call the method stored in the IntPtr, and at this very moment the program will blow off).

one typical such case is the WNDCLASS, which is something like this in C++
typedef struct tagWNDCLASS {
  UINT      style;
  WNDPROC   lpfnWndProc;
  int       cbClsExtra;
  int       cbWndExtra;
  HINSTANCE hInstance;
  HICON     hIcon;
  HCURSOR   hCursor;
  HBRUSH    hbrBackground;
  LPCTSTR   lpszMenuName;
  LPCTSTR   lpszClassName;
} WNDCLASS, *PWNDCLASS;
You have one option, One is the literal translation, and you can come up something like this:

// compare this to the hand-written one
    // 
    public struct WNDCLASS
    {
        public UInt32 style;
        public IntPtr lpfnWndProc;
        public UInt32 cbClsExtra;
        public UInt32 cbWndExtra;
        public IntPtr hInstance;
        public IntPtr hIcon;
        public IntPtr hCursor;
        public IntPtr hbrBackground;
        public string lpszMenuName;
        public string lpszClassName;
    }
This is according to the Type Conversion Table at  Using P/Invoke to call Unmanaged APIs from your Managed Classes.

However, as you program into such as the following.

hInstance = System.Diagnostics.Process.GetCurrentProcess().Handle;
            WNDCLASS wndClass;
            string szName =  "HelloWin";
            wndClass.style = (uint) (ClassStyles.HorizontalRedraw | ClassStyles.VerticalRedraw);

            wndClass.lpfnWndProc = (WndProc) ((hWnd, message, wParam, lParam) => { 
                IntPtr hdc;
                PAINTSTRUCT ps;
                RECT rect;
                //switch ((WM) message)
                //{
                //    WinAPI.BeginPaint(hWnd, out ps);
                //    break;
                //}
                switch ((WM)message)
                {
                    case WM.PAINT:
                        hdc = WinAPI.BeginPaint(hWnd, out ps);
                        break;
                }
                return WinAPI.DefWindowProc(hWnd, (WM)message, wParam, lParam);
            }
            );
as you can see there is conversion problem from the Function poiner to the Handle (Long or what so ever) .

However, as we know when we define the structure, we can use the Marshal attribute to tell PInvoke system to marshal between the managed boundary and the unmanaged boundary.

As it is been said about the MarshalAs attribute.

MarshallAs Attribute At times the exact type required by the unmanaged function does not exist in managed code nor can it be substituted or mapped to another managed type. At this point you have to manually provide the marshaller with the type information using the MarshallAs attribute. The MarshallAs attribute overrides the default marshaling behavior of the CLR marshalling service as we have done in the TOOLINFO struct in the balloon tip example.
So the MarshalAs can be applied on both the Structure as well as the Functions dllsimports.example is given as below. 
//Applied to a parameter.
  public void M1 ([MarshalAs(UnmanagedType.LPWStr)]String msg);
//Applied to a field within a class.
  class MsgText {
    [MarshalAs(UnmanagedType.LPWStr)] Public String msg;
  }
//Applied to a return value.
[return: MarshalAs(UnmanagedType.LPWStr)]
public String GetMessage();
Notice the special scope modifier "return", this is because we can apply attribute on paramters, but w cannot qualify the return value, and c# offered this special syntax to support this.

So an alternative way is to do this:

// http://www.pinvoke.net/default.aspx/Structures.WNDCLASS
    [StructLayout(LayoutKind.Sequential)]
    public struct WNDCLASS
    {
        public ClassStyles style;
        [MarshalAs(UnmanagedType.FunctionPtr)]
        public WndProc lpfnWndProc;
        public int cbClsExtra;
        public int cbWndExtra;
        public IntPtr hInstance;
        public IntPtr hIcon;
        public IntPtr hCursor;
        public IntPtr hbrBackground;
        [MarshalAs(UnmanagedType.LPTStr)]
        public string lpszMenuName;
        [MarshalAs(UnmanagedType.LPTStr)]
        public string lpszClassName;
    }
and we restrict the lpfnWndProc to be a delegate and use the MarhalAs attribute to let the runtime marshal the rigth value back and forth.

Below is the value of the delegate definition.

public delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);


References:

WNDCLASS structure 
Using P/Invoke to call Unmanaged APIs from your Managed Classes


你可能感兴趣的:(C#)