C#使用C++动态库

今天对通过C#工程来调用C++的dll做了一些尝试,记录一些收获与遇到问题。

C++ dll 代码:

/**my_dll.cpp**/

#include "UserApi.h"
#define PB_API __declspec(dllexport) 

extern "C"
{
	typedef PB_API int(*SumFunc)(int a, int b);
	typedef PB_API int(*MinusFunc)(int a, int b);
	typedef PB_API int(*MultiFunc)(int a, int b);
	typedef PB_API int(*DivideFunc)(int a, int b);
	typedef PB_API struct
	{
		SumFunc sum_func;
		MinusFunc minus_func;
		MultiFunc multi_func;
		DivideFunc divide_func;
	}FuncCallback;

	static FuncCallback* s_func_callback;

	PB_API void register_spi(FuncCallback* func_callback)
	{
		s_func_callback = func_callback;
	}
	PB_API int call_sum(int a, int b)
	{
		return s_func_callback->sum_func(a, b);
	}
	PB_API int call_minus(int a, int b)
	{
		return s_func_callback->minus_func(a, b);
	}
	PB_API int call_multi(int a, int b)
	{
		return s_func_callback->multi_func(a, b);
	}
	PB_API int call_divide(int a, int b)
	{
		return s_func_callback->divide_func(a, b);
	}


	PB_API int sum(int a, int b)
	{
		return a + b;
	}
	PB_API int sum_1(int* a, int* b)
	{
		return *a + *b;
	}
	PB_API int sum_2(SumFunc sum_func, int a, int b)
	{
		return sum_func(a, b);
	}
    PB_API void* create_user_api()
	{
		return new UserApi();
	}
	PB_API int sum_with_api(UserApi* user_api)
	{
		return user_api->m_a + user_api->m_b;
	}
	PB_API int divide_with_api(UserApi* user_api)
	{
		return user_api->m_a / user_api->m_b;
	}
}
/**UserApi.h**/
class UserApi
{
public:
	UserApi();
	~UserApi();
public:
	int m_a;
	int m_b;
};
/**UserApi.cpp**/

#include "UserApi.h"

UserApi::UserApi() :m_a(100), m_b(9)
{
}

UserApi::~UserApi()
{
}

C# 代码:

/**Program.cs**/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace DllTest
{
    [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public delegate int MyFunc(int a, int b);
    struct FuncCallback
    {
        public MyFunc sum_func;
        public MyFunc minus_func;
        public MyFunc multi_func;
        public MyFunc divide_func;
    }
    class Program
    {
        private const string DLL_NAME = "MyDll.dll";
        [DllImport(DLL_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        public static extern int sum(int a, int b);
        [DllImport(DLL_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        public static extern int sum_1(ref int a, ref int b);
        [DllImport(DLL_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        public static extern int sum_2(MyFunc my_sum, int a, int b);

        [DllImport(DLL_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        public static extern void register_spi(IntPtr func_callback);
        [DllImport(DLL_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        public static extern int call_sum(int a, int b);
        [DllImport(DLL_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        public static extern int call_minus(int a, int b);
        [DllImport(DLL_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        public static extern int call_multi(int a, int b);
        [DllImport(DLL_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        public static extern int call_divide(int a, int b);

        [DllImport(DLL_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr create_user_api();
        [DllImport(DLL_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        public static extern int sum_with_api(IntPtr user_api);
        [DllImport(DLL_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        public static extern int divide_with_api(IntPtr user_api);


        static FuncCallback m_func_callback;
        static IntPtr m_func_callback_point;
        static IntPtr m_user_api;
        public static int my_sum(int a, int b)
        {
            return a + b;
        }
        public static int my_minus(int a, int b)
        {
            return a - b;
        }
        public static int my_multi(int a, int b)
        {
            return a * b;
        }
        public static int my_divide(int a, int b)
        {
            return a / b;
        }
        static void Main(string[] args)
        {
            m_func_callback = new FuncCallback();
            m_func_callback.sum_func = new MyFunc(my_sum);
            m_func_callback.minus_func = new MyFunc(my_minus);
            m_func_callback.multi_func = new MyFunc(my_multi);
            m_func_callback.divide_func = new MyFunc(my_divide);
            int size = Marshal.SizeOf(m_func_callback);
            m_func_callback_point = Marshal.AllocHGlobal(size);
            Marshal.StructureToPtr(m_func_callback, m_func_callback_point, true);
            register_spi(m_func_callback_point);

            Console.WriteLine("sum: " + sum(3, 5).ToString());
            int a = 10, b = 20;
            Console.WriteLine("sum_1: " + sum_1(ref a, ref b).ToString());
            Console.WriteLine("sum_2: " + sum_2(m_func_callback.sum_func, 9, 2).ToString());
            Console.WriteLine("call_sum: " + call_sum(1, 2).ToString());
            Console.WriteLine("call_minus: " + call_minus(1, 2).ToString());
            Console.WriteLine("call_multi: " + call_multi(1, 2).ToString());
            Console.WriteLine("call_divide: " + call_divide(1, 2).ToString());

            m_user_api = create_user_api();
            Console.WriteLine("sum_with_api: {0}", sum_with_api(m_user_api));
            Console.WriteLine("divide_with_api: {0}", divide_with_api(m_user_api));


            Console.Read();
        }
    }
}

运行结果:

sum: 8
sum_1: 30
sum_2: 11
call_sum: 3
call_minus: -1
call_multi: 2
call_divide: 0
sum_with_api: 109
divide_with_api: 11

这里只是测试了一下简单的函数,后面有需要其他有代表性的再行补充。

说一下遇到的问题以及需要注意的地方:

1、调用约定,C++中函数默认采用__cdecl的调用约定,而C#中默认则是__stdcall,这里需要统一,可以在C++中使用__stdcall声明函数,或者在C#中导出Dll的时候,使用CallingConvention = CallingConvention.Cdecl参数,指定导出函数的调用约定。

2、函数指针,在各种API中,使用回调函数是一种比较常见的通信方式,C++这里的函数指针在C#中对应的是委托。

3、指针类型参数,这里测试了两种指针类型的参数,int* 与一个结构体指针,对于int* 类型参数,在C#的导出函数中,对应为ref int类型,对于结构类型的指针,在C#导出函数中,对应为IntPtr类型。

4、指针类型返回值,可以通过C++函数返回指向C++对象的指针,C#中使用IntPtr保存,再将该指针作为参数传递给接受该指针类型参数的函数,可以正常调用。

 

GitHub:https://github.com/xunmeng2002/User_C-_Dll_in_C-

 

你可能感兴趣的:(C++,C#,紫云的程序人生)