Mục lục thư viện
Thư viện tài liệuBắt đầu theo ngôn ngữ
Bắt đầu với thư viện C
Thư viện C là SDK C++ của ClavisAUH bọc sau 19 hàm C, cho mọi ngôn ngữ gọi được một hàm C: C, Delphi và Free Pascal, Lua, Python qua ctypes, C# qua P/Invoke, VBA, AutoIt, AutoHotkey, PureBasic… Bài này đi từ tải thư viện tới lần kích hoạt đầu tiên, với mã mẫu bằng C, Python, Lua và Delphi.
6 phút đọc
Cần có
- Windows x64 hoặc Linux x64. Thư viện hiện chỉ có bản 64-bit: chương trình 32-bit (VB6, AutoIt và AutoHotkey bản 32-bit, Office 32-bit, Delphi build Win32) chưa nạp được. Chưa có bản macOS và ARM.
- Một ứng dụng trên dashboard. Trang Tích hợp › Kết nối phần mềm của nó có địa chỉ API, App ID, khoá công khai và nút Tạo key thử nghiệm. Địa chỉ API kết thúc bằng
/api, không có/v1:https://<trang ClavisAUH của bạn>/api.
1. Tải thư viện
Tải ở trang Tải về và kiểm SHA-256:
- Windows:
clavis-c-1.0.0-windows-x64.zip, gồmclavis.dll(một file, chỉ cần DLL có sẵn của Windows),clavis.libđể liên kết với MSVC, và thư mụcincludevớiclavis.h,clavis_errors.h. - Linux:
clavis-c-1.0.0-linux-x64.tar.gz, gồmlibclavis.so.1(vàlibclavis.sotrỏ tới nó) cùng thư mụcinclude. Máy chạy cần glibc 2.32 trở lên,libsodiumvàlibcurlcủa hệ thống (Debian, Ubuntu:apt install libsodium23 libcurl4).
Đặt clavis.dll cạnh file chạy của bạn trên Windows. Trên Linux, nạp thư viện theo đường dẫn, hoặc đặt thư mục chứa nó vào LD_LIBRARY_PATH.
2. Quy tắc chung
Năm điều này đúng với mọi ngôn ngữ gọi thư viện:
- Chuỗi vào và ra đều là UTF-8, kết thúc bằng byte 0. Ngôn ngữ dùng UTF-16 (C#, Delphi, AutoHotkey) phải chuyển trước khi gọi.
- Chuỗi thư viện trả về thuộc về client, chỉ còn đúng tới lượt gọi kế tiếp trên client đó: chép ra nếu cần giữ.
- Mọi hàm trả
inttrả0(CLAVIS_OK) khi thành công, hoặc một số lỗi cố định, cùng bảng Mã lỗi với mọi SDK (cột C). - Một client chỉ dùng trên một luồng. Mỗi lượt gọi chờ mạng, nên đừng gọi trên luồng giao diện.
- Trên Windows, mọi hàm theo quy ước gọi
cdecl. Thư viện tự giữ khoá thiết bị và token offline mới nhất trong thư mục trạng thái bạn truyền vào, nên phiên và token offline dùng tiếp được giữa các lần chạy mà bạn không phải đụng tới file nào (còn suất trên license đi theo các định danh đã băm của máy).
3. K1 bằng C
Mở một client cho máy này. Địa chỉ API, App ID và khoá công khai là ba giá trị ở trang Kết nối phần mềm; tham số cuối là phiên bản phần mềm của bạn:
clavis_client* client = clavis_open(env("CLAVIS_BASE_URL", ""), env("CLAVIS_APP_ID", ""),
env("CLAVIS_APP_KEY", ""), env("CLAVIS_STATE_DIR", ".clavis"), "1.0.0");
if (!client) {
printf("FAIL settings\n");
return 1;
}
Kích hoạt key của khách. Khi không nhận được câu trả lời, clavis_offline cho biết token offline đã lưu còn dùng được trên máy này không. Thư viện lưu token trên mọi gói, trừ khi Chế độ chỉ online đang bật; không có token dùng được thì clavis_offline trả CLAVIS_E_OFFLINE_UNAVAILABLE (offline_unavailable): cần lên mạng, chờ thì không giúp gì.
int status = clavis_init(client);
if (status == CLAVIS_OK) status = clavis_activate(client, env("CLAVIS_LICENSE", ""));
if (status != CLAVIS_OK) {
/* No answer: the offline token saved last time keeps the software working. */
if (clavis_retryable(status) && clavis_offline(client) == CLAVIS_OK) {
printf("Offline: using the license saved on this machine\nOK k1 offline\n");
clavis_close(client);
return 0;
}
printf("%s\nFAIL %s\n", clavis_error_message(client, lang), clavis_error_code(client));
clavis_close(client);
return 1;
}
Khi phần mềm đang chạy, gửi heartbeat đều đặn để giữ phiên, khoảng mỗi 5 tới 30 phút: phiên hết sau 60 phút không có heartbeat.
status = clavis_heartbeat(client);
Biên dịch nguyên file mẫu k1.c dưới đây với thư viện vừa tải:
# Linux
gcc k1.c -I clavis-c-1.0.0-linux-x64/include -L clavis-c-1.0.0-linux-x64 -lclavis -o k1
LD_LIBRARY_PATH=clavis-c-1.0.0-linux-x64 ./k1
:: Windows, trong x64 Native Tools Command Prompt for VS 2022
cl /utf-8 k1.c /I clavis-c-1.0.0-windows-x64\include clavis-c-1.0.0-windows-x64\clavis.lib
copy clavis-c-1.0.0-windows-x64\clavis.dll .
k1.exe
/* K1 through the C library: activate, heartbeat, offline when the server is out of reach.
* Settings come from CLAVIS_BASE_URL, CLAVIS_APP_ID, CLAVIS_APP_KEY, CLAVIS_LICENSE, CLAVIS_STATE_DIR, CLAVIS_LANG.
* Ends with "OK k1 active" (or "OK k1 offline"), or the sentence for the user and "FAIL <code>". */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "clavis.h"
static const char* env(const char* name, const char* fallback) {
const char* value = getenv(name);
return value && *value ? value : fallback;
}
int main(void) {
const char* lang = env("CLAVIS_LANG", "en");
if (clavis_abi_version() != CLAVIS_ABI_VERSION) {
printf("FAIL abi\n");
return 1;
}
clavis_client* client = clavis_open(env("CLAVIS_BASE_URL", ""), env("CLAVIS_APP_ID", ""),
env("CLAVIS_APP_KEY", ""), env("CLAVIS_STATE_DIR", ".clavis"), "1.0.0");
if (!client) {
printf("FAIL settings\n");
return 1;
}
int status = clavis_init(client);
if (status == CLAVIS_OK) status = clavis_activate(client, env("CLAVIS_LICENSE", ""));
if (status != CLAVIS_OK) {
/* No answer: the offline token saved last time keeps the software working. */
if (clavis_retryable(status) && clavis_offline(client) == CLAVIS_OK) {
printf("Offline: using the license saved on this machine\nOK k1 offline\n");
clavis_close(client);
return 0;
}
printf("%s\nFAIL %s\n", clavis_error_message(client, lang), clavis_error_code(client));
clavis_close(client);
return 1;
}
status = clavis_heartbeat(client);
if (status != CLAVIS_OK) {
printf("%s\nFAIL %s\n", clavis_error_message(client, lang), clavis_error_code(client));
clavis_close(client);
return 1;
}
printf("Message of the day: %s\n", clavis_variable(client, "motd"));
clavis_logout(client);
clavis_close(client);
printf("OK k1 active\n");
return 0;
}
Mẫu đọc cài đặt từ biến môi trường CLAVIS_BASE_URL, CLAVIS_APP_ID, CLAVIS_APP_KEY và CLAVIS_LICENSE (CLAVIS_APP_KEY là khoá công khai; license là key thử nghiệm tạo bằng Tạo key thử nghiệm):
$env:CLAVIS_BASE_URL = "<địa chỉ API>"
$env:CLAVIS_APP_ID = "<App ID>"
$env:CLAVIS_APP_KEY = "<khoá công khai>"
$env:CLAVIS_LICENSE = "<key thử nghiệm>"
export CLAVIS_BASE_URL="<địa chỉ API>" CLAVIS_APP_ID="<App ID>" CLAVIS_APP_KEY="<khoá công khai>" CLAVIS_LICENSE="<key thử nghiệm>"
Dòng cuối in ra OK k1 active. Chạy lại trên cùng máy vẫn thành công và không tốn thêm suất: server nhận ra máy qua các định danh đã băm (Thiết bị, HWID và chuyển máy). Dòng Message of the day: in biến tên motd của ứng dụng, để trống cho tới khi bạn tạo biến đó (Biến ứng dụng).
4. Từ ngôn ngữ khác
Mỗi mẫu dưới đây làm đúng K1 như trên, qua cơ chế gọi hàm C của ngôn ngữ đó. Mẫu Python và Lua chạy với API thật trong CI; mẫu Delphi chưa được biên dịch trong CI (CI không có Delphi).
Python (ctypes, không cần cài gói nào, Python 3.8 trở lên). Nguyên file mẫu: nó khai báo kiểu của từng hàm, truyền chuỗi dạng byte UTF-8 (truyền str của Python sẽ bị từ chối) và giải mã byte nhận về:
"""K1 through the C library with ctypes: Python 3.8 or later, nothing to install, only the library file.
Put clavis.dll (Windows, one file) or libclavis.so (Linux) next to this file, or point CLAVIS_LIBRARY at it. Settings: CLAVIS_BASE_URL, CLAVIS_APP_ID, CLAVIS_APP_KEY, CLAVIS_LICENSE,
CLAVIS_STATE_DIR, CLAVIS_LANG. Strings cross into the library as UTF-8 bytes (setting() below encodes them), and
the strings it returns come back as bytes to decode.
"""
import ctypes
import os
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
NAME = {"win32": "clavis.dll", "darwin": "libclavis.dylib"}.get(sys.platform, "libclavis.so")
PATH = os.environ.get("CLAVIS_LIBRARY") or os.path.join(HERE, NAME)
if sys.platform == "win32":
os.add_dll_directory(os.path.dirname(os.path.abspath(PATH))) # where Windows looks for clavis.dll
lib = ctypes.CDLL(PATH)
c_str, c_int, handle = ctypes.c_char_p, ctypes.c_int, ctypes.c_void_p
lib.clavis_open.restype, lib.clavis_open.argtypes = handle, [c_str] * 5
lib.clavis_close.restype, lib.clavis_close.argtypes = None, [handle]
for name in ("clavis_init", "clavis_heartbeat", "clavis_logout", "clavis_offline"):
getattr(lib, name).restype, getattr(lib, name).argtypes = c_int, [handle]
lib.clavis_activate.restype, lib.clavis_activate.argtypes = c_int, [handle, c_str]
lib.clavis_variable.restype, lib.clavis_variable.argtypes = c_str, [handle, c_str]
lib.clavis_error_code.restype, lib.clavis_error_code.argtypes = c_str, [handle]
lib.clavis_error_message.restype, lib.clavis_error_message.argtypes = c_str, [handle, c_str]
lib.clavis_retryable.restype, lib.clavis_retryable.argtypes = c_int, [c_int]
lib.clavis_abi_version.restype, lib.clavis_abi_version.argtypes = c_int, []
# The declarations above match ABI version 1 (CLAVIS_ABI_VERSION in clavis.h).
if lib.clavis_abi_version() != 1:
sys.exit("FAIL abi")
def setting(name, fallback=""):
return os.environ.get(name, fallback).encode("utf-8")
# Messages in Vietnamese need a UTF-8 console (the default on Linux and in Windows Terminal).
sys.stdout.reconfigure(encoding="utf-8")
LANG = setting("CLAVIS_LANG", "en")
client = lib.clavis_open(
setting("CLAVIS_BASE_URL"),
setting("CLAVIS_APP_ID"),
setting("CLAVIS_APP_KEY"),
setting("CLAVIS_STATE_DIR", ".clavis"),
b"1.0.0",
)
if not client:
sys.exit("FAIL settings")
def fail():
"""Shows the last error in the customer's language, closes the client and stops."""
print(lib.clavis_error_message(client, LANG).decode("utf-8"))
print("FAIL", lib.clavis_error_code(client).decode())
lib.clavis_close(client)
sys.exit(1)
status = lib.clavis_init(client) or lib.clavis_activate(client, setting("CLAVIS_LICENSE"))
if status != 0:
if lib.clavis_retryable(status) and lib.clavis_offline(client) == 0:
print("Offline: using the license saved on this machine")
print("OK k1 offline")
lib.clavis_close(client)
sys.exit(0)
fail()
if lib.clavis_heartbeat(client) != 0:
fail()
print("Message of the day:", lib.clavis_variable(client, b"motd").decode("utf-8"))
lib.clavis_logout(client)
lib.clavis_close(client)
print("OK k1 active")
Chạy bằng python k1_ctypes.py, với clavis.dll hoặc libclavis.so cạnh file, hoặc đường dẫn của nó trong biến CLAVIS_LIBRARY.
Lua (LuaJIT FFI). Khai báo:
ffi.cdef([[
typedef struct clavis_client clavis_client;
int clavis_abi_version(void);
clavis_client* clavis_open(const char* base_url, const char* app_id, const char* app_public_key,
const char* state_dir, const char* app_version);
void clavis_close(clavis_client* client);
int clavis_init(clavis_client* client);
int clavis_activate(clavis_client* client, const char* key);
int clavis_heartbeat(clavis_client* client);
int clavis_logout(clavis_client* client);
int clavis_offline(clavis_client* client);
const char* clavis_variable(clavis_client* client, const char* name);
const char* clavis_error_code(clavis_client* client);
const char* clavis_error_message(clavis_client* client, const char* lang);
int clavis_retryable(int error);
]])
local clavis = ffi.load(os.getenv("CLAVIS_LIBRARY") or "clavis")
Gọi:
local status = clavis.clavis_init(client)
if status == 0 then status = clavis.clavis_activate(client, setting("CLAVIS_LICENSE")) end
if status ~= 0 then
if clavis.clavis_retryable(status) == 1 and clavis.clavis_offline(client) == 0 then
print("Offline: using the license saved on this machine")
print("OK k1 offline")
os.exit(0)
end
print(ffi.string(clavis.clavis_error_message(client, setting("CLAVIS_LANG", "en"))))
print("FAIL " .. ffi.string(clavis.clavis_error_code(client)))
os.exit(1)
end
Chạy bằng CLAVIS_LIBRARY=./libclavis.so luajit k1.lua.
Delphi và Free Pascal (build Win64). Khai báo:
type
TClavisClient = Pointer;
function clavis_open(BaseUrl, AppId, AppPublicKey, StateDir, AppVersion: PAnsiChar): TClavisClient; cdecl;
external ClavisLib;
procedure clavis_close(Client: TClavisClient); cdecl; external ClavisLib;
function clavis_init(Client: TClavisClient): Integer; cdecl; external ClavisLib;
function clavis_activate(Client: TClavisClient; Key: PAnsiChar): Integer; cdecl; external ClavisLib;
function clavis_heartbeat(Client: TClavisClient): Integer; cdecl; external ClavisLib;
function clavis_logout(Client: TClavisClient): Integer; cdecl; external ClavisLib;
function clavis_offline(Client: TClavisClient): Integer; cdecl; external ClavisLib;
function clavis_variable(Client: TClavisClient; Name: PAnsiChar): PAnsiChar; cdecl; external ClavisLib;
function clavis_error_code(Client: TClavisClient): PAnsiChar; cdecl; external ClavisLib;
function clavis_error_message(Client: TClavisClient; Lang: PAnsiChar): PAnsiChar; cdecl; external ClavisLib;
function clavis_retryable(Error: Integer): Integer; cdecl; external ClavisLib;
Gọi:
Status := clavis_init(Client);
if Status = 0 then
Status := clavis_activate(Client, PAnsiChar(Setting('CLAVIS_LICENSE', '')));
if Status <> 0 then
begin
if (clavis_retryable(Status) = 1) and (clavis_offline(Client) = 0) then
begin
WriteLn('Offline: using the license saved on this machine');
WriteLn('OK k1 offline');
Halt(0);
end;
WriteLn(UTF8ToString(clavis_error_message(Client, PAnsiChar(Setting('CLAVIS_LANG', 'en')))));
WriteLn('FAIL ', string(clavis_error_code(Client)));
Halt(1);
end;
C# và .NET Framework (P/Invoke). Xem Bắt đầu với Unity: cùng mã chạy được ngoài Unity.
VBA (Office 64-bit), AutoIt, AutoHotkey, PureBasic. Chưa có mẫu kiểm thử cho các ngôn ngữ này. Gọi đúng tên hàm trong include/clavis.h bằng cơ chế gọi DLL của ngôn ngữ (Declare PtrSafe, DllCall…), theo các quy tắc ở mục 2: chương trình 64-bit, chuỗi UTF-8, chép chuỗi trả về trước lượt gọi kế tiếp.
5. Thấy trên dashboard
- Tích hợp › Kết nối phần mềm: bước 4 chuyển sang Đã kết nối, kèm
c/1.0.0và hệ điều hành của máy. - Phát hành & bản quyền › License: key chuyển từ Chưa dùng sang Đang dùng.
- Người dùng cuối › Thiết bị & phiên: máy vừa chạy, cùng phiên nó vừa mở.
6. Xử lý lỗi
Sau một lượt gọi trả số khác 0:
clavis_error_code(client): mã dạng chữ, ví dụinvalid_credentialshaydevice_limit.clavis_error_message(client, "vi"): câu cho người dùng phần mềm của bạn, tiếng Việt hoặc"en".clavis_retryable(số lỗi): khác0khi không nhận được câu trả lời; thử lại sau có thể thành công.
Số lỗi lấy từ clavis_errors.h và không bao giờ đổi. Khi build với một bản clavis.h rồi chạy với bản thư viện khác, so clavis_abi_version() với CLAVIS_ABI_VERSION như mẫu C: thư viện chỉ thêm hàm, không đổi hay bỏ hàm cũ.
7. Đóng gói và phát hành
- Windows: phát hành
clavis.dllcạnh file chạy của bạn, không cần cài thêm gì. Linux: kèmlibclavis.so.1và ghi rõ máy khách cầnlibsodiumvàlibcurl. - Thư mục trạng thái (tham số thứ tư của
clavis_open) đặt ở nơi người dùng ghi được, riêng cho từng người dùng:%APPDATA%\TênPhầnMềmtrên Windows,~/.config/ten-phan-memtrên Linux. Đừng đặt trong thư mục cài đặt. - Truyền phiên bản bạn phát hành vào
clavis_open: server dùng nó để chặn bản quá cũ và báo có bản mới, xem Bản phát hành có ký và cập nhật. - Trước khi bán, bật Đã phát hành công khai ở Tích hợp › Cài đặt ứng dụng. Mọi key TEST ngừng hoạt động, nên key thử nào lỡ lọt ra ngoài cũng không dùng được nữa.
Câu hỏi thường gặp
Ngôn ngữ của tôi không gọi được hàm C? Dùng công cụ dòng lệnh: chương trình nào chạy được một lệnh và đọc được JSON đều dùng được.
Có những hàm nào? 19 hàm, mỗi hàm một dòng giải thích trong include/clavis.h: mở và đóng client, kích hoạt, dùng thử, đăng ký, đăng nhập, đổi key, heartbeat, đăng xuất, quyền tính năng (clavis_has), biến (clavis_variable), kiểm tra cập nhật, offline và đọc lỗi.
Khách cài lại hệ điều hành hay đổi máy thì sao? Cài lại trên cùng máy, server vẫn nhận ra máy nhờ dấu nhận diện phần cứng đã băm. Máy mới chiếm một suất mới; hết suất thì khách nhận device_limit, và bạn Đặt lại thiết bị trong trang License. Xem Thiết bị, HWID và chuyển máy.