dll引用小结
一、dll与应用程序
动态链接库(也称为DLL,即为“Dynamic Link Library”的缩写)是Microsoft Windows最重要的组成要素之一,打开Windows系统文件夹,你会发现文件夹中有很多DLL文件,Windows就是将一些主要的系统功能以DLL模块的形式实现。
动态链接库是不能直接执行的,也不能接收消息,它只是一个独立的文件,其中包含能被程序或其它DLL调用来完成一定操作的函数(方法。注:C#中一般称为“方法”),但这些函数不是执行程序本身的一部分,而是根据进程的需要按需载入,此时才能发挥作用。
DLL只有在应用程序需要时才被系统加载到进程的虚拟空间中,成为调用进程的一部分,此时该DLL也只能被该进程的线程访问,它的句柄可以被调用进程所使用,而调用进程的句柄也可以被该DLL所使用。在内存中,一个DLL只有一个实例,且它的编制与具体的编程语言和编译器都没有关系,所以可以通过DLL来实现混合语言编程。DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。
下面列出了当程序使用 DLL 时提供的一些优点:[1]
- 使用较少的资源
当多个程序使用同一个函数库时,DLL 可以减少在磁盘和物理内存中加载的代码的重复量。这不仅可以大大影响在前台运行的程序,而且可以大大影响其他在 Windows 操作系统上运行的程序。 - 推广模块式体系结构
DLL 有助于促进模块式程序的开发。这可以帮助您开发要求提供多个语言版本的大型程序或要求具有模块式体系结构的程序。模块式程序的一个示例是具有多个可以在运行时动态加载的模块的计帐程序。 - 简化部署和安装
当 DLL 中的函数需要更新或修复时,部署和安装 DLL 不要求重新建立程序与该 DLL 的链接。此外,如果多个程序使用同一个 DLL,那么多个程序都将从该更新或修复中获益。当您使用定期更新或修复的第三方 DLL 时,此问题可能会更频繁地出现。
二、Dll的调用
每种编程语言调用DLL的方法都不尽相同,在此只对用C#调用DLL的方法进行介绍。首先,您需要了解什么是托管,什么是非托管。一般可以认为:非托管代码主要是基于win 32平台开发的DLL,activeX的组件,托管代码是基于.net平台开发的。如果您想深入了解托管与非托管的关系与区别,及它们的运行机制,请您自行查找资料,本文件在此不作讨论。
调用DLL非托管函数的一般方法
首先,应该在C#语言源程序中声明外部方法,其基本形式是:
[DLLImport(“DLL文件”)]
修饰符 extern 返回变量类型 方法名称 (参数列表)
其中:
DLL文件:包含定义外部方法的库文件。
修饰符: 访问修饰符,除了abstract以外在声明方法时可以使用的修饰符。
返回变量类型:在DLL文件中你需调用方法的返回变量类型。
方法名称:在DLL文件中你需调用方法的名称。
参数列表:在DLL文件中你需调用方法的列表。
注意:需要在程序声明中使用System.Runtime.InteropServices命名空间。
DllImport只能放置在方法声明上。
DLL文件必须位于程序当前目录或系统定义的查询路径中(即:系统环境变量中Path所设置的路径)。
返回变量类型、方法名称、参数列表一定要与DLL文件中的定义相一致。
若要使用其它函数名,可以使用EntryPoint属性设置,如:
[DllImport("user32.dll", EntryPoint="MessageBoxA")]
static extern int MsgBox(int hWnd, string msg, string caption, int type);
其它可选的 DllImportAttribute 属性:
CharSet 指示用在入口点中的字符集,如:CharSet=CharSet.Ansi;
SetLastError 指示方法是否保留 Win32"上一错误",如:SetLastError=true;
ExactSpelling 指示 EntryPoint 是否必须与指示的入口点的拼写完全匹配,如:ExactSpelling=false;
PreserveSig指示方法的签名应当被保留还是被转换, 如:PreserveSig=true;
CallingConvention指示入口点的调用约定, 如:CallingConvention=CallingConvention.Winapi;
C#例子:
1.启动VS.NET,新建一个项目,项目名称为“import1”,模板为“控制台应用程序”。
代码如下
namespace import1
{
class Program
{
[DllImport("user32.dll", EntryPoint = "MessageBoxA")]
static extern int MsgBox(int hWnd, string msg, string caption, int type);
static void Main(string[] args)
{
MsgBox(0, " 这就是用 DllImport 调用 DLL 弹出的提示框哦! ", " 挑战杯 ", 0x30);
}
}
}
然后运行即可
生成一个自定义的C语言dll
所用平台VisualStudio 2017
新建C++空项目
右键项目--->属性--->选择配置类型为动态库
在头文件和源文件下分别建立test.h和test.c
test.h
__declspec(dllexport) int sum(int a, int b);
test.c
//test.c
#include "test.h"
#include <stdio.h>
int sum(int a, int b) {
return a + b;
}
生成项目,在相应的Debug文件夹下可以找到EasyHelloWorlddll.dll
找不到可以用listary搜索。
然后把该dll拷贝到C#项目文件的exe文件夹内(点击生成之后的Debug或者release文件夹)。
C# Vs中使用C的Dll
新建C#控制台程序ConsoleAppTestDllImport
Program.cs代码如下
using System;
namespace ConsoleAppTestDllImport
{
using System.Runtime.InteropServices;
class Program
{
[DllImport("EasyHelloWorlddll", EntryPoint = "sum", CallingConvention = CallingConvention.Cdecl)]
public static extern int Sum(int a, int b);
static void Main(string[] args)
{
Console.WriteLine(Sum(2,3));
Console.ReadKey();
}
}
其中 EntryPoint="sum"是定义入口函数名,使用这个可以自行在下面修改函数名称,不使用则必须与C中的函数名相同。
这里注意[DllImport("EasyHelloWorlddll", EntryPoint = "sum", CallingConvention = CallingConvention.Cdecl)]
如果调用不加CallingConvertion,容易出现错误
详细见:https://blog.csdn.net/wjeson/article/details/8263335
C++生成Dll
这里只是介绍最简单的生成方法,以后学多了考虑出个合集
同上C建立新工程,只有代码不同
C++ test.h
#pragma once //头文件只编译一次
extern "C" __declspec(dllexport) int __stdcall sum(int n1, int n2);
test.cpp
//test.cpp
#include "test.h"
#include <stdio.h>
int __stdcall sum(int a, int b)
{
return a + b;
}
C#上调用也是一样的
{
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("EasyHelloWorlddll", EntryPoint = "sum")]
public static extern int Sum(int a, int b);
static void Main(string[] args)
{
Console.WriteLine(Sum(2,3));
Console.ReadKey();
}
}
关于32位和64位DLL
因为新版的Unity不支持32位框架dll的导入,于是需要创建64位dll,
创建方式,把之前的改为64位,同时把属性中的exe换为dll。可行
C++ 调用 C++
生成 myfish.h
#pragma once
extern "C" __declspec(dllexport) double __stdcall sum(double delta, double freq, double phase);
生成 myfish.cpp
#include "psm.h"
#include <stdio.h>
#include <math.h>
double __stdcall sum(double delta,double freq ,double phase) {
double res = 100f * sin(delta+freq+ phase ) ;
return res;
}
生成看情况,正常可以x64 Release
生成myfish.dll
新建项目aaa.sln
属性->VC++ 目录->包含目录,包含.h的目录
属性->VC++ 目录->库目录,包含.lib的目录
链接器->输入->附加依赖项->添加myfish.lib
#include <iostream>
#include "myfish.h"
int main()
{
std::cout << "Sum" << sum(7, 8, 0);
std::cout << "Hello World!\n";
system("pause");
}