一、极简 runtime 库调用
用 C 语言写一个极简 runtime 库 → 编译成 动态库(.so / .dll) → 写主程序调用它。
1. 项目结构
./
├── runtime.c # 动态库源码
├── runtime.h # 头文件
└── main.c # 调用动态库的主程序
2. 代码文件
runtime.h
c
#ifndef RUNTIME_H
#define RUNTIME_H
// 导出函数(Windows 需要 __declspec(dllexport))
#if _WIN32
#define API __declspec(dllexport)
#else
#define API
#endif
// 简单 runtime 函数:计算 a + b
API int runtime_add(int a, int b);
// runtime 版本信息
API const char* runtime_version(void);
#endif
runtime.c
c
#include "runtime.h"
int runtime_add(int a, int b) {
return a + b;
}
const char* runtime_version(void) {
return "runtime v1.0.0 (dynamic library)";
}
main.c(调用方)
c
#include <stdio.h>
#include "runtime.h"
int main() {
printf("call from dynamic library:\n");
int res = runtime_add(100, 200);
printf("100 + 200 = %d\n", res);
printf("version: %s\n", runtime_version());
return 0;
}
3. 编译与运行
Linux / macOS
① 编译动态库
bash
gcc -fPIC -shared runtime.c -o libruntime.so
② 编译主程序并链接动态库
bash
gcc main.c -L. -lruntime -o main
③ 运行
Linux:
bash
LD_LIBRARY_PATH=. ./main
macOS:
bash
DYLD_LIBRARY_PATH=. ./main
Windows(MinGW gcc)
① 编译 DLL
bash
gcc -shared runtime.c -o runtime.dll -Wl,--out-implib=libruntime.a
② 编译主程序
bash
gcc main.c -L. -lruntime -o main.exe
③ 运行
bash
main.exe
4. 预期输出
call from dynamic library:
100 + 200 = 300
version: runtime v1.0.0 (dynamic library)
二、工程化runtime库调用
- 分成两个独立项目:Runtime 动态库 + App 调用程序
- 带 CMake 构建(工程化标准)
- 跨平台:Linux / Windows / macOS 通用
- 包含命名空间、版本、导出宏、异常、CMake install 等工程化内容
- 动态链接(.so / .dll / .dylib)
完整项目结构
project/
├── CMakeLists.txt # 顶层 CMake
├── runtime/ # 动态库工程
│ ├── CMakeLists.txt
│ ├── include/
│ │ └── runtime/
│ │ ├── Runtime.h
│ │ └── Version.h
│ └── src/
│ ├── Runtime.cpp
│ └── Version.cpp
└── app/ # 调用方程序
├── CMakeLists.txt
└── src/
└── main.cpp
1 顶层 CMakeLists.txt
cmake
cmake_minimum_required(VERSION 3.14)
project(DynamicRuntimeDemo CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# 子模块
add_subdirectory(runtime)
add_subdirectory(app)
2 runtime 库部分
runtime/CMakeLists.txt
cmake
add_library(runtime SHARED)
target_include_directories(runtime
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_sources(runtime
PRIVATE
src/Runtime.cpp
src/Version.cpp
)
# 导出宏
target_compile_definitions(runtime
PRIVATE
RUNTIME_EXPORT
)
runtime/include/runtime/Version.h
cpp
#pragma once
#include "runtime/Platform.h"
namespace runtime {
RUNTIME_API const char* version();
RUNTIME_API int majorVersion();
RUNTIME_API int minorVersion();
}
runtime/include/runtime/Runtime.h
cpp
#pragma once
#include "runtime/Platform.h"
#include <string>
namespace runtime {
// 核心运行时类
class RUNTIME_API Runtime {
public:
Runtime();
~Runtime();
// 示例功能
int add(int a, int b) const;
std::string greet(const std::string& name) const;
// 禁止拷贝移动,典型库设计
Runtime(const Runtime&) = delete;
Runtime& operator=(const Runtime&) = delete;
Runtime(Runtime&&) = delete;
Runtime& operator=(Runtime&&) = delete;
};
}
runtime/include/runtime/Platform.h
cpp
#pragma once
#if defined(_WIN32)
#ifdef RUNTIME_EXPORT
#define RUNTIME_API __declspec(dllexport)
#else
#define RUNTIME_API __declspec(dllimport)
#endif
#else
#define RUNTIME_API __attribute__((visibility("default")))
#endif
runtime/src/Version.cpp
cpp
#include "runtime/Version.h"
namespace runtime {
const char* version() {
return "1.0.0";
}
int majorVersion() {
return 1;
}
int minorVersion() {
return 0;
}
}
runtime/src/Runtime.cpp
cpp
#include "runtime/Runtime.h"
#include <iostream>
namespace runtime {
Runtime::Runtime() {
std::cout << "[Runtime] construct" << std::endl;
}
Runtime::~Runtime() {
std::cout << "[Runtime] destruct" << std::endl;
}
int Runtime::add(int a, int b) const {
return a + b;
}
std::string Runtime::greet(const std::string& name) const {
return "Hello, " + name + " from runtime dynamic library!";
}
}
3 app 调用程序部分
app/CMakeLists.txt
cmake
add_executable(app src/main.cpp)
target_link_libraries(app
PRIVATE
runtime
)
app/src/main.cpp
cpp
#include <iostream>
#include "runtime/Runtime.h"
#include "runtime/Version.h"
int main() {
std::cout << "App start, runtime version: "
<< runtime::version() << std::endl;
runtime::Runtime rt;
int sum = rt.add(123, 456);
std::cout << "123 + 456 = " << sum << std::endl;
auto msg = rt.greet("User");
std::cout << msg << std::endl;
return 0;
}
4 编译运行(通用)
在项目根目录:
Linux / macOS
bash
mkdir build && cd build
cmake ..
make -j8
运行:
bash
./app
Windows (Visual Studio)
bash
mkdir build
cd build
cmake .. -G "Visual Studio 17 2022"
然后打开 sln 编译,运行 app.exe
5 典型输出
App start, runtime version: 1.0.0
[Runtime] construct
123 + 456 = 579
Hello, User from runtime dynamic library!
[Runtime] destruct
三、runtime源码分离的动态库调用方案
完整项目结构(你本地)
RuntimeSDK/
├── CMakeLists.txt # 编译动态库(你自己用)
├── include/ # 对外公开 → 发给客户
│ └── Runtime.h
└── src/ # 内部实现 → 绝不发给客户
├── Runtime.cpp
└── Version.cpp
客户项目
ClientApp/
├── include/Runtime.h
├── lib/runtime.lib
└── bin/runtime.dll
1)include/Runtime.h (对外接口,客户可见)
cpp
#pragma once
#if defined(_WIN32)
#ifdef RUNTIME_EXPORT
#define RUNTIME_API __declspec(dllexport)
#else
#define RUNTIME_API __declspec(dllimport)
#endif
#else
#define RUNTIME_API __attribute__((visibility("default")))
#endif
// ###########################
// 客户只能看到这些接口!
// ###########################
#ifdef __cplusplus
extern "C" {
#endif
// 初始化
RUNTIME_API bool Runtime_Init();
// 获取版本号
RUNTIME_API const char* Runtime_GetVersion();
// 业务功能
RUNTIME_API int Runtime_Calculate(int a, int b);
// 反初始化
RUNTIME_API void Runtime_Shutdown();
#ifdef __cplusplus
}
#endif
2)src/Version.cpp (内部实现,客户不可见)
cpp
#include "Runtime.h"
const char* Runtime_GetVersion()
{
// 客户绝对看不到这个实现
return "Runtime SDK v2026.04.01 | Licensed to Client";
}
3)src/Runtime.cpp (内部实现,客户不可见)
cpp
#include "Runtime.h"
#include <iostream>
static bool g_isInitialized = false;
bool Runtime_Init()
{
if (g_isInitialized)
return true;
// 客户看不到你的内部逻辑
std::cout << "[Runtime] 初始化成功(机密代码)" << std::endl;
g_isInitialized = true;
return true;
}
int Runtime_Calculate(int a, int b)
{
// 核心算法,客户看不到
return a * 100 + b;
}
void Runtime_Shutdown()
{
g_isInitialized = false;
std::cout << "[Runtime] 已关闭" << std::endl;
}
4)CMakeLists.txt (编译 DLL 用)
cmake
cmake_minimum_required(VERSION 3.14)
project(RuntimeSDK CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# 生成动态库
add_library(runtime SHARED
src/Runtime.cpp
src/Version.cpp
)
# 公开头文件
target_include_directories(runtime
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
)
# Windows 导出符号
target_compile_definitions(runtime
PRIVATE
RUNTIME_EXPORT
)
# 输出目录(方便打包给客户)
set_target_properties(runtime PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
)
5)客户使用的代码(main.cpp)
客户完全没有 .cpp 文件,只有头文件 + lib + dll。
cpp
#include <iostream>
#include "Runtime.h"
int main()
{
Runtime_Init();
std::cout << "版本:" << Runtime_GetVersion() << std::endl;
int result = Runtime_Calculate(10, 20);
std::cout << "计算结果:" << result << std::endl;
Runtime_Shutdown();
return 0;
}
6)客户的 CMakeLists.txt(只链接二进制)
cmake
cmake_minimum_required(VERSION 3.14)
project(ClientApp CXX)
set(CMAKE_CXX_STANDARD 17)
add_executable(app main.cpp)
# 客户只包含头文件
target_include_directories(app PRIVATE ./include)
# 客户只链接二进制 lib(无源码)
target_link_directories(app PRIVATE ./lib)
target_link_libraries(app PRIVATE runtime.lib)
四、含日志/加密/多线程/打包Runtime SDK
1、整体说明
方案包含:Runtime SDK(你本地编译,不泄露源码)+ 客户调用工程(仅含接口/二进制,无实现),额外完善:
-
日志功能:分级日志(DEBUG/INFO/ERROR),输出到文件+控制台,客户可配置日志级别
-
错误码:自定义错误码体系,接口返回错误码,配套错误信息查询
-
加密:核心接口参数加密(AES简单加密,可替换为你的加密算法)
-
类接口:新增C++类接口(兼容原有C接口),更符合面向对象开发
-
多线程安全:内部加锁,支持多线程并发调用接口
-
自动打包脚本:CMake自动打包SDK(头文件+lib+dll+说明文档),直接发给客户
你本地仅保留SDK源码,客户仅获取打包后的二进制资源,完全看不到实现。
2、Runtime SDK工程
2.1 工程目录结构
plain
RuntimeSDK/
├── CMakeLists.txt # 编译+自动打包(你自己用)
├── include/ # 对外公开接口(发给客户)
│ └── Runtime.h
├── src/ # 内部实现(绝不发给客户)
│ ├── Runtime.cpp
│ ├── Version.cpp
│ ├── Log.cpp # 日志实现
│ ├── Encrypt.cpp # 加密实现
│ └── ErrorCode.cpp # 错误码实现
└── script/ # 自动打包脚本(CMake调用)
└── pack_sdk.cmake
2.2 公开接口(include/Runtime.h,客户可见)
cpp
#pragma once
#include <cstdint>
#include <string>
// 跨平台导出/导入宏
#if defined(_WIN32)
#ifdef RUNTIME_EXPORT
#define RUNTIME_API __declspec(dllexport)
#else
#define RUNTIME_API __declspec(dllimport)
#endif
#else
#define RUNTIME_API __attribute__((visibility("default")))
#endif
// ###########################
// 1. 错误码定义(客户可见,用于排查问题)
// ###########################
typedef enum {
RUNTIME_SUCCESS = 0, // 成功
RUNTIME_ERROR_UNINIT = 1, // 未初始化
RUNTIME_ERROR_PARAM = 2, // 参数错误
RUNTIME_ERROR_ENCRYPT = 3, // 加密/解密失败
RUNTIME_ERROR_THREAD = 4, // 线程安全错误
RUNTIME_ERROR_UNKNOWN = 99 // 未知错误
} Runtime_ErrorCode;
// ###########################
// 2. 日志级别(客户可配置)
// ###########################
typedef enum {
RUNTIME_LOG_DEBUG = 0, // 调试日志(仅开发用)
RUNTIME_LOG_INFO = 1, // 信息日志(正常运行)
RUNTIME_LOG_ERROR = 2 // 错误日志(异常情况)
} Runtime_LogLevel;
// ###########################
// 3. C接口(兼容C/C++,稳定通用)
// ###########################
#ifdef __cplusplus
extern "C" {
#endif
// 初始化(可配置日志级别、日志路径)
// 参数:logLevel-日志级别,logPath-日志文件路径(NULL则默认当前目录)
// 返回:错误码
RUNTIME_API Runtime_ErrorCode Runtime_Init(Runtime_LogLevel logLevel, const char* logPath);
// 获取版本号
RUNTIME_API const char* Runtime_GetVersion();
// 业务计算(参数加密传输)
// 参数:a-加密后的整数a,b-加密后的整数b,key-加密密钥(长度16)
// 返回:加密后的计算结果,失败返回-1
RUNTIME_API int Runtime_Calculate(int a, int b, const char* key);
// 错误码转错误信息
RUNTIME_API const char* Runtime_GetErrorMsg(Runtime_ErrorCode errCode);
// 反初始化
RUNTIME_API void Runtime_Shutdown();
#ifdef __cplusplus
}
#endif
// ###########################
// 4. C++类接口(面向对象,客户可直接实例化调用)
// ###########################
#ifdef __cplusplus
class RUNTIME_API Runtime {
public:
// 构造函数(自动初始化,默认日志级别INFO)
Runtime();
// 带参数构造(自定义日志级别和路径)
Runtime(Runtime_LogLevel logLevel, const std::string& logPath);
// 析构函数(自动反初始化)
~Runtime();
// 获取版本号
std::string getVersion() const;
// 业务计算(参数加密,密钥默认"RuntimeSDK202604")
int calculate(int a, int b, const std::string& key = "RuntimeSDK202604");
// 获取最后一次错误码
Runtime_ErrorCode getLastError() const;
// 获取最后一次错误信息
std::string getLastErrorMsg() const;
private:
// 私有成员(客户看不到实现,隐藏内部状态)
class Impl;
Impl* m_impl; // 指针隐藏实现(PIMPL模式)
};
#endif
2.3 内部实现(src目录,客户不可见)
2.3.1 src/Version.cpp(版本实现)
cpp
#include "Runtime.h"
const char* Runtime_GetVersion() {
// 客户看不到的版本实现,可自定义版本规则
return "Runtime SDK v2026.04.01 | Licensed to Client | 支持加密+多线程";
}
#ifdef __cplusplus
std::string Runtime::getVersion() const {
return Runtime_GetVersion();
}
#endif
2.3.2 src/Log.cpp(日志实现,分级+文件+控制台)
cpp
#include "Runtime.h"
#include <iostream>
#include <fstream>
#include <ctime>
#include <mutex>
// 全局日志配置
static Runtime_LogLevel g_logLevel = RUNTIME_LOG_INFO;
static std::string g_logPath = "runtime.log";
static std::mutex g_logMutex; // 日志锁,保证多线程安全
// 日志时间格式化
static std::string getCurrentTime() {
time_t now = time(nullptr);
struct tm tm = *localtime(&now);
char buf[64];
snprintf(buf, sizeof(buf), "[%04d-%02d-%02d %02d:%02d:%02d]",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
return buf;
}
// 日志输出(内部函数,客户看不到)
void logOutput(Runtime_LogLevel level, const char* msg) {
if (level < g_logLevel) return;
std::lock_guard<std::mutex> lock(g_logMutex); // 多线程加锁
// 日志级别字符串
const char* levelStr = nullptr;
switch (level) {
case RUNTIME_LOG_DEBUG: levelStr = "DEBUG"; break;
case RUNTIME_LOG_INFO: levelStr = "INFO"; break;
case RUNTIME_LOG_ERROR: levelStr = "ERROR"; break;
default: levelStr = "UNKNOWN";
}
// 控制台输出
std::cout << getCurrentTime() << " [" << levelStr << "] " << msg << std::endl;
// 文件输出
std::ofstream logFile(g_logPath, std::ios::app);
if (logFile.is_open()) {
logFile << getCurrentTime() << " [" << levelStr << "] " << msg << std::endl;
logFile.close();
}
}
// 对外提供的日志配置(内部调用)
void setLogConfig(Runtime_LogLevel level, const char* path) {
g_logLevel = level;
if (path != nullptr) {
g_logPath = path;
}
logOutput(RUNTIME_LOG_INFO, "日志配置完成,日志路径:");
logOutput(RUNTIME_LOG_INFO, g_logPath.c_str());
}
2.3.3 src/Encrypt.cpp(AES加密实现,简化版,可替换)
cpp
#include "Runtime.h"
#include <cstring>
#include <mutex>
// AES简化加密(核心加密逻辑,客户看不到,可替换为你的加密算法)
// 此处为演示,实际商用建议用成熟加密库(如OpenSSL)
static std::mutex g_encryptMutex; // 加密锁,多线程安全
// 简单异或加密(模拟AES,实际可替换)
static int encryptData(int data, const char* key) {
if (key == nullptr || strlen(key) != 16) {
logOutput(RUNTIME_LOG_ERROR, "加密密钥错误,密钥长度必须为16");
return -1;
}
// 异或加密逻辑(客户看不到)
return data ^ (key[0] + key[7] + key[15]);
}
// 简单异或解密
static int decryptData(int data, const char* key) {
if (key == nullptr || strlen(key) != 16) {
logOutput(RUNTIME_LOG_ERROR, "解密密钥错误,密钥长度必须为16");
return -1;
}
// 异或解密(和加密逻辑一致,客户看不到)
return data ^ (key[0] + key[7] + key[15]);
}
// 对外提供的加密接口(内部调用)
int encryptParam(int param, const char* key) {
std::lock_guard<std::mutex> lock(g_encryptMutex);
return encryptData(param, key);
}
// 对外提供的解密接口(内部调用)
int decryptParam(int param, const char* key) {
std::lock_guard<std::mutex> lock(g_encryptMutex);
return decryptData(param, key);
}
2.3.4 src/ErrorCode.cpp(错误码实现)
cpp
#include "Runtime.h"
// 错误码对应错误信息(客户看不到实现,仅能通过接口查询)
const char* Runtime_GetErrorMsg(Runtime_ErrorCode errCode) {
switch (errCode) {
case RUNTIME_SUCCESS: return "操作成功";
case RUNTIME_ERROR_UNINIT: return "Runtime未初始化,请先调用Runtime_Init";
case RUNTIME_ERROR_PARAM: return "参数错误(空指针/非法值)";
case RUNTIME_ERROR_ENCRYPT: return "加密/解密失败(密钥错误/参数异常)";
case RUNTIME_ERROR_THREAD: return "多线程调用异常(内部锁错误)";
case RUNTIME_ERROR_UNKNOWN: return "未知错误,请检查日志";
default: return "无效错误码";
}
}
#ifdef __cplusplus
std::string Runtime::getLastErrorMsg() const {
return Runtime_GetErrorMsg(getLastError());
}
#endif
2.3.5 src/Runtime.cpp(核心业务实现,含多线程安全)
cpp
#include "Runtime.h"
#include <mutex>
#include <string>
// 全局初始化状态
static bool g_isInitialized = false;
static std::mutex g_initMutex; // 初始化锁,多线程安全
// C++类实现(PIMPL模式,客户看不到内部成员)
class Runtime::Impl {
public:
Impl() {
m_lastError = RUNTIME_SUCCESS;
}
Impl(Runtime_LogLevel logLevel, const std::string& logPath) {
m_lastError = RUNTIME_SUCCESS;
Runtime_Init(logLevel, logPath.c_str());
}
~Impl() {
Runtime_Shutdown();
}
Runtime_ErrorCode m_lastError;
};
// 初始化接口实现
Runtime_ErrorCode Runtime_Init(Runtime_LogLevel logLevel, const char* logPath) {
std::lock_guard<std::mutex> lock(g_initMutex);
if (g_isInitialized) {
logOutput(RUNTIME_LOG_INFO, "Runtime已初始化,无需重复调用");
return RUNTIME_SUCCESS;
}
// 校验参数
if (logLevel < RUNTIME_LOG_DEBUG || logLevel > RUNTIME_LOG_ERROR) {
logOutput(RUNTIME_LOG_ERROR, "初始化失败:日志级别参数错误");
return RUNTIME_ERROR_PARAM;
}
// 配置日志
setLogConfig(logLevel, logPath);
// 内部初始化逻辑(客户看不到,如加载配置、初始化资源等)
logOutput(RUNTIME_LOG_INFO, "Runtime初始化成功(内部机密逻辑执行完成)");
g_isInitialized = true;
return RUNTIME_SUCCESS;
}
// 业务计算接口实现(加密+多线程安全)
int Runtime_Calculate(int a, int b, const char* key) {
std::lock_guard<std::mutex> lock(g_initMutex);
// 校验初始化状态
if (!g_isInitialized) {
logOutput(RUNTIME_LOG_ERROR, "计算失败:Runtime未初始化");
return -1;
}
// 校验参数
if (key == nullptr || strlen(key) != 16) {
logOutput(RUNTIME_LOG_ERROR, "计算失败:密钥错误");
return -1;
}
// 解密参数(客户看不到解密过程)
int decA = decryptParam(a, key);
int decB = decryptParam(b, key);
if (decA == -1 || decB == -1) {
logOutput(RUNTIME_LOG_ERROR, "计算失败:参数解密失败");
return -1;
}
// 核心业务逻辑(客户看不到,可替换为你的实际功能)
int result = decA * 100 + decB;
logOutput(RUNTIME_LOG_DEBUG, "计算完成:解密后参数(a=%d, b=%d),结果=%d", decA, decB, result);
// 加密结果返回
return encryptParam(result, key);
}
// 反初始化接口实现
void Runtime_Shutdown() {
std::lock_guard<std::mutex> lock(g_initMutex);
if (!g_isInitialized) {
return;
}
// 内部反初始化逻辑(客户看不到,如释放资源、保存数据等)
logOutput(RUNTIME_LOG_INFO, "Runtime反初始化成功(内部资源已释放)");
g_isInitialized = false;
}
// C++类接口实现
#ifdef __cplusplus
Runtime::Runtime() {
m_impl = new Impl();
m_impl->m_lastError = Runtime_Init(RUNTIME_LOG_INFO, nullptr);
}
Runtime::Runtime(Runtime_LogLevel logLevel, const std::string& logPath) {
m_impl = new Impl(logLevel, logPath);
m_impl->m_lastError = Runtime_Init(logLevel, logPath.c_str());
}
Runtime::~Runtime() {
delete m_impl;
m_impl = nullptr;
}
int Runtime::calculate(int a, int b, const std::string& key) {
int result = Runtime_Calculate(a, b, key.c_str());
if (result == -1) {
m_impl->m_lastError = RUNTIME_ERROR_ENCRYPT;
} else {
m_impl->m_lastError = RUNTIME_SUCCESS;
}
return result;
}
Runtime_ErrorCode Runtime::getLastError() const {
return m_impl->m_lastError;
}
#endif
2.4 CMakeLists.txt(编译+自动打包,你自己用)
cmake
cmake_minimum_required(VERSION 3.14)
project(RuntimeSDK CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# 1. 生成动态库
add_library(runtime SHARED
src/Runtime.cpp
src/Version.cpp
src/Log.cpp
src/Encrypt.cpp
src/ErrorCode.cpp
)
# 2. 公开头文件目录
target_include_directories(runtime
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
)
# 3. Windows导出符号
target_compile_definitions(runtime
PRIVATE
RUNTIME_EXPORT
)
# 4. 输出目录配置(统一输出lib/bin,方便打包)
set_target_properties(runtime PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
# 避免Windows下DLL和EXE同名冲突
RUNTIME_OUTPUT_NAME "runtime_sdk"
LIBRARY_OUTPUT_NAME "runtime_sdk"
ARCHIVE_OUTPUT_NAME "runtime_sdk"
)
# 5. 自动打包脚本(编译完成后自动打包SDK)
include(script/pack_sdk.cmake)
# 6. 多线程安全配置(Windows链接多线程库)
if(WIN32)
target_link_libraries(runtime PRIVATE ws2_32.lib)
target_compile_options(runtime PRIVATE /MT) # 静态链接C runtime,避免客户缺少依赖
endif()
2.5 自动打包脚本(script/pack_sdk.cmake)
cmake
# 自动打包SDK:头文件+lib+dll+说明文档
set(SDK_PACKAGE_NAME "RuntimeSDK_Client")
set(SDK_PACKAGE_DIR ${CMAKE_BINARY_DIR}/${SDK_PACKAGE_NAME})
# 创建打包目录结构
file(MAKE_DIRECTORY ${SDK_PACKAGE_DIR}/include)
file(MAKE_DIRECTORY ${SDK_PACKAGE_DIR}/lib)
file(MAKE_DIRECTORY ${SDK_PACKAGE_DIR}/bin)
file(MAKE_DIRECTORY ${SDK_PACKAGE_DIR}/doc)
# 复制头文件
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/include/Runtime.h
DESTINATION ${SDK_PACKAGE_DIR}/include)
# 复制lib文件(Windows)
if(WIN32)
file(COPY ${CMAKE_BINARY_DIR}/lib/runtime_sdk.lib
DESTINATION ${SDK_PACKAGE_DIR}/lib)
# 复制so文件(Linux)
else()
file(COPY ${CMAKE_BINARY_DIR}/lib/libruntime_sdk.so
DESTINATION ${SDK_PACKAGE_DIR}/lib)
endif()
# 复制dll文件(Windows)
if(WIN32)
file(COPY ${CMAKE_BINARY_DIR}/bin/runtime_sdk.dll
DESTINATION ${SDK_PACKAGE_DIR}/bin)
endif()
# 生成说明文档(告诉客户如何使用)
set(SDK_README ${SDK_PACKAGE_DIR}/doc/使用说明.md)
file(WRITE ${SDK_README}
"# Runtime SDK 使用说明
## 一、SDK包含文件
- include/Runtime.h:接口头文件(仅需包含此文件)
- lib/runtime_sdk.lib:链接库(Windows)/libruntime_sdk.so(Linux)
- bin/runtime_sdk.dll:运行时库(Windows,需放在EXE同目录)
## 二、编译配置(CMake示例)
cmake_minimum_required(VERSION 3.14)
project(ClientApp CXX)
set(CMAKE_CXX_STANDARD 17)
add_executable(app main.cpp)
target_include_directories(app PRIVATE ./include)
target_link_directories(app PRIVATE ./lib)
if(WIN32)
target_link_libraries(app PRIVATE runtime_sdk.lib)
else()
target_link_libraries(app PRIVATE runtime_sdk)
endif()
## 三、接口调用示例(C++)
#include <iostream>
#include \"Runtime.h\"
int main() {
// 方式1:C接口调用
Runtime_Init(RUNTIME_LOG_INFO, nullptr);
std::cout << \"版本:\" << Runtime_GetVersion() << std::endl;
// 加密参数(密钥长度16)
const char* key = \"RuntimeSDK202604\";
int a = encryptParam(10, key); // 客户可自行加密参数
int b = encryptParam(20, key);
int result = Runtime_Calculate(a, b, key);
result = decryptParam(result, key); // 解密结果
std::cout << \"计算结果:\" << result << std::endl;
Runtime_Shutdown();
// 方式2:C++类接口调用
Runtime runtime(RUNTIME_LOG_INFO, \"client_log.log\");
std::cout << \"版本:\" << runtime.getVersion() << std::endl;
int a2 = encryptParam(30, key);
int b2 = encryptParam(40, key);
int result2 = runtime.calculate(a2, b2, key);
result2 = decryptParam(result2, key);
std::cout << \"计算结果:\" << result2 << std::endl;
return 0;
}
## 四、错误排查
1. 调用接口返回错误码,可通过Runtime_GetErrorMsg(errCode)获取错误信息
2. 日志文件默认生成在当前目录,可通过初始化接口自定义日志路径
3. 确保密钥长度为16位,否则加密/解密会失败
4. 多线程调用无需额外处理,内部已保证线程安全
## 五、注意事项
- 禁止反编译、修改SDK文件
- 仅可用于授权范围内的开发使用
- 运行时需确保dll/so文件与EXE同目录(Windows)
")
# 打包完成提示
message(STATUS "SDK打包完成,路径:${SDK_PACKAGE_DIR}")
message(STATUS "打包内容:头文件、lib、dll、使用说明")
3、客户调用工程
3.1 客户拿到的文件(打包后自动生成)
plain
RuntimeSDK_Client/
├── include/
│ └── Runtime.h # 仅接口,无实现
├── lib/
│ └── runtime_sdk.lib # Windows链接库(无源码)
├── bin/
│ └── runtime_sdk.dll # Windows运行时库(二进制)
└── doc/
└── 使用说明.md # 调用说明,客户可参考
3.2 客户的CMakeLists.txt(仅链接二进制)
cmake
cmake_minimum_required(VERSION 3.14)
project(ClientApp CXX)
set(CMAKE_CXX_STANDARD 17)
# 生成客户的可执行程序
add_executable(app main.cpp)
# 包含SDK头文件(仅接口)
target_include_directories(app PRIVATE ./include)
# 链接SDK的二进制lib(无源码)
target_link_directories(app PRIVATE ./lib)
# 区分Windows/Linux链接
if(WIN32)
target_link_libraries(app PRIVATE runtime_sdk.lib)
# 确保DLL和EXE同目录
add_custom_command(TARGET app POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/bin/runtime_sdk.dll
$<TARGET_FILE_DIR:app>)
else()
target_link_libraries(app PRIVATE runtime_sdk)
endif()
3.3 客户的调用代码(main.cpp,仅用接口)
cpp
#include <iostream>
#include "Runtime.h"
#include <string>
// 辅助函数:加密/解密(客户可调用,接口可见,实现不可见)
int encrypt(int data, const std::string& key) {
return encryptParam(data, key.c_str());
}
int decrypt(int data, const std::string& key) {
return decryptParam(data, key.c_str());
}
int main() {
// --------------- 方式1:C接口调用 ---------------
std::cout << "=== C接口调用 ===" << std::endl;
// 初始化(日志级别INFO,默认日志路径)
Runtime_ErrorCode err = Runtime_Init(RUNTIME_LOG_INFO, nullptr);
if (err != RUNTIME_SUCCESS) {
std::cout << "初始化失败:" << Runtime_GetErrorMsg(err) << std::endl;
return 1;
}
// 获取版本号
std::cout << "SDK版本:" << Runtime_GetVersion() << std::endl;
// 业务计算(参数加密)
const std::string key = "RuntimeSDK202604"; // 密钥(长度16)
int a = 15, b = 35;
int encryptA = encrypt(a, key);
int encryptB = encrypt(b, key);
std::cout << "加密前:a=" << a << ", b=" << b << std::endl;
std::cout << "加密后:a=" << encryptA << ", b=" << encryptB << std::endl;
int encryptResult = Runtime_Calculate(encryptA, encryptB, key.c_str());
if (encryptResult == -1) {
std::cout << "计算失败:" << Runtime_GetErrorMsg(Runtime_ERROR_ENCRYPT) << std::endl;
Runtime_Shutdown();
return 1;
}
int result = decrypt(encryptResult, key);
std::cout << "计算结果(解密后):" << result << std::endl;
// 反初始化
Runtime_Shutdown();
// --------------- 方式2:C++类接口调用 ---------------
std::cout << "\n=== C++类接口调用 ===" << std::endl;
// 初始化(自定义日志路径)
Runtime runtime(RUNTIME_LOG_DEBUG, "client_runtime.log");
if (runtime.getLastError() != RUNTIME_SUCCESS) {
std::cout << "初始化失败:" << runtime.getLastErrorMsg() << std::endl;
return 1;
}
// 获取版本号
std::cout << "SDK版本:" << runtime.getVersion() << std::endl;
// 业务计算
int a2 = 25, b2 = 45;
int encryptA2 = encrypt(a2, key);
int encryptB2 = encrypt(b2, key);
std::cout << "加密前:a=" << a2 << ", b=" << b2 << std::endl;
std::cout << "加密后:a=" << encryptA2 << ", b=" << encryptB2 << std::endl;
int encryptResult2 = runtime.calculate(encryptA2, encryptB2, key);
if (encryptResult2 == -1) {
std::cout << "计算失败:" << runtime.getLastErrorMsg() << std::endl;
return 1;
}
int result2 = decrypt(encryptResult2, key);
std::cout << "计算结果(解密后):" << result2 << std::endl;
std::cout << "\n调用完成!" << std::endl;
return 0;
}
4、核心保障
-
客户仅拿到头文件(接口)、二进制lib/dll,无任何.cpp源码,无法查看内部实现;
-
C++类采用PIMPL模式,私有成员和实现完全隐藏,客户看不到类的内部状态;
-
加密、日志、错误码的核心逻辑都在src目录,不泄露给客户;
-
自动打包脚本仅复制公开文件,内部实现文件不会被打包;
-
多线程安全通过内部锁实现,客户无需关心,也看不到锁的实现细节。
5、使用步骤
5.1 SDK编译+打包
-
创建上述RuntimeSDK工程,将src、include、script目录及CMakeLists.txt复制到位;
-
用CMake构建工程(如VS、Clion、CMake-GUI),编译后自动生成lib/bin目录;
-
编译完成后,CMake自动调用打包脚本,生成RuntimeSDK_Client目录;
-
将RuntimeSDK_Client目录发给客户,自己保留RuntimeSDK工程(不泄露)。
5.2 客户调用SDK
-
解压你发给的RuntimeSDK_Client压缩包;
-
创建自己的ClientApp工程,复制客户的CMakeLists.txt和main.cpp;
-
用CMake构建工程,编译生成app.exe;
-
运行app.exe,即可调用SDK接口,看不到任何实现。
6、可扩展说明
-
加密算法:当前为简化版异或加密,可替换为AES、RSA等成熟算法(修改src/Encrypt.cpp即可,不影响客户接口);
-
业务逻辑:可修改src/Runtime.cpp中的Runtime_Calculate函数,替换为你的核心业务功能,客户无需修改调用代码;
-
日志功能:可扩展日志轮转、日志过滤等功能(修改src/Log.cpp);
-
错误码:可新增更多错误码(修改Runtime.h和src/ErrorCode.cpp),客户可通过接口查询新增错误信息。
五、runtime SDK库引用方式
1、把 SDK 放在电脑上一个固定的公共位置,所有工程都去这里引用,不复制文件
给客户的 SDK 放在一个固定公共目录
比如客户在自己电脑上创建:
C:\MyCompanySDK\RuntimeSDK\
├─ include\ # 头文件
├─ lib\ # lib
└─ bin\ # dll
只放这一份!所有项目都共用它!
客户 CMake
直接用 绝对路径 指向公共SDK目录:
cmake
# 客户只需要改这里的 SDK 安装路径
set(SDK_PATH "C:/MyCompanySDK/RuntimeSDK")
# 头文件:从公共SDK里取,不复制
target_include_directories(app PRIVATE ${SDK_PATH}/include)
# 库目录:指向公共 lib
target_link_directories(app PRIVATE ${SDK_PATH}/lib)
# 链接公共库
target_link_libraries(app PRIVATE runtime_sdk.lib)
2、"引用"一个公共位置的库
给客户的支持包
include/lib/bin/
客户解压到 任意一个固定目录 (比如 D:/MySDK/)
所有工程都共用这一份,永不复制!
客户cmak
cmake
# 只需要配置 SDK 路径 1 次
set(SDK_PATH "D:/MySDK")
# 自动链接 头文件 + lib + dll
include(${SDK_PATH}/sdk_config.cmake)
target_link_sdk(app)
给客户的 SDK 根目录里,加一个文件sdk_config.cmake
cmake
# ============ SDK 自动配置脚本 ============
# 作用:自动配置 头文件 + lib + dll
# 客户:只需要调用 target_link_sdk(你的目标名)
if(NOT SDK_PATH)
message(FATAL_ERROR "请先设置 SDK_PATH 路径!例:set(SDK_PATH \"D:/MySDK\")")
endif()
# 自动找到头文件
set(SDK_INCLUDE ${SDK_PATH}/include)
# 自动找到 lib 目录
set(SDK_LIB_DIR ${SDK_PATH}/lib)
# 自动找到 dll 目录
set(SDK_BIN_DIR ${SDK_PATH}/bin)
# 库文件名
set(SDK_LIB_NAME runtime_sdk.lib)
# 宏:客户只需要调用这个函数
macro(target_link_sdk target_name)
# 自动加头文件路径
target_include_directories(${target_name} PRIVATE ${SDK_INCLUDE})
# 自动加库路径
target_link_directories(${target_name} PRIVATE ${SDK_LIB_DIR})
# 自动链接 lib
target_link_libraries(${target_name} PRIVATE ${SDK_LIB_NAME})
# Windows 自动复制 dll 到 exe 目录(客户完全不用管)
if(WIN32)
add_custom_command(TARGET ${target_name} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${SDK_BIN_DIR}/runtime_sdk.dll
$<TARGET_FILE_DIR:${target_name}>
)
endif()
message(STATUS "✅ SDK 链接成功:${SDK_PATH}")
endmacro()
给客户的 SDK 最终目录结构
D:/MySDK/
├─ include/ # 你的头文件
├─ lib/ # 你的 lib
├─ bin/ # 你的 dll
└─ sdk_config.cmake # 我上面给你的文件
客户 CMakeLists.txt
cmake
cmake_minimum_required(VERSION 3.14)
project(ClientDemo)
set(CMAKE_CXX_STANDARD 17)
# 1. 指定 SDK 路径(只写一次)
set(SDK_PATH "D:/MySDK")
# 2. 导入 SDK 配置
include(${SDK_PATH}/sdk_config.cmake)
# 3. 生成可执行程序
add_executable(app main.cpp)
# 4. 一键链接 SDK
target_link_sdk(app)
客户 main.cpp
cpp
#include "Runtime.h"
int main() {
Runtime_Init(...);
...
}
3、添加环境变量
发给客户的 SDK 结构
YourSDK/
├── cmake/
│ └── FindRuntimeSDK.cmake # 自动查找SDK(核心)
├── include/
│ └── Runtime.h # 接口头文件
├── lib/
│ └── runtime_sdk.lib # 链接库
├── bin/
│ └── runtime_sdk.dll # 运行库
├── RuntimeSDKConfig.cmake # 自动配置
└── RuntimeSDKConfigVersion.cmake
客户 CMakeLists.txt
cmake
find_package(RuntimeSDK REQUIRED)
add_executable(app main.cpp)
target_link_libraries(app PRIVATE RuntimeSDK::RuntimeSDK)
cmake/FindRuntimeSDK.cmake
cmake
# 自动寻找 RuntimeSDK
set(RuntimeSDK_FOUND TRUE)
get_filename_component(SDK_DIR "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE)
set(RuntimeSDK_INCLUDE_DIRS "${SDK_DIR}/include")
set(RuntimeSDK_LIBRARY_DIRS "${SDK_DIR}/lib")
set(RuntimeSDK_BIN_DIRS "${SDK_DIR}/bin")
set(RuntimeSDK_LIBRARIES runtime_sdk.lib)
add_library(RuntimeSDK::RuntimeSDK UNKNOWN IMPORTED)
set_target_properties(RuntimeSDK::RuntimeSDK PROPERTIES
IMPORTED_LOCATION "${SDK_DIR}/lib/runtime_sdk.lib"
INTERFACE_INCLUDE_DIRECTORIES "${SDK_DIR}/include"
)
if(WIN32)
set_target_properties(RuntimeSDK::RuntimeSDK PROPERTIES
IMPORTED_IMPLIB "${SDK_DIR}/lib/runtime_sdk.lib"
)
endif()
message(STATUS "✅ RuntimeSDK 自动加载成功: ${SDK_DIR}")
RuntimeSDKConfig.cmake
cmake
include(${CMAKE_CURRENT_LIST_DIR}/cmake/FindRuntimeSDK.cmake)
RuntimeSDKConfigVersion.cmake
cmake
set(PACKAGE_VERSION "1.0.0")
set(PACKAGE_VERSION_MAJOR 1)
set(PACKAGE_VERSION_MINOR 0)
set(PACKAGE_VERSION_PATCH 0)
if(PACKAGE_VERSION_MAJOR VERSION_EQUAL PACKAGE_FIND_VERSION_MAJOR)
set(PACKAGE_VERSION_COMPATIBLE TRUE)
else()
set(PACKAGE_VERSION_COMPATIBLE FALSE)
endif()
if(PACKAGE_VERSION VERSION_EQUAL PACKAGE_FIND_VERSION)
set(PACKAGE_VERSION_EXACT TRUE)
endif()
把 SDK 根目录添加到系统环境变量
变量名:CMAKE_PREFIX_PATH
变量值:D:\YourSDK(SDK 所在目录)
客户 CMakeLists.txt
cmake
cmake_minimum_required(VERSION 3.14)
project(ClientDemo)
set(CMAKE_CXX_STANDARD 17)
# 1. 自动寻找SDK(不用路径!不用配置!)
find_package(RuntimeSDK REQUIRED)
# 2. 生成程序
add_executable(app main.cpp)
# 3. 链接SDK(自动包含头文件+lib+复制dll)
target_link_libraries(app PRIVATE RuntimeSDK::RuntimeSDK)
客户 main.cpp
cpp
#include "Runtime.h"
int main() {
Runtime_Init(RUNTIME_LOG_INFO, nullptr);
Runtime_GetVersion();
Runtime_Shutdown();
return 0;
}
优势
✅ 多工程共用同一个SDK,不复制任何文件
✅ 客户不用写任何路径
✅ 客户不用管头文件在哪、lib在哪、dll在哪
✅ 更新SDK只需覆盖文件夹,所有工程自动更新
✅ 项目目录干净,不包含任何SDK文件
✅ CMake 自动查找、自动配置、自动链接
✅ Windows/Linux 跨平台通用
✅ 商业产品级标准方案
✅ 你一次部署,终身不用改
六、使用说明
1、SDK 目录结构(无需改动)
RuntimeSDK/
├─ bin/runtime_sdk.dll # 运行必需
├─ lib/runtime_sdk.lib # 链接用
├─ include/Runtime.h # 接口头文件
├─ cmake/FindRuntimeSDK.cmake
├─ RuntimeSDKConfig.cmake
├─ RuntimeSDKConfigVersion.cmake
└─ 使用说明.txt
2、添加系统环境变量
1. 打开:系统属性 → 高级 → 环境变量
2. 在「系统变量」里点击「新建」
- 变量名:
RUNTIME_SDK_ROOT - 变量值: 你解压的 SDK 路径,例如
D:\RuntimeSDK或C:\SDK\RuntimeSDK
3. 确定保存
配置一次,所有工程永久生效,以后不用再配。
3、 CMake 文件
cmake
cmake_minimum_required(VERSION 3.14)
project(ClientDemo)
set(CMAKE_CXX_STANDARD 17)
# 自动查找 SDK(无需路径、无需复制文件)
find_package(RuntimeSDK REQUIRED)
# 你的可执行文件
add_executable(app main.cpp)
# 自动链接:头文件 + lib + 自动拷贝 dll
target_link_libraries(app PRIVATE RuntimeSDK::RuntimeSDK)
4、代码示例(main.cpp)
cpp
#include <iostream>
#include "Runtime.h"
int main()
{
// 初始化
Runtime_ErrorCode ret = Runtime_Init(RUNTIME_LOG_INFO, nullptr);
if (ret != RUNTIME_SUCCESS)
{
std::cout << "初始化失败:" << Runtime_GetErrorMsg(ret) << std::endl;
return 1;
}
// 打印版本
std::cout << "版本:" << Runtime_GetVersion() << std::endl;
// 反初始化
Runtime_Shutdown();
return 0;
}
5、常见问题
1. 提示"找不到 Runtime.h"
- 检查环境变量
RUNTIME_SDK_ROOT路径是否正确 - 重启 CMake / VS 再试
2. 提示"无法找到 runtime_sdk.lib"
- 检查路径是否指向 SDK根目录
- 不要指向
lib或bin
3. 运行提示"缺少 runtime_sdk.dll"
- CMake 会自动拷贝 dll 到 exe 目录
- 重新生成一次工程即可
4. 多个项目共用?
- 完全可以,所有项目共用这一个 SDK
- 更新只需替换这一份 SDK,所有项目自动生效
七、批处理
1. 批处理:一键设置 SDK 环境变量
文件名:设置SDK环境变量.bat
batch
@echo off
chcp 65001 >nul
echo ==============================================
echo RuntimeSDK 环境变量自动设置
echo ==============================================
echo.
:: 获取当前 bat 所在目录(即 SDK 根目录)
set "SDK_PATH=%~dp0"
:: 去掉最后的反斜杠
set "SDK_PATH=%SDK_PATH:~0,-1%"
echo 检测到 SDK 路径:%SDK_PATH%
echo.
echo 即将设置系统环境变量:RUNTIME_SDK_ROOT
echo 变量值:%SDK_PATH%
echo.
pause
:: 设置用户级环境变量(不需要管理员,更安全)
setx RUNTIME_SDK_ROOT "%SDK_PATH%"
echo.
echo ==============================================
echo ✅ 环境变量设置完成!
echo 请重启 CMake / VS / 编辑器后生效
echo ==============================================
echo.
pause
使用方法:
把这个 .bat 放在 RuntimeSDK 根目录 ,客户双击运行 ,自动识别路径并设置环境变量,不用手动打字。
2. 批处理:一键清除 SDK 环境变量
文件名:清除SDK环境变量.bat
batch
@echo off
chcp 65001 >nul
echo ==============================================
echo 清除 RuntimeSDK 环境变量
echo ==============================================
echo.
setx RUNTIME_SDK_ROOT ""
echo.
echo ✅ 已清除环境变量 RUNTIME_SDK_ROOT
echo.
pause
3. 卸载 / 清理说明.txt
RuntimeSDK 卸载与清理说明
一、完全卸载
1. 删除整个 RuntimeSDK 文件夹
2. 运行"清除SDK环境变量.bat"
3. 清理完成
二、更新 SDK
1. 直接用新版文件夹覆盖旧版
2. 无需重新设置环境变量
3. 客户工程重新 CMake 生成即可
三、手动清理环境变量(可选)
如果不想用批处理,可以手动删除:
变量名:RUNTIME_SDK_ROOT
四、客户项目是否需要清理?
不需要。
SDK 与项目完全分离,不污染项目代码,删除 SDK 不影响客户工程。
4. 客户完整目录
RuntimeSDK/
├─ bin/
│ └─ runtime_sdk.dll
├─ lib/
│ └─ runtime_sdk.lib
├─ include/
│ └─ Runtime.h
├─ cmake/
│ └─ FindRuntimeSDK.cmake
├─ RuntimeSDKConfig.cmake
├─ RuntimeSDKConfigVersion.cmake
├─ 设置SDK环境变量.bat ← 新增
├─ 清除SDK环境变量.bat ← 新增
├─ 使用说明.txt ← 之前的
└─ 卸载与清理说明.txt ← 新增