C#引用C++dll

参考:C#调用C++的dll方法_c#调用c++dll-CSDN博客

_stdcall与_cdecl区别 (QT 加载MFC的dll时,要注意的"_stdcall"或者CALLBACK的问题)_qt stdcall-CSDN博客

c#调用c++的DLL的实现方法_C#教程_脚本之家  

首先C++dll的制作

不论是使用C++builder还是vs来制作C++的dll,在导出c接口的时候都是要使用__stdcall的方式,即接口导出为:

extern "C" __declspec(dllexport) int __stdcall ExecuteSchTest(int nSchNo, char *pChannelInfo);

同样的对于如果是使用回调函数 的时候,回调函数的定义也要使用__stdcall

//定义带回调函数的接口
extern "C" __declspec(dllexport) int __stdcall ExecuteSchTest(int nSchNo, char *pChannelInfo);
extern "C" __declspec(dllexport) int __stdcall SetProductSpecification(int nSetLength,double* pSetData);
//定义回调函数
typedef bool ( __stdcall *FnPowerOn)(int, double*);
extern "C" __declspec(dllexport) void __stdcall RegisterInterface_PowerOn(FnPowerOn fn);
//定义回调函数
typedef bool ( __stdcall *CallBackReadStdData)(int , double*);
extern "C" __declspec(dllexport) void __stdcall RegisterInterface_ReadStdData(CallBackReadStdData fn);

以上是对于C++接口导出C接口的要求,要求使用__stdcall,否则在C#中调用接口会出现问题( 或者全部都是用_cdecl方式导出c接口方式,同时对应在C#调用时要强调使用_cdecl的方式加载接口,原因参考:_stdcall与_cdecl区别 (QT 加载MFC的dll时,要注意的"_stdcall"或者CALLBACK的问题)_qt stdcall-CSDN博客)

C#写法:

使用windows的LoadLibrary加载 dll文件:

[DllImport("Kernel32", SetLastError = true)]
private static extern int GetProcAddress(int handle, string funcName);

[DllImport("Kernel32", SetLastError = true)]
private static extern int LoadLibrary(string dllPath);

[DllImport("Kernel32", SetLastError = true)]
private static extern int FreeLibrary(int handle);

//普通的接口注册  string---->对应C++dll中的char*
public delegate int ExecuteSchTest(int n, string p);

/*带参数为double数组的接口,double[]---->对应C++dll中的double* ,
*需要注意如果使用数组,需要在使用[In, MarshalAs(UnmanagedType.LPArray, SizeConst = 50)]方式
*说明以下数组的类型和长度,对于如果该形参数组是接口的输入参数,用In来表示参数是输入类型
*In类型是可以省略的,即:[MarshalAs(UnmanagedType.LPArray, SizeConst = 50)],即默认就为In类型,但是如果参数为输出参数,用Out来表示为输出参数,Out是不可省略的
*/
public delegate int SetProductSpecification(int nSetLength, [In, MarshalAs(UnmanagedType.LPArray, SizeConst = 50)]double[] pSetData);

//带参数为回调函数的接口,使用代理的方式  注意如果如果在参数中带有了
public delegate bool CallBackPowerOn(int nSetLength, [In, MarshalAs(UnmanagedType.LPArray, SizeConst = 50)]double[] fSetData);
public static CallBackPowerOn PowerOn = SetPowerOn;
public delegate void RegisterInterface_PowerOn(CallBackPowerOn callback);

//切记此处的SizeConst必须固定与dll中数组长度一致,否则将会导致dll出现异常
/*
*下面回调函数中有一个形参为输出类型,即从C#中注册一个回调接口到C++dll中,C++主动发送一个消息到C#中,而该回调接口中存在一个从c++dll中开辟一个数组空间,C++dll把该数组地址传出给C#,然后由C#修改数据,在接口返回后,c++dll使用该数组数据,即该参数是Out类型
需要注意的一点是:如果是Out数组形参,SizeConst长度必须与C++dll中开辟的数组空间长度一致,如果不一致会导致C++dll栈数据出现问题。
*/
public delegate bool CallBackGetStdData(int nSetLength, [Out, MarshalAs(UnmanagedType.LPArray, SizeConst = 25)]double[] pSetData);
public static CallBackGetStdData GetStdData = ReadStdMeter;
public delegate void RegisterInterface_ReadStdData(CallBackGetStdData fn);

//定义局部变量
private int dllHandle = 0;
public ExecuteSchTest ProExecuteSchTest;
public RegisterInterface_PowerOn ProRegisterInterface_PowerOn;
public SetProductSpecification ProSetProductSpecification;
public RegisterInterface_PowerOn ProRegisterInterface_PowerOn;
public RegisterInterface_ReadStdData ProRegisterInterface_ReadStdData;


private static Delegate GetAddress(int dllModule, string functionname, Type t)
{
    int addr = GetProcAddress(dllModule, functionname);

    if (addr == 0)
         return null;
     else
         return Marshal.GetDelegateForFunctionPointer(new IntPtr(addr), t);
}

public void FreeLinbary()
{
    if (this.dllHandle >= 32)
    {
        FreeLibrary(this.dllHandle);
    }
}

public int LoadDll(string dllPath)
{
    this.dllHandle = LoadLibrary(dllPath);
    if (this.dllHandle >= 32)
    {
        this.ProExecuteSchTest = (ExecuteSchTest)GetAddress(dllHandle, "ExecuteSchTest", typeof(ExecuteSchTest));
        this.ProSetProductSpecification = (SetProductSpecification)GetAddress(dllHandle, "SetProductSpecification", typeof(SetProductSpecification));
        this.ProRegisterInterface_PowerOn = (RegisterInterface_PowerOn)GetAddress(dllHandle, "RegisterInterface_PowerOn", typeof(RegisterInterface_PowerOn));
        this.ProRegisterInterface_ReadStdData = (RegisterInterface_ReadStdData)GetAddress(dllHandle, "RegisterInterface_ReadStdData", typeof(RegisterInterface_ReadStdData));
        double[] fData = { 0, 220, 5, 60, 2000, 4, 4, 50, 0, 0, 32, 1, 0, 0, 0 };
        int a = this.ProSetProductSpecification(15, fData);
        this.ProRegisterInterface_PowerOn(PowerOn);
        this.ProRegisterInterface_ReadStdData(GetStdData);
        string strComm = "111111111";
return 0;
        }
        else
        {
            return 1;
        }
    }
    int nRlt = this.ProExecuteSchTest(10000010, strComm);

    public Form1()
    {
        InitializeComponent();
    }
    
    private void Form1_Load(object sender, EventArgs e)
    {
        string str1 = System.AppDomain.CurrentDomain.BaseDirectory;
        string path = str1 + "\\Test.dll";
        LoadDll(path);
     }  
        public static bool SetPowerOn(int nLength, double[] fData)
        {
            if (nLength != 15)
                return false;
            for (int i = 0; i < nLength; i++)
            {
                double d = fData[i];
            }
            return true;        
        }

    public static bool SetLoop(int nLength)
    {
        return true;
    }

    public static bool ReadStdMeter(int nLength,  double[] pSetData)
    {
        if (nLength != 25)
        {
            return false;
        }
        int nPos = 0;
        for(int i = 0; i < 25; i++)
        {
            pSetData[nPos] = i;
        }
        return true;
    }
}
}

注意:

带参数为double数组的接口,double[]---->对应C++dll中的double* ,
需要注意如果使用数组,需要在使用[In, MarshalAs(UnmanagedType.LPArray, SizeConst = 50)]方式
说明以下数组的类型和长度,对于如果该形参数组是接口的输入参数,用In来表示参数是输入类型
In类型是可以省略的,即:[MarshalAs(UnmanagedType.LPArray, SizeConst = 50)],即默认就为In类型,但是如果参数为输出参数,用Out来表示为输出参数,Out是不可省略的,而且对于Out类型的参数SizeConst必须是要dll中的长度一致,否则会出问题。

遇到的问题:

在使用过程中由于使用C++builder定义回调函数的时候使用了_cdecl, 而导出接口使用了__stdcall,导致在C#中使用调用接口的时候,C++dll中出现问题,排查问题过程中,调试发现接口附近的临时变量,在调用了之后,附近的变量值无缘无故的发生了原因,其根本原因就是_cdecl和__stdcall对于形参的入栈顺序不一致,c++dll中修改为__stdcall之后,解决问题。

参考:_stdcall与_cdecl区别 (QT 加载MFC的dll时,要注意的"_stdcall"或者CALLBACK的问题)_qt stdcall-CSDN博客

相关推荐

  1. C++ 引用

    2024-03-14 15:44:05       55 阅读
  2. <span style='color:red;'>C</span>++<span style='color:red;'>引用</span>

    C++引用

    2024-03-14 15:44:05      51 阅读
  3. C++(引用

    2024-03-14 15:44:05       32 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-14 15:44:05       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-14 15:44:05       106 阅读
  3. 在Django里面运行非项目文件

    2024-03-14 15:44:05       87 阅读
  4. Python语言-面向对象

    2024-03-14 15:44:05       96 阅读

热门阅读

  1. CDQ分治+树状数组,LOJ6270. 数据结构板子题

    2024-03-14 15:44:05       43 阅读
  2. SpringBoot线程池的使用和扩展

    2024-03-14 15:44:05       34 阅读
  3. 力扣80:删除排序数组中的重复项 II

    2024-03-14 15:44:05       41 阅读
  4. AI辅助研发:2024年科技与工业领域的新革命

    2024-03-14 15:44:05       44 阅读
  5. Redis的快速入门【全方位进攻】

    2024-03-14 15:44:05       44 阅读
  6. mac笔记本执行定时任务

    2024-03-14 15:44:05       53 阅读
  7. Rust 的 inline 内联编译策略

    2024-03-14 15:44:05       41 阅读
  8. Elastic script_score的使用

    2024-03-14 15:44:05       45 阅读