eduwin 工具 API

数字与字符串工具

int rand_int(int min, int max);

返回 [min, max] 闭区间的随机整数。

const WCHAR *itow(int value);

整数 → 宽字符串。如 itow(42) 返回 L"42"

const WCHAR *ftow(double value);

小数 → 宽字符串。如 ftow(3.14) 返回 L"3.14"

void str_concat(WCHAR *dest, int size, ...);

拼接多个宽字符串,最后一个参数必须是 NULL 表示结束。

WCHAR buf[64];
str_concat(buf, 64, L"你点击了 ", itow(count), L" 次", NULL);
set_text(label, buf);
void str_format(WCHAR *buf, int size, const WCHAR *fmt, ...);
void str_format_a(char *buf, int size, const char *fmt, ...);

格式化字符串到 buf,用法类似 printf,自动截断不会溢出。str_format 输出到宽字符串(WCHAR),str_format_a 输出到 UTF-8 字符串(char)。

WCHAR buf[64];
str_format(buf, 64, L"Client: %d x %d", w, h);  /* buf = L"Client: 682 x 453" */
str_format(buf, 64, L"%.1f%%", 95.7f);          /* buf = L"95.7%" */

char buf_a[64];
str_format_a(buf_a, 64, "{\"result\":%d}", 42); /* buf_a = {"result":42} */

剪贴板

void clipboard_copy(const WCHAR *text);

将文本复制到系统剪贴板。


Base64 解码

int base64_decode(const char *in, int in_len, unsigned char *out);

解码 Base64 字符串为原始字节。in_len 传入输入字符串长度(用 strlen),返回值是输出的字节数。out 缓冲区需足够大(最大约为 in_len * 3 / 4)。

unsigned char data[1024];
int len = base64_decode("SGVsbG8=", 8, data);  // len=5, data="Hello"

内部在 eduwin_d3d 的 glTF 加载器中使用,解码 data URI 中的 base64 数据。


文件读写

文本文件读写

int read_text_file(const WCHAR *filepath, WCHAR *buf, int size);
int write_text_file(const WCHAR *filepath, const WCHAR *text);

read_text_file 读取文本文件内容到宽字符串缓冲区。自动以 UTF-8 编码读取(兼容含 BOM 的文件)。成功返回读取的字符数(不含末尾 \0),失败返回 -1

write_text_file 将宽字符串写入文本文件,编码为 UTF-8(含 BOM)。成功返回 1,失败返回 0

WCHAR buf[1024];
int n = read_text_file(L"data.txt", buf, 1024);

二进制文件读写

int read_bin_file(const WCHAR *filepath, void *buf, int elem_size, int max_count);
int write_bin_file(const WCHAR *filepath, void *buf, int elem_size, int count);

read_bin_file 从二进制文件中读取数据到缓冲区。elem_size 为每个元素的字节数,max_count 为最多读取多少个元素。成功返回实际读取的元素数,失败返回 -1

write_bin_file 将缓冲区数据写入二进制文件。count 为要写入的元素个数。成功返回 1,失败返回 0

常用于读写结构体或结构体数组:

typedef struct {
    int id;
    WCHAR name[32];
    int score;
} Student;

Student students[3] = {
    {1001, L"张三", 85},
    {1002, L"李四", 92},
    {1003, L"王五", 78}
};

write_bin_file(L"students.bin", students, sizeof(Student), 3);

Student buf[3];
int n = read_bin_file(L"students.bin", buf, sizeof(Student), 3);

注意事项:

  1. 不可跨平台——结构体内存布局(对齐、填充字节)在不同编译器下可能不同。在 eduwin(MSVC x64)环境下读写无问题。
  2. 不能包含指针——结构体里有 char *WCHAR * 指针时,只会写入指针地址值而不是指向的字符串内容。应改用固定大小的字符数组(如 WCHAR name[32])。
  3. 路径用宽字符串——与 eduwin 其他文件 API 一致。
  4. 防止缓冲区溢出——buf 必须足够大以容纳 elem_size * max_count 字节。

二进制文件读取(动态分配)

unsigned char* read_bin_file_alloc(const char *path, int *out_len);

将整个文件一次性读入堆内存,自动以 malloc 分配缓冲区。out_len 返回文件字节数。调用方需用 free() 释放返回的缓冲区。失败返回 NULL

常用于需要将整个文件加载到内存的场景,如图片文件加载、glTF 二进制数据处理等。

int len;
unsigned char *data = read_bin_file_alloc("image.bmp", &len);
if (data) {
    // 使用 data...
    free(data);
}

数据库

eduwin 已内置 SQLite 嵌入式数据库引擎,无需任何额外配置:

#include "eduwin.h"
#include "sqlite3.h"

sqlite3 *db;
sqlite3_open("test.db", &db);
sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS ...", NULL, NULL, NULL);
sqlite3_close(db);

详见 eduwin-sqlite.mdsamples\sqlite_demo\sqlite_demo.c 是一个完整的用户信息增删改查演示。


进程管理

HANDLE run_process(const WCHAR *exe_path, const WCHAR *cmdline, int show);
int    kill_process(HANDLE proc);
int    kill_process_pid(DWORD pid);
int    wait_process(HANDLE proc);

run_process 创建新进程。exe_path 为可执行文件路径,cmdline 为命令行参数(可为 NULL),show=1 显示窗口,show=0 隐藏窗口。成功返回进程句柄,失败返回 NULL

kill_process 强制终止进程并关闭句柄,成功返回 0。

kill_process_pid 按进程 PID 终止进程(内部调用 OpenProcess + kill_process),成功返回 0。

wait_process 等待进程结束,返回进程退出码(内部会自动关闭句柄)。

// 启动记事本,等待退出
HANDLE h = run_process(L"C:\\Windows\\System32\\notepad.exe", NULL, 1);
if (h) {
    int code = wait_process(h);
    log_info(L"记事本已退出");
}

// 后台启动 cmd
HANDLE h2 = run_process(L"C:\\Windows\\System32\\cmd.exe", NULL, 1);
// 稍后终止
kill_process(h2);

// 按 PID 终止进程
kill_process_pid(1234);

进程枚举

typedef struct {
    DWORD pid;
    WCHAR name[260];
} process_entry_t;

int enum_processes(process_entry_t **out, int *count);

enum_processes 枚举所有系统进程,动态分配数组并通过 out 返回。count 返回进程数量。成功返回 0。调用方需 free() 释放返回的数组。

process_entry_t *procs = NULL;
int count = 0;
if (enum_processes(&procs, &count) == 0) {
    for (int i = 0; i < count; i++)
        wprintf(L"%u  %s\n", procs[i].pid, procs[i].name);
    free(procs);
}

系统信息

typedef struct {
    WCHAR computer_name[64];
    WCHAR user_name[64];
    WCHAR os_version[64];
    int   cpu_cores;
    WCHAR cpu_name[128];
    unsigned long long mem_total_mb;
    unsigned long long mem_avail_mb;
    WCHAR windows_dir[MAX_PATH];
} sys_info_t;

int get_sys_info(sys_info_t *info);

get_sys_info 填充系统信息到 sys_info_t 结构体。成功返回 0。

sys_info_t info;
get_sys_info(&info);
// info.computer_name → 计算机名
// info.os_version → "Windows 10 Pro (Build 19045, 22H2)"
// info.cpu_cores → 8
// info.cpu_name → "Intel(R) Core(TM) i7-10700"
// info.mem_total_mb → 16384

注册表读写

int reg_read_str(HKEY root, const WCHAR *key, const WCHAR *value, WCHAR *buf, int size);
int reg_read_dword(HKEY root, const WCHAR *key, const WCHAR *value, DWORD *out);
int reg_write_str(HKEY root, const WCHAR *key, const WCHAR *value, const WCHAR *data);
int reg_write_dword(HKEY root, const WCHAR *key, const WCHAR *value, DWORD data);
int reg_delete_value(HKEY root, const WCHAR *key, const WCHAR *value);

注册表读写操作。root 为根键(如 HKEY_LOCAL_MACHINE)。key 为键路径。成功返回 0。写操作会自动创建不存在的键。

WCHAR cpu_name[128];
reg_read_str(HKEY_LOCAL_MACHINE,
    L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
    L"ProcessorNameString", cpu_name, 128);

DWORD value = 1;
reg_write_dword(HKEY_CURRENT_USER,
    L"Software\\MyApp", L"Setting", value);

环境变量

int get_env(const WCHAR *name, WCHAR *buf, int size);
int set_env(const WCHAR *name, const WCHAR *value);

get_env 读取环境变量,成功返回 0。set_env 设置环境变量(仅影响当前进程),成功返回 0。

WCHAR path_val[4096];
get_env(L"PATH", path_val, 4096);

set_env(L"MY_VAR", L"hello");

特殊文件夹

#define FOLDER_DESKTOP      0
#define FOLDER_DOCUMENTS    1
#define FOLDER_DOWNLOADS    2
#define FOLDER_PICTURES     3
#define FOLDER_MUSIC        4
#define FOLDER_VIDEOS       5
#define FOLDER_APPDATA      6
#define FOLDER_LOCALAPPDATA 7
#define FOLDER_PROGRAMFILES 8
#define FOLDER_STARTUP      9

int get_special_folder(int folder_id, WCHAR *buf, int size);

get_special_folder 获取系统特殊文件夹路径。成功返回 0。

WCHAR path[MAX_PATH];
get_special_folder(FOLDER_DOWNLOADS, path, MAX_PATH);
// path → "C:\Users\xxx\Downloads"

日志

void log_info(const WCHAR *text);
void log_warning(const WCHAR *text);
void log_error(const WCHAR *title);
void log_clear(void);

将文本写入 exe 所在目录下的 log.txt 文件,自动在行首添加日期时间戳(精确到毫秒)和标签([INFO] / [WARN] / [ERROR])。日志以追加方式写入。

log_error 除了写入时间戳和 [ERROR] 标签外,还会自动追加 GetLastError() 的错误码和对应的系统错误描述。

log_clear 清空日志文件。

log_info(L"程序启动完成");
log_warning(L"纹理加载失败,使用默认纹理");
log_error(L"CreateDevice");  // 自动追加 err=87 - 参数错误
log_clear();

输出示例:

2026-06-23 14:30:01.123 [INFO] 程序启动完成
2026-06-23 14:30:02.456 [WARN] 纹理加载失败,使用默认纹理
2026-06-23 14:30:03.789 [ERROR] CreateDevice (err=87 - 参数错误)