求救:C# 调用C++ 回调函数的问题,将无效 VARIANT 传递给 CLR 会导致意外的异常

时间:2023-01-24 19:00:38
客户给的dll文件,用c#调用,其中有个方法是要传回调函数c++是这样定义的
1、On_exec_result SetExecResult(On_exec_result pHandler);
2、typedef void(CALLBACK* On_exec_result)(const TAnswer *pAnswer);
3、TAnswer{
  char  chInfo [64]
  int  nAnswer
  int  nMarkets
  char  chMarketFlag [32][4]
  int  nDynDate [32]
}
(真搞不明白搞不明白SetExecResult的返回值也是个On_exec_result函数?)
我c#是这样写的:
 1、 [DllImport("TAPI.dll", CallingConvention = CallingConvention.Cdecl)]
   public static extern void SetExecResult(On_exec_result pHandler);
 2、public delegate void On_exec_result (TAnswer tAnswer);
 3、public struct TAnswer 
     {        
         [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 64)]
         public byte[] chInfo;
         [MarshalAs(UnmanagedType.I4)]
         public int nAnswer;
         [MarshalAs(UnmanagedType.I4)]
         public int nMarkets;
         [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 3 * 16)]
         public byte[] chMarketFlag;
         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
         public byte[] nDynDate;
    }
  调用的地方:
   On_exec_result tHandler = new On_exec_result(OnExecHandler);
   SetExecResult(tHandler);
   public void OnExecHandle(TAnswer tAnswer)
   {
       Logger.Info(tAnswer.nAnswer);
   }

运行的时候错误:从非托管 VARIANT 转换为托管对象的过程中检测到无效 VARIANT。将无效 VARIANT 传递给 CLR 会导致意外的异常、损坏或数据丢失
   

14 个解决方案

#1


struct 定义错误

#2


         [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 64)]
         public byte[] chInfo;
         [MarshalAs(UnmanagedType.I4)]
         public int nAnswer;
         [MarshalAs(UnmanagedType.I4)]
         public int nMarkets;
         [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 3 * 16)]
         public byte[] chMarketFlag; //二维数组尺寸似乎不对
         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
         public byte[] nDynDate; //这里要求的 int ,

#3


多谢楼上兄弟回复!
我换成
         [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 64)]
         public byte[] chInfo;

         [MarshalAs(UnmanagedType.I4)]
         public int nAnswer;

         [MarshalAs(UnmanagedType.I4)]
         public int nMarkets;

         [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 32 * 4)]
         public byte[] chMarketFlag;

         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
         public int[] nDynDate;
还是一样的错误,感觉好像不是这个结构定义的问题,我自己按照一样的结构定义写了个c++ dll做测试
  extern "C" __declspec(dllexport)  void Test(TAnswer* mystruct)
 {
     mystruct->nAnswer=1;
 }
然后c# :
   [DllImport("standerMFC.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int Test(ref TAnswer tAnswer);

  TAnswer tAnswer=new TAnswer ();
  Test(ref sAnswer);
  Console.WriteLine(tAnswer.nAnswer);
能得到1

#4


应该是struct的定义有问题吧,楼上应该是正解

#5


哦,请教下这个struct应该怎样定义?

#6


UnmanagedType.ByValTStr是把托管代码转化为非托管代码的数组,SizeConst是数组长度。
抛出来的错误是不是跟这个结构体的定义有关?

#7


楼主,嗯,花了我2小时,我在VS2010里面模拟了你的问题,得到正确结构返回了,不像你只传了一个整数。
问题就是在传递结构的时候出错,我建议你还是多搜索一下网上关于C#、C++互操作的文章。
1.SetExecResult返回是个函数指针是为了调用C#里面的相应函数(委托)。调用可以是双方的,而不只是C#单方向调用C++的函数。
2.正如楼上说的,你就托管代码转换成非托管代码时候用MarshalAs显示定义数组元素所占位置大小是可取的,但是你改后的SizeConst还有一个错的,没算对,最后一个是int型数组,应该定义成32*4。
3.最后在C#定义结构时显示的声明[structLayout(LayoutKind.Sequential)],虽然MSDN上说默认是这个选项,但我建议你还是显示写出来。字段顺序很重要。
4.在你引用DLL是声明可CallingConvention.Cdecl,但是DLL中的是函数是CALLBACK,这个在定义是#define CALLBACK __stdcall。
还有一些小问题就不讲了,你先试试看,不行的话,我给你贴我的代码。

#8


万分感谢dav0913兄弟,我按照您的方式,最后一个数组换成32*4还是同样的错误,如果CallingConvention.Cdecl改成CallingConvention.StdCall,会抛个“调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配”的错误。另外结构体有声明[structLayout(LayoutKind.Sequential)],呵呵。能做到这一步已经连续网上搜了n天资料的结果了,实在木有办法才来这里发帖。。麻烦看看您的代码,多谢多谢!

#9


额。休假1天归来。dav0913兄弟呢,求代码....谢谢 求救:C# 调用C++ 回调函数的问题,将无效 VARIANT 传递给 CLR 会导致意外的异常

#10


求救:C# 调用C++ 回调函数的问题,将无效 VARIANT 传递给 CLR 会导致意外的异常
有木有人啊。。

#11


[DllImport(@"D:\Visual Studio 2010\Projects\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "SetExecResult")]
        extern unsafe  static void SetExecResult(CSCallbackp pAnswer);
unsafe delegate void CSCallbackp(TAnswer* pAnswer);
 [StructLayout(LayoutKind.Sequential)]
        unsafe struct TAnswer
        {
           public  fixed byte chInfo[64];
           public  int nAnswer;
           public  int nMarkets;
           public  fixed byte chMarketFlag[128];
           public  fixed int nDynDate[32];
        }
调用地方:
            unsafe
            {
                callbackp = CSCallbackpFunction;
                SetExecResult(callbackp);
            }
 unsafe static void CSCallbackpFunction(TAnswer* pAnswer)
        {
         //ToDo something;   
        }

#12


多谢dav0913,今天才看到。谢谢。貌似你的不是c#?俺再研究研究..... 求救:C# 调用C++ 回调函数的问题,将无效 VARIANT 传递给 CLR 会导致意外的异常

#13


测试代码果然能运行,谢谢dav0913

#14


貌似用unsafe很多地方都有问题,唉,悲剧..

#1


struct 定义错误

#2


         [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 64)]
         public byte[] chInfo;
         [MarshalAs(UnmanagedType.I4)]
         public int nAnswer;
         [MarshalAs(UnmanagedType.I4)]
         public int nMarkets;
         [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 3 * 16)]
         public byte[] chMarketFlag; //二维数组尺寸似乎不对
         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
         public byte[] nDynDate; //这里要求的 int ,

#3


多谢楼上兄弟回复!
我换成
         [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 64)]
         public byte[] chInfo;

         [MarshalAs(UnmanagedType.I4)]
         public int nAnswer;

         [MarshalAs(UnmanagedType.I4)]
         public int nMarkets;

         [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 32 * 4)]
         public byte[] chMarketFlag;

         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
         public int[] nDynDate;
还是一样的错误,感觉好像不是这个结构定义的问题,我自己按照一样的结构定义写了个c++ dll做测试
  extern "C" __declspec(dllexport)  void Test(TAnswer* mystruct)
 {
     mystruct->nAnswer=1;
 }
然后c# :
   [DllImport("standerMFC.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int Test(ref TAnswer tAnswer);

  TAnswer tAnswer=new TAnswer ();
  Test(ref sAnswer);
  Console.WriteLine(tAnswer.nAnswer);
能得到1

#4


应该是struct的定义有问题吧,楼上应该是正解

#5


哦,请教下这个struct应该怎样定义?

#6


UnmanagedType.ByValTStr是把托管代码转化为非托管代码的数组,SizeConst是数组长度。
抛出来的错误是不是跟这个结构体的定义有关?

#7


楼主,嗯,花了我2小时,我在VS2010里面模拟了你的问题,得到正确结构返回了,不像你只传了一个整数。
问题就是在传递结构的时候出错,我建议你还是多搜索一下网上关于C#、C++互操作的文章。
1.SetExecResult返回是个函数指针是为了调用C#里面的相应函数(委托)。调用可以是双方的,而不只是C#单方向调用C++的函数。
2.正如楼上说的,你就托管代码转换成非托管代码时候用MarshalAs显示定义数组元素所占位置大小是可取的,但是你改后的SizeConst还有一个错的,没算对,最后一个是int型数组,应该定义成32*4。
3.最后在C#定义结构时显示的声明[structLayout(LayoutKind.Sequential)],虽然MSDN上说默认是这个选项,但我建议你还是显示写出来。字段顺序很重要。
4.在你引用DLL是声明可CallingConvention.Cdecl,但是DLL中的是函数是CALLBACK,这个在定义是#define CALLBACK __stdcall。
还有一些小问题就不讲了,你先试试看,不行的话,我给你贴我的代码。

#8


万分感谢dav0913兄弟,我按照您的方式,最后一个数组换成32*4还是同样的错误,如果CallingConvention.Cdecl改成CallingConvention.StdCall,会抛个“调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配”的错误。另外结构体有声明[structLayout(LayoutKind.Sequential)],呵呵。能做到这一步已经连续网上搜了n天资料的结果了,实在木有办法才来这里发帖。。麻烦看看您的代码,多谢多谢!

#9


额。休假1天归来。dav0913兄弟呢,求代码....谢谢 求救:C# 调用C++ 回调函数的问题,将无效 VARIANT 传递给 CLR 会导致意外的异常

#10


求救:C# 调用C++ 回调函数的问题,将无效 VARIANT 传递给 CLR 会导致意外的异常
有木有人啊。。

#11


[DllImport(@"D:\Visual Studio 2010\Projects\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "SetExecResult")]
        extern unsafe  static void SetExecResult(CSCallbackp pAnswer);
unsafe delegate void CSCallbackp(TAnswer* pAnswer);
 [StructLayout(LayoutKind.Sequential)]
        unsafe struct TAnswer
        {
           public  fixed byte chInfo[64];
           public  int nAnswer;
           public  int nMarkets;
           public  fixed byte chMarketFlag[128];
           public  fixed int nDynDate[32];
        }
调用地方:
            unsafe
            {
                callbackp = CSCallbackpFunction;
                SetExecResult(callbackp);
            }
 unsafe static void CSCallbackpFunction(TAnswer* pAnswer)
        {
         //ToDo something;   
        }

#12


多谢dav0913,今天才看到。谢谢。貌似你的不是c#?俺再研究研究..... 求救:C# 调用C++ 回调函数的问题,将无效 VARIANT 传递给 CLR 会导致意外的异常

#13


测试代码果然能运行,谢谢dav0913

#14


貌似用unsafe很多地方都有问题,唉,悲剧..