当前位置: 首页 > news >正文

【开源软件移植】NitroShare 适配鸿蒙 PC 全流程实战 — Qt-OHOS × 手把手移植教程

【开源软件移植】NitroShare 适配鸿蒙 PC 全流程实战 — Qt-OHOS × 手把手移植教程

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

注意(本部分必看):
开始本文工作前需要完成:
从0创建项目指南,新手先看这篇,都写到这篇文章内了,重复的步骤不过多重复写,后续工作都在用HAP 壳工程:
https://blog.csdn.net/weixin_52908342/article/details/161343743
准备 OpenHarmony SDK
准备 Qt for HarmonyOS
复现最小 Qt Widgets Demo
准备 DevEco HAP 壳工程

完成上面步骤,即可跟着本文进行手把手教学适配NitroShare!

NitroShare 适配鸿蒙 PC 全流程实战,博主苦战了2天半,踩了很多坑。输出了这篇手把手教学文章,小白可以快速上手从0复现。


源码开源仓库:https://atomgit.com/weixin_52908342/OH-NitroShare

0. 写在前面:为什么是 NitroShare?

NitroShare 是一个由 Nathan Osman 开发的局域网文件秒传工具——可以理解为“开源版 AirDrop / LocalSend 的 Qt 实现”。它在 GitHub 有 1.6K star,体积只有 1.4 MB 源码、70 个 .cpp/.h 文件,是这个系列里最小的 Qt 桌面应用候选

挑它的 4 个理由:

维度NitroShare 的特殊性
构建系统CMake(前 4 篇都是 qmake,这是本系列首次讲 CMake
依赖矩阵只用Qt5Network/Widgets/Svg,无 KF5、无 sqlite、无 OpenGL
网络栈暴露了Qt-OHOS 5.12.12 没编 OpenSSL的特性,要剥QSslConfiguration(新课题)
运行期暴露了鸿蒙沙箱不允许 bind 任意 TCP/UDP 端口——listen(40818)必失败,引出

跟前 4 篇形成的对照矩阵:

应用构建依赖难点运行期难点产物体积篇幅时间
1gloggqmake几无7.0 MB21 min
2KDiff3qmakeKF5 剥离9.5 MB~1 h
3DiffPDFqmakepoppler-qt52.1 MB~1.5 h
4QElectroTechqmakeKF5 + sqlite + moc17 MB2 h
5NitroShareCMakeQT_NO_SSL鸿蒙沙箱 listen 失败 + 模态错误框假性闪退587 KB → 891 KB35 min + 真机调试 ~10 min

新手必看前置步骤

从0创建项目指南,新手先看这篇:
https://blog.csdn.net/weixin_52908342/article/details/161343743

QT官方鸿蒙版开源地址:https://wiki.qt.io/Qt5.12.12_Open_Source_Release_for_HarmonyOS_zh

QT官方文档地址:https://wiki.qt.io/Qt_for_OpenHarmony/zh

环境要求
(HarmonyOS/OpenHarmony)鸿蒙版本 API20+
Qt Creator安装 安装电脑版Qt5.12或以上版本(5.14、5.15),获得QtCreator的IDE。
华为 DevEco Studio 安装 如果您想开发Qt for HarmonyOS应用程序,除了使用Qt Creator之外,还需要依赖DevEco Studio。

准备 DevEco HAP 壳工程

这一步在 DevEco Studio 里做。

创建工程

在 DevEco Studio 中:

File New Create Project

选择一个最简单的 Stage 模板。工程名可以叫:

QtOhosDemo

目标结构大致类似:

QtOhosDemo/ entry/ src/main/ ets/ cpp/ libs/

如果你使用的是 Qt for HarmonyOS 官方模板,里面通常已经有加载 Qt runtime 的代码。

设置加载库名

找到类似文件:

entry/src/main/ets/common/QtAppConstants.ets

设置:

exportconstAPP_LIBRARY_NAME='libqt_ohos_demo.so';

如果你的模板没有这个文件,就搜索:

APP_LIBRARY_NAME loadLibrary libqohos

目标是让 HAP 启动时加载:

libqt_ohos_demo.so

放入动态库

创建目录:

entry/libs/arm64-v8a/

把下面文件放进去:

libqt_ohos_demo.so libqohos.so libQt6Core.so 或 libQt5Core.so libQt6Gui.so 或 libQt5Gui.so libQt6Widgets.so 或 libQt5Widgets.so

libqt_ohos_demo.so来自:

~/qt-ohos-demo/build-ohos/libqt_ohos_demo.so

Qt runtime 的.so来自你的 Qt for HarmonyOS 安装目录。

签名 .so

在构建主机上签名:

exportSIGN_TOOL=$OHOS_SDK_ROOT/toolchains/lib/binary-sign-toolcd/path/to/QtOhosDemo/entry/libs/arm64-v8a$SIGN_TOOLsign-inFilelibqt_ohos_demo.so-outFilelibqt_ohos_demo.so-selfSign1

如果你自己拷贝了其他三方库.so,也一起签名。

运行

在 DevEco Studio 中:

Sync Project Build Hap Run

成功标志:

鸿蒙 PC 上出现一个窗口,显示 Hello Qt on HarmonyOS PC

如果这里成功,说明:

Qt runtime 可以加载 HAP 壳工程可以运行 你的业务 .so 可以被鸿蒙应用加载

到这里就可以开始本章的NitroShare适配工作了。

1. 阶段 1:环境

服务器上的 Qt-OHOS 工具链早就在/opt/qt-ohos/qt-5.12.12-ohos/里就绪,OHOS LLVM 在/root/ohos-sdk/ohos-sdk/linux/native/,本次完全不需要新装环境:

[root@VM-0-13-opencloudos ~]# ls /opt/qt-ohos/qt-5.12.12-ohos/qt-5.12.12-ohos/lib/cmake/Qt5/Qt5Config.cmake Qt5ConfigVersion.cmake Qt5ModuleLocation.cmake[root@VM-0-13-opencloudos ~]# ls /opt/qt-ohos/qt-5.12.12-ohos/qt-5.12.12-ohos/lib/libQt5{Core,Gui,Widgets,Network,Svg}.solibQt5Core.so libQt5Gui.so libQt5Network.so libQt5Svg.so libQt5Widgets.so[root@VM-0-13-opencloudos ~]# which moc-qt5 uic-qt5 rcc-qt5 lupdate-qt5 lrelease-qt5/usr/bin/moc-qt5 /usr/bin/uic-qt5 /usr/bin/rcc-qt5 /usr/bin/lupdate-qt5 /usr/bin/lrelease-qt5[root@VM-0-13-opencloudos ~]# ls /root/ohos-sdk/ohos-sdk/linux/native/llvm/bin/aarch64-unknown-linux-ohos-clang*aarch64-unknown-linux-ohos-clang aarch64-unknown-linux-ohos-clang++[root@VM-0-13-opencloudos ~]# cmake --version | head -1cmake version3.26.5

⚠️关键点:Qt-OHOS 5.12.12 包里bin/目录下的moc.exe / uic.exe / rcc.exeWindows 二进制.exe后缀,且来自 windows-backup);本系列前 4 篇用 qmake 时通过把 mkspec 的host_bins.path指向 Linux 系统包的/usr/bin/moc-qt5解决。CMake 路径不同——见 §3 toolchain 那一行set(Qt5_MOC_EXECUTABLE /usr/bin/moc-qt5 CACHE FILEPATH ... FORCE)


2. 阶段 2:下载源码 + 项目侧写

[root@VM-0-13-opencloudos ~]# mkdir -p /root/NitroShareGOGO && cd /root/NitroShareGOGO[root@VM-0-13-opencloudos NitroShareGOGO]# curl -sL -o nitroshare.tar.gz \https://codeload.github.com/nitroshare/nitroshare-desktop/tar.gz/refs/tags/0.3.4[root@VM-0-13-opencloudos NitroShareGOGO]# tar xzf nitroshare.tar.gz[root@VM-0-13-opencloudos NitroShareGOGO]# du -sh nitroshare-desktop-0.3.4/1.4M nitroshare-desktop-0.3.4/

源码侧写:

[recon] 子目录:cmake/ shell/ src/ src/api src/application src/bundle src/data src/device src/dist src/icon src/mdns src/settings src/transfer src/util [recon] .cpp: 32 .h: 38 总计 70 个源文件 [recon] 构建系统:CMake (3 个 CMakeLists.txt) [recon] 顶层 find_package:Qt5LinguistTools / Qt5Network / Qt5Widgets / Qt5Svg [recon] 可选 find_package:qhttpengine / qmdnsengine(HTTP API + mDNS 发现) [recon] 平台分支:if(WIN32) shell extension + WinExtras if(APPLE) MacExtras Linux: pkg_check_modules(APPINDICATOR gtk+-2.0 ...)

3. 阶段 3:写 OHOS toolchain.cmake(CMake 系列首篇核心)

CMake 工程的鸿蒙 PC 交叉编译,最干净的做法是一份独立 toolchain 文件——这是 qmake 系列没有的概念。把所有平台 / 编译器 / sysroot / Qt 路径 / host 工具的指定都放在一个 43 行的文件里:

# /root/NitroShareGOGO/ohos-toolchain.cmake set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR aarch64) # OHOS LLVM 工具链 set(OHOS_NATIVE "/root/ohos-sdk/ohos-sdk/linux/native") set(OHOS_SYSROOT "${OHOS_NATIVE}/sysroot") set(CMAKE_C_COMPILER "${OHOS_NATIVE}/llvm/bin/aarch64-unknown-linux-ohos-clang") set(CMAKE_CXX_COMPILER "${OHOS_NATIVE}/llvm/bin/aarch64-unknown-linux-ohos-clang++") set(CMAKE_AR "${OHOS_NATIVE}/llvm/bin/llvm-ar") set(CMAKE_RANLIB "${OHOS_NATIVE}/llvm/bin/llvm-ranlib") set(CMAKE_LINKER "${OHOS_NATIVE}/llvm/bin/ld.lld") # Qt-OHOS 5.12.12 已交叉编译好的 sysroot set(QT_OHOS_PREFIX "/opt/qt-ohos/qt-5.12.12-ohos/qt-5.12.12-ohos") list(PREPEND CMAKE_PREFIX_PATH "${QT_OHOS_PREFIX}") # 编译/链接旗标 —— -fPIC + 走 OHOS sysroot set(_OHOS_FLAGS "--target=aarch64-linux-ohos --sysroot=${OHOS_SYSROOT} -fPIC") set(CMAKE_C_FLAGS_INIT "${_OHOS_FLAGS}") set(CMAKE_CXX_FLAGS_INIT "${_OHOS_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS_INIT "--target=aarch64-linux-ohos --sysroot=${OHOS_SYSROOT}") set(CMAKE_SHARED_LINKER_FLAGS_INIT "--target=aarch64-linux-ohos --sysroot=${OHOS_SYSROOT}") # CMake 找 lib/header 时,target 走 sysroot;package 走 host set(CMAKE_FIND_ROOT_PATH ${OHOS_SYSROOT} ${QT_OHOS_PREFIX}) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH) # ★ 关键:Qt5 host 工具的 .exe 探测会失败(Windows 包), # 强制走 Linux 系统 Qt5 开发包提供的 *-qt5 set(Qt5_MOC_EXECUTABLE /usr/bin/moc-qt5 CACHE FILEPATH "host moc" FORCE) set(Qt5_UIC_EXECUTABLE /usr/bin/uic-qt5 CACHE FILEPATH "host uic" FORCE) set(Qt5_RCC_EXECUTABLE /usr/bin/rcc-qt5 CACHE FILEPATH "host rcc" FORCE) set(Qt5_LUPDATE_EXECUTABLE /usr/bin/lupdate-qt5 CACHE FILEPATH "host lupdate" FORCE) set(Qt5_LRELEASE_EXECUTABLE /usr/bin/lrelease-qt5 CACHE FILEPATH "host lrelease" FORCE)

💡5 个隐藏要点(CMake 老手都未必全踩过):

  1. CMAKE_*_FLAGS_INIT而非CMAKE_*_FLAGS:前者只在 toolchain 加载时初始化一次,后者会被项目 CMakeLists 覆盖。
  2. --target=aarch64-linux-ohos必须同时出现在编译和链接阶段,否则链接器走 host gcc。
  3. CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTHfind_package(Qt5...)既能扫 OHOS sysroot 里的 Qt5Config.cmake,又能扫 host /usr。
  4. Qt5_MOC_EXECUTABLECACHE ... FORCE三连:FORCE 必须有,否则 Qt5LinguistToolsConfig 在 find_package 时会被自动重置成.exe后缀。
  5. CMake 会智能继承 toolchain 里的所有_INIT变量到子目录的 add_subdirectory,不需要在每个 CMakeLists 重复指定

4. 阶段 4:CMake 工程剥离 —— 顶层 92→39 行 / src 288→102 行

NitroShare 上游 CMakeLists 包了大量 Win32 / Apple / Linux-Gnome 平台分支以及 qhttpengine、qmdnsengine 两个可选依赖。鸿蒙 PC 上全部不需要,剥到只剩 Qt5 四件套:

4.1 顶层 CMakeLists(92 → 39 行)

--- CMakeLists.txt.bak +++ CMakeLists.txt -find_package(Qt5LinguistTools 5.1 REQUIRED) -find_package(Qt5Network 5.1 REQUIRED) -find_package(Qt5Widgets 5.1 REQUIRED) -find_package(Qt5Svg 5.1 REQUIRED) - -if(WIN32) - find_package(Qt5WinExtras) -endif() -if(APPLE) - find_package(Qt5MacExtras) -endif() -find_package(qhttpengine NAMES qhttpengine QHttpEngine QUIET) -find_package(qmdnsengine QUIET) -if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") - find_package(PkgConfig) - pkg_check_modules(APPINDICATOR gtk+-2.0 appindicator-0.1 libnotify) -endif() +find_package(Qt5LinguistTools 5.1 REQUIRED) +find_package(Qt5Network 5.1 REQUIRED) +find_package(Qt5Widgets 5.1 REQUIRED) +find_package(Qt5Svg 5.1 REQUIRED) +# 平台分支 / 可选依赖 全部剥离

4.2 src/CMakeLists.txt(288 → 102 行)+ ★ app→library

最核心的改造一行:把add_executable(... WIN32 MACOSX_BUNDLE)改成add_library(... SHARED),这样产物从 ELF executable 变成可被dlopen的 ELF shared object,鸿蒙 HAP 加载器才能识别。

-add_executable(nitroshare WIN32 MACOSX_BUNDLE ${SRC} ${UI} ${QRC}) +add_library(nitroshare SHARED ${SRC} ${UI} ${QRC}) +add_dependencies(nitroshare qm) +set_target_properties(nitroshare PROPERTIES + OUTPUT_NAME "nitroshare" + PREFIX "lib" +) target_link_libraries(nitroshare Qt5::Widgets Qt5::Network Qt5::Svg) -if(Qt5WinExtras_FOUND) ... endif() -if(Qt5MacExtras_FOUND) ... endif() -if(qhttpengine_FOUND) ... endif() -if(qmdnsengine_FOUND) ... endif() -if(APPINDICATOR_FOUND) ... endif() -include(GNUInstallDirs) -install(TARGETS nitroshare ...)

🛑首坑:第一次试时我顺手加了SOVERSION ""VERSION "",结果 SONAME 变成libnitroshare.so.(带个孤零零的点),鸿蒙加载器直接拒绝。正确做法是干脆不设这两个属性,让 CMake 默认产libnitroshare.so


5. 阶段 5:第一次 cmake configure —— 一次过 ⭐

[root@VM-0-13-opencloudos build]# cmake -G "Unix Makefiles" \-DCMAKE_TOOLCHAIN_FILE=/root/NitroShareGOGO/ohos-toolchain.cmake\-DCMAKE_BUILD_TYPE=Release..-- The C compiler identification is Clang15.0.4 -- The CXX compiler identification is Clang15.0.4 -- Detecting C compiler ABI info -done-- Checkforworking C compiler: /root/ohos-sdk/ohos-sdk/linux/native/llvm/bin/aarch64-unknown-linux-ohos-clang - skipped -- Checkforworking CXX compiler: /root/ohos-sdk/ohos-sdk/linux/native/llvm/bin/aarch64-unknown-linux-ohos-clang++ - skipped -- Configuringdone(0.2s)-- Generatingdone(0.0s)-- Build files have been written to: /root/NitroShareGOGO/.../build

0.2 秒Configuring done——比前 4 篇 qmake 任意一篇都快。CMake + 干净 toolchain 的优势在这里看得很清楚。


6. 阶段 6:试编译 —— 一次大坑(QT_NO_SSL)+ 一次小坑(QStyle)

6.1 第 1 次 build:error: unknown type name 'QSslConfiguration'(共 7 个 error)

src/transfer/transfer.h:58:14: error: unknown type name 'QSslConfiguration' Transfer(QSslConfiguration *configuration, TransferModel::Direction direction); ^ src/transfer/transferreceiver.h:40:22: error: unknown type name 'QSslConfiguration' src/transfer/transfersender.h:42:20: error: unknown type name 'QSslConfiguration' src/transfer/transfermodel_p.h:54:5: error: unknown type name 'QSslConfiguration' ... 7 errors generated.

奇怪——#include <QSslConfiguration>明明有写,头文件也确实存在qsslconfiguration.h。直接打开看:

// /opt/qt-ohos/.../include/QtNetwork/qsslconfiguration.h60:#include<QtNetwork/qssl.h>65:#ifndef QT_NO_SSL// ← ★ 整个类被这个守卫包了66:QT_BEGIN_NAMESPACE...82:classQ_NETWORK_EXPORTQSslConfiguration83:{...};210:#endif// QT_NO_SSL

root cause:Qt-OHOS 5.12.12 这一版编译时configure -no-openssl,所以QT_NO_SSL被定义,整段 SSL 类都被预处理掉了。

两条路

  • ❌ 给 Qt-OHOS 加 OpenSSL 重编 → 几个小时
  • NitroShare 源码层面剥 SSL——SSL 在 NitroShare 是可选路径Transfer::Transfer(QSslConfiguration *configuration, ...)configuration == nullptr时直接走明文QTcpSocket。剥 SSL 就是把签名里的QSslConfiguration *改成void *,永远传 nullptr。

写一个小脚本patch_ssl_strip.sh,30 秒内一次性改 8 个文件:

[root@... src]# bash patch_ssl_strip.shtransfer.cpp patched transfermodel.cpp patched[before patch]QSsl 出现39次(8 个 .h/.cpp) QSslSocket(11)QSslConfiguration(11)QSslCertificate(9)QSslKey(7)QSslError(5)QSsl(4)[after patch]transfer/ 下 QSsl 残留0

6.2 第 2 次 build:incomplete type 'QStyle' named in nested name specifier

src/application/splashdialog.cpp:40:17: error: incomplete type 'QStyle' named in nested name specifier setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, ...)); ^~~~~~~~ qapplication.h:58:7: note: forward declaration of 'QStyle' class QStyle; ^

简单——#include <QApplication>只前向声明了QStyle,要用静态方法得#include <QStyle>。一行 sed:

sed-i'0,/^#include <QApplication>/s//#include <QApplication>\n#include <QStyle>/'\src/application/splashdialog.cpp

6.3 第 3 次 build —— 一次过

[ 17%] Built target nitroshare_autogen ← AUTOMOC 走 /usr/bin/moc-qt5 OK [ 30%] Building application/splashdialog.cpp.o [ 32%] Building transfer/acceptdialog.cpp.o [ 38%] Building main.cpp.o [ 40%] Building qrc_resource.cpp.o [ 42%] Building qrc_resource_qm.cpp.o 1 warning generated. ← 仅 "delete void*" 一个无害 warning [ 44%] Linking CXX shared library ../out/libnitroshare.so [100%] Built target nitroshare

0 errors,1 warning(无影响),8 分钟内 3 次 build 通关


7. 阶段 7:5 项最终验证 ⭐⭐⭐

每篇都要的"生死门"。NitroShare 这套 5 项验证如下:

[1]file: libnitroshare.so: ELF64-bit LSB shared object, ARM aarch64, version1(SYSV), dynamically linked, with debug_info, not stripped[2]size: -rwxr-xr-x1root root 587K May2423:47 libnitroshare.so[3]T main: 000000000007035c T main ← ✅ 鸿蒙 dlopen+dlsym("main")能找到[4]SONAME + NEEDED:(NEEDED)libQt5Network.so(NEEDED)libQt5Svg.so(NEEDED)libQt5Widgets.so(NEEDED)libQt5Gui.so(NEEDED)libQt5Core.so(NEEDED)libc++_shared.so(NEEDED)libc.so(SONAME)libnitroshare.so[5]LOAD 段 4KB 对齐: LOAD 0x0000000000000000... R 0x1000 ← 4KB ✓ LOAD 0x000000000004b3d4... R E 0x1000 LOAD 0x0000000000072900... RW 0x1000 LOAD 0x0000000000076400... RW 0x1000

5 项全绿,**libnitroshare.so = 587 KB*


8. 阶段 8:HAP 工程从 0 搭建 + 4 项自检全过

复用 GloggOhos 那套壳,做 4 件事:

# 1) 整壳复制cp-RDemoOhos NitroShareOhos# 2) 替换 .so(删 glogg 的,放 nitroshare 的;补 Svg)rmNitroShareOhos/entry/libs/arm64-v8a/libglogg.socpNitroShareGOGO/artifacts/nitroshare-ohos-libs/libnitroshare.so\NitroShareOhos/entry/libs/arm64-v8a/cpQElectroTechOhos/entry/libs/arm64-v8a/libQt5Svg.so\NitroShareOhos/entry/libs/arm64-v8a/# 3) 删 glogg 专用 rawfile 资源rm-rfNitroShareOhos/entry/src/main/resources/rawfile/glogg# 4) 改 4 个文件 7 处文案# - AppScope/app.json5 bundleName → com.example.nitroshareohos# - AppScope/.../element/string.json "glogg" → "NitroShare"# - entry/.../element/string.json 3 处 glogg 文案 → NitroShare# - entry/.../common/QtAppConstants.ets libglogg.so→libnitroshare.so + LOG_TAG

4 项自检:

[check 1] bundleName: com.example.nitroshareohos ✅ [check 2] glogg/Glogg/GLOGG 残留: 0 ✅ [check 3] libnitroshare.so 已就位: 587 KB ✅ [check 4] APP_LIBRARY_NAME: libnitroshare.so + NitroShareOhos ✅

entry/libs/arm64-v8a/最终就位的 12 个 .so:

libQt5Concurrent.so 41 K libQt5Svg.so 2.4 M libQt5Core.so 8.7 M libQt5Widgets.so 9.1 M libQt5Gui.so 8.2 M libQt5Xml.so 364 K libQt5Network.so 2.0 M libc++_shared.so 1.2 M libQt5OhosExtras.so 605 K libnitroshare.so 587 K ← 应用主体 libQt5PrintSupport.so 529 K libqohos.so 17 M ← Qt-OHOS 平台胶水

总 HAP 体积约51 MB(其中 50 MB 全是 Qt5 + libqohos,应用本身只有 587 KB——这才是 CMake + 极简依赖的真正威力)。


9. 阶段 9:真机调试 —— 鸿蒙沙箱网络限制 + 模态错误框假性闪退

这一章是整个系列里最有再用价值的一节——它不属于编译期,而是发生在 HAP 装机首跑那一刻。它给所有"带网络服务的 Qt 桌面应用 → 鸿蒙 PC"的后续移植,留了一份可直接照抄的模板。

9.1 症状

HAP 4 项自检全过、▶ Run 也成功推到机器,但 splash 一闪即逝、应用立即退出。仔细抓帧才看到中间一闪而过的模态错误框:

点 OK 后整个 app 直接退出——外观上完全是"启动闪退",但实际上 app 是被自己弹出的对话框关掉的。

9.2 根因链——5 步推到 setQuitOnLastWindowClosed

挖一遍源码,整条链路如下:

main.cpp:84 Application *nitroshare = new Application; ↓ application.cpp:54+ 构造函数里 connect(&mTransferServer, &TransferServer::error, this, &Application::notifyError) application.cpp:90 mTransferServer.start(); ↓ TransferServer::start() → mServer.listen(QHostAddress::Any, 40818) ↓ ✗ 鸿蒙沙箱拒绝 bind 任意 TCP 端口 ↓ emit error("Unable to listen on port 40818") ↓ application.cpp::notifyError(const QString &message) { QMessageBox::critical(nullptr, tr("Error"), message); ← ★ 元凶 } ↓ 模态对话框抢焦点,splash 还没建起来 / 被压在底下 ↓ 用户点 OK → 关闭 QMessageBox ↓ QApplication::setQuitOnLastWindowClosed(true) 默认行为触发 ↓ app.exec() 返回 → 进程退出

关键洞察:错误信号本身只是"端口被拒"这种可恢复故障,但 NitroShare 的notifyError把它弹成了模态框——再叠加 Qt 默认的"最后一个窗口关闭就退应用"——一个网络小故障升级成了"启动闪退"。

9.3 为什么不修 listen 本身

直觉的反应是"那我加个ohos.permission.INTERNET让它能 listen 不就行了?"——不行。鸿蒙 PC 的应用沙箱对任意端口的 TCP/UDP listen有平台层限制:

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

也就是说,这是平台层面的限制,不是 NitroShare 的 bug。正确的工程姿态是 —— 让 listen 失败时优雅降级,而不是去硬怼平台。

9.4 修复 ⭐ 5 行代码 + 1 行#include

打开src/application/application.cpp,做两处改动:

#include <QApplication> +#include <QDebug> #include <QMessageBox> ... void Application::notifyError(const QString &message) { - QMessageBox::critical(nullptr, tr("Error"), message); + // OHOS PC: do NOT pop up a modal QMessageBox here. + // + // On HarmonyOS PC the application sandbox forbids binding several + // reserved ports (e.g. TCP 40818 / UDP 40816), so TransferServer::start() + // legitimately fails and emits this error during normal startup. + // A modal dialog at this point grabs the focus, blocks the splash, and + // (because setQuitOnLastWindowClosed(true)) makes the app quit when the + // user dismisses it -> looks exactly like a startup crash. + // + // Demote the error to a warning log; the app keeps running with file + // transfer disabled, which is fine for a single-device demo. + qWarning() << "NitroShare:" << message; }

效果:

  • ✅ 端口起不来时不弹模态框,只在 hilog 里看到W qWarning: NitroShare: "Unable to listen on port 40818"
  • ✅ Splash / 主界面正常显示,setQuitOnLastWindowClosed不会被错误框关闭事件意外触发
  • ✅ 文件传输功能"静默禁用"——单机 demo 场景下这恰好是想要的行为;如果未来真有两台设备测试,再讨论是否要打通鸿蒙原生 socket 桥接

9.5 重编 + 同步 + 4 项验证

[root@VM ~]# cd /root/NitroShareGOGO/nitroshare-desktop-0.3.4/build[root@VM build]# rm -f src/CMakeFiles/nitroshare.dir/application/application.cpp.o out/libnitroshare.so[root@VM build]# make -j4 nitroshare 2>&1 | tail -3[96%]Building CXX object src/CMakeFiles/nitroshare.dir/application/application.cpp.o[98%]Linking CXX shared library../out/libnitroshare.so[100%]Built target nitroshare[root@VM build]# ls -lh out/libnitroshare.so-rwxr-xr-x1root root 891K out/libnitroshare.so ← 587K → 891K(带额外 debug_info)[root@VM build]# llvm-nm -D out/libnitroshare.so | grep ' T main'00000000000b9a84 T main ← ✅ 入口仍在# 拉回本地 + 同步两份 .so(本地仓库 + HAP 工程)$scproot@SERVER:/root/.../out/libnitroshare.so NitroShareOhos/entry/libs/arm64-v8a/ $cpNitroShareOhos/entry/libs/arm64-v8a/libnitroshare.so NitroShareGOGO/artifacts/nitroshare-ohos-libs/ $ md5-qNitroShareOhos/.../libnitroshare.so NitroShareGOGO/artifacts/.../libnitroshare.so 85c09e47c8b... 85c09e47c8b... ← ✅ 两侧 md5 完全一致

9.6 通用价值——一份"鸿蒙沙箱网络限制"模板

这一节给后续移植留了一份可直接照抄的模板,适用对象是:

任何在启动期会主动 bind/listen TCP/UDP 端口的 Qt 桌面应用——例如 KDE Connect、Syncthing GUI、qBittorrent、qmmp、多数局域网工具。

抓出三个特征指纹:

指纹应用代码典型形态
① 入口构造里server.listen(...)Application::Application()/MainWindow::MainWindow()里直接 listen
② 错误通过信号回报上层connect(&server, &XxxServer::error, this, &App::notifyXxx)
③ notifyXxx 里弹模态框QMessageBox::critical/warning(...)

只要看到这三个指纹同时出现,就可以直接照抄本节的修法:把QMessageBox::critical改成qWarning() <<——5 行代码 + 1 行#include <QDebug>,全部搞定。


10. 收尾感受

  1. 可执行→共享库—— qmakeTEMPLATE = lib + CONFIG += shared/ CMakeadd_library(... SHARED),导出T main,HAP 加载器dlopen + dlsym("main")入口。
  2. host 工具链—— 系统moc-qt5/uic-qt5/rcc-qt5(/usr/bin),强制覆盖 Qt-OHOS 包里的.exe
  3. 依赖剥离 vs 内嵌—— 能剥就剥(KF5、appindicator、可选 SSL);不能剥就内嵌(sqlite3 amalgamation)。
  4. CMake 走 toolchain file,qmake 走 mkspec patch—— 两条不同的路径,但目的一样:让 host 工具走 Linux Qt5、target 走 OHOS Qt5。
  5. 产物 5 项验证 + HAP 4 项自检—— 不验产物盲目装 HAP,调到一半的时间都在排查。
  6. 启动期错误模态框一律降级日志(NitroShare 新增)—— 鸿蒙沙箱有大量隐性限制(端口 listen、文件路径、麦克风/相机),任何QMessageBox::critical出现在构造期 / 启动期都极其危险,会被setQuitOnLastWindowClosed捎带退出整个 app。统统改成qWarning() <<
http://www.jsqmd.com/news/900414/

相关文章:

  • 工业视觉辅助系统:实时检测与装配质量优化
  • 分数阶微积分导向的离散制造检测数据融合技术【附算法】
  • 05 - Tool 工具调用:让 AI “长出双手“
  • 从‘找不到文件’到成功运行:一次完整的Windows 10家庭版gpedit.msc启用记录与排错心得
  • 存储芯片和逻辑芯片的区别是什么?
  • 窗口尺寸调整难题的终极解决方案:WindowResizer使用全攻略
  • 研究生读文献亲测好用的工具
  • GS算法与Fienup算法详解:为什么你的相位恢复总不收敛?可能是反馈机制没搞懂
  • CrossOver容器访问Mac外置硬盘?手把手教你映射D盘(保姆级图文)
  • 06 - MCP 模型上下文协议:统一 AI 工具的“Type-C 接口“
  • 从CS231N作业到你的实验:Tiny-ImageNet数据集完整使用指南(含预处理与可视化)
  • 2026年智慧工地系统推荐榜单:工地人脸识别/塔吊防碰撞/AI视频巡检/扬尘监测/实名制考勤/车辆道闸/升降机监控/劳务管理平台全解析 - 品牌企业推荐师(官方)
  • 微信AI机器人终极指南:打造智能群聊助手的完整教程
  • G1舞蹈开发三步曲:从预设到强化学习
  • 【限时解密】头部咨询公司内部禁用的ChatGPT决策辅助工具黑名单:12个触发监管红线的操作模式
  • CUSUM控制图在Python金融风控中的应用:如何用它监测交易策略的失效?
  • DSM在零延迟仿真中的异常行为分析与解决方案
  • MIT-BIH ECG信号预处理避坑指南:中值滤波窗大小设置与边界失真处理实战
  • 品牌设计全案使用后交付偏差先分阶段确认验收标准
  • 告别命令行恐惧:Windows 10/11 下 SRA Toolkit 安装与配置保姆级图文教程
  • ChatGPT生日派对创意避坑指南:87%新手踩中的3类提示陷阱及权威修复路径
  • 4J36板材怎么选?国内主流厂家盘点,助您快速匹配优质供应商 - 品牌2025
  • Text to SQL准确率为什么上不去?三个核心难点
  • Mac IDEA 2026.1 Java开发痛点与智能化方案
  • 别再踩坑了!Ubuntu 20.04上TensorRT 8.x的deb安装保姆级避坑指南
  • 量子溢出检测电路在生物医学图像处理中的应用与Qiskit实现
  • 032、图像分类模型部署后精度下降?预处理管线一致性、归一化对齐与推理加速方案
  • Zotero 结合 Codex 打造智能学术工作流实战
  • 通过curl命令快速诊断taotoken api连接与认证问题的排查方法
  • Linux内核里dma_map_sg()怎么把零散内存‘粘’成连续IOVA?一个SMMUv3驱动的实战解析