c#调用c++ dll 中函数,然后c++回调c# ,结果把c#程序结束了怎么回事

时间:2021-10-05 20:29:38
c#调用 c++的函数,参数是结构体指针,
这个结构体里有个函数指针,无参;
在c++ 程序里得到函数后通过结构体回调这个函数指针(不过此时把函数指针强制转换为带参数的)
然后我在运行c# 程序,执行到c++的那个函数,执行异常,执行完之后关闭整个程序。
我不知道这个是否回收机制的问题,难道此时的结构体已经被释放,
请求大神们指教一下

14 个解决方案

#1


一个无参的函数指针强制转换成有参数函数指针执行,必然会出错。这有什么问题?

#2


不要做A语言代码修改为B语言代码的无用功。
也不要做用A语言代码直接调用B语言代码库这样复杂、这样容易出错的傻事。
只需让A、B语言代码的输入输出重定向到文本文件,或修改A、B语言代码让其通过文本文件输入输出。
即可很方便地让A、B两种语言之间协调工作。
比如:
A将请求数据写到文件a.txt,写完后改名为aa.txt
B发现aa.txt存在时,读取其内容,调用相应功能,将结果写到文件b.txt,写完后删除aa.txt,改名为bb.txt
A发现bb.txt存在时,读取其内容,读完后删除bb.txt
以上A可以替换为任何一种开发语言或开发环境,B可以替换为任何一种与A不同的开发语言或开发环境。
除非A或B不支持判断文件是否存在、文件读写和文件更名。
但是谁又能举出不支持判断文件是否存在、文件读写和文件更名的开发语言或开发环境呢?

共享临时文本文件这种进程之间的通讯方法相比其它方法的优点有很多,下面仅列出我现在能想到的:
·进程之间松耦合
·进程可在同一台机器上,也可跨机,跨操作系统,跨硬件平台,甚至跨国。
·方便调试和监视,只需让第三方或人工查看该临时文本文件即可。
·方便在线开关服务,只需删除或创建该临时文本文件即可。
·方便实现分布式和负载均衡。
·方便队列化提供服务,而且几乎不可能发生队列满的情况(除非硬盘空间满)
·……

#3


谢谢回答,我想还是帮我看下代码吧
我现在需要在c# 程序中把从c++ 得到的数据得到。
对于共享临时文本文件,我估计可能用不到。
c++ 的dll
下面是我的c++dll code
在testdll.h 文件中
typedef int ( _stdcall *API)();
typedef  struct   twi_context_
{
long   callback_en;
API edriver_mm_comm_protocol;
API edriver_eui_mdio_comm_protocol;
API edriver_eui_i2c_comm_protocol;
API edriver_eui_gpio_comm_protocol;
char* edriver_comm_protocol_data;
char* api_message;
}twi_context ;

extern "C" {
_declspec(dllexport) long  _stdcall edriver_mem_api(twi_context_ * twi_context);
};

在testdl.cpp 文件中
typedef long (_stdcall *API_comm_protocol)(twi_context_ * , long);
typedef long (_stdcall *API_eui_mdio_comm_protocol)(twi_context_ *, long);
typedef long (_stdcall *API_eui_i2c_comm_protocol)(twi_context_ *, long);
typedef long (_stdcall *API_eui_gpio_comm_protocol)(twi_context_ *, long);
long  _stdcall cpp_comm_protocol(twi_context_ * twi_context, long flag)
{

long  value = ((API_comm_protocol)(twi_context->edriver_mm_comm_protocol))(twi_context, flag);
return  value ;

}
long  _stdcall cpp_eui_gpio_protocol(twi_context_ * twi_context, long flag)
{

((API_eui_mdio_comm_protocol)(twi_context->edriver_eui_gpio_comm_protocol))(twi_context, flag);
return 4;
}
long  _stdcall cpp_eui_mdio_protocol(twi_context_ * twi_context, long flag)
{
((API_eui_gpio_comm_protocol)(twi_context->edriver_eui_mdio_comm_protocol))(twi_context, flag);
return 4;
}
long  _stdcall cpp_eui_i2c_protocol(twi_context_ * twi_context, long flag)
{
((API_eui_i2c_comm_protocol)(twi_context->edriver_eui_i2c_comm_protocol))(twi_context, flag);
return 4;
}
long _stdcall edriver_mem_api(twi_context_ * twi_context)
{
long flag = cpp_comm_protocol( twi_context, 1);
cpp_eui_i2c_protocol(twi_context,2);
cpp_eui_mdio_protocol(twi_context,3);
cpp_eui_gpio_protocol(twi_context,4);
cin.get();
return 1;
}

然后我在c# 程序里调用
namespace Test_Form
{
    public partial class Form1 : Form
    {
        //[UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.StdCall)]
         [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate int Func_delegate();
         [StructLayout(LayoutKind.Sequential)]
         public struct MyData
         {
             public int callback_en;
             public Func_delegate edriver_mm_comm_protocol;
             public Func_delegate edriver_eui_mdio_comm_protocol;
             public Func_delegate edriver_eui_i2c_comm_protocol;
             public Func_delegate edriver_eui_gpio_comm_protocol;
             public string edriver_comm_protocol_data;
             public string api_message;
         }
        [DllImport("MyDll.dll", EntryPoint = "edriver_mem_api")]
        public static extern int edriver_mem_api(ref MyData my_struct);
        public Form1()
        {
            InitializeComponent();           
        }

        private void button1_Click(object sender, EventArgs e)
        {
            MyData Str_Test = new MyData();
            Str_Test.callback_en = 1;
            string data = "temp data";
            string msg = " send message";
            Str_Test.edriver_comm_protocol_data = data;
            Str_Test.api_message = msg;

            //Func_delegate del_mm_comm = edriver_mm_comm_protocol;
       
            Func_delegate del_eui_mdio = edriver_eui_mdio_comm_protocol;
            Func_delegate del_eui_i2c = edriver_eui_i2c_comm_protocol;
            Func_delegate del_eui_gpio = edriver_eui_gpio_comm_protocol;
            Func_delegate del_mm_comm = edriver_mm_comm_protocol;
            

            Str_Test.edriver_mm_comm_protocol = del_mm_comm;
            Str_Test.edriver_eui_mdio_comm_protocol = del_eui_mdio;
            Str_Test.edriver_eui_i2c_comm_protocol = del_eui_i2c;
            Str_Test.edriver_eui_gpio_comm_protocol = del_eui_gpio;

            try

                 int value = edriver_mem_api(ref Str_Test);
                if (value == 1)
                {

                }
                else
                {

                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        public  int edriver_mm_comm_protocol()
        {
            MessageBox.Show("edriver_mm_comm_protocol is triggered");          
            return 1;
        }
        public  int edriver_eui_mdio_comm_protocol()
        {
            MessageBox.Show("edriver_eui_mdio_protocol is triggered");          
            return 1;
        }
        public  int edriver_eui_i2c_comm_protocol()
        {
            MessageBox.Show("edriver_eui_i2c_comm_protocol is triggered");       
            return 1;
        }
        public  int edriver_eui_gpio_comm_protocol()
        {
            MessageBox.Show("edriver_eui_gpio_comm_protocol is triggered");          
            return 1;
        }

    }
}



然后我现在的程序时执行 int value = edriver_mem_api(ref Str_Test);之后程序直接关闭,
根本不会跳到if(value == 1)
{} 

我猜测是执行了long flag = cpp_comm_protocol( twi_context, 1);//c++ 里面的
后面的三条没执行,但是这条执行的也不对,它执行时把c#的程序给关掉了。
因为c++的dll已经写好了,我需要测试这种调用方法对不对,
我该怎么办,实在想不出来哪里出了问题。。。

#4


把给你写 C++ DLL 的家伙打一顿。

你的 C# 程序必须按照 C++ 的代码来写。
让提供 C++ DLL 的人用 C++ 写一个调用程序,你会发现应该怎样写。

#5


我自己写了一个c++程序 ,运行没有问题,。打一顿就算啦

#6


那你定义的那些回调函数都是 无参数的吗?

#7


你是指我写的那个c++程序吗?我没有用load dll的方式
只是用了同样的code去写了个测试。

#8


在c++ 程序中也是回调,把结构体中成员无参函数转换有参函数,然后在主程序中回调四个,没有程序关掉的问题,运行也正常。

#9


二楼又来混经验了。。。

#10


这个问题我解决掉了,就是c# 中要定义两个委托,一个有参,一个无参,但是结构体传递的成员必须是有参数的,
要不然回调回来找不到,这样其实传递的两个结构体是不太匹配的,
但是调通了,而且程序运行没问题,我也凌乱了,生怕再有什么隐藏的问题

#11


引用 9 楼 zxh707wk 的回复:
二楼又来混经验了。。。

不来混经验才奇怪吧。 c#调用c++ dll 中函数,然后c++回调c# ,结果把c#程序结束了怎么回事

#12


引用 6 楼 Saleayas 的回复:
那你定义的那些回调函数都是 无参数的吗?

dll中把无参的回调函数转换成了有参的回调函数
然后我在c# 程序里,也必须定义两种,一种有参的,一种无参的,但是传递结构体的
时候必须传有参的,
但是dll 接收时那边定义的结构体是无参的啊,这不是不对应了吗,
但是程序通了,我搞不懂了啊

#13


引用 11 楼 zhao4zhong1 的回复:
Quote: 引用 9 楼 zxh707wk 的回复:

二楼又来混经验了。。。

不来混经验才奇怪吧。 c#调用c++ dll 中函数,然后c++回调c# ,结果把c#程序结束了怎么回事

嗯哪,你们分享一下已有的经验吧

#14


引用 13 楼 u014157697 的回复:
Quote: 引用 11 楼 zhao4zhong1 的回复:

Quote: 引用 9 楼 zxh707wk 的回复:

二楼又来混经验了。。。

不来混经验才奇怪吧。 c#调用c++ dll 中函数,然后c++回调c# ,结果把c#程序结束了怎么回事

嗯哪,你们分享一下已有的经验吧

二楼内容就是十几年的经验啊。

#1


一个无参的函数指针强制转换成有参数函数指针执行,必然会出错。这有什么问题?

#2


不要做A语言代码修改为B语言代码的无用功。
也不要做用A语言代码直接调用B语言代码库这样复杂、这样容易出错的傻事。
只需让A、B语言代码的输入输出重定向到文本文件,或修改A、B语言代码让其通过文本文件输入输出。
即可很方便地让A、B两种语言之间协调工作。
比如:
A将请求数据写到文件a.txt,写完后改名为aa.txt
B发现aa.txt存在时,读取其内容,调用相应功能,将结果写到文件b.txt,写完后删除aa.txt,改名为bb.txt
A发现bb.txt存在时,读取其内容,读完后删除bb.txt
以上A可以替换为任何一种开发语言或开发环境,B可以替换为任何一种与A不同的开发语言或开发环境。
除非A或B不支持判断文件是否存在、文件读写和文件更名。
但是谁又能举出不支持判断文件是否存在、文件读写和文件更名的开发语言或开发环境呢?

共享临时文本文件这种进程之间的通讯方法相比其它方法的优点有很多,下面仅列出我现在能想到的:
·进程之间松耦合
·进程可在同一台机器上,也可跨机,跨操作系统,跨硬件平台,甚至跨国。
·方便调试和监视,只需让第三方或人工查看该临时文本文件即可。
·方便在线开关服务,只需删除或创建该临时文本文件即可。
·方便实现分布式和负载均衡。
·方便队列化提供服务,而且几乎不可能发生队列满的情况(除非硬盘空间满)
·……

#3


谢谢回答,我想还是帮我看下代码吧
我现在需要在c# 程序中把从c++ 得到的数据得到。
对于共享临时文本文件,我估计可能用不到。
c++ 的dll
下面是我的c++dll code
在testdll.h 文件中
typedef int ( _stdcall *API)();
typedef  struct   twi_context_
{
long   callback_en;
API edriver_mm_comm_protocol;
API edriver_eui_mdio_comm_protocol;
API edriver_eui_i2c_comm_protocol;
API edriver_eui_gpio_comm_protocol;
char* edriver_comm_protocol_data;
char* api_message;
}twi_context ;

extern "C" {
_declspec(dllexport) long  _stdcall edriver_mem_api(twi_context_ * twi_context);
};

在testdl.cpp 文件中
typedef long (_stdcall *API_comm_protocol)(twi_context_ * , long);
typedef long (_stdcall *API_eui_mdio_comm_protocol)(twi_context_ *, long);
typedef long (_stdcall *API_eui_i2c_comm_protocol)(twi_context_ *, long);
typedef long (_stdcall *API_eui_gpio_comm_protocol)(twi_context_ *, long);
long  _stdcall cpp_comm_protocol(twi_context_ * twi_context, long flag)
{

long  value = ((API_comm_protocol)(twi_context->edriver_mm_comm_protocol))(twi_context, flag);
return  value ;

}
long  _stdcall cpp_eui_gpio_protocol(twi_context_ * twi_context, long flag)
{

((API_eui_mdio_comm_protocol)(twi_context->edriver_eui_gpio_comm_protocol))(twi_context, flag);
return 4;
}
long  _stdcall cpp_eui_mdio_protocol(twi_context_ * twi_context, long flag)
{
((API_eui_gpio_comm_protocol)(twi_context->edriver_eui_mdio_comm_protocol))(twi_context, flag);
return 4;
}
long  _stdcall cpp_eui_i2c_protocol(twi_context_ * twi_context, long flag)
{
((API_eui_i2c_comm_protocol)(twi_context->edriver_eui_i2c_comm_protocol))(twi_context, flag);
return 4;
}
long _stdcall edriver_mem_api(twi_context_ * twi_context)
{
long flag = cpp_comm_protocol( twi_context, 1);
cpp_eui_i2c_protocol(twi_context,2);
cpp_eui_mdio_protocol(twi_context,3);
cpp_eui_gpio_protocol(twi_context,4);
cin.get();
return 1;
}

然后我在c# 程序里调用
namespace Test_Form
{
    public partial class Form1 : Form
    {
        //[UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.StdCall)]
         [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate int Func_delegate();
         [StructLayout(LayoutKind.Sequential)]
         public struct MyData
         {
             public int callback_en;
             public Func_delegate edriver_mm_comm_protocol;
             public Func_delegate edriver_eui_mdio_comm_protocol;
             public Func_delegate edriver_eui_i2c_comm_protocol;
             public Func_delegate edriver_eui_gpio_comm_protocol;
             public string edriver_comm_protocol_data;
             public string api_message;
         }
        [DllImport("MyDll.dll", EntryPoint = "edriver_mem_api")]
        public static extern int edriver_mem_api(ref MyData my_struct);
        public Form1()
        {
            InitializeComponent();           
        }

        private void button1_Click(object sender, EventArgs e)
        {
            MyData Str_Test = new MyData();
            Str_Test.callback_en = 1;
            string data = "temp data";
            string msg = " send message";
            Str_Test.edriver_comm_protocol_data = data;
            Str_Test.api_message = msg;

            //Func_delegate del_mm_comm = edriver_mm_comm_protocol;
       
            Func_delegate del_eui_mdio = edriver_eui_mdio_comm_protocol;
            Func_delegate del_eui_i2c = edriver_eui_i2c_comm_protocol;
            Func_delegate del_eui_gpio = edriver_eui_gpio_comm_protocol;
            Func_delegate del_mm_comm = edriver_mm_comm_protocol;
            

            Str_Test.edriver_mm_comm_protocol = del_mm_comm;
            Str_Test.edriver_eui_mdio_comm_protocol = del_eui_mdio;
            Str_Test.edriver_eui_i2c_comm_protocol = del_eui_i2c;
            Str_Test.edriver_eui_gpio_comm_protocol = del_eui_gpio;

            try

                 int value = edriver_mem_api(ref Str_Test);
                if (value == 1)
                {

                }
                else
                {

                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        public  int edriver_mm_comm_protocol()
        {
            MessageBox.Show("edriver_mm_comm_protocol is triggered");          
            return 1;
        }
        public  int edriver_eui_mdio_comm_protocol()
        {
            MessageBox.Show("edriver_eui_mdio_protocol is triggered");          
            return 1;
        }
        public  int edriver_eui_i2c_comm_protocol()
        {
            MessageBox.Show("edriver_eui_i2c_comm_protocol is triggered");       
            return 1;
        }
        public  int edriver_eui_gpio_comm_protocol()
        {
            MessageBox.Show("edriver_eui_gpio_comm_protocol is triggered");          
            return 1;
        }

    }
}



然后我现在的程序时执行 int value = edriver_mem_api(ref Str_Test);之后程序直接关闭,
根本不会跳到if(value == 1)
{} 

我猜测是执行了long flag = cpp_comm_protocol( twi_context, 1);//c++ 里面的
后面的三条没执行,但是这条执行的也不对,它执行时把c#的程序给关掉了。
因为c++的dll已经写好了,我需要测试这种调用方法对不对,
我该怎么办,实在想不出来哪里出了问题。。。

#4


把给你写 C++ DLL 的家伙打一顿。

你的 C# 程序必须按照 C++ 的代码来写。
让提供 C++ DLL 的人用 C++ 写一个调用程序,你会发现应该怎样写。

#5


我自己写了一个c++程序 ,运行没有问题,。打一顿就算啦

#6


那你定义的那些回调函数都是 无参数的吗?

#7


你是指我写的那个c++程序吗?我没有用load dll的方式
只是用了同样的code去写了个测试。

#8


在c++ 程序中也是回调,把结构体中成员无参函数转换有参函数,然后在主程序中回调四个,没有程序关掉的问题,运行也正常。

#9


二楼又来混经验了。。。

#10


这个问题我解决掉了,就是c# 中要定义两个委托,一个有参,一个无参,但是结构体传递的成员必须是有参数的,
要不然回调回来找不到,这样其实传递的两个结构体是不太匹配的,
但是调通了,而且程序运行没问题,我也凌乱了,生怕再有什么隐藏的问题

#11


引用 9 楼 zxh707wk 的回复:
二楼又来混经验了。。。

不来混经验才奇怪吧。 c#调用c++ dll 中函数,然后c++回调c# ,结果把c#程序结束了怎么回事

#12


引用 6 楼 Saleayas 的回复:
那你定义的那些回调函数都是 无参数的吗?

dll中把无参的回调函数转换成了有参的回调函数
然后我在c# 程序里,也必须定义两种,一种有参的,一种无参的,但是传递结构体的
时候必须传有参的,
但是dll 接收时那边定义的结构体是无参的啊,这不是不对应了吗,
但是程序通了,我搞不懂了啊

#13


引用 11 楼 zhao4zhong1 的回复:
Quote: 引用 9 楼 zxh707wk 的回复:

二楼又来混经验了。。。

不来混经验才奇怪吧。 c#调用c++ dll 中函数,然后c++回调c# ,结果把c#程序结束了怎么回事

嗯哪,你们分享一下已有的经验吧

#14


引用 13 楼 u014157697 的回复:
Quote: 引用 11 楼 zhao4zhong1 的回复:

Quote: 引用 9 楼 zxh707wk 的回复:

二楼又来混经验了。。。

不来混经验才奇怪吧。 c#调用c++ dll 中函数,然后c++回调c# ,结果把c#程序结束了怎么回事

嗯哪,你们分享一下已有的经验吧

二楼内容就是十几年的经验啊。