一、前言
作为系统级语言,rust可以很容易的和C++一样进行系统级编程,但实际操作后我相信你会遇到相当多的问题。
比如以本文要介绍的windows系统编程为例,由于windows系统底层使用的C/C++语言,导致其大量api的参数都是指针,而rust作为一门安全性极高的语言,对于指针的操作会有极大的限制。
其次就是字符串的问题,windows系统大量api都使用的宽窄字符集,而rust默认使用的是UTF-8编码,这之间就涉及编码转换的问题。
对编码不了解的可以参考这篇文章:编码
而本文就将针对这些问题展开,让你能快速熟悉在rust中使用win api的基本过程。
二、指针
首先第一个麻烦就是指针,rust中也是存在像C/C++那样的指针的,只不过需要使用as
关键字进行显示的强制转换:
在rust中,指针和变量一样分为两种类型,第一种是不可变指针,也就是*const
,另一种为可变指针,为*mut
,其后再跟实际的指针类型,比如这里为i32
类型。
注意这里的可变与不可变针对的都是变量pa
、pb
指向变量的值,比如pa
由于是不可变的,所以你不能更改它所保存的a地址上的值,而pb
由于是可变的,那么可以修改它所指向的b
地址上的值:
设置指针的值同样是通过*pa
这样的形式实现的,但由于指针操作是一件非常不安全的事情,所以你必须要将其包裹在unsafe
中才能编译通过。
从上图就能看出两者的区别,因为pa
指向的不可变值,所以这里就无法为其赋值,但此时pb
却是可以的。
如果你希望修改pa
、pb
本身的值,也就是指针本身所存储的地址,换一个其它变量的地址,那么只需要像普通变量那样在let
后面添加一个mut
关键字即可:
let mut pa=&a as *const i32;
pa=&b as *const i32 //此时就能修改其上存储的地址
除了这些基本的,标准库也专门提供了一个指针库,比如在C/C++中最常见的null
指针,就可以通过下面的代码获取:
看上去这种写法似乎有些奇怪,但实际上就是两个函数而已,第一个为null
函数,也就是获取一个不可变的*const
指针,而第二个为null_mut
函数,用于获取可变的*mut
指针。
只不过由于指针也需要具体的类型,所以这里是通过模板的语法在函数名后面添加::<i32>
的方式来指明它的类型为i32
。
这里需要这样写的因为是我们用不上它们,但在实际写代码过程中,一旦我们用于将其填入某个函数参数中,而函数参数本身已经有类型了,所以即使不写::<>
指明类型,rust编译器也能自动推断出其类型。
除此之外,正如前面所说,Windows系统底层使用的C/C++语言实现,所以自然就不可避免的在某些时候需要用到C/C++中的数据类型。
比如最常见的void
类型指针,在后面的win api编程中你会大量的遇到,而rust本身是没有void
类型的。
不过rust同样提供了库ffi
来解决这个问题:
ffi
库中包含了所有C/C++中的基本数据类型,可以直接使用。
比如我这里想要获取一个c_void
类型的指针,就需要先将其引用类型转换为指针,然后再进一步转换为c_void
类型的指针,这里用as
连续转换了两次,所以看起来有点长,分开看即可。
三、windows
rust中的指针有了前面的基础基本也就够了,字符串的问题放在后面,这里先来聊聊如何在rust中使用win api。
想要在rust中使用win api目前主要有三个crate:
- winapi:网上资料较多,但已经有很久没更新过了,目前用的人最多
- windows:官网维护、使用rust将api封装了一层,使用更简洁,积极更新中
- windows-sys:官方维护、几乎未封装,完全原生的api,积极更新中
如果你现在在网上搜索相关的资料,大部分应该都是使用的winapi这个crate,虽然也能用,但其数年不更新让我感觉还是有点不太稳当。
至于windows-sys,基本就是完全原生的api,和你在C/C++中写的代码差不多,但又由于有rust的安全机制在,导致其用起来相当的麻烦。
所以我一般习惯于使用windows,该库是官方维护的库、持续更新,同时又用rust将其封装了一层,用起来相对要简洁的多。
除了在配置文件的依赖项中添加:
windows = "0.52.0"
你也可以直接运行命令cargo add windows
下载最新的版本。
不过上面两种方式都不推荐,最好是直接复制官方提供的代码模板到配置文件中: