C# - 方法的类型签名与 PInvoke 不兼容。使用 MarshalDirectiveException

Posted

技术标签:

【中文标题】C# - 方法的类型签名与 PInvoke 不兼容。使用 MarshalDirectiveException【英文标题】:C# - Method's type signature is not PInvoke compatible. With MarshalDirectiveException 【发布时间】:2016-11-14 18:21:55 【问题描述】:

当我运行 GetBoard 方法时出现以下异常(但方法 Initialize 工作正常):

System.Runtime.InteropServices.MarshalDirectiveException was unhandled
HResult=-2146233035
Message=Method's type signature is not PInvoke compatible.
Source=MatinChess.Net
StackTrace:
   at MatinChess.Net.ExternMethods.GetBoard()
   at MatinChess.Net.MatinChess.GetBoard() in C:\Users\Matin\Documents\GitHub\MatinChessDLL\dotnet\MatinChess.Net\MatinChess.cs:line 12
   at MatinChess.Net.Demo.Program.PrintBoard(MatinChess chess) in C:\Users\Matin\Documents\GitHub\MatinChessDLL\dotnet\MatinChess.Net.Demo\Program.cs:line 53
   at MatinChess.Net.Demo.Program.Main(String[] args) in C:\Users\Matin\Documents\GitHub\MatinChessDLL\dotnet\MatinChess.Net.Demo\Program.cs:line 14
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()
InnerException: 

我编写了以下基于 x86 设置的 C# 结构:

[DllImport("MatinChess.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
public extern static void Initialize();

[DllImport("MatinChess.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
public extern static ChessBoard GetBoard();

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ChessBoard

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
    byte[] board;

    public char this[int x, int y]
    
        get
        
            return (char)board[y * 8 + x];
        
    

这是我用MSVC2015 32位编译的C++结构:

struct ChessBoard

    char board[8][8];
;

【问题讨论】:

我猜它不喜欢结构返回值。请尝试使用 out 参数。 c# 代码中没有 char 的位置,而且 CharSet 和 SetLastError 设置看起来没有必要。 ***.com/questions/33806649/… - 这可能是你需要的 虽然这是一个很好的答案,但我认为它不适用于这里。 【参考方案1】:

我必须自己编写代码并进行测试,这样才能确定。

好的。我写了代码并测试了它。

因为它是 C# 代码中的结构,所以您无法从本机 C 代码中获取它,因此您必须通过 GC 分配它,然后通过 API 填充它,或者您只需获取指向本机代码中结构的指针并将其编组为你在 C# 中的结构

第一个:

C 代码:

    __declspec(dllexport) void __cdecl GetBoard(ChessBoard& chess);

C#代码:

[DllImport("testDll.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void GetBoard2(ref ChessBoard  ptr);

public static ChessBoard GetChessBoard()

    ChessBoard chess = new ChessBoard();
    GetBoard2(ref chess);
    return chess;

第二种方法:

C 代码:

__declspec(dllexport) ChessBoard* __cdecl GetBoard();

__declspec(dllexport) void __cdecl FreeMemory(void *);

C#代码:

[DllImport("testDll.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr GetBoard();

[DllImport("testDll.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void FreeMemory(IntPtr ptr);


public static ChessBoard GetChessBoard()

    var boradPtr = GetBoard();
    var chessBoard = (ChessBoard)Marshal.PtrToStructure(boradPtr, typeof(ChessBoard));

    FreeMemory(boradPtr);
    return chessBoard;

【讨论】:

以上是关于C# - 方法的类型签名与 PInvoke 不兼容。使用 MarshalDirectiveException的主要内容,如果未能解决你的问题,请参考以下文章

将带有结构字段的结构从 c++ 返回到 c# pinvoke

C# DllImport“调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配 ”

函数调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。

C# 编组、不平衡堆栈和正确获取 PInvoke 签名

托管调试助手 "PInvokeStackImbalance":的调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调

C#和C++传递结构体