> ## Documentation Index
> Fetch the complete documentation index at: https://docs.arkffi.hmbill.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 使用外部預構建庫

> 集成外部 開發環境 項目編譯的 .so 庫到 arkffi 項目中

本指南演示如何將在 開發環境 中獨立編譯的 C/C++ 庫集成到 arkffi 項目中，並通過 `dlopen` 調用其函數。

## 步驟概覽

1. 在 開發環境 中創建 C/C++ 庫項目
2. 使用 HarmonyOS NDK 交叉編譯爲 `arm64-v8a` 架構
3. 將編譯產物放入 `entry/libs/arm64-v8a/`
4. 在 ArkTS 中通過 `dlopen` 加載並調用

***

## 1. 創建外部庫項目

自行創建 C/C++ 庫項目。

**源代碼** `library.cpp`：

```c theme={null}
#ifdef __cplusplus
extern "C" {
#endif

    const char* hello(void) {
        return "Hello, World!";
    }

#ifdef __cplusplus
}
#endif
```

<Note>
  `extern "C"` 防止 C++ 編譯器對函數名進行改編（name mangling），確保 `dlsym("hello")` 能正確找到符號。如果不加，編譯後的符號名會被改成 `_Z5hellov`，導致 `dlopen` 無法識別。
</Note>

**CMakeLists.txt**：

```cmake theme={null}
cmake_minimum_required(VERSION 3.3)
project(hello)
set(CMAKE_CXX_STANDARD 14)
add_library(hello SHARED library.cpp)
```

## 2. 交叉編譯爲 HarmonyOS

在 開發環境 中使用 HarmonyOS NDK 的交叉編譯器進行編譯：

```bash theme={null}
export TOOLCHAIN=/Applications/DevEco-Studio.app/contents/sdk/default/openharmony/native/llvm
export SYSROOT=/Applications/DevEco-Studio.app/contents/sdk/default/openharmony/native/sysroot

$TOOLCHAIN/bin/aarch64-linux-ohos-clang++ \
  --sysroot=$SYSROOT \
  -fPIC -shared \
  -o libhello.so \
  library.cpp
```

<Tip>
  確保使用 HarmonyOS NDK 的 `aarch64-linux-ohos-*` 工具鏈，而非 macOS 自帶的 Clang。`-fPIC` 是編譯共享庫必需的選項。
</Tip>

## 3. 放置編譯產物

將編譯生成的 `libhello.so` 放入項目的以下目錄：

```
entry/libs/arm64-v8a/libhello.so
```

<Note>
  如果項目還支持 `x86_64` 模擬器，需要在 `entry/libs/x86_64/` 下也放置對應架構的編譯產物。
</Note>

## 4. 配置 abiFilters

確保 `library/build-profile.json5` 中 `abiFilters` 包含編譯產物對應的架構：

```json theme={null}
{
  "buildOption": {
    "externalNativeOptions": {
      "path": "./src/main/cpp/CMakeLists.txt",
      "abiFilters": ["arm64-v8a"]
    }
  }
}
```

## 5. 在 ArkTS 中調用

```typescript theme={null}
import { dlopen, FFIType, CString, CFunction, JSCallback, Library } from 'arkffi';

const libhello = dlopen('libhello.so', {
  hello: { args: [], returns: FFIType.int64 },
});

const resultPtr = libhello.symbols.hello();
const resultStr = new CString(resultPtr);

console.log(resultStr.toString()); // -> "Hello, World!"

libhello.close();
```

### 注意事項

<Warning>
  * 返回 `const char*` 的函數**必須**使用 `returns: FFIType.int64` 而非 `returns: FFIType.CString`，因爲底層分發器將 `'s'` 編碼視爲參數類型，不處理爲返回類型。
  * 拿到指針後需通過 `CString` 類讀取實際字符串內容。
  * 確保庫已用 `extern "C"` 編譯，否則 `dlsym` 因名字改編而找不到函數。
</Warning>

## 故障排查

| 問題                               | 原因                | 解決                                            |
| -------------------------------- | ----------------- | --------------------------------------------- |
| `dlsym failed: Symbol not found` | C++ name mangling | 在源碼中添加 `extern "C"`                           |
| `cannot locate library`          | `.so` 不在正確路徑      | 檢查 `entry/libs/${OHOS_ARCH}/` 路徑              |
| `cannot locate symbol`           | `.so` 依賴的運行時庫缺失   | 確認使用 HarmonyOS NDK 編譯，而非宿主系統工具鏈               |
| 調用返回空字符串                         | 指針讀取錯誤            | 確認返回類型使用 `FFIType.int64` 而非 `FFIType.CString` |
