oh_tool 0.1.0

dotnet tool install --global oh_tool --version 0.1.0
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local oh_tool --version 0.1.0
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=oh_tool&version=0.1.0
                    
nuke :add-package oh_tool --version 0.1.0
                    

oh_dotnettool

HarmonyOS .NET 工具集。当前主要内容:

  • oh_tool — HarmonyOS 设备连接与调试 CLI
  • HAP 模板工作流 — 用 HAP 模板替换 .so 实现快速迭代
  • csharp/logic/ — C# NativeAOT 示例(编译成 liblogic.so 注入 HAP)

oh_tool 安装

依赖:

  • .NET 10 SDK
  • HarmonyOS 真机(已开启网络调试,persist.hdc.mode == tcp
  • ~/bin 在 PATH 中
./install.sh

脚本做三件事:dotnet build → symlink 到 ~/bin/oh_tool → 检查 PATH。

oh_tool 用法

用法: oh_tool <命令> [参数]

命令:

  open    打开 URL 或文件(--webview / --webhost 修饰符)

  view    用浏览器预览文件(open --webview 别名)
          支持 .md .markdown .html .htm .cs .js .txt .json .xml + 图片
          Markdown: marked.js 渲染
          HTML: 原样,相对路径资源一并复制
          代码 (.cs .js .json .xml): 内嵌高亮
          文本 (.txt): <pre> 包裹
          图片: 居中显示
          Markdown/HTML 自动检测相对引用 (图片/css/js),复制并保持路径

  host    以文件或目录为 webroot 起临时 HTTP 服务器(open --webhost 别名)
          oh_tool host .         浏览当前目录(最常用)
          oh_tool host ./docs    浏览指定目录
          oh_tool host index.html  仅服务单个 HTML 文件
          目录模式: 左侧目录树 + 右侧预览,懒加载子目录

  hap     HAP 操作(详细参数请输入 oh_tool hap)

常见用法

# 浏览当前目录(树形 + 预览)
oh_tool host .

# 用浏览器预览 Markdown / 代码 / 图片
oh_tool view README.md
oh_tool view src/Program.cs
oh_tool view docs/diagram.png

# 安装/启动 HAP
oh_tool hap install dist/entry.hap
oh_tool hap run light.hx.dotnet

HAP 模板工作流

为什么用模板

HAP 本质是 ZIP,包含 ArkTS 字节码、资源、libs/*.somodule.json 等。完整构建需要 DevEco Studio 跑完 ArkTS 编译 + 资源编译 + 打包 + 签名,单次 30-60 秒。

如果只改 native(C++/C#),ArkTS 和资源没动,重打整个 HAP 是浪费。模板替换法:把 DevEco 构建出的 HAP 作为模板,每次迭代只换里面的 .so + 重签,5-10 秒搞定。

传统:    改 .cpp → cmake → DevEco 全量构建 → 签名 → 安装     (30-60s)
模板替换: 改 .cpp → cmake → 解 HAP → 换 .so → 打包 → 签名      (5-10s)

准备模板(一次性)

用 DevEco Studio 构建 hapsrc/loader/,把产物复制为模板:

cp hapsrc/loader/entry/build/default/outputs/default/entry-default-signed.hap \
   hapsrc/loader/dist/template.hap

何时需要重建模板:

  • 改了 ArkTS 代码 (*.ets)
  • 改了资源 (resources/)
  • 改了 module.json
  • 新增了 .so 依赖库

仅改 native 代码 → 不需要重建模板

用 .NET (NativeAOT) 开发 native

C# 可以通过 NativeAOT 编译成 HarmonyOS 能加载的 .so。仓库里的 csharp/logic/ 是示例。

1. C# 项目配置(参考 csharp/logic/logic.csproj):

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <RuntimeIdentifier>ohos-arm64</RuntimeIdentifier>
    <PublishAot>true</PublishAot>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    <AssemblyName>logic</AssemblyName>
    <OutputType>Library</OutputType>
  </PropertyGroup>
</Project>

关键点:

  • RuntimeIdentifier 必须是 ohos-arm64(不是 linux-arm64,否则会链接到 HarmonyOS 缺失的系统库)
  • PublishAot 输出 liblogic.so(基名取 AssemblyName)

2. C# 导出 app_init,C++ 通过 dlopen+dlsym 调用它并传入 bridge 的查询函数

bridge.cpp 维护一张 host 侧函数表,按名字(UTF-16 + strlen)查函数指针:

// bridge.cpp
static void* bridge_get_func_ptr(const char16_t* name, int strlen) {
    u16sv key(name, (size_t)strlen);
    if (key == u"log")                 return (void*)bridge_log_impl;
    if (key == u"gl.get_proc_utf8raw") return (void*)bridge_gl_get_proc_impl;
    if (key == u"asset.open")          return (void*)bridge_asset_open_impl;
    // ...
    return nullptr;
}

static void load_logic(void) {
    void* h = dlopen("liblogic.so", RTLD_NOW | RTLD_LOCAL);
    auto init = (void (*)(OHBridge_GetFuncPtr))dlsym(h, "app_init");
    init(&bridge_get_func_ptr);  // 把 host 查询函数传给 C#
}

C# 侧 app_init 接收查询函数,缓存需要的 host 接口,然后注册自己的回调(on_render_frame 等):

// Logic.cs
private static delegate* unmanaged<char*, int, void*> _get;  // bridge_get_func_ptr
private static delegate* unmanaged<int, char*, int, void> _log;

[UnmanagedCallersOnly(EntryPoint = "app_init")]
public static void AppInit(delegate* unmanaged<char*, int, void*> get)
{
    _get = get;
    _log = (delegate* unmanaged<int, char*, int, void>) Query("log");
    // ... 查 gl.get_proc_utf8raw / asset.* / SetCallback 等

    RegisterCallback("on_render_frame", &OnRenderFrame);
    RegisterCallback("on_shutdown",      &OnShutdown);
}

ABI 约定:

  • 字符串参数:UTF-16 char* + 显式 strlen(C# 是 UTF-16,C++ 侧用 char16_t
  • 例外:gl.get_proc_utf8raw 是原始 UTF-8 const char* 不带 strlen,省一次转码
  • 回调:C# [UnmanagedCallersOnly] 静态方法,通过 bridge 的 SetCallback(name, ptr) 注册

详见 docs/native-bridge-api.md

3. 编译

dotnet publish csharp/logic -r ohos-arm64 -c Release
# 产物: csharp/logic/bin/Release/net10.0/ohos-arm64/publish/logic.so

4. 注入 HAP(推荐 oh_tool hap quickpack):

写一个 oh_tool_pack.json 描述怎么打包:

{
  "output": "hapsrc/loader/dist/quickpack-test.hap",
  "template": "hapsrc/loader/dist/template.hap",
  "sign_config": "sign-config.json",
  "libs": [
    {"src": "hapsrc/loader/entry/src/main/cpp/build/libsurface_provider.so",
     "dst": "libs/arm64-v8a/libsurface_provider.so"},
    {"src": "csharp/logic/bin/Release/net10.0/ohos-arm64/publish/logic.so",
     "dst": "libs/arm64-v8a/liblogic.so"}
  ],
  "resource_dir": "hapsrc/loader/entry/src/main/resources/rawfile"
}

签名参数走单独的 JSON(密码不进配置文件):

// sign-config.json
{
  "keystore": "/path/to/keystore.p12",
  "cert":     "/path/to/cert.cer",
  "profile":  "/path/to/profile.p7b",
  "keyAlias": "app_key"
}

执行:

oh_tool hap quickpack -keyPwd 123456 -keystorePwd 123456

quickpack 自动:解模板 → 替换 .so → 注入 rawfile 资源 → 重新打包 → 签名。

5. 安装测试

oh_tool hap install hapsrc/loader/dist/quickpack-test.hap
oh_tool hap run light.hx.dotnet

详见 docs/hap-workflow/

项目结构

oh_dotnettool/
├── src/
│   └── oh_tool/              # oh_tool CLI
│       ├── Program.cs
│       ├── OpenCommands.cs    # open/view/host
│       ├── HapCommands.cs     # hap install/unpack/pack/sign/quickpack/run/info
│       ├── WebHostCommands.cs # HTTP 服务器
│       ├── HtmlResources.cs   # 模板渲染 + 资源扫描
│       └── content/           # HTML 模板(markdown/code/explorer)
├── hapsrc/loader/            # GLES loader HAP(DevEco 项目)
│   ├── entry/src/main/cpp/   # C++ bridge
│   └── dist/template.hap     # HAP 模板
├── csharp/logic/             # C# NativeAOT 示例(产出 liblogic.so)
├── docs/
│   ├── hap-workflow/         # HAP 模板工作流详细文档
│   ├── native-bridge-api.md  # bridge ⇄ logic 函数指针 ABI
│   └── native-bridge-arch.md # 架构
├── install.sh                # oh_tool 安装脚本
└── rebuild-so.sh             # 仓库内快速重编译(quickpack 的前身)

环境

  • 运行:HarmonyOS 真机(HongMeng Kernel 1.12.0)
  • hdc:/data/service/hnp/bin/hdc
  • 签名证书:/storage/Users/currentUser/Documents/signner/
  • 打包/签名工具:从 builder_oh 仓库获取
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
0.1.0 119 7/6/2026