我只是想把一张截图从 Mac 传到鸿蒙 PC——于是开始移植 NitroShare

欢迎加入开源鸿蒙 PC 社区:https://harmonypc.csdn.net/

一个开发者周末的傍晚故事。
没有什么"宏大叙事",就是一件被生活逼出来的小事。


一张截图卡了我 20 分钟

5 月 18 日,周日傍晚。

我在咖啡馆,桌上摊着两台电脑——Mac 在写 IronLog 的下一个版本,鸿蒙 PC 在跑测试。

中间有那么一刻,我在 Mac 上截了个图,想发到鸿蒙 PC 上看看那边的字体渲染。

就这么一个动作。

我先去想 AirDrop——鸿蒙 PC 不支持。
想微信文件传输——得在鸿蒙上登微信,但鸿蒙微信还没正式版桌面。
想浏览器 QQ 网页传——传是能传,但要扫码、要打开手机微信、要批准、要找接收文件夹……
插 U 盘——身边没带。

最后我用了最蠢的办法:把图发到自己的邮箱,登 Mac 邮箱,在鸿蒙 PC 上开浏览器再登一次邮箱,下载。

整个过程 20 多分钟。中间我喝完了一杯美式。

我盯着屏幕想——就这么一件事

用 AirDrop 多少年了?
用 LocalSend 在 Mac 和 Win 之间互传多少年了?
这种"在自己的设备之间传个东西"的小动作,我已经 5 年没认真想过了。

现在突然又要花 20 分钟想这件事——
这就是"换平台"的真实代价。

那天傍晚我把这事记在备忘录里,叫"鸿蒙 PC 缺一个 AirDrop"。

3 天以后,5 月 21 日,我开始动手。


在这里插入图片描述

为什么是 NitroShare

不是 LocalSend,也不是 SnapDrop。是 NitroShare。
在这里插入图片描述

理由很现实:

LocalSendFlutter 写的。Flutter 在鸿蒙 PC 上的支持还不成熟,硬上是另一个 1-2 个月的工程。我手里没这个时间。

SnapDrop纯网页的(基于 WebRTC + WebSocket)。它要一个公网中转服务器,对于"两台同局域网设备直连"反而绕远路。

NitroShare 是个我没怎么用过的小工具——但它有三个对我特别合适的属性:

属性 NitroShare 的样子
技术栈 Qt5(C++) ✅ 我已经做过 4 个 Qt 项目了
体积 1.4 MB 源码 / 70 个 .cpp+.h ✅ 小得像一个练习题
构建 CMake(前 4 个都是 qmake)✅ 顺便补上仓库这块空白
依赖 Qt5Network/Widgets/Svg,无 KF5、无 sqlite、无 OpenGL ✅
GitHub 1.6k star、Nathan Osman 单人维护 ✅

我看了一眼 GitHub——上次提交是 2022 年。NitroShare 几乎停更了。但这不影响——0.3.4 稳定版功能完整,对一个"传文件"工具来说完整就够了

我点了下载。


35 分钟的"过分顺利"

这是整个故事里最反讽的一段。

5 月 21 日晚上 9 点,我在腾讯云 OpenCloudOS 服务器上开干。Qt-OHOS 工具链早就配好了(在 IronLog 那次配齐),不用再装。

21:00  下载源码、解压、recon
21:08  写 CMake toolchain.cmake
21:15  第一次 configure - 报错(QSslConfiguration 找不到,Qt-OHOS 没编 OpenSSL)
21:18  剥离 SSL(在 CMakeLists 里把 ssl.cpp 排除,加 QT_NO_SSL 宏)
21:22  第二次 configure - 通过
21:28  ninja build - 一次链接通过
21:30  ls -lh 看 libnitroshare.so:587 KB
21:33  做 HAP 壳(套模板,把 libnitroshare.so 塞进 libs/arm64-v8a/)
21:35  4 项自检 ✅

从源码下载到 HAP 完成——35 分钟

NitroShare 编译产物:libnitroshare.so 仅 587 KB,HAP 4 项自检全过

我那一刻是真的兴奋的。这是我做过的 5 个 Qt 移植项目里最快的一次

之前最快是 glogg,21 分钟。
KDiff3 花了 1 小时(KF5 剥离很头疼)。
DiffPDF 花了 1.5 小时(poppler-qt5 是只大野兽)。
QElectroTech 花了 2 小时(KF5 + sqlite + moc 跨版本三重打击)。

NitroShare——35 分钟

我把 HAP 包推到设备上,▶ Run。

splash 一闪。

应用关了。


装上、点开、闪退

第一次看到这一幕我以为是误触关闭键。

再 Run 一次。splash 一闪。又关了。

第三次我盯着屏幕——确实是闪退

我心里凉了一下。

35 分钟的"过分顺利"是有代价的——它换来了一个完全没有提示信息的闪退

我拿出 hdc,开 hilog,过滤进程名:

hdc shell hilog | grep -E "nitroshare|cppcrash"

——没有 cppcrash。这不是 native 崩溃
——但也没有任何明显的 ERROR 日志。

我又试了一次。这次我学聪明了——手机录屏对着鸿蒙 PC 屏幕,慢动作回放。

回放里我看到了它:

应用启动
   ↓
splash 出现 (蓝色背景 + NitroShare logo)
   ↓
[这里有一个东西闪过] ← 帧 1/30 秒
   ↓
splash 消失
   ↓
应用退出

那个"闪过的东西"——我把那帧暂停下来仔细看——是个模态对话框

字看不清。我把视频放大,调亮,截图——

┌─────────── Error ───────────┐
│  Unable to listen on port    │
│  40818                       │
│                              │
│              [   OK   ]      │
└──────────────────────────────┘

NitroShare 项目侧写:1.4 MB 源码、70 个 .cpp+.h 文件,listen(40818) 是关键入口

我笑出声了。


顿悟:鸿蒙沙箱不让我 listen

那一刻我突然明白了。

NitroShare 启动时做的第一件事是 mServer.listen(QHostAddress::Any, 40818)——这是 NitroShare 自己的传输服务端口。它得 listen 才能接收别的设备发来的文件。

但鸿蒙 PC 的应用沙箱不允许应用 bind 任意 TCP/UDP 端口

路径 鸿蒙的态度
鸿蒙原生 @ohos.net.socket(ArkTS) ✅ 允许
musl libc 的 raw socket(Qt 走的就是这条) ❌ 拒绝
ohos.permission.INTERNET 权限 对 ArkTS 有效,对 Qt 走的 musl 路径不解决问题

也就是说——
这不是 NitroShare 的 bug。
这不是 Qt 的 bug。
这是鸿蒙桌面应用的平台级限制

我盯着屏幕想了一会儿——

我做这个东西的初衷,是想在两台设备之间传文件。
但平台从根上就不让我 listen 端口。
那我做这个移植还有意义吗?

想了 5 分钟,我又想明白了——这件事的意义反而更大了

因为:

  1. 即使 listen 失败,NitroShare 的"发送端"还是能用的——它向已知地址发文件不需要本地 listen
  2. 鸿蒙原生 socket API 走 ArkTS 是允许的——长期看是可以做一个 ArkTS 桥接的
  3. 现在最迫切的问题是"启动闪退"——一个网络小故障升级成了"启动闪退",这是工程问题,不是平台问题

让我看一下闪退是怎么来的。


5 步推到根因

我顺着 hilog 里能看到的零星线索,反推:

TransferServer::start()
        ↓
   server.listen(Any, 40818)
        ↓
   ✗ 鸿蒙沙箱拒绝
        ↓
   emit error("Unable to listen on port 40818")
        ↓
   application.cpp::notifyError(const QString &message)
   {
       QMessageBox::critical(nullptr, tr("Error"), message);  ← ★ 元凶
   }
        ↓
   模态对话框抢焦点
        ↓
   用户(我)点 OK 关闭它
        ↓
   QApplication::setQuitOnLastWindowClosed(true)  默认行为触发
        ↓
   app.exec() 返回 → 进程退出

整条链最讽刺的地方

  • 鸿蒙拒绝 listen → 本来只是 “网络功能用不了” 这种可恢复故障
  • 但 NitroShare 把它弹成了模态对话框
  • 模态框被点掉时——splash 还没正式显示,主窗口还没建——它是 app 当时唯一的窗口
  • 关掉它 → setQuitOnLastWindowClosed 默认触发 → 整个 app 退了

一个网络小故障升级成了启动闪退

整条链长,但每一步都"看起来合理"——这是工程世界最坑的一种 bug。


5 行代码救活一切

凌晨 1 点 12 分。我打开 src/application/application.cpp,找到 notifyError

 #include <QApplication>
+#include <QDebug>
 ...
 void Application::notifyError(const QString &message)
 {
-    QMessageBox::critical(nullptr, tr("Error"), message);
+    //
+    // On HarmonyOS PC the application sandbox forbids binding
+    // arbitrary ports. listen() failure here is a normal startup
+    // event, NOT a fatal error. A modal dialog grabs focus, blocks
+    // splash, and (via setQuitOnLastWindowClosed) makes the app quit
+    // when user dismisses it -> looks exactly like a crash.
+    //
+    // Demote to warning log; the app keeps running with file transfer
+    // disabled, which is fine for a single-device demo.
+    qWarning() << "NitroShare:" << message;
 }

5 行有效代码(外加一行 #include 和一段注释)。

rm -f xxx.o && ninja——0.3 秒重编。
重新打 HAP,推到设备,▶ Run。

splash 出现。
主窗口出现。
应用没退

我笑出声。

凌晨 1:14。从录屏发现模态框到看到主界面活下来——2 分钟

我截了张图发到自己的备忘录里——「NitroShare on HarmonyOS PC 0.3.4 - 5 月 22 日 01:14 跑通」。

5 行代码降级后的最终产物:891 KB HAP、libnitroshare.so 4KB 对齐、依赖闭包完整


第二天早上的对话

第二天上午我在咖啡馆,前晚的同事也在。

我把鸿蒙 PC 摆出来打开 NitroShare:

“看,跑通了。”

他点开看了下:

“传文件能用吗?”

我说:“接收用不了——鸿蒙不让 listen。发送应该能用——但要测试得有第二台同款设备才能验。”

他想了一下:“那……不算完整功能?”

我说:“不算。”

他:“那你弄它干嘛。”

我喝了一口咖啡。想了几秒。

"嗯——

  • 第一,技术答案出来了:‘鸿蒙 PC + Qt 写的局域网工具’ 能跑到什么程度,现在我知道边界在哪了。是平台层 listen 限制,不是 Qt 限制,不是 NitroShare 写得不好。这个边界本身就是社区想要的答案

  • 第二,5 行代码 + 模板已经留下了:以后任何 Qt 应用启动期 QMessageBox::critical 引发的"假性闪退",都能照抄。KDE Connect / Syncthing GUI / qBittorrent / qmmp / 多数局域网工具——它们如果要做鸿蒙移植,都会撞上同一个坑。这一份 5 行 patch 模板,未来能省别人几小时排查。

  • 第三,这个故事本身就是文章:‘一张截图传 20 分钟逼我移植 NitroShare’——这种’小痛点驱动技术决策’的故事,正是社区里现在缺的’有人味’的内容。"

他愣了一下,说:“你越来越像产品经理了。”

我笑:

"不是产品经理。

用户。"


在这里插入图片描述

这件事让我想明白的几件事

1. 跨设备协作是平台软实力的试金石

鸿蒙 PC 单看应用质量、单看性能、单看界面,已经能用了。

用户的世界从来不是单设备的。我每天至少在 3 台设备之间切换(Mac、鸿蒙 PC、手机)。“传一张截图要 20 分钟”——这就是单设备做得再好、跨设备做不好就会被吐槽的根本原因。

AirDrop 是 iPhone+Mac 黏度的底层粘合剂。它技术上没多神秘——但没有它你就是不舒服

鸿蒙 PC 缺这个。NitroShare 这种社区项目不该是唯一答案——官方应该出一个跨鸿蒙设备的 AirDrop

2. 平台限制不等于不能做

我一开始看到 listen 失败 三个字心都凉了。但想清楚之后发现:

  • listen 失败 ≠ 应用没用——发送端还能工作
  • 应用沙箱 ≠ 永远封死——ArkTS 那条路是允许的,未来可以做桥接
  • 第一版 30% 功能 > 等到 100% 完美再发

很多人卡在"完美主义"上不动手。先让它跑起来、看到主窗口、写一篇博客记录下来——这就是下一个开发者要看的"路径已通"的证据。

3. 小工具反而是验证生态最锋利的探针

NitroShare 只有 1.4 MB 源码。但它撞出了鸿蒙 PC 应用沙箱网络限制这个平台层级的发现——比我之前做的 KDiff3(9.5 MB)、QElectroTech(17 MB)这种大块头都更有"探针价值"。

为什么?因为:

  • 大项目的复杂性会遮蔽平台限制——你不知道是项目 bug 还是平台 bug
  • 小项目的纯粹性反而把平台限制照得清清楚楚——587 KB 的二进制只做一件事,它撞墙就是平台撞墙

给后续移植者的建议:第 1 个不要选 VS Code 这种庞然大物。先选一个 1-2 MB 的小工具——它撞出来的边界对你后面规划更大项目有指导价值。

4. 5 行代码也能写一篇文章

我做完后写了一篇详细的踩坑文(《NitroShare 适配鸿蒙 PC 全流程实战》),其中第 9 节 “鸿蒙沙箱网络限制 + 模态错误框假性闪退”——就这 5 行代码 + 一段诊断逻辑——是整篇文章被最多人转的部分。

技术博客的价值不取决于代码量,取决于洞察的稀缺性

5 行代码 + 一张错误框截图 + 一条 5 步推导链——比 5000 行重构代码更值钱。


对鸿蒙 PC 的三档期望

短期(半年内):

  • 官方出一份"应用沙箱网络限制"白皮书 —— 哪些端口能 listen、哪些不能、ArkTS 桥接桌面 Qt 应用的官方姿势是什么。让开发者知道边界在哪,比"自己撞一遍"要省 80% 的时间。

中期(1-2 年内):

  • 官方桌面 AirDrop / Quick Share——能在鸿蒙手机、鸿蒙 PC、鸿蒙平板之间秒传图片/文件。
  • 鸿蒙原生 socket API 桥接成熟的 Qt/Electron 应用 —— 让局域网工具不用走 musl 路径,绕过沙箱限制。

长期(3-5 年):

  • 跨厂商互联协议——鸿蒙 + iOS + Win + Android 互相 AirDrop。

第一档是技术问题(出个文档就行),第二档是工程问题(生态推一推),第三档是政治问题(不强求)。


写在最后

5 月 18 日傍晚那张被卡住 20 分钟的截图,5 月 21-22 日的两天移植——前后整个事件历时4 天

期间产出:

  • libnitroshare.so 587 KB,HAP 891 KB
  • ✅ 1 篇 28KB 的完整踩坑文
  • ✅ 10 张终端截图
  • ✅ “鸿蒙沙箱网络限制 + 模态框假性闪退” 5 行代码降级模板
  • ✅ 一份Qt 桌面应用在鸿蒙 PC 上的网络能力边界文档

但坦白说——

我到现在为止还没真正用 NitroShare 在两台设备之间传过一张图。

因为接收端 listen 不了,发送端没有第二台设备测试,它实际上还是个"半成品"

下一次去咖啡馆的时候,我大概率还是会用邮箱传截图——因为这件事真的"还没解决"。

但有一件事变了:

我现在知道路在哪里

我知道是平台限制不是项目问题。
我知道未来要打通的是 ArkTS 桥接而不是再纠结 Qt。
我知道 5 行代码就能让它别假性闪退。
我知道下一个想做 Qt 局域网工具的人,看了我的文章可以省 5 个小时

这就够了。

适配这件事本质上不是为了"现在好用"——是为了**“路被人走过一遍,下一个人就不用再迷路”**。


给你

如果你也有一个"很小但每天都烦"的痛点——
比如 PDF 比较、文本对比、日志查看、传文件、键盘工具——
看看清单里有没有 Qt / Electron 的同类开源软件。

选一个体积 1-2 MB 的小工具。
不要选 VS Code 这种庞然大物。

一个周末。
一杯咖啡(或啤酒)。
一份服务器上的 Qt-OHOS 工具链。
一台真机。

撞一次墙。
把撞墙的过程写成文章。
把 5 行代码或者 50 行代码留在仓库里。

这就是参与生态最朴素的方式。

不需要等"完全准备好"。
没人完全准备好过。
我自己每次开始都觉得"再等等吧"。

但是周日傍晚那张截图卡了我 20 分钟之后——
我就开始了。


附录:NitroShare 移植真实数据

如果你要复现,下面是实测数据:

数值
NitroShare 版本 0.3.4
源码体积 1.4 MB / 70 个 .cpp+.h
构建系统 CMake 3.26.5
编译耗时 35 分钟(含配置 + 编译 + HAP 打包)
真机调试耗时 10 分钟
编译产物 libnitroshare.so 587 KB
HAP 包 891 KB
关键修复 5 行(QMessageBox::critical → qWarning)

后记:那张 5 月 18 日的截图,最终我用邮箱传了过去。
在鸿蒙 PC 上看那个字体渲染——其实挺好看的。
所以本质上这一切都是因为:那台鸿蒙 PC 的字体渲染让我想多看一眼。
工具应该让看一眼这件事变得容易。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐