【问题标题】:SafeArrayTypeMismatchException when passing c# struct to unmanaged C++ DLL将 c# 结构传递给非托管 C++ DLL 时出现 SafeArrayTypeMismatchException
【发布时间】:2013-10-08 00:18:39
【问题描述】:

您好,我是 C# 和 C++ 编程新手,并尝试在 C# 项目 (.NET 3.5) 中使用非托管 C++ dll。我被这个错误困住了:

System.Runtime.InteropServices.SafeArrayTypeMismatchException:指定的数组不是预期的类型。

这是DLL的头文件

#ifdef FUNCTIONLIB_EXPORTS
#define FUNCTIONLIB_API __declspec(dllexport)
#else
#define FUNCTIONLIB_API __declspec(dllimport)
#endif
typedef struct _FunctionOpt 
{
    char                UserName[32];
    char                Password[32];
    char                ServerIP[128];
    int                 ServerPort;
    int                 Index;          
    char                TargetSubChannel;
    long                Timeout;
    char                Filepath[256];
    bool                isFirst;        
    DWORD               SyncTime;               
    char                *pOutBuf;               
    int                 OutBufSize;
    unsigned short      OutImgResW;
    unsigned short      OutImgResH;

}STFUNCTIONOPT, *PSTFUNCTIONOPT;

FUNCTIONLIB_API int FunctionLib_Snapshot( PSTFUNCTIONOPT pstFunctionOpt ); 

我无权访问 C++ 代码,因此我只能更改以下 C# 代码。我的相关代码如下:

    public unsafe struct PSTFUNCTIONOPT            
    {
        public char[] UserName;
        public char[] Password;
        public char[] ServerIP;
        public int ServerPort;
        public int Index;                      
        public char TargetSubChannel;              
        public long Timeout;              
        public char[] Filepath;
        public bool isFirst;               
        public uint SyncTime;                       
        public char *pOutBuf;   // example C++ usage: myStruct.pOutBuf = (char*)malloc(1920 * 1080 * 3);
        public int OutBufSize;
        public ushort OutImgResW;
        public ushort OutImgResH;
    }

    // need to use dumpbin to get entrypoint name due to c++ mangling
    [DllImport(@"C:\Location\To\Project\bin\FunctionLibLib.dll", EntryPoint = "?FunctionLib_Snapshot@@YAHPAU_FunctionOpt@@@Z")]
    public static extern int FunctionLib_Snapshot(PSTFUNCTIONOPT pstFunctionOpt);

    public unsafe int FunctionLib_Run()
    {
        PSTFUNCTIONOPT stFunctionOpt = new PSTFUNCTIONOPT();

        stFunctionOpt.UserName = ("uname").ToCharArray();
        stFunctionOpt.Password = ("pword").ToCharArray();
        stFunctionOpt.ServerIP = ("192.168.1.1").ToCharArray();
        stFunctionOpt.ServerPort = 80;
        stFunctionOpt.Index = 255;
        stFunctionOpt.Timeout = 15000;
        stFunctionOpt.Filepath = ("c:\\temp\\test.jpg").ToCharArray();
        stFunctionOpt.isFirst = true;
        stFunctionOpt.SyncTime = 0;
    //stFunctionOpt.pOutBuf = new char*[10000];     // not sure how to do this yet
        stFunctionOpt.OutBufSize = 10000;

        // get result from DLL 
        return FunctionLib_Snapshot(stFunctionOpt);
    }

如何正确地将结构传递给这个非托管 DLL?该错误似乎很简单,但我无法缩小问题范围。任何帮助表示赞赏!

【问题讨论】:

    标签: c# c++ interop dllimport


    【解决方案1】:

    几个问题:

        public char[] UserName;
    

    需要将其编组为嵌入字符串:

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string UserName;
    

    对像这样声明的其他字段重复此操作。

        public char *pOutBuf;   // example C++ usage: myStruct.pOutBuf = (char*)malloc(1920 * 1080 * 3);
    

    尚不清楚是 C++ 函数分配该缓冲区还是客户端代码应该这样做。如果是前者那么你有一个大问题,你不能调用 free() 函数再次释放缓冲区。您唯一的希望是您的 C# 代码应该分配它,并非不可能。在这种情况下是:

        public byte[] pOutBuf;
        ...
        stFunctionOpt.pOutBuf = new byte[10000];
        stFunctionOpt.OutBufSize = stFunctionOpt.pOutBuf.Length;
    

    pinvoke 声明错误:

    [DllImport(@"C:\Location\To\Project\bin\FunctionLibLib.dll", EntryPoint = "?FunctionLib_Snapshot@@YAHPAU_FunctionOpt@@@Z")]
    public static extern int FunctionLib_Snapshot(PSTFUNCTIONOPT pstFunctionOpt);
    

    参数是ref PSTFUNCTIONOPT pstFunctionOpt。请从结构名称中删除 P,它不是指针类型。避免对 DLL 的路径进行硬编码,这在您的用户机器上是行不通的。只需确保在构建输出目录中有 DLL 的副本即可。

    【讨论】:

      【解决方案2】:

      我认为您在结构定义中遗漏了几件事

      • 结构在内存中的布局在c++中是不同的,所以你需要在你的结构中添加[StructLayout(LayoutKind.Sequential)]属性
      • 在 C++ 中,您有固定长度的 char 数组,因此您还需要在 c# 中使用属性 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = your_array_lenght)] 指定它,此外,如果您这样做,编组器将处理字符串和 char 数组之间的转换。

      你的结构定义应该看起来像

      [StructLayout(LayoutKind.Sequential)]
      public struct PSTFUNCTIONOPT            
      {
          [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
          public string UserName;
          ...
          [MarshalAs(UnmanagedType.LPStr)]
          public string pOutBuf;  
      }
      

      欲了解更多信息,请查看http://msdn.microsoft.com/en-us/library/s9ts558h.aspx

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-02
        • 2021-12-29
        • 1970-01-01
        • 2021-08-19
        • 1970-01-01
        相关资源
        最近更新 更多