RegameDesk 开发笔记【4】C++

C++ 20

根据 jetbrains 的统计,2023 年时,C++ 20 的使用率是 29%,仅次于 C++ 17 的 43%。在嵌入式领域和游戏开发领域,C++ 20 的使用率更高,分别为 37% 和 39%。

众所周知,C++ 版本越高,就越强大,并且解决以前版本的一些问题。讲道理的话,现在 2025 年就应该用 C++ 20。不用的人,大致理由都是成本问题。其实,大可以把工程的版本设置为 C++ 20,然后按照已经学会的版本去用。这种情况下,遇到问题,通常就是遇到旧版本的不足或过时的部分。按照稣的经验,目前主流大语言模型对 C++ 20 的支持是不错的,尤其是语法和标准库使用上。

参考:

CppCoreGuidelines

这是由 C++ 之父 Bjarne Stroustrup 领导的行业巨佬们写的宝典,对于写好工程有巨大帮助。有些初学者,买了工具书学完语法,就开始写代码,然后就会遇到一个典型的问题:代码写多了就乱,乱到一定程度自己都不想继续。即使是更好点的情况,靠毅力把代码写到上线,后面却发现乱得自己都不想维护。CppCoreGuidelines 就是用来解决这类问题的。

入门 CppCoreGuidelines 的第一步是:把 GSL 用起来。

参考:

第二步是:记住不要写 STUPID 的代码。仔细看看 CppCoreGuidelines,里面有具体规则。

  • Singleton - 单例(I.3: 避免使用单例)

  • Tight Coupling - 紧密耦合(C: 类和类层次)

  • Untestability - 不可测试(P.12: 适当采用支持工具、P.13: 适当采用支持程序库)

  • Premature Optimization - 过早优化(Per.1: 请勿进行无理由的优化、Per.2: 请勿进行不成熟的优化、Per.3: 请勿对非性能关键的代码进行优化)

  • Indescriptive Naming - 非描述性命名(P.3: 表达你的设计意图)

  • Duplication - 重复代码(ES.3: 避免重复(DRY),避免冗余代码)

RegameDesk 开发笔记【3】Boost 1.88.0

前言

今天是 2025-02-20,Boost 1.88.0 还没发布,但目前的版本在实现 RegameDesk 时遇到一些问题,导致使用了不优雅的解决方案,按照稣和作者们的沟通,下个版本都能解决,所以稣认为 1.88.0 才是适合远程桌面​的 Boost 版本。

Why Boost?

在开发过程中,选择一种基础库或库的集合(注:实际上 Boost 是一个集合)几乎是必然的。如果您的产品使用了 Qt,那么许多基础功能很可能会直接借助 Qt 来实现。有些项目可能会选择 Google 的代码作为基础,从而引入 Abseil。甚至还有一些团队会单独提取 Chromium 的 base 模块来使用。

稣曾遇到过有人推荐使用 Folly,而 Folly 本身也依赖于 Boost。如果您能够接受 Folly,那么当团队不再使用它时,接受 Boost 也应该不是问题。

以上例子其实暗示了一个事实:C++ 标准库的功能相对有限,难以满足实际项目的需求。为了解决这一问题,开发团队通常会选择引入第三方库。在选择第三方库时,主要有两种思路:一种是引入一个功能强大且尽可能全面的大型库(集合),再配合少数其它必要的小型库,以最大化地保持代码风格的一致性;另一种则是引入多个专门解决特定需求的小型库,这种方式虽然可以让每个库都“小而美”,但可能会导致代码风格的不一致。选择哪种方式其实很简单——选择您最熟悉的那一种。

注意!上一段说的“小而美”,从整体上看,可能是假象,尤其当项目很大、成员较多时。当然您可以提出 Chromium 来反驳,不过您需要一定实力和精力去驾驭,所以稣只是说“可能”!

对稣来说,Boost 是一个显而易见的选择。它经过多年的发展,积累了丰富的文档和社区讨论,学习和使用难度都不大。过去有人抱怨 Boost 的编译时间过长,现在早已不存在。而且在如今普遍配备 64GB 内存、高速 SSD 的开发环境中,加载大量头文件已经不再是问题,怪罪 Boost 使工程加载变慢的人也能放心了。

片面地安利 Boost

使用 Boost 相比自己实现而言,有以下好处​:

  1. ​开发更快;

  2. 运行更快;

  3. 运行更稳​;

  4. 代码可读性更高。

后面会举真实例子说明为啥是和自己实现比!先说事实,以上三条总有 1~2 条符合,甚至对于某些团队——可能是中 3~4 条。

首先,开发更快,可能是最有争议的,很多人会反驳说——光学它就要很多时间​。这要是放在以前,稣可能想不出啥好招给这部分人洗脑,现在有大语言模型,各种辅助手段,如果还这么说,完全也不用去反驳,没必要了。

另外,Boost 有部分库确实性能不行,并且官方文档也是明说的,这种情况是求稳定,比如 Boost.Format​。

剩下的,Boost 通常有十分优秀的性能,除非极端的具体领域优化,不然大多数人能把性能写赢 Boost 的概率几乎是 1%(多给 1 分,怕您是真大佬!)。举个例子,Boost.JSON 的性能是高于 RapidJSON 的。RapidJSON 这名字起得好(快),不一定就是真的好(快)。

等等,第 4 条是怎么回事?有些人会说他自己手撸的更好理解,如何反驳?嗯,很可能只是对于作者本人才更好理解,别人看都不想看(笑)。作为团队合作的产物,更多共同点才是好的。比如说,整个团队都熟悉 STL,那么基于 STL 的接口/实现就不会差,而把它等价地改为基于某个第三方库,如果没有强力的理由,通常会被(不用脑地)认为不好。自己写的,对别人来说,何尝不是一种“第三方”?显然,大家普遍认可的“第三方”才可能是更好沟通的,更好达成共识的。(注:这里的“大家”是普遍意义上的大家,不是说某个团队里的少数几名成员,毕竟有的团队就两名写代码的,并不存在“多数”和“少数”!)

例子

大家最喜欢的案例分析来了……稣正好遇到这样一个活生生的例子:实现一个 IPC​ 用于 Service 和工作进程之间通信。

这个故事发生在雪蛤油打工时。一开始,稣就打算使用 Boost 封装的 Pipe,因为以前干过类似的活,​有成功案例。但不幸的是,稣是第二个加入团队的,原来已经有人弄过一个实现,纯手撸的,基于共享内存和事件通知​。关键是,大佬说这实现在他前公司用了 2 年很稳定​。稣想了一下,虽然自己的实现代码量不到它的 1/5,但只接受了几周的考验​。还是别冒险,于是那份手撸版本上​了生产。

然后有一个周末,没回家,随手拿 example 改改对比性能,使用 Boost 的版本速度居然是那手撸实现的 4 倍!在应用层,共享内存是 Windows 上最快的 IPC 机制没错,但它需要其它内核对象的辅助,最终完成时速度就拖慢了。Boost 使用 Pipe 作为 IPC 机制,而没用共享内存和事件通知复合实现,很可能作者是知道这门道的​。

对了,每次发送多少字节对性能是有影响的,如果您打算测试,需要对不同大小的信息进行测试,不能用固定的大小,以免得出不全面的​结论。

参考

来自 CppCoreGuidelines 的一节:

  • ES.1: 优先采用标准库而不是其他的库或者“手工自制代码”

总结

本文,乃至本系列文章,重点在于心法,并没打算详细介绍项目里用了 Boost 具体哪些类库。因为很简单的道理:您要的功能,如果 Boost 有,考虑用它即可。

看到这里,如果您还记得稣写的基于 Boost 的 IPC 代码,它又多接受了三个月的考验——极其稳定,重点是它用起来简单多了。

声音共享 SoundShare

故事 The Story

SoundShare 是 RegameDesk 开发过程中的副产品。

SoundShare is a by-product of the development process of RegameDesk.

在金山云开发鎏光时,领导给了两个设定:仅用于边缘网络、示范性实现。所以没有使用当时很火的 WebRTC,直接使用 WebSocket 传输音视频裸流。可想而知,在很多场景下,其音频的效果是不好的。

During the development of Liuguang at Kingsoft Cloud, the leadership provided two directives: it should be designed only for edge networks and serve as a demonstrative implementation. Consequently, instead of utilizing the then-popular WebRTC, I directly employed WebSocket to transmit raw audio and video streams. As one might expect, the audio quality was suboptimal in many scenarios.

后来,在雪蛤油开发远程桌面产品时,稣便发现使用 RTC SDK 能有效地改善音频的效果。但是这个 RTC SDK 是公司其它团队开发的,而且开发者人数众多,想必是个大型库。稣的重心放在除了传输协议之外的其余部分,对 WebRTC 可以说一知半解。

Later, while developing a remote desktop product at Xuehayou, I discovered that employing an RTC SDK could significantly enhance audio quality. However, this RTC SDK was developed by another team within the company, and given the large number of developers involved, it was presumably a substantial library. My focus was primarily on aspects other than the transmission protocol, and thus, my understanding of WebRTC was, at best, rudimentary.

现在,既然要做自己的远程桌面产品,就不得不亲自面对 WebRTC。当然,前公司的 RTC SDK 肯定是不能用的,因为它以 Google WebRTC m89 为基准,这个库,稣认为太复杂了,没那么多时间去重新整理一遍。所以稣选择了一个比较小型但新颖的库,作为熟悉它的试验性项目,“声音共享”就诞生了。

Now, as I embark on creating my own remote desktop product, it is imperative that I confront WebRTC directly. Naturally, the RTC SDK from the previous company is out of the question for use, as it is based on Google WebRTC m89—a library I consider to be overly complex, and one that I simply do not have the time to reorganize from scratch. Therefore, I opted for a smaller yet more innovative library as an experimental project to familiarize myself with it, and thus, SoundShare was born.

开发过程 Development Process

首先在 Copilot 和 DeepSeek 的帮助下,理清了许多 WebRTC 的概念和原理,后来在 vcpkg 里搜索 rtc,对比了几个库,做好库的选型。

Initially, with the assistance of Copilot and DeepSeek, I clarified many concepts and principles of WebRTC. Subsequently, I searched for ‘rtc’ within vcpkg, compared several libraries, and finalized the selection of the appropriate library.

接下来,考虑先做个小工具练练手,不然直接把一个陌生的库上到 RegameDesk,万一后面发现不好用,岂不是要返工?

Next, I contemplated starting with a small tool to practice, reasoning that directly integrating an unfamiliar library into RegameDesk could lead to rework if it later proved unsuitable.

刚好,稣的笔记本扬声器很差,另一台迷你主机干脆就没有扬声器,还好又有一台脑波退下来的 iPhone 11 Pro Max,外放效果特别好,于是想到可以做个串流工具,让 iPhone 当笔记本的扬声器。

Coincidentally, my laptop’s speakers are subpar, and my mini PC lacks speakers altogether. However, I happen to have a decommissioned iPhone 11 Pro Max from my wife, which boasts excellent external audio performance. This led me to conceive of a streaming tool that would allow the iPhone to serve as the laptop’s speaker.

这里不得不吐槽一下 iPhone 11 Pro Max 的蜂窝信号,真的太差了,所以它基本上就是为 WiFi 而生,十分适合用来做"声音共享"的测试设备。

Here, I must vent a bit about the iPhone 11 Pro Max’s cellular signal—it’s truly abysmal. As a result, it’s essentially designed for WiFi, making it an ideal candidate for testing SoundShare.

虽然这本是一个练手的项目,但稣将它发布给一位朋友时,他竟然打赏了一块钱,于是干脆加点码,​做好一些!

Although this was initially a practice project, when I shared it with a friend, he unexpectedly tipped me one CNY. This gesture motivated me to enhance the project further and refine it to a higher standard!​

项目地址 Project Location

https://github.com/RegameDesk/sound_share

https://gitee.com/RegameDesk/sound_share

RegameDesk 开发笔记【2】Avro

数据交换采用 Avro。但在开始介绍之前,先进入大家最喜欢的答疑环节。

答疑

  1. 稣这是创业了吗?

没有。创业需要能赚钱的产品,还需要有投资(即,不是赚市场的钱,就是赚投资人的钱),稣两者都没有。再说创业肯定都是偷偷摸摸的。单纯就是回报社会主义社会。

  1. 不赚钱,你是 SX 吗?

众所周知,稣使用自然码双拼,普通的拼音简写早就看不懂了。SX 是啥?

经验

几乎在每家公司都用过 Google Protocol Buffers,除了它还没被开发之前。在网稣时,不仅大家推荐稣(在 Linux)使用 ProtoBuf,稣也推荐大家(在 Windows)用 ProtoBuf。但是在做远程桌面时,却和搭档很默契地不用它!最神奇的是,我俩一个写客户端,一个写服务端,期间也没对过具体协议,联调时数据结构居然 100% 一样,一跑就通。

当然这在工程上,并不是什么好事!因为这说明用的就是最简单的封装,相当于没用数据交换库,纯手工打造。要说为啥,那肯定就是偷懒,当时在 Windows 上使用 PB 还比较麻烦,为了“快速实现”就先牺牲扩展性。

到金斗云时,有的工程不用 ProtoBuf,有的用。原本设计上是:网络通信用 ProtoBuf,IPC 则用 FlatBuffers。但由于采用驱动抓图的方式,避开了 IPC 的使用(具体原因以后分析),所以其实 FlatBuffers 没用上。想用 FlatBuffers 是因为它效率高,但又因为它并不安全,所以不能用于网络上,只能用于 IPC。

在雪蛤油时,采用 ProtoBuf 是原有团队的积累,自己也熟悉,没理由不继续用。但发现两个版本的 Debug 版都会被 Boost.Test 报内存泄漏,这时候就凸显库臃肿的坏处了……ProtoBuf 这么大坨,根本没人愿意去分析,如果升级版本,又会出现两份(因为别组开发的 DLL 库里也用,稣只能升自己这边的,那边的他们不升),最后,看着没啥表面问题就直接弃疗。

分析

目前,RegameDesk 采用 Apache Avro 1.12.0。实际上,但凡有点名气的数据交换(或者叫数据序列化)格式和库都被研究得很多了,大可不必自己去选型,看看论文即可。比如这篇:[2201.03051] A Benchmark of JSON-compatible Binary Serialization Specifications

在大部分的对比项中,Avro 和 Microsoft Bond 常常是前两名。

ASN.1 也挺极致的,但它不好用。

Bond 的代码里有 Haskell,学习成本高于只有纯 C++ 的 Avro。而且它已经快停止维护了,详见 Bond project ending March 2025 #1215

简单看过 Avro 的代码,里面有 Zigzag 算法,用于压缩整形,这可太数学、太极致了,是基于统计学的——0 附近的数字被用得多,所以它们用更少的字节来表示。能考虑到这种点,光是想象中,基本上性能就不会落后(当然前面的论文也有实际验证)。

另外,Avro 本身很小巧,第三方依赖主要是 Boost.Iostreams,但 Boost 本来就是稣惯用的库,可以说维护上毫无压力。相对的,ProtoBuff 还带着不熟悉的 abseil,二进制整体加起来大 Avro 好几倍。以下对比基于 vcpkg 默认配置的编译结果:

文件 大小
ProtoBuff Lite abseil_dll.dll, libprotobuf-lite.dll 3.46MB
ProtoBuff abseil_dll.dll, libprotobuf.dll 14.9MB
Avro avrocpp.dll, boost_iostreams-vc143-mt-x64-1_86.dll, zlib1.dll, bz2.dll, liblzma.dll, zstd.dll 1.84MB

(实际上 4 个压缩库是能优化掉其中不用的几个,即还能更小)

使用案例

在设计上,RegameDesk 支持 4 种操作模式:

模式 具体功能
Assist 协助模式:适用于服务器前有人在操作的场景
1. 屏幕拓扑、分辨率等设置均以服务器为主,不改服务器的任何配置
2. 不会创建虚拟显示器
3. 允许多个此模式的客户端;兼容其它模式
Control 控制模式:适用于服务器为物理机,尤其是办公机
1. 屏幕拓扑、分辨率等设置均以客户端为主,将客户端的配置同步到服务器
2. 必定会创建虚拟显示器
3. 会断开服务器上所有物理显示器,防止服务器侧隐私泄露
4. 会使服务器静音,防止服务器侧隐私泄露,而且会阻止用户取消静音
5. 退出时,还原服务端设置
6. 只允许一个此模式客户端;与 headless 模式互斥
Headless 无头模式:适用于服务器不插显示器的场景(物理机或虚拟机皆可)
1. 假定服务器只有虚拟显示器,如果有物理屏幕,则断开,防止服务器侧隐私泄露
2. 屏幕拓扑、分辨率等设置均以客户端为主,将客户端的配置同步到服务器
3. 会使服务器静音,防止服务器侧隐私泄露,但不会阻止用户取消静音
4. 退出时,不还原服务端任何设置
5. 只允许一个此模式客户端;与 control 模式互斥
View 仅查看模式:不能操控
1. 屏幕拓扑、分辨率等设置均以服务器为主,不改服务器的任何配置
2. 不会创建虚拟显示器
3. 允许多个此模式的客户端;兼容其它模式

用 Avro 来表示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"type": "record",
"name": "OperationMode",
"fields": [
{
"name": "mode",
"type": {
"type": "enum",
"name": "Mode",
"symbols": [ "kAssist", "kControl", "kHeadless", "kView" ]
}
}
]
}

人类都说:JSON 的可读性最好。是这样没错!

RegameDesk 开发笔记【1】立项

故事

在遥远的古代,那时候还没有掐鸡,甚至搜索引擎都才刚刚起步,热爱学习的稣还只能去图书馆查阅资料。机缘巧合下,稣发现厦门大学图书馆不仅有很多“砖头”(工具书),还可以免费上机。是的,免费,当时“白嫖”这词还没被发明。

当年的厦门大学图书馆可以说是高科技满级,直到今天看也并不落后——居然是云架构!瘦终端远程登陆到 NT4 的终端服务,这套东西和今天人们用 Windows 自带的 mstsc.exe 或macOS 上的 Microsoft Remote Desktop/Windows App,远程登录 Windows 2000/XP/2003/Vista/7/8/8.1/10/10……可以说,根本就是一样的。

总之,当时天上打了个雷,震惊程度和高中时代看《黑客帝国》不相上下。

上班后,虽然做过很多产品,但潜意识里一直被远程桌面吸引,十几年的观测后,终于慢慢靠拢。目前,稣在三家公司做过远程桌面。

经验

注:以下公司名都是虚拟的,不会有雷同。

第一家网稣,开发起码有 6 名,稣当时还是救火员到处帮忙,所以只负责保驾护航,没多久就去其它部门,再后来就离职了。当时不算核心开发,只是一起搞了个 Demo,验证我们能做出来原型,而且给网吧内网用问题不大。但要做精,做在互联网上用的,火候差很多。比如当时直接用 Live555 做传输库,没人专项优化 rtsp 协议,抗弱网能力这个指标从没被关注到。外设也只支持键盘、鼠标,还是最基本的 SendInput 重放。

第二家金斗云,开始时就稣一人同时做云游戏两个版本和一个远程桌面 Demo,以及另一些项目……后来远程桌面多了一个十几人的团队,至今已经做了多年。这一次,稣的重心是想放在虚拟驱动上的,研究了各种驱动框架,基本知道如何做好一个远程桌面产品。不过由于家里太穷,两年没加薪,脑波意见很大。部门的新领导正好有意让稣去深圳发展,干脆看看机会,结果有个猎头正好找稣说有个远程桌面的岗位,而且是给公司的员工做的,不需要考虑盈利!无论从事业还是家庭角度看,都合算,于是跑了。

第三家雪蛤油,入职时已经有大佬做了一个 Demo,完成度和网稣的那个差不多,只是 UI 丑了很多,毕竟网稣的界面是专业的 DUI 开发 2 人做的。进来前妄想是这公司有钱,人应该比较多,能专心做核心驱动,结果正好相反。一进来就发现开发,包含稣就 2 名,招聘没几天领导就宣告不招了。整个开发周期就 2 名全职 C++ 开发,一名外援 C++ 和一名外援前端。然而,团队又是有许多技术积累、管理手段和周期要求的,按各项最优的来阻力大,所以最终是留着一半原有风格,局部替换为稣认为的足够极致的解。最大收获是产品运营起来后,发现用户的各种毛病怎么防治。毕竟这是一个系统级软件,任何用户自己的问题,都可以被用户怀疑是“软件的 bug”,防范“瓜田李下”是必备的。

新愿景

RegameDesk 将从零开始,吸收在之前三家公司经历过的教训,每项关键点都追求安全和极致。框架上,会比较倾向金斗云时的设计。

产品理念:**为全宇宙打工,搞一个程序员认为安全的软件。**具体说,市面上,除了微软的远程桌面,估计还没有其它能被程序员们认为安全。大部分产品,都有账户体系,会维持和厂商服务器连接,数据可能经过厂商中转。它们可能自称是加密的,但密钥都不能自定义,厂商到底能不能解密都难讲。

罗老师:稣真不是为了赚钱,这样的产品能赚钱才怪。

不建议使用 using namespace

故事

最近稣在梳理一份 Google C++ Coding Style 的更严格版本,其中,有一条是“在大范围禁止使用 using namespace”,结果获得 100% 员工的支持。员工人数:1!

分析

具体来说,大范围指的是比函数更大的范围。通常的 Coding Style 可能会提到:禁止在头文件里使用 using namespace,而这里的禁止显然涉及的地方更多。禁用它的理由通常有以下这些:

  1. 降低可维护性:using namespace 会降低代码的可读性,因为它隐藏了实体的来源,读者需要额外的工作来确定一个名称属于哪个命名空间。如果后续添加或修改了同名的实体,可能导致编译失败,甚至可能引入难以发现的错误。

  2. 降低编译性能:using namespace 可能会导致编译器查找符号时的性能下降,因为它需要在所有导入的命名空间中搜索。

同时,我们放开了例外的限制,没错,您可以在任何地方,比如头文件里,使用 using namespace 各种 std::literals

如果您觉得不使用 using namespace,会导致需要写大量很长的 namespace,那您应该使用 namespace 定义别名来缩短,如果是本项目的 namespace 倾向于使用最短的简称。例如:

1
2
3
4
5
6
namespace d = umutech::regame::diagnostics; // 自己的命名空间,尽量短
namespace fs = boost::filesystem;
namespace nw = boost::nowide;
namespace po = boost::program_options;

using namespace std::string_view_literals; // 例外

microsoft/proxy 学习笔记

1. 用于啥需求?

实现多态性。它允许统一管理和操作多种子类对象,在传统方法中,这通常通过基类指针来实现。

2. 何时使用 Proxy?

每当你在代码中使用 virtual 关键字定义虚函数时,可以考虑使用 Proxy 库作为替代方案,以提高性能和简化代码管理。

3. 有时候 CRTP 也可以替代 virtual,CRTP 也能用 Proxy 替代吗?

虽然 Curiously Recurring Template Pattern (CRTP) 可以在某些情况下替代 virtual 函数,但 Proxy 并不是用来替代 CRTP 的。如果你的目标是代码复用,而不是统一管理多种子类,那么 CRTP 仍然是更合适的选择。例如,如果你有多个项目使用相似的代码段,并且这些代码段被封装在一个基类中,通过 CRTP 实现代码复用,而这些子类只在各自的项目模块中使用,那么继续使用 CRTP 是合适的,使用 Proxy 则不太适合。

Windows 平台编译 FFmpeg

需求

  • FFmpeg 7.0 开始已经支持 D3D12VA,可以自己编译一份采用 LGPL 协议的定制版了。

  • 以前都使用 ShiftMediaProject,现在想学习一种新的编译方式。

解决

这次采用较为普遍的方式:在 MSYS2 环境下编译。

1. 下载、安装

官网下载,并且官网有 Installation 说明。安装完 MSYS2 本身后,需要在 MSYS2 环境下安装编译 FFmpeg 所需的工具:

1
pacman -S autotools yasm

当然,您还需要下载 FFmpeg 代码,目前合适的版本为 7.1。下载完,解压到合适的目录,比如 D:\devel\ffmpeg,这样代码目录就是 D:\devel\ffmpeg\ffmpeg-7.1

2. 启动编译环境

为了编译 x64 版本,运行 x64 Native Tools Command Prompt for VS 2022。然后进入 MSYS2 安装目录,输入以下命令:

1
msys2_shell.cmd -use-full-path

此时获得一个新打开的 MSYS2 实例窗口,后面的命令在这里输入(即,不需要 x64 Native Tools Command Prompt for VS 2022 窗口了)。

3. 编译

由于稣只需要 D3D12VA 对 HEVC 的支持,所以编译其实很快,命令行如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cd /d/devel/ffmpeg/ffmpeg-7.1
export OUT_DIR=../build
./configure \
--target-os=win64 \
--arch=x86_64 \
--toolchain=msvc \
--enable-shared \
--disable-all \
--disable-autodetect \
--enable-swresample \
--enable-avcodec \
--enable-encoder=hevc_d3d12va \
--enable-decoder=hevc \
--enable-d3d12va \
--enable-hwaccel=hevc_d3d12va \
--prefix="$OUT_DIR"

make -j
make install

Windows 平台用 VBS 保护私钥安全

问题

之前稣写过《macOS 上用触控 ID 安全登录 SSH》和《Debian 12 上用 TPM 2.0 安全登陆 SSH》,为何没 Windows 版?

分析

当然有!只是太简单,稣以为大家都会……直到今天才发现不少人都不懂保护自己的私钥,真以为没人惦记你的三千块钱存款吗?

解决方案

网络上能轻易找到的一种解决方案是使用虚拟智能卡,它使用 TPM 2.0 保护私钥,而且使用时需要输入 PIN,安全性是可以的,唯一的问题就是很麻烦。

详情参考:Secure SSH Access with TPM2-Backed Key

稣的解决方案基于 VBS,全称 Virtualization-based Security那啥,不是 VBScript 这种已经去世几十年的东西。 VBS 的底层有一部分是 TPM 2.0,安全性有保障。

1. 离线产生私钥

  • 专门买一台百来块的擸𢶍小主机,从不接入网络,是从不,连局域网都不。

  • 产生密钥导出时需要使用强密码保护。

  • 将加密的密钥文件(.pfx)复制到一个安全的 U 盘,但是这个 U 盘丢了也不怕,因为擸𢶍小主机上还有密钥,而且 U 盘里的文件也有密码。

当然,如果您觉得私钥丢了也无所谓,那可以不用保存私钥副本,只需要保证私钥不被偷。

2. 导入使用密钥的机器

这步隐含“使用密钥的机器”是不那么安全的,所以需要处处防御:

  • 机器拔网线、断 WiFi,重启,进入安全模式。

  • 插入 U 盘,快速导入私钥,导入时必须选择“使用虚拟化安全保护私钥(不可导出)”,快速移除 U 盘。

  • 重启。

导入时有几个可选项:

  • “启用强私钥保护。如果启用这个选项,每次应用程序使用私钥时,你都会收到提示。”稣一般也开启,以便知道哪些应用不老实。

  • 密码。如果机器只有自己使用,可以不用设置,把“此密钥需要密码”点掉。即使不设置密码,被 VBS 保护的私钥也不可能被偷走。

后记

本文没有任何命令和图片,单纯传授心法,因为绝大多数的观众老爷们根本没有安全需求,贴出图片也是浪费时间和流量。