利用dll实现跨语言编程(C语言与C#篇)

一、引言

在学校做了一个遥控车的项目。我选择的是用电脑的键盘控制小车的方向,电脑与蓝牙模块进行通信的遥控方式。于是我找到了一个C语言中的库函数,可以检测按键按下事件:

  • GetAsyncKeyState(int vKey);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
#include <Windows.h>

int main()
{
while (1)
{
if (GetAsyncKeyState(VK_UP))//按方向键上键
{
printf("w");
}
if (GetAsyncKeyState(VK_DOWN))//按方向键下键
{
printf("s");
}
if (GetAsyncKeyState(VK_LEFT))//按方向键左键
{
printf("a");
}
if (GetAsyncKeyState(VK_RIGHT))//按方向键右键
{
printf("d");
}
if (GetAsyncKeyState(VK_SPACE))//按空格退出循环
{
break;
}
}

return 0;
}

可以去试一下这个程序,可见这个函数是非常灵敏的wwwww

然而对于电脑的蓝牙通信,网上只找到了使用C#开发的资料,使用C语言开发蓝牙上位机的实例很少。在C#里我又找不到这种按键检测贼灵敏的函数(也许有吧,但是我没找到)。于是我产生了利用dll实现跨语言编程的想法。

二、准备

  • 软件准备:Visual Studio
  • 什么是dll(动态链接库)
    • dll(动态链接库):
      动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。
      个人理解,dll开放一些接口供外部使用,在运行时,如果有需要则访问这个dll。所以缺少dll文件的话,编译不会出错,一旦需要访问dll的函数,就会出现错误。
    • lib(静态链接库)
      与dll相似,但是lib是参与编译的,缺少lib文件的话编译不会通过。

所以我需要把C语言中的GetAsyncKeyState(int vKey)函数开放一个接口,生成dll文件,然后通过C#来使用这个接口。

三、C语言生成dll文件

  • 打开VS新建工程,选择C++语言,动态链接库(DLL)

新建工程
新建工程

  • 新建源文件 MyFirstDll.c
1
2
3
4
5
6
7
#include <Windows.h>

__declspec(dllexport) int GetKey(int key)
{
int n = GetAsyncKeyState(key);
return n;//这个函数的返回值
}

其中,__declspec(dllexport)不是C语言的关键字,是微软定义的关键字,代表的含义是对外开放这个函数的接口

  • 在上方物品栏中选项目->属性
    属性

  • 配置属性->C/C++->预编译头->不使用预编译头
    预编译头

  • 高级-> 编译为C代码(/TC)
    高级

  • 点击确定

  • 上方物品栏:生成->生成 MyFirstDll
    生成

  • 生成的.dll文件在根目录下/x64/Debug
    dll

四、查看dll文件中的函数

在生成完成dll文件后,需要检查一下dll文件里是否留有我定义的接口
需要用到VC自带的dumpbin指令

  • win+R 输入 cmd打开命令窗口输入指令
  dumpbin /exports (dll的路径)

命令行
命令行

  • 可以看到在这个dll文件里雀食存在一个叫GetKey的函数接口,代表这个dll文件生成成功了

五、C#调用dll的函数

  • VS新建C#项目
    新建C#项目

  • 命名空间

1
using System.Runtime.InteropServices;
  • 定义外部函数
1
2
3
4
5
6
7
8
internal class Program
{
[DllImport("MyFirstDll.dll")]
public static extern int GetKey(int key);
static void Main(string[] args)
{
}
}

注:定义外部函数要在类内定义,并且必须用”static extern”修饰

  • 把刚刚生成的dll文件放入根目录/bin/debug/下
    调用dll

  • 然后直接调用这个函数即可。

  • 上完整程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Runtime.InteropServices;

namespace UseDll
{
internal class Program
{
[DllImport("MyFirstDll.dll")]
public static extern int GetKey(int key);
static void Main(string[] args)
{
while(true)
{
if (Convert.ToBoolean(GetKey(37)))//37是左方向键的标号
{
Console.Write("a");
}
if (Convert.ToBoolean(GetKey(38)))//38是上方向键的标号
{
Console.Write("w");
}
if (Convert.ToBoolean(GetKey(39)))//39是右方向键的标号
{
Console.Write("d");
}
if (Convert.ToBoolean(GetKey(40)))//40是左方向键的标号
{
Console.Write("s");
}
if (Convert.ToBoolean(GetKey(32)))//32是空格键的标号
{
break;
}
}
}
}
}
  • 直接运行发现出现错误
    错误

  • 这是因为dll文件是在64位机上生成的,而上面的配置默认是Any CPU
    CPU

  • 打开上方物品栏的项目->属性
    属性

  • 生成->目标平台->x64
    目标平台

  • 保存,再次运行项目,发现项目正常运行。还是那个熟悉的味道
    结果