目录

1 简介

2 Lazarus要读写的文件夹放在哪?

3 增加 napi_init.cpp

4 修改原有的 libLazarusOHOS_Wrapper.cpp

5 编译libLazarusOHOS_Wrapper.cpp 

6 鸿蒙项目增加文件 libLazarusOHOS_Wrapper.d.ts

7 鸿蒙项目修改文件 QAbilityStage.ets

8 lazarus 程序增加一个单元 OHOSPaths

9 修改Lazarus程序主窗体代码

10 运行结果


本文基于秋风的10多篇lazarus鸿蒙开发博文及资源整理修改: 秋·风 - 博客园

1 简介

鸿蒙应用安装后,资源被自动解压到应用沙箱路径中,通过 context.resourceDir 获取目录后可直接以文件路径访问(只读)。如果需要读写操作,需要先将文件复制到 filesDir 沙箱目录中。Lazarus程序不能直接使用 context.resourceDir,需要采取另外办法取得该路径进行读写操作。另外,不同环境下,这些目录不同,不能写死在Lazarus程序中。

秋风的博客内容及资源,成功解决了鸿蒙与Lazarus的整合,在特定细节需求方面,需要自行修改完善。本文简要介绍鸿蒙打包lazarus程序,如何获取资源的准确路径。

2 Lazarus要读写的文件夹放在哪?

Lazarus需要读写的文件夹,假设是:cust_data ,放在鸿蒙项目目录:

\entry\src\main\resources\resfile

3 增加 napi_init.cpp

在1.LazarusOHOS_Wrapper文件夹中,增加 napi_init.cpp,负责从 AbilityContext 中提取路径,并提供给 Lazarus 侧使用。内容:

// 这个文件负责从 AbilityContext 中提取路径,并提供给 Lazarus 侧使用

// napi_init.cpp
#include <napi/native_api.h>
#include <cstring>
#include <hilog/log.h>

// 缓存真实沙箱路径(静态存储,与 OHOS_GetFilesDir 等共享)
static char g_realFilesDir[1024] = {0};
static char g_realCacheDir[1024] = {0};
static char g_realResourceDir[1024] = {0};
static bool g_pathsReady = false;

// 辅助函数:从 context 对象获取字符串属性并缓存
static bool GetAndCachePath(napi_env env, napi_value context,
                            const char* propertyName, char* buffer, size_t bufSize) {
    napi_value prop;
    napi_status status = napi_get_named_property(env, context, propertyName, &prop);
    if (status != napi_ok) {
        OH_LOG_ERROR(LOG_APP, "[NAPI] Failed to get property: %{public}s", propertyName);
        return false;
    }

    // 可能返回的是 resourceManager 或 FilePath 对象,需要区分处理
    // 对于 filesDir/cacheDir/resourceDir,在 context 下通常是直接字符串或 getter 函数
    // HarmonyOS API 9+ 中 context.filesDir 直接返回 string
    size_t strLen = 0;
    status = napi_get_value_string_utf8(env, prop, buffer, bufSize, &strLen);
    if (status != napi_ok) {
        OH_LOG_ERROR(LOG_APP, "[NAPI] Failed to get string for: %{public}s", propertyName);
        return false;
    }
    buffer[strLen] = '\0';
    OH_LOG_INFO(LOG_APP, "[NAPI] %{public}s = %{public}s", propertyName, buffer);
    return true;
}

// 外部可调用的初始化接口
extern "C" __attribute__((visibility("default"))) 
napi_value OHOS_InitPaths(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    if (status != napi_ok || argc < 1) {
        OH_LOG_ERROR(LOG_APP, "[NAPI] OHOS_InitPaths: No context provided");
        napi_value ret;
        napi_get_boolean(env, false, &ret);
        return ret;
    }

    napi_value context = args[0];

    // 依次获取路径
    bool success = true;
    success &= GetAndCachePath(env, context, "filesDir", g_realFilesDir, sizeof(g_realFilesDir));
    success &= GetAndCachePath(env, context, "cacheDir", g_realCacheDir, sizeof(g_realCacheDir));
    success &= GetAndCachePath(env, context, "resourceDir", g_realResourceDir, sizeof(g_realResourceDir));

    g_pathsReady = success;

    napi_value result;
    napi_get_boolean(env, success, &result);
    return result;
}

// Lazarus 侧调用的 C 接口(替代原来依赖 Qt 的函数)
extern "C" const char* OHOS_GetFilesDir() {
    if (g_pathsReady) return g_realFilesDir;
    else return "";   // 未初始化时返回空串
}

extern "C" const char* OHOS_GetCacheDir() {
    if (g_pathsReady) return g_realCacheDir;
    else return "";
}

extern "C" const char* OHOS_GetResourceDir() {
    if (g_pathsReady) return g_realResourceDir;
    else return "";
}

// NAPI 模块注册
static napi_value RegisterInitPaths(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {
        {"OHOS_InitPaths", nullptr, OHOS_InitPaths, nullptr, nullptr, nullptr, napi_default, nullptr}
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = RegisterInitPaths,
    .nm_modname = "lazarusohos",
    .nm_priv = nullptr,
    .reserved = {0},
};

extern "C" __attribute__((constructor)) void RegisterModule(void) {
    napi_module_register(&demoModule);
}

4 修改原有的 libLazarusOHOS_Wrapper.cpp

内容为:

// libLazarusOHOS_Wrapper.cpp - HarmonyOS Qt5 + Lazarus LCL wrapper
// QApplication is already created by Qt OHOS plugin (libqohos.so)
// before this library is loaded. Do NOT create another one.

// 现在 OHOS_GetFilesDir 等函数已经在 napi_init.cpp 中实现了,
// 原来的 wrapper 就不需要再通过 Qt 获取路径了,
// 可以保留 wrapper 的 main 函数用于加载 Lazarus 库,
// 但删除路径获取代码。

// libLazarusOHOS_Wrapper.cpp
#include <cstdio>
#include <dlfcn.h>

// 路径函数声明由 napi_init.cpp 提供,这里不用定义
extern "C" const char* OHOS_GetFilesDir();
extern "C" const char* OHOS_GetCacheDir();
extern "C" const char* OHOS_GetBundleDir(); // 不再使用,可留空实现返回 ""

typedef void (*InitAndShowFormFunc)();

extern "C" int main(int, char**) {
    // 此时路径可能还未初始化,打印为空
    fprintf(stderr, "[Wrapper] OHOS_GetFilesDir: %s\n", OHOS_GetFilesDir());
    fprintf(stderr, "[Wrapper] OHOS_GetCacheDir: %s\n", OHOS_GetCacheDir());

    // QApplication already exists from libqohos.so
    void* lib = dlopen("libOHOS_QT_Lazarus.so", RTLD_NOW | RTLD_GLOBAL);
    if (!lib) {
        fprintf(stderr, "[Wrapper] dlopen failed: %s\n", dlerror());
        return 1;
    }

    InitAndShowFormFunc InitAndShowForm = (InitAndShowFormFunc)dlsym(lib, "InitAndShowForm");
    if (!InitAndShowForm) {
        fprintf(stderr, "[Wrapper] dlsym failed: %s\n", dlerror());
        dlclose(lib);
        return 1;
    }

    InitAndShowForm();
    return 0;
}

5 编译libLazarusOHOS_Wrapper.cpp 


执行以下指令,生成 aarch64 格式的so文件。为了访问hap包安装后的资源文件目录和沙箱目录,此处经过修改,与原文有所不同。原文指令:
lazarus鸿蒙开发3:编译libLazarusOHOS_Wrapper.so - 秋·风 - 博客园

注意先删除原来存在的文件: libLazarusOHOS_Wrapper.so

SET NATIVE_OHOS_SDK=d:/fpc4ohos/sdk/default/openharmony/native
SET SYSROOT=%NATIVE_OHOS_SDK%/sysroot
SET QT5DIR=d:/oh/Qt-5.12.12-ohos-aarch64

%NATIVE_OHOS_SDK%\llvm\bin\clang++ -shared ^
  -o libLazarusOHOS_Wrapper.so ^
  -I"%SYSROOT%\usr\include" ^
  -I"%SYSROOT%\usr\include\napi" ^
  -I"%QT5DIR%\include" ^
  -I"%QT5DIR%\include\QtCore" ^
  -I"%QT5DIR%\include\QtGui" ^
  -I"%QT5DIR%\include\QtWidgets" ^
  -L"%SYSROOT%\usr\lib\aarch64-linux-ohos" ^
  -L"%QT5DIR%\lib" ^
  -lace_napi.z ^
  -lQt5Core ^
  -lQt5Gui ^
  -lQt5Widgets ^
  -lc ^
  -ldl ^
  --sysroot="%SYSROOT%" ^
  -target aarch64-linux-ohos ^
  -fPIC ^
  napi_init.cpp ^
  libLazarusOHOS_Wrapper.cpp

 执行以下指令,生成 x86_64 格式的so文件。为了访问hap包安装后的资源文件目录和沙箱目录,此处经过修改,与原文有所不同。原文指令:
lazarus鸿蒙开发3:编译libLazarusOHOS_Wrapper.so - 秋·风 - 博客园

SET NATIVE_OHOS_SDK=d:/fpc4ohos/sdk/default/openharmony/native
SET SYSROOT=%NATIVE_OHOS_SDK%/sysroot
SET QT5DIR=d:/oh/Qt-5.12.12-ohos-x86_64

%NATIVE_OHOS_SDK%\llvm\bin\clang++ -shared ^
  -o libLazarusOHOS_Wrapper.so ^
  -I"%SYSROOT%\usr\include" ^
  -I"%SYSROOT%\usr\include\napi" ^
  -I"%QT5DIR%\include" ^
  -I"%QT5DIR%\include\QtCore" ^
  -I"%QT5DIR%\include\QtGui" ^
  -I"%QT5DIR%\include\QtWidgets" ^
  -L"%SYSROOT%\usr\lib\x86_64-linux-ohos" ^
  -L"%QT5DIR%\lib" ^
  -lace_napi.z ^
  -lQt5Core ^
  -lQt5Gui ^
  -lQt5Widgets ^
  -lc ^
  -ldl ^
  --sysroot="%SYSROOT%" ^
  -target x86_64-linux-ohos ^
  -fPIC ^
  napi_init.cpp ^
  libLazarusOHOS_Wrapper.cpp

6 鸿蒙项目增加文件 libLazarusOHOS_Wrapper.d.ts

D:\fpc4ohos\ohos_demo\2.ohos_hap_project\entry\src\main\ets\common\libLazarusOHOS_Wrapper.d.ts

内容为:

// entry/src/main/ets/common/libLazarusOHOS_Wrapper.d.ts
export interface Wrapper {
  OHOS_InitPaths(context: object): boolean;
}

declare const wrapper: Wrapper;
export default wrapper;

7 鸿蒙项目修改文件 QAbilityStage.ets

D:\fpc4ohos\ohos_demo\2.ohos_hap_project\entry\src\main\ets\qabilitystage\QAbilityStage.ets

代码:

// import lazarushos from 'libLazarusOHOS_Wrapper.so';   // 新增导入
import lazarushos from 'libLazarusOHOS_Wrapper.so';
import { Wrapper } from '../common/libLazarusOHOS_Wrapper';  // 导入接口

// import AbilityStage from '@ohos.app.ability.AbilityStage';
import QAbility from '../qability/QAbility';
import QChildProcess from '../process/QChildProcess';
import QtUtils from '../qability/QtUtils';
import Want from '@ohos.app.ability.Want';
import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';
import qpa from 'libqohos.so';
import {APP_LIBRARY_NAME, LOG_DOMAIN, LOG_TAG} from '../common/QtAppConstants';
import { AbilityStage } from '@kit.AbilityKit';
import { fileIo } from '@kit.CoreFileKit';



export default class QAbilityStage extends AbilityStage {

  // setting "appArgs" overrides arguments from initial Want object
  private static appArgs?: Array<string>;

  private static setupQtApplicationCalled: boolean = false;

  private static initQtAppContextImpl(appContext: common.ApplicationContext, abilityClassName: string, uiExtensionMode: boolean): void {
    if (!QAbilityStage.setupQtApplicationCalled) {
      hilog.info(LOG_DOMAIN, LOG_TAG, 'ccc QAbilityStage::initQtAppContextImpl: init with uiExtensionMode=' + uiExtensionMode);
      QAbilityStage.setupQtApplicationCalled = true;
      qpa.setupQtApplication({
        appContext: appContext,
        modules: QtUtils.getModulesMapForQt(),
        appName: APP_LIBRARY_NAME,
        appArgs: QAbilityStage.appArgs,
        abilityClassName: abilityClassName,
        uiExtensionMode: uiExtensionMode,
        _unusedQChildProcess: new QChildProcess(),
      });
    } else {
      hilog.info(LOG_DOMAIN, LOG_TAG, 'ccc QAbilityStage::initQtAppContextImpl: already initialized');
    }
  }

  public static initQtAppContextIfNeeded(appContext: common.ApplicationContext): void {
    QAbilityStage.initQtAppContextImpl(appContext, QAbility.name, false);
  }

  public static initQtAppContextInUiExtensionMode(appContext: common.ApplicationContext, abilityClassName: string): void {
    QAbilityStage.initQtAppContextImpl(appContext, abilityClassName, true);
  }

  // 直接使用 fileIo.copyDirSync(适用于层级少、文件小的场景)
  // public static copyDirSimple(srcPath: string, destPath: string): void {
  //   // 检查目标目录是否已存在
  //   let destExists: boolean = fileIo.accessSync(destPath, fileIo.AccessModeType.EXIST);
  //   if (destExists) {
  //     console.info(`目标目录已存在,跳过复制: ${destPath}`);
  //     return;
  //   }
  //   // 直接复制整个目录
  //   fileIo.copyDirSync(srcPath, destPath);
  // }

  onCreate(): void {
    hilog.info(LOG_DOMAIN, LOG_TAG, 'QAbilityStage::onCreate()');
    qpa.handleAbilityStageOnCreate(this);

    // 获取 AbilityStageContext
    let context: common.AbilityStageContext = this.context;

    // 初始化 wrapper 的路径缓存(必须在 wrapper.main 执行之前)
    try {

      // 直接通过 as 断言调用,杜绝 any 类型
      let initResult: boolean = (lazarushos as Wrapper).OHOS_InitPaths(context);
      console.info(`ccc [QAbilityStage] OHOS_InitPaths result: ${initResult}`);

    } catch (e) {
      console.error(`ccc [QAbilityStage] OHOS_InitPaths failed: ${e}`);
    }

    // 获取 resfile 资源目录(只读)和 filesDir 沙箱目录(可读写)
    let resourceDir: string = context.resourceDir;
    let filesDir: string = context.filesDir;

    console.info(`ccc resourceDir: ${resourceDir}`);
    console.info(`ccc filesDir: ${filesDir}`);


  }

  onNewProcessRequest(want: Want): string {
    hilog.info(LOG_DOMAIN, LOG_TAG, 'QAbilityStage::onNewProcessRequest: want.parameters: ' + JSON.stringify(want.parameters));
    QAbilityStage.initQtAppContextIfNeeded(this.context.getApplicationContext());
    let processKey: string = qpa.handleAbilityStageOnNewProcessRequest(this, want);
    hilog.info(LOG_DOMAIN, LOG_TAG, 'ccc QAbilityStage::onNewProcessRequest: processKey: "' + processKey + '"');
    return processKey;
  }

  onAcceptWant(want: Want): string {
    hilog.info(LOG_DOMAIN, LOG_TAG, 'ccc QAbilityStage::onAcceptWant: want.parameters: ' + JSON.stringify(want.parameters));
    QAbilityStage.initQtAppContextIfNeeded(this.context.getApplicationContext());
    let instanceKey: string = qpa.handleAbilityStageOnAcceptWant(this, want);
    hilog.info(LOG_DOMAIN, LOG_TAG, 'ccc QAbilityStage::onAcceptWant: instanceKey: "' + instanceKey + '"');
    return instanceKey;
  }

  onDestroy() {
    hilog.info(LOG_DOMAIN, LOG_TAG, 'ccc QAbilityStage::onDestroy()');
    qpa.handleAbilityStageOnDestroy(this);
  }
}

8 lazarus 程序增加一个单元 OHOSPaths

unit OHOSPaths;

interface

uses
  SysUtils;

//function OHOS_GetFilesDir: PChar; cdecl; external 'libLazarusOHOS_Wrapper.so';
//function OHOS_GetCacheDir: PChar; cdecl; external 'libLazarusOHOS_Wrapper.so';
//function OHOS_GetBundleDir: PChar; cdecl; external 'libLazarusOHOS_Wrapper.so';

function OHOS_GetFilesDir: PChar; cdecl; external 'libLazarusOHOS_Wrapper.so';
function OHOS_GetCacheDir: PChar; cdecl; external 'libLazarusOHOS_Wrapper.so';
function OHOS_GetResourceDir: PChar; cdecl; external 'libLazarusOHOS_Wrapper.so';

//function GetOHOSFilesPath: string;
//function GetOHOSCachePath: string;
//function GetOHOSBundlePath: string;

function GetOHOSFilesPath: string;
function GetOHOSResourcePath: string;
function GetOHOSCachePath: string;

implementation

//function SafeStr(P: PChar): string; inline;
//begin
//  if P = nil then Result := '' else Result := StrPas(P);
//end;
//
//function GetOHOSFilesPath: string;
//begin
//  Result := SafeStr(OHOS_GetFilesDir);
//  if Result <> '' then Result := IncludeTrailingPathDelimiter(Result);
//end;
//
//function GetOHOSCachePath: string;
//begin
//  Result := SafeStr(OHOS_GetCacheDir);
//  if Result <> '' then Result := IncludeTrailingPathDelimiter(Result);
//end;
//
//function GetOHOSBundlePath: string;
//begin
//  Result := SafeStr(OHOS_GetBundleDir);
//  if Result <> '' then Result := IncludeTrailingPathDelimiter(Result);
//end;

function GetOHOSFilesPath: string;
begin
  Result := string(AnsiString(OHOS_GetFilesDir));
end;

function GetOHOSCachePath: string;
begin
  Result := string(AnsiString(OHOS_GetCacheDir));
end;

function GetOHOSResourcePath: string;
begin
  Result := string(AnsiString(OHOS_GetResourceDir));
end;

end.

9 修改Lazarus程序主窗体代码

uses 
  OHOSPaths;

......

  showmessage(format('GetOHOSFilesPath: %s, GetOHOSResourcePath: %s', [GetOHOSFilesPath, GetOHOSResourcePath]));


......

10 运行结果

DevEco:

模拟器中,Lazarus程序弹出窗口:

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐