# eduwin 快速入门

eduwin 是一个基于 Win32 API 封装的简易 GUI 框架，湘潭大学 自动化与电子信息工程 宋小波 开发（sxb@xtu.edu.cn），专为 C 语言实训设计。

---

## 如何开始

三分钟跑通你的第一个 GUI 程序。

**第一步**：打开 `samples\hello_demo\hello.c`，这就是全部代码：

```c
#include "eduwin.h"

static HWND text;   /* 文本框 */
static HWND label;  /* 标签 */

/* 点击按钮时，把文本框内容显示到标签上 */
static void on_click(void *data)
{
    const WCHAR *s = get_text(text);
    set_text(label, s);
    set_text(text, L"");
}

int main(void)
{
    init_app();
    HWND win = create_window(L"Hello eduwin", 400, 200);

    text  = create_text(win, L"请输入文字", 20, 20, 250, 24);
    HWND btn = create_button(win, L"显示", 280, 20, 80, 24);
    label = create_label(win, L"", 20, 60, 340, 80);

    on_button_click(btn, on_click, NULL);

    show_window(win);
    run_app();
    return 0;
}
```

不到 30 行。一个文本框、一个按钮、一个标签，点击按钮后标签显示文本框内容。

**第二步**：打开终端（PowerShell 或命令提示符），进入目录，运行编译：

```
cd samples\hello_demo
build.bat
```

**第三步**：目录下新生成了 `hello.exe`，双击运行即可。

> 如果 `build.bat` 报错"Build Tools not found"，说明你的 VS 安装路径和作者不一样。用记事本打开 `build.bat`，把第 5 行的路径改成你的 `vcvars64.bat` 位置即可（详见下方编译章节）。

---

## 为什么需要 eduwin

不靠任何第三方库，纯 C 语言 + Windows 原生 API 就能写出带图形界面的程序。然而原生的 Win32 编程体验是这样的：

- 注册窗口类、写 WndProc、在巨大的 switch-case 里处理几十种消息
- 手动创建字体、处理 DPI、初始化通用控件库
- 按钮、文本框、下拉框全部裸写 CreateWindow，传一堆看不懂的样式常量
- 打开文件对话框要填一个十几行的 OPENFILENAMEW 结构体
- 想显示一张 PNG 图片？先学 GDI+ 或者找第三方库
- 想在程序里存数据？要么自己手写文件解析，要么折腾 ADO/ODBC 连接数据库，光连接字符串就能写一页

**以上这些，eduwin 全部封装掉了，甚至还多走了一步。**

eduwin 的 **画布组件** 不只是用来画图的它内置了**帧循环定时器、键盘实时检测、鼠标输入、碰撞检测、精灵系统**，完全可以支撑 2D 小游戏的开发。`samples\shooting_demo\shooting_demo.c` 是一个完整的打靶游戏示例，编译运行就能玩。

更重磅的是，eduwin 已将 **SQLite 嵌入式数据库引擎**直接编译进框架 `#include "sqlite3.h"` 即可使用，不需要额外的 `.dll`，不需要配置数据库服务器，一行 `sqlite3_open` 就能创建一个完整的数据库文件。`demo\sqlite_demo.c` 是一个完整的用户信息增删改查演示。这在纯 C + Win32 原生开发中是极其罕见的你既不需要 C++，不需要 .NET，也不需要 Python，一个记事本 + 一个编译器就能写出带图形界面和数据库的完整应用。

eduwin 让你只需要关心三件事：

1. **创建控件** `create_button(win, L"点击", x, y, w, h)`，就这么简单
2. **处理事件** `on_button_click(btn, callback, data)`，回调函数搞定
3. **组织业务逻辑**其他琐事交给框架

目前 eduwin 已提供 **40+ 个 API**，覆盖从基础控件到高级功能的完整链条：

| 类别   | 控件                                                                           |     |
| ------ | ------------------------------------------------------------------------------ | --- |
| 基础   | 按钮、标签、文本框、多行文本、密码框、复选框、单选按钮、分组框                 |     |
| 列表   | 下拉框、列表框、树状列表、详细列表                                             |     |
| 数值   | 进度条、滑块、滚动条、数值调节、IP 地址输入                                    |     |
| 综合   | 标签页、日期选择、状态栏、工具栏、富文本编辑、动画                             |     |
| 图像   | 图片显示（bmp/png/jpg）、图标、画布（GDI+ 绘图，支持直线/矩形/椭圆/文本/保存） |     |
| 游戏   | 帧循环定时器、键盘实时检测、鼠标输入、碰撞检测、随机数、局部清除               |     |
| 菜单   | 主菜单（含子菜单、勾选、禁用）、系统菜单、上下文菜单、快捷键（加速键）         |     |
| 文件   | 打开/保存文件对话框、浏览目录对话框、文本文件读写、二进制文件读写              |     |
| 数据库 | SQLite 嵌入式数据库，已编译进框架，`#include "sqlite3.h"` 开箱即用             |     |
| 音效   | WAV 播放、蜂鸣器                                                               |     |
| 工具   | 整数/小数转字符串、字符串拼接、加载图像、只读开关                              |     |

而这一切，都只需要 `#include "eduwin.h"` 再加一行 `link` 命令即可。

---

## 核心概念

- **所有控件都是 `HWND` 句柄**，即 Windows 原生的窗口句柄
- **通过回调函数处理事件**，不需要写消息循环
- **绝对坐标定位**（x, y, w, h），简单直观
- **窗口坐标体系**：理解控件的坐标原点在哪非常重要

### 窗口坐标体系

一个窗口由两层区域组成：

```
整体窗口区域
├── 非客户区 (Non-client Area)     ← 系统绘制，你无法放控件
│   ├── 标题栏
│   ├── 菜单栏                      ← SetMenu 挂到这里
│   └── 边框
│
└── 客户区 (Client Area)            ← create_window 的 width × height 指的是这里
    ├── 你创建的控件（按钮、画布...） ← 坐标相对于客户区左上角 (0,0)
    └── 状态栏
```

关键结论：

- **菜单栏不在客户区里**，它位于非客户区，因此画布写 `(10, 10)` 不会也不可能遮挡菜单栏
- **客户区的 `(0, 0)` 在菜单栏正下方**，而不是窗口的物理左上角
- `GetClientRect` 拿到的是客户区尺寸，不含标题栏、菜单栏、边框
- 控件创建时的 x、y 坐标**全部相对于客户区左上角**

---

## 目录结构

```
你的项目目录/
├── samples\              # 所有示例
│   ├── hello_demo\       # 最简入门（文本框 + 按钮）
│   │   ├── hello.c / app.ico / app.rc / build.bat
│   ├── menu_demo\        # 菜单功能演示
│   │   ├── menu_demo.c / app.ico / app.rc / build.bat
│   ├── stack\            # 栈动画演示
│   │   ├── stack_animation.c / app.ico / app.rc / build.bat
│   ├── fighter_demo\     # 飞机射击游戏
│   │   ├── fighter_demo.c / flight.png / bullet.png / bgm.mp3
│   ├── tree_demo\        # 树形列表演示
│   │   ├── tree_demo.c / app.ico / app.rc / build.bat
│   ├── progress_demo\    # 进度条滑块演示
│   │   ├── progress_demo.c / app.ico / app.rc / build.bat
│   ├── canvas_demo\      # 图形绘制演示
│   │   ├── canvas_demo.c / app.ico / app.rc / build.bat
│   ├── shooting_demo\        # 打靶游戏演示
│   │   ├── shooting_demo.c / app.ico / app.rc / build.bat
│   └── sqlite_demo\      # SQLite 增删改查演示
│       ├── sqlite_demo.c / app.ico / app.rc / build.bat
│
└── eduwin\               # eduwin 库（分发时拷贝这个目录）
    ├── eduwin.c
    ├── eduwin.manifest    # 清单文件
    ├── build_lib.bat     # 重新编译库（一般不需要用）
    ├── include\
    │   └── eduwin.h      # 头文件
    └── lib\
        └── eduwin.lib    # 编译好的静态库
```

eduwin 以静态库形式提供，学生只需要引用 `include\eduwin.h` 和链接 `lib\eduwin.lib`。

---

## 编码说明

eduwin 的所有字符串参数都使用**宽字符串**（`WCHAR*`，即 `wchar_t*`），字符串字面量需要加 `L""` 前缀：

```c
create_window(L"标题", 320, 200);           // ✓ 正确
create_window("标题", 320, 200);            // ✗ 错误（编译不过）
```

为什么不是普通字符串？

Windows 底层有两种 API：

- **A 系列**（如 `CreateWindowExA`）：接收 ANSI 字符串，用系统代码页解码。中国区 Windows 默认代码页是 GBK，如果你传的是 UTF-8 字节，会产生乱码。
- **W 系列**（如 `CreateWindowExW`）：接收 UTF-16 宽字符串，不依赖系统代码页，不乱码。

eduwin 全部使用 W 系列 API。因此字符串必须传递宽字符串。

什么是宽字符？

**宽字符（wide character）** 就是 **UTF-16**。在 Windows 上，每个宽字符固定占 2 个字节（有些特殊字符占 4 个字节）。C 语言中用 `wchar_t` 类型存放，字符串字面量前加 `L""` 前缀。

Windows 内核底层全部用 UTF-16 处理文本，所以 W 系列 API 直接接收宽字符串。这是 Windows 自己的规则，和 UTF-8 没有关系——eduwin 内部用 W 系列 API，所以必须传宽字符串。

---

## 编译

确保已安装 Visual Studio Build Tools。

> **关于 `build.bat`：**
>
> 每个 build.bat 只有 30 多行，结构完全一致，你只需要关注两个地方：
>
> **1. VCVARS 路径（第 4-5 行）**
>
> ```bat
> setlocal EnableDelayedExpansion
> set "VCVARS=C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
> ```
>
> 这是作者机器的 VS 安装位置。如果你的 VS 装在其他地方，修改 `set "VCVARS=..."` 这一行即可。
>
> 如果不知道自己的 VS 装在哪里，到资源管理器里搜索 `vcvars64.bat`，找到后把完整路径抄过来。
>
> **注意：** build.bat 文件内容中不要出现任何中文（包括注释），否则可能因编码问题导致脚本解析失败。
>
> **2. SOURCE 与 PROGRAM（第 15-16 行）**
>
> ```bat
> set SOURCE=hello.c
> set PROGRAM=hello.exe
> ```
>
> `SOURCE` 是你的 C 源文件名，`PROGRAM` 是生成的 exe 名。如果你想编译自己的程序，只改这两行即可，其他不用动。

### 更换图标

每个示例目录下都有 `app.ico`。你需要：

1. 准备一个 `.ico` 格式的图标文件
2. 替换对应目录的 `app.ico`
3. 重新编译

### app.rc 与清单文件（manifest）

每个示例目录自带 `app.rc`，内容只有两行：

```
1 ICON "app.ico"
1 24 "..\\..\\eduwin\\eduwin.manifest"
```

- 第 1 行：将 `app.ico` 编译为程序图标资源
- 第 2 行：将 `eduwin.manifest` 嵌入 exe，用来启用视觉样式和 DPI 感知

**如果你是拷贝示例目录到别处使用**，需要注意 `app.rc` 中 manifest 的路径相对于你的项目位置是否仍然正确。如果示例目录与 `eduwin` 目录的相对层级变了，你需要修改 `app.rc` 第 2 行中 `..\\..\\eduwin\\eduwin.manifest` 的路径。

同时 `init_app()` 中还会调用 `SetProcessDPIAware()` 做双重 DPI 保证。

### Hello World

`samples\hello_demo\` 是最简示例（一个文本框 + 一个按钮），适合作为学习起点：

```
cd samples\hello_demo
build.bat
```

## VS Code 断点调试

### 前提条件

1. 安装 **VS Code** + **C/C++ 扩展**（发布者 Microsoft，扩展面板搜索 `C/C++`）
2. Visual Studio Build Tools 已装调试器组件

`.vscode\` 目录下已提供 `launch.json` 和 `tasks.json`（`pub.bat` 打包时已包含），解压即可用。

### 使用步骤

1. 在 VS Code 中打开项目根目录
2. 在左侧文件浏览器中**点选你要调试的 `.c` 文件**（如 `samples\menu_demo\menu_demo.c`）——这一步很重要，调试器会根据当前选中的文件来确定编译哪个程序
3. 按 `F5`（或点左侧 Run and Debug → 绿色播放按钮），VS Code 会自动调用 `build_debug.bat` 编译，然后启动调试

### build_debug.bat 怎么改

如果你要给其他示例加调试，复制 `samples\menu_demo\build_debug.bat` 到目标目录，只改两行：

```bat
set SOURCE=你的源文件.c
set PROGRAM=你的程序_debug.exe
```

- `SOURCE` 改为你要调试的源文件名
- `PROGRAM` 随意，加 `_debug` 后缀

其他不用动（`/Zi /Od /DEBUG` 都是调试必需的编译选项）。
