如何 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);
我正在尝试从 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);