ZhilianSDK 接入文档

v1.0.0
Windows DLL · Linux .so · C/C++ · 易语言 · Python · C# · Delphi

一个 SDK,Windows / Linux 都能接

无需自己实现加密、签名、机器码、心跳——调用几个函数就能完成卡密登录、账号登录、到期检测、点数扣减等全部流程。Windows 是 DLL,Linux 是 .so,两边接口完全一致。

34 个业务接口 AES-256-GCM / ChaCha20 Ed25519 验签 6 种机器码方案 Windows x64 / x86 Linux x86_64

1这是什么

ZhilianSDK 是一套网络验证客户端 SDK:Windows 下是 DLL,Linux 下是 .so,两个平台的函数名、参数、返回值完全一致。你的程序只要调用它,就能对接验证服务器,完成:

  • 登录验证:卡密登录、账号密码登录、设备试用登录
  • 状态维护:心跳保活、到期时间查询、账号状态检测
  • 账号体系:注册、充值、改密、找回密码、转绑
  • 数据交互:程序公告、远程变量、远程函数、账号云数据、点数
  • 加壳配套:WinLicense 授权、VMP 授权码
✓
你不需要关心的事:请求加密、响应验签、时间戳防重放、机器码采集、心跳周期、挑战码——全部由 SDK 内部自动处理。

2目录里有什么

Windows 包

DLL
ZhilianSDK_x64.dll
64 位主程序。默认放 exe 同目录;也支持内嵌成单文件(见常见问题)
DLL
ZhilianSDK_x86.dll
32 位主程序(易语言、VB6、Delphi 32 位用这个)
LIB
ZhilianSDK_x64.lib / ZhilianSDK_x86.lib
导入库,仅 C/C++ 静态链接时需要;其它语言不用

Linux 包

SO
libZhilianSDK.so
Linux 版全部内容,就这一个文件:x86_64 · 约 895 KB · 无需任何额外运行库
i
包里不含头文件(.h)。交付物就是上面这些库文件 + 本文档。各语言按「八、接口清单」自己声明要用的函数:易语言填 DLL 命令、C# 用 DllImport、Python 用 ctypes;C/C++ 同样不需要头文件。
!
位数必须匹配:64 位程序加载 x64 的 DLL,32 位程序加载 x86 的 DLL。搞反了会直接报「无法加载 DLL」或 0xC0000135。Linux 包目前只提供 x86_64。
i
不需要附带任何运行库,也不需要额外的配置文件:Windows 的 VC++ 运行库已静态编进 DLL,Linux 只依赖系统自带的 libc / libpthread / libdl。拷过去就能用。

35 分钟接入

拿到你的对接参数

登录验证后台 → 进入你的应用 → 点「一键生成对接参数」→ 复制那串加密文本(product_token)。
这串东西里已经包含了服务器地址、应用 UUID、加密密钥、验签公钥、算法,不用再手填任何字段。

把库放到程序目录

Windows:把 ZhilianSDK_x64.dll(或 x86)复制到 exe 旁边。
Linux:把 libZhilianSDK.so 放到程序同目录,编译时加 -Wl,-rpath,'$ORIGIN'(详见第七节)。

三步调用

ZL_Create → ZL_SetConfig(对接串) → ZL_CardLogin(卡密),完事。

C / C++
#include <stdio.h>

int main(void) {
    void* h = ZL_Create();
    if (!h) return 1;

    // ① 配置:粘贴后台「一键生成对接参数」里的整串
    if (ZL_SetConfig(h, "这里粘贴 product_token") != 0) {
        char err[512];
        ZL_GetLastErrorMessage(h, err, sizeof(err));
        printf("配置失败: %s\n", err);
        return 1;
    }

    // ② 卡密登录
    int r = ZL_CardLogin(h, "你的卡密");
    if (r != 0) {
        char err[512];
        ZL_GetLastErrorMessage(h, err, sizeof(err));
        printf("登录失败: %s\n", err);
        ZL_Destroy(h);
        return 1;
    }

    // ③ 开启自动心跳(默认是关的,建议打开)
    ZL_SetAutoHeartbeat(h, 1, 0);

    // ④ 取数据
    char buf[65536];
    ZL_GetExpire(h);
    ZL_GetResult(h, buf, sizeof(buf));
    printf("到期信息: %s\n", buf);

    ZL_Logout(h);
    ZL_Destroy(h);
    return 0;
}
✓
就这么点代码。加密、签名、机器码、挑战码全在 ZL_SetConfig 和 ZL_CardLogin 里自动完成。

4配置参数

方式一:整串配置(推荐)

直接把后台生成的 product_token 交给 ZL_SetConfig:

C / C++
ZL_SetConfig(h, "后台复制的那一整串");

返回值 0 表示配置成功;返回 -2 表示这串内容不合法(抄漏了、被改过、或根本不是对接串)。

i
对接串是加密的。它的作用除了省事,还能防止别人把你的程序改成连他自己的服务器——改了字符就解不开,SDK 会直接拒绝。

方式二:逐项设置(一般不用)

如果你确实需要自己控制每一项,可以分别调用:

函数说明必填
ZL_SetServer服务器地址,如 https://your-server.com(不要带 /api/open)必填
ZL_SetAppUUID后台应用 UUID必填
ZL_SetSecretEd25519 验签公钥(sign_scheme=1)或对称 Secret(=0)必填
ZL_SetKeysAES 与 ChaCha20 密钥(64 位 hex)按算法
ZL_SetCryptoalgorithm:0 明文 / 1 AES / 2 ChaCha20;sign_scheme:0 对称 / 1 Ed25519必填
ZL_SetClientVersion你的程序版本号,如 1.0.0建议
ZL_SetProgramName程序名(用于提示框标题、后台显示)选填
!
配置不完整时调用任何接口都会返回 -3,错误信息里会直接写「缺少:xxx」,照着补即可。

其它可调项

函数作用默认
ZL_SetAutoHeartbeat开启内置自动心跳线程。interval=0 表示跟随服务端下发的周期关闭
ZL_SetMachineRule机器码采集方案,见第九节规则 2
ZL_SetMachineId手动指定机器码(覆盖自动生成)自动
ZL_SetDeviceName设备名,默认「系统名-计算机名」自动
ZL_SetChallengeMode挑战码模式:-1 自动 / 0 关闭 / 1 强制-1 自动

5调用流程

标准流程如下,绝大多数场景照这个顺序写就行:

流程
ZL_Create()                 创建实例,拿到句柄
      │
ZL_SetConfig(对接串)         配置(一次性)
      │
ZL_SetAutoHeartbeat(1, 0)   开自动心跳(建议)
      │
ZL_CardLogin(卡密)           登录 → 成功返回 0
      │  └─ 失败:ZL_GetLastErrorMessage 取原因
      │
   ┌──┴──────────────────────┐
   │  业务调用                │
   │  ZL_GetExpire            │
   │  ZL_GetNotice            │
   │  ZL_GetVariable          │
   │  ZL_PointsDeduct         │
   │  ...                     │
   └──┬──────────────────────┘
      │
ZL_Logout()                 退出登录
      │
ZL_Destroy()                销毁实例(自动停心跳、释放资源)
!
ZL_Destroy 必须调用,否则心跳线程和句柄资源不会释放。程序退出前记得销毁。

结果怎么取

所有业务接口只返回一个 int(成功 0 / 失败非 0),真正的数据要用 ZL_GetResult 取:

C / C++
char buf[65536];
int r = ZL_GetExpire(h);
if (r == 0) {
    ZL_GetResult(h, buf, sizeof(buf));
    // buf 里是 JSON,例如:
    // {"code":0,"data":{"expired_at":"2026-12-31T23:59:59+08:00",...}}
    printf("%s\n", buf);
} else {
    char err[512];
    ZL_GetLastErrorMessage(h, err, sizeof(err));
    printf("失败(%d): %s\n", r, err);
}
i
缓冲区建议不低于 64 KB——公告、账号数据这类返回可能较长。缓冲区不够时 ZL_GetResult 返回 -9,内容会被截断,不会崩。

6各语言示例

i
本节是 Windows 示例(易语言、C#、VB6、Delphi 也只在 Windows 上用)。Linux 的 C / Python 示例在第七节——函数完全一样,只是加载方式不同。

C / C++

工程设置:不需要包含目录(包里没有头文件),链接器附加依赖项加 ZhilianSDK_x64.lib 即可。

C / C++
#pragma comment(lib, "ZhilianSDK_x64.lib")

void* h = ZL_Create();
ZL_SetConfig(h, token);
if (ZL_CardLogin(h, card) == 0) {
    ZL_SetAutoHeartbeat(h, 1, 0);
    // 登录成功
}
ZL_Destroy(h);

Python

Python
import ctypes, os

# DLL 与脚本同目录;若放别处,把该目录加进搜索路径
os.add_dll_directory(os.path.dirname(os.path.abspath(__file__)))
dll = ctypes.WinDLL("ZhilianSDK_x64.dll")

dll.ZL_Create.restype = ctypes.c_void_p
dll.ZL_SetConfig.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
dll.ZL_CardLogin.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
dll.ZL_GetResult.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_uint32]
dll.ZL_Destroy.argtypes = [ctypes.c_void_p]

h = dll.ZL_Create()
assert dll.ZL_SetConfig(h, open("token.txt").read().strip().encode()) == 0
assert dll.ZL_CardLogin(h, b"你的卡密") == 0

buf = ctypes.create_string_buffer(65536)
dll.ZL_GetExpire(h)
dll.ZL_GetResult(h, buf, 65536)
print(buf.value.decode("utf-8"))

dll.ZL_Destroy(h)
!
Python 里所有字符串参数都是 bytes(.encode()),返回值也要 .decode("utf-8")。别忘了给每个函数设 restype/argtypes,否则 64 位下句柄会被截断。

易语言

易语言编译出来是 32 位程序,请用 ZhilianSDK_x86.dll。

  1. 菜单「插入」→「DLL命令」,新建一条:
项填写内容
DLL命令名ZL_Create
返回值类型整数型
公开否
库文件名ZhilianSDK_x86.dll
在库中对应命令名ZL_Create
调用方式stdcall(一定要选这个)

按同样方式依次添加:

命令名返回值参数
ZL_SetConfig整数型句柄(整数型)、配置串(文本型)
ZL_CardLogin整数型句柄(整数型)、卡密(文本型)
ZL_GetResult整数型句柄(整数型)、缓冲区(文本型,传址)、长度(整数型)
ZL_GetLastErrorMessage整数型句柄(整数型)、缓冲区(文本型,传址)、长度(整数型)
ZL_SetAutoHeartbeat整数型句柄(整数型)、开启(整数型)、间隔(整数型)
ZL_Destroy无返回值句柄(整数型)
易语言(伪代码)
.局部变量 句柄, 整数型
.局部变量 结果, 文本型
.局部变量 长度, 整数型

句柄 = ZL_Create ()
.如果真 (ZL_SetConfig (句柄, 配置串) ≠ 0)
    信息框 (“配置失败”, 0, , )
    返回 ()
.如果真结束

.如果真 (ZL_CardLogin (句柄, 卡密) ≠ 0)
    结果 = 取空白文本 (512)
    ZL_GetLastErrorMessage (句柄, 结果, 512)
    信息框 (结果, 0, , )
    ZL_Destroy (句柄)
    返回 ()
.如果真结束

ZL_SetAutoHeartbeat (句柄, 1, 0)
' ... 业务代码 ...
ZL_Destroy (句柄)
i
中文乱码怎么办?接口返回的是 UTF-8。易语言用 ZL_Utf8ToAnsi(utf8, 缓冲, 长度) 转成 GBK 再显示,或者直接用支持 UTF-8 的组件。

C#

C#
using System;
using System.Runtime.InteropServices;
using System.Text;

class ZL {
    const string D = "ZhilianSDK_x64.dll";

    [DllImport(D, CallingConvention = CallingConvention.StdCall)]
    public static extern IntPtr ZL_Create();

    [DllImport(D, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
    public static extern int ZL_SetConfig(IntPtr h, string json);

    [DllImport(D, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
    public static extern int ZL_CardLogin(IntPtr h, string card);

    [DllImport(D, CallingConvention = CallingConvention.StdCall)]
    public static extern int ZL_SetAutoHeartbeat(IntPtr h, int enable, int interval);

    [DllImport(D, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
    public static extern int ZL_GetLastErrorMessage(IntPtr h, StringBuilder buf, uint size);

    [DllImport(D, CallingConvention = CallingConvention.StdCall)]
    public static extern void ZL_Destroy(IntPtr h);
}

// 调用
IntPtr h = ZL.ZL_Create();
if (ZL.ZL_SetConfig(h, token) != 0) { /* 失败 */ }
if (ZL.ZL_CardLogin(h, "卡密") == 0) {
    ZL.ZL_SetAutoHeartbeat(h, 1, 0);
}
ZL.ZL_Destroy(h);

Delphi / VB6 / 其他

Delphi
function ZL_Create: Pointer; stdcall; external 'ZhilianSDK_x86.dll';
function ZL_SetConfig(h: Pointer; cfg: PAnsiChar): Integer; stdcall; external 'ZhilianSDK_x86.dll';
function ZL_CardLogin(h: Pointer; card: PAnsiChar): Integer; stdcall; external 'ZhilianSDK_x86.dll';
VB6
Private Declare Function ZL_Create Lib "ZhilianSDK_x86.dll" () As Long
Private Declare Function ZL_SetConfig Lib "ZhilianSDK_x86.dll" (ByVal h As Long, ByVal cfg As String) As Long
Private Declare Function ZL_CardLogin Lib "ZhilianSDK_x86.dll" (ByVal h As Long, ByVal card As String) As Long
i
调用约定统一是 stdcall,字符串统一 UTF-8 的 char*。只要是支持调用 Windows DLL 的语言,按这个规则都能接。

7Linux 版(.so)

Linux 版和 Windows 版是同一份源码编译出来的:64 个函数名、参数、返回值、协议、加密、验签完全一致,服务端和后台配置都不用改。不同的只有文件形态和几处平台相关的东西。

运行时只要一个 .so

项目值
文件libZhilianSDK.so(约 895 KB)
架构x86_64(ELF64)
glibc≥ 2.17(CentOS 7 / Ubuntu 16.04 起全兼容)
依赖只有 libc / libpthread / libdl
导出64 个 ZL_*,与 Windows 逐个一致
✓
不依赖 libstdc++,也不硬依赖 OpenSSL。HTTPS 是运行时按需加载系统 OpenSSL(libssl.so.3 / 1.1),没有就自动降级成 HTTP,绝不会因为缺库导致 .so 加载失败。
!
不要改文件名。SONAME 是 libZhilianSDK.so,客户程序链接后记的就是这个名字,改名会报 cannot open shared object file——和 Windows 上把 DLL 改名是同一类问题。

编译与链接

-Wl,-rpath,'$ORIGIN' 的意思是「优先在程序自己的目录找 .so」。这样客户把程序和 .so 一起拷到任何目录都能跑,不用配 LD_LIBRARY_PATH。

Shell
# C
gcc main.c -I. -L. -lZhilianSDK -Wl,-rpath,'$ORIGIN' -o myapp

# C++
g++ main.cpp -I. -L. -lZhilianSDK -Wl,-rpath,'$ORIGIN' -o myapp

不想用 rpath 的话还有两种放法:

方式做法适用场景
程序同目录-Wl,-rpath,'$ORIGIN'推荐,随程序一起分发
系统库目录cp libZhilianSDK.so /usr/local/lib/ && ldconfig装在服务器上给多个程序共用
环境变量export LD_LIBRARY_PATH=/your/path:$LD_LIBRARY_PATH临时调试

C 示例

C
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    void* h = ZL_Create();
    if (!h) { printf("创建失败\n"); return 1; }

    /* 粘贴后台「一键生成对接参数」里那串加密文本 */
    int r = ZL_SetConfig(h, "粘贴你的 product_token");
    if (r != 0) { printf("配置失败: %d\n", r); ZL_Destroy(h); return 1; }

    r = ZL_CardLogin(h, "你的卡密");
    if (r != 0) {
        char err[1024] = {0};
        ZL_GetLastErrorMessage(h, err, sizeof(err));
        printf("登录失败: %d %s\n", r, err);
        ZL_Destroy(h);
        return 1;
    }

    char* buf = (char*)malloc(65536);
    ZL_GetResult(h, buf, 65536);
    printf("登录结果: %s\n", buf);
    free(buf);

    ZL_SetAutoHeartbeat(h, 1, 0);   /* 自动心跳,周期跟随服务端下发 */
    /* ... 你的业务逻辑 ... */
    ZL_Destroy(h);
    return 0;
}

Python 示例

用 ctypes 一定要先设 restype = c_void_p,否则 64 位下句柄会被截成 32 位整数,后面所有调用都会失败。

Python
import ctypes

lib = ctypes.CDLL("./libZhilianSDK.so")
H, S, U = ctypes.c_void_p, ctypes.c_char_p, ctypes.c_uint32

lib.ZL_Create.restype        = H
lib.ZL_SetConfig.argtypes    = [H, S]
lib.ZL_CardLogin.argtypes    = [H, S]
lib.ZL_GetResult.argtypes    = [H, S, U]
lib.ZL_GetLastErrorMessage.argtypes = [H, S, U]
lib.ZL_SetAutoHeartbeat.argtypes    = [H, ctypes.c_int, ctypes.c_int]
lib.ZL_Destroy.argtypes      = [H]

h = lib.ZL_Create()
print("SetConfig =", lib.ZL_SetConfig(h, b"粘贴你的 product_token"))
print("CardLogin =", lib.ZL_CardLogin(h, b"你的卡密"))

buf = ctypes.create_string_buffer(65536)
lib.ZL_GetResult(h, buf, 65536)
print(buf.value.decode("utf-8", "replace"))

lib.ZL_SetAutoHeartbeat(h, 1, 0)
# ... 业务 ...
lib.ZL_Destroy(h)

与 Windows 版的差异

项目WindowsLinux
交付文件DLL + 导入库单个 .so
调用约定__stdcall无(ZL_CALL 展开为空)
机器码来源CPUID / SMBIOS / DeviceIoControl/proc/cpuinfo、/sys/class/dmi/id、getifaddrs
同一台机器的机器码一套另一套(与 Windows 不同,设计如此)
HTTPS系统 WinINet 自带运行时加载 OpenSSL,缺则仅 HTTP
弹窗接口MessageBox 生效函数保留,但没有窗口
Utf8ToAnsi真实转码(给 GBK 环境用)原样返回(全链路 UTF-8)
加壳VMProtect 可加无对应工具(交付前已 strip 符号)
i
同一台机器双系统会被当成两台设备。算法和 6 种规则完全没变,只是硬件信息来源不同。客户同时发 Win / Linux 版时,后台的机器绑定要按两台处理;要复用同一张卡,得先在后台解绑或转绑。

跑不起来怎么查

现象原因解决
cannot open shared object file找不到 .so放程序同目录 + rpath,或 LD_LIBRARY_PATH,或装到 /usr/local/lib 后 ldconfig
wrong ELF class: ELFCLASS3232 位程序加载 64 位 .so本包只有 x86_64,需用 64 位程序
version `GLIBC_2.28' not found系统 glibc 太老本包要求 glibc ≥ 2.17
提示「系统缺少 OpenSSL」没装 libssl装 openssl-libs,或服务端改用 HTTP
中文乱码终端 / locale 不是 UTF-8输出本身就是 UTF-8,换个 UTF-8 终端或设 LANG
改名后加载失败SONAME 固定保持文件名 libZhilianSDK.so

上线前自检

Linux 包里带了两个自检程序,随便用哪个:

Shell
chmod +x selftest

./selftest              # 不带卡:加载 / 机器码 / 配置 / 公告 / 更新 / 版本 / 挑战码
./selftest 你的卡密      # 完整:登录 / 心跳 / 到期 / 数据 / 授权 / 自动心跳

# 或者用 Python 版(信息更详细,会打印系统版本、glibc、.so 的 MD5)
python3 test_so.py 你的卡密

8接口清单

全部 64 个导出函数。返回 0 成功;>0 服务端业务错误;<0 SDK 层错误。

i
各语言填参数类型时按这个对照:h = 句柄(指针,易语言填整数型);card / account / password / token / json / name = UTF-8 字符串 const char*;buf, size = 输出缓冲区 + 容量;amount / seconds / minutes = 整数;返回值统一 int。

配置与生命周期

接口函数说明
—ZL_Create / ZL_Destroy创建 / 销毁实例
—ZL_SetConfig(h, json)一次性配置(对接串或 JSON)
—ZL_SetServer / SetAppUUID / SetSecret / SetKeys / SetCrypto逐项配置
—ZL_SetClientVersion / SetProgramName版本号 / 程序名
—ZL_SetMachineRule / SetMachineId / GetMachineId / SetDeviceName机器码与设备名
—ZL_SetAutoHeartbeat / SetChallengeMode自动心跳 / 挑战码模式

登录

接口函数说明
5ZL_CardLogin(h, card)卡密登录,成功后 token 由 SDK 内部保存
6ZL_AccountLogin(h, user, pass)账号密码登录
30ZL_DeviceTrialLogin(h, email, code)设备试用登录(需后台开启)
13ZL_Logout(h)退出登录

账号

接口函数说明
7ZL_UserRegister(h, user, pass, email, code, card)注册(code 为邮箱验证码,card 可传空串)
8ZL_Recharge(h, user, card)账号充值
9ZL_SendEmailCode(h, email)发送注册验证码
10ZL_ClaimTrial(h, user, pass)领取试用
11ZL_SendResetCode(h, email)发送找回密码验证码
12ZL_ResetPassword(h, email, code, newpass)找回密码
20ZL_UpdatePassword(h, user, oldpass, newpass)修改密码
21ZL_ChangeBind(h, user, pass, replace_machine)转绑(replace_machine 传空 = 删除当前机器绑定)

查询与数据

接口函数说明
1ZL_GetNotice(h)程序公告 / 更新信息(免登录可调)
2ZL_GetUpdateUrl(h)更新地址
3ZL_CheckVersion(h, version)检测最新版本(version 传 NULL 用配置版本)
4ZL_GetCardInfo(h, card)卡密信息(免登录可调)
14ZL_GetExpire(h)到期时间
15ZL_Heartbeat(h)心跳 / 检测账号状态
16ZL_GetAppData(h)程序数据
17ZL_GetVariable(h, alias)取远程变量
18ZL_ExecuteFunction(h, alias, params)执行远程函数(params 传 "{}")
19ZL_GetUserData(h)取账号云数据
22ZL_SetUserData(h, json)存账号云数据
28ZL_PointsDeduct(h, amount, biz_no, remark)扣减点数(biz_no 幂等号可传 NULL,remark 为备注)
29ZL_PointsQuery(h)查询点数余额
31ZL_SendTrialCode(h, email)发送试用邮箱验证码
34ZL_PublicChallenge(h)申请挑战码(登录自动处理,一般不用手动调)
35ZL_ExecuteLua(h, alias, params)执行 LUA 远程函数

授权与支付

接口函数说明
26ZL_WinLicense(h, machine_code)申请 WinLicense 授权文件(machine_code 传 NULL 用当前机器码)
36ZL_VMPLicense(h, vmp_hwid)申请 VMP 授权码(vmp_hwid 为 VMP 环境的硬件 ID)
37ZL_OnlinePay(h, tier_id)在线支付下单
38ZL_Call(h, 38, "{}")获取 VMP 成品授权(RSA 公钥等),无独立函数
!
接口 23 / 24 / 25(封停用户、加黑名单、扣时间)属于管理操作,服务端禁止客户端调用,调用会返回「该接口不允许通过客户端调用」。请在后台操作。

工具函数

函数说明
ZL_GetResult(h, buf, size)取上次操作的完整结果 JSON(UTF-8)
ZL_GetLastError(h)取上次错误码
ZL_GetLastErrorMessage(h, buf, size)取错误描述(含服务端返回的中文提示)
ZL_GetState(h)当前状态:0 已创建未配置 / 1 已配置 / 2 已登录 / 3 已登出
ZL_GetToken(h, buf, size)取当前会话 token
ZL_GetHeartbeatInterval(h)服务端下发的心跳间隔(秒)
ZL_Utf8ToAnsi / ZL_AnsiToUtf8UTF-8 ↔ GBK 转换(易语言常用)
ZL_SetUICallback(h, cb, ud)自定义提示回调(替代系统 MessageBox)
ZL_ShowErrorBox / ZL_ShowResultBox可选提示框,只有你显式调用才会弹
ZL_GetVersionString()SDK 版本号(两平台一致)
ZL_IsProtected()是否已被 VMProtect 加壳:Windows 加壳后 1、未加壳 0;Linux 恒 0
✓
SDK 不会自己弹窗。所有结果和错误都通过返回值 + ZL_GetResult / ZL_GetLastErrorMessage 给你,怎么显示完全由你决定。ZL_Show*Box 不调用就永远不会有窗口(Linux 上这两个函数保留,但没有窗口可弹)。

9返回值与错误码

返回值的三种情况

返回值含义怎么处理
= 0成功用 ZL_GetResult 取数据
> 0服务端业务错误(如卡号不存在、账号已到期)用 ZL_GetLastErrorMessage 取中文提示,直接展示给用户
< 0SDK 层错误(网络、配置、验签等)见下表

错误码

值常量含义与处理
-1ZL_ERR_INVALID_HANDLE句柄无效,检查是否已 ZL_Destroy
-2ZL_ERR_INVALID_ARG参数不合法(如对接串被改过)
-3ZL_ERR_NOT_CONFIGURED配置不完整,错误信息会写明缺哪个字段
-4ZL_ERR_NETWORK连不上服务器,检查地址、端口、防火墙
-5ZL_ERR_TIMEOUT请求超时
-6ZL_ERR_HTTPHTTP 层错误
-7ZL_ERR_JSON响应格式异常
-8ZL_ERR_CRYPTO加解密失败(密钥不对)
-9ZL_ERR_BUFFER_SMALL你的缓冲区太小,换大一点
-10ZL_ERR_INTERNAL内部错误
-11ZL_ERR_NOT_LOGGED_IN还没登录就调了需要登录的接口
-12ZL_ERR_SESSION_EXPIRED会话失效,需要重新登录
-13ZL_ERR_SIGN_VERIFY响应验签失败(数据被篡改,或配置的公钥与服务器不匹配)
-14ZL_ERR_MACHINE_CODE机器码生成失败
i
实际写代码时,建议统一按 r == 0 判定成功,非 0 就把 ZL_GetLastErrorMessage 的内容显示出来——服务端返回的中文提示已经足够友好,不用自己再翻译一遍。

10机器码说明

机器码用于「一机一码」绑定。SDK 会自动采集硬件信息并生成 32 位大写 MD5,你只需要调 ZL_GetMachineId 取。

不同硬件环境(虚拟机、云主机、部分精简系统)可能取不到某些项,可以换规则:

规则采集项适用场景
1CPU + 主板UUID + 磁盘 + 主板序列号 + MAC最严格,但换网卡会变
2CPU + 主板UUID + 磁盘 + 主板序列号默认,稳定性最好
3磁盘 + 主板序列号CPU 等信息取不到时
4仅 CPU虚拟机环境
5仅磁盘换硬件频繁时
6仅 MAC特殊场景
C / C++
ZL_SetMachineRule(h, ZL_MACHINE_RULE_DEFAULT);   // 2
char mc[128];
ZL_GetMachineId(h, mc, sizeof(mc));
printf("机器码: %s\n", mc);   // 例如 EDB3C3254251E0206503850039E74BE6
!
规则一旦上线别随便改——换了规则机器码就变了,老用户会被判定成新机器,需要重新绑定。后台如果开了「机器码校验」,改规则前先通知用户。

11常见问题

加载 DLL 报 0xC0000135 / 找不到模块

  • DLL 没放在 exe 同目录(或程序搜索路径里)
  • 位数不匹配:32 位程序加载了 x64 的 DLL,或反过来

Linux 上加载 .so 失败

按报错对号入座:

  • cannot open shared object file —— 找不到文件。放程序同目录并加 -Wl,-rpath,'$ORIGIN',或设 LD_LIBRARY_PATH
  • wrong ELF class —— 位数不对,本包只有 x86_64
  • version `GLIBC_2.xx' not found —— 系统 glibc 太老,本包要求 ≥ 2.17

完整对照表和示例代码见第七节。

返回 -3,说「缺少 xxx」

配置不完整。用对接串一般不会出现;如果是逐项设置,按错误信息补对应字段即可。

返回 -13 验签失败

三种可能:① 对接串里是别人的密钥;② 服务器地址填错,连到了另一个站;③ 中间有代理改包。先核对对接串是不是这个应用的。

中文显示乱码

接口返回的是 UTF-8。易语言、VB6 这类 GBK 环境用 ZL_Utf8ToAnsi 转一下;Python 用 .decode("utf-8");C# 用 Encoding.UTF8.GetString。

要不要自己开心跳

建议开:ZL_SetAutoHeartbeat(h, 1, 0)。默认是关闭的,不开的话长时间在线可能被服务端判定掉线。interval 传 0 就会自动跟随服务端下发的周期,不用自己算。

多线程能共用一个句柄吗

SDK 内部有锁,多线程调同一个句柄不会崩,但结果会互相覆盖(ZL_GetResult 取到的是"最后一次")。推荐每个线程用独立句柄(各自 ZL_Create),互不干扰。

公告里说的「挑战码」我要处理吗

不用。SDK 默认会自动判断:后台开了公开效验就自动申请并带上,没开就不发。除非你手动调了 ZL_SetChallengeMode(h, 0) 关掉,否则不用管。

WinLicense / VMP 授权怎么用

登录成功后调 ZL_WinLicense(接口 26)或 ZL_VMPLicense(接口 36),把返回的授权数据交给对应的加壳工具。machine_code / vmp_hwid 传 NULL 时会自动用当前环境的值。

能不能做成单文件,不单独带 DLL

可以,DLL 不是非得躺在 exe 旁边。常见三种做法:

  1. 资源嵌入 + 运行时释放再加载(最稳,推荐)
    把 DLL 作为 RCDATA 资源编进 exe,启动时写到 %TEMP% 或 %LOCALAPPDATA% 目录,再 LoadLibrary 那个路径。加壳版 DLL 也能正常用,兼容性最好。
  2. 用打包工具虚拟进 exe
    比如 Enigma Virtual Box,配置好把 DLL 虚拟进去,不落盘。省事,但个别杀软可能对产物误报。
  3. MemoryModule 之类内存加载(完全不落盘)
    注意:本 DLL 经过 VMProtect 加壳,标准 MemoryModule 实现通常不处理 TLS 回调和异常目录,大概率加载失败。想走这条路必须自己实测,别直接上生产。
i
内嵌不会削弱 DLL 自身的加壳保护(保护在 DLL 内部,跟它从哪加载无关)。但「释放到临时目录」这种文件会短暂落盘,介意的话选第 2 或第 3 种。
!
随包提供的 .lib 是导入库,不是静态库——用它链接后运行时仍然需要 DLL,不能靠它做到「真静态链接」。要做单文件只能用上面三种方式。

需要头文件(ZhilianSDK.h)吗

不需要,包里不提供 .h。声明函数各语言都能自己做,一次写完就不用再管:

  • 易语言:菜单「插入 → DLL命令」,按「八、接口清单」填函数名和参数类型
  • C#:[DllImport("ZhilianSDK_x64.dll")]
  • Delphi / VB6:external / Declare Function ... Lib
  • Python:ctypes 设 restype / argtypes
  • C / C++:自己声明原型,或用 GetProcAddress / dlsym 动态取地址

12注意事项

  • 位数必须匹配:32 位程序用 x86,64 位用 x64;Linux 目前只提供 x86_64。
  • Windows 的 DLL 默认放 exe 同目录,或保证在系统搜索路径里;不想单独带文件的话可以内嵌成单文件(见常见问题)。
  • Linux 的 .so 放程序同目录并加 -Wl,-rpath,'$ORIGIN';文件名不要改,SONAME 固定为 libZhilianSDK.so。
  • 用完必须 ZL_Destroy,否则心跳线程不退出。
  • 句柄不要跨进程/跨模块传递,它只在当前进程有效。
  • 字符串统一 UTF-8,GBK 环境记得转码。
  • 缓冲区建议 ≥ 64KB,公告和账号数据可能比较长。
  • 对接串属于敏感信息,不要明文写在会被别人看到的地方;它已经做了加密,但能被复制就能被使用。
  • 如果程序要加壳(VMProtect / WinLicense 等),先完成接入调试,再加壳,否则排错会很痛苦。Linux 没有对应的加壳工具,交付的 .so 已 strip 掉符号表。
✓
文档到这里就完了。实际接入中如果遇到本文没覆盖的情况,把 ZL_GetLastErrorMessage 的输出和调用顺序发过来,定位起来很快。