C#调用带有回调函数的DLL的问题 !!!!急!!!!!!

时间:2022-09-06 14:00:14
//------------dll中的回调函数---------------

void kinescope(void(* kines)(float x, float y))
{
D3DXVECTOR3 vec = d3d->GetCamera()->GetPos();
kines(vec.x, vec.y);


//----------------C#中调用代码---------------

//------------委托声明-------------
    public delegate void kinds(float x, float y);

//----------------函数声明---------------
[DllImport("skyll_05.dll")]
static extern void kinescope(kinds k);

//---------------委托定义-------------------
public static void kds(float x, float y)
{
     MessageBox.Show(x.ToString() + "+" + y.ToString());
}

//------------------调用---------------
kinds kk = new kinds(kds);
kinescope(kk);


错误提示:
Debug Error!

...省略...

File:

Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

感谢大家解答,小弟拜谢了!!!!!!

31 个解决方案

#1


void kinescope(void(* kines)(float x, float y))
具体的c++ 声明
怎么引出的

#2


?楼上说的没看明白  
你是说怎么导出的这个函数吗?
是在.def文件里 EXPORTS 出来的

#3


用反射试试...

   Assembly embodiedAssembly = Assembly.Load("MathLibrary.dll"); //获取嵌入的程序集 
  Object obj = embodiedAssembly.CreateInstance("MathLibrary.Arithmetic");
            //获取方法对象
    MethodInfo  mtd = typ.GetMethod("ShowForm");
            //调用方法
            mtd.Invoke(obj, null);

#4


上面只是泛泛的说了c++的定义,没有具体表明你的这个函数到底是
stdcall _cdecl 等等?

#5


To hdt :
     这个dll不是我写的,同事写的,我刚看了下,这就是一个全局函数,没有写你说的那些前缀(不好意思,对C++不了解,说法如有误请谅解)
是不是dll写的不对呢?这个dll写的方法是:因为是非托管类,所以对类中需要C#调用的函数透出为全局函数,并在.def文件中EXPORTS,(我试过不能直接调用非托管类中的函数,只能调用全局的),所以这个函数也写得是全局的。

#6


c++ 输出的函数调用约定是多种的

例如

1、Cdecl 调用方清理堆栈。这使您能够调用具有 varargs 的函数(如 Printf),使之可用于接受可变数目的参数的方法。  
 2、FastCall 不支持此调用约定。  
3、 StdCall 被调用方清理堆栈。这是使用平台 invoke 调用非托管函数的默认约定。  
4、 ThisCall 第一个参数是 this 指针,它存储在寄存器 ECX 中。其他参数被推送到堆栈上。此调用约定用于对从非托管 DLL 导出的类调用方法。  
5、 Winapi 此成员实际上不是调用约定,而是使用了默认平台调用约定。例如,在 Windows 上默认为 StdCall,在 Windows CE.NET 上默认为 Cdecl。  

#7


明白了  你说的是调用规则是吗?

void* 默认的好像是__stdcall规则  而编译器默认的是__cdecl规则是吗?

但是解决办法呢?

是在C++中加上调用规则 还是 在C#中强调调用规则?

#8


呵呵  你回帖真快  我还没刷新都被你抢了一楼   呵呵  谢谢你  能回答我7楼的问题吗?  最好是帮忙修改下我的代码

#9


在c#中要和c++一直
类似

[DllImport("msvcrt.dll", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.Cdecl)]
public static extern int printf(String format, int i, double d); 

#10


void*   默认的好像是__stdcall规则

跟void 没关系,看你的编译器设置,和显式设置,显式设置覆盖编译器设置。一般是__cdcel.但.net中默认是__stdcall


但是解决办法呢? 

是在C++中加上调用规则   还是   在C#中强调调用规则?


可以都加,,,重要是调用规则要保持一致。

#11


一直=〉一致

#12


mygod  简直像IM了  呵呵

#13


我在我的C#里照9楼的加了规则限制,仍然出错,应该是C++里没有约定的关系把
那么C++中怎么写?  我不懂比如说都约定为__stdcall  就拿我顶楼的代码说

#14


出错信息是什么??????
另外 ,你有没有看过编译后的函数名字。?。编译后的有什么变化? 有没有用extern "C" 禁用名字粉碎?

#15


同事在前面加了CALLBACK,他说这和__stdcall一样

CALLBACK void kinescope(void(* kines)(float x, float y))
{
    D3DXVECTOR3 vec = d3d->GetCamera()->GetPos();
    kines(vec.x, vec.y);
}

然后我设定CallinConvention.StdCall
还是出错!郁闷!

#16


study

#17


To haiwangstar :
      出错信息写在顶楼了,编译后函数的名字?好像没变化啊,我在5楼说是在.def中导出的,这个也是全局函数,没有相应的头文件,所以没有加extern "c"

#18


如果还是顶楼的错误的话,那还是调用约定不对。

#19


我在网上有看到一种方法,换了种调用方式,定义了一个结构,代码如下:

    public delegate void kinds(float x, float y);

    [StructLayout(LayoutKind.Sequential,Pack=1)]
    public struct abc
    {
        public float ax;
        public float by;
        public kinds k;
    };

        [DllImport("skyll_05.dll")]
        public static extern void kinescope([In] ref abc i);

        public static void kds(float x, float y)
        {
            MessageBox.Show(x.ToString() + "+" + y.ToString());
        }

            abc i;
            i.ax = 0.0f;
            i.by = 0.0f;
            i.k = new kinds(kds);
            kinescope(ref i);


这回调试运行到kinescope(ref i)这句时,出现说是“尝试读取或写入受保护的内存。这通常指示其他内存已损坏”,这又是怎么了啊??疯了!是内存布局不同?

#20


错了  上面是C#代码

#21


用vc++.net封装一下再用c#调用,可以解决此问题。

#22


To schumyxp :
    目的不是兼容就行,那样就都用C#写了,更简单,目的就是搞明白托管与非托管之间的互操作

#23


周末来继续!!!

#24


你让你同事用c++ 

loadlibrary 
getprocessaddress 
方式调用??

#25


mark~

#26


#27


就像6楼说的,基本上是签名的问题。另外就是非托管类型于托管类型直接的对应关系的问题。
也许你需要写一些unsafe的代码才可以很好的解决这个问题吧。.net库的于 win32直接就有很多这样的操作,可以参考一下。

#28


lz的问题不是在函数调用方式,而是Marshal数据错误,lz这样试试:

//------------dll中的回调函数--------------- 

void kinescope(void(* kines)(float x, float y))
{
    D3DXVECTOR3 vec = d3d->GetCamera()->GetPos();
    kines(vec.x, vec.y);
}


//----------------C#中调用代码--------------- 


//------------委托声明-------------
public delegate void kinds(float x, float y);

//----------------函数声明---------------
[DllImport("skyll_05.dll")]
static extern void kinescope( IntPtr pKines);

//---------------委托定义-------------------
public static void kds(float x, float y)
{
     MessageBox.Show(x.ToString() + "+" + y.ToString());
}

//------------------调用---------------
kinds kk = new kinds(kds);
IntPtr pKinds = Marshal.GetFunctionPointerForDelegate(kk);
kinescope( pKinds);

#29


学习

#30


非托管代码在回调托管代码时的调用约定不一致的缘故。将非托管代码函数指针定义的调用约定改下就可以了。

#31


或者,你可以修改委托定义时,作为函数指针在被回调时的调用顺序
如下:
//------------委托声明-------------
[UnmanagedFunctionPointer( CallingConvention.Cdecl)]
    public delegate void kinds(float x, float y);

//----------------函数声明---------------
[DllImport("skyll_05.dll")]
static extern void kinescope(kinds k);

//---------------委托定义-------------------
public static void kds(float x, float y)
{
     MessageBox.Show(x.ToString() + "+" + y.ToString());
}

//------------------调用---------------
kinds kk = new kinds(kds);
kinescope(kk);

#1


void kinescope(void(* kines)(float x, float y))
具体的c++ 声明
怎么引出的

#2


?楼上说的没看明白  
你是说怎么导出的这个函数吗?
是在.def文件里 EXPORTS 出来的

#3


用反射试试...

   Assembly embodiedAssembly = Assembly.Load("MathLibrary.dll"); //获取嵌入的程序集 
  Object obj = embodiedAssembly.CreateInstance("MathLibrary.Arithmetic");
            //获取方法对象
    MethodInfo  mtd = typ.GetMethod("ShowForm");
            //调用方法
            mtd.Invoke(obj, null);

#4


上面只是泛泛的说了c++的定义,没有具体表明你的这个函数到底是
stdcall _cdecl 等等?

#5


To hdt :
     这个dll不是我写的,同事写的,我刚看了下,这就是一个全局函数,没有写你说的那些前缀(不好意思,对C++不了解,说法如有误请谅解)
是不是dll写的不对呢?这个dll写的方法是:因为是非托管类,所以对类中需要C#调用的函数透出为全局函数,并在.def文件中EXPORTS,(我试过不能直接调用非托管类中的函数,只能调用全局的),所以这个函数也写得是全局的。

#6


c++ 输出的函数调用约定是多种的

例如

1、Cdecl 调用方清理堆栈。这使您能够调用具有 varargs 的函数(如 Printf),使之可用于接受可变数目的参数的方法。  
 2、FastCall 不支持此调用约定。  
3、 StdCall 被调用方清理堆栈。这是使用平台 invoke 调用非托管函数的默认约定。  
4、 ThisCall 第一个参数是 this 指针,它存储在寄存器 ECX 中。其他参数被推送到堆栈上。此调用约定用于对从非托管 DLL 导出的类调用方法。  
5、 Winapi 此成员实际上不是调用约定,而是使用了默认平台调用约定。例如,在 Windows 上默认为 StdCall,在 Windows CE.NET 上默认为 Cdecl。  

#7


明白了  你说的是调用规则是吗?

void* 默认的好像是__stdcall规则  而编译器默认的是__cdecl规则是吗?

但是解决办法呢?

是在C++中加上调用规则 还是 在C#中强调调用规则?

#8


呵呵  你回帖真快  我还没刷新都被你抢了一楼   呵呵  谢谢你  能回答我7楼的问题吗?  最好是帮忙修改下我的代码

#9


在c#中要和c++一直
类似

[DllImport("msvcrt.dll", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.Cdecl)]
public static extern int printf(String format, int i, double d); 

#10


void*   默认的好像是__stdcall规则

跟void 没关系,看你的编译器设置,和显式设置,显式设置覆盖编译器设置。一般是__cdcel.但.net中默认是__stdcall


但是解决办法呢? 

是在C++中加上调用规则   还是   在C#中强调调用规则?


可以都加,,,重要是调用规则要保持一致。

#11


一直=〉一致

#12


mygod  简直像IM了  呵呵

#13


我在我的C#里照9楼的加了规则限制,仍然出错,应该是C++里没有约定的关系把
那么C++中怎么写?  我不懂比如说都约定为__stdcall  就拿我顶楼的代码说

#14


出错信息是什么??????
另外 ,你有没有看过编译后的函数名字。?。编译后的有什么变化? 有没有用extern "C" 禁用名字粉碎?

#15


同事在前面加了CALLBACK,他说这和__stdcall一样

CALLBACK void kinescope(void(* kines)(float x, float y))
{
    D3DXVECTOR3 vec = d3d->GetCamera()->GetPos();
    kines(vec.x, vec.y);
}

然后我设定CallinConvention.StdCall
还是出错!郁闷!

#16


study

#17


To haiwangstar :
      出错信息写在顶楼了,编译后函数的名字?好像没变化啊,我在5楼说是在.def中导出的,这个也是全局函数,没有相应的头文件,所以没有加extern "c"

#18


如果还是顶楼的错误的话,那还是调用约定不对。

#19


我在网上有看到一种方法,换了种调用方式,定义了一个结构,代码如下:

    public delegate void kinds(float x, float y);

    [StructLayout(LayoutKind.Sequential,Pack=1)]
    public struct abc
    {
        public float ax;
        public float by;
        public kinds k;
    };

        [DllImport("skyll_05.dll")]
        public static extern void kinescope([In] ref abc i);

        public static void kds(float x, float y)
        {
            MessageBox.Show(x.ToString() + "+" + y.ToString());
        }

            abc i;
            i.ax = 0.0f;
            i.by = 0.0f;
            i.k = new kinds(kds);
            kinescope(ref i);


这回调试运行到kinescope(ref i)这句时,出现说是“尝试读取或写入受保护的内存。这通常指示其他内存已损坏”,这又是怎么了啊??疯了!是内存布局不同?

#20


错了  上面是C#代码

#21


用vc++.net封装一下再用c#调用,可以解决此问题。

#22


To schumyxp :
    目的不是兼容就行,那样就都用C#写了,更简单,目的就是搞明白托管与非托管之间的互操作

#23


周末来继续!!!

#24


你让你同事用c++ 

loadlibrary 
getprocessaddress 
方式调用??

#25


mark~

#26


#27


就像6楼说的,基本上是签名的问题。另外就是非托管类型于托管类型直接的对应关系的问题。
也许你需要写一些unsafe的代码才可以很好的解决这个问题吧。.net库的于 win32直接就有很多这样的操作,可以参考一下。

#28


lz的问题不是在函数调用方式,而是Marshal数据错误,lz这样试试:

//------------dll中的回调函数--------------- 

void kinescope(void(* kines)(float x, float y))
{
    D3DXVECTOR3 vec = d3d->GetCamera()->GetPos();
    kines(vec.x, vec.y);
}


//----------------C#中调用代码--------------- 


//------------委托声明-------------
public delegate void kinds(float x, float y);

//----------------函数声明---------------
[DllImport("skyll_05.dll")]
static extern void kinescope( IntPtr pKines);

//---------------委托定义-------------------
public static void kds(float x, float y)
{
     MessageBox.Show(x.ToString() + "+" + y.ToString());
}

//------------------调用---------------
kinds kk = new kinds(kds);
IntPtr pKinds = Marshal.GetFunctionPointerForDelegate(kk);
kinescope( pKinds);

#29


学习

#30


非托管代码在回调托管代码时的调用约定不一致的缘故。将非托管代码函数指针定义的调用约定改下就可以了。

#31


或者,你可以修改委托定义时,作为函数指针在被回调时的调用顺序
如下:
//------------委托声明-------------
[UnmanagedFunctionPointer( CallingConvention.Cdecl)]
    public delegate void kinds(float x, float y);

//----------------函数声明---------------
[DllImport("skyll_05.dll")]
static extern void kinescope(kinds k);

//---------------委托定义-------------------
public static void kds(float x, float y)
{
     MessageBox.Show(x.ToString() + "+" + y.ToString());
}

//------------------调用---------------
kinds kk = new kinds(kds);
kinescope(kk);