C#调用c++函数

在工控行业中会遇到C#调用C++底层函数的情况,比如机械的运动控制底层提供了C++的DLL,而应用系统是C#语言编写的。

下面尝试编写简单C++函数生成DLL,以及C#调用C++函数

1 简单C++函数编写以及生成DLL

  •     1.1 创建C++项目

  

  • 1.2 目录结
  • 1.3 头文件 stdafx.h 源码就以下 3行
#include <stdio.h>
#include "stdlib.h"
#include <tchar.h>
  • 1.4 函数源码

extern "C" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。而被extern "C"修饰的变量和函数是按照C语言方式编译和连接的。

      __declspec(dllexport)的目的是为了将对应的函数放入到DLL动态库中。

      extern "C" __declspec(dllexport)加起来的目的是为了使用DllImport调用非托管C++的DLL文件。因为使用DllImport只能调用由C语言函数做成的DLL。

#include "stdafx.h"

extern "C" __declspec(dllexport) int Add(int x, int y)
{
 return x + y;
}
extern "C" __declspec(dllexport) int Sub(int x, int y)
{
return x - y;
}
extern "C" __declspec(dllexport) int Multiply(int x, int y)
{
return x * y;
}
extern "C" __declspec(dllexport) int Divide(int x, int y)
{
 return x / y;
 }
  • 1.5 修改项目属性为生成DLL

2. C# 调用C++函数

    2.1 调用方式1 import dll

        2.1.1 定义委托import dll的函数

       调用函数可能报错:“调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配
主要原因是c++和c#类型不一致导致的,解决办法是在委托的Dll import语句中加上这一句:
CallingConvention = CallingConvention.Cdecl

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

namespace Com.Gaaban.Framwork.Common.Cplus
{
    public class DemoDelegate
    {

        [DllImport("CplusDll.dll",CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        public static extern int Add(int x, int y);

        [DllImport("CplusDll.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        public static extern int Sub(int x, int y);

        [DllImport("CplusDll.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        public static extern int Multiply(int x, int y);
 
        [DllImport("CplusDll.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        public static extern int Divide(int x, int y);

    }
}

2.1.2 调用

int ret1 = DemoDelegate.Add(1, 2);

2.2 方式二

   2.2.1 加载DLL及获取函数指针工具类

  

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Runtime.InteropServices;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 
 8 namespace Com.Gaaban.Framwork.Common.Cplus
 9 {
10     public class DllInvoke
11     {
12         [DllImport("kernel32.dll", SetLastError = true)]
13         private extern static IntPtr LoadLibrary(String dllPath);
14 
15         [DllImport("kernel32.dll", SetLastError = true)]
16         static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);
17 
18         [DllImport("kernel32.dll", SetLastError = true)]
19         private extern static IntPtr GetProcAddress(IntPtr lib, string funcName);
20 
21         [DllImport("kernel32.dll", SetLastError = true)]
22         private extern static bool FreeLibrary(IntPtr lib);
23 
24         private IntPtr hLib;
25 
26         public DllInvoke(string dllPath)
27         {
28             hLib = LoadLibrary(dllPath);
29             if (hLib == IntPtr.Zero)
30             {
31                 hLib = LoadLibraryEx(dllPath, IntPtr.Zero, 8);
32             }
33             if (hLib == IntPtr.Zero)
34             {
35                 throw new Exception(string.Format("LoadLibrary {0} Error {1}!", dllPath, Marshal.GetLastWin32Error()));
36             }
37         }
38 
39         /// <summary>
40         /// 获取C++方法对应的C#委托实例
41         /// </summary>
42         /// <param name="funcName">C++方法名</param>
43         /// <param name="type">C#委托</param>
44         /// <returns></returns>
45         public Delegate GetDelegate(string funcName, Type type)
46         {
47             IntPtr api = GetProcAddress(hLib, funcName);
48             Delegate del = (Delegate)Marshal.GetDelegateForFunctionPointer(api, type);
49             return del;
50         }
51 
52         /// <summary>
53         /// 获取C++方法对应的C#委托实例
54         /// </summary>
55         /// <param name="address">C++函数地址</param>
56         /// <param name="type">C#委托</param>
57         /// <returns></returns>
58         public Delegate GetDelegateFromIntPtr(IntPtr address, Type t)
59         {
60             if (address == IntPtr.Zero)
61                 return null;
62             else
63                 return Marshal.GetDelegateForFunctionPointer(address, t);
64         }
65 
66         /// <summary>
67         /// 获取C++方法对应的C#委托实例
68         /// </summary>
69         /// <param name="address">C++函数地址</param>
70         /// <param name="type">C#委托</param>
71         /// <returns></returns>
72         public Delegate GetDelegateFromInt(Int32 address, Type t)
73         {
74             if (address == 0)
75                 return null;
76             else
77                 return Marshal.GetDelegateForFunctionPointer(new IntPtr(address), t);
78         }
79     }
80 }
View Code

2.2.2 声明委托

调用函数可能报错:“调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配
主要原因是c++和c#类型不一致导致的,解决办法是在声明委托前面加上这一句:
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
 delegate int Add(int mid, int errid);

2.2.3 调用函数

DllInvoke inv = new DllInvoke(@"F:\Code\Com.Gaaban.Framwork.UI.Test\Bin\C++\CplusDll.dll");

            Add add = (Add)inv.GetDelegate("Add", typeof(Add));

            int ret = add(1, 2);
View Code

   

猜你喜欢

转载自www.cnblogs.com/gaaban/p/9758049.html