# eduwin 工具 API

## 数字与字符串工具

```c
int rand_int(int min, int max);
```

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

```c
const WCHAR *itow(int value);
```

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

```c
const WCHAR *ftow(double value);
```

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

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

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

```c
WCHAR buf[64];
str_concat(buf, 64, L"你点击了 ", itow(count), L" 次", NULL);
set_text(label, buf);
```

```c
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`）。

```c
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} */
```

---

## 剪贴板

```c
void clipboard_copy(const WCHAR *text);
```

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

---

## Base64 解码

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

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

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

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

---

## 文件读写

### 文本文件读写

```c
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`。

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

### 二进制文件读写

```c
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`。

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

```c
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` 字节。

### 二进制文件读取（动态分配）

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

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

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

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

---

## 数据库

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

```c
#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.md](../tutorial/eduwin-sqlite.md)。`samples\sqlite_demo\sqlite_demo.c` 是一个完整的用户信息增删改查演示。

---

## 进程管理

```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` 等待进程结束，返回进程退出码（内部会自动关闭句柄）。

```c
// 启动记事本，等待退出
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);
```

## 进程枚举

```c
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()` 释放返回的数组。

```c
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);
}
```

## 系统信息

```c
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。

```c
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
```

## 注册表读写

```c
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。写操作会自动创建不存在的键。

```c
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);
```

## 环境变量

```c
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。

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

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

## 特殊文件夹

```c
#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。

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

## 日志

```c
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` 清空日志文件。

```c
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 - 参数错误)
```
