Skip to main content

Shiply Windows 远程资源 SDK 集成

一、隐私安全说明

Shiply 资源发布 Windows SDK
版本:1.2.17
更新时间:2024年12月25日
SDK介绍:为移动开发者提供专业的资源分发能力,帮助开发者动态下载远程资源文件。
服务提供方:深圳市腾讯计算机系统有限公司
接入指引:《Shiply资源发布SDK接入指引》
隐私保护规则:《Shiply资源发布SDK个人信息保护规则》
合规使用指南:《Shiply资源SDK合规使用指南》

前置概念:接入前请先阅读 通用概念,了解各平台共用的 appId/appKey/userId/deviceId/target/environment/资源加载策略 等核心概念,避免跨平台混淆。

二、SDK 整体架构

分层结构

Windows SDK 采用三层架构:

┌──────────────────────────────────────────────────────┐
│ C API 公开层 │
│ reshub_c.h / rdelivery_c_api.h │
│ (extern "C" 导出函数,DLL 边界安全) │
├──────────────────────────────────────────────────────┤
│ C API 桥接/实现层 │
│ reshub_c.cpp / reshub_service_c.cpp │
│ ResHubInstanceHolder (void* 句柄包装) │
│ ResHubDependencyInjector (依赖注入适配) │
├──────────────────────────────────────────────────────┤
│ C++ 核心层 │
│ ResHubCenter (单例, SDK 总入口) │
│ ├── ResHub (资源操作核心, 每个 App+Env 一个实例) │
│ │ ├── ResHubConfigFetchManager (配置拉取管理) │
│ │ │ └── rdelivery::RDeliveryService (底层引擎)│
│ │ ├── ResHubResLoader (单资源加载器) │
│ │ └── ResHubLocalResManager (本地资源管理) │
│ └── ResHubParam (全局初始化参数) │
└──────────────────────────────────────────────────────┘

核心类职责

  • ResHubCenter(单例):SDK 唯一入口。initSDK() 初始化全局参数,resHubWithAppId()(appId + env) 维度创建/缓存 ResHub 实例。
  • ResHub:每个 App+Env 对应一个实例,负责所有资源操作(加载、锁定、预加载、删除、状态检查)。
  • ResHubConfigFetchManager:每个 ResHub 拥有一个,内部创建独立的 RDeliveryService 实例(system_id=10010),负责拉取资源元数据(版本号、下载URL、MD5等)。

资源 SDK 与配置 SDK 的关系

两者底层使用同一个 rdelivery::RDeliveryService C++ 类,但是不同的实例,配置不同的 system_id

                     ┌─ ResHub (资源SDK) ─┐
│ system_id=10010 │
RDelivery C++ 核心 ──┤ ├── 资源元数据拉取 + 文件下载
(RDeliveryService) │ 内部自动管理 │
└────────────────────┘

┌─ 独立配置SDK ──────┐
│ system_id=10001 │
RDelivery C++ 核心 ──┤ ├── 开关/配置值拉取
(RDeliveryService) │ 用户自行创建管理 │
└────────────────────┘

ResHub 的 RDeliveryService 实例由其内部自动管理,用户不需要(也不应该)直接操作。

最小集成步骤(快速开始)

// 1. 引入头文件
#include "reshub/c_api/include/reshub_c.h"
#include "reshub/c_api/include/reshub_entities_c.h"
#include "reshub/c_api/include/reshub_callbacks_c.h"

// 2. 设置日志回调(可选,强烈建议)
setResHubLogImpl(myLogFunction);

// 3. 填充初始化参数(⚠️ 必须先 memset 清零)
struct ResHubParam_C params;
memset(&params, 0, sizeof(params));
params.app_version = "1.0.0";
params.qimei = "device_id";
params.device_type = "Windows PC";
params.system_version = "Windows 10";
params.platform = ResHubPlatformWindows_C;
params.environment = 0; // 0=Release
params.res_storage_path = "C:\\app\\reshub";
params.res_config_storage_path = "C:\\app\\reshub_cfg";

// 4. 初始化 SDK 中心
initResHubNative(&params);

// 5. 获取 ResHub 实例
struct ResHubAppInfo_C appInfo;
memset(&appInfo, 0, sizeof(appInfo));
appInfo.appId = "your_app_id";
appInfo.appKey = "your_app_key";
appInfo.env = "";
void* instance = getResHubNative(appInfo);

// 6. 加载资源
LoadCompleteBlock complete = { NULL, myCallback };
loadWithIdNative(instance, "my_res", NULL, &complete);

// 7. 释放(逆序)
releaseResHubNative(instance);

以上是最小可运行骨架,完整的字段表、API 参考和注意事项见后续章节。

三、为何使用 C 接口

Windows 平台使用 C API(非 C++ 类)提供 SDK 能力。这是为确保 ABI 兼容性的刻意设计,原因有三:

  1. C ABI 稳定,C++ ABI 不稳定:不同 MSVC 版本(VS 2019/2022/2025)编译的 C++ 库互相不兼容,符号修饰(name mangling)、异常处理、虚表布局都可能不同。C 接口无此问题。
  2. 运行时库(CRT)匹配:SDK 以 /MT(静态 CRT)编译。若使用 /MD(动态 CRT)链接 C++ 接口,std::stringstd::shared_ptr 等类型的内存布局和分配器不同,导致堆损坏。C 接口的简单类型(指针、枚举、基本类型)不受 CRT 差异影响。
  3. 跨语言绑定:C 接口可直接用于其他语言(C#/Rust/Go)的 FFI 绑定,无中间层开销。

因此所有 SDK 头文件均以 _c.h 后缀暴露 C 函数和 C 结构体,禁止直接使用 SDK 内部的 C++ 类。

四、SDK 获取

预编译包(推荐)

下载 SDK 和 Demo:ShiplyCppSDK

参数说明
versionSDK 版本,当前 1.2.17
variantTencentCommercialQQ

ZIP 内部结构(以 Tencent x64 为例):

libshiply-Tencent-v1.2.17.zip
├── x64_Release/
│ ├── shiply.dll # 主动态库
│ ├── shiply.pdb # 调试符号
│ └── shiply.lib # 导入库
├── x86_Release/ # Win32 构建
├── arm64_Release/ # ARM64 构建
└── include/ # C 头文件

QQ 变体额外依赖:QQ 变体的认证链路不同,链接后还需引入 crypto.dllssl.dll(SDK 包内含)。

从源码构建

环境要求

  • Visual Studio 2022(MSVC v143 工具链)
  • CMake 3.16+
  • Windows 10 SDK 或更新

构建命令

cd rdelivery-cpp

# 单架构构建
scripts\build_windows_sdk.bat x64 Tencent

# 全架构构建(x64, x86, arm64)
scripts\build_windows_sdk.bat all Commercial

产出位置:rdelivery-cpp/build_windows/{arch}_Release/

MSVC 编译标志

标志说明
/MT静态链接 CRT(发布方必须与消费方一致)
/EHaC++ 异常处理 + 结构化异常(SEH)
/MP多进程并行编译
/Zi生成完整调试信息(PDB)

依赖项

依赖获取方式说明
OpenSSLlibs/{triplet} 预编译静态库,无需 vcpkg
ZLIBlibs/{triplet} 预编译静态库
MMKV源码编译(third_party/MMKV随 SDK 构建一起编译
minizip-ng源码编译Windows 中文路径解压必需

预编译依赖已随仓库提供在 libs/ 目录下,无需安装 vcpkg

五、CMake 集成

链接 SDK

CMake 构建产出三个动态库(rdelivery-cpp/CMakeLists.txt):

目标内容推荐场景
shiplyreshub + rdelivery 全部源码合并(单体库)推荐接入,避免跨 DLL CRT 边界
rdelivery仅配置 SDK只需配置拉取
reshub资源 SDK(内部链接 rdelivery)只需资源功能
# 方式 A(推荐):链接单体 shiply 动态库(含 rdelivery + reshub)
target_link_libraries(your_target shiply)
target_include_directories(your_target PRIVATE ${SHIPLY_CPP_ROOT})

# 方式 B:仅需配置功能
target_link_libraries(your_target rdelivery)
target_include_directories(your_target PRIVATE ${SHIPLY_CPP_ROOT})

# 方式 C:仅需资源功能(内部仍链接 rdelivery)
target_link_libraries(your_target reshub)
target_include_directories(your_target PRIVATE ${SHIPLY_CPP_ROOT})

系统依赖

find_package(Threads REQUIRED)
target_link_libraries(your_target ${CMAKE_THREAD_LIBS_INIT})

六、初始化

#include "reshub/c_api/include/reshub_c.h"
#include "rdelivery/c_api/include/rdelivery_c_api.h"

// ⚠️ CRITICAL: 任何包含函数指针的结构体必须在填值前清零
// MSVC Debug 模式用 0xCC 填充未初始化栈内存,
// 结构体中残留的 0xCCCCCCCC 作为函数指针调用会立即崩溃。

// 1. 构造依赖注入句柄(可选,自定义网络/下载/解压时用)
ResHubDependencyInjectorHandle dep = ResHubDependencyInjector_Create();
// ResHubDependencyInjector_SetNetworkCallbacks(dep, &myNetworkVTable);
// ResHubDependencyInjector_SetDownloadCallbacks(dep, &myDownloadVTable);
// ResHubDependencyInjector_SetUnzipCallbacks(dep, &myUnzipVTable);
// ResHubDependencyInjector_SetAutoUnzipCallbacks(dep, &myAutoUnzipVTable);

// 2. 构造 ResHubParam_C(⚠️ 必须先 memset 清零)
struct ResHubParam_C params;
memset(&params, 0, sizeof(params)); // 必须!避免 MSVC Debug 0xCC 填充
params.app_version = "1.0.0";
params.system_version = "10.0";
params.device_type = "PC";
params.qimei = "deviceQimei";
params.platform = ResHubPlatformWindows_C; // 平台 = 9 (Windows)
params.environment = 0; // 0=Release, 1=Test, 2=Pre(预发布)
params.res_config_storage_path = "C:\\app\\reshub_cfg";
params.res_storage_path = "C:\\app\\reshub";

// ⚠️ enable_diff_patch 默认值为 TRUE,但 memset 会将其置为 FALSE
// 如果需要差分补丁,必须在 memset 之后显式设置:
params.enable_diff_patch = true; // 按需设置,默认 TRUE 是推荐值

// 3. 初始化 SDK 中心
initResHubNativeWithDependencyInjector(&params, dep);

// 4. 获取 App 实例
// ⚠️ 字段名为 camelCase: appId, appKey(非 snake_case)
struct ResHubAppInfo_C appInfo;
memset(&appInfo, 0, sizeof(appInfo)); // MSVC Debug 安全做法
appInfo.appId = "your_app_id"; // 注意:appId 非 app_id
appInfo.appKey = "your_app_key"; // 注意:appKey 非 app_key
appInfo.env = ""; // logicEnv,空字符串表示默认环境
appInfo.target = 0; // PullTarget: 0=Project, 1=App, 2=Both

void* instance = getResHubNative(appInfo);

配置 SDK 补充初始化

如果单独使用配置 SDK(RDelivery),Windows 上必须在创建实例后显式初始化结构体:

// 创建配置服务
RDConfig config;
RDConfig_Init(&config); // 必须先 Init 清零函数指针
// ... 填入 config 字段 ...

RDEventListener listener;
RDEventListener_Init(&listener); // 必须先 Init 清零函数指针
// ... 填入 listener 回调 ...

RDConfig_Init()RDEventListener_Init() 内部执行 memset(ptr, 0, sizeof(*ptr)),避免 MSVC Debug 的 0xCC 填充问题。

七、ResHubParam_C 完整字段表

真值源:reshub_entities_c.h。结构体包含 24 个字段,分类如下。

必填字段

字段类型说明
app_versionconst char*应用版本号
qimeiconst char*设备唯一标识
device_typeconst char*设备型号
system_versionconst char*操作系统版本
platformint平台枚举,Windows 固定 ResHubPlatformWindows_C(= 9)

可选字段

字段类型默认说明
rdm_testboolfalseDebug 包标记,影响测试环境路由
res_storage_pathconst char*SDK 默认路径资源下载存储目录
res_config_storage_pathconst char*SDK 默认路径资源配置持久化目录
local_preset_res_pathconst char*NULL预置资源路径(打包进安装目录的资源)
multi_process_modeboolfalse多进程模式,开启后跨进程共享配置
is_main_processboolfalsemulti_process_mode 配合,标记主进程
environmentint0物理环境:0=Release, 1=Test, 2=Pre(预发布)
config_update_modeint参见枚举配置更新策略(按位掩码)
config_update_intervalintSDK 默认值配置定时更新间隔(秒)
custom_server_urlconst char*NULL独立部署域名,NULL 使用默认域名
variant_mapconst char*NULL业务变体映射,用于 AB 实验路由

高级字段

字段类型默认说明
enable_diff_patchboolTRUE差分补丁。⚠ 默认 TRUE 但 memset 变为 FALSE,需显式设置
enable_preset_res_validationboolfalse预置资源 MD5 校验(初始化阶段耗时)
fetch_project_when_app_onlyboolfalse仅拉取 App 配置时是否同时拉取项目配置
config_store_suffixconst char*NULL配置存储后缀,多账户隔离用

代理字段

字段类型说明
proxy_hostconst char*代理服务器地址
proxy_portint代理服务器端口
proxy_usernameconst char*代理认证用户名
proxy_passwordconst char*代理认证密码

其他

字段类型说明
dev_manufacturerconst char*设备制造商(如 "Dell"、"Lenovo")

八、资源加载 API

三套语义(Lock / Latest / Realtime Latest):

C 函数语义说明
resWithIdNative(instance, resId, needValidate)Lock 同步本地锁定版本,不发网络
latestResWithIdNative(instance, resId, needValidate)Latest 同步本地最新版本
loadWithIdNative(instance, resId, progress, completed)Lock 异步锁定版本,可能触发网络
loadLatestWithIdNative(instance, resId, progress, completed)Latest 异步本地配置中最新
loadRealtimeLatestWithIdNative(instance, resId, progress, completed)Realtime 异步强制实时请求配置
batchLoadWithIdsNative(instance, resIds, progress, completed)批量 Lock
batchLoadLatestWithIdsNative(instance, resIds, progress, completed)批量 Latest
batchLoadWithSceneIdNative(instance, sceneId, progress, completed)场景 Lock按场景 ID 批量
batchLoadLatestWithSceneIdNative(instance, sceneId, progress, completed)场景 Latest
preloadLatestWithIdNative / prebatchLoadLatestWithIdsNative预加载受 CDN 流量与拦截规则限制

回调类型(reshub_callbacks_c.h):LoadProgressBlockLoadCompleteBlockBatchLoadProgressBlockBatchLoadCompleteBlockAllConfigsCompletionBlockUpdateConfigsCompletionBlock

每个 Block 结构体包含两个字段:

字段类型说明
callbackRefvoid*用户透传上下文(调用方传入,回调中原样回传)
callback函数指针实际的回调函数(具体签名见对应 Block 类型定义)

示例LoadProgressBlock

LoadProgressBlock progress;
progress.callbackRef = this; // 透传上下文
progress.callback = [](void* ctx, int64_t received, int64_t total) {
// received: 已下载字节数, total: 总字节数
};

LoadCompleteBlock / BatchLoadProgressBlock / BatchLoadCompleteBlock 结构相同,仅 callback 签名不同(见 reshub_callbacks_c.h)。MSVC Debug 下必须初始化后使用。

典型使用流程

// 加载资源(异步,回调)
LoadProgressBlock progress = { NULL, MyProgressCallback };
LoadCompleteBlock completed = { NULL, MyCompletedCallback };
struct ResHubModel_C* m = loadWithIdNative(instance, "my_res", &progress, &completed);

// 同步读取本地
struct ResHubModel_C* local = resWithIdNative(instance, "my_res", true);

// 释放返回对象(必须!跨 DLL)
releaseResHubModelNative(local);

// 上报
reportResDownloadedNative(instance, "my_res", "taskId", 1); // version 参数类型为 int64_t

// 释放实例
releaseResHubNative(instance);
ResHubDependencyInjector_Destroy(dep);

九、配置拉取与管理

C 函数说明
updateAllConfigsWithCompletedNative(instance, completed)手动全量拉取配置
updateConfigWithKeysNative(instance, resIds, completed)按 key 拉取配置
latestConfigWithId(instance, resId)同步获取远程最新配置(不下载)
fetchedResConfigWithIdNative(instance, resId)同步本地配置(不网络)
fetchResConfigWithIdNative(instance, resId, completed)异步拉取配置(不下载)
allReshubConfigDictNative(instance)所有远程配置

十、状态/校验/删除/取消

C 函数说明
getStatusNative(instance, resId)资源可用状态
isResFileValidNative(instance, model)MD5 校验
deleteResNative(instance, resId)删除单个资源
deleteAllNative(instance)删除全部
cancelDownloadWithIdNative(instance, resId)取消下载

十一、上报

C 函数说明
reportResDownloadedNative(instance, resId, taskId, version)上报下载
reportResLoadedNative(instance, resId, taskId, version)上报加载
reportResPulledNative(instance, resId, taskId, version)上报拉取
setCustomPropertyValueNative(instance, value, key)更新自定义属性
reportToShiplyNative(instance, eventName, extInfo)上报自定义事件

代理

setProxyConfigNative(ProxyConfig_C* config) — 动态设置代理,传 NULL 清除。

十二、回调与监听

Windows ResHub C API 通过 reshub_callbacks_c.h 提供 6 个全局级回调设置函数:

// 日志回调
setResHubLogImpl(myLogFunction); // typedef int (*ResHubLog)(int level, const char *content);

// 资源生命周期回调
setOnResFirstLoaded(onResFirstLoaded); // void (*OnResFirstLoaded)(const char *resId, ResHubModel_C *model)
setOnResRefreshed(onResRefreshed); // void (*OnResRefreshed)(const char *resId, ResHubModel_C *model)

// 配置变更回调
setOnResConfigInfoPullTypeAllFinished(onFinished); // void (*)(ResHubModelDict *dict)
setOnConfigDeleted(onConfigDeleted); // void (*OnConfigDeleted)(const StringArray *resIds)

// 版本控制
setGetMinVersionDelegate(delegate); // int (*)(ResHubAppInfo_C *, const char *resId)

这些回调仅支持 set 操作(无 remove 对应函数)。如需清除,再次调用并传入 NULL 即可。所有回调必须在 initResHubNative 之前设置。

线程注意事项:回调执行线程由 ResHubParam_C.callbackOnMainThread 控制(默认值:false,即后台线程触发)。监听器在触发事件时所在的线程上执行。C API 不提供显式的线程安全保证。

十三、依赖注入

资源 SDK 依赖注入句柄

ResHubDependencyInjectorHandle ResHubDependencyInjector_Create();
void ResHubDependencyInjector_Destroy(handle);
void ResHubDependencyInjector_SetNetworkCallbacks(handle, const RDNetworkVTable_C*);
void ResHubDependencyInjector_SetDownloadCallbacks(handle, const RDDownloadVTable_C*);
void ResHubDependencyInjector_SetUnzipCallbacks(handle, const RDUnzipVTable_C*);
void ResHubDependencyInjector_SetAutoUnzipCallbacks(handle, const RDAutoUnzipVTable_C*);

未设置的模块走 SDK 默认实现。

配置 SDK 依赖注入

配置 SDK 的 DependencyInjector 结构体含:logger(ILog)、network(INetwork)、kvFactoryImpl(RAFTKVStorageFactoryProtocol)、rsa(IRSA)、aes(IAES)。

RDDependencyInjector_Create() / RDDependencyInjector_Destroy(handle)
RDDependencyInjector_SetNetworkCallbacks(handle, networkCallbacks)

十四、配置 SDK C API(rdelivery_c_api.h)

C 接口供 NAPI 桥接与跨语言绑定使用:

// ——实例生命周期——
RDService_Create() / RDService_Destroy(service)
// ——初始化(ktContext 为回调透传上下文)——
RDService_Init(service, db_root_path, config, ktContext, logCallback)
RDService_InitWithDependencyInjector(service, db_root_path, config, depInjector, ktContext, logCallback)
// ——读取(异步,回调返回)——
RDService_GetRDeliveryDataByKey(service, key, ktContext, OnGetDataResultCallback)
RDService_GetRDeliveryAllDataMap(service, ktContext, OnGetDataMapCallback)
// ——读取(同步,直接返回)——
RDService_SyncGetRDeliveryDataByKey(service, key)
RDService_SyncGetRDeliveryAllDataMap(service)
// ——远端拉取——
RDService_RequestFullRemoteData(service, custom_properties_json, ktContext, OnRequestCallback)
RDService_RequestBatchRemoteDataByScene(service, scene_id, custom_properties_json, ktContext, OnRequestCallback)
RDService_RequestBatchRemoteDataByScenes(service, scene_ids_json, custom_properties_json, ktContext, OnRequestCallback)
RDService_RequestSingleRemoteDataByKey(service, key, custom_properties_json, ktContext, OnRequestCallback)
RDService_RequestRemoteDataByKeys(service, keys_json, custom_properties_json, ktContext, OnRequestCallback)
// ——事件监听——
RDService_AddEventListener(service, listener) / RDService_RemoveEventListener(service, listener)
// ——用户/环境切换——
RDService_SwitchUserId(service, user_id, ktContext, OnCallback)
RDService_SwitchEnvironment(service, env, ktContext, OnCallback)
RDService_SwitchHostEnvironment(service, env, ktContext, OnCallback) // 仅 TAB 使用
// ——TAB 与代理——
RDService_SetTabBizSettings(service, settings)
RDService_SetProxyConfig(service, config)
// ——工具——
RDService_GetSDKVersion(service)
// ——结构体初始化(C 调用前必须先 Init,否则函数指针字段为未定义值)——
RDTabBizSettings_Init(bizSettings) / RDConfig_Init(config) / RDEventListener_Init(listener)

枚举(type_define.h):RDPlatform(9=Win,10=Mac,11=NodeServer,12=VisionOS,13=Harmony,14=HarmonyPad)、RDSwitchRDValueTypeRDHostEnvironment(Release/Test/Pre)、RDPullConfigTypeRDPullType(0=Unknown,1=Deprecated,2=Group,3=Config,4=All)。

RDeliveryConfig 核心字段

类别字段说明
必填app_idapp_key应用鉴权
用户user_id(guid)用户标识
设备device_id(qimei)、devModeldevManufacturer设备标识与型号
系统platform(RDPlatform)、os_versionsdk_version平台/系统/SDK 版本
环境logic_environmenthostEnvironmentbundle_id逻辑环境、物理环境、包名
自定义custom_propertieslanguageis_debug_package自定义属性、语言、调试标记
更新update_strategyupdate_interval更新策略与间隔
拉取system_idtarget(项目/App)、fixed_after_hit_keys拉取目标与固化 key
存储configStoreSuffixmultiProcessModeconfigStoreCryptKey存储隔离与加密
TABtab_biz_settings(RDTabBizSettings)TAB 模式场景与参数
网络custom_server_urlproxy_config(ProxyConfig)独立部署域名、代理
加密rsa_impl(IRSA)、aes_impl(IAES)注入的加解密实现

RDEventListener 结构体字段

类别字段说明
回调OnDataInitComplete本地数据初始化完成
回调OnDataAdd数据新增
回调OnDataChange数据变更
回调OnBizDataUpdate业务数据更新
回调OnAllDataUpdated全量数据更新完成
回调OnDataDelete数据删除
上下文OnDataInitCompleteKtContext各回调对应的透传上下文指针

线程安全:C API(RDService_*)的 listener 列表无内部同步AddEventListener / RemoveEventListener 需在同一线程调用或外部加锁同步。

十五、生命周期管理

释放顺序

释放必须在初始化方向逆序执行:

// 1. 先释放 App 实例
releaseResHubNative(instance);

// 2. 再销毁依赖注入句柄
ResHubDependencyInjector_Destroy(dep);
RDDependencyInjector_Destroy(dep);
RDService_Destroy(service);

跨 DLL 安全释放

SDK 通过自身堆分配内存,必须用 SDK 提供的 release 函数释放。禁止使用 free()delete 操作 SDK 返回的对象。

SDK 分配的对象必须使用的释放函数
ResHubModel_C*releaseResHubModelNative(model)
ResHubConfigItem_C*releaseResHubConfigItemNative(item)
ResHubModelDictreleaseResHubModelDictNative(dict)
ResHubConfigItemDictreleaseResHubConfigItemDictNative(dict)
ResHubErrorDictreleaseResHubErrorDictNative(dict)

十五、版本检测

// 配置 SDK 版本(资源 SDK 复用同一底层版本)
const char* version = RDService_GetSDKVersion(service);
// 返回 "1.2.17"

建议在初始化完成后立即调用,日志记录或运行时校验使用的 SDK 版本是否匹配预期。

十七、自定义解压注入(7z 等非 zip 格式)

SDK 默认解压实现(ResHubFileImpl)仅支持 zip 格式(基于 minizip/zlib)。当资源为 7z 等非 zip 格式时,需要业务方通过 ResHubDependencyInjector_SetUnzipCallbacks 注入自有解压库。

注意:SDK 的自动解压判断层(ResHubAutoUnzipMediator)已默认将 .7z 后缀的资源标记为「需要解压」,注入的解压实现会被 ResHubUnzipProcessor 自动调用,无需修改框架代码。

// 1. 实现解压回调(示例:调用业务自有 7z 库)
int MyUnzipImpl(void* ctx, const char* path, const char* dest,
int overwrite, const char* password, const char** error_msg) {
int ret = My7zExtract(path, dest, password, overwrite != 0);
if (ret != 0) {
*error_msg = "7z extraction failed";
}
return ret;
}

// 2. 构造 VTable 并注入
RDUnzipVTable_C unzipVTable;
memset(&unzipVTable, 0, sizeof(unzipVTable)); // MSVC Debug 必须清零
unzipVTable.impl_context = NULL; // 业务自定义上下文,可为 NULL
unzipVTable.UnzipFile = MyUnzipImpl;

ResHubDependencyInjectorHandle dep = ResHubDependencyInjector_Create();
ResHubDependencyInjector_SetUnzipCallbacks(dep, &unzipVTable);
参数说明
path (in)压缩包文件路径(SDK 下载的资源文件)
destination (in)解压目标目录
overwrite (in)是否覆盖已存在文件:1=覆盖,0=不覆盖
password (in)解压密码,无密码时为 NULL
error_msg_out (out)失败时写入错误描述,仅需在函数返回前保持有效
返回值0=成功,非 0=业务自定义错误码

传入 NULLUnzipFile == NULL 的 VTable 会清除自定解压实现,回退到 SDK 默认 zip 解压。

十八、自定义解压判断注入

SDK 默认按文件后缀自动判断是否需要解压(.zip / .7z)。当业务使用自定义压缩格式(如 .dat.bin 等),需要让 SDK 对其执行解压流程时,通过 ResHubDependencyInjector_SetAutoUnzipCallbacks 注入自定义判断逻辑:

// 1. 实现判断回调
int MyNeedUnzip(void* ctx, const char* resId, const char* downloadUrl,
const char* fileExtra, int noNeedUnZip) {
if (noNeedUnZip) return 0; // 配置明确标记不解压
if (strstr(downloadUrl, ".dat")) return 1; // 自定义格式需要解压
return 0; // SDK 对 zip/7z 仍会兜底判断
}

// 2. 构造 VTable 并注入
RDAutoUnzipVTable_C autoUnzipVTable;
memset(&autoUnzipVTable, 0, sizeof(autoUnzipVTable));
autoUnzipVTable.impl_context = NULL;
autoUnzipVTable.NeedUnzip = MyNeedUnzip;

ResHubDependencyInjectorHandle dep = ResHubDependencyInjector_Create();
ResHubDependencyInjector_SetAutoUnzipCallbacks(dep, &autoUnzipVTable);
参数说明
resId (in)资源 ID
downloadUrl (in)下载地址(可用于检查文件后缀)
fileExtra (in)扩展信息
noNeedUnZip (in)配置中标记的不需要解压标志(1=不需要)
返回值非 0=需要解压,0=不需要

注意:业务回调返回非 0 时,即使后缀不是 zip/7z,ResHubAutoUnzipMediator 也会将该资源标记为「需要解压」,随后进入解压流程(调用 fileImpl,可以是 SetUnzipCallbacks 注入的自定义解压器)。

传入 NULLNeedUnzip == NULL 的 VTable 会清除自定义判断,回退到 SDK 内置 zip/7z 后缀判断。

十九、重要注意事项

1. MSVC Debug 0xCC 填充(最关键)

MSVC Debug 模式将未初始化的栈内存填充为 0xCCResHubParam_CResHubAppInfo_CRDConfigRDEventListener 等含函数指针的结构体若不清零,0xCCCCCCCC 作为函数指针调用会直接 Access Violation务必 memset(&s, 0, sizeof(s)) 或调用配套的 _Init() 函数。

// 必须遵守的初始化规则
RDEventListener_Init(&listener);
RDConfig_Init(&config);
RDTabBizSettings_Init(&settings);

ResHubParam_C params;
memset(&params, 0, sizeof(params));

2. enableDiffPatch 默认值陷阱

字段实际默认值为 TRUE,但标准初始化流程 memset(&params, 0, ...) 会将其置为 FALSE。如需差分补丁功能,必须在 memset 之后显式设置:

params.enable_diff_patch = true;

3. /MT 静态 CRT 一致性

SDK 以 /MT 编译。消费者项目也必须使用 /MT(非 /MD)。CRT 类型不匹配会导致 std::string 内存在不同堆之间泄漏/损坏,表现可能是崩溃、堆断言失败或静默数据损坏。

4. 跨 DLL 内存管理

SDK 从 DLL 内部分配内存的对象必须通过 SDK 释放函数归还。绝对禁止 free()delete

5. ResHubAppInfo_C 字段名

appIdappKey 为 camelCase,不是 app_idapp_key。填错字段名会导致编译器报错。

6. Windows 路径分隔符

正斜杠(/)和反斜杠(\)在 C API 中均有效。推荐使用正斜杠或双反斜杠(\\)以避免转义问题。

7. 中文路径解压

Windows 使用 minizip-ng 替代标准 zlib 解压以支持中文路径。构建 SDK 时此依赖自动处理,消费者无需额外配置。

8. QQ 变体额外 DLL

使用 QQ 变体时需额外部署 crypto.dllssl.dll,否则运行时加载失败。

9. 初始化先后顺序

先调用 initResHubNativeWithDependencyInjector 初始化 SDK 中心,再调用 getResHubNative 获取 App 实例。顺序颠倒返回 NULL。

10. 链接单体 shiply

推荐链接 shiply(含 rdelivery + reshub),避免分别链接 rdelivery/reshub 时的跨 DLL CRT 问题。即使不直接使用配置功能,单体库也更安全。

这篇文档对您有帮助吗?