C++C#相互调函数指针

按照之前对C++函数指针的理解,C++函数指针为内存地址,可以用int或者intptr_t保存地址信息,在需要调用时候再转换成相应的函数指针。委托作为C#的指针实现形式,那么理论上可以用C#的IntPtr类型接C++返回的intptr_t地址后在转换为C#的委托对象。然后通过委托对象执行函数调用。理论上也可以把C#的函数转换成IntPtr传递给C++,C++得到intptr_t后把地址转换成相应的函数指针执行函数调用。

首先实现C++的dll库代码

提供的申明头

#include <string>
using std::string;

//求和
extern "C" __declspec(dllexport) int Add(int &a,int &b); 
//求和1
extern "C" __declspec(dllexport) int Add1(int a, int b);

//两个浮点数相加
extern "C" __declspec(dllexport) double DoubleAdd(double a, double b);

//两个浮点数相减
extern "C" __declspec(dllexport) double DoubleJian(double a, double b);

//得到操作两个数的函数指针函数指针
extern "C" __declspec(dllexport) intptr_t GetTowNumberRes(int funName, intptr_t callBack);

//回调C#Handder的申明,一定要加_stdcall,不然会回调后C#程序退出
int _declspec(dllexport)_stdcall CallBackCSharp(char *);



在这里插入图片描述

实现

#include "MyDLL.h" 
#include <iostream>
using namespace std; 

//求和
int Add(int &a,int &b) 
{ 
  return a+b;  
}  

//求和1
int Add1(int a, int b)
{
    return a + b;
}

//两个浮点数相加
double DoubleAdd(double a,double b)
{
    return a + b;
}

//两个浮点数相减
double DoubleJian(double a, double b)
{
    return a - b;
}

//得到操作两个数的函数指针函数指针
intptr_t GetTowNumberRes(int funName, intptr_t callBack)
{
    decltype(CallBackCSharp)* callHander = NULL;
    if (callBack != 0)
    {
        callHander = (decltype(CallBackCSharp)*)callBack;
    }
    if (callHander != NULL)
    {
        int ret=callHander("C#你好,我是C++,我收到你请求返回指针请求,马上给你取指针!");
    }
    //按类别返回相应函数指针
    if (funName == 1)
    {
        return (intptr_t)DoubleAdd;
    }
    //按类别返回相应函数指针
    else if (funName == 2)
    {
        return (intptr_t)DoubleJian;
    }
    else
    {
        return NULL;
    }
}

在这里插入图片描述

C#调用方面

		/// <summary>
        /// 得到操作两个浮点数的方法指针
        /// </summary>
        /// <param name="hModule"></param>
        /// <param name="lpProcName"></param>
        /// <returns></returns>
        [System.Runtime.InteropServices.DllImport("MyDLL.dll")]
        private static extern IntPtr GetTowNumberRes(int funName, IntPtr callBack);

/// <summary>
        /// 操作两个double返回结果的委托
        /// </summary>
        /// <param name="a">数据a</param>
        /// <param name="b">数据b</param>
        /// <returns>返回值</returns>
        public delegate double GetTowNumberResDelegate(double a, double b);

        /// <summary>
        /// 回调方法
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        public delegate int CallBackCSharpDelegate(string msg);

        /// <summary>
        /// 测试指针和委托
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnPointAndDelegate_Click(object sender, EventArgs e)
        {
            //返回加方法指针
            int caseInt = 1;
            //得到回调委托的指针
            IntPtr callBackHander = Marshal.GetFunctionPointerForDelegate((Delegate)(CallBackCSharpDelegate)ShowInfo);
            //得到操作两个浮点数的指针
            IntPtr hander = GetTowNumberRes(caseInt, callBackHander);
            //转换委托
            GetTowNumberResDelegate callAdd = (GetTowNumberResDelegate)InvokeDllUtil.GetDelegateFromIntPtr(hander, typeof(GetTowNumberResDelegate));
            double num1 = 1.2;
            double num2 = 11.3;
            //调用
            double retResAdd= callAdd(num1, num2);
            //得到减方法指针
            caseInt = 2;
            //得到操作两个浮点数的指针
            IntPtr handerJian = GetTowNumberRes(caseInt, callBackHander);
            //转换委托
            GetTowNumberResDelegate callJian = (GetTowNumberResDelegate)InvokeDllUtil.GetDelegateFromIntPtr(handerJian, typeof(GetTowNumberResDelegate));
            //调用
            double retResJian = callJian(num1, num2);
            MessageBox.Show("DoubleAdd通过方法指针返回:" + retResAdd+ ",DoubleJian通过方法指针返回:" + retResJian);
        }

        /// <summary>
        /// 显示信息的回调方法
        /// </summary>
        /// <param name="msg">信息</param>
        /// <returns></returns>
        public int ShowInfo(string msg)
        {
            labInfo.Text = msg;
            return 1;
        }

封装的工具类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Windows.Forms;

namespace DllInvokTest
{
    ///<summary  NoteObject="Class">
    /// [功能描述:动态调用dll的工具类] <para/>
    /// [创建者:zlz] <para/>
    /// [创建时间:2020年5月12日] <para/>
    ///<说明>
    ///  [说明:动态调用dll的工具类]<para/>
    ///</说明>
    ///<修改记录>
    ///    [修改时间:本次修改时间]<para/>
    ///    [修改内容:本次修改内容]<para/>
    ///</修改记录>
    ///<修改记录>
    ///    [修改时间:本次修改时间]<para/>
    ///    [修改内容:本次修改内容]<para/>
    ///</修改记录>
    ///</summary>
    public class InvokeDllUtil
    {
        /// <summary>
        /// 加载程序集
        /// </summary>
        /// <param name="lpLibFileName"></param>
        /// <returns></returns>
        [System.Runtime.InteropServices.DllImport("kernel32")]
        private static extern long GetLastError();

        /// <summary>
        /// 加载程序集
        /// </summary>
        /// <param name="lpLibFileName"></param>
        /// <returns></returns>
        [System.Runtime.InteropServices.DllImport("kernel32")]
        private static extern IntPtr LoadLibrary(string lpLibFileName);

        /// <summary>
        /// 得到方法地址
        /// </summary>
        /// <param name="hModule"></param>
        /// <param name="lpProcName"></param>
        /// <returns></returns>
        [System.Runtime.InteropServices.DllImport("kernel32")]
        private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);

        /// <summary>
        /// 释放程序集
        /// </summary>
        /// <param name="hLibModule"></param>
        /// <returns></returns>
        [System.Runtime.InteropServices.DllImport("kernel32")]
        private static extern IntPtr FreeLibrary(IntPtr hLibModule);

        ///<summary>
        ///通过非托管函数名转换为对应的委托, by jingzhongrong
        ///</summary>
        ///<param name="dllModule">Get DLL handle by LoadLibrary</param>
        ///<param name="functionName">Unmanaged function name</param>
        ///<param name="t">ManageR type对应的委托类型</param>
        ///<returns>委托实例,可强制转换为适当的委托类型</returns>
        public static Delegate GetFunctionAddress(string fileName, string funName, Type t)
        {
            IntPtr libHandle = IntPtr.Zero;
            try
            {
                //获取函数地址
                libHandle = LoadLibrary(fileName);
                if (libHandle == IntPtr.Zero)
                {
                    long err = GetLastError();
                    throw new Exception("加载程序集异常:" + err);
                }
                IntPtr procAddres = GetProcAddress(libHandle, funName);
                if (procAddres == IntPtr.Zero)
                {
                    long err = GetLastError();
                    throw new Exception("加载程序集里方法异常:" + err);
                }
                else
                {
                    return Marshal.GetDelegateForFunctionPointer(procAddres, t);
                }    
            }
            catch
            {
                return null;
            }
            finally
            {
                if (libHandle != IntPtr.Zero)
                {
                    //释放资源
                    FreeLibrary(libHandle);
                }
            }
        }

        /// <summary>
        /// 将表示函数地址的IntPtr实例转换成对应的委托
        /// </summary>
        /// <param name="address">指针地址</param>
        /// <param name="t">类型</param>
        /// <returns></returns>
        public static Delegate GetDelegateFromIntPtr(IntPtr address, Type t)
        {
            if (address == IntPtr.Zero)
            {
                return null;
            }
            else
            {
                return Marshal.GetDelegateForFunctionPointer(address, t);
            }
                
        }

        /// <summary>
        /// 将表示函数地址的int转换成对应的委托
        /// </summary>
        /// <param name="address">地址</param>
        /// <param name="t">类型</param>
        /// <returns></returns>
        public static Delegate GetDelegateFromIntPtr(int address, Type t)
        {
            if (address == 0)
            {
                return null;
            }
            else
            {
                return Marshal.GetDelegateForFunctionPointer(new IntPtr(address), t);
            }   
        }

        /// <summary>
        /// 动态调用Windows DLL
        /// </summary>
        /// <param name="fileName">Dll文件名</param>
        /// <param name="funName">待调用的函数名</param>
        /// <param name="objParams">函数参数</param>
        /// <param name="returnType">返回值</param>
        /// <returns>调用结果</returns>
        public static object CommonDllInvoke(string fileName, string funName, object[] objParams, Type returnType)
        {
            IntPtr libHandle = IntPtr.Zero;
            try
            {
                //获取函数地址
                libHandle = LoadLibrary(fileName);
                if (libHandle == IntPtr.Zero)
                {
                    long err=GetLastError();
                    throw new Exception("加载程序集异常:"+ err);
                }
                IntPtr procAddres = GetProcAddress(libHandle, funName);
                if (procAddres == IntPtr.Zero)
                {
                    long err = GetLastError();
                    throw new Exception("加载程序集里方法异常:" + err);
                }
                //获取参数类型
                Type[] paramTypes = new Type[objParams.Length];
                for (int i = 0; i < objParams.Length; ++i)
                {
                    paramTypes[i] = objParams[i].GetType();
                }

                //构建调用方法模型
                AssemblyName asembyName = new AssemblyName();
                asembyName.Name = "WinDllInvoke_Assembly";
                AssemblyBuilder asembyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asembyName, AssemblyBuilderAccess.Run);
                ModuleBuilder moduleBuilder = asembyBuilder.DefineDynamicModule("WinDllInvoke");
                MethodBuilder methodBuilder = moduleBuilder.DefineGlobalMethod("InvokeFun", MethodAttributes.Public | MethodAttributes.Static, returnType, paramTypes);

                //获取一个 ILGenerator ,用于发送所需的 IL 
                ILGenerator IL = methodBuilder.GetILGenerator();
                for (int j = 0; j < paramTypes.Length; ++j)
                {
                    //将参数压入堆栈
                    if (paramTypes[j].IsValueType)
                    {
                        //By Value
                        IL.Emit(OpCodes.Ldarg, j);
                    }
                    else
                    {
                        //By Addrsss
                        IL.Emit(OpCodes.Ldarga, j);
                    }
                }

                // 判断处理器类型
                if (IntPtr.Size == 4)
                {
                    IL.Emit(OpCodes.Ldc_I4, procAddres.ToInt32());
                }
                else if (IntPtr.Size == 8)
                {
                    IL.Emit(OpCodes.Ldc_I8, procAddres.ToInt64());
                }
                else
                {
                    throw new PlatformNotSupportedException("不支持的处理器类型!");
                }

                IL.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, returnType, paramTypes);
                // 返回值 
                IL.Emit(OpCodes.Ret);
                moduleBuilder.CreateGlobalFunctions();
                // 取得方法信息 
                MethodInfo methodInfo = moduleBuilder.GetMethod("InvokeFun");
                object retObj= methodInfo.Invoke(null, objParams);
                // 调用方法,并返回其值
                return retObj;
            }
            catch
            {
                return null;
            }
            finally
            {
                if (libHandle != IntPtr.Zero)
                {
                    //释放资源
                    FreeLibrary(libHandle);
                }
            }
        }
    }
}

效果
在这里插入图片描述

基于这个指针概念,双方约定方法申明,传输IntPtr相互调用没问题。试了整整一天才试通,VS调试模式老是报P/Invok不匹配,也是醉了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章