Search code examples
c#pinvokecmark

How to PInvoke CMark's cmark_markdown_to_html


I'm trying to PInvoke the following function from GitHub's fork of CMark

char *cmark_markdown_to_html(const char *text, size_t len, int options)

and here's my PInvoke signature:

[DllImport("cmark.dll")]
    public static extern IntPtr cmark_markdown_to_html(
        [In()] [MarshalAs(UnmanagedType.LPStr)] string text, 
        [MarshalAs(UnmanagedType.SysUInt)] uint len,
        [MarshalAs(UnmanagedType.SysInt)] int options);

I call it as follows:

var retValue = cmark_markdown_to_html(markdown, 0, 0);

However, it throws a Marshaling exception the with the message:

Cannot marshal 'parameter #2': 
  Invalid managed/unmanaged type combination 
  (Int32/UInt32 must be paired with I4, U4, or Error). 

OK, so I change the signature to:

[DllImport("cmark.dll")]
    public static extern IntPtr cmark_markdown_to_html(
        [In, MarshalAs(UnmanagedType.LPStr)] string text, 
        [MarshalAs(UnmanagedType.U4)] uint len,
        [MarshalAs(UnmanagedType.I4)] int options); 

Now it throws a PInvokeStackImbalance error

The name '$exception' does not exist in the current context

Native stuff is a mystery to me. Can someone help?


Solution

  • PInvokeStackImbalance

    Presumably CMark is using the default calling convention. The default calling convention for C code is "Cdecl", while the default used for P/invoke calls is "StdCall". The calling convention specifies how the arguments and return value are placed on the call stack. A mismatch between the used calling convention causes the stack to become imbalanced.

    You can specify the calling convention to use from C# code in the DllImport attribute.

    [DllImport("cmark.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr cmark_markdown_to_html(
        [In, MarshalAs(UnmanagedType.LPStr)] string text, 
        [MarshalAs(UnmanagedType.U4)] uint len,
        [MarshalAs(UnmanagedType.I4)] int options); 
    

    Marshalling

    In case you want your code to be compatible with architectures other than 32-bit, you would want to marshall size_t with UIntPtr. (Read @Eugene Podskal answer)

    [DllImport("cmark.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr cmark_markdown_to_html(
        [In, MarshalAs(UnmanagedType.LPStr)] string text, 
        [MarshalAs(UnmanagedType.U4)] UIntPtr len,
        [MarshalAs(UnmanagedType.I4)] int options);
    

    Then you can call the method like so

    var retValue = cmark_markdown_to_html(markdown, UIntPtr.Zero, 0);