C# 调用C++代码

目录

一、需求

二、C++项目

1.创建项目

2.C++代码

3.生成DLL

三、C#项目

1.新建项目

2.调用方式一

3.调用方式二

结束


一、需求

平时我们写的 C# 类库,在 Visual Studio 中添加引用,然后调用 DLL 中的方法就好了,但是用C++生成的DLL并不行,一般的项目中,很少用到 C# 调用 C++ 代码的情况,但在上位机,工控行业很常见,视觉,人工智能行业中,算法很多都是 C++ 写的,而上位机大部分都是 C# 开发的,那么这篇文章就从头开始,教你如何从创建一个简单的C++项目,到C#的调用。

二、C++项目

1.创建项目

新建一个C++项目,选择动态链接库

项目的名字就默认的吧

2.C++代码

创建成功后,会自动生成一些默认代码

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:		
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

 这些代码可以直接删除,但是,#include "pch.h"  这个头文件不能删

讲代码替换如下:

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"

//BOOL APIENTRY DllMain( HMODULE hModule,
//                       DWORD  ul_reason_for_call,
//                       LPVOID lpReserved
//                     )
//{
//    switch (ul_reason_for_call)
//    {
//    case DLL_PROCESS_ATTACH:		
//    case DLL_THREAD_ATTACH:
//    case DLL_THREAD_DETACH:
//    case DLL_PROCESS_DETACH:
//        break;
//    }
//    return TRUE;
//}


#include <stdio.h>
#include "stdlib.h"
#include <tchar.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;
}

3.生成DLL

在别的一些帖子,会提示你修改配置类型,那是因为他们创建的项目模板不是动态链接库,我们这里可以忽略这一步

如果你C#项目用的是64位,平台这里,也可以改位64位 

 点击项目生成

打开项目的Debug目录,你会发现,根本没有DLL文件

那是因为生成的DLL不在这里,我们找到项目的根目录,就会发现一个Debug目录

DLL就在这里了

三、C#项目

1.新建项目

接下来,我们就在C#项目中进行调用,我们先创建一个C# 控制台项目,打开项目的Debug文件夹,将刚才生成的C++ DLL 放进去。

接下来写 C# 代码。

2.调用方式一

创建一个 DemoDelegate 类,代码如下:

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

namespace 调用C加加DLL
{
    public class DemoDelegate
    {
        [DllImport("DLL1.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        public static extern int Add(int x, int y);

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

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

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

 程序入口

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

namespace 调用C加加DLL
{
    class Program
    {
        static void Main(string[] args)
        {
            int ret1 = DemoDelegate.Add(6, 6);
            Console.WriteLine(ret1);

            Console.ReadKey();
        }
    }
}

运行后,效果:

可以看到,计算的结果是对的 

3.调用方式二

新建一个类 DllInvoke

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

namespace 调用C加加DLL
{
    public class DllInvoke
    {
        [DllImport("kernel32.dll", SetLastError = true)]
        private extern static IntPtr LoadLibrary(String dllPath);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);

        [DllImport("kernel32.dll", SetLastError = true)]
        private extern static IntPtr GetProcAddress(IntPtr lib, string funcName);

        [DllImport("kernel32.dll", SetLastError = true)]
        private extern static bool FreeLibrary(IntPtr lib);

        private IntPtr hLib;

        public DllInvoke(string dllPath)
        {
            hLib = LoadLibrary(dllPath);
            if (hLib == IntPtr.Zero)
            {
                hLib = LoadLibraryEx(dllPath, IntPtr.Zero, 8);
            }
            if (hLib == IntPtr.Zero)
            {
                throw new Exception(string.Format("LoadLibrary {0} Error {1}!", dllPath, Marshal.GetLastWin32Error()));
            }
        }

        /// <summary>
        /// 获取C++方法对应的C#委托实例
        /// </summary>
        /// <param name="funcName">C++方法名</param>
        /// <param name="type">C#委托</param>
        /// <returns></returns>
        public Delegate GetDelegate(string funcName, Type type)
        {
            IntPtr api = GetProcAddress(hLib, funcName);
            Delegate del = (Delegate)Marshal.GetDelegateForFunctionPointer(api, type);
            return del;
        }

        /// <summary>
        /// 获取C++方法对应的C#委托实例
        /// </summary>
        /// <param name="address">C++函数地址</param>
        /// <param name="type">C#委托</param>
        /// <returns></returns>
        public Delegate GetDelegateFromIntPtr(IntPtr address, Type t)
        {
            if (address == IntPtr.Zero)
                return null;
            else
                return Marshal.GetDelegateForFunctionPointer(address, t);
        }

        /// <summary>
        /// 获取C++方法对应的C#委托实例
        /// </summary>
        /// <param name="address">C++函数地址</param>
        /// <param name="type">C#委托</param>
        /// <returns></returns>
        public Delegate GetDelegateFromInt(Int32 address, Type t)
        {
            if (address == 0)
                return null;
            else
                return Marshal.GetDelegateForFunctionPointer(new IntPtr(address), t);
        }
    }
}

Program.cs

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

namespace 调用C加加DLL
{
    class Program
    {
        [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
        delegate int Add(int mid, int errid);

        static void Main(string[] args)
        {
            //int ret1 = DemoDelegate.Add(6, 6);
            //Console.WriteLine(ret1);

            DllInvoke inv = new DllInvoke("D:\\Project\\C++\\Dll1\\Debug\\DLL1.dll");
            Add add = (Add)inv.GetDelegate("Add", typeof(Add));
            int result = add(2, 2);
            Console.WriteLine(result);

            Console.ReadKey();
        }
    }
}

在上面代码中 Add 委托的上面有一个特性

[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]

主要是为了解决一个错误

“调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配”

原因是C++和C#的数据类型不一致导致的。

结束

如果这个帖子对你有用,欢迎 关注 + 点赞 + 留言,谢谢

end

  • 25
    点赞
  • 131
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

熊思宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值