如何 PInvoke CMark 的 cmark_markdown_to_html

How to PInvoke CMark's cmark_markdown_to_html

我正在尝试从 GitHub 的 CMark

分支中调用以下函数
char *cmark_markdown_to_html(const char *text, size_t len, int options)

这是我的 PInvoke 签名:

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

我这样称呼它:

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

但是,它会抛出一个编组异常并显示以下消息:

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

好的,所以我将签名更改为:

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

现在它抛出 PInvokeStackImbalance 错误

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

本土的东西对我来说是个谜。有人可以帮忙吗?

堆栈不平衡

堆栈不平衡的原因在Why are Cdecl calls often mismatched in the "standard" P/Invoke Convention?中描述。

基本上你需要在 DllImport 中指定正确的调用约定,这可能是 cdecl:

[DllImport("cmark.dll", CallingConvention = CallingConvention.Cdecl)]

编组size_t

中讨论了那个。

  • 假设 CMark 始终是原生的(x86 上为 32 位,x64 上为 64 位),您应该使用 (U)IntPtr
  • 如果总是32位,那么应该使用Int32

PInvokeStackImbalance

推测 CMark 使用的是默认调用约定。 C 代码的默认调用约定是 "Cdecl",而用于 P/invoke 调用的默认调用约定是 "StdCall"。调用约定指定参数和 return 值如何放置在 call stack 上。使用的调用约定之间的不匹配导致堆栈变得不平衡。

您可以在 DllImport 属性中指定要从 C# 代码使用的调用约定。

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

编组

如果您希望代码与 32 位以外的体系结构兼容,则需要将 size_t 编组为 UIntPtr。 (阅读@Eugene Podskal 的回答)

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

然后就可以这样调用方法了

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