C++ 之动态链接库DLL使用注意事项及C#调用详解

C++ 之动态链接库DLL使用注意事项及C#调用详解

  • 有时候算法开发完成之后需要封装成动态链接库DLL来进行集成,一方面增加了算法or代码的复用或者广泛使用性,另一方面也起了保密的效果
  • 平时封装成DLL之后放到一台新的电脑上会出现问题,所以本文总结一下其中问题及解决方式
  • C#调用DLL这个和C++也有不同之处,至于详细解释,后边慢慢增加
  • 关于C++ 与 C# 之间的数据对应关系,可以参考我的另一篇文章:https://blog.csdn.net/yohnyang/article/details/128355275

1. DLL封装

1.1 封装

  • 当C++开发好了code后,封装可以有两种方式:

      1. VS中新建DLL项目,可以将开发的code复制到新项目中,编译生成DLL。一般新建一个DLL项目后,会自动生成如下几个文件:
        在这里插入图片描述
//**************************************************************************************************
//framework.h
#pragma once

#define WIN32_LEAN_AND_MEAN             // 从 Windows 头文件中排除极少使用的内容
// Windows 头文件
#include <windows.h>

//**************************************************************************************************
// pch.h: 这是预编译标头文件。
// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。
#ifndef PCH_H
#define PCH_H

// 添加要在此处预编译的标头
#include "framework.h"
#endif //PCH_H

//**************************************************************************************************
// pch.cpp: 与预编译标头对应的源文件

#include "pch.h"
#include<iostream>

// 当使用预编译的头时,需要使用此源文件,编译才能成功。

//**************************************************************************************************
// 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;
}

将想要封装的code填充到pch中:

// pch.h: 这是预编译标头文件。
// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。
#ifndef PCH_H
#define PCH_H

// 添加要在此处预编译的标头
#include "framework.h"

extern "C" _declspec(dllexport) int test1(int a, float* output);

#endif //PCH_H

//***********************************************************************************************
// pch.cpp: 与预编译标头对应的源文件

#include "pch.h"
#include<iostream>

// 当使用预编译的头时,需要使用此源文件,编译才能成功。

int test1(int a, float* output)
{
    
    
	output[0] = a;
	printf("test1 ok....");
	return 1;
}
  • 另一种封装方式就是直接在C++项目中包含常用头文件,然后将输出文件有.exe改为.dll,其他属性酌情修改~
#inlcude<Windows.h>

在这里插入图片描述

1.2 查询DLL依赖

  • 打开VS2019目录下自带的命令行窗口,输入命令dumpbin /dependents xx.dll 查询即可:
    在这里插入图片描述

  • 如果调用时无法打开,可以先查询一下依赖,然后在编译电脑中找出相关的.dll复制到目标路径下即可,一般是在C:/ 盘路径下的C:\Windows\System32 文件夹中及环境变量路径下进行 search

    • C++ 调用.dll打不开时会有提示缺少哪个
    • C# 调用.dll打不开时只会提示无法找到.dll

2. 调用

2.1 C++ 调用

  • 新建VS项目,右键项目名称添加新建项 test_0.cpp,编写测试code:
    在这里插入图片描述
/*
* 测试dll
*/
#include <iostream>
#include <Windows.h>

int main()
{
    
    
    typedef int(*testFunc)();
    HMODULE module = LoadLibrary(L".\\Dll1.dll");
    if (module == NULL)
    {
    
    
        std::cout << "加载DemoDLL.dll动态库失败\n" << std::endl;
        return -1;
    }
    else {
    
    
        std::cout << "加载成功...\n";
    }

    testFunc test1 = (testFunc)GetProcAddress(module, "test1");
    test1();

    return 1;
}

2.2 C# 调用

  • 新建VS 中C#空项目,将Dll1.dll放到 .\bin\x64\Release 目录下,然后 coding ex:
    在这里插入图片描述
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace ConsoleApp1
{
    
    
    class Program
    {
    
    
        public class ABC
        {
    
    
            [DllImport(@"Dll1.dll", EntryPoint = "test1", CharSet = CharSet.Auto, ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)]
            public static extern int test1(int a, float[] output);

        }

        static void Main(string[] args)
        {
    
    
            float[] data = new float[10];
            ABC.test1(3,data);
            Console.ReadLine();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/yohnyang/article/details/132271513