[{"data":1,"prerenderedAt":22365},["ShallowReactive",2],{"versions":3},[4,380,654,1854,2153,3793,5029,5394,5572,5990,6179,6765,7668,7772,8241,9569,10649,11731,11841,11909,12139,12292,12354,12413,12476,12544,12600,12748,12937,12999,13114,13181,13243,13305,13367,13504,13566,13775,13877,14171,15227,16794,17117,18453,19356,20024,20122,20488,20533,20570,20610,20652,20708,20760,20818,21367,22112],{"id":5,"title":6,"body":7,"description":55,"extension":374,"meta":375,"navigation":173,"path":376,"seo":377,"stem":378,"__hash__":379},"content\u002Fdocs\u002Fapi\u002Feduwin-ai-api.md","eduwin AI 大模型 API",{"type":8,"value":9,"toc":365},"minimark",[10,14,30,45,49,65,76,92,95,110,117,150,156,159,180,187,216,219,239,250,296,299,305,308,314,344,347,353,361],[11,12,6],"h1",{"id":13},"eduwin-ai-大模型-api",[15,16,17],"blockquote",{},[18,19,20,21,25,26,29],"p",{},"头文件：",[22,23,24],"code",{},"eduwin_ai.h","（或通过 ",[22,27,28],{},"eduwin.h"," 统一引用）",[18,31,32,33,36,37,40,41,44],{},"基于 WinHTTP 调用大语言模型 API，提供同步、异步、窗口异步三种调用方式。依赖 ",[22,34,35],{},"eduwin_net"," 的 HTTP 能力。线程安全——异步接口内部使用 ",[22,38,39],{},"eduwin_thread"," 的 ",[22,42,43],{},"thread_run"," 管理线程。",[46,47,48],"h2",{"id":48},"初始化",[50,51,56],"pre",{"className":52,"code":53,"language":54,"meta":55,"style":55},"language-c shiki shiki-themes github-light github-dark","void ai_setup(const char *api_key, const char *model, const char *base_url);\n","c","",[22,57,58],{"__ignoreMap":55},[59,60,63],"span",{"class":61,"line":62},"line",1,[59,64,53],{},[18,66,67,68,75],{},"配置 API Key、模型名、Base URL。",[69,70,71,72],"strong",{},"必须先调 ",[22,73,74],{},"net_init()","。",[50,77,79],{"className":52,"code":78,"language":54,"meta":55,"style":55},"net_init();\nai_setup(\"sk-xxx\", \"deepseek-chat\", \"https:\u002F\u002Fapi.deepseek.com\u002Fv1\");\n",[22,80,81,86],{"__ignoreMap":55},[59,82,83],{"class":61,"line":62},[59,84,85],{},"net_init();\n",[59,87,89],{"class":61,"line":88},2,[59,90,91],{},"ai_setup(\"sk-xxx\", \"deepseek-chat\", \"https:\u002F\u002Fapi.deepseek.com\u002Fv1\");\n",[46,93,94],{"id":94},"同步调用",[50,96,98],{"className":52,"code":97,"language":54,"meta":55,"style":55},"char* ai_chat(const char *prompt);\nvoid  ai_free(char *ptr);\n",[22,99,100,105],{"__ignoreMap":55},[59,101,102],{"class":61,"line":62},[59,103,104],{},"char* ai_chat(const char *prompt);\n",[59,106,107],{"class":61,"line":88},[59,108,109],{},"void  ai_free(char *ptr);\n",[18,111,112,113,116],{},"发送 prompt，阻塞等待完整回复。返回的字符串需用 ",[22,114,115],{},"ai_free"," 释放。",[50,118,120],{"className":52,"code":119,"language":54,"meta":55,"style":55},"char *reply = ai_chat(\"用 C 语言实现冒泡排序\");\nif (reply) {\n    printf(\"%s\", reply);\n    ai_free(reply);\n}\n",[22,121,122,127,132,138,144],{"__ignoreMap":55},[59,123,124],{"class":61,"line":62},[59,125,126],{},"char *reply = ai_chat(\"用 C 语言实现冒泡排序\");\n",[59,128,129],{"class":61,"line":88},[59,130,131],{},"if (reply) {\n",[59,133,135],{"class":61,"line":134},3,[59,136,137],{},"    printf(\"%s\", reply);\n",[59,139,141],{"class":61,"line":140},4,[59,142,143],{},"    ai_free(reply);\n",[59,145,147],{"class":61,"line":146},5,[59,148,149],{},"}\n",[18,151,152,155],{},[69,153,154],{},"注意："," 同步调用会阻塞当前线程。GUI 程序请用异步或窗口异步接口。",[46,157,158],{"id":158},"异步调用",[50,160,162],{"className":52,"code":161,"language":54,"meta":55,"style":55},"typedef void (*ai_callback_t)(const char *reply, void *userdata);\n\nvoid ai_chat_async(const char *prompt, ai_callback_t callback, void *userdata);\n",[22,163,164,169,175],{"__ignoreMap":55},[59,165,166],{"class":61,"line":62},[59,167,168],{},"typedef void (*ai_callback_t)(const char *reply, void *userdata);\n",[59,170,171],{"class":61,"line":88},[59,172,174],{"emptyLinePlaceholder":173},true,"\n",[59,176,177],{"class":61,"line":134},[59,178,179],{},"void ai_chat_async(const char *prompt, ai_callback_t callback, void *userdata);\n",[18,181,182,183,186],{},"不阻塞调用线程，请求完成后通过回调返回结果。",[22,184,185],{},"userdata"," 在回调中原样传回。",[50,188,190],{"className":52,"code":189,"language":54,"meta":55,"style":55},"void on_reply(const char *reply, void *userdata) {\n    if (reply) set_text(g_label, L\"AI 回复已收到\");\n    ai_free((void*)reply);\n}\nai_chat_async(\"你好\", on_reply, NULL);\n",[22,191,192,197,202,207,211],{"__ignoreMap":55},[59,193,194],{"class":61,"line":62},[59,195,196],{},"void on_reply(const char *reply, void *userdata) {\n",[59,198,199],{"class":61,"line":88},[59,200,201],{},"    if (reply) set_text(g_label, L\"AI 回复已收到\");\n",[59,203,204],{"class":61,"line":134},[59,205,206],{},"    ai_free((void*)reply);\n",[59,208,209],{"class":61,"line":140},[59,210,149],{},[59,212,213],{"class":61,"line":146},[59,214,215],{},"ai_chat_async(\"你好\", on_reply, NULL);\n",[46,217,218],{"id":218},"窗口异步调用",[50,220,222],{"className":52,"code":221,"language":54,"meta":55,"style":55},"void ai_chat_async_to_hwnd(HWND hwnd, const char *prompt, void *userdata);\nint  ai_handle_async_msg(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam,\n                                void (*on_result)(const char *reply, void *userdata));\n",[22,223,224,229,234],{"__ignoreMap":55},[59,225,226],{"class":61,"line":62},[59,227,228],{},"void ai_chat_async_to_hwnd(HWND hwnd, const char *prompt, void *userdata);\n",[59,230,231],{"class":61,"line":88},[59,232,233],{},"int  ai_handle_async_msg(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam,\n",[59,235,236],{"class":61,"line":134},[59,237,238],{},"                                void (*on_result)(const char *reply, void *userdata));\n",[18,240,241,242,245,246,249],{},"请求完成后通过 ",[22,243,244],{},"PostMessage"," 通知指定窗口，在窗口过程中用 ",[22,247,248],{},"ai_handle_async_msg"," 处理。",[50,251,253],{"className":52,"code":252,"language":54,"meta":55,"style":55},"\u002F\u002F 窗口过程中\nLRESULT CALLBACK WndProc(HWND h, UINT m, WPARAM w, LPARAM l) {\n    if (ai_handle_async_msg(h, m, w, l, on_result))\n        return 0;\n}\n\n\u002F\u002F 任意位置发起请求\nai_chat_async_to_hwnd(g_win, \"写一首诗\", NULL);\n",[22,254,255,260,265,270,275,279,284,290],{"__ignoreMap":55},[59,256,257],{"class":61,"line":62},[59,258,259],{},"\u002F\u002F 窗口过程中\n",[59,261,262],{"class":61,"line":88},[59,263,264],{},"LRESULT CALLBACK WndProc(HWND h, UINT m, WPARAM w, LPARAM l) {\n",[59,266,267],{"class":61,"line":134},[59,268,269],{},"    if (ai_handle_async_msg(h, m, w, l, on_result))\n",[59,271,272],{"class":61,"line":140},[59,273,274],{},"        return 0;\n",[59,276,277],{"class":61,"line":146},[59,278,149],{},[59,280,282],{"class":61,"line":281},6,[59,283,174],{"emptyLinePlaceholder":173},[59,285,287],{"class":61,"line":286},7,[59,288,289],{},"\u002F\u002F 任意位置发起请求\n",[59,291,293],{"class":61,"line":292},8,[59,294,295],{},"ai_chat_async_to_hwnd(g_win, \"写一首诗\", NULL);\n",[46,297,298],{"id":298},"完整示例",[18,300,301,302,75],{},"参见 ",[22,303,304],{},"samples\u002Fai_chat_demo\u002F",[46,306,307],{"id":307},"模型兼容性",[18,309,310,313],{},[22,311,312],{},"eduwin_ai"," 使用 OpenAI 兼容 API 格式（Chat Completion），支持：",[315,316,317,325,331,337],"ul",{},[318,319,320,321,324],"li",{},"DeepSeek (",[22,322,323],{},"api.deepseek.com",")",[318,326,327,328,324],{},"智谱 GLM (",[22,329,330],{},"open.bigmodel.cn",[318,332,333,334,324],{},"阿里通义千问 (",[22,335,336],{},"dashscope.aliyuncs.com\u002Fcompatible-mode",[318,338,339,340,343],{},"任何提供 ",[22,341,342],{},"\u002Fv1\u002Fchat\u002Fcompletions"," 端点的服务",[46,345,346],{"id":346},"编译要求",[18,348,349,350,352],{},"与 ",[22,351,35],{}," 相同，需要在 link 命令中添加：",[50,354,359],{"className":355,"code":357,"language":358},[356],"language-text","ws2_32.lib winhttp.lib\n","text",[22,360,357],{"__ignoreMap":55},[362,363,364],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":55,"searchDepth":88,"depth":88,"links":366},[367,368,369,370,371,372,373],{"id":48,"depth":88,"text":48},{"id":94,"depth":88,"text":94},{"id":158,"depth":88,"text":158},{"id":218,"depth":88,"text":218},{"id":298,"depth":88,"text":298},{"id":307,"depth":88,"text":307},{"id":346,"depth":88,"text":346},"md",{},"\u002Fdocs\u002Fapi\u002Feduwin-ai-api",{"title":6,"description":55},"docs\u002Fapi\u002Feduwin-ai-api","6OxCEzfo-eu6G7UzhOjnbLgN7SjK8Rygv_CGbDIfAS8",{"id":381,"title":382,"body":383,"description":55,"extension":374,"meta":649,"navigation":173,"path":650,"seo":651,"stem":652,"__hash__":653},"content\u002Fdocs\u002Fapi\u002Feduwin-app-api.md","eduwin 应用框架 API",{"type":8,"value":384,"toc":644},[385,388,391,400,403,412,419,428,431,434,437,446,449,458,469,479,488,497,504,512,515,517,520,529,536,545,555,604,642],[11,386,382],{"id":387},"eduwin-应用框架-api",[46,389,390],{"id":390},"应用生命周期",[50,392,394],{"className":52,"code":393,"language":54,"meta":55,"style":55},"int init_app(void);\n",[22,395,396],{"__ignoreMap":55},[59,397,398],{"class":61,"line":62},[59,399,393],{},[18,401,402],{},"初始化 eduwin 框架。必须在任何其他 eduwin 函数之前调用，成功返回 0，失败返回 -1。",[50,404,406],{"className":52,"code":405,"language":54,"meta":55,"style":55},"void run_app(void);\n",[22,407,408],{"__ignoreMap":55},[59,409,410],{"class":61,"line":62},[59,411,405],{},[18,413,414,415,418],{},"启动消息循环（阻塞），直到 ",[22,416,417],{},"quit_app()"," 被调用才返回。创建第一个窗口后，该窗口关闭时自动退出消息循环。",[50,420,422],{"className":52,"code":421,"language":54,"meta":55,"style":55},"void quit_app(void);\n",[22,423,424],{"__ignoreMap":55},[59,425,426],{"class":61,"line":62},[59,427,421],{},[18,429,430],{},"退出消息循环。",[432,433],"hr",{},[46,435,436],{"id":436},"窗口",[50,438,440],{"className":52,"code":439,"language":54,"meta":55,"style":55},"HWND create_window(const WCHAR *title, int width, int height);\n",[22,441,442],{"__ignoreMap":55},[59,443,444],{"class":61,"line":62},[59,445,439],{},[18,447,448],{},"创建一个标准重叠窗口（带标题栏、边框、可缩放）。",[50,450,452],{"className":52,"code":451,"language":54,"meta":55,"style":55},"HWND create_hidden_window(int w, int h);\n",[22,453,454],{"__ignoreMap":55},[59,455,456],{"class":61,"line":62},[59,457,451],{},[18,459,460,461,464,465,468],{},"创建一个不显示的傀儡窗口（",[22,462,463],{},"WS_POPUP"," + ",[22,466,467],{},"STATIC"," 类），最轻量、无边框、全程不可见。",[18,470,471,474,475,478],{},[69,472,473],{},"用途："," 某些子系统（如 WebView2）强制要求传入有效的父窗口句柄，传 ",[22,476,477],{},"NULL"," 会崩溃。在 headless \u002F 后台服务模式下，用此函数创建一个不可见的\"挂架\"窗口。",[18,480,481,484,485,75],{},[69,482,483],{},"销毁："," 程序退出时自行 ",[22,486,487],{},"DestroyWindow",[50,489,491],{"className":52,"code":490,"language":54,"meta":55,"style":55},"void show_window(HWND win);\n",[22,492,493],{"__ignoreMap":55},[59,494,495],{"class":61,"line":62},[59,496,490],{},[18,498,499,500,503],{},"创建一个窗口，",[22,501,502],{},"title"," 是标题栏文本。返回窗口句柄。",[50,505,506],{"className":52,"code":490,"language":54,"meta":55,"style":55},[22,507,508],{"__ignoreMap":55},[59,509,510],{"class":61,"line":62},[59,511,490],{},[18,513,514],{},"显示窗口。必须在添加完所有控件和菜单后调用。",[432,516],{},[46,518,519],{"id":519},"事件",[50,521,523],{"className":52,"code":522,"language":54,"meta":55,"style":55},"void on_window_close(HWND win, int (*callback)(void *), void *data);\n",[22,524,525],{"__ignoreMap":55},[59,526,527],{"class":61,"line":62},[59,528,522],{},[18,530,531,532,535],{},"注册窗口关闭事件。",[22,533,534],{},"callback"," 返回 1 时关闭窗口，返回 0 则不关闭。",[50,537,539],{"className":52,"code":538,"language":54,"meta":55,"style":55},"void on_window_resize(HWND win, void (*callback)(int w, int h));\n",[22,540,541],{"__ignoreMap":55},[59,542,543],{"class":61,"line":62},[59,544,538],{},[18,546,547,548,550,551,554],{},"注册窗口大小变化事件。",[22,549,534],{}," 的参数是窗口",[69,552,553],{},"客户区","的宽和高（像素），不含标题栏和边框。",[50,556,558],{"className":52,"code":557,"language":54,"meta":55,"style":55},"\u002F\u002F 窗口缩放时在状态栏显示客户区尺寸\nvoid on_size(int w, int h)\n{\n    WCHAR buf[128];\n    str_format(buf, 128, L\"Client: %d x %d\", w, h);\n    statusbar_set_text(sb, buf);\n}\n\non_window_resize(win, on_size);\n",[22,559,560,565,570,575,580,585,590,594,598],{"__ignoreMap":55},[59,561,562],{"class":61,"line":62},[59,563,564],{},"\u002F\u002F 窗口缩放时在状态栏显示客户区尺寸\n",[59,566,567],{"class":61,"line":88},[59,568,569],{},"void on_size(int w, int h)\n",[59,571,572],{"class":61,"line":134},[59,573,574],{},"{\n",[59,576,577],{"class":61,"line":140},[59,578,579],{},"    WCHAR buf[128];\n",[59,581,582],{"class":61,"line":146},[59,583,584],{},"    str_format(buf, 128, L\"Client: %d x %d\", w, h);\n",[59,586,587],{"class":61,"line":281},[59,588,589],{},"    statusbar_set_text(sb, buf);\n",[59,591,592],{"class":61,"line":286},[59,593,149],{},[59,595,596],{"class":61,"line":292},[59,597,174],{"emptyLinePlaceholder":173},[59,599,601],{"class":61,"line":600},9,[59,602,603],{},"on_window_resize(win, on_size);\n",[15,605,606,611],{},[18,607,608],{},[69,609,610],{},"回调签名说明：",[18,612,613,616,617,620,621,624,625,628,629,616,632,635,636,639,640,75],{},[22,614,615],{},"on_window_resize"," 的回调是 ",[22,618,619],{},"void (*)(int w, int h)","，",[69,622,623],{},"不是"," ",[22,626,627],{},"void (*)(void *)","。\n",[22,630,631],{},"on_window_close",[22,633,634],{},"int (*)(void *)","。\n其他控件事件（如 ",[22,637,638],{},"on_button_click","）的回调是 ",[22,641,627],{},[362,643,364],{},{"title":55,"searchDepth":88,"depth":88,"links":645},[646,647,648],{"id":390,"depth":88,"text":390},{"id":436,"depth":88,"text":436},{"id":519,"depth":88,"text":519},{},"\u002Fdocs\u002Fapi\u002Feduwin-app-api",{"title":382,"description":55},"docs\u002Fapi\u002Feduwin-app-api","_uIorCvjMGLv7vTZHZGAijDnj7BSmaoz1M-yhtcbrtg",{"id":655,"title":656,"body":657,"description":55,"extension":374,"meta":1849,"navigation":173,"path":1850,"seo":1851,"stem":1852,"__hash__":1853},"content\u002Fdocs\u002Fapi\u002Feduwin-canvas-api.md","eduwin 画布与精灵 API",{"type":8,"value":658,"toc":1825},[659,662,665,674,677,700,702,705,730,747,773,775,778,818,868,943,945,948,957,963,988,990,993,1002,1005,1014,1016,1019,1023,1038,1055,1058,1073,1095,1098,1123,1129,1153,1166,1223,1226,1241,1244,1247,1352,1354,1357,1364,1367,1417,1420,1450,1501,1504,1539,1581,1584,1619,1622,1642,1664,1667,1692,1711,1714,1729,1739,1753,1757,1760,1790,1793,1823],[11,660,656],{"id":661},"eduwin-画布与精灵-api",[46,663,664],{"id":664},"画布",[50,666,668],{"className":52,"code":667,"language":54,"meta":55,"style":55},"HWND create_canvas(HWND parent, int x, int y, int w, int h);\n",[22,669,670],{"__ignoreMap":55},[59,671,672],{"class":61,"line":62},[59,673,667],{},[18,675,676],{},"创建一个可绘制的画布控件，使用 GDI+ 作为后端。画布维护一个离屏位图，所有绘图操作都在位图上进行，窗口重绘时自动刷新。",[15,678,679,688],{},[18,680,681],{},[69,682,683,684,687],{},"重要：所有绘图操作必须在 ",[22,685,686],{},"show_window"," 之后调用！",[18,689,690,692,693,696,697,699],{},[22,691,686],{}," 会触发 ",[22,694,695],{},"WM_SIZE"," 消息，画布会销毁当前离屏位图并按实际客户区尺寸重建。如果在 ",[22,698,686],{}," 之前绘图，内容会被清空。",[432,701],{},[46,703,704],{"id":704},"绘图状态设置",[50,706,708],{"className":52,"code":707,"language":54,"meta":55,"style":55},"void canvas_clear(HWND canvas, int r, int g, int b, int a);\nvoid canvas_set_pen(HWND canvas, int r, int g, int b, int a, int width);\nvoid canvas_set_brush(HWND canvas, int r, int g, int b, int a);\nvoid canvas_set_font(HWND canvas, const WCHAR *family, int size);\n",[22,709,710,715,720,725],{"__ignoreMap":55},[59,711,712],{"class":61,"line":62},[59,713,714],{},"void canvas_clear(HWND canvas, int r, int g, int b, int a);\n",[59,716,717],{"class":61,"line":88},[59,718,719],{},"void canvas_set_pen(HWND canvas, int r, int g, int b, int a, int width);\n",[59,721,722],{"class":61,"line":134},[59,723,724],{},"void canvas_set_brush(HWND canvas, int r, int g, int b, int a);\n",[59,726,727],{"class":61,"line":140},[59,728,729],{},"void canvas_set_font(HWND canvas, const WCHAR *family, int size);\n",[18,731,732,735,736,735,739,742,743,746],{},[22,733,734],{},"r"," \u002F ",[22,737,738],{},"g",[22,740,741],{},"b"," 为颜色的红、绿、蓝分量，范围 0–255。",[22,744,745],{},"a"," 为透明通道（alpha），0 完全透明，255 完全不透明。",[315,748,749,755,761,767],{},[318,750,751,754],{},[22,752,753],{},"canvas_clear"," 用指定颜色填充整个画布（清除原有内容）",[318,756,757,760],{},[22,758,759],{},"canvas_set_pen"," 设置轮廓画笔颜色、透明度和线宽（默认黑色 1px 不透明）",[318,762,763,766],{},[22,764,765],{},"canvas_set_brush"," 设置填充画刷颜色和透明度（默认白色不透明）",[318,768,769,772],{},[22,770,771],{},"canvas_set_font"," 设置文本字体和字号（默认 Microsoft YaHei 14）",[432,774],{},[46,776,777],{"id":777},"绘图操作",[50,779,781],{"className":52,"code":780,"language":54,"meta":55,"style":55},"void canvas_draw_line(HWND canvas, int x1, int y1, int x2, int y2);\nvoid canvas_draw_rect(HWND canvas, int x, int y, int w, int h);\nvoid canvas_fill_rect(HWND canvas, int x, int y, int w, int h);\nvoid canvas_draw_ellipse(HWND canvas, int x, int y, int w, int h);\nvoid canvas_fill_ellipse(HWND canvas, int x, int y, int w, int h);\nvoid canvas_draw_text(HWND canvas, const WCHAR *text, int x, int y, int r, int g, int b, int a, int size);\nvoid canvas_draw_image(HWND canvas, void *image, int x, int y, int w, int h);\n",[22,782,783,788,793,798,803,808,813],{"__ignoreMap":55},[59,784,785],{"class":61,"line":62},[59,786,787],{},"void canvas_draw_line(HWND canvas, int x1, int y1, int x2, int y2);\n",[59,789,790],{"class":61,"line":88},[59,791,792],{},"void canvas_draw_rect(HWND canvas, int x, int y, int w, int h);\n",[59,794,795],{"class":61,"line":134},[59,796,797],{},"void canvas_fill_rect(HWND canvas, int x, int y, int w, int h);\n",[59,799,800],{"class":61,"line":140},[59,801,802],{},"void canvas_draw_ellipse(HWND canvas, int x, int y, int w, int h);\n",[59,804,805],{"class":61,"line":146},[59,806,807],{},"void canvas_fill_ellipse(HWND canvas, int x, int y, int w, int h);\n",[59,809,810],{"class":61,"line":281},[59,811,812],{},"void canvas_draw_text(HWND canvas, const WCHAR *text, int x, int y, int r, int g, int b, int a, int size);\n",[59,814,815],{"class":61,"line":286},[59,816,817],{},"void canvas_draw_image(HWND canvas, void *image, int x, int y, int w, int h);\n",[315,819,820,832,841,858],{},[318,821,822,735,825,735,828,831],{},[22,823,824],{},"canvas_draw_line",[22,826,827],{},"canvas_draw_rect",[22,829,830],{},"canvas_draw_ellipse"," — 用当前画笔绘制轮廓",[318,833,834,735,837,840],{},[22,835,836],{},"canvas_fill_rect",[22,838,839],{},"canvas_fill_ellipse"," — 用当前画刷填充",[318,842,843,846,847,850,851,854,855,857],{},[22,844,845],{},"canvas_draw_text"," — 在指定位置绘制文本，",[22,848,849],{},"r g b a"," 为文字颜色和透明度，",[22,852,853],{},"size"," 为字号（传 0 使用 ",[22,856,771],{}," 设定的字号）",[318,859,860,863,864,867],{},[22,861,862],{},"canvas_draw_image"," — 绘制由 ",[22,865,866],{},"load_image_from_file"," 返回的图像",[50,869,871],{"className":52,"code":870,"language":54,"meta":55,"style":55},"HWND cv = create_canvas(win, 10, 10, 400, 300);\n\ncanvas_clear(cv, 255, 255, 255, 255);               \u002F\u002F 白色不透明背景\n\ncanvas_set_pen(cv, 0, 0, 255, 255, 3);              \u002F\u002F 蓝色 3px 不透明画笔\ncanvas_draw_rect(cv, 20, 20, 100, 80);              \u002F\u002F 矩形边框\n\ncanvas_set_brush(cv, 255, 200, 200, 255);            \u002F\u002F 粉色不透明画刷\ncanvas_fill_ellipse(cv, 140, 20, 100, 80);          \u002F\u002F 填充椭圆\n\ncanvas_draw_line(cv, 20, 120, 300, 120);             \u002F\u002F 直线\n\ncanvas_draw_text(cv, L\"Hello 画布\", 20, 140,         \u002F\u002F 文字\n                 0, 0, 0, 255, 20);\n",[22,872,873,878,882,887,891,896,901,905,910,915,920,926,931,937],{"__ignoreMap":55},[59,874,875],{"class":61,"line":62},[59,876,877],{},"HWND cv = create_canvas(win, 10, 10, 400, 300);\n",[59,879,880],{"class":61,"line":88},[59,881,174],{"emptyLinePlaceholder":173},[59,883,884],{"class":61,"line":134},[59,885,886],{},"canvas_clear(cv, 255, 255, 255, 255);               \u002F\u002F 白色不透明背景\n",[59,888,889],{"class":61,"line":140},[59,890,174],{"emptyLinePlaceholder":173},[59,892,893],{"class":61,"line":146},[59,894,895],{},"canvas_set_pen(cv, 0, 0, 255, 255, 3);              \u002F\u002F 蓝色 3px 不透明画笔\n",[59,897,898],{"class":61,"line":281},[59,899,900],{},"canvas_draw_rect(cv, 20, 20, 100, 80);              \u002F\u002F 矩形边框\n",[59,902,903],{"class":61,"line":286},[59,904,174],{"emptyLinePlaceholder":173},[59,906,907],{"class":61,"line":292},[59,908,909],{},"canvas_set_brush(cv, 255, 200, 200, 255);            \u002F\u002F 粉色不透明画刷\n",[59,911,912],{"class":61,"line":600},[59,913,914],{},"canvas_fill_ellipse(cv, 140, 20, 100, 80);          \u002F\u002F 填充椭圆\n",[59,916,918],{"class":61,"line":917},10,[59,919,174],{"emptyLinePlaceholder":173},[59,921,923],{"class":61,"line":922},11,[59,924,925],{},"canvas_draw_line(cv, 20, 120, 300, 120);             \u002F\u002F 直线\n",[59,927,929],{"class":61,"line":928},12,[59,930,174],{"emptyLinePlaceholder":173},[59,932,934],{"class":61,"line":933},13,[59,935,936],{},"canvas_draw_text(cv, L\"Hello 画布\", 20, 140,         \u002F\u002F 文字\n",[59,938,940],{"class":61,"line":939},14,[59,941,942],{},"                 0, 0, 0, 255, 20);\n",[432,944],{},[46,946,947],{"id":947},"局部清除",[50,949,951],{"className":52,"code":950,"language":54,"meta":55,"style":55},"void canvas_clear_rect(HWND canvas, int x, int y, int w, int h, int r, int g, int b, int a);\n",[22,952,953],{"__ignoreMap":55},[59,954,955],{"class":61,"line":62},[59,956,950],{},[18,958,959,960,962],{},"用颜色填充指定矩形区域（局部清除），",[22,961,745],{}," 为透明度。",[50,964,966],{"className":52,"code":965,"language":54,"meta":55,"style":55},"\u002F\u002F 在灰色区域中间挖一个透明洞\ncanvas_set_brush(canvas, 200, 200, 200, 255);\ncanvas_fill_rect(canvas, 10, 10, 200, 100);\ncanvas_clear_rect(canvas, 50, 30, 100, 40, 100, 200, 50, 255);\n",[22,967,968,973,978,983],{"__ignoreMap":55},[59,969,970],{"class":61,"line":62},[59,971,972],{},"\u002F\u002F 在灰色区域中间挖一个透明洞\n",[59,974,975],{"class":61,"line":88},[59,976,977],{},"canvas_set_brush(canvas, 200, 200, 200, 255);\n",[59,979,980],{"class":61,"line":134},[59,981,982],{},"canvas_fill_rect(canvas, 10, 10, 200, 100);\n",[59,984,985],{"class":61,"line":140},[59,986,987],{},"canvas_clear_rect(canvas, 50, 30, 100, 40, 100, 200, 50, 255);\n",[432,989],{},[46,991,992],{"id":992},"文件输出",[50,994,996],{"className":52,"code":995,"language":54,"meta":55,"style":55},"void canvas_save_image(HWND canvas, const WCHAR *filepath);\n",[22,997,998],{"__ignoreMap":55},[59,999,1000],{"class":61,"line":62},[59,1001,995],{},[18,1003,1004],{},"将画布的当前内容保存为图像文件（根据扩展名自动选择编码器，支持 png\u002Fjpg\u002Fbmp 等）。",[50,1006,1008],{"className":52,"code":1007,"language":54,"meta":55,"style":55},"canvas_save_image(cv, L\"output.png\");   \u002F\u002F 保存到文件\n",[22,1009,1010],{"__ignoreMap":55},[59,1011,1012],{"class":61,"line":62},[59,1013,1007],{},[432,1015],{},[46,1017,1018],{"id":1018},"游戏开发扩展",[1020,1021,1022],"h3",{"id":1022},"帧循环",[50,1024,1026],{"className":52,"code":1025,"language":54,"meta":55,"style":55},"void canvas_start_timer(HWND canvas, int fps, void (*callback)(void *), void *data);\nvoid canvas_stop_timer(HWND canvas);\n",[22,1027,1028,1033],{"__ignoreMap":55},[59,1029,1030],{"class":61,"line":62},[59,1031,1032],{},"void canvas_start_timer(HWND canvas, int fps, void (*callback)(void *), void *data);\n",[59,1034,1035],{"class":61,"line":88},[59,1036,1037],{},"void canvas_stop_timer(HWND canvas);\n",[18,1039,1040,1043,1044,1047,1048,1050,1051,1054],{},[22,1041,1042],{},"canvas_start_timer"," 启动帧循环，",[22,1045,1046],{},"fps"," 为每秒回调次数，",[22,1049,534],{}," 每帧调用一次。",[22,1052,1053],{},"canvas_stop_timer"," 停止帧循环。",[1020,1056,1057],{"id":1057},"键盘输入",[50,1059,1061],{"className":52,"code":1060,"language":54,"meta":55,"style":55},"int canvas_is_key_down(HWND canvas, int vkey);\nvoid canvas_on_key(HWND canvas, void (*down)(int), void (*up)(int));\n",[22,1062,1063,1068],{"__ignoreMap":55},[59,1064,1065],{"class":61,"line":62},[59,1066,1067],{},"int canvas_is_key_down(HWND canvas, int vkey);\n",[59,1069,1070],{"class":61,"line":88},[59,1071,1072],{},"void canvas_on_key(HWND canvas, void (*down)(int), void (*up)(int));\n",[18,1074,1075,1078,1079,1082,1083,1086,1087,1090,1091,1094],{},[22,1076,1077],{},"canvas_is_key_down"," 实时检测按键是否按住，",[22,1080,1081],{},"vkey"," 为虚拟键码（如 ",[22,1084,1085],{},"VK_LEFT","、",[22,1088,1089],{},"'A'","）。",[22,1092,1093],{},"canvas_on_key"," 注册按键按下 \u002F 释放事件回调。",[1020,1096,1097],{"id":1097},"鼠标输入",[50,1099,1101],{"className":52,"code":1100,"language":54,"meta":55,"style":55},"void canvas_on_mouse(HWND canvas, void (*move)(int, int), void (*down)(int, int, int), void (*up)(int, int, int));\nint canvas_get_mouse_x(HWND canvas);\nint canvas_get_mouse_y(HWND canvas);\nint canvas_is_mouse_down(HWND canvas, int btn);\n",[22,1102,1103,1108,1113,1118],{"__ignoreMap":55},[59,1104,1105],{"class":61,"line":62},[59,1106,1107],{},"void canvas_on_mouse(HWND canvas, void (*move)(int, int), void (*down)(int, int, int), void (*up)(int, int, int));\n",[59,1109,1110],{"class":61,"line":88},[59,1111,1112],{},"int canvas_get_mouse_x(HWND canvas);\n",[59,1114,1115],{"class":61,"line":134},[59,1116,1117],{},"int canvas_get_mouse_y(HWND canvas);\n",[59,1119,1120],{"class":61,"line":140},[59,1121,1122],{},"int canvas_is_mouse_down(HWND canvas, int btn);\n",[18,1124,1125,1128],{},[22,1126,1127],{},"canvas_on_mouse"," 注册鼠标移动 \u002F 按下 \u002F 释放回调。",[315,1130,1131,1137,1147],{},[318,1132,1133,1136],{},[22,1134,1135],{},"move(x, y)"," — 鼠标移动",[318,1138,1139,1142,1143,1146],{},[22,1140,1141],{},"down(btn, x, y)"," — 鼠标按下，",[22,1144,1145],{},"btn"," 0=左键, 1=中键, 2=右键",[318,1148,1149,1152],{},[22,1150,1151],{},"up(btn, x, y)"," — 鼠标释放",[18,1154,1155,1158,1159,1162,1163,1165],{},[22,1156,1157],{},"canvas_get_mouse_x\u002Fy"," 获取鼠标当前坐标。",[22,1160,1161],{},"canvas_is_mouse_down"," 检测鼠标是否按住（",[22,1164,1145],{}," 0=左, 1=右）。",[15,1167,1168,1190],{},[18,1169,1170,1173,1174,1177,1178,1181,1182,1185,1186,1189],{},[69,1171,1172],{},"坐标注意："," 画布鼠标回调中的 ",[22,1175,1176],{},"x, y"," 是",[69,1179,1180],{},"画布客户区坐标","。如果需要与 ",[22,1183,1184],{},"show_context_menu"," 等需要屏幕坐标的函数配合使用，必须用 ",[22,1187,1188],{},"ClientToScreen"," 转换：",[50,1191,1193],{"className":52,"code":1192,"language":54,"meta":55,"style":55},"void on_mouse_down(int btn, int x, int y)\n{\n    POINT pt = {x, y};\n    ClientToScreen(canvas, &pt);\n    show_context_menu(ctx_menu, canvas, pt.x, pt.y);\n}\n",[22,1194,1195,1200,1204,1209,1214,1219],{"__ignoreMap":55},[59,1196,1197],{"class":61,"line":62},[59,1198,1199],{},"void on_mouse_down(int btn, int x, int y)\n",[59,1201,1202],{"class":61,"line":88},[59,1203,574],{},[59,1205,1206],{"class":61,"line":134},[59,1207,1208],{},"    POINT pt = {x, y};\n",[59,1210,1211],{"class":61,"line":140},[59,1212,1213],{},"    ClientToScreen(canvas, &pt);\n",[59,1215,1216],{"class":61,"line":146},[59,1217,1218],{},"    show_context_menu(ctx_menu, canvas, pt.x, pt.y);\n",[59,1220,1221],{"class":61,"line":281},[59,1222,149],{},[1020,1224,1225],{"id":1225},"画布尺寸",[50,1227,1229],{"className":52,"code":1228,"language":54,"meta":55,"style":55},"int canvas_get_width(HWND canvas);\nint canvas_get_height(HWND canvas);\n",[22,1230,1231,1236],{"__ignoreMap":55},[59,1232,1233],{"class":61,"line":62},[59,1234,1235],{},"int canvas_get_width(HWND canvas);\n",[59,1237,1238],{"class":61,"line":88},[59,1239,1240],{},"int canvas_get_height(HWND canvas);\n",[18,1242,1243],{},"获取画布的实际像素尺寸。",[1020,1245,1246],{"id":1246},"游戏循环示例",[50,1248,1250],{"className":52,"code":1249,"language":54,"meta":55,"style":55},"void game_tick(void *data)\n{\n    canvas_clear(canvas, 0, 0, 0, 255);\n\n    \u002F\u002F WASD 控制方块\n    static float px = 100, py = 100;\n    if (canvas_is_key_down(canvas, 'W')) py -= 3;\n    if (canvas_is_key_down(canvas, 'S')) py += 3;\n    if (canvas_is_key_down(canvas, 'A')) px -= 3;\n    if (canvas_is_key_down(canvas, 'D')) px += 3;\n\n    canvas_set_brush(canvas, 0, 200, 0, 255);\n    canvas_fill_rect(canvas, (int)px, (int)py, 40, 40);\n\n    canvas_draw_text(canvas, L\"按 ESC 退出\", 5, 5, 128, 128, 128, 255, 14);\n}\n\n\u002F\u002F 在 main 中：\ncanvas_start_timer(canvas, 60, game_tick, NULL);\ncanvas_on_key(canvas, NULL, NULL);  \u002F\u002F 确保画布获得焦点\n",[22,1251,1252,1257,1261,1266,1270,1275,1280,1285,1290,1295,1300,1304,1309,1314,1318,1324,1329,1334,1340,1346],{"__ignoreMap":55},[59,1253,1254],{"class":61,"line":62},[59,1255,1256],{},"void game_tick(void *data)\n",[59,1258,1259],{"class":61,"line":88},[59,1260,574],{},[59,1262,1263],{"class":61,"line":134},[59,1264,1265],{},"    canvas_clear(canvas, 0, 0, 0, 255);\n",[59,1267,1268],{"class":61,"line":140},[59,1269,174],{"emptyLinePlaceholder":173},[59,1271,1272],{"class":61,"line":146},[59,1273,1274],{},"    \u002F\u002F WASD 控制方块\n",[59,1276,1277],{"class":61,"line":281},[59,1278,1279],{},"    static float px = 100, py = 100;\n",[59,1281,1282],{"class":61,"line":286},[59,1283,1284],{},"    if (canvas_is_key_down(canvas, 'W')) py -= 3;\n",[59,1286,1287],{"class":61,"line":292},[59,1288,1289],{},"    if (canvas_is_key_down(canvas, 'S')) py += 3;\n",[59,1291,1292],{"class":61,"line":600},[59,1293,1294],{},"    if (canvas_is_key_down(canvas, 'A')) px -= 3;\n",[59,1296,1297],{"class":61,"line":917},[59,1298,1299],{},"    if (canvas_is_key_down(canvas, 'D')) px += 3;\n",[59,1301,1302],{"class":61,"line":922},[59,1303,174],{"emptyLinePlaceholder":173},[59,1305,1306],{"class":61,"line":928},[59,1307,1308],{},"    canvas_set_brush(canvas, 0, 200, 0, 255);\n",[59,1310,1311],{"class":61,"line":933},[59,1312,1313],{},"    canvas_fill_rect(canvas, (int)px, (int)py, 40, 40);\n",[59,1315,1316],{"class":61,"line":939},[59,1317,174],{"emptyLinePlaceholder":173},[59,1319,1321],{"class":61,"line":1320},15,[59,1322,1323],{},"    canvas_draw_text(canvas, L\"按 ESC 退出\", 5, 5, 128, 128, 128, 255, 14);\n",[59,1325,1327],{"class":61,"line":1326},16,[59,1328,149],{},[59,1330,1332],{"class":61,"line":1331},17,[59,1333,174],{"emptyLinePlaceholder":173},[59,1335,1337],{"class":61,"line":1336},18,[59,1338,1339],{},"\u002F\u002F 在 main 中：\n",[59,1341,1343],{"class":61,"line":1342},19,[59,1344,1345],{},"canvas_start_timer(canvas, 60, game_tick, NULL);\n",[59,1347,1349],{"class":61,"line":1348},20,[59,1350,1351],{},"canvas_on_key(canvas, NULL, NULL);  \u002F\u002F 确保画布获得焦点\n",[432,1353],{},[46,1355,1356],{"id":1356},"精灵系统",[18,1358,1359,1360,1363],{},"精灵（Sprite）是画布上可独立控制的图形对象。所有精灵内部都统一为位图（",[22,1361,1362],{},"GpBitmap","）处理——形状创建时自动渲染为位图，图片从文件加载后也是位图。后续所有操作（颜色叠加、变换等）都直接作用于位图数据。",[1020,1365,1366],{"id":1366},"形状类型常量",[50,1368,1370],{"className":52,"code":1369,"language":54,"meta":55,"style":55},"#define SHAPE_RECT      0   \u002F* 矩形 *\u002F\n#define SHAPE_ELLIPSE   1   \u002F* 椭圆\u002F圆 *\u002F\n#define SHAPE_TRIANGLE  2   \u002F* 三角形（朝上） *\u002F\n#define SHAPE_DIAMOND   3   \u002F* 菱形 *\u002F\n#define SHAPE_PENTAGON  4   \u002F* 正五边形 *\u002F\n#define SHAPE_HEXAGON   5   \u002F* 正六边形 *\u002F\n#define SHAPE_STAR      6   \u002F* 五角星 *\u002F\n#define SHAPE_ARROW     7   \u002F* 箭头（朝右） *\u002F\n#define SHAPE_PIE       8   \u002F* 扇形（缺右上角） *\u002F\n",[22,1371,1372,1377,1382,1387,1392,1397,1402,1407,1412],{"__ignoreMap":55},[59,1373,1374],{"class":61,"line":62},[59,1375,1376],{},"#define SHAPE_RECT      0   \u002F* 矩形 *\u002F\n",[59,1378,1379],{"class":61,"line":88},[59,1380,1381],{},"#define SHAPE_ELLIPSE   1   \u002F* 椭圆\u002F圆 *\u002F\n",[59,1383,1384],{"class":61,"line":134},[59,1385,1386],{},"#define SHAPE_TRIANGLE  2   \u002F* 三角形（朝上） *\u002F\n",[59,1388,1389],{"class":61,"line":140},[59,1390,1391],{},"#define SHAPE_DIAMOND   3   \u002F* 菱形 *\u002F\n",[59,1393,1394],{"class":61,"line":146},[59,1395,1396],{},"#define SHAPE_PENTAGON  4   \u002F* 正五边形 *\u002F\n",[59,1398,1399],{"class":61,"line":281},[59,1400,1401],{},"#define SHAPE_HEXAGON   5   \u002F* 正六边形 *\u002F\n",[59,1403,1404],{"class":61,"line":286},[59,1405,1406],{},"#define SHAPE_STAR      6   \u002F* 五角星 *\u002F\n",[59,1408,1409],{"class":61,"line":292},[59,1410,1411],{},"#define SHAPE_ARROW     7   \u002F* 箭头（朝右） *\u002F\n",[59,1413,1414],{"class":61,"line":600},[59,1415,1416],{},"#define SHAPE_PIE       8   \u002F* 扇形（缺右上角） *\u002F\n",[1020,1418,1419],{"id":1419},"精灵创建与销毁",[50,1421,1423],{"className":52,"code":1422,"language":54,"meta":55,"style":55},"Sprite *sprite_create_from_shape(int type, int w, int h);\nSprite *sprite_create_from_file(const WCHAR *filepath);\nSprite *sprite_create_from_image(void *image);\nSprite *sprite_clone(Sprite *s);\nvoid sprite_destroy(Sprite *s);\n",[22,1424,1425,1430,1435,1440,1445],{"__ignoreMap":55},[59,1426,1427],{"class":61,"line":62},[59,1428,1429],{},"Sprite *sprite_create_from_shape(int type, int w, int h);\n",[59,1431,1432],{"class":61,"line":88},[59,1433,1434],{},"Sprite *sprite_create_from_file(const WCHAR *filepath);\n",[59,1436,1437],{"class":61,"line":134},[59,1438,1439],{},"Sprite *sprite_create_from_image(void *image);\n",[59,1441,1442],{"class":61,"line":140},[59,1443,1444],{},"Sprite *sprite_clone(Sprite *s);\n",[59,1446,1447],{"class":61,"line":146},[59,1448,1449],{},"void sprite_destroy(Sprite *s);\n",[315,1451,1452,1466,1472,1481,1495],{},[318,1453,1454,1457,1458,1461,1462,1465],{},[22,1455,1456],{},"sprite_create_from_shape(type, w, h)"," 创建指定形状和大小的精灵，",[22,1459,1460],{},"type"," 为上述 ",[22,1463,1464],{},"SHAPE_*"," 常量之一",[318,1467,1468,1471],{},[22,1469,1470],{},"sprite_create_from_file"," 从图片文件创建精灵，尺寸自动匹配",[318,1473,1474,1477,1478,1480],{},[22,1475,1476],{},"sprite_create_from_image"," 从 ",[22,1479,866],{}," 返回的图像创建精灵（内部克隆副本，不直接引用外部指针）",[318,1482,1483,1486,1487,1490,1491,1494],{},[22,1484,1485],{},"sprite_clone"," 克隆精灵，内部共享原始图片备份（",[22,1488,1489],{},"origin_image","），克隆体有独立的工作副本（",[22,1492,1493],{},"image","）",[318,1496,1497,1500],{},[22,1498,1499],{},"sprite_destroy"," 销毁精灵并释放资源",[1020,1502,1503],{"id":1503},"精灵变换",[50,1505,1507],{"className":52,"code":1506,"language":54,"meta":55,"style":55},"void sprite_set_pos(Sprite *s, float x, float y);\nvoid sprite_move(Sprite *s, float dx, float dy);\nvoid sprite_set_scale(Sprite *s, float sx, float sy);\nvoid sprite_set_rotation(Sprite *s, float degrees);\nvoid sprite_set_color(Sprite *s, int r, int g, int b, int a);\nvoid sprite_set_image(Sprite *s, void *image);\n",[22,1508,1509,1514,1519,1524,1529,1534],{"__ignoreMap":55},[59,1510,1511],{"class":61,"line":62},[59,1512,1513],{},"void sprite_set_pos(Sprite *s, float x, float y);\n",[59,1515,1516],{"class":61,"line":88},[59,1517,1518],{},"void sprite_move(Sprite *s, float dx, float dy);\n",[59,1520,1521],{"class":61,"line":134},[59,1522,1523],{},"void sprite_set_scale(Sprite *s, float sx, float sy);\n",[59,1525,1526],{"class":61,"line":140},[59,1527,1528],{},"void sprite_set_rotation(Sprite *s, float degrees);\n",[59,1530,1531],{"class":61,"line":146},[59,1532,1533],{},"void sprite_set_color(Sprite *s, int r, int g, int b, int a);\n",[59,1535,1536],{"class":61,"line":281},[59,1537,1538],{},"void sprite_set_image(Sprite *s, void *image);\n",[315,1540,1541,1547,1553,1559,1565,1575],{},[318,1542,1543,1546],{},[22,1544,1545],{},"sprite_set_pos"," 设置精灵左上角位置",[318,1548,1549,1552],{},[22,1550,1551],{},"sprite_move"," 相对当前位置移动",[318,1554,1555,1558],{},[22,1556,1557],{},"sprite_set_scale"," 缩放精灵（1.0 为原始大小）",[318,1560,1561,1564],{},[22,1562,1563],{},"sprite_set_rotation"," 旋转精灵（角度制），绕原点（默认中心）旋转",[318,1566,1567,1570,1571,1574],{},[22,1568,1569],{},"sprite_set_color"," 设置精灵的整体颜色叠加（",[69,1572,1573],{},"正片叠底 Multiply 混色模式","，透明像素不受影响）",[318,1576,1577,1580],{},[22,1578,1579],{},"sprite_set_image"," 更换精灵的图像（内部克隆为新副本，并更新原始图备份）",[1020,1582,1583],{"id":1583},"精灵属性读取",[50,1585,1587],{"className":52,"code":1586,"language":54,"meta":55,"style":55},"float sprite_get_x(Sprite *s);\nfloat sprite_get_y(Sprite *s);\nfloat sprite_get_w(Sprite *s);\nfloat sprite_get_h(Sprite *s);\nfloat sprite_get_sx(Sprite *s);\nfloat sprite_get_sy(Sprite *s);\n",[22,1588,1589,1594,1599,1604,1609,1614],{"__ignoreMap":55},[59,1590,1591],{"class":61,"line":62},[59,1592,1593],{},"float sprite_get_x(Sprite *s);\n",[59,1595,1596],{"class":61,"line":88},[59,1597,1598],{},"float sprite_get_y(Sprite *s);\n",[59,1600,1601],{"class":61,"line":134},[59,1602,1603],{},"float sprite_get_w(Sprite *s);\n",[59,1605,1606],{"class":61,"line":140},[59,1607,1608],{},"float sprite_get_h(Sprite *s);\n",[59,1610,1611],{"class":61,"line":146},[59,1612,1613],{},"float sprite_get_sx(Sprite *s);\n",[59,1615,1616],{"class":61,"line":281},[59,1617,1618],{},"float sprite_get_sy(Sprite *s);\n",[1020,1620,1621],{"id":1621},"精灵绘制与碰撞",[50,1623,1625],{"className":52,"code":1624,"language":54,"meta":55,"style":55},"void sprite_draw(HWND canvas, Sprite *s);\nint sprite_collide(Sprite *a, Sprite *b);\nint sprite_hit_test(Sprite *s, float px, float py);\n",[22,1626,1627,1632,1637],{"__ignoreMap":55},[59,1628,1629],{"class":61,"line":62},[59,1630,1631],{},"void sprite_draw(HWND canvas, Sprite *s);\n",[59,1633,1634],{"class":61,"line":88},[59,1635,1636],{},"int sprite_collide(Sprite *a, Sprite *b);\n",[59,1638,1639],{"class":61,"line":134},[59,1640,1641],{},"int sprite_hit_test(Sprite *s, float px, float py);\n",[18,1643,1644,1647,1648,1651,1652,1655,1656,1659,1660,1663],{},[22,1645,1646],{},"sprite_draw"," 将精灵绘制到指定画布，自动处理位置、缩放、旋转。",[22,1649,1650],{},"sprite_collide"," AABB 碰撞检测，传入两个 ",[22,1653,1654],{},"Sprite*","，返回 1 表示碰撞。",[22,1657,1658],{},"sprite_hit_test"," 检测点 ",[22,1661,1662],{},"(px, py)"," 是否在精灵矩形内（含缩放）。",[1020,1665,1666],{"id":1666},"精灵事件",[50,1668,1670],{"className":52,"code":1669,"language":54,"meta":55,"style":55},"void on_sprite_click(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);\nvoid on_sprite_down(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);\nvoid on_sprite_up(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);\nvoid on_sprite_hover(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);\n",[22,1671,1672,1677,1682,1687],{"__ignoreMap":55},[59,1673,1674],{"class":61,"line":62},[59,1675,1676],{},"void on_sprite_click(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);\n",[59,1678,1679],{"class":61,"line":88},[59,1680,1681],{},"void on_sprite_down(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);\n",[59,1683,1684],{"class":61,"line":134},[59,1685,1686],{},"void on_sprite_up(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);\n",[59,1688,1689],{"class":61,"line":140},[59,1690,1691],{},"void on_sprite_hover(HWND canvas, Sprite *s, void (*cb)(Sprite *, void *), void *data);\n",[18,1693,1694,1695,1698,1699,1702,1703,1706,1707,1710],{},"为精灵注册鼠标事件回调。",[22,1696,1697],{},"click"," 在精灵上点击时触发，",[22,1700,1701],{},"down","\u002F",[22,1704,1705],{},"up"," 在精灵上按下\u002F释放鼠标时触发，",[22,1708,1709],{},"hover"," 在鼠标悬停在精灵上时持续触发。销毁精灵时自动清理事件。",[1020,1712,1713],{"id":1713},"精灵颜色叠加说明",[18,1715,1716,1718,1719,75],{},[22,1717,1569],{}," 采用",[69,1720,1721,1722,1728],{},"正片叠底（Multiply）",[69,1723,1724,1725,1727],{},"混色模式，每次调用都从原始备份图（",[22,1726,1489],{},"）重新克隆工作副本再执行混色，因此","多次调用不会累积加深颜色",[18,1730,1731,1732,1735,1736,1738],{},"颜色参数 ",[22,1733,1734],{},"r\u002Fg\u002Fb"," 控制色调，",[22,1737,745],{}," 控制强度（0=完全透明\u002F无效果，255=完全叠加）：",[315,1740,1741,1747],{},[318,1742,1743,1746],{},[22,1744,1745],{},"sprite_set_color(s, 255, 0, 0, 120)"," 叠加红色调",[318,1748,1749,1752],{},[22,1750,1751],{},"sprite_set_color(s, 255, 255, 255, 255)"," 恢复原始颜色",[1020,1754,1756],{"id":1755},"sprite-结构体字段","Sprite 结构体字段",[18,1758,1759],{},"Sprite 的公有字段可以直接读写：",[50,1761,1763],{"className":52,"code":1762,"language":54,"meta":55,"style":55},"sprite->x = 100;       \u002F* 直接设坐标 *\u002F\nsprite->y = 200;\nsprite->visible = 0;   \u002F* 隐藏 *\u002F\nsprite->ox = 0;        \u002F* 原点移到左上角 *\u002F\nsprite->oy = 0;\n",[22,1764,1765,1770,1775,1780,1785],{"__ignoreMap":55},[59,1766,1767],{"class":61,"line":62},[59,1768,1769],{},"sprite->x = 100;       \u002F* 直接设坐标 *\u002F\n",[59,1771,1772],{"class":61,"line":88},[59,1773,1774],{},"sprite->y = 200;\n",[59,1776,1777],{"class":61,"line":134},[59,1778,1779],{},"sprite->visible = 0;   \u002F* 隐藏 *\u002F\n",[59,1781,1782],{"class":61,"line":140},[59,1783,1784],{},"sprite->ox = 0;        \u002F* 原点移到左上角 *\u002F\n",[59,1786,1787],{"class":61,"line":146},[59,1788,1789],{},"sprite->oy = 0;\n",[18,1791,1792],{},"内部字段（不应直接修改）：",[315,1794,1795,1800,1809],{},[318,1796,1797,1799],{},[22,1798,1493],{}," — 当前绘制用的位图（工作副本）",[318,1801,1802,1804,1805,1808],{},[22,1803,1489],{}," — 原始图片备份，每次 ",[22,1806,1807],{},"set_color"," 从此重新克隆",[318,1810,1811,1814,1815,1818,1819,1822],{},[22,1812,1813],{},"is_owner"," — ",[22,1816,1817],{},"1","=销毁时释放全部资源，",[22,1820,1821],{},"0","=克隆体仅释放自己的工作副本",[362,1824,364],{},{"title":55,"searchDepth":88,"depth":88,"links":1826},[1827,1828,1829,1830,1831,1832,1839],{"id":664,"depth":88,"text":664},{"id":704,"depth":88,"text":704},{"id":777,"depth":88,"text":777},{"id":947,"depth":88,"text":947},{"id":992,"depth":88,"text":992},{"id":1018,"depth":88,"text":1018,"children":1833},[1834,1835,1836,1837,1838],{"id":1022,"depth":134,"text":1022},{"id":1057,"depth":134,"text":1057},{"id":1097,"depth":134,"text":1097},{"id":1225,"depth":134,"text":1225},{"id":1246,"depth":134,"text":1246},{"id":1356,"depth":88,"text":1356,"children":1840},[1841,1842,1843,1844,1845,1846,1847,1848],{"id":1366,"depth":134,"text":1366},{"id":1419,"depth":134,"text":1419},{"id":1503,"depth":134,"text":1503},{"id":1583,"depth":134,"text":1583},{"id":1621,"depth":134,"text":1621},{"id":1666,"depth":134,"text":1666},{"id":1713,"depth":134,"text":1713},{"id":1755,"depth":134,"text":1756},{},"\u002Fdocs\u002Fapi\u002Feduwin-canvas-api",{"title":656,"description":55},"docs\u002Fapi\u002Feduwin-canvas-api","DmrYjyjIFMplPF1jyjKaG4bqal7VACvBxPAZkW0llw0",{"id":1855,"title":1856,"body":1857,"description":55,"extension":374,"meta":2148,"navigation":173,"path":2149,"seo":2150,"stem":2151,"__hash__":2152},"content\u002Fdocs\u002Fapi\u002Feduwin-cmd-api.md","eduwin 命令行参数 API",{"type":8,"value":1858,"toc":2142},[1859,1862,1871,1888,1891,1958,1962,1977,1991,1994,2019,2059,2120,2123,2132,2139],[11,1860,1856],{"id":1861},"eduwin-命令行参数-api",[15,1863,1864],{},[18,1865,20,1866,25,1869,29],{},[22,1867,1868],{},"eduwin_cmd.h",[22,1870,28],{},[18,1872,1873,1874,1086,1877,1086,1880,1883,1884,1887],{},"命令行参数解析模块，支持 ",[22,1875,1876],{},"--key value",[22,1878,1879],{},"--flag",[22,1881,1882],{},"--key=\"value with space\""," 等格式，自动调用 ",[22,1885,1886],{},"CommandLineToArgvW"," 完成解析。",[46,1889,1890],{"id":1890},"示例",[50,1892,1896],{"className":1893,"code":1894,"language":1895,"meta":55,"style":55},"language-bash shiki shiki-themes github-light github-dark","# 不传参运行\ncmd_demo.exe\n\n# 传参运行\ncmd_demo.exe --visible --url https:\u002F\u002Fwww.bing.com\ncmd_demo.exe --name MyApp --port 8080\ncmd_demo.exe --help\n","bash",[22,1897,1898,1904,1910,1914,1919,1935,1951],{"__ignoreMap":55},[59,1899,1900],{"class":61,"line":62},[59,1901,1903],{"class":1902},"sJ8bj","# 不传参运行\n",[59,1905,1906],{"class":61,"line":88},[59,1907,1909],{"class":1908},"sScJk","cmd_demo.exe\n",[59,1911,1912],{"class":61,"line":134},[59,1913,174],{"emptyLinePlaceholder":173},[59,1915,1916],{"class":61,"line":140},[59,1917,1918],{"class":1902},"# 传参运行\n",[59,1920,1921,1924,1928,1931],{"class":61,"line":146},[59,1922,1923],{"class":1908},"cmd_demo.exe",[59,1925,1927],{"class":1926},"sj4cs"," --visible",[59,1929,1930],{"class":1926}," --url",[59,1932,1934],{"class":1933},"sZZnC"," https:\u002F\u002Fwww.bing.com\n",[59,1936,1937,1939,1942,1945,1948],{"class":61,"line":281},[59,1938,1923],{"class":1908},[59,1940,1941],{"class":1926}," --name",[59,1943,1944],{"class":1933}," MyApp",[59,1946,1947],{"class":1926}," --port",[59,1949,1950],{"class":1926}," 8080\n",[59,1952,1953,1955],{"class":61,"line":286},[59,1954,1923],{"class":1908},[59,1956,1957],{"class":1926}," --help\n",[46,1959,1961],{"id":1960},"初始化-清理","初始化 \u002F 清理",[50,1963,1965],{"className":52,"code":1964,"language":54,"meta":55,"style":55},"void cmd_init(void);\nvoid cmd_cleanup(void);\n",[22,1966,1967,1972],{"__ignoreMap":55},[59,1968,1969],{"class":61,"line":62},[59,1970,1971],{},"void cmd_init(void);\n",[59,1973,1974],{"class":61,"line":88},[59,1975,1976],{},"void cmd_cleanup(void);\n",[18,1978,1979,1982,1983,1986,1987,1990],{},[22,1980,1981],{},"cmd_init()"," 在程序最开头调用（",[22,1984,1985],{},"init_app()"," 之前）。",[22,1988,1989],{},"cmd_cleanup()"," 可选（进程退出自动回收）。",[46,1992,1993],{"id":1993},"参数查询",[50,1995,1997],{"className":52,"code":1996,"language":54,"meta":55,"style":55},"int         cmd_has(const WCHAR *name);\nconst WCHAR *cmd_get(const WCHAR *name);\nconst WCHAR *cmd_get_def(const WCHAR *name, const WCHAR *def);\nint         cmd_is(const WCHAR *name);\n",[22,1998,1999,2004,2009,2014],{"__ignoreMap":55},[59,2000,2001],{"class":61,"line":62},[59,2002,2003],{},"int         cmd_has(const WCHAR *name);\n",[59,2005,2006],{"class":61,"line":88},[59,2007,2008],{},"const WCHAR *cmd_get(const WCHAR *name);\n",[59,2010,2011],{"class":61,"line":134},[59,2012,2013],{},"const WCHAR *cmd_get_def(const WCHAR *name, const WCHAR *def);\n",[59,2015,2016],{"class":61,"line":140},[59,2017,2018],{},"int         cmd_is(const WCHAR *name);\n",[315,2020,2021,2027,2040,2046],{},[318,2022,2023,2026],{},[22,2024,2025],{},"cmd_has(name)"," — 是否有该参数（包括开关和带值）",[318,2028,2029,2032,2033,2036,2037],{},[22,2030,2031],{},"cmd_get(name)"," — 获取参数值（如 ",[22,2034,2035],{},"--url"," 的值），不存在返回 ",[22,2038,2039],{},"L\"\"",[318,2041,2042,2045],{},[22,2043,2044],{},"cmd_get_def(name, def)"," — 同上，带默认值",[318,2047,2048,2051,2052,1086,2055,2058],{},[22,2049,2050],{},"cmd_is(name)"," — 检测无参开关（如 ",[22,2053,2054],{},"--headless",[22,2056,2057],{},"--help","），存在返回 1",[50,2060,2062],{"className":52,"code":2061,"language":54,"meta":55,"style":55},"cmd_init();\n\nif (cmd_is(L\"--help\")) {\n    cmd_print_help(L\"AI Browser\");\n    return 0;\n}\n\nconst WCHAR *url = cmd_get_def(L\"--url\", L\"about:blank\");\nint port = _wtoi(cmd_get_def(L\"--port\", L\"9099\"));\nint headless = cmd_is(L\"--headless\");\n\ncmd_cleanup();\n",[22,2063,2064,2069,2073,2078,2083,2088,2092,2096,2101,2106,2111,2115],{"__ignoreMap":55},[59,2065,2066],{"class":61,"line":62},[59,2067,2068],{},"cmd_init();\n",[59,2070,2071],{"class":61,"line":88},[59,2072,174],{"emptyLinePlaceholder":173},[59,2074,2075],{"class":61,"line":134},[59,2076,2077],{},"if (cmd_is(L\"--help\")) {\n",[59,2079,2080],{"class":61,"line":140},[59,2081,2082],{},"    cmd_print_help(L\"AI Browser\");\n",[59,2084,2085],{"class":61,"line":146},[59,2086,2087],{},"    return 0;\n",[59,2089,2090],{"class":61,"line":281},[59,2091,149],{},[59,2093,2094],{"class":61,"line":286},[59,2095,174],{"emptyLinePlaceholder":173},[59,2097,2098],{"class":61,"line":292},[59,2099,2100],{},"const WCHAR *url = cmd_get_def(L\"--url\", L\"about:blank\");\n",[59,2102,2103],{"class":61,"line":600},[59,2104,2105],{},"int port = _wtoi(cmd_get_def(L\"--port\", L\"9099\"));\n",[59,2107,2108],{"class":61,"line":917},[59,2109,2110],{},"int headless = cmd_is(L\"--headless\");\n",[59,2112,2113],{"class":61,"line":922},[59,2114,174],{"emptyLinePlaceholder":173},[59,2116,2117],{"class":61,"line":928},[59,2118,2119],{},"cmd_cleanup();\n",[46,2121,2122],{"id":2122},"帮助信息",[50,2124,2126],{"className":52,"code":2125,"language":54,"meta":55,"style":55},"void cmd_print_help(const WCHAR *desc);\n",[22,2127,2128],{"__ignoreMap":55},[59,2129,2130],{"class":61,"line":62},[59,2131,2125],{},[18,2133,2134,2135,2138],{},"打印帮助信息到控制台。",[22,2136,2137],{},"desc"," 为程序描述文字。帮助内容通过一个内部注册表自动生成。",[362,2140,2141],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":55,"searchDepth":88,"depth":88,"links":2143},[2144,2145,2146,2147],{"id":1890,"depth":88,"text":1890},{"id":1960,"depth":88,"text":1961},{"id":1993,"depth":88,"text":1993},{"id":2122,"depth":88,"text":2122},{},"\u002Fdocs\u002Fapi\u002Feduwin-cmd-api",{"title":1856,"description":55},"docs\u002Fapi\u002Feduwin-cmd-api","EQ0L3PSMDq5PgChkngp2YQADYp79AXIWmWZlSNuMQBM",{"id":2154,"title":2155,"body":2156,"description":55,"extension":374,"meta":3788,"navigation":173,"path":3789,"seo":3790,"stem":3791,"__hash__":3792},"content\u002Fdocs\u002Fapi\u002Feduwin-ctrl-api.md","eduwin 控件 API",{"type":8,"value":2157,"toc":3742},[2158,2161,2164,2167,2170,2185,2188,2199,2202,2211,2218,2238,2241,2250,2257,2272,2275,2295,2302,2305,2314,2317,2332,2346,2366,2369,2394,2437,2444,2459,2461,2464,2467,2482,2488,2517,2519,2522,2531,2534,2578,2593,2595,2598,2607,2619,2621,2624,2633,2640,2642,2645,2654,2662,2664,2667,2687,2702,2704,2707,2725,2735,2737,2740,2749,2752,2754,2757,2782,2797,2799,2802,2832,2849,2851,2854,2869,2876,2878,2881,2901,2912,2914,2917,2937,2947,2949,2952,2972,2983,2985,2988,3003,3010,3025,3027,3030,3045,3052,3054,3057,3107,3110,3160,3185,3187,3190,3225,3236,3242,3262,3272,3281,3284,3293,3296,3305,3311,3313,3316,3341,3352,3361,3390,3392,3395,3404,3407,3409,3413,3433,3444,3446,3449,3464,3471,3473,3476,3485,3492,3494,3498,3501,3510,3517,3537,3553,3562,3565,3574,3577,3602,3617,3619,3622,3625,3634,3653,3656,3665,3672,3675,3684,3690,3692,3695,3698,3707,3710,3713,3722,3725,3728,3737,3740],[11,2159,2155],{"id":2160},"eduwin-控件-api",[46,2162,2163],{"id":2163},"辅助函数",[18,2165,2166],{},"以下函数适用于多种控件类型。",[1020,2168,2169],{"id":2169},"文本内容",[50,2171,2173],{"className":52,"code":2172,"language":54,"meta":55,"style":55},"void set_text(HWND hwnd, const WCHAR *text);\nconst WCHAR *get_text(HWND hwnd);\n",[22,2174,2175,2180],{"__ignoreMap":55},[59,2176,2177],{"class":61,"line":62},[59,2178,2179],{},"void set_text(HWND hwnd, const WCHAR *text);\n",[59,2181,2182],{"class":61,"line":88},[59,2183,2184],{},"const WCHAR *get_text(HWND hwnd);\n",[18,2186,2187],{},"设置\u002F获取控件的文本内容。适用于按钮、标签、文本框、输入框、状态栏等。",[15,2189,2190],{},[18,2191,2192,2195,2196,2198],{},[22,2193,2194],{},"get_text"," 内部为每个控件独立分配缓冲区，多次调用返回不同指针，互不干扰。缓冲区在下次对同一控件调用 ",[22,2197,2194],{}," 时自动释放。",[1020,2200,2201],{"id":2201},"只读控制",[50,2203,2205],{"className":52,"code":2204,"language":54,"meta":55,"style":55},"void set_readonly(HWND hwnd, int readonly);\n",[22,2206,2207],{"__ignoreMap":55},[59,2208,2209],{"class":61,"line":62},[59,2210,2204],{},[18,2212,2213,2214,2217],{},"将文本框、多行文本框、密码框设为只读或可编辑。",[22,2215,2216],{},"readonly"," 为 1 时只读，为 0 时可编辑。",[50,2219,2221],{"className":52,"code":2220,"language":54,"meta":55,"style":55},"HWND text = create_text(win, L\"只读内容\", 10, 10, 200, 24);\nset_readonly(text, 1);   \u002F\u002F 设为只读\nset_readonly(text, 0);   \u002F\u002F 恢复可编辑\n",[22,2222,2223,2228,2233],{"__ignoreMap":55},[59,2224,2225],{"class":61,"line":62},[59,2226,2227],{},"HWND text = create_text(win, L\"只读内容\", 10, 10, 200, 24);\n",[59,2229,2230],{"class":61,"line":88},[59,2231,2232],{},"set_readonly(text, 1);   \u002F\u002F 设为只读\n",[59,2234,2235],{"class":61,"line":134},[59,2236,2237],{},"set_readonly(text, 0);   \u002F\u002F 恢复可编辑\n",[1020,2239,2240],{"id":2240},"启用禁用",[50,2242,2244],{"className":52,"code":2243,"language":54,"meta":55,"style":55},"void set_enable(HWND hwnd, int enable);\n",[22,2245,2246],{"__ignoreMap":55},[59,2247,2248],{"class":61,"line":62},[59,2249,2243],{},[18,2251,2252,2253,2256],{},"启用或禁用控件。",[22,2254,2255],{},"enable"," 为 1 时可用，为 0 时禁用（灰显且不可操作）。",[50,2258,2260],{"className":52,"code":2259,"language":54,"meta":55,"style":55},"set_enable(btn_delete, 0);  \u002F\u002F 禁用删除按钮\nset_enable(btn_delete, 1);  \u002F\u002F 启用删除按钮\n",[22,2261,2262,2267],{"__ignoreMap":55},[59,2263,2264],{"class":61,"line":62},[59,2265,2266],{},"set_enable(btn_delete, 0);  \u002F\u002F 禁用删除按钮\n",[59,2268,2269],{"class":61,"line":88},[59,2270,2271],{},"set_enable(btn_delete, 1);  \u002F\u002F 启用删除按钮\n",[1020,2273,2274],{"id":2274},"显隐",[50,2276,2278],{"className":52,"code":2277,"language":54,"meta":55,"style":55},"void show(HWND hwnd);\nvoid hide(HWND hwnd);\nint  is_visible(HWND hwnd);\n",[22,2279,2280,2285,2290],{"__ignoreMap":55},[59,2281,2282],{"class":61,"line":62},[59,2283,2284],{},"void show(HWND hwnd);\n",[59,2286,2287],{"class":61,"line":88},[59,2288,2289],{},"void hide(HWND hwnd);\n",[59,2291,2292],{"class":61,"line":134},[59,2293,2294],{},"int  is_visible(HWND hwnd);\n",[18,2296,2297,2298,2301],{},"显示或隐藏控件。",[22,2299,2300],{},"is_visible"," 返回 1 表示可见，0 表示隐藏。",[1020,2303,2304],{"id":2304},"布局",[50,2306,2308],{"className":52,"code":2307,"language":54,"meta":55,"style":55},"void move(HWND hwnd, int x, int y);\n",[22,2309,2310],{"__ignoreMap":55},[59,2311,2312],{"class":61,"line":62},[59,2313,2307],{},[18,2315,2316],{},"移动控件到指定位置（仅改变位置，不改变尺寸）。",[50,2318,2320],{"className":52,"code":2319,"language":54,"meta":55,"style":55},"void set_size(HWND hwnd, int w, int h);\nvoid get_size(HWND hwnd, int *w, int *h);\n",[22,2321,2322,2327],{"__ignoreMap":55},[59,2323,2324],{"class":61,"line":62},[59,2325,2326],{},"void set_size(HWND hwnd, int w, int h);\n",[59,2328,2329],{"class":61,"line":88},[59,2330,2331],{},"void get_size(HWND hwnd, int *w, int *h);\n",[18,2333,2334,2335,2338,2339,2342,2343,2345],{},"设置或获取控件的客户区尺寸。",[22,2336,2337],{},"w"," 或 ",[22,2340,2341],{},"h"," 可传 ",[22,2344,477],{}," 跳过读取。",[50,2347,2349],{"className":52,"code":2348,"language":54,"meta":55,"style":55},"int cw, ch;\nget_size(win, &cw, &ch);    \u002F\u002F 获取窗口客户区宽高\nset_size(btn, 120, 30);     \u002F\u002F 调整按钮尺寸\n",[22,2350,2351,2356,2361],{"__ignoreMap":55},[59,2352,2353],{"class":61,"line":62},[59,2354,2355],{},"int cw, ch;\n",[59,2357,2358],{"class":61,"line":88},[59,2359,2360],{},"get_size(win, &cw, &ch);    \u002F\u002F 获取窗口客户区宽高\n",[59,2362,2363],{"class":61,"line":134},[59,2364,2365],{},"set_size(btn, 120, 30);     \u002F\u002F 调整按钮尺寸\n",[1020,2367,2368],{"id":2368},"窗口样式与扩展属性",[50,2370,2372],{"className":52,"code":2371,"language":54,"meta":55,"style":55},"LONG_PTR get_style(HWND hwnd);\nvoid set_style(HWND hwnd, LONG_PTR style);\nLONG_PTR get_attr(HWND hwnd);\nvoid set_attr(HWND hwnd, LONG_PTR attr);\n",[22,2373,2374,2379,2384,2389],{"__ignoreMap":55},[59,2375,2376],{"class":61,"line":62},[59,2377,2378],{},"LONG_PTR get_style(HWND hwnd);\n",[59,2380,2381],{"class":61,"line":88},[59,2382,2383],{},"void set_style(HWND hwnd, LONG_PTR style);\n",[59,2385,2386],{"class":61,"line":134},[59,2387,2388],{},"LONG_PTR get_attr(HWND hwnd);\n",[59,2390,2391],{"class":61,"line":140},[59,2392,2393],{},"void set_attr(HWND hwnd, LONG_PTR attr);\n",[315,2395,2396,2419],{},[318,2397,2398,735,2401,2404,2405,2408,2409,2412,2413,1086,2416,1494],{},[22,2399,2400],{},"get_style",[22,2402,2403],{},"set_style"," 读取或设置窗口样式（",[22,2406,2407],{},"GWL_STYLE","，对应 ",[22,2410,2411],{},"WS_*"," 常量，如 ",[22,2414,2415],{},"WS_THICKFRAME",[22,2417,2418],{},"WS_BORDER",[318,2420,2421,735,2424,2427,2428,2408,2431,2412,2434,1494],{},[22,2422,2423],{},"get_attr",[22,2425,2426],{},"set_attr"," 读取或设置窗口扩展样式（",[22,2429,2430],{},"GWL_EXSTYLE",[22,2432,2433],{},"WS_EX_*",[22,2435,2436],{},"WS_EX_TOOLWINDOW",[18,2438,2439,735,2441,2443],{},[22,2440,2403],{},[22,2442,2426],{}," 调用后会自动刷新窗口的非客户区，确保样式变更立即生效。",[50,2445,2447],{"className":52,"code":2446,"language":54,"meta":55,"style":55},"\u002F\u002F 为窗口添加可缩放边框和最大化按钮\nset_style(win, get_style(win) | WS_THICKFRAME | WS_MAXIMIZEBOX);\n",[22,2448,2449,2454],{"__ignoreMap":55},[59,2450,2451],{"class":61,"line":62},[59,2452,2453],{},"\u002F\u002F 为窗口添加可缩放边框和最大化按钮\n",[59,2455,2456],{"class":61,"line":88},[59,2457,2458],{},"set_style(win, get_style(win) | WS_THICKFRAME | WS_MAXIMIZEBOX);\n",[432,2460],{},[46,2462,2463],{"id":2463},"控件参考",[1020,2465,2466],{"id":2466},"按钮",[50,2468,2470],{"className":52,"code":2469,"language":54,"meta":55,"style":55},"HWND create_button(HWND parent, const WCHAR *text, int x, int y, int w, int h);\nvoid on_button_click(HWND btn, void (*callback)(void *), void *data);\n",[22,2471,2472,2477],{"__ignoreMap":55},[59,2473,2474],{"class":61,"line":62},[59,2475,2476],{},"HWND create_button(HWND parent, const WCHAR *text, int x, int y, int w, int h);\n",[59,2478,2479],{"class":61,"line":88},[59,2480,2481],{},"void on_button_click(HWND btn, void (*callback)(void *), void *data);\n",[18,2483,2484,2485,2487],{},"创建一个按钮，通过 ",[22,2486,638],{}," 注册点击事件回调。",[50,2489,2491],{"className":52,"code":2490,"language":54,"meta":55,"style":55},"void btn_click(void *data) {\n    set_text(label, L\"按钮被点击\");\n}\nHWND btn = create_button(win, L\"点击我\", 10, 10, 100, 30);\non_button_click(btn, btn_click, NULL);\n",[22,2492,2493,2498,2503,2507,2512],{"__ignoreMap":55},[59,2494,2495],{"class":61,"line":62},[59,2496,2497],{},"void btn_click(void *data) {\n",[59,2499,2500],{"class":61,"line":88},[59,2501,2502],{},"    set_text(label, L\"按钮被点击\");\n",[59,2504,2505],{"class":61,"line":134},[59,2506,149],{},[59,2508,2509],{"class":61,"line":140},[59,2510,2511],{},"HWND btn = create_button(win, L\"点击我\", 10, 10, 100, 30);\n",[59,2513,2514],{"class":61,"line":146},[59,2515,2516],{},"on_button_click(btn, btn_click, NULL);\n",[432,2518],{},[1020,2520,2521],{"id":2521},"标签",[50,2523,2525],{"className":52,"code":2524,"language":54,"meta":55,"style":55},"HWND create_label(HWND parent, const WCHAR *text, int x, int y, int w, int h);\n",[22,2526,2527],{"__ignoreMap":55},[59,2528,2529],{"class":61,"line":62},[59,2530,2524],{},[18,2532,2533],{},"创建一个标签（静态文本），用于显示文字。",[15,2535,2536,2542,2567],{},[18,2537,2538,2541],{},[69,2539,2540],{},"提示："," C 语言中，相邻的字符串字面量会被自动拼接。多行文本可以拆成多行写，提高可读性：",[50,2543,2545],{"className":52,"code":2544,"language":54,"meta":55,"style":55},"create_label(win, L\"第一行\\n\"\n                  L\"第二行\\n\"\n                  L\"第三行\",\n             10, 40, 200, 80);\n",[22,2546,2547,2552,2557,2562],{"__ignoreMap":55},[59,2548,2549],{"class":61,"line":62},[59,2550,2551],{},"create_label(win, L\"第一行\\n\"\n",[59,2553,2554],{"class":61,"line":88},[59,2555,2556],{},"                  L\"第二行\\n\"\n",[59,2558,2559],{"class":61,"line":134},[59,2560,2561],{},"                  L\"第三行\",\n",[59,2563,2564],{"class":61,"line":140},[59,2565,2566],{},"             10, 40, 200, 80);\n",[18,2568,2569,2570,2573,2574,2577],{},"编译器会自动合并为 ",[22,2571,2572],{},"L\"第一行\\n第二行\\n第三行\"","。这个技巧适用于所有接受 ",[22,2575,2576],{},"const WCHAR *"," 参数的函数。",[50,2579,2581],{"className":52,"code":2580,"language":54,"meta":55,"style":55},"HWND label = create_label(win, L\"用户名：\", 10, 40, 200, 24);\nset_text(label, L\"新文字\");\n",[22,2582,2583,2588],{"__ignoreMap":55},[59,2584,2585],{"class":61,"line":62},[59,2586,2587],{},"HWND label = create_label(win, L\"用户名：\", 10, 40, 200, 24);\n",[59,2589,2590],{"class":61,"line":88},[59,2591,2592],{},"set_text(label, L\"新文字\");\n",[432,2594],{},[1020,2596,2597],{"id":2597},"文本框",[50,2599,2601],{"className":52,"code":2600,"language":54,"meta":55,"style":55},"HWND create_text(HWND parent, const WCHAR *text, int x, int y, int w, int h);\n",[22,2602,2603],{"__ignoreMap":55},[59,2604,2605],{"class":61,"line":62},[59,2606,2600],{},[18,2608,2609,2610,2612,2613,735,2616,2618],{},"创建一个单行文本框，",[22,2611,358],{}," 为初始文本。可通过 ",[22,2614,2615],{},"set_text",[22,2617,2194],{}," 读写内容。",[432,2620],{},[1020,2622,2623],{"id":2623},"多行文本",[50,2625,2627],{"className":52,"code":2626,"language":54,"meta":55,"style":55},"HWND create_textarea(HWND parent, const WCHAR *text, int x, int y, int w, int h);\n",[22,2628,2629],{"__ignoreMap":55},[59,2630,2631],{"class":61,"line":62},[59,2632,2626],{},[18,2634,2635,2636,735,2638,2618],{},"创建一个多行文本输入框，带垂直滚动条，支持换行和中文。可通过 ",[22,2637,2615],{},[22,2639,2194],{},[432,2641],{},[1020,2643,2644],{"id":2644},"密码框",[50,2646,2648],{"className":52,"code":2647,"language":54,"meta":55,"style":55},"HWND create_password(HWND parent, int x, int y, int w, int h);\n",[22,2649,2650],{"__ignoreMap":55},[59,2651,2652],{"class":61,"line":62},[59,2653,2647],{},[18,2655,2656,2657,735,2659,2661],{},"创建一个密码输入框，输入内容显示为黑点（•）。可通过 ",[22,2658,2615],{},[22,2660,2194],{}," 读写密码文本。",[432,2663],{},[1020,2665,2666],{"id":2666},"复选框",[50,2668,2670],{"className":52,"code":2669,"language":54,"meta":55,"style":55},"HWND create_checkbox(HWND parent, const WCHAR *text, int x, int y, int w, int h);\nint is_checked(HWND hwnd);\nvoid set_checked(HWND hwnd, int checked);\n",[22,2671,2672,2677,2682],{"__ignoreMap":55},[59,2673,2674],{"class":61,"line":62},[59,2675,2676],{},"HWND create_checkbox(HWND parent, const WCHAR *text, int x, int y, int w, int h);\n",[59,2678,2679],{"class":61,"line":88},[59,2680,2681],{},"int is_checked(HWND hwnd);\n",[59,2683,2684],{"class":61,"line":134},[59,2685,2686],{},"void set_checked(HWND hwnd, int checked);\n",[18,2688,2689,2690,2693,2694,2697,2698,2701],{},"创建一个复选框。点击时自动切换选中\u002F未选状态。",[22,2691,2692],{},"is_checked"," 返回 1 表示已选中；",[22,2695,2696],{},"set_checked(1)"," 设选中，",[22,2699,2700],{},"set_checked(0)"," 取消。",[432,2703],{},[1020,2705,2706],{"id":2706},"单选按钮",[50,2708,2710],{"className":52,"code":2709,"language":54,"meta":55,"style":55},"HWND create_radio(HWND parent, const WCHAR *text, int x, int y, int w, int h);\nint is_checked(HWND hwnd);\nvoid set_checked(HWND hwnd, int checked);\n",[22,2711,2712,2717,2721],{"__ignoreMap":55},[59,2713,2714],{"class":61,"line":62},[59,2715,2716],{},"HWND create_radio(HWND parent, const WCHAR *text, int x, int y, int w, int h);\n",[59,2718,2719],{"class":61,"line":88},[59,2720,2681],{},[59,2722,2723],{"class":61,"line":134},[59,2724,2686],{},[18,2726,2727,2728,2730,2731,2734],{},"创建一个单选按钮。同一父窗口下的单选按钮自动互斥。通过 ",[22,2729,2692],{}," 和 ",[22,2732,2733],{},"set_checked"," 读取\u002F设定状态。",[432,2736],{},[1020,2738,2739],{"id":2739},"分组框",[50,2741,2743],{"className":52,"code":2742,"language":54,"meta":55,"style":55},"HWND create_groupbox(HWND parent, const WCHAR *text, int x, int y, int w, int h);\n",[22,2744,2745],{"__ignoreMap":55},[59,2746,2747],{"class":61,"line":62},[59,2748,2742],{},[18,2750,2751],{},"创建一个分组框（视觉框线 + 标题），常用来包裹按钮、单选等控件。",[432,2753],{},[1020,2755,2756],{"id":2756},"下拉框",[50,2758,2760],{"className":52,"code":2759,"language":54,"meta":55,"style":55},"HWND create_combobox(HWND parent, int x, int y, int w, int h);\nvoid combobox_add_text(HWND combobox, const WCHAR *text);\nint combobox_selected_index(HWND combobox);\nconst WCHAR *combobox_selected_text(HWND combobox);\n",[22,2761,2762,2767,2772,2777],{"__ignoreMap":55},[59,2763,2764],{"class":61,"line":62},[59,2765,2766],{},"HWND create_combobox(HWND parent, int x, int y, int w, int h);\n",[59,2768,2769],{"class":61,"line":88},[59,2770,2771],{},"void combobox_add_text(HWND combobox, const WCHAR *text);\n",[59,2773,2774],{"class":61,"line":134},[59,2775,2776],{},"int combobox_selected_index(HWND combobox);\n",[59,2778,2779],{"class":61,"line":140},[59,2780,2781],{},"const WCHAR *combobox_selected_text(HWND combobox);\n",[18,2783,2784,2785,2788,2789,2792,2793,2796],{},"创建一个下拉选择框（不可编辑）。",[22,2786,2787],{},"combobox_add_text"," 添加选项；",[22,2790,2791],{},"combobox_selected_index"," 返回选中索引（-1 表示未选中）；",[22,2794,2795],{},"combobox_selected_text"," 返回选中项的文本。",[432,2798],{},[1020,2800,2801],{"id":2801},"列表框",[50,2803,2805],{"className":52,"code":2804,"language":54,"meta":55,"style":55},"HWND create_listbox(HWND parent, int x, int y, int w, int h);\nvoid listbox_add_text(HWND listbox, const WCHAR *text);\nint listbox_selected_index(HWND listbox);\nint listbox_count(HWND listbox);\nconst WCHAR *listbox_selected_text(HWND listbox);\n",[22,2806,2807,2812,2817,2822,2827],{"__ignoreMap":55},[59,2808,2809],{"class":61,"line":62},[59,2810,2811],{},"HWND create_listbox(HWND parent, int x, int y, int w, int h);\n",[59,2813,2814],{"class":61,"line":88},[59,2815,2816],{},"void listbox_add_text(HWND listbox, const WCHAR *text);\n",[59,2818,2819],{"class":61,"line":134},[59,2820,2821],{},"int listbox_selected_index(HWND listbox);\n",[59,2823,2824],{"class":61,"line":140},[59,2825,2826],{},"int listbox_count(HWND listbox);\n",[59,2828,2829],{"class":61,"line":146},[59,2830,2831],{},"const WCHAR *listbox_selected_text(HWND listbox);\n",[18,2833,2834,2835,2788,2838,2841,2842,2845,2846,2796],{},"创建一个列表框。",[22,2836,2837],{},"listbox_add_text",[22,2839,2840],{},"listbox_selected_index"," 返回选中索引；",[22,2843,2844],{},"listbox_count"," 返回总项数；",[22,2847,2848],{},"listbox_selected_text",[432,2850],{},[1020,2852,2853],{"id":2853},"进度条",[50,2855,2857],{"className":52,"code":2856,"language":54,"meta":55,"style":55},"HWND create_progressbar(HWND parent, int x, int y, int w, int h);\nvoid set_progress(HWND progress, int value);\n",[22,2858,2859,2864],{"__ignoreMap":55},[59,2860,2861],{"class":61,"line":62},[59,2862,2863],{},"HWND create_progressbar(HWND parent, int x, int y, int w, int h);\n",[59,2865,2866],{"class":61,"line":88},[59,2867,2868],{},"void set_progress(HWND progress, int value);\n",[18,2870,2871,2872,2875],{},"创建一个进度条，默认范围 0–100，",[22,2873,2874],{},"set_progress"," 设定当前进度值。",[432,2877],{},[1020,2879,2880],{"id":2880},"滑块",[50,2882,2884],{"className":52,"code":2883,"language":54,"meta":55,"style":55},"HWND create_slider(HWND parent, int x, int y, int w, int h);\nint slider_get_pos(HWND slider);\nvoid slider_set_pos(HWND slider, int value);\n",[22,2885,2886,2891,2896],{"__ignoreMap":55},[59,2887,2888],{"class":61,"line":62},[59,2889,2890],{},"HWND create_slider(HWND parent, int x, int y, int w, int h);\n",[59,2892,2893],{"class":61,"line":88},[59,2894,2895],{},"int slider_get_pos(HWND slider);\n",[59,2897,2898],{"class":61,"line":134},[59,2899,2900],{},"void slider_set_pos(HWND slider, int value);\n",[18,2902,2903,2904,2907,2908,2911],{},"创建一个滑块（带刻度线），默认范围 0–100。",[22,2905,2906],{},"slider_get_pos"," 读取位置，",[22,2909,2910],{},"slider_set_pos"," 设定位置。",[432,2913],{},[1020,2915,2916],{"id":2916},"滚动条",[50,2918,2920],{"className":52,"code":2919,"language":54,"meta":55,"style":55},"HWND create_scrollbar(HWND parent, int x, int y, int w, int h);\nint scrollbar_get_pos(HWND scrollbar);\nvoid scrollbar_set_pos(HWND scrollbar, int pos);\n",[22,2921,2922,2927,2932],{"__ignoreMap":55},[59,2923,2924],{"class":61,"line":62},[59,2925,2926],{},"HWND create_scrollbar(HWND parent, int x, int y, int w, int h);\n",[59,2928,2929],{"class":61,"line":88},[59,2930,2931],{},"int scrollbar_get_pos(HWND scrollbar);\n",[59,2933,2934],{"class":61,"line":134},[59,2935,2936],{},"void scrollbar_set_pos(HWND scrollbar, int pos);\n",[18,2938,2939,2940,735,2943,2946],{},"创建一个滚动条。根据宽高比自动决定水平或垂直（宽 > 高为水平，高 > 宽为垂直）。",[22,2941,2942],{},"scrollbar_get_pos",[22,2944,2945],{},"scrollbar_set_pos"," 读取\u002F设定位置。",[432,2948],{},[1020,2950,2951],{"id":2951},"数值调节",[50,2953,2955],{"className":52,"code":2954,"language":54,"meta":55,"style":55},"HWND create_spin(HWND parent, int x, int y, int w, int h);\nvoid spin_set_range(HWND spin, int min, int max);\nint spin_get_pos(HWND spin);\n",[22,2956,2957,2962,2967],{"__ignoreMap":55},[59,2958,2959],{"class":61,"line":62},[59,2960,2961],{},"HWND create_spin(HWND parent, int x, int y, int w, int h);\n",[59,2963,2964],{"class":61,"line":88},[59,2965,2966],{},"void spin_set_range(HWND spin, int min, int max);\n",[59,2968,2969],{"class":61,"line":134},[59,2970,2971],{},"int spin_get_pos(HWND spin);\n",[18,2973,2974,2975,2978,2979,2982],{},"创建一个数值调节按钮（Spin\u002FUp-Down）。自动绑定前面的编辑框作为伙伴。",[22,2976,2977],{},"spin_set_range"," 设定范围，",[22,2980,2981],{},"spin_get_pos"," 读取当前数值。",[432,2984],{},[1020,2986,2987],{"id":2987},"状态栏",[50,2989,2991],{"className":52,"code":2990,"language":54,"meta":55,"style":55},"HWND create_statusbar(HWND parent);\nvoid statusbar_set_text(HWND statusbar, const WCHAR *text);\n",[22,2992,2993,2998],{"__ignoreMap":55},[59,2994,2995],{"class":61,"line":62},[59,2996,2997],{},"HWND create_statusbar(HWND parent);\n",[59,2999,3000],{"class":61,"line":88},[59,3001,3002],{},"void statusbar_set_text(HWND statusbar, const WCHAR *text);\n",[18,3004,3005,3006,3009],{},"创建一个底部状态栏，自动吸附在窗口底部。",[22,3007,3008],{},"statusbar_set_text"," 设置显示文本。",[50,3011,3013],{"className":52,"code":3012,"language":54,"meta":55,"style":55},"HWND sb = create_statusbar(win);\nstatusbar_set_text(sb, L\"就绪\");\n",[22,3014,3015,3020],{"__ignoreMap":55},[59,3016,3017],{"class":61,"line":62},[59,3018,3019],{},"HWND sb = create_statusbar(win);\n",[59,3021,3022],{"class":61,"line":88},[59,3023,3024],{},"statusbar_set_text(sb, L\"就绪\");\n",[432,3026],{},[1020,3028,3029],{"id":3029},"工具栏",[50,3031,3033],{"className":52,"code":3032,"language":54,"meta":55,"style":55},"HWND create_toolbar(HWND parent);\nvoid toolbar_add_button(HWND toolbar, const WCHAR *text);\n",[22,3034,3035,3040],{"__ignoreMap":55},[59,3036,3037],{"class":61,"line":62},[59,3038,3039],{},"HWND create_toolbar(HWND parent);\n",[59,3041,3042],{"class":61,"line":88},[59,3043,3044],{},"void toolbar_add_button(HWND toolbar, const WCHAR *text);\n",[18,3046,3047,3048,3051],{},"创建一个顶部工具栏。",[22,3049,3050],{},"toolbar_add_button"," 添加文字按钮。",[432,3053],{},[1020,3055,3056],{"id":3056},"树状列表",[50,3058,3060],{"className":52,"code":3059,"language":54,"meta":55,"style":55},"HWND create_treeview(HWND parent, int x, int y, int w, int h);\nHWND treeview_add_text(HWND treeview, const WCHAR *text);\nHWND treeview_add_child(HWND treeview, HWND parent_item, const WCHAR *text);\nHWND treeview_selected(HWND treeview);\nconst WCHAR *treeview_selected_text(HWND treeview);\nvoid treeview_delete(HWND treeview, HWND item);\nvoid treeview_set_text(HWND treeview, HWND item, const WCHAR *text);\nvoid treeview_expand(HWND treeview, HWND item);\nvoid treeview_collapse(HWND treeview, HWND item);\n",[22,3061,3062,3067,3072,3077,3082,3087,3092,3097,3102],{"__ignoreMap":55},[59,3063,3064],{"class":61,"line":62},[59,3065,3066],{},"HWND create_treeview(HWND parent, int x, int y, int w, int h);\n",[59,3068,3069],{"class":61,"line":88},[59,3070,3071],{},"HWND treeview_add_text(HWND treeview, const WCHAR *text);\n",[59,3073,3074],{"class":61,"line":134},[59,3075,3076],{},"HWND treeview_add_child(HWND treeview, HWND parent_item, const WCHAR *text);\n",[59,3078,3079],{"class":61,"line":140},[59,3080,3081],{},"HWND treeview_selected(HWND treeview);\n",[59,3083,3084],{"class":61,"line":146},[59,3085,3086],{},"const WCHAR *treeview_selected_text(HWND treeview);\n",[59,3088,3089],{"class":61,"line":281},[59,3090,3091],{},"void treeview_delete(HWND treeview, HWND item);\n",[59,3093,3094],{"class":61,"line":286},[59,3095,3096],{},"void treeview_set_text(HWND treeview, HWND item, const WCHAR *text);\n",[59,3098,3099],{"class":61,"line":292},[59,3100,3101],{},"void treeview_expand(HWND treeview, HWND item);\n",[59,3103,3104],{"class":61,"line":600},[59,3105,3106],{},"void treeview_collapse(HWND treeview, HWND item);\n",[18,3108,3109],{},"创建一个树状列表视图。",[315,3111,3112,3118,3124,3133,3139,3145,3151],{},[318,3113,3114,3117],{},[22,3115,3116],{},"treeview_add_text"," 添加根节点，返回节点句柄",[318,3119,3120,3123],{},[22,3121,3122],{},"treeview_add_child"," 在指定父节点下添加子节点，返回节点句柄",[318,3125,3126,3129,3130,3132],{},[22,3127,3128],{},"treeview_selected"," 返回当前选中节点句柄（",[22,3131,477],{}," 表示无选中）",[318,3134,3135,3138],{},[22,3136,3137],{},"treeview_selected_text"," 返回当前选中节点的文本",[318,3140,3141,3144],{},[22,3142,3143],{},"treeview_delete"," 删除指定节点及其所有子节点",[318,3146,3147,3150],{},[22,3148,3149],{},"treeview_set_text"," 修改指定节点的显示文本",[318,3152,3153,735,3156,3159],{},[22,3154,3155],{},"treeview_expand",[22,3157,3158],{},"treeview_collapse"," 展开\u002F折叠指定节点",[50,3161,3163],{"className":52,"code":3162,"language":54,"meta":55,"style":55},"HWND root  = treeview_add_text(tree, L\"中国\");\nHWND city1 = treeview_add_child(tree, root, L\"北京\");\nHWND city2 = treeview_add_child(tree, root, L\"上海\");\ntreeview_expand(tree, root);\n",[22,3164,3165,3170,3175,3180],{"__ignoreMap":55},[59,3166,3167],{"class":61,"line":62},[59,3168,3169],{},"HWND root  = treeview_add_text(tree, L\"中国\");\n",[59,3171,3172],{"class":61,"line":88},[59,3173,3174],{},"HWND city1 = treeview_add_child(tree, root, L\"北京\");\n",[59,3176,3177],{"class":61,"line":134},[59,3178,3179],{},"HWND city2 = treeview_add_child(tree, root, L\"上海\");\n",[59,3181,3182],{"class":61,"line":140},[59,3183,3184],{},"treeview_expand(tree, root);\n",[432,3186],{},[1020,3188,3189],{"id":3189},"详细列表",[50,3191,3193],{"className":52,"code":3192,"language":54,"meta":55,"style":55},"HWND create_listview(HWND parent, int x, int y, int w, int h);\nvoid listview_add_column(HWND listview, const WCHAR *text, int width);\nHWND listview_add_text(HWND listview, const WCHAR *text);\nvoid listview_set_text(HWND listview, int row, int col, const WCHAR *text);\nint listview_count(HWND listview);\nint listview_selected_index(HWND listview);\n",[22,3194,3195,3200,3205,3210,3215,3220],{"__ignoreMap":55},[59,3196,3197],{"class":61,"line":62},[59,3198,3199],{},"HWND create_listview(HWND parent, int x, int y, int w, int h);\n",[59,3201,3202],{"class":61,"line":88},[59,3203,3204],{},"void listview_add_column(HWND listview, const WCHAR *text, int width);\n",[59,3206,3207],{"class":61,"line":134},[59,3208,3209],{},"HWND listview_add_text(HWND listview, const WCHAR *text);\n",[59,3211,3212],{"class":61,"line":140},[59,3213,3214],{},"void listview_set_text(HWND listview, int row, int col, const WCHAR *text);\n",[59,3216,3217],{"class":61,"line":146},[59,3218,3219],{},"int listview_count(HWND listview);\n",[59,3221,3222],{"class":61,"line":281},[59,3223,3224],{},"int listview_selected_index(HWND listview);\n",[18,3226,3227,3228,3231,3232,3235],{},"创建一个详细列表视图（报表模式）。先 ",[22,3229,3230],{},"listview_add_column"," 添加列（指定列名和宽度），再 ",[22,3233,3234],{},"listview_add_text"," 添加行。",[18,3237,3238,3241],{},[22,3239,3240],{},"listview_set_text"," 设置指定行列的文本，用于在多列模式下填充第 2 列及之后的列：",[50,3243,3245],{"className":52,"code":3244,"language":54,"meta":55,"style":55},"listview_add_text(listview, L\"北京\");                    \u002F\u002F 第 0 列（姓名）\nlistview_set_text(listview, 0, 1, L\"beijing@test.com\");  \u002F\u002F 第 1 列（邮箱）\nlistview_set_text(listview, 0, 2, L\"010-1234\");          \u002F\u002F 第 2 列（电话）\n",[22,3246,3247,3252,3257],{"__ignoreMap":55},[59,3248,3249],{"class":61,"line":62},[59,3250,3251],{},"listview_add_text(listview, L\"北京\");                    \u002F\u002F 第 0 列（姓名）\n",[59,3253,3254],{"class":61,"line":88},[59,3255,3256],{},"listview_set_text(listview, 0, 1, L\"beijing@test.com\");  \u002F\u002F 第 1 列（邮箱）\n",[59,3258,3259],{"class":61,"line":134},[59,3260,3261],{},"listview_set_text(listview, 0, 2, L\"010-1234\");          \u002F\u002F 第 2 列（电话）\n",[18,3263,3264,3267,3268,3271],{},[22,3265,3266],{},"listview_count"," 返回当前总行数，",[22,3269,3270],{},"listview_selected_index"," 返回选中行的索引（-1 表示未选中）。",[50,3273,3275],{"className":52,"code":3274,"language":54,"meta":55,"style":55},"void listview_delete_all(HWND listview);\n",[22,3276,3277],{"__ignoreMap":55},[59,3278,3279],{"class":61,"line":62},[59,3280,3274],{},[18,3282,3283],{},"清空列表视图的所有行。",[50,3285,3287],{"className":52,"code":3286,"language":54,"meta":55,"style":55},"void listview_get_text(HWND listview, int row, int col, WCHAR *buf, int size);\n",[22,3288,3289],{"__ignoreMap":55},[59,3290,3291],{"class":61,"line":62},[59,3292,3286],{},[18,3294,3295],{},"读取列表视图指定行列的文本到缓冲区。常用于获取选中行的某列数据（如 PID）。",[50,3297,3299],{"className":52,"code":3298,"language":54,"meta":55,"style":55},"void on_listview_dblclick(HWND listview, void (*callback)(void *), void *data);\n",[22,3300,3301],{"__ignoreMap":55},[59,3302,3303],{"class":61,"line":62},[59,3304,3298],{},[18,3306,3307,3308,3310],{},"注册列表视图的双击回调。双击某行时触发，配合 ",[22,3309,3270],{}," 获取被双击的行。",[432,3312],{},[1020,3314,3315],{"id":3315},"标签页",[50,3317,3319],{"className":52,"code":3318,"language":54,"meta":55,"style":55},"HWND create_tabbar(HWND parent, int x, int y, int w, int h);\nvoid tabbar_add_text(HWND tabbar, const WCHAR *text);\nint tabbar_selected_index(HWND tabbar);\nvoid on_tabbar_select(HWND tabbar, void (*callback)(void *), void *data);\n",[22,3320,3321,3326,3331,3336],{"__ignoreMap":55},[59,3322,3323],{"class":61,"line":62},[59,3324,3325],{},"HWND create_tabbar(HWND parent, int x, int y, int w, int h);\n",[59,3327,3328],{"class":61,"line":88},[59,3329,3330],{},"void tabbar_add_text(HWND tabbar, const WCHAR *text);\n",[59,3332,3333],{"class":61,"line":134},[59,3334,3335],{},"int tabbar_selected_index(HWND tabbar);\n",[59,3337,3338],{"class":61,"line":140},[59,3339,3340],{},"void on_tabbar_select(HWND tabbar, void (*callback)(void *), void *data);\n",[18,3342,3343,3344,3347,3348,3351],{},"创建一个标签页控件。",[22,3345,3346],{},"tabbar_add_text"," 添加标签页，",[22,3349,3350],{},"tabbar_selected_index"," 返回当前选中标签的索引。",[18,3353,3354,3357,3358,3360],{},[22,3355,3356],{},"on_tabbar_select"," 注册标签切换回调。用户点击不同标签页时触发，回调中通过 ",[22,3359,3350],{}," 获取当前选中索引。",[50,3362,3364],{"className":52,"code":3363,"language":54,"meta":55,"style":55},"void on_tab_changed(void *d) {\n    int idx = tabbar_selected_index(g_tab);\n    \u002F\u002F 切换到 idx 对应的面板\n}\non_tabbar_select(g_tab, on_tab_changed, NULL);\n",[22,3365,3366,3371,3376,3381,3385],{"__ignoreMap":55},[59,3367,3368],{"class":61,"line":62},[59,3369,3370],{},"void on_tab_changed(void *d) {\n",[59,3372,3373],{"class":61,"line":88},[59,3374,3375],{},"    int idx = tabbar_selected_index(g_tab);\n",[59,3377,3378],{"class":61,"line":134},[59,3379,3380],{},"    \u002F\u002F 切换到 idx 对应的面板\n",[59,3382,3383],{"class":61,"line":140},[59,3384,149],{},[59,3386,3387],{"class":61,"line":146},[59,3388,3389],{},"on_tabbar_select(g_tab, on_tab_changed, NULL);\n",[432,3391],{},[1020,3393,3394],{"id":3394},"日期选择",[50,3396,3398],{"className":52,"code":3397,"language":54,"meta":55,"style":55},"HWND create_datepicker(HWND parent, int x, int y, int w, int h);\n",[22,3399,3400],{"__ignoreMap":55},[59,3401,3402],{"class":61,"line":62},[59,3403,3397],{},[18,3405,3406],{},"创建一个日期选择器。",[432,3408],{},[1020,3410,3412],{"id":3411},"ip-输入","IP 输入",[50,3414,3416],{"className":52,"code":3415,"language":54,"meta":55,"style":55},"HWND create_ip_input(HWND parent, int x, int y, int w, int h);\nvoid get_ip(HWND ip_input, int ip[4]);\nvoid set_ip(HWND ip_input, int a, int b, int c, int d);\n",[22,3417,3418,3423,3428],{"__ignoreMap":55},[59,3419,3420],{"class":61,"line":62},[59,3421,3422],{},"HWND create_ip_input(HWND parent, int x, int y, int w, int h);\n",[59,3424,3425],{"class":61,"line":88},[59,3426,3427],{},"void get_ip(HWND ip_input, int ip[4]);\n",[59,3429,3430],{"class":61,"line":134},[59,3431,3432],{},"void set_ip(HWND ip_input, int a, int b, int c, int d);\n",[18,3434,3435,3436,3439,3440,3443],{},"创建一个 IP 地址输入控件（4 段式）。",[22,3437,3438],{},"get_ip"," 获取 4 个段，",[22,3441,3442],{},"set_ip"," 设置 IP 地址。",[432,3445],{},[1020,3447,3448],{"id":3448},"动画",[50,3450,3452],{"className":52,"code":3451,"language":54,"meta":55,"style":55},"HWND create_animation(HWND parent, int x, int y, int w, int h);\nvoid play_animation(HWND anim, int res_id);\n",[22,3453,3454,3459],{"__ignoreMap":55},[59,3455,3456],{"class":61,"line":62},[59,3457,3458],{},"HWND create_animation(HWND parent, int x, int y, int w, int h);\n",[59,3460,3461],{"class":61,"line":88},[59,3462,3463],{},"void play_animation(HWND anim, int res_id);\n",[18,3465,3466,3467,3470],{},"创建一个 AVI 动画控件。",[22,3468,3469],{},"play_animation"," 播放资源中指定 ID 的 AVI 文件。",[432,3472],{},[1020,3474,3475],{"id":3475},"富文本",[50,3477,3479],{"className":52,"code":3478,"language":54,"meta":55,"style":55},"HWND create_richedit(HWND parent, int x, int y, int w, int h);\n",[22,3480,3481],{"__ignoreMap":55},[59,3482,3483],{"class":61,"line":62},[59,3484,3478],{},[18,3486,3487,3488,735,3490,2618],{},"创建一个富文本编辑框，支持多行文本、中文和换行。可通过 ",[22,3489,2615],{},[22,3491,2194],{},[432,3493],{},[1020,3495,3497],{"id":3496},"日志面板logview","日志面板（LogView）",[18,3499,3500],{},"基于 ListView 的自定义日志面板组件。三列：时间 | 级别 | 消息，自动管理行数上限，新日志自动滚底。",[50,3502,3504],{"className":52,"code":3503,"language":54,"meta":55,"style":55},"HWND create_logview(HWND parent, int x, int y, int w, int h);\n",[22,3505,3506],{"__ignoreMap":55},[59,3507,3508],{"class":61,"line":62},[59,3509,3503],{},[18,3511,3512,3513,3516],{},"创建日志面板。返回 ListView 句柄，后续 ",[22,3514,3515],{},"logview_info\u002Fwarn\u002Ferror"," 通过该句柄写入日志。",[50,3518,3520],{"className":52,"code":3519,"language":54,"meta":55,"style":55},"void logview_info(HWND hwnd, const WCHAR *fmt, ...);\nvoid logview_warn(HWND hwnd, const WCHAR *fmt, ...);\nvoid logview_error(HWND hwnd, const WCHAR *fmt, ...);\n",[22,3521,3522,3527,3532],{"__ignoreMap":55},[59,3523,3524],{"class":61,"line":62},[59,3525,3526],{},"void logview_info(HWND hwnd, const WCHAR *fmt, ...);\n",[59,3528,3529],{"class":61,"line":88},[59,3530,3531],{},"void logview_warn(HWND hwnd, const WCHAR *fmt, ...);\n",[59,3533,3534],{"class":61,"line":134},[59,3535,3536],{},"void logview_error(HWND hwnd, const WCHAR *fmt, ...);\n",[18,3538,3539,3540,3543,3544,735,3547,735,3550,75],{},"添加日志行，支持 ",[22,3541,3542],{},"printf"," 风格变参格式化。自动附加时间戳，分别标记为 ",[22,3545,3546],{},"INFO",[22,3548,3549],{},"WARN",[22,3551,3552],{},"ERROR",[50,3554,3556],{"className":52,"code":3555,"language":54,"meta":55,"style":55},"void logview_clear(HWND hwnd);\n",[22,3557,3558],{"__ignoreMap":55},[59,3559,3560],{"class":61,"line":62},[59,3561,3555],{},[18,3563,3564],{},"清空面板所有日志行。",[50,3566,3568],{"className":52,"code":3567,"language":54,"meta":55,"style":55},"void logview_set_max_rows(HWND hwnd, int max_rows);\n",[22,3569,3570],{"__ignoreMap":55},[59,3571,3572],{"class":61,"line":62},[59,3573,3567],{},[18,3575,3576],{},"设置最大行数（默认 500）。超出时自动删除最旧行。",[50,3578,3580],{"className":52,"code":3579,"language":54,"meta":55,"style":55},"HWND lv = create_logview(win, 860, 10, 330, 780);\nlogview_info(lv, L\"应用已启动，端口 %d\", 9099);\nlogview_warn(lv, L\"连接超时 (conn_id=%d)\", 5);\nlogview_error(lv, L\"无效 JSON: %s\", raw);\n",[22,3581,3582,3587,3592,3597],{"__ignoreMap":55},[59,3583,3584],{"class":61,"line":62},[59,3585,3586],{},"HWND lv = create_logview(win, 860, 10, 330, 780);\n",[59,3588,3589],{"class":61,"line":88},[59,3590,3591],{},"logview_info(lv, L\"应用已启动，端口 %d\", 9099);\n",[59,3593,3594],{"class":61,"line":134},[59,3595,3596],{},"logview_warn(lv, L\"连接超时 (conn_id=%d)\", 5);\n",[59,3598,3599],{"class":61,"line":140},[59,3600,3601],{},"logview_error(lv, L\"无效 JSON: %s\", raw);\n",[15,3603,3604],{},[18,3605,3606,3608,3609,3612,3613,3616],{},[69,3607,154],{}," LogView 在非 GUI 线程（如后台工作线程）中调用 ",[22,3610,3611],{},"logview_info"," 等函数是安全的。但创建 ",[22,3614,3615],{},"create_logview"," 必须在 GUI 线程中执行。",[432,3618],{},[46,3620,3621],{"id":3621},"对话框",[1020,3623,3624],{"id":3624},"打开文件对话框",[50,3626,3628],{"className":52,"code":3627,"language":54,"meta":55,"style":55},"int open_file_dialog(HWND parent, WCHAR *buf, int size, const WCHAR *filter);\n",[22,3629,3630],{"__ignoreMap":55},[59,3631,3632],{"class":61,"line":62},[59,3633,3627],{},[18,3635,3636,3637,3640,3641,3644,3645,3648,3649,3652],{},"弹出打开文件对话框。",[22,3638,3639],{},"buf"," 接收选中文件的完整路径；",[22,3642,3643],{},"filter"," 为筛选器字符串，格式如 ",[22,3646,3647],{},"L\"文本文件\\0*.txt\\0所有文件\\0*.*\\0\"","（注意用 ",[22,3650,3651],{},"\\0"," 分隔）。成功返回 1，取消返回 0。",[1020,3654,3655],{"id":3655},"保存文件对话框",[50,3657,3659],{"className":52,"code":3658,"language":54,"meta":55,"style":55},"int save_file_dialog(HWND parent, WCHAR *buf, int size, const WCHAR *filter);\n",[22,3660,3661],{"__ignoreMap":55},[59,3662,3663],{"class":61,"line":62},[59,3664,3658],{},[18,3666,3667,3668,3671],{},"弹出保存文件对话框。参数含义同 ",[22,3669,3670],{},"open_file_dialog","，已选择的文件会提示覆盖确认。",[1020,3673,3674],{"id":3674},"浏览目录对话框",[50,3676,3678],{"className":52,"code":3677,"language":54,"meta":55,"style":55},"int browse_folder_dialog(HWND parent, WCHAR *buf, int size);\n",[22,3679,3680],{"__ignoreMap":55},[59,3681,3682],{"class":61,"line":62},[59,3683,3677],{},[18,3685,3686,3687,3689],{},"弹出选择目录对话框。",[22,3688,3639],{}," 接收选中目录的完整路径。成功返回 1，取消返回 0。",[432,3691],{},[46,3693,3694],{"id":3694},"消息框",[1020,3696,3697],{"id":3697},"alert",[50,3699,3701],{"className":52,"code":3700,"language":54,"meta":55,"style":55},"void alert(const WCHAR *title, const WCHAR *message);\n",[22,3702,3703],{"__ignoreMap":55},[59,3704,3705],{"class":61,"line":62},[59,3706,3700],{},[18,3708,3709],{},"弹出仅含\"确定\"按钮的消息框。",[1020,3711,3712],{"id":3712},"confirm",[50,3714,3716],{"className":52,"code":3715,"language":54,"meta":55,"style":55},"int confirm(const WCHAR *title, const WCHAR *message);\n",[22,3717,3718],{"__ignoreMap":55},[59,3719,3720],{"class":61,"line":62},[59,3721,3715],{},[18,3723,3724],{},"弹出\"确定\u002F取消\"对话框，返回 1 表示确定。",[1020,3726,3727],{"id":3727},"ask",[50,3729,3731],{"className":52,"code":3730,"language":54,"meta":55,"style":55},"int ask(const WCHAR *title, const WCHAR *message);\n",[22,3732,3733],{"__ignoreMap":55},[59,3734,3735],{"class":61,"line":62},[59,3736,3730],{},[18,3738,3739],{},"弹出\"是\u002F否\"对话框，返回 1 表示是。",[362,3741,364],{},{"title":55,"searchDepth":88,"depth":88,"links":3743},[3744,3752,3778,3783],{"id":2163,"depth":88,"text":2163,"children":3745},[3746,3747,3748,3749,3750,3751],{"id":2169,"depth":134,"text":2169},{"id":2201,"depth":134,"text":2201},{"id":2240,"depth":134,"text":2240},{"id":2274,"depth":134,"text":2274},{"id":2304,"depth":134,"text":2304},{"id":2368,"depth":134,"text":2368},{"id":2463,"depth":88,"text":2463,"children":3753},[3754,3755,3756,3757,3758,3759,3760,3761,3762,3763,3764,3765,3766,3767,3768,3769,3770,3771,3772,3773,3774,3775,3776,3777],{"id":2466,"depth":134,"text":2466},{"id":2521,"depth":134,"text":2521},{"id":2597,"depth":134,"text":2597},{"id":2623,"depth":134,"text":2623},{"id":2644,"depth":134,"text":2644},{"id":2666,"depth":134,"text":2666},{"id":2706,"depth":134,"text":2706},{"id":2739,"depth":134,"text":2739},{"id":2756,"depth":134,"text":2756},{"id":2801,"depth":134,"text":2801},{"id":2853,"depth":134,"text":2853},{"id":2880,"depth":134,"text":2880},{"id":2916,"depth":134,"text":2916},{"id":2951,"depth":134,"text":2951},{"id":2987,"depth":134,"text":2987},{"id":3029,"depth":134,"text":3029},{"id":3056,"depth":134,"text":3056},{"id":3189,"depth":134,"text":3189},{"id":3315,"depth":134,"text":3315},{"id":3394,"depth":134,"text":3394},{"id":3411,"depth":134,"text":3412},{"id":3448,"depth":134,"text":3448},{"id":3475,"depth":134,"text":3475},{"id":3496,"depth":134,"text":3497},{"id":3621,"depth":88,"text":3621,"children":3779},[3780,3781,3782],{"id":3624,"depth":134,"text":3624},{"id":3655,"depth":134,"text":3655},{"id":3674,"depth":134,"text":3674},{"id":3694,"depth":88,"text":3694,"children":3784},[3785,3786,3787],{"id":3697,"depth":134,"text":3697},{"id":3712,"depth":134,"text":3712},{"id":3727,"depth":134,"text":3727},{},"\u002Fdocs\u002Fapi\u002Feduwin-ctrl-api",{"title":2155,"description":55},"docs\u002Fapi\u002Feduwin-ctrl-api","74iIFztsCLgfBpAklGWFJscJegR5t0hkbKOpFadIibg",{"id":3794,"title":3795,"body":3796,"description":3803,"extension":374,"meta":5024,"navigation":173,"path":5025,"seo":5026,"stem":5027,"__hash__":5028},"content\u002Fdocs\u002Fapi\u002Feduwin-d3d-api.md","eduwin 3D 渲染 API（D3D11）",{"type":8,"value":3797,"toc":5012},[3798,3801,3804,3809,3839,3841,3844,3853,3872,3907,3910,3919,3932,3941,3951,3953,3956,3965,3968,3991,4000,4003,4012,4018,4027,4034,4043,4046,4048,4051,4071,4085,4107,4109,4112,4175,4178,4217,4219,4223,4233,4306,4316,4331,4342,4395,4402,4417,4420,4435,4438,4453,4456,4471,4474,4489,4492,4501,4504,4513,4516,4518,4521,4530,4533,4548,4565,4585,4596,4604,4647,4649,4652,4661,4666,4675,4678,4687,4690,4699,4709,4711,4736,4738,4742,4751,4764,4773,4780,4782,4826,4828,4831,4842,4897,4899,4902,5008,5010],[11,3799,3795],{"id":3800},"eduwin-3d-渲染-apid3d11",[18,3802,3803],{},"eduwin 对 Direct3D 11 的轻量封装。",[18,3805,3806],{},[69,3807,3808],{},"核心约定：",[315,3810,3811,3826,3833,3836],{},[318,3812,3813,3814,3817,3818,3821,3822,3825],{},"矩阵采用 ",[69,3815,3816],{},"row-major 存储","，与 ",[22,3819,3820],{},"D3DMATRIX"," 和 HLSL 的 ",[22,3823,3824],{},"row_major float4x4"," 一致",[318,3827,3828,3829,3832],{},"顶点格式包含",[69,3830,3831],{},"位置 + 法线 + UV + 顶点色","（共 48 字节）",[318,3834,3835],{},"常量缓冲包含世界\u002F视图\u002F投影矩阵 + 光照参数",[318,3837,3838],{},"着色器支持：环境光 + 方向光 + 点光源的漫反射光照",[432,3840],{},[46,3842,3843],{"id":3843},"数据类型",[50,3845,3847],{"className":52,"code":3846,"language":54,"meta":55,"style":55},"typedef struct { float m[16]; } dx_mat4;\n",[22,3848,3849],{"__ignoreMap":55},[59,3850,3851],{"class":61,"line":62},[59,3852,3846],{},[18,3854,3855,3856,3859,3860,620,3863,620,3866,620,3869,75],{},"4×4 矩阵，row-major 存储（",[22,3857,3858],{},"m[row*4+col]","）。例如 ",[22,3861,3862],{},"m[0]=_11",[22,3864,3865],{},"m[1]=_12",[22,3867,3868],{},"m[4]=_21",[22,3870,3871],{},"m[12]=_41",[50,3873,3875],{"className":52,"code":3874,"language":54,"meta":55,"style":55},"typedef struct {\n    float x, y, z;       \u002F\u002F 位置  0-11\n    float nx, ny, nz;    \u002F\u002F 法线  12-23\n    float u, v;          \u002F\u002F UV    24-31\n    float r, g, b, a;    \u002F\u002F 颜色  32-47\n} dx_vertex;\n",[22,3876,3877,3882,3887,3892,3897,3902],{"__ignoreMap":55},[59,3878,3879],{"class":61,"line":62},[59,3880,3881],{},"typedef struct {\n",[59,3883,3884],{"class":61,"line":88},[59,3885,3886],{},"    float x, y, z;       \u002F\u002F 位置  0-11\n",[59,3888,3889],{"class":61,"line":134},[59,3890,3891],{},"    float nx, ny, nz;    \u002F\u002F 法线  12-23\n",[59,3893,3894],{"class":61,"line":140},[59,3895,3896],{},"    float u, v;          \u002F\u002F UV    24-31\n",[59,3898,3899],{"class":61,"line":146},[59,3900,3901],{},"    float r, g, b, a;    \u002F\u002F 颜色  32-47\n",[59,3903,3904],{"class":61,"line":281},[59,3905,3906],{},"} dx_vertex;\n",[18,3908,3909],{},"顶点格式：位置（3 float）+ 法线（3 float）+ UV（2 float）+ 顶点色（4 float），共 48 字节。",[50,3911,3913],{"className":52,"code":3912,"language":54,"meta":55,"style":55},"typedef struct { void *vb; void *ib; int icount; } dx_geometry;\n",[22,3914,3915],{"__ignoreMap":55},[59,3916,3917],{"class":61,"line":62},[59,3918,3912],{},[18,3920,3921,3924,3925,3928,3929,116],{},[22,3922,3923],{},"dx_geometry"," 包含顶点缓冲和索引缓冲。通过 ",[22,3926,3927],{},"dx_create_geometry"," 创建，",[22,3930,3931],{},"dx_free_geometry",[50,3933,3935],{"className":52,"code":3934,"language":54,"meta":55,"style":55},"typedef struct dx_texture_t dx_texture;\n",[22,3936,3937],{"__ignoreMap":55},[59,3938,3939],{"class":61,"line":62},[59,3940,3934],{},[18,3942,3943,3944,2338,3947,3950],{},"纹理是不透明类型，通过 ",[22,3945,3946],{},"dx_create_texture",[22,3948,3949],{},"dx_create_texture_rgba"," 创建。",[432,3952],{},[46,3954,3955],{"id":3955},"生命周期",[50,3957,3959],{"className":52,"code":3958,"language":54,"meta":55,"style":55},"int dx_init(HWND hwnd, int w, int h);\n",[22,3960,3961],{"__ignoreMap":55},[59,3962,3963],{"class":61,"line":62},[59,3964,3958],{},[18,3966,3967],{},"初始化 D3D11。创建 swap chain、编译 shader、创建常量缓冲和光栅化状态。成功返回 1，失败返回 0。",[315,3969,3970,3976,3984],{},[318,3971,3972,3975],{},[22,3973,3974],{},"hwnd"," — 渲染窗口句柄",[318,3977,3978,3980,3981,3983],{},[22,3979,2337],{},", ",[22,3982,2341],{}," — 客户区宽高（像素）",[318,3985,3986,3987,3990],{},"内部自动调用 ",[22,3988,3989],{},"dx_resize"," 创建 render target 和 depth stencil",[50,3992,3994],{"className":52,"code":3993,"language":54,"meta":55,"style":55},"void dx_shutdown(void);\n",[22,3995,3996],{"__ignoreMap":55},[59,3997,3998],{"class":61,"line":62},[59,3999,3993],{},[18,4001,4002],{},"销毁所有 D3D11 资源（rasterizer state、constant buffer、input layout、shaders、render target、depth stencil、swap chain、device context、device）。",[50,4004,4006],{"className":52,"code":4005,"language":54,"meta":55,"style":55},"void dx_begin(float r, float g, float b, float a);\n",[22,4007,4008],{"__ignoreMap":55},[59,4009,4010],{"class":61,"line":62},[59,4011,4005],{},[18,4013,4014,4015],{},"开始一帧渲染。清空 render target 为指定颜色，清空 depth stencil。",[69,4016,4017],{},"每帧必须在所有绘制前调用一次。",[50,4019,4021],{"className":52,"code":4020,"language":54,"meta":55,"style":55},"void dx_end(void);\n",[22,4022,4023],{"__ignoreMap":55},[59,4024,4025],{"class":61,"line":62},[59,4026,4020],{},[18,4028,4029,4030,4033],{},"结束一帧渲染。调用 ",[22,4031,4032],{},"Present(1, 0)"," 交换前后缓冲。",[50,4035,4037],{"className":52,"code":4036,"language":54,"meta":55,"style":55},"void dx_resize(int w, int h);\n",[22,4038,4039],{"__ignoreMap":55},[59,4040,4041],{"class":61,"line":62},[59,4042,4036],{},[18,4044,4045],{},"重建 render target 和 depth stencil。窗口大小变化时调用。",[432,4047],{},[46,4049,4050],{"id":4050},"矩阵设置",[50,4052,4054],{"className":52,"code":4053,"language":54,"meta":55,"style":55},"void dx_set_world(dx_mat4 world);\nvoid dx_set_view(dx_mat4 view);\nvoid dx_set_projection(dx_mat4 proj);\n",[22,4055,4056,4061,4066],{"__ignoreMap":55},[59,4057,4058],{"class":61,"line":62},[59,4059,4060],{},"void dx_set_world(dx_mat4 world);\n",[59,4062,4063],{"class":61,"line":88},[59,4064,4065],{},"void dx_set_view(dx_mat4 view);\n",[59,4067,4068],{"class":61,"line":134},[59,4069,4070],{},"void dx_set_projection(dx_mat4 proj);\n",[18,4072,4073,4074,735,4077,4080,4081,4084],{},"设置世界矩阵、视图矩阵、投影矩阵。在 ",[22,4075,4076],{},"dx_begin",[22,4078,4079],{},"dx_end"," 之间调用，每次 ",[22,4082,4083],{},"dx_draw_indexed"," 前需先设 world。三个矩阵在常量缓冲中的 HLSL 代码路径为：",[50,4086,4090],{"className":4087,"code":4088,"language":4089,"meta":55,"style":55},"language-hlsl shiki shiki-themes github-light github-dark","p = mul(p, mWorld);\np = mul(p, mView);\np = mul(p, mProjection);\n","hlsl",[22,4091,4092,4097,4102],{"__ignoreMap":55},[59,4093,4094],{"class":61,"line":62},[59,4095,4096],{},"p = mul(p, mWorld);\n",[59,4098,4099],{"class":61,"line":88},[59,4100,4101],{},"p = mul(p, mView);\n",[59,4103,4104],{"class":61,"line":134},[59,4105,4106],{},"p = mul(p, mProjection);\n",[432,4108],{},[46,4110,4111],{"id":4111},"相机封装",[50,4113,4115],{"className":52,"code":4114,"language":54,"meta":55,"style":55},"typedef struct {\n    float position[3];   \u002F* 相机位置   camera.position *\u002F\n    float target[3];     \u002F* 观察目标   camera.lookAt *\u002F\n    float up[3];         \u002F* 上方向 *\u002F\n    float fov;           \u002F* 视场角 *\u002F\n    float aspect;        \u002F* 宽高比     width\u002Fheight *\u002F\n    float near_z;        \u002F* 近裁剪面 *\u002F\n    float far_z;         \u002F* 远裁剪面 *\u002F\n} dx_camera;\n\nvoid dx_camera_init(dx_camera *cam, float px, float py, float pz, float aspect);\nvoid dx_camera_update(dx_camera *cam);\n",[22,4116,4117,4121,4126,4131,4136,4141,4146,4151,4156,4161,4165,4170],{"__ignoreMap":55},[59,4118,4119],{"class":61,"line":62},[59,4120,3881],{},[59,4122,4123],{"class":61,"line":88},[59,4124,4125],{},"    float position[3];   \u002F* 相机位置   camera.position *\u002F\n",[59,4127,4128],{"class":61,"line":134},[59,4129,4130],{},"    float target[3];     \u002F* 观察目标   camera.lookAt *\u002F\n",[59,4132,4133],{"class":61,"line":140},[59,4134,4135],{},"    float up[3];         \u002F* 上方向 *\u002F\n",[59,4137,4138],{"class":61,"line":146},[59,4139,4140],{},"    float fov;           \u002F* 视场角 *\u002F\n",[59,4142,4143],{"class":61,"line":281},[59,4144,4145],{},"    float aspect;        \u002F* 宽高比     width\u002Fheight *\u002F\n",[59,4147,4148],{"class":61,"line":286},[59,4149,4150],{},"    float near_z;        \u002F* 近裁剪面 *\u002F\n",[59,4152,4153],{"class":61,"line":292},[59,4154,4155],{},"    float far_z;         \u002F* 远裁剪面 *\u002F\n",[59,4157,4158],{"class":61,"line":600},[59,4159,4160],{},"} dx_camera;\n",[59,4162,4163],{"class":61,"line":917},[59,4164,174],{"emptyLinePlaceholder":173},[59,4166,4167],{"class":61,"line":922},[59,4168,4169],{},"void dx_camera_init(dx_camera *cam, float px, float py, float pz, float aspect);\n",[59,4171,4172],{"class":61,"line":928},[59,4173,4174],{},"void dx_camera_update(dx_camera *cam);\n",[18,4176,4177],{},"用法：",[50,4179,4181],{"className":52,"code":4180,"language":54,"meta":55,"style":55},"dx_camera cam;\ndx_camera_init(&cam, 0, 3, -5, 800.0f \u002F 600.0f);  \u002F\u002F 初始化\ndx_camera_update(&cam);                             \u002F\u002F 每帧更新\n\n\u002F\u002F 窗口 resize 时改 aspect 再 update：\ncam.aspect = 1024.0f \u002F 768.0f;\ndx_camera_update(&cam);\n",[22,4182,4183,4188,4193,4198,4202,4207,4212],{"__ignoreMap":55},[59,4184,4185],{"class":61,"line":62},[59,4186,4187],{},"dx_camera cam;\n",[59,4189,4190],{"class":61,"line":88},[59,4191,4192],{},"dx_camera_init(&cam, 0, 3, -5, 800.0f \u002F 600.0f);  \u002F\u002F 初始化\n",[59,4194,4195],{"class":61,"line":134},[59,4196,4197],{},"dx_camera_update(&cam);                             \u002F\u002F 每帧更新\n",[59,4199,4200],{"class":61,"line":140},[59,4201,174],{"emptyLinePlaceholder":173},[59,4203,4204],{"class":61,"line":146},[59,4205,4206],{},"\u002F\u002F 窗口 resize 时改 aspect 再 update：\n",[59,4208,4209],{"class":61,"line":281},[59,4210,4211],{},"cam.aspect = 1024.0f \u002F 768.0f;\n",[59,4213,4214],{"class":61,"line":286},[59,4215,4216],{},"dx_camera_update(&cam);\n",[432,4218],{},[46,4220,4222],{"id":4221},"网格mesh-geometry-material-transform","网格（Mesh = Geometry + Material + Transform）",[18,4224,4225,4228,4229,4232],{},[22,4226,4227],{},"dx_mesh"," 封装了几何体 + 材质 + 变换，隐藏矩阵操作，仿 Three.js 的 ",[22,4230,4231],{},"Mesh","：",[50,4234,4236],{"className":52,"code":4235,"language":54,"meta":55,"style":55},"typedef struct {\n    dx_geometry *geometry;  \u002F* 顶点数据 *\u002F\n    dx_material *material;  \u002F* 材质（可 NULL） *\u002F\n    float position[3];      \u002F* 平移 *\u002F\n    float rotation[3];      \u002F* 旋转（XYZ 欧拉角） *\u002F\n    float scale[3];         \u002F* 缩放 *\u002F\n} dx_mesh;\n\ndx_mesh* dx_create_mesh(dx_geometry *geo, dx_material *mat);\nvoid     dx_mesh_set_position(dx_mesh *m, float x, float y, float z);\nvoid     dx_mesh_set_rotation(dx_mesh *m, float x, float y, float z);\nvoid     dx_mesh_set_scale(dx_mesh *m, float x, float y, float z);\nvoid     dx_mesh_draw(dx_mesh *m);\nvoid     dx_free_mesh(dx_mesh *m);\n",[22,4237,4238,4242,4247,4252,4257,4262,4267,4272,4276,4281,4286,4291,4296,4301],{"__ignoreMap":55},[59,4239,4240],{"class":61,"line":62},[59,4241,3881],{},[59,4243,4244],{"class":61,"line":88},[59,4245,4246],{},"    dx_geometry *geometry;  \u002F* 顶点数据 *\u002F\n",[59,4248,4249],{"class":61,"line":134},[59,4250,4251],{},"    dx_material *material;  \u002F* 材质（可 NULL） *\u002F\n",[59,4253,4254],{"class":61,"line":140},[59,4255,4256],{},"    float position[3];      \u002F* 平移 *\u002F\n",[59,4258,4259],{"class":61,"line":146},[59,4260,4261],{},"    float rotation[3];      \u002F* 旋转（XYZ 欧拉角） *\u002F\n",[59,4263,4264],{"class":61,"line":281},[59,4265,4266],{},"    float scale[3];         \u002F* 缩放 *\u002F\n",[59,4268,4269],{"class":61,"line":286},[59,4270,4271],{},"} dx_mesh;\n",[59,4273,4274],{"class":61,"line":292},[59,4275,174],{"emptyLinePlaceholder":173},[59,4277,4278],{"class":61,"line":600},[59,4279,4280],{},"dx_mesh* dx_create_mesh(dx_geometry *geo, dx_material *mat);\n",[59,4282,4283],{"class":61,"line":917},[59,4284,4285],{},"void     dx_mesh_set_position(dx_mesh *m, float x, float y, float z);\n",[59,4287,4288],{"class":61,"line":922},[59,4289,4290],{},"void     dx_mesh_set_rotation(dx_mesh *m, float x, float y, float z);\n",[59,4292,4293],{"class":61,"line":928},[59,4294,4295],{},"void     dx_mesh_set_scale(dx_mesh *m, float x, float y, float z);\n",[59,4297,4298],{"class":61,"line":933},[59,4299,4300],{},"void     dx_mesh_draw(dx_mesh *m);\n",[59,4302,4303],{"class":61,"line":939},[59,4304,4305],{},"void     dx_free_mesh(dx_mesh *m);\n",[18,4307,4308,4311,4312,4315],{},[22,4309,4310],{},"dx_mesh_draw"," 内部自动计算 ",[22,4313,4314],{},"world = S × Rx × Ry × Rz × T","，绑定材质，绘制几何体。可直接读写字段：",[50,4317,4319],{"className":52,"code":4318,"language":54,"meta":55,"style":55},"mesh->position[0] = 2;    \u002F* X 位置 *\u002F\nmesh->rotation[1] = 0.5f; \u002F* Y 旋转 *\u002F\n",[22,4320,4321,4326],{"__ignoreMap":55},[59,4322,4323],{"class":61,"line":62},[59,4324,4325],{},"mesh->position[0] = 2;    \u002F* X 位置 *\u002F\n",[59,4327,4328],{"class":61,"line":88},[59,4329,4330],{},"mesh->rotation[1] = 0.5f; \u002F* Y 旋转 *\u002F\n",[18,4332,4333,4334,4337,4338,4341],{},"网格通常由 ",[22,4335,4336],{},"dx_create_mesh"," 作为指针创建，",[22,4339,4340],{},"dx_free_mesh"," 释放（注意：不会释放 geometry\u002Fmaterial，共享引用需调用方管理）。",[50,4343,4345],{"className":52,"code":4344,"language":54,"meta":55,"style":55},"obj.ry = 0.5f;         \u002F\u002F 绕 Y 转\nobj.rx = 0.1f;         \u002F\u002F 绕 X 转\nobj.sx = 2.0f;         \u002F\u002F X 方向放大 2 倍\n---\n\n## 几何体操作\n\n```c\ndx_geometry* dx_create_geometry(dx_vertex *verts, int vcount,\n                                unsigned short *indices, int icount);\n",[22,4346,4347,4352,4357,4362,4367,4371,4376,4380,4385,4390],{"__ignoreMap":55},[59,4348,4349],{"class":61,"line":62},[59,4350,4351],{},"obj.ry = 0.5f;         \u002F\u002F 绕 Y 转\n",[59,4353,4354],{"class":61,"line":88},[59,4355,4356],{},"obj.rx = 0.1f;         \u002F\u002F 绕 X 转\n",[59,4358,4359],{"class":61,"line":134},[59,4360,4361],{},"obj.sx = 2.0f;         \u002F\u002F X 方向放大 2 倍\n",[59,4363,4364],{"class":61,"line":140},[59,4365,4366],{},"---\n",[59,4368,4369],{"class":61,"line":146},[59,4370,174],{"emptyLinePlaceholder":173},[59,4372,4373],{"class":61,"line":281},[59,4374,4375],{},"## 几何体操作\n",[59,4377,4378],{"class":61,"line":286},[59,4379,174],{"emptyLinePlaceholder":173},[59,4381,4382],{"class":61,"line":292},[59,4383,4384],{},"```c\n",[59,4386,4387],{"class":61,"line":600},[59,4388,4389],{},"dx_geometry* dx_create_geometry(dx_vertex *verts, int vcount,\n",[59,4391,4392],{"class":61,"line":917},[59,4393,4394],{},"                                unsigned short *indices, int icount);\n",[18,4396,4397,4398,4401],{},"创建自定义几何体。上传顶点和索引数据到 D3D11 的默认用途（",[22,4399,4400],{},"D3D11_USAGE_DEFAULT","）缓冲。",[50,4403,4405],{"className":52,"code":4404,"language":54,"meta":55,"style":55},"dx_geometry* dx_create_box(float w, float h, float d,\n                           float r, float g, float b, float a);\n",[22,4406,4407,4412],{"__ignoreMap":55},[59,4408,4409],{"class":61,"line":62},[59,4410,4411],{},"dx_geometry* dx_create_box(float w, float h, float d,\n",[59,4413,4414],{"class":61,"line":88},[59,4415,4416],{},"                           float r, float g, float b, float a);\n",[18,4418,4419],{},"创建立方体几何体。自动生成 8 个顶点和 36 个索引（12 个三角形），中心在原点。",[50,4421,4423],{"className":52,"code":4422,"language":54,"meta":55,"style":55},"dx_geometry* dx_create_sphere(float radius,\n                              float r, float g, float b, float a);\n",[22,4424,4425,4430],{"__ignoreMap":55},[59,4426,4427],{"class":61,"line":62},[59,4428,4429],{},"dx_geometry* dx_create_sphere(float radius,\n",[59,4431,4432],{"class":61,"line":88},[59,4433,4434],{},"                              float r, float g, float b, float a);\n",[18,4436,4437],{},"创建 UV 球体几何体（12 环 × 16 段），中心在原点。",[50,4439,4441],{"className":52,"code":4440,"language":54,"meta":55,"style":55},"dx_geometry* dx_create_cylinder(float radius, float height,\n                                float r, float g, float b, float a);\n",[22,4442,4443,4448],{"__ignoreMap":55},[59,4444,4445],{"class":61,"line":62},[59,4446,4447],{},"dx_geometry* dx_create_cylinder(float radius, float height,\n",[59,4449,4450],{"class":61,"line":88},[59,4451,4452],{},"                                float r, float g, float b, float a);\n",[18,4454,4455],{},"创建圆柱体几何体（16 段），中心在原点。",[50,4457,4459],{"className":52,"code":4458,"language":54,"meta":55,"style":55},"dx_geometry* dx_create_pyramid(float w, float h, float d,\n                               float r, float g, float b, float a);\n",[22,4460,4461,4466],{"__ignoreMap":55},[59,4462,4463],{"class":61,"line":62},[59,4464,4465],{},"dx_geometry* dx_create_pyramid(float w, float h, float d,\n",[59,4467,4468],{"class":61,"line":88},[59,4469,4470],{},"                               float r, float g, float b, float a);\n",[18,4472,4473],{},"创建四棱锥几何体（5 顶点 \u002F 6 三角形），中心在原点。",[50,4475,4477],{"className":52,"code":4476,"language":54,"meta":55,"style":55},"dx_geometry* dx_create_plane(float w, float h,\n                             float r, float g, float b, float a);\n",[22,4478,4479,4484],{"__ignoreMap":55},[59,4480,4481],{"class":61,"line":62},[59,4482,4483],{},"dx_geometry* dx_create_plane(float w, float h,\n",[59,4485,4486],{"class":61,"line":88},[59,4487,4488],{},"                             float r, float g, float b, float a);\n",[18,4490,4491],{},"创建平面几何体（4 顶点 \u002F 2 三角形），默认在 XY 平面、面向 +Z 方向、中心在原点。通过旋转可以变成地面（绕 X 转 -90°）或墙面。",[50,4493,4495],{"className":52,"code":4494,"language":54,"meta":55,"style":55},"void dx_draw_geometry(dx_geometry *geo, int count, int start_index, int base_vertex);\n",[22,4496,4497],{"__ignoreMap":55},[59,4498,4499],{"class":61,"line":62},[59,4500,4494],{},[18,4502,4503],{},"绘制几何体。每次调用前必须设置好 world 矩阵。",[50,4505,4507],{"className":52,"code":4506,"language":54,"meta":55,"style":55},"void dx_free_geometry(dx_geometry *geo);\n",[22,4508,4509],{"__ignoreMap":55},[59,4510,4511],{"class":61,"line":62},[59,4512,4506],{},[18,4514,4515],{},"释放几何体的顶点和索引缓冲，并释放几何体本身。",[432,4517],{},[46,4519,4520],{"id":4520},"光照",[50,4522,4524],{"className":52,"code":4523,"language":54,"meta":55,"style":55},"void dx_set_ambient_light(float r, float g, float b, float intensity);\n",[22,4525,4526],{"__ignoreMap":55},[59,4527,4528],{"class":61,"line":62},[59,4529,4523],{},[18,4531,4532],{},"设置环境光颜色和强度（漫反射系数）。环境光均匀照亮所有物体，无方向感。",[50,4534,4536],{"className":52,"code":4535,"language":54,"meta":55,"style":55},"void dx_set_directional_light(float dx, float dy, float dz,\n                               float r, float g, float b, float intensity);\n",[22,4537,4538,4543],{"__ignoreMap":55},[59,4539,4540],{"class":61,"line":62},[59,4541,4542],{},"void dx_set_directional_light(float dx, float dy, float dz,\n",[59,4544,4545],{"class":61,"line":88},[59,4546,4547],{},"                               float r, float g, float b, float intensity);\n",[18,4549,4550,4551,1177,4554,4557,4558,4560,4561,4564],{},"设置方向光。",[22,4552,4553],{},"dx\u002Fdy\u002Fdz",[69,4555,4556],{},"指向光源","的方向向量（会被归一化），",[22,4559,1734],{}," 是颜色，",[22,4562,4563],{},"intensity"," 是强度。",[50,4566,4568],{"className":52,"code":4567,"language":54,"meta":55,"style":55},"void dx_set_point_light(float px, float py, float pz,\n                         float r, float g, float b, float intensity,\n                         float range);\n",[22,4569,4570,4575,4580],{"__ignoreMap":55},[59,4571,4572],{"class":61,"line":62},[59,4573,4574],{},"void dx_set_point_light(float px, float py, float pz,\n",[59,4576,4577],{"class":61,"line":88},[59,4578,4579],{},"                         float r, float g, float b, float intensity,\n",[59,4581,4582],{"class":61,"line":134},[59,4583,4584],{},"                         float range);\n",[18,4586,4587,4588,4591,4592,4595],{},"设置点光源。",[22,4589,4590],{},"px\u002Fpy\u002Fpz"," 是世界坐标位置，",[22,4593,4594],{},"range"," 是衰减距离。",[18,4597,4598,4599,735,4601,4603],{},"光照在 ",[22,4600,4076],{},[22,4602,4079],{}," 之间、绘制之前设置：",[50,4605,4607],{"className":52,"code":4606,"language":54,"meta":55,"style":55},"dx_begin(0.15f, 0.15f, 0.2f, 1);\n\ndx_set_ambient_light(0.4f, 0.4f, 0.4f, 1.0f);\ndx_set_directional_light(0.3f, -1, 0.5f, 1, 1, 1, 1.2f);\ndx_set_point_light(1.5f, 3, 2, 1, 0.8f, 0.6f, 0.8f, 6);\n\ndx_mesh_draw(mesh);\ndx_end();\n",[22,4608,4609,4614,4618,4623,4628,4633,4637,4642],{"__ignoreMap":55},[59,4610,4611],{"class":61,"line":62},[59,4612,4613],{},"dx_begin(0.15f, 0.15f, 0.2f, 1);\n",[59,4615,4616],{"class":61,"line":88},[59,4617,174],{"emptyLinePlaceholder":173},[59,4619,4620],{"class":61,"line":134},[59,4621,4622],{},"dx_set_ambient_light(0.4f, 0.4f, 0.4f, 1.0f);\n",[59,4624,4625],{"class":61,"line":140},[59,4626,4627],{},"dx_set_directional_light(0.3f, -1, 0.5f, 1, 1, 1, 1.2f);\n",[59,4629,4630],{"class":61,"line":146},[59,4631,4632],{},"dx_set_point_light(1.5f, 3, 2, 1, 0.8f, 0.6f, 0.8f, 6);\n",[59,4634,4635],{"class":61,"line":281},[59,4636,174],{"emptyLinePlaceholder":173},[59,4638,4639],{"class":61,"line":286},[59,4640,4641],{},"dx_mesh_draw(mesh);\n",[59,4643,4644],{"class":61,"line":292},[59,4645,4646],{},"dx_end();\n",[432,4648],{},[46,4650,4651],{"id":4651},"纹理",[50,4653,4655],{"className":52,"code":4654,"language":54,"meta":55,"style":55},"dx_texture* dx_create_texture(const char *filename);\n",[22,4656,4657],{"__ignoreMap":55},[59,4658,4659],{"class":61,"line":62},[59,4660,4654],{},[18,4662,4663,4664,75],{},"从 BMP\u002FPNG\u002FJPG 文件创建纹理。返回纹理句柄，失败返回 ",[22,4665,477],{},[50,4667,4669],{"className":52,"code":4668,"language":54,"meta":55,"style":55},"dx_texture* dx_create_texture_rgba(int w, int h, const unsigned char *rgba);\n",[22,4670,4671],{"__ignoreMap":55},[59,4672,4673],{"class":61,"line":62},[59,4674,4668],{},[18,4676,4677],{},"从内存 RGBA 像素数据创建纹理，用于程序生成的纹理。",[50,4679,4681],{"className":52,"code":4680,"language":54,"meta":55,"style":55},"void dx_free_texture(dx_texture *tex);\n",[22,4682,4683],{"__ignoreMap":55},[59,4684,4685],{"class":61,"line":62},[59,4686,4680],{},[18,4688,4689],{},"释放纹理资源。",[50,4691,4693],{"className":52,"code":4692,"language":54,"meta":55,"style":55},"void dx_bind_texture(dx_texture *tex, int slot);\n",[22,4694,4695],{"__ignoreMap":55},[59,4696,4697],{"class":61,"line":62},[59,4698,4692],{},[18,4700,4701,4702,4705,4706,4708],{},"将纹理绑定到着色器的指定插槽（",[22,4703,4704],{},"slot"," 从 0 开始）。绑定后后续绘制的物体会使用该纹理。传 ",[22,4707,477],{}," 可解除绑定。",[18,4710,4177],{},[50,4712,4714],{"className":52,"code":4713,"language":54,"meta":55,"style":55},"dx_texture *tex = dx_create_texture(\"checker.bmp\");\ndx_bind_texture(tex, 0);    \u002F\u002F 绑定到 slot 0\ndx_mesh_draw(mesh);         \u002F\u002F 使用纹理绘制\ndx_bind_texture(NULL, 0);   \u002F\u002F 解绑\n",[22,4715,4716,4721,4726,4731],{"__ignoreMap":55},[59,4717,4718],{"class":61,"line":62},[59,4719,4720],{},"dx_texture *tex = dx_create_texture(\"checker.bmp\");\n",[59,4722,4723],{"class":61,"line":88},[59,4724,4725],{},"dx_bind_texture(tex, 0);    \u002F\u002F 绑定到 slot 0\n",[59,4727,4728],{"class":61,"line":134},[59,4729,4730],{},"dx_mesh_draw(mesh);         \u002F\u002F 使用纹理绘制\n",[59,4732,4733],{"class":61,"line":140},[59,4734,4735],{},"dx_bind_texture(NULL, 0);   \u002F\u002F 解绑\n",[432,4737],{},[46,4739,4741],{"id":4740},"gltf-加载","glTF 加载",[50,4743,4745],{"className":52,"code":4744,"language":54,"meta":55,"style":55},"dx_mesh** dx_load_gltf(const char *path, int *out_count);\n",[22,4746,4747],{"__ignoreMap":55},[59,4748,4749],{"class":61,"line":62},[59,4750,4744],{},[18,4752,4753,4754,4757,4758,4761,4762,75],{},"从 glTF 文件加载网格，返回 ",[22,4755,4756],{},"dx_mesh*"," 数组。",[22,4759,4760],{},"out_count"," 返回网格数量。失败返回 ",[22,4763,477],{},[50,4765,4767],{"className":52,"code":4766,"language":54,"meta":55,"style":55},"void dx_free_geometries(dx_geometry **geos, int count);\n",[22,4768,4769],{"__ignoreMap":55},[59,4770,4771],{"class":61,"line":62},[59,4772,4766],{},[18,4774,4775,4776,4779],{},"释放 ",[22,4777,4778],{},"dx_load_gltf"," 返回的整个网格数组。",[18,4781,4177],{},[50,4783,4785],{"className":52,"code":4784,"language":54,"meta":55,"style":55},"int count;\ndx_geometry **geos = dx_load_gltf(\"model.gltf\", &count);\nif (geos) {\n    for (int i = 0; i \u003C count; i++)\n        dx_mesh *m = dx_create_mesh(geos[i], NULL);\n    \u002F\u002F ...\n    dx_free_geometries(geos, count);\n}\n",[22,4786,4787,4792,4797,4802,4807,4812,4817,4822],{"__ignoreMap":55},[59,4788,4789],{"class":61,"line":62},[59,4790,4791],{},"int count;\n",[59,4793,4794],{"class":61,"line":88},[59,4795,4796],{},"dx_geometry **geos = dx_load_gltf(\"model.gltf\", &count);\n",[59,4798,4799],{"class":61,"line":134},[59,4800,4801],{},"if (geos) {\n",[59,4803,4804],{"class":61,"line":140},[59,4805,4806],{},"    for (int i = 0; i \u003C count; i++)\n",[59,4808,4809],{"class":61,"line":146},[59,4810,4811],{},"        dx_mesh *m = dx_create_mesh(geos[i], NULL);\n",[59,4813,4814],{"class":61,"line":281},[59,4815,4816],{},"    \u002F\u002F ...\n",[59,4818,4819],{"class":61,"line":286},[59,4820,4821],{},"    dx_free_geometries(geos, count);\n",[59,4823,4824],{"class":61,"line":292},[59,4825,149],{},[432,4827],{},[46,4829,4830],{"id":4830},"矩阵数学辅助",[18,4832,4833,4834,4837,4838,4841],{},"所有矩阵函数使用 ",[69,4835,4836],{},"row-major"," 约定，与 ",[22,4839,4840],{},"D3DXMATRIX"," 一致。",[50,4843,4845],{"className":52,"code":4844,"language":54,"meta":55,"style":55},"void dx_mat4_identity(dx_mat4 *m);\nvoid dx_mat4_mul(dx_mat4 *out, const dx_mat4 *a, const dx_mat4 *b);\nvoid dx_mat4_rotation_x(dx_mat4 *m, float angle);\nvoid dx_mat4_rotation_y(dx_mat4 *m, float angle);\nvoid dx_mat4_rotation_z(dx_mat4 *m, float angle);\nvoid dx_mat4_translation(dx_mat4 *m, float x, float y, float z);\nvoid dx_mat4_look_at_lh(dx_mat4 *m, const float eye[3],\n                        const float at[3], const float up[3]);\nvoid dx_mat4_perspective_fov_lh(dx_mat4 *m, float fov_y,\n                                float aspect, float zn, float zf);\n",[22,4846,4847,4852,4857,4862,4867,4872,4877,4882,4887,4892],{"__ignoreMap":55},[59,4848,4849],{"class":61,"line":62},[59,4850,4851],{},"void dx_mat4_identity(dx_mat4 *m);\n",[59,4853,4854],{"class":61,"line":88},[59,4855,4856],{},"void dx_mat4_mul(dx_mat4 *out, const dx_mat4 *a, const dx_mat4 *b);\n",[59,4858,4859],{"class":61,"line":134},[59,4860,4861],{},"void dx_mat4_rotation_x(dx_mat4 *m, float angle);\n",[59,4863,4864],{"class":61,"line":140},[59,4865,4866],{},"void dx_mat4_rotation_y(dx_mat4 *m, float angle);\n",[59,4868,4869],{"class":61,"line":146},[59,4870,4871],{},"void dx_mat4_rotation_z(dx_mat4 *m, float angle);\n",[59,4873,4874],{"class":61,"line":281},[59,4875,4876],{},"void dx_mat4_translation(dx_mat4 *m, float x, float y, float z);\n",[59,4878,4879],{"class":61,"line":286},[59,4880,4881],{},"void dx_mat4_look_at_lh(dx_mat4 *m, const float eye[3],\n",[59,4883,4884],{"class":61,"line":292},[59,4885,4886],{},"                        const float at[3], const float up[3]);\n",[59,4888,4889],{"class":61,"line":600},[59,4890,4891],{},"void dx_mat4_perspective_fov_lh(dx_mat4 *m, float fov_y,\n",[59,4893,4894],{"class":61,"line":917},[59,4895,4896],{},"                                float aspect, float zn, float zf);\n",[432,4898],{},[46,4900,4901],{"id":4901},"内部结构",[4903,4904,4905,4918],"table",{},[4906,4907,4908],"thead",{},[4909,4910,4911,4915],"tr",{},[4912,4913,4914],"th",{},"组件",[4912,4916,4917],{},"说明",[4919,4920,4921,4933,4943,4956,4969,4981,4989],"tbody",{},[4909,4922,4923,4927],{},[4924,4925,4926],"td",{},"Swap Chain",[4924,4928,4929,4930],{},"双缓冲，",[22,4931,4932],{},"DXGI_SWAP_EFFECT_DISCARD",[4909,4934,4935,4938],{},[4924,4936,4937],{},"Feature Level",[4924,4939,4940],{},[22,4941,4942],{},"D3D_FEATURE_LEVEL_11_0",[4909,4944,4945,4948],{},[4924,4946,4947],{},"Shader Model",[4924,4949,4950,735,4953],{},[22,4951,4952],{},"vs_5_0",[22,4954,4955],{},"ps_5_0",[4909,4957,4958,4961],{},[4924,4959,4960],{},"光栅化",[4924,4962,4963,464,4966],{},[22,4964,4965],{},"D3D11_FILL_SOLID",[22,4967,4968],{},"D3D11_CULL_NONE",[4909,4970,4971,4974],{},[4924,4972,4973],{},"常量缓冲",[4924,4975,4976,4977,4980],{},"单个 ",[22,4978,4979],{},"register(b0)","，包含 world \u002F view \u002F projection + 光照参数",[4909,4982,4983,4986],{},[4924,4984,4985],{},"光照模型",[4924,4987,4988],{},"环境光 + 方向光漫反射 + 点光源漫反射衰减",[4909,4990,4991,4994],{},[4924,4992,4993],{},"顶点格式",[4924,4995,4996,464,4999,464,5002,464,5005],{},[22,4997,4998],{},"POSITION",[22,5000,5001],{},"NORMAL",[22,5003,5004],{},"TEXCOORD",[22,5006,5007],{},"COLOR",[432,5009],{},[362,5011,364],{},{"title":55,"searchDepth":88,"depth":88,"links":5013},[5014,5015,5016,5017,5018,5019,5020,5021,5022,5023],{"id":3843,"depth":88,"text":3843},{"id":3955,"depth":88,"text":3955},{"id":4050,"depth":88,"text":4050},{"id":4111,"depth":88,"text":4111},{"id":4221,"depth":88,"text":4222},{"id":4520,"depth":88,"text":4520},{"id":4651,"depth":88,"text":4651},{"id":4740,"depth":88,"text":4741},{"id":4830,"depth":88,"text":4830},{"id":4901,"depth":88,"text":4901},{},"\u002Fdocs\u002Fapi\u002Feduwin-d3d-api",{"title":3795,"description":3803},"docs\u002Fapi\u002Feduwin-d3d-api","A-wZPuJufv31uMaJ8D9Bz9iBY8Bcu6mrDhavI6fAkcM",{"id":5030,"title":5031,"body":5032,"description":5039,"extension":374,"meta":5389,"navigation":173,"path":5390,"seo":5391,"stem":5392,"__hash__":5393},"content\u002Fdocs\u002Fapi\u002Feduwin-i18n-api.md","eduwin 国际化 API",{"type":8,"value":5033,"toc":5383},[5034,5037,5040,5042,5051,5062,5065,5182,5206,5209,5218,5231,5234,5243,5249,5276,5278,5380],[11,5035,5031],{"id":5036},"eduwin-国际化-api",[18,5038,5039],{},"eduwin 提供基于 JSON 文件的轻量多语言支持方案。",[46,5041,48],{"id":48},[50,5043,5045],{"className":52,"code":5044,"language":54,"meta":55,"style":55},"void i18n_init(const WCHAR *json_path);\n",[22,5046,5047],{"__ignoreMap":55},[59,5048,5049],{"class":61,"line":62},[59,5050,5044],{},[18,5052,5053,5054,5057,5058,5061],{},"加载指定路径的 JSON 翻译文件。文件编码为 ",[69,5055,5056],{},"UTF-8","。加载成功后自动读取 ",[22,5059,5060],{},"current"," 字段作为当前语言。",[18,5063,5064],{},"JSON 文件格式：",[50,5066,5070],{"className":5067,"code":5068,"language":5069,"meta":55,"style":55},"language-json shiki shiki-themes github-light github-dark","{\n    \"current\": \"zh\",\n    \"labels\": [\n        {\"id\": \"hello\", \"zh\": \"你好\", \"en\": \"hello\", \"jp\": \"こんにちは\"},\n        {\"id\": \"goodbye\", \"zh\": \"再见\", \"en\": \"goodbye\"}\n    ]\n}\n","json",[22,5071,5072,5077,5091,5099,5143,5173,5178],{"__ignoreMap":55},[59,5073,5074],{"class":61,"line":62},[59,5075,574],{"class":5076},"sVt8B",[59,5078,5079,5082,5085,5088],{"class":61,"line":88},[59,5080,5081],{"class":1926},"    \"current\"",[59,5083,5084],{"class":5076},": ",[59,5086,5087],{"class":1933},"\"zh\"",[59,5089,5090],{"class":5076},",\n",[59,5092,5093,5096],{"class":61,"line":134},[59,5094,5095],{"class":1926},"    \"labels\"",[59,5097,5098],{"class":5076},": [\n",[59,5100,5101,5104,5107,5109,5112,5114,5116,5118,5121,5123,5126,5128,5130,5132,5135,5137,5140],{"class":61,"line":140},[59,5102,5103],{"class":5076},"        {",[59,5105,5106],{"class":1926},"\"id\"",[59,5108,5084],{"class":5076},[59,5110,5111],{"class":1933},"\"hello\"",[59,5113,3980],{"class":5076},[59,5115,5087],{"class":1926},[59,5117,5084],{"class":5076},[59,5119,5120],{"class":1933},"\"你好\"",[59,5122,3980],{"class":5076},[59,5124,5125],{"class":1926},"\"en\"",[59,5127,5084],{"class":5076},[59,5129,5111],{"class":1933},[59,5131,3980],{"class":5076},[59,5133,5134],{"class":1926},"\"jp\"",[59,5136,5084],{"class":5076},[59,5138,5139],{"class":1933},"\"こんにちは\"",[59,5141,5142],{"class":5076},"},\n",[59,5144,5145,5147,5149,5151,5154,5156,5158,5160,5163,5165,5167,5169,5171],{"class":61,"line":146},[59,5146,5103],{"class":5076},[59,5148,5106],{"class":1926},[59,5150,5084],{"class":5076},[59,5152,5153],{"class":1933},"\"goodbye\"",[59,5155,3980],{"class":5076},[59,5157,5087],{"class":1926},[59,5159,5084],{"class":5076},[59,5161,5162],{"class":1933},"\"再见\"",[59,5164,3980],{"class":5076},[59,5166,5125],{"class":1926},[59,5168,5084],{"class":5076},[59,5170,5153],{"class":1933},[59,5172,149],{"class":5076},[59,5174,5175],{"class":61,"line":281},[59,5176,5177],{"class":5076},"    ]\n",[59,5179,5180],{"class":61,"line":286},[59,5181,149],{"class":5076},[315,5183,5184,5189],{},[318,5185,5186,5188],{},[22,5187,5060],{}," — 默认语言代码，与 labels 中各条目的字段名对应",[318,5190,5191,5194,5195],{},[22,5192,5193],{},"labels"," — 翻译词条数组\n",[315,5196,5197,5203],{},[318,5198,5199,5202],{},[22,5200,5201],{},"id"," — 词条唯一标识，程序中通过该 id 引用",[318,5204,5205],{},"其他字段为各语言的翻译文本，字段名即语言代码",[46,5207,5208],{"id":5208},"切换语言",[50,5210,5212],{"className":52,"code":5211,"language":54,"meta":55,"style":55},"void i18n_set_lang(const char *lang);\n",[22,5213,5214],{"__ignoreMap":55},[59,5215,5216],{"class":61,"line":62},[59,5217,5211],{},[18,5219,5220,5221,5224,5225,1086,5227,1086,5229,1090],{},"运行时切换语言，无需重新加载文件。",[22,5222,5223],{},"lang"," 为语言代码（如 ",[22,5226,5125],{},[22,5228,5087],{},[22,5230,5134],{},[46,5232,5233],{"id":5233},"获取翻译",[50,5235,5237],{"className":52,"code":5236,"language":54,"meta":55,"style":55},"const WCHAR *i18n_get_str(const char *id);\n",[22,5238,5239],{"__ignoreMap":55},[59,5240,5241],{"class":61,"line":62},[59,5242,5236],{},[18,5244,5245,5246,5248],{},"按 ",[22,5247,5201],{}," 返回当前语言的翻译文字（宽字符串）。查找规则：",[5250,5251,5252,5260,5263,5270],"ol",{},[318,5253,5254,5255,5257,5258],{},"在 ",[22,5256,5193],{}," 数组中查找匹配的 ",[22,5259,5201],{},[318,5261,5262],{},"找到后读取当前语言对应的字段",[318,5264,5265,5266,5269],{},"如果当前语言字段不存在，回退到 ",[22,5267,5268],{},"zh"," 字段",[318,5271,5272,5273,5275],{},"如果仍不存在，返回 ",[22,5274,5201],{}," 本身",[46,5277,298],{"id":298},[50,5279,5281],{"className":52,"code":5280,"language":54,"meta":55,"style":55},"#include \"eduwin.h\"\n\nint main(void)\n{\n    if (init_app() != 0) return 1;\n\n    \u002F* 加载翻译文件 *\u002F\n    i18n_init(L\"lang.json\");\n\n    HWND win = create_window(i18n_get_str(\"title\"), 400, 200);\n    if (!win) return 1;\n\n    HWND label = create_label(win, i18n_get_str(\"hello\"), 20, 20, 360, 30);\n\n    HWND btn = create_button(win, i18n_get_str(\"switch_en\"), 20, 70, 100, 28);\n    on_button_click(btn, switch_en, NULL);\n\n    show_window(win);\n    run_app();\n    return 0;\n}\n",[22,5282,5283,5288,5292,5297,5301,5306,5310,5315,5320,5324,5329,5334,5338,5343,5347,5352,5357,5361,5366,5371,5375],{"__ignoreMap":55},[59,5284,5285],{"class":61,"line":62},[59,5286,5287],{},"#include \"eduwin.h\"\n",[59,5289,5290],{"class":61,"line":88},[59,5291,174],{"emptyLinePlaceholder":173},[59,5293,5294],{"class":61,"line":134},[59,5295,5296],{},"int main(void)\n",[59,5298,5299],{"class":61,"line":140},[59,5300,574],{},[59,5302,5303],{"class":61,"line":146},[59,5304,5305],{},"    if (init_app() != 0) return 1;\n",[59,5307,5308],{"class":61,"line":281},[59,5309,174],{"emptyLinePlaceholder":173},[59,5311,5312],{"class":61,"line":286},[59,5313,5314],{},"    \u002F* 加载翻译文件 *\u002F\n",[59,5316,5317],{"class":61,"line":292},[59,5318,5319],{},"    i18n_init(L\"lang.json\");\n",[59,5321,5322],{"class":61,"line":600},[59,5323,174],{"emptyLinePlaceholder":173},[59,5325,5326],{"class":61,"line":917},[59,5327,5328],{},"    HWND win = create_window(i18n_get_str(\"title\"), 400, 200);\n",[59,5330,5331],{"class":61,"line":922},[59,5332,5333],{},"    if (!win) return 1;\n",[59,5335,5336],{"class":61,"line":928},[59,5337,174],{"emptyLinePlaceholder":173},[59,5339,5340],{"class":61,"line":933},[59,5341,5342],{},"    HWND label = create_label(win, i18n_get_str(\"hello\"), 20, 20, 360, 30);\n",[59,5344,5345],{"class":61,"line":939},[59,5346,174],{"emptyLinePlaceholder":173},[59,5348,5349],{"class":61,"line":1320},[59,5350,5351],{},"    HWND btn = create_button(win, i18n_get_str(\"switch_en\"), 20, 70, 100, 28);\n",[59,5353,5354],{"class":61,"line":1326},[59,5355,5356],{},"    on_button_click(btn, switch_en, NULL);\n",[59,5358,5359],{"class":61,"line":1331},[59,5360,174],{"emptyLinePlaceholder":173},[59,5362,5363],{"class":61,"line":1336},[59,5364,5365],{},"    show_window(win);\n",[59,5367,5368],{"class":61,"line":1342},[59,5369,5370],{},"    run_app();\n",[59,5372,5373],{"class":61,"line":1348},[59,5374,2087],{},[59,5376,5378],{"class":61,"line":5377},21,[59,5379,149],{},[362,5381,5382],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":55,"searchDepth":88,"depth":88,"links":5384},[5385,5386,5387,5388],{"id":48,"depth":88,"text":48},{"id":5208,"depth":88,"text":5208},{"id":5233,"depth":88,"text":5233},{"id":298,"depth":88,"text":298},{},"\u002Fdocs\u002Fapi\u002Feduwin-i18n-api",{"title":5031,"description":5039},"docs\u002Fapi\u002Feduwin-i18n-api","E0GsjHwtJcBRTaH29oJ6dkD_TlCu46Es3XIveM1sjXE",{"id":5395,"title":5396,"body":5397,"description":55,"extension":374,"meta":5567,"navigation":173,"path":5568,"seo":5569,"stem":5570,"__hash__":5571},"content\u002Fdocs\u002Fapi\u002Feduwin-image-api.md","eduwin 图像 API",{"type":8,"value":5398,"toc":5562},[5399,5402,5405,5414,5429,5432,5452,5454,5457,5472,5479,5489,5518,5520,5523,5532,5551,5560],[11,5400,5396],{"id":5401},"eduwin-图像-api",[46,5403,5404],{"id":5404},"图像加载",[50,5406,5408],{"className":52,"code":5407,"language":54,"meta":55,"style":55},"void *load_image_from_file(const WCHAR *filepath, int *width, int *height);\n",[22,5409,5410],{"__ignoreMap":55},[59,5411,5412],{"class":61,"line":62},[59,5413,5407],{},[18,5415,5416,5417,5420,5421,2730,5424,2342,5427,75],{},"从文件加载图像（bmp \u002F png \u002F jpg 等），返回不透明句柄（实际为 GDI+ 的 ",[22,5418,5419],{},"GpImage*","，对用户透明）。如果不需要尺寸信息，",[22,5422,5423],{},"width",[22,5425,5426],{},"height",[22,5428,477],{},[18,5430,5431],{},"返回的句柄由内部管理，用户不需要手动释放。主要用于需要获得图片原始尺寸或传入画布绘制的场景：",[50,5433,5435],{"className":52,"code":5434,"language":54,"meta":55,"style":55},"int w, h;\nvoid *img = load_image_from_file(L\"photo.png\", &w, &h);\n\u002F\u002F 现在 w \u002F h 是图片的原始像素宽高\n",[22,5436,5437,5442,5447],{"__ignoreMap":55},[59,5438,5439],{"class":61,"line":62},[59,5440,5441],{},"int w, h;\n",[59,5443,5444],{"class":61,"line":88},[59,5445,5446],{},"void *img = load_image_from_file(L\"photo.png\", &w, &h);\n",[59,5448,5449],{"class":61,"line":134},[59,5450,5451],{},"\u002F\u002F 现在 w \u002F h 是图片的原始像素宽高\n",[432,5453],{},[46,5455,5456],{"id":5456},"图片控件",[50,5458,5460],{"className":52,"code":5459,"language":54,"meta":55,"style":55},"HWND create_image(HWND parent, const WCHAR *filepath, int x, int y, int w, int h);\nvoid set_image(HWND img, const WCHAR *filepath);\n",[22,5461,5462,5467],{"__ignoreMap":55},[59,5463,5464],{"class":61,"line":62},[59,5465,5466],{},"HWND create_image(HWND parent, const WCHAR *filepath, int x, int y, int w, int h);\n",[59,5468,5469],{"class":61,"line":88},[59,5470,5471],{},"void set_image(HWND img, const WCHAR *filepath);\n",[18,5473,5474,5475,5478],{},"创建一个图片显示控件。支持 ",[69,5476,5477],{},"bmp \u002F png \u002F jpg \u002F jpeg \u002F gif"," 等格式（内部使用 GDI+ 解码）。图片会自动缩放适配控件区域。",[18,5480,5481,5484,5485,5488],{},[22,5482,5483],{},"create_image"," 在创建时直接加载文件显示；",[22,5486,5487],{},"set_image"," 可在运行时动态切换图片。",[50,5490,5492],{"className":52,"code":5491,"language":54,"meta":55,"style":55},"\u002F\u002F 显示 PNG 图片\nHWND img = create_image(main, L\"photo.png\", 10, 10, 200, 150);\n\n\u002F\u002F 运行时切换为 JPG\nset_image(img, L\"photo.jpg\");\n",[22,5493,5494,5499,5504,5508,5513],{"__ignoreMap":55},[59,5495,5496],{"class":61,"line":62},[59,5497,5498],{},"\u002F\u002F 显示 PNG 图片\n",[59,5500,5501],{"class":61,"line":88},[59,5502,5503],{},"HWND img = create_image(main, L\"photo.png\", 10, 10, 200, 150);\n",[59,5505,5506],{"class":61,"line":134},[59,5507,174],{"emptyLinePlaceholder":173},[59,5509,5510],{"class":61,"line":140},[59,5511,5512],{},"\u002F\u002F 运行时切换为 JPG\n",[59,5514,5515],{"class":61,"line":146},[59,5516,5517],{},"set_image(img, L\"photo.jpg\");\n",[432,5519],{},[46,5521,5522],{"id":5522},"图标控件",[50,5524,5526],{"className":52,"code":5525,"language":54,"meta":55,"style":55},"HWND create_icon(HWND parent, const WCHAR *filepath, int x, int y, int w, int h);\n",[22,5527,5528],{"__ignoreMap":55},[59,5529,5530],{"class":61,"line":62},[59,5531,5525],{},[18,5533,5534,5535,5538,5539,735,5541,5543,5544,735,5547,5550],{},"创建一个图标显示控件，加载 ",[22,5536,5537],{},".ico"," 文件并以系统标准图标大小居中显示。",[22,5540,2337],{},[22,5542,2341],{}," 参数控制控件占位区域的大小，实际图标按系统尺寸（",[22,5545,5546],{},"SM_CXICON",[22,5548,5549],{},"SM_CYICON","）绘制。",[50,5552,5554],{"className":52,"code":5553,"language":54,"meta":55,"style":55},"HWND icon = create_icon(main, L\"app.ico\", 220, 10, 48, 48);\n",[22,5555,5556],{"__ignoreMap":55},[59,5557,5558],{"class":61,"line":62},[59,5559,5553],{},[362,5561,364],{},{"title":55,"searchDepth":88,"depth":88,"links":5563},[5564,5565,5566],{"id":5404,"depth":88,"text":5404},{"id":5456,"depth":88,"text":5456},{"id":5522,"depth":88,"text":5522},{},"\u002Fdocs\u002Fapi\u002Feduwin-image-api",{"title":5396,"description":55},"docs\u002Fapi\u002Feduwin-image-api","EamWpv131y5Y5z1mcqmHX4Vl1PnBimNi4O4ixMiXNSo",{"id":5573,"title":5574,"body":5575,"description":5582,"extension":374,"meta":5985,"navigation":173,"path":5986,"seo":5987,"stem":5988,"__hash__":5989},"content\u002Fdocs\u002Fapi\u002Feduwin-json-api.md","eduwin JSON API",{"type":8,"value":5576,"toc":5977},[5577,5580,5583,5586,5595,5601,5604,5619,5627,5633,5658,5661,5691,5701,5730,5733,5748,5758,5791,5795,5820,5823,5858,5878,5944,5947,5962,5975],[11,5578,5574],{"id":5579},"eduwin-json-api",[18,5581,5582],{},"eduwin 内置 cJSON 引擎，提供简洁的 JSON 解析与构建接口。",[46,5584,5585],{"id":5585},"类型",[50,5587,5589],{"className":52,"code":5588,"language":54,"meta":55,"style":55},"typedef struct cJSON json_t;\n",[22,5590,5591],{"__ignoreMap":55},[59,5592,5593],{"class":61,"line":62},[59,5594,5588],{},[18,5596,5597,5600],{},[22,5598,5599],{},"json_t"," 即 cJSON 结构体，所有 JSON API 均通过此指针操作。",[46,5602,5603],{"id":5603},"解析与释放",[50,5605,5607],{"className":52,"code":5606,"language":54,"meta":55,"style":55},"json_t *json_parse(const char *utf8);\nvoid json_free(json_t *obj);\n",[22,5608,5609,5614],{"__ignoreMap":55},[59,5610,5611],{"class":61,"line":62},[59,5612,5613],{},"json_t *json_parse(const char *utf8);\n",[59,5615,5616],{"class":61,"line":88},[59,5617,5618],{},"void json_free(json_t *obj);\n",[18,5620,5621,5624,5625,75],{},[22,5622,5623],{},"json_parse"," 解析 UTF-8 编码的 JSON 字符串，返回根对象指针。解析失败返回 ",[22,5626,477],{},[18,5628,5629,5632],{},[22,5630,5631],{},"json_free"," 释放整个 JSON 树。解析得到的对象使用完毕后必须调用此函数释放内存。",[50,5634,5636],{"className":52,"code":5635,"language":54,"meta":55,"style":55},"json_t *root = json_parse(str);\nif (!root) { \u002F* 解析失败 *\u002F }\n\u002F* ... 读取数据 ... *\u002F\njson_free(root);\n",[22,5637,5638,5643,5648,5653],{"__ignoreMap":55},[59,5639,5640],{"class":61,"line":62},[59,5641,5642],{},"json_t *root = json_parse(str);\n",[59,5644,5645],{"class":61,"line":88},[59,5646,5647],{},"if (!root) { \u002F* 解析失败 *\u002F }\n",[59,5649,5650],{"class":61,"line":134},[59,5651,5652],{},"\u002F* ... 读取数据 ... *\u002F\n",[59,5654,5655],{"class":61,"line":140},[59,5656,5657],{},"json_free(root);\n",[46,5659,5660],{"id":5660},"读取值",[50,5662,5664],{"className":52,"code":5663,"language":54,"meta":55,"style":55},"int json_get_int(json_t *obj, const char *key);\ndouble json_get_double(json_t *obj, const char *key);\nconst char *json_get_str(json_t *obj, const char *key);\njson_t *json_get_obj(json_t *obj, const char *key);\njson_t *json_get_arr(json_t *obj, const char *key);\n",[22,5665,5666,5671,5676,5681,5686],{"__ignoreMap":55},[59,5667,5668],{"class":61,"line":62},[59,5669,5670],{},"int json_get_int(json_t *obj, const char *key);\n",[59,5672,5673],{"class":61,"line":88},[59,5674,5675],{},"double json_get_double(json_t *obj, const char *key);\n",[59,5677,5678],{"class":61,"line":134},[59,5679,5680],{},"const char *json_get_str(json_t *obj, const char *key);\n",[59,5682,5683],{"class":61,"line":140},[59,5684,5685],{},"json_t *json_get_obj(json_t *obj, const char *key);\n",[59,5687,5688],{"class":61,"line":146},[59,5689,5690],{},"json_t *json_get_arr(json_t *obj, const char *key);\n",[18,5692,5693,5694,735,5696,735,5699,1090],{},"从 JSON 对象中按 key 读取对应字段。key 不存在或类型不匹配时返回默认值（",[22,5695,1821],{},[22,5697,5698],{},"0.0",[22,5700,477],{},[50,5702,5704],{"className":52,"code":5703,"language":54,"meta":55,"style":55},"json_t *root = json_parse(str);\nint score = json_get_int(root, \"score\");\nconst char *name = json_get_str(root, \"name\");\njson_t *child = json_get_obj(root, \"config\");\njson_t *items = json_get_arr(root, \"items\");\n",[22,5705,5706,5710,5715,5720,5725],{"__ignoreMap":55},[59,5707,5708],{"class":61,"line":62},[59,5709,5642],{},[59,5711,5712],{"class":61,"line":88},[59,5713,5714],{},"int score = json_get_int(root, \"score\");\n",[59,5716,5717],{"class":61,"line":134},[59,5718,5719],{},"const char *name = json_get_str(root, \"name\");\n",[59,5721,5722],{"class":61,"line":140},[59,5723,5724],{},"json_t *child = json_get_obj(root, \"config\");\n",[59,5726,5727],{"class":61,"line":146},[59,5728,5729],{},"json_t *items = json_get_arr(root, \"items\");\n",[46,5731,5732],{"id":5732},"数组操作",[50,5734,5736],{"className":52,"code":5735,"language":54,"meta":55,"style":55},"int json_arr_size(json_t *arr);\njson_t *json_arr_get(json_t *arr, int index);\n",[22,5737,5738,5743],{"__ignoreMap":55},[59,5739,5740],{"class":61,"line":62},[59,5741,5742],{},"int json_arr_size(json_t *arr);\n",[59,5744,5745],{"class":61,"line":88},[59,5746,5747],{},"json_t *json_arr_get(json_t *arr, int index);\n",[18,5749,5750,5753,5754,5757],{},[22,5751,5752],{},"json_arr_size"," 返回数组元素个数。",[22,5755,5756],{},"json_arr_get"," 按索引获取数组元素（从 0 开始）。",[50,5759,5761],{"className":52,"code":5760,"language":54,"meta":55,"style":55},"int n = json_arr_size(arr);\nfor (int i = 0; i \u003C n; i++)\n{\n    json_t *item = json_arr_get(arr, i);\n    const char *name = json_get_str(item, \"name\");\n}\n",[22,5762,5763,5768,5773,5777,5782,5787],{"__ignoreMap":55},[59,5764,5765],{"class":61,"line":62},[59,5766,5767],{},"int n = json_arr_size(arr);\n",[59,5769,5770],{"class":61,"line":88},[59,5771,5772],{},"for (int i = 0; i \u003C n; i++)\n",[59,5774,5775],{"class":61,"line":134},[59,5776,574],{},[59,5778,5779],{"class":61,"line":140},[59,5780,5781],{},"    json_t *item = json_arr_get(arr, i);\n",[59,5783,5784],{"class":61,"line":146},[59,5785,5786],{},"    const char *name = json_get_str(item, \"name\");\n",[59,5788,5789],{"class":61,"line":281},[59,5790,149],{},[46,5792,5794],{"id":5793},"构建-json","构建 JSON",[50,5796,5798],{"className":52,"code":5797,"language":54,"meta":55,"style":55},"json_t *json_create_obj(void);\njson_t *json_create_arr(void);\njson_t *json_create_str(const char *val);\njson_t *json_create_num(double val);\n",[22,5799,5800,5805,5810,5815],{"__ignoreMap":55},[59,5801,5802],{"class":61,"line":62},[59,5803,5804],{},"json_t *json_create_obj(void);\n",[59,5806,5807],{"class":61,"line":88},[59,5808,5809],{},"json_t *json_create_arr(void);\n",[59,5811,5812],{"class":61,"line":134},[59,5813,5814],{},"json_t *json_create_str(const char *val);\n",[59,5816,5817],{"class":61,"line":140},[59,5818,5819],{},"json_t *json_create_num(double val);\n",[18,5821,5822],{},"创建空的 JSON 对象或数组。",[50,5824,5826],{"className":52,"code":5825,"language":54,"meta":55,"style":55},"void json_add_int(json_t *obj, const char *key, int val);\nvoid json_add_double(json_t *obj, const char *key, double val);\nvoid json_add_str(json_t *obj, const char *key, const char *val);\nvoid json_add_obj(json_t *obj, const char *key, json_t *val);\nvoid json_add_arr(json_t *obj, const char *key, json_t *val);\nvoid json_arr_add(json_t *arr, json_t *item);\n",[22,5827,5828,5833,5838,5843,5848,5853],{"__ignoreMap":55},[59,5829,5830],{"class":61,"line":62},[59,5831,5832],{},"void json_add_int(json_t *obj, const char *key, int val);\n",[59,5834,5835],{"class":61,"line":88},[59,5836,5837],{},"void json_add_double(json_t *obj, const char *key, double val);\n",[59,5839,5840],{"class":61,"line":134},[59,5841,5842],{},"void json_add_str(json_t *obj, const char *key, const char *val);\n",[59,5844,5845],{"class":61,"line":140},[59,5846,5847],{},"void json_add_obj(json_t *obj, const char *key, json_t *val);\n",[59,5849,5850],{"class":61,"line":146},[59,5851,5852],{},"void json_add_arr(json_t *obj, const char *key, json_t *val);\n",[59,5854,5855],{"class":61,"line":281},[59,5856,5857],{},"void json_arr_add(json_t *arr, json_t *item);\n",[18,5859,5860,5861,2730,5864,5867,5868,5871,5872,5875,5876,75],{},"向对象或数组中添加字段\u002F元素。",[22,5862,5863],{},"json_add_obj",[22,5865,5866],{},"json_add_arr"," 会将传入的 ",[22,5869,5870],{},"json_t*"," 所有权移交给父对象，",[69,5873,5874],{},"不要","再对子对象调用 ",[22,5877,5631],{},[50,5879,5881],{"className":52,"code":5880,"language":54,"meta":55,"style":55},"json_t *root = json_create_obj();\njson_t *arr = json_create_arr();\n\njson_add_str(root, \"name\", \"张三\");\njson_add_int(root, \"age\", 18);\njson_add_arr(root, \"scores\", arr);\n\njson_arr_add(arr, json_create_int(95));\njson_arr_add(arr, json_create_int(88));\n\nchar *out = json_to_str(root);  \u002F* {\"name\":\"张三\",\"age\":18,\"scores\":[95,88]} *\u002F\nfree(out);\njson_free(root);\n",[22,5882,5883,5888,5893,5897,5902,5907,5912,5916,5921,5926,5930,5935,5940],{"__ignoreMap":55},[59,5884,5885],{"class":61,"line":62},[59,5886,5887],{},"json_t *root = json_create_obj();\n",[59,5889,5890],{"class":61,"line":88},[59,5891,5892],{},"json_t *arr = json_create_arr();\n",[59,5894,5895],{"class":61,"line":134},[59,5896,174],{"emptyLinePlaceholder":173},[59,5898,5899],{"class":61,"line":140},[59,5900,5901],{},"json_add_str(root, \"name\", \"张三\");\n",[59,5903,5904],{"class":61,"line":146},[59,5905,5906],{},"json_add_int(root, \"age\", 18);\n",[59,5908,5909],{"class":61,"line":281},[59,5910,5911],{},"json_add_arr(root, \"scores\", arr);\n",[59,5913,5914],{"class":61,"line":286},[59,5915,174],{"emptyLinePlaceholder":173},[59,5917,5918],{"class":61,"line":292},[59,5919,5920],{},"json_arr_add(arr, json_create_int(95));\n",[59,5922,5923],{"class":61,"line":600},[59,5924,5925],{},"json_arr_add(arr, json_create_int(88));\n",[59,5927,5928],{"class":61,"line":917},[59,5929,174],{"emptyLinePlaceholder":173},[59,5931,5932],{"class":61,"line":922},[59,5933,5934],{},"char *out = json_to_str(root);  \u002F* {\"name\":\"张三\",\"age\":18,\"scores\":[95,88]} *\u002F\n",[59,5936,5937],{"class":61,"line":928},[59,5938,5939],{},"free(out);\n",[59,5941,5942],{"class":61,"line":933},[59,5943,5657],{},[46,5945,5946],{"id":5946},"输出为字符串",[50,5948,5950],{"className":52,"code":5949,"language":54,"meta":55,"style":55},"char *json_to_str(json_t *root);\nchar *json_to_str_min(json_t *root);\n",[22,5951,5952,5957],{"__ignoreMap":55},[59,5953,5954],{"class":61,"line":62},[59,5955,5956],{},"char *json_to_str(json_t *root);\n",[59,5958,5959],{"class":61,"line":88},[59,5960,5961],{},"char *json_to_str_min(json_t *root);\n",[18,5963,5964,5967,5968,5971,5972,116],{},[22,5965,5966],{},"json_to_str"," 返回格式化的 JSON 字符串（带缩进换行）。",[22,5969,5970],{},"json_to_str_min"," 返回压缩的 JSON 字符串（无空白字符）。返回的字符串需要调用 ",[22,5973,5974],{},"free()",[362,5976,364],{},{"title":55,"searchDepth":88,"depth":88,"links":5978},[5979,5980,5981,5982,5983,5984],{"id":5585,"depth":88,"text":5585},{"id":5603,"depth":88,"text":5603},{"id":5660,"depth":88,"text":5660},{"id":5732,"depth":88,"text":5732},{"id":5793,"depth":88,"text":5794},{"id":5946,"depth":88,"text":5946},{},"\u002Fdocs\u002Fapi\u002Feduwin-json-api",{"title":5574,"description":5582},"docs\u002Fapi\u002Feduwin-json-api","PC7K1TAeBrLxdxeEjcjntZX5TZWsqciipkJ-P4veziU",{"id":5991,"title":5992,"body":5993,"description":55,"extension":374,"meta":6174,"navigation":173,"path":6175,"seo":6176,"stem":6177,"__hash__":6178},"content\u002Fdocs\u002Fapi\u002Feduwin-logview-api.md","eduwin 日志面板 API",{"type":8,"value":5994,"toc":6168},[5995,5998,6007,6010,6013,6021,6030,6033,6049,6063,6118,6138,6141,6149,6152,6155,6163,6166],[11,5996,5992],{"id":5997},"eduwin-日志面板-api",[15,5999,6000],{},[18,6001,20,6002,25,6005,29],{},[22,6003,6004],{},"eduwin_logview.h",[22,6006,28],{},[18,6008,6009],{},"LogView 是基于 ListView 的日志面板控件，三列显示（时间 \u002F 级别 \u002F 消息），自动限制行数、自动滚动到底部。",[46,6011,6012],{"id":6012},"创建",[50,6014,6015],{"className":52,"code":3503,"language":54,"meta":55,"style":55},[22,6016,6017],{"__ignoreMap":55},[59,6018,6019],{"class":61,"line":62},[59,6020,3503],{},[18,6022,6023,6024,735,6027,4841],{},"创建一个日志面板。参数与 ",[22,6025,6026],{},"create_button",[22,6028,6029],{},"create_label",[46,6031,6032],{"id":6032},"输出日志",[50,6034,6035],{"className":52,"code":3519,"language":54,"meta":55,"style":55},[22,6036,6037,6041,6045],{"__ignoreMap":55},[59,6038,6039],{"class":61,"line":62},[59,6040,3526],{},[59,6042,6043],{"class":61,"line":88},[59,6044,3531],{},[59,6046,6047],{"class":61,"line":134},[59,6048,3536],{},[18,6050,6051,6052,6055,6056,6059,6060,6062],{},"向日志面板追加一行。",[22,6053,6054],{},"fmt"," 和后续参数与 ",[22,6057,6058],{},"str_format"," 一致（类似 ",[22,6061,3542],{},"）。自动附加时间戳和级别标签。",[4903,6064,6065,6078],{},[4906,6066,6067],{},[4909,6068,6069,6072,6075],{},[4912,6070,6071],{},"函数",[4912,6073,6074],{},"级别标签",[4912,6076,6077],{},"颜色",[4919,6079,6080,6092,6105],{},[4909,6081,6082,6086,6089],{},[4924,6083,6084],{},[22,6085,3611],{},[4924,6087,6088],{},"ⓘ",[4924,6090,6091],{},"黑色",[4909,6093,6094,6099,6102],{},[4924,6095,6096],{},[22,6097,6098],{},"logview_warn",[4924,6100,6101],{},"⚠",[4924,6103,6104],{},"橙色",[4909,6106,6107,6112,6115],{},[4924,6108,6109],{},[22,6110,6111],{},"logview_error",[4924,6113,6114],{},"✕",[4924,6116,6117],{},"红色",[50,6119,6121],{"className":52,"code":6120,"language":54,"meta":55,"style":55},"logview_info(g_logview, L\"应用已启动，端口 %d\", 9099);\nlogview_warn(g_logview, L\"连接超时 (conn_id=%d)\", 5);\nlogview_error(g_logview, L\"网络错误\");\n",[22,6122,6123,6128,6133],{"__ignoreMap":55},[59,6124,6125],{"class":61,"line":62},[59,6126,6127],{},"logview_info(g_logview, L\"应用已启动，端口 %d\", 9099);\n",[59,6129,6130],{"class":61,"line":88},[59,6131,6132],{},"logview_warn(g_logview, L\"连接超时 (conn_id=%d)\", 5);\n",[59,6134,6135],{"class":61,"line":134},[59,6136,6137],{},"logview_error(g_logview, L\"网络错误\");\n",[46,6139,6140],{"id":6140},"清空",[50,6142,6143],{"className":52,"code":3555,"language":54,"meta":55,"style":55},[22,6144,6145],{"__ignoreMap":55},[59,6146,6147],{"class":61,"line":62},[59,6148,3555],{},[18,6150,6151],{},"清空日志面板所有行。",[46,6153,6154],{"id":6154},"行数限制",[50,6156,6157],{"className":52,"code":3567,"language":54,"meta":55,"style":55},[22,6158,6159],{"__ignoreMap":55},[59,6160,6161],{"class":61,"line":62},[59,6162,3567],{},[18,6164,6165],{},"设置最大行数（默认 500）。超限时自动删除最旧的行。",[362,6167,364],{},{"title":55,"searchDepth":88,"depth":88,"links":6169},[6170,6171,6172,6173],{"id":6012,"depth":88,"text":6012},{"id":6032,"depth":88,"text":6032},{"id":6140,"depth":88,"text":6140},{"id":6154,"depth":88,"text":6154},{},"\u002Fdocs\u002Fapi\u002Feduwin-logview-api",{"title":5992,"description":55},"docs\u002Fapi\u002Feduwin-logview-api","H1HlMKOjS6mSi52WUejGuQtQ71Fo1pqrZQ_9syFV0DE",{"id":6180,"title":6181,"body":6182,"description":6759,"extension":374,"meta":6760,"navigation":173,"path":6761,"seo":6762,"stem":6763,"__hash__":6764},"content\u002Fdocs\u002Fapi\u002Feduwin-menu-api.md","eduwin 菜单 API",{"type":8,"value":6183,"toc":6750},[6184,6187,6202,6204,6206,6241,6251,6269,6283,6384,6386,6388,6403,6417,6437,6439,6441,6456,6467,6480,6515,6518,6527,6536,6539,6568,6576,6587,6625,6634,6636,6639,6654,6664,6679,6681,6684,6693,6716,6731,6748],[11,6185,6181],{"id":6186},"eduwin-菜单-api",[18,6188,6189,6190,6193,6194,6197,6198,6201],{},"eduwin 支持三种菜单：",[69,6191,6192],{},"主菜单","（窗口菜单栏）、",[69,6195,6196],{},"系统菜单","（标题栏图标菜单）、",[69,6199,6200],{},"上下文菜单","（右键弹出）。",[432,6203],{},[46,6205,6192],{"id":6192},[50,6207,6209],{"className":52,"code":6208,"language":54,"meta":55,"style":55},"HMENU create_main_menu(HWND win);\nHMENU add_main_menu(HMENU main, const WCHAR *text);\nint add_menu_item(HMENU popup, const WCHAR *text);\nvoid add_menu_separator(HMENU popup);\nHMENU add_sub_menu(HMENU parent, const WCHAR *text);\nvoid on_menu_click(int id, void (*callback)(void *), void *data);\n",[22,6210,6211,6216,6221,6226,6231,6236],{"__ignoreMap":55},[59,6212,6213],{"class":61,"line":62},[59,6214,6215],{},"HMENU create_main_menu(HWND win);\n",[59,6217,6218],{"class":61,"line":88},[59,6219,6220],{},"HMENU add_main_menu(HMENU main, const WCHAR *text);\n",[59,6222,6223],{"class":61,"line":134},[59,6224,6225],{},"int add_menu_item(HMENU popup, const WCHAR *text);\n",[59,6227,6228],{"class":61,"line":140},[59,6229,6230],{},"void add_menu_separator(HMENU popup);\n",[59,6232,6233],{"class":61,"line":146},[59,6234,6235],{},"HMENU add_sub_menu(HMENU parent, const WCHAR *text);\n",[59,6237,6238],{"class":61,"line":281},[59,6239,6240],{},"void on_menu_click(int id, void (*callback)(void *), void *data);\n",[18,6242,6243,6246,6247,6250],{},[22,6244,6245],{},"create_main_menu"," 创建菜单栏并附加到窗口，返回菜单栏句柄。",[22,6248,6249],{},"add_main_menu"," 在菜单栏添加一个顶级弹出菜单（如\"文件\"），返回该弹出菜单句柄。",[18,6252,6253,6256,6257,6260,6261,6264,6265,6268],{},[22,6254,6255],{},"add_menu_item"," 向弹出菜单添加可点击的菜单项，返回自动分配的 ID。",[22,6258,6259],{},"add_menu_separator"," 添加分隔线。",[22,6262,6263],{},"add_sub_menu"," 添加子菜单，返回子菜单句柄。",[22,6266,6267],{},"on_menu_click"," 注册点击回调。",[18,6270,6271,6272,6275,6276,6279,6280,1090],{},"菜单项文本中可以用 ",[22,6273,6274],{},"(&F)"," 指定助记符（Alt+F 快捷访问），用 ",[22,6277,6278],{},"\\t"," 分隔快捷键提示文字（仅显示，快捷键注册见 ",[22,6281,6282],{},"set_menu_shortcut",[50,6284,6286],{"className":52,"code":6285,"language":54,"meta":55,"style":55},"HMENU main = create_main_menu(win);\n\nHMENU file = add_main_menu(main, L\"文件(&F)\");\nint id_new  = add_menu_item(file, L\"新建\\tCtrl+N\");\nint id_open = add_menu_item(file, L\"打开\\tCtrl+O\");\nadd_menu_separator(file);\nint id_exit = add_menu_item(file, L\"退出\");\n\non_menu_click(id_new,  on_new,  NULL);\non_menu_click(id_open, on_open, NULL);\non_menu_click(id_exit, on_exit, NULL);\n\n\u002F\u002F 带子菜单的\"视图\"菜单\nHMENU view = add_main_menu(main, L\"视图(&V)\");\nint id_status = add_menu_item(view, L\"状态栏\");\nset_menu_checked(id_status, 1);\n\nHMENU sort_sub = add_sub_menu(view, L\"排序方式\");\nint id_sort_name = add_menu_item(sort_sub, L\"按名称\");\non_menu_click(id_sort_name, on_sort_name, NULL);\n",[22,6287,6288,6293,6297,6302,6307,6312,6317,6322,6326,6331,6336,6341,6345,6350,6355,6360,6365,6369,6374,6379],{"__ignoreMap":55},[59,6289,6290],{"class":61,"line":62},[59,6291,6292],{},"HMENU main = create_main_menu(win);\n",[59,6294,6295],{"class":61,"line":88},[59,6296,174],{"emptyLinePlaceholder":173},[59,6298,6299],{"class":61,"line":134},[59,6300,6301],{},"HMENU file = add_main_menu(main, L\"文件(&F)\");\n",[59,6303,6304],{"class":61,"line":140},[59,6305,6306],{},"int id_new  = add_menu_item(file, L\"新建\\tCtrl+N\");\n",[59,6308,6309],{"class":61,"line":146},[59,6310,6311],{},"int id_open = add_menu_item(file, L\"打开\\tCtrl+O\");\n",[59,6313,6314],{"class":61,"line":281},[59,6315,6316],{},"add_menu_separator(file);\n",[59,6318,6319],{"class":61,"line":286},[59,6320,6321],{},"int id_exit = add_menu_item(file, L\"退出\");\n",[59,6323,6324],{"class":61,"line":292},[59,6325,174],{"emptyLinePlaceholder":173},[59,6327,6328],{"class":61,"line":600},[59,6329,6330],{},"on_menu_click(id_new,  on_new,  NULL);\n",[59,6332,6333],{"class":61,"line":917},[59,6334,6335],{},"on_menu_click(id_open, on_open, NULL);\n",[59,6337,6338],{"class":61,"line":922},[59,6339,6340],{},"on_menu_click(id_exit, on_exit, NULL);\n",[59,6342,6343],{"class":61,"line":928},[59,6344,174],{"emptyLinePlaceholder":173},[59,6346,6347],{"class":61,"line":933},[59,6348,6349],{},"\u002F\u002F 带子菜单的\"视图\"菜单\n",[59,6351,6352],{"class":61,"line":939},[59,6353,6354],{},"HMENU view = add_main_menu(main, L\"视图(&V)\");\n",[59,6356,6357],{"class":61,"line":1320},[59,6358,6359],{},"int id_status = add_menu_item(view, L\"状态栏\");\n",[59,6361,6362],{"class":61,"line":1326},[59,6363,6364],{},"set_menu_checked(id_status, 1);\n",[59,6366,6367],{"class":61,"line":1331},[59,6368,174],{"emptyLinePlaceholder":173},[59,6370,6371],{"class":61,"line":1336},[59,6372,6373],{},"HMENU sort_sub = add_sub_menu(view, L\"排序方式\");\n",[59,6375,6376],{"class":61,"line":1342},[59,6377,6378],{},"int id_sort_name = add_menu_item(sort_sub, L\"按名称\");\n",[59,6380,6381],{"class":61,"line":1348},[59,6382,6383],{},"on_menu_click(id_sort_name, on_sort_name, NULL);\n",[432,6385],{},[46,6387,6196],{"id":6196},[50,6389,6391],{"className":52,"code":6390,"language":54,"meta":55,"style":55},"int create_sys_menu(HWND win, const WCHAR *text);\nvoid add_sys_separator(HWND win);\n",[22,6392,6393,6398],{"__ignoreMap":55},[59,6394,6395],{"class":61,"line":62},[59,6396,6397],{},"int create_sys_menu(HWND win, const WCHAR *text);\n",[59,6399,6400],{"class":61,"line":88},[59,6401,6402],{},"void add_sys_separator(HWND win);\n",[18,6404,6405,6406,6409,6410,6413,6414,6416],{},"向系统菜单（标题栏图标右键菜单）添加自定义项。",[22,6407,6408],{},"create_sys_menu"," 添加一个菜单项并返回自动分配的 ID，",[22,6411,6412],{},"add_sys_separator"," 添加分隔线。回调通过 ",[22,6415,6267],{}," 注册。",[50,6418,6420],{"className":52,"code":6419,"language":54,"meta":55,"style":55},"add_sys_separator(win);\nint id_sys = create_sys_menu(win, L\"关于 (&A)\");\non_menu_click(id_sys, on_sys_about, NULL);\n",[22,6421,6422,6427,6432],{"__ignoreMap":55},[59,6423,6424],{"class":61,"line":62},[59,6425,6426],{},"add_sys_separator(win);\n",[59,6428,6429],{"class":61,"line":88},[59,6430,6431],{},"int id_sys = create_sys_menu(win, L\"关于 (&A)\");\n",[59,6433,6434],{"class":61,"line":134},[59,6435,6436],{},"on_menu_click(id_sys, on_sys_about, NULL);\n",[432,6438],{},[46,6440,6200],{"id":6200},[50,6442,6444],{"className":52,"code":6443,"language":54,"meta":55,"style":55},"HMENU create_context_menu(void);\nvoid show_context_menu(HMENU menu, HWND relative, int x, int y);\n",[22,6445,6446,6451],{"__ignoreMap":55},[59,6447,6448],{"class":61,"line":62},[59,6449,6450],{},"HMENU create_context_menu(void);\n",[59,6452,6453],{"class":61,"line":88},[59,6454,6455],{},"void show_context_menu(HMENU menu, HWND relative, int x, int y);\n",[18,6457,6458,6461,6462,735,6464,6466],{},[22,6459,6460],{},"create_context_menu"," 创建一个空的上下文菜单，",[22,6463,6255],{},[22,6465,6259],{}," 添加菜单项。",[18,6468,6469,6471,6472,6475,6476,6479],{},[22,6470,1184],{}," 在屏幕坐标 ",[22,6473,6474],{},"(x, y)"," 处弹出菜单。",[22,6477,6478],{},"relative"," 用于确定菜单弹出后的归属。",[50,6481,6483],{"className":52,"code":6482,"language":54,"meta":55,"style":55},"HMENU ctx = create_context_menu();\nint id_info   = add_menu_item(ctx, L\"查看详情\");\nadd_menu_separator(ctx);\nint id_delete = add_menu_item(ctx, L\"删除\");\non_menu_click(id_info,   on_ctx_info,   NULL);\non_menu_click(id_delete, on_ctx_delete, NULL);\n",[22,6484,6485,6490,6495,6500,6505,6510],{"__ignoreMap":55},[59,6486,6487],{"class":61,"line":62},[59,6488,6489],{},"HMENU ctx = create_context_menu();\n",[59,6491,6492],{"class":61,"line":88},[59,6493,6494],{},"int id_info   = add_menu_item(ctx, L\"查看详情\");\n",[59,6496,6497],{"class":61,"line":134},[59,6498,6499],{},"add_menu_separator(ctx);\n",[59,6501,6502],{"class":61,"line":140},[59,6503,6504],{},"int id_delete = add_menu_item(ctx, L\"删除\");\n",[59,6506,6507],{"class":61,"line":146},[59,6508,6509],{},"on_menu_click(id_info,   on_ctx_info,   NULL);\n",[59,6511,6512],{"class":61,"line":281},[59,6513,6514],{},"on_menu_click(id_delete, on_ctx_delete, NULL);\n",[1020,6516,6517],{"id":6517},"上下文菜单的坐标处理",[18,6519,6520,6522,6523,6526],{},[22,6521,1184],{}," 要求 ",[69,6524,6525],{},"屏幕坐标","，不同场景下获取坐标的方式不同：",[18,6528,6529],{},[69,6530,6531,6532,6535],{},"普通窗口（",[22,6533,6534],{},"WM_CONTEXTMENU","）：",[18,6537,6538],{},"右键消息自带的坐标已经是屏幕坐标，可直接使用。",[50,6540,6542],{"className":52,"code":6541,"language":54,"meta":55,"style":55},"\u002F\u002F 子类化窗口过程截获 WM_CONTEXTMENU\nif (msg == WM_CONTEXTMENU) {\n    POINT pt = {LOWORD(lp), HIWORD(lp)};\n    show_context_menu(ctx_menu, (HWND)wp, pt.x, pt.y);\n}\n",[22,6543,6544,6549,6554,6559,6564],{"__ignoreMap":55},[59,6545,6546],{"class":61,"line":62},[59,6547,6548],{},"\u002F\u002F 子类化窗口过程截获 WM_CONTEXTMENU\n",[59,6550,6551],{"class":61,"line":88},[59,6552,6553],{},"if (msg == WM_CONTEXTMENU) {\n",[59,6555,6556],{"class":61,"line":134},[59,6557,6558],{},"    POINT pt = {LOWORD(lp), HIWORD(lp)};\n",[59,6560,6561],{"class":61,"line":140},[59,6562,6563],{},"    show_context_menu(ctx_menu, (HWND)wp, pt.x, pt.y);\n",[59,6565,6566],{"class":61,"line":146},[59,6567,149],{},[18,6569,6570],{},[69,6571,6572,6573,6575],{},"画布控件（",[22,6574,1127],{}," 回调）：",[18,6577,6578,6579,1177,6581,6583,6584,6586],{},"画布鼠标回调中的 ",[22,6580,1176],{},[69,6582,1180],{},"，必须用 ",[22,6585,1188],{}," 转换为屏幕坐标：",[50,6588,6590],{"className":52,"code":6589,"language":54,"meta":55,"style":55},"void on_mouse_down(int btn, int x, int y)\n{\n    if (btn != 1) return; \u002F\u002F 只处理右键\n\n    POINT pt = {x, y};\n    ClientToScreen(canvas, &pt);\n    show_context_menu(ctx_menu, canvas, pt.x, pt.y);\n}\n",[22,6591,6592,6596,6600,6605,6609,6613,6617,6621],{"__ignoreMap":55},[59,6593,6594],{"class":61,"line":62},[59,6595,1199],{},[59,6597,6598],{"class":61,"line":88},[59,6599,574],{},[59,6601,6602],{"class":61,"line":134},[59,6603,6604],{},"    if (btn != 1) return; \u002F\u002F 只处理右键\n",[59,6606,6607],{"class":61,"line":140},[59,6608,174],{"emptyLinePlaceholder":173},[59,6610,6611],{"class":61,"line":146},[59,6612,1208],{},[59,6614,6615],{"class":61,"line":281},[59,6616,1213],{},[59,6618,6619],{"class":61,"line":286},[59,6620,1218],{},[59,6622,6623],{"class":61,"line":292},[59,6624,149],{},[15,6626,6627],{},[18,6628,6629,6630,6633],{},"不能直接用 ",[22,6631,6632],{},"GetCursorPos"," 替代，因为右键消息不一定来自鼠标（也可能是键盘 Shift+F10 或触控）。",[432,6635],{},[46,6637,6638],{"id":6638},"菜单状态控制",[50,6640,6642],{"className":52,"code":6641,"language":54,"meta":55,"style":55},"void set_menu_checked(int id, int checked);\nvoid set_menu_enabled(int id, int enabled);\n",[22,6643,6644,6649],{"__ignoreMap":55},[59,6645,6646],{"class":61,"line":62},[59,6647,6648],{},"void set_menu_checked(int id, int checked);\n",[59,6650,6651],{"class":61,"line":88},[59,6652,6653],{},"void set_menu_enabled(int id, int enabled);\n",[18,6655,6656,6659,6660,6663],{},[22,6657,6658],{},"set_menu_checked"," 设置菜单项勾选状态（前面打勾 ✓）。",[22,6661,6662],{},"set_menu_enabled"," 设置菜单项启用\u002F禁用（灰色不可点击）。",[50,6665,6667],{"className":52,"code":6666,"language":54,"meta":55,"style":55},"set_menu_checked(id_status, 1);   \u002F\u002F 勾选\nset_menu_enabled(id_paste, 0);    \u002F\u002F 禁用\n",[22,6668,6669,6674],{"__ignoreMap":55},[59,6670,6671],{"class":61,"line":62},[59,6672,6673],{},"set_menu_checked(id_status, 1);   \u002F\u002F 勾选\n",[59,6675,6676],{"class":61,"line":88},[59,6677,6678],{},"set_menu_enabled(id_paste, 0);    \u002F\u002F 禁用\n",[432,6680],{},[46,6682,6683],{"id":6683},"快捷键",[50,6685,6687],{"className":52,"code":6686,"language":54,"meta":55,"style":55},"void set_menu_shortcut(int id, BYTE vkey, int ctrl, int shift);\n",[22,6688,6689],{"__ignoreMap":55},[59,6690,6691],{"class":61,"line":62},[59,6692,6686],{},[18,6694,6695,6696,6698,6699,1086,6702,1086,6705,6708,6709,2730,6712,6715],{},"为菜单项注册真正的键盘快捷键（加速键）。",[22,6697,1081],{}," 是虚拟键码（如 ",[22,6700,6701],{},"'N'",[22,6703,6704],{},"'Z'",[22,6706,6707],{},"VK_F5","），",[22,6710,6711],{},"ctrl",[22,6713,6714],{},"shift"," 表示是否需要按下 Ctrl \u002F Shift 键。",[50,6717,6719],{"className":52,"code":6718,"language":54,"meta":55,"style":55},"set_menu_shortcut(id_new,  'N', 1, 0);    \u002F\u002F Ctrl+N\nset_menu_shortcut(id_undo, 'Z', 1, 0);   \u002F\u002F Ctrl+Z\n",[22,6720,6721,6726],{"__ignoreMap":55},[59,6722,6723],{"class":61,"line":62},[59,6724,6725],{},"set_menu_shortcut(id_new,  'N', 1, 0);    \u002F\u002F Ctrl+N\n",[59,6727,6728],{"class":61,"line":88},[59,6729,6730],{},"set_menu_shortcut(id_undo, 'Z', 1, 0);   \u002F\u002F Ctrl+Z\n",[15,6732,6733],{},[18,6734,6735,6736,6738,6739,6741,6742,6708,6745,6747],{},"快捷键提示文字需要在 ",[22,6737,6255],{}," 的文本中用 ",[22,6740,6278],{}," 指定（如 ",[22,6743,6744],{},"L\"新建\\tCtrl+N\"",[22,6746,6282],{}," 只负责实际的键盘响应，不修改菜单文字。",[362,6749,364],{},{"title":55,"searchDepth":88,"depth":88,"links":6751},[6752,6753,6754,6757,6758],{"id":6192,"depth":88,"text":6192},{"id":6196,"depth":88,"text":6196},{"id":6200,"depth":88,"text":6200,"children":6755},[6756],{"id":6517,"depth":134,"text":6517},{"id":6638,"depth":88,"text":6638},{"id":6683,"depth":88,"text":6683},"eduwin 支持三种菜单：主菜单（窗口菜单栏）、系统菜单（标题栏图标菜单）、上下文菜单（右键弹出）。",{},"\u002Fdocs\u002Fapi\u002Feduwin-menu-api",{"title":6181,"description":6759},"docs\u002Fapi\u002Feduwin-menu-api","kK6p183lC-eWI7CJfs_tXJh9CgzrONYIfXY1S7Tnkb4",{"id":6766,"title":6767,"body":6768,"description":55,"extension":374,"meta":7663,"navigation":173,"path":7664,"seo":7665,"stem":7666,"__hash__":7667},"content\u002Fdocs\u002Fapi\u002Feduwin-net-api.md","eduwin 网络模块 API",{"type":8,"value":6769,"toc":7645},[6770,6773,6782,6785,6787,6812,6833,6852,6856,6865,6875,6895,6899,6985,7004,7048,7052,7096,7112,7116,7170,7180,7214,7218,7227,7230,7234,7237,7240,7254,7258,7282,7291,7294,7319,7351,7355,7395,7430,7433,7479,7481,7609,7611,7619,7621,7624,7629,7632,7643],[11,6771,6767],{"id":6772},"eduwin-网络模块-api",[15,6774,6775],{},[18,6776,20,6777,25,6780,29],{},[22,6778,6779],{},"eduwin_net.h",[22,6781,28],{},[18,6783,6784],{},"基于 Winsock2 + WinHTTP，零外部依赖。提供 TCP、UDP、HTTP、DNS 四层能力。",[46,6786,1961],{"id":1960},[50,6788,6790],{"className":52,"code":6789,"language":54,"meta":55,"style":55},"int  net_init(void);\nvoid net_cleanup(void);\nconst char* net_error(void);\nvoid net_free(void *ptr);\n",[22,6791,6792,6797,6802,6807],{"__ignoreMap":55},[59,6793,6794],{"class":61,"line":62},[59,6795,6796],{},"int  net_init(void);\n",[59,6798,6799],{"class":61,"line":88},[59,6800,6801],{},"void net_cleanup(void);\n",[59,6803,6804],{"class":61,"line":134},[59,6805,6806],{},"const char* net_error(void);\n",[59,6808,6809],{"class":61,"line":140},[59,6810,6811],{},"void net_free(void *ptr);\n",[18,6813,6814,6815,6817,6818,75,6821,6824,6825,6828,6829,6832],{},"所有网络操作前必须先调 ",[22,6816,74],{},"，退出时调 ",[22,6819,6820],{},"net_cleanup()",[22,6822,6823],{},"net_error()"," 返回最近一次操作的错误描述（仅调试用，值不会 auto-clear）。",[22,6826,6827],{},"net_free()"," 释放模块内部分配的内存（如 ",[22,6830,6831],{},"tcp_peer_addr"," 的返回值）。",[50,6834,6836],{"className":52,"code":6835,"language":54,"meta":55,"style":55},"net_init();\n\u002F\u002F ... 网络操作 ...\nnet_cleanup();\n",[22,6837,6838,6842,6847],{"__ignoreMap":55},[59,6839,6840],{"class":61,"line":62},[59,6841,85],{},[59,6843,6844],{"class":61,"line":88},[59,6845,6846],{},"\u002F\u002F ... 网络操作 ...\n",[59,6848,6849],{"class":61,"line":134},[59,6850,6851],{},"net_cleanup();\n",[46,6853,6855],{"id":6854},"dns-解析","DNS 解析",[50,6857,6859],{"className":52,"code":6858,"language":54,"meta":55,"style":55},"int dns_lookup(const char *hostname, char *ip, int ip_size);\n",[22,6860,6861],{"__ignoreMap":55},[59,6862,6863],{"class":61,"line":62},[59,6864,6858],{},[18,6866,6867,6868,6871,6872,1090],{},"将域名解析为 IPv4 地址字符串。返回 0 成功，",[22,6869,6870],{},"ip"," 中存储点分十进制 IP（如 ",[22,6873,6874],{},"\"202.202.0.100\"",[50,6876,6878],{"className":52,"code":6877,"language":54,"meta":55,"style":55},"char ip[64];\nif (dns_lookup(\"www.xtu.edu.cn\", ip, sizeof(ip)) == 0)\n    printf(\"IP: %s\", ip);  \u002F\u002F 输出: IP: 202.202.0.100\n",[22,6879,6880,6885,6890],{"__ignoreMap":55},[59,6881,6882],{"class":61,"line":62},[59,6883,6884],{},"char ip[64];\n",[59,6886,6887],{"class":61,"line":88},[59,6888,6889],{},"if (dns_lookup(\"www.xtu.edu.cn\", ip, sizeof(ip)) == 0)\n",[59,6891,6892],{"class":61,"line":134},[59,6893,6894],{},"    printf(\"IP: %s\", ip);  \u002F\u002F 输出: IP: 202.202.0.100\n",[46,6896,6898],{"id":6897},"tcp","TCP",[50,6900,6902],{"className":52,"code":6901,"language":54,"meta":55,"style":55},"typedef struct tcp_s tcp_t;\n\n\u002F\u002F 客户端连接\ntcp_t* tcp_connect(const char *host, int port);\n\n\u002F\u002F 服务端监听\ntcp_t* tcp_listen(int port);\ntcp_t* tcp_accept(tcp_t *server);\n\n\u002F\u002F 收发\nint  tcp_send(tcp_t *conn, const char *data, int len);\nint  tcp_recv(tcp_t *conn, char *buf, int buf_size);\nint  tcp_recv_timeout(tcp_t *conn, char *buf, int buf_size, int timeout_ms);\n\n\u002F\u002F 工具\nchar* tcp_peer_addr(tcp_t *conn);  \u002F\u002F 需 net_free\nvoid  tcp_close(tcp_t *conn);\n",[22,6903,6904,6909,6913,6918,6923,6927,6932,6937,6942,6946,6951,6956,6961,6966,6970,6975,6980],{"__ignoreMap":55},[59,6905,6906],{"class":61,"line":62},[59,6907,6908],{},"typedef struct tcp_s tcp_t;\n",[59,6910,6911],{"class":61,"line":88},[59,6912,174],{"emptyLinePlaceholder":173},[59,6914,6915],{"class":61,"line":134},[59,6916,6917],{},"\u002F\u002F 客户端连接\n",[59,6919,6920],{"class":61,"line":140},[59,6921,6922],{},"tcp_t* tcp_connect(const char *host, int port);\n",[59,6924,6925],{"class":61,"line":146},[59,6926,174],{"emptyLinePlaceholder":173},[59,6928,6929],{"class":61,"line":281},[59,6930,6931],{},"\u002F\u002F 服务端监听\n",[59,6933,6934],{"class":61,"line":286},[59,6935,6936],{},"tcp_t* tcp_listen(int port);\n",[59,6938,6939],{"class":61,"line":292},[59,6940,6941],{},"tcp_t* tcp_accept(tcp_t *server);\n",[59,6943,6944],{"class":61,"line":600},[59,6945,174],{"emptyLinePlaceholder":173},[59,6947,6948],{"class":61,"line":917},[59,6949,6950],{},"\u002F\u002F 收发\n",[59,6952,6953],{"class":61,"line":922},[59,6954,6955],{},"int  tcp_send(tcp_t *conn, const char *data, int len);\n",[59,6957,6958],{"class":61,"line":928},[59,6959,6960],{},"int  tcp_recv(tcp_t *conn, char *buf, int buf_size);\n",[59,6962,6963],{"class":61,"line":933},[59,6964,6965],{},"int  tcp_recv_timeout(tcp_t *conn, char *buf, int buf_size, int timeout_ms);\n",[59,6967,6968],{"class":61,"line":939},[59,6969,174],{"emptyLinePlaceholder":173},[59,6971,6972],{"class":61,"line":1320},[59,6973,6974],{},"\u002F\u002F 工具\n",[59,6976,6977],{"class":61,"line":1326},[59,6978,6979],{},"char* tcp_peer_addr(tcp_t *conn);  \u002F\u002F 需 net_free\n",[59,6981,6982],{"class":61,"line":1331},[59,6983,6984],{},"void  tcp_close(tcp_t *conn);\n",[18,6986,6987,40,6990,6993,6994,6997,6998,7000,7001,75],{},[22,6988,6989],{},"tcp_recv_timeout",[22,6991,6992],{},"timeout_ms"," 传 ",[22,6995,6996],{},"-1"," 表示无限等待。",[22,6999,6831],{}," 返回格式 ",[22,7002,7003],{},"\"ip:port\"",[50,7005,7007],{"className":52,"code":7006,"language":54,"meta":55,"style":55},"\u002F\u002F 时间服务器查询示例\ntcp_t *conn = tcp_connect(\"time.nist.gov\", 13);\nif (conn) {\n    char buf[1024] = {0};\n    int n = tcp_recv_timeout(conn, buf, sizeof(buf)-1, 5000);\n    if (n > 0) printf(\"时间: %s\", buf);\n    tcp_close(conn);\n}\n",[22,7008,7009,7014,7019,7024,7029,7034,7039,7044],{"__ignoreMap":55},[59,7010,7011],{"class":61,"line":62},[59,7012,7013],{},"\u002F\u002F 时间服务器查询示例\n",[59,7015,7016],{"class":61,"line":88},[59,7017,7018],{},"tcp_t *conn = tcp_connect(\"time.nist.gov\", 13);\n",[59,7020,7021],{"class":61,"line":134},[59,7022,7023],{},"if (conn) {\n",[59,7025,7026],{"class":61,"line":140},[59,7027,7028],{},"    char buf[1024] = {0};\n",[59,7030,7031],{"class":61,"line":146},[59,7032,7033],{},"    int n = tcp_recv_timeout(conn, buf, sizeof(buf)-1, 5000);\n",[59,7035,7036],{"class":61,"line":281},[59,7037,7038],{},"    if (n > 0) printf(\"时间: %s\", buf);\n",[59,7040,7041],{"class":61,"line":286},[59,7042,7043],{},"    tcp_close(conn);\n",[59,7045,7046],{"class":61,"line":292},[59,7047,149],{},[46,7049,7051],{"id":7050},"udp","UDP",[50,7053,7055],{"className":52,"code":7054,"language":54,"meta":55,"style":55},"typedef struct udp_s udp_t;\n\nudp_t* udp_create(int port);          \u002F\u002F port=0 自动分配\nint  udp_send(udp_t *sock, const char *host, int port,\n                     const char *data, int len);\nint  udp_recv(udp_t *sock, char *buf, int buf_size,\n                     char *src_ip, int *src_port, int timeout_ms);\nvoid udp_close(udp_t *sock);\n",[22,7056,7057,7062,7066,7071,7076,7081,7086,7091],{"__ignoreMap":55},[59,7058,7059],{"class":61,"line":62},[59,7060,7061],{},"typedef struct udp_s udp_t;\n",[59,7063,7064],{"class":61,"line":88},[59,7065,174],{"emptyLinePlaceholder":173},[59,7067,7068],{"class":61,"line":134},[59,7069,7070],{},"udp_t* udp_create(int port);          \u002F\u002F port=0 自动分配\n",[59,7072,7073],{"class":61,"line":140},[59,7074,7075],{},"int  udp_send(udp_t *sock, const char *host, int port,\n",[59,7077,7078],{"class":61,"line":146},[59,7079,7080],{},"                     const char *data, int len);\n",[59,7082,7083],{"class":61,"line":281},[59,7084,7085],{},"int  udp_recv(udp_t *sock, char *buf, int buf_size,\n",[59,7087,7088],{"class":61,"line":286},[59,7089,7090],{},"                     char *src_ip, int *src_port, int timeout_ms);\n",[59,7092,7093],{"class":61,"line":292},[59,7094,7095],{},"void udp_close(udp_t *sock);\n",[18,7097,7098,7101,7102,40,7105,2730,7108,7111],{},[22,7099,7100],{},"udp_create(0)"," 创建绑定到随机端口的 UDP 套接字。",[22,7103,7104],{},"udp_recv",[22,7106,7107],{},"src_ip",[22,7109,7110],{},"src_port"," 可传 NULL 跳过读取。",[46,7113,7115],{"id":7114},"http","HTTP",[50,7117,7119],{"className":52,"code":7118,"language":54,"meta":55,"style":55},"typedef struct http_s http_t;\n\nhttp_t* http_get(const char *url);\nhttp_t* http_post(const char *url, const char *body,\n                                 const char *content_type,\n                                 const char *auth_header);\nint  http_status(http_t *resp);\nconst char* http_body(http_t *resp);\nint  http_body_len(http_t *resp);\nvoid http_free(http_t *resp);\n",[22,7120,7121,7126,7130,7135,7140,7145,7150,7155,7160,7165],{"__ignoreMap":55},[59,7122,7123],{"class":61,"line":62},[59,7124,7125],{},"typedef struct http_s http_t;\n",[59,7127,7128],{"class":61,"line":88},[59,7129,174],{"emptyLinePlaceholder":173},[59,7131,7132],{"class":61,"line":134},[59,7133,7134],{},"http_t* http_get(const char *url);\n",[59,7136,7137],{"class":61,"line":140},[59,7138,7139],{},"http_t* http_post(const char *url, const char *body,\n",[59,7141,7142],{"class":61,"line":146},[59,7143,7144],{},"                                 const char *content_type,\n",[59,7146,7147],{"class":61,"line":281},[59,7148,7149],{},"                                 const char *auth_header);\n",[59,7151,7152],{"class":61,"line":286},[59,7153,7154],{},"int  http_status(http_t *resp);\n",[59,7156,7157],{"class":61,"line":292},[59,7158,7159],{},"const char* http_body(http_t *resp);\n",[59,7161,7162],{"class":61,"line":600},[59,7163,7164],{},"int  http_body_len(http_t *resp);\n",[59,7166,7167],{"class":61,"line":917},[59,7168,7169],{},"void http_free(http_t *resp);\n",[18,7171,7172,7173,2730,7176,7179],{},"支持 HTTPS（通过 WinHTTP）。",[22,7174,7175],{},"content_type",[22,7177,7178],{},"auth_header"," 可传 NULL 跳过。",[50,7181,7183],{"className":52,"code":7182,"language":54,"meta":55,"style":55},"\u002F\u002F 获取公网 IP\nhttp_t *resp = http_get(\"https:\u002F\u002Fhttpbin.org\u002Fip\");\nif (resp) {\n    printf(\"HTTP %d: %s\", http_status(resp), http_body(resp));\n    http_free(resp);\n}\n",[22,7184,7185,7190,7195,7200,7205,7210],{"__ignoreMap":55},[59,7186,7187],{"class":61,"line":62},[59,7188,7189],{},"\u002F\u002F 获取公网 IP\n",[59,7191,7192],{"class":61,"line":88},[59,7193,7194],{},"http_t *resp = http_get(\"https:\u002F\u002Fhttpbin.org\u002Fip\");\n",[59,7196,7197],{"class":61,"line":134},[59,7198,7199],{},"if (resp) {\n",[59,7201,7202],{"class":61,"line":140},[59,7203,7204],{},"    printf(\"HTTP %d: %s\", http_status(resp), http_body(resp));\n",[59,7206,7207],{"class":61,"line":146},[59,7208,7209],{},"    http_free(resp);\n",[59,7211,7212],{"class":61,"line":281},[59,7213,149],{},[46,7215,7217],{"id":7216},"url-编码","URL 编码",[50,7219,7221],{"className":52,"code":7220,"language":54,"meta":55,"style":55},"char* url_encode(const char *str);  \u002F\u002F 需 net_free\n",[22,7222,7223],{"__ignoreMap":55},[59,7224,7225],{"class":61,"line":62},[59,7226,7220],{},[18,7228,7229],{},"对字符串进行 URL 百分号编码。适用于构造自定义 HTTP 查询参数。",[46,7231,7233],{"id":7232},"websocket-hub","WebSocket Hub",[18,7235,7236],{},"基于原生 Winsock WebSocket 协议（RFC 6455）实现的轻量级 pub\u002Fsub 消息枢纽。单线程、无锁、非阻塞轮询。",[1020,7238,7239],{"id":7239},"设计思路",[315,7241,7242,7245,7248,7251],{},[318,7243,7244],{},"服务端监听端口，接受 WS 客户端连接",[318,7246,7247],{},"客户端通过 JSON 指令订阅\u002F取消订阅话题",[318,7249,7250],{},"消息通过话题路由，发送者不会收到自己的消息",[318,7252,7253],{},"所有客户端对等，不分角色",[1020,7255,7257],{"id":7256},"创建-关闭","创建 \u002F 关闭",[50,7259,7261],{"className":52,"code":7260,"language":54,"meta":55,"style":55},"typedef struct ws_hub_s ws_hub_t;\n\nws_hub_t*  ws_hub_create(int port);\nvoid       ws_hub_close(ws_hub_t *hub);\n",[22,7262,7263,7268,7272,7277],{"__ignoreMap":55},[59,7264,7265],{"class":61,"line":62},[59,7266,7267],{},"typedef struct ws_hub_s ws_hub_t;\n",[59,7269,7270],{"class":61,"line":88},[59,7271,174],{"emptyLinePlaceholder":173},[59,7273,7274],{"class":61,"line":134},[59,7275,7276],{},"ws_hub_t*  ws_hub_create(int port);\n",[59,7278,7279],{"class":61,"line":140},[59,7280,7281],{},"void       ws_hub_close(ws_hub_t *hub);\n",[18,7283,7284,7287,7288,1090],{},[22,7285,7286],{},"ws_hub_create"," 在指定端口启动 WS 服务器（",[22,7289,7290],{},"ws:\u002F\u002F",[1020,7292,7293],{"id":7293},"连接管理",[50,7295,7297],{"className":52,"code":7296,"language":54,"meta":55,"style":55},"int  ws_hub_accept(ws_hub_t *hub, int timeout_ms);\nint  ws_hub_count(ws_hub_t *hub);\nint  ws_hub_conn_id(ws_hub_t *hub, int index);\nvoid ws_hub_disconnect(ws_hub_t *hub, int conn_id);\n",[22,7298,7299,7304,7309,7314],{"__ignoreMap":55},[59,7300,7301],{"class":61,"line":62},[59,7302,7303],{},"int  ws_hub_accept(ws_hub_t *hub, int timeout_ms);\n",[59,7305,7306],{"class":61,"line":88},[59,7307,7308],{},"int  ws_hub_count(ws_hub_t *hub);\n",[59,7310,7311],{"class":61,"line":134},[59,7312,7313],{},"int  ws_hub_conn_id(ws_hub_t *hub, int index);\n",[59,7315,7316],{"class":61,"line":140},[59,7317,7318],{},"void ws_hub_disconnect(ws_hub_t *hub, int conn_id);\n",[315,7320,7321,7331,7337,7345],{},[318,7322,7323,7326,7327,7330],{},[22,7324,7325],{},"ws_hub_accept"," — 接受新连接，返回 ",[22,7328,7329],{},"conn_id","（-1=超时\u002F错误）",[318,7332,7333,7336],{},[22,7334,7335],{},"ws_hub_count"," — 当前连接数",[318,7338,7339,7342,7343],{},[22,7340,7341],{},"ws_hub_conn_id"," — 按索引获取 ",[22,7344,7329],{},[318,7346,7347,7350],{},[22,7348,7349],{},"ws_hub_disconnect"," — 断开指定连接",[1020,7352,7354],{"id":7353},"订阅-发布","订阅 \u002F 发布",[50,7356,7358],{"className":52,"code":7357,"language":54,"meta":55,"style":55},"int  ws_hub_subscribe(ws_hub_t *hub, int conn_id, const char *topic);\nint  ws_hub_unsubscribe(ws_hub_t *hub, int conn_id, const char *topic);\nint  ws_hub_publish(ws_hub_t *hub, int sender_id, const char *topic,\n                    const char *data, int len);\nint  ws_hub_recv(ws_hub_t *hub, int conn_id,\n                 char *buf, int buf_size, int timeout_ms);\nint  ws_hub_poll(ws_hub_t *hub, int timeout_ms);\n",[22,7359,7360,7365,7370,7375,7380,7385,7390],{"__ignoreMap":55},[59,7361,7362],{"class":61,"line":62},[59,7363,7364],{},"int  ws_hub_subscribe(ws_hub_t *hub, int conn_id, const char *topic);\n",[59,7366,7367],{"class":61,"line":88},[59,7368,7369],{},"int  ws_hub_unsubscribe(ws_hub_t *hub, int conn_id, const char *topic);\n",[59,7371,7372],{"class":61,"line":134},[59,7373,7374],{},"int  ws_hub_publish(ws_hub_t *hub, int sender_id, const char *topic,\n",[59,7376,7377],{"class":61,"line":140},[59,7378,7379],{},"                    const char *data, int len);\n",[59,7381,7382],{"class":61,"line":146},[59,7383,7384],{},"int  ws_hub_recv(ws_hub_t *hub, int conn_id,\n",[59,7386,7387],{"class":61,"line":281},[59,7388,7389],{},"                 char *buf, int buf_size, int timeout_ms);\n",[59,7391,7392],{"class":61,"line":286},[59,7393,7394],{},"int  ws_hub_poll(ws_hub_t *hub, int timeout_ms);\n",[315,7396,7397,7403,7409,7415,7421],{},[318,7398,7399,7402],{},[22,7400,7401],{},"ws_hub_subscribe"," — 订阅话题（一个连接可订阅多个）",[318,7404,7405,7408],{},[22,7406,7407],{},"ws_hub_unsubscribe"," — 取消订阅",[318,7410,7411,7414],{},[22,7412,7413],{},"ws_hub_publish"," — 向某话题所有订阅者（排除发送者）推送消息",[318,7416,7417,7420],{},[22,7418,7419],{},"ws_hub_recv"," — 接收指定连接的一条消息",[318,7422,7423,7426,7427,7429],{},[22,7424,7425],{},"ws_hub_poll"," — 轮询所有连接，返回有消息待读的 ",[22,7428,7329],{},"（-1=无）",[1020,7431,7432],{"id":7432},"限制",[4903,7434,7435,7445],{},[4906,7436,7437],{},[4909,7438,7439,7442],{},[4912,7440,7441],{},"项",[4912,7443,7444],{},"上限",[4919,7446,7447,7455,7463,7471],{},[4909,7448,7449,7452],{},[4924,7450,7451],{},"最大连接数",[4924,7453,7454],{},"64",[4909,7456,7457,7460],{},[4924,7458,7459],{},"话题名长度",[4924,7461,7462],{},"64 字符",[4909,7464,7465,7468],{},[4924,7466,7467],{},"每连接可订阅话题数",[4924,7469,7470],{},"16",[4909,7472,7473,7476],{},[4924,7474,7475],{},"单条消息最大长度",[4924,7477,7478],{},"16 KB",[1020,7480,1890],{"id":1890},[50,7482,7484],{"className":52,"code":7483,"language":54,"meta":55,"style":55},"ws_hub_t *hub = ws_hub_create(9099);\nif (!hub) { \u002F* 错误处理 *\u002F }\n\nwhile (g_running) {\n    \u002F* 接受新连接 *\u002F\n    int cid = ws_hub_accept(hub, 200);\n    if (cid >= 0) {\n        ws_hub_subscribe(hub, cid, \"agent\");\n        ws_hub_subscribe(hub, cid, \"js\");\n    }\n\n    \u002F* 轮询消息 *\u002F\n    int ready = ws_hub_poll(hub, 200);\n    if (ready \u003C 0) continue;\n\n    char buf[16384];\n    int len = ws_hub_recv(hub, ready, buf, sizeof(buf) - 1, 0);\n    if (len \u003C= 0) { ws_hub_disconnect(hub, ready); continue; }\n    buf[len] = '\\0';\n\n    \u002F* 解析 JSON，按 topic 发布 *\u002F\n    \u002F\u002F ws_hub_publish(hub, ready, topic, buf, len);\n}\n\nws_hub_close(hub);\n",[22,7485,7486,7491,7496,7500,7505,7510,7515,7520,7525,7530,7535,7539,7544,7549,7554,7558,7563,7568,7573,7578,7582,7587,7593,7598,7603],{"__ignoreMap":55},[59,7487,7488],{"class":61,"line":62},[59,7489,7490],{},"ws_hub_t *hub = ws_hub_create(9099);\n",[59,7492,7493],{"class":61,"line":88},[59,7494,7495],{},"if (!hub) { \u002F* 错误处理 *\u002F }\n",[59,7497,7498],{"class":61,"line":134},[59,7499,174],{"emptyLinePlaceholder":173},[59,7501,7502],{"class":61,"line":140},[59,7503,7504],{},"while (g_running) {\n",[59,7506,7507],{"class":61,"line":146},[59,7508,7509],{},"    \u002F* 接受新连接 *\u002F\n",[59,7511,7512],{"class":61,"line":281},[59,7513,7514],{},"    int cid = ws_hub_accept(hub, 200);\n",[59,7516,7517],{"class":61,"line":286},[59,7518,7519],{},"    if (cid >= 0) {\n",[59,7521,7522],{"class":61,"line":292},[59,7523,7524],{},"        ws_hub_subscribe(hub, cid, \"agent\");\n",[59,7526,7527],{"class":61,"line":600},[59,7528,7529],{},"        ws_hub_subscribe(hub, cid, \"js\");\n",[59,7531,7532],{"class":61,"line":917},[59,7533,7534],{},"    }\n",[59,7536,7537],{"class":61,"line":922},[59,7538,174],{"emptyLinePlaceholder":173},[59,7540,7541],{"class":61,"line":928},[59,7542,7543],{},"    \u002F* 轮询消息 *\u002F\n",[59,7545,7546],{"class":61,"line":933},[59,7547,7548],{},"    int ready = ws_hub_poll(hub, 200);\n",[59,7550,7551],{"class":61,"line":939},[59,7552,7553],{},"    if (ready \u003C 0) continue;\n",[59,7555,7556],{"class":61,"line":1320},[59,7557,174],{"emptyLinePlaceholder":173},[59,7559,7560],{"class":61,"line":1326},[59,7561,7562],{},"    char buf[16384];\n",[59,7564,7565],{"class":61,"line":1331},[59,7566,7567],{},"    int len = ws_hub_recv(hub, ready, buf, sizeof(buf) - 1, 0);\n",[59,7569,7570],{"class":61,"line":1336},[59,7571,7572],{},"    if (len \u003C= 0) { ws_hub_disconnect(hub, ready); continue; }\n",[59,7574,7575],{"class":61,"line":1342},[59,7576,7577],{},"    buf[len] = '\\0';\n",[59,7579,7580],{"class":61,"line":1348},[59,7581,174],{"emptyLinePlaceholder":173},[59,7583,7584],{"class":61,"line":5377},[59,7585,7586],{},"    \u002F* 解析 JSON，按 topic 发布 *\u002F\n",[59,7588,7590],{"class":61,"line":7589},22,[59,7591,7592],{},"    \u002F\u002F ws_hub_publish(hub, ready, topic, buf, len);\n",[59,7594,7596],{"class":61,"line":7595},23,[59,7597,149],{},[59,7599,7601],{"class":61,"line":7600},24,[59,7602,174],{"emptyLinePlaceholder":173},[59,7604,7606],{"class":61,"line":7605},25,[59,7607,7608],{},"ws_hub_close(hub);\n",[46,7610,298],{"id":298},[18,7612,301,7613,1086,7616,75],{},[22,7614,7615],{},"samples\u002Fnetwork_info_demo\u002F",[22,7617,7618],{},"samples\u002Fai_browser_demo\u002F",[46,7620,346],{"id":346},[18,7622,7623],{},"链接时需要额外两个库：",[50,7625,7627],{"className":7626,"code":357,"language":358},[356],[22,7628,357],{"__ignoreMap":55},[18,7630,7631],{},"对应的 build.bat 模板：",[50,7633,7637],{"className":7634,"code":7635,"language":7636,"meta":55,"style":55},"language-bat shiki shiki-themes github-light github-dark","cl main.c \u002FFe:demo.exe \u002Flink eduwin.lib user32.lib gdi32.lib gdiplus.lib comctl32.lib comdlg32.lib shell32.lib winmm.lib ws2_32.lib winhttp.lib\n","bat",[22,7638,7639],{"__ignoreMap":55},[59,7640,7641],{"class":61,"line":62},[59,7642,7635],{},[362,7644,364],{},{"title":55,"searchDepth":88,"depth":88,"links":7646},[7647,7648,7649,7650,7651,7652,7653,7661,7662],{"id":1960,"depth":88,"text":1961},{"id":6854,"depth":88,"text":6855},{"id":6897,"depth":88,"text":6898},{"id":7050,"depth":88,"text":7051},{"id":7114,"depth":88,"text":7115},{"id":7216,"depth":88,"text":7217},{"id":7232,"depth":88,"text":7233,"children":7654},[7655,7656,7657,7658,7659,7660],{"id":7239,"depth":134,"text":7239},{"id":7256,"depth":134,"text":7257},{"id":7293,"depth":134,"text":7293},{"id":7353,"depth":134,"text":7354},{"id":7432,"depth":134,"text":7432},{"id":1890,"depth":134,"text":1890},{"id":298,"depth":88,"text":298},{"id":346,"depth":88,"text":346},{},"\u002Fdocs\u002Fapi\u002Feduwin-net-api",{"title":6767,"description":55},"docs\u002Fapi\u002Feduwin-net-api","tHJg0snz2RY3jy9c6j75m2mlI8Z1Z6EaLuzeyoPMc48",{"id":7669,"title":7670,"body":7671,"description":7678,"extension":374,"meta":7767,"navigation":173,"path":7768,"seo":7769,"stem":7770,"__hash__":7771},"content\u002Fdocs\u002Fapi\u002Feduwin-sound-api.md","eduwin 音效 API",{"type":8,"value":7672,"toc":7765},[7673,7676,7679,7704,7738,7763],[11,7674,7670],{"id":7675},"eduwin-音效-api",[18,7677,7678],{},"eduwin 使用 Windows 的 MCI（Media Control Interface）播放音频，支持常见的音频格式（wav、mp3 等）。",[50,7680,7682],{"className":52,"code":7681,"language":54,"meta":55,"style":55},"int play_sound(const WCHAR *filepath);\nint play_sound_loop(const WCHAR *filepath);\nint stop_sound(void);\nint play_beep(int freq, int duration);\n",[22,7683,7684,7689,7694,7699],{"__ignoreMap":55},[59,7685,7686],{"class":61,"line":62},[59,7687,7688],{},"int play_sound(const WCHAR *filepath);\n",[59,7690,7691],{"class":61,"line":88},[59,7692,7693],{},"int play_sound_loop(const WCHAR *filepath);\n",[59,7695,7696],{"class":61,"line":134},[59,7697,7698],{},"int stop_sound(void);\n",[59,7700,7701],{"class":61,"line":140},[59,7702,7703],{},"int play_beep(int freq, int duration);\n",[315,7705,7706,7712,7718,7724],{},[318,7707,7708,7711],{},[22,7709,7710],{},"play_sound"," 播放音频文件（异步，不阻塞程序），成功返回 1，失败返回 0",[318,7713,7714,7717],{},[22,7715,7716],{},"play_sound_loop"," 循环播放音频文件，成功返回 1",[318,7719,7720,7723],{},[22,7721,7722],{},"stop_sound"," 停止当前正在播放的音频",[318,7725,7726,7729,7730,7733,7734,7737],{},[22,7727,7728],{},"play_beep"," 通过 PC 喇叭播放指定频率和时长的蜂鸣声，",[22,7731,7732],{},"freq"," 为频率（Hz），",[22,7735,7736],{},"duration"," 为时长（毫秒）",[50,7739,7741],{"className":52,"code":7740,"language":54,"meta":55,"style":55},"play_sound(L\"hit.wav\");             \u002F\u002F 播放音效\nplay_sound_loop(L\"bgm.mp3\");        \u002F\u002F 循环播放背景音乐\nstop_sound();                       \u002F\u002F 停止所有声音\nplay_beep(1000, 200);               \u002F\u002F 蜂鸣 1000Hz、200ms\n",[22,7742,7743,7748,7753,7758],{"__ignoreMap":55},[59,7744,7745],{"class":61,"line":62},[59,7746,7747],{},"play_sound(L\"hit.wav\");             \u002F\u002F 播放音效\n",[59,7749,7750],{"class":61,"line":88},[59,7751,7752],{},"play_sound_loop(L\"bgm.mp3\");        \u002F\u002F 循环播放背景音乐\n",[59,7754,7755],{"class":61,"line":134},[59,7756,7757],{},"stop_sound();                       \u002F\u002F 停止所有声音\n",[59,7759,7760],{"class":61,"line":140},[59,7761,7762],{},"play_beep(1000, 200);               \u002F\u002F 蜂鸣 1000Hz、200ms\n",[362,7764,364],{},{"title":55,"searchDepth":88,"depth":88,"links":7766},[],{},"\u002Fdocs\u002Fapi\u002Feduwin-sound-api",{"title":7670,"description":7678},"docs\u002Fapi\u002Feduwin-sound-api","F-4Fb0tHtHM5SJlVeHintFBuK-Ubmfnup8BiPRMyTIs",{"id":7773,"title":7774,"body":7775,"description":55,"extension":374,"meta":8236,"navigation":173,"path":8237,"seo":8238,"stem":8239,"__hash__":8240},"content\u002Fdocs\u002Fapi\u002Feduwin-thread-api.md","eduwin 线程模块 API",{"type":8,"value":7776,"toc":8229},[7777,7780,7789,7802,7804,7819,7836,7866,7869,7913,7926,7949,7955,8003,8007,8016,8025,8030,8036,8043,8085,8088,8127,8134,8215,8217,8227],[11,7778,7774],{"id":7779},"eduwin-线程模块-api",[15,7781,7782],{},[18,7783,20,7784,25,7787,29],{},[22,7785,7786],{},"eduwin_thread.h",[22,7788,28],{},[18,7790,7791,7792,7798,7799,7801],{},"封装 Win32 线程创建、互斥锁、线程安全 UI 更新。",[69,7793,7794,7795],{},"核心亮点：",[22,7796,7797],{},"execute_on_main()"," 隐藏了 ",[22,7800,244],{}," + 隐藏窗口调度的细节，任意线程中调用即可安全更新 UI。",[46,7803,1961],{"id":1960},[50,7805,7807],{"className":52,"code":7806,"language":54,"meta":55,"style":55},"int  thread_init(void);\nvoid thread_cleanup(void);\n",[22,7808,7809,7814],{"__ignoreMap":55},[59,7810,7811],{"class":61,"line":62},[59,7812,7813],{},"int  thread_init(void);\n",[59,7815,7816],{"class":61,"line":88},[59,7817,7818],{},"void thread_cleanup(void);\n",[18,7820,7821,7824,7825,7827,7828,7831,7832,7835],{},[22,7822,7823],{},"thread_init()"," 必须在 ",[22,7826,1985],{}," 之后调用（因为内部需要消息循环）。",[22,7829,7830],{},"thread_cleanup()"," 在 ",[22,7833,7834],{},"run_app()"," 返回后调用。",[50,7837,7839],{"className":52,"code":7838,"language":54,"meta":55,"style":55},"init_app();\nthread_init();\n\u002F\u002F ... 创建窗口、启动线程 ...\nrun_app();\nthread_cleanup();\n",[22,7840,7841,7846,7851,7856,7861],{"__ignoreMap":55},[59,7842,7843],{"class":61,"line":62},[59,7844,7845],{},"init_app();\n",[59,7847,7848],{"class":61,"line":88},[59,7849,7850],{},"thread_init();\n",[59,7852,7853],{"class":61,"line":134},[59,7854,7855],{},"\u002F\u002F ... 创建窗口、启动线程 ...\n",[59,7857,7858],{"class":61,"line":140},[59,7859,7860],{},"run_app();\n",[59,7862,7863],{"class":61,"line":146},[59,7864,7865],{},"thread_cleanup();\n",[46,7867,7868],{"id":7868},"线程创建",[50,7870,7872],{"className":52,"code":7871,"language":54,"meta":55,"style":55},"typedef void (*thread_func_t)(void *arg);\ntypedef struct thread_s thread_t;\n\nthread_t* thread_run(thread_func_t func, void *arg);\nint  thread_join(thread_t *t, int timeout_ms);\nvoid thread_detach(thread_t *t);\nDWORD thread_self_id(void);\nvoid thread_sleep(int ms);\n",[22,7873,7874,7879,7884,7888,7893,7898,7903,7908],{"__ignoreMap":55},[59,7875,7876],{"class":61,"line":62},[59,7877,7878],{},"typedef void (*thread_func_t)(void *arg);\n",[59,7880,7881],{"class":61,"line":88},[59,7882,7883],{},"typedef struct thread_s thread_t;\n",[59,7885,7886],{"class":61,"line":134},[59,7887,174],{"emptyLinePlaceholder":173},[59,7889,7890],{"class":61,"line":140},[59,7891,7892],{},"thread_t* thread_run(thread_func_t func, void *arg);\n",[59,7894,7895],{"class":61,"line":146},[59,7896,7897],{},"int  thread_join(thread_t *t, int timeout_ms);\n",[59,7899,7900],{"class":61,"line":281},[59,7901,7902],{},"void thread_detach(thread_t *t);\n",[59,7904,7905],{"class":61,"line":286},[59,7906,7907],{},"DWORD thread_self_id(void);\n",[59,7909,7910],{"class":61,"line":292},[59,7911,7912],{},"void thread_sleep(int ms);\n",[18,7914,7915,7917,7918,7921,7922,7925],{},[22,7916,43],{}," 启动一个新线程。线程签名简化为 ",[22,7919,7920],{},"void func(void*)","，无需 ",[22,7923,7924],{},"DWORD WINAPI"," 模板代码。",[18,7927,7928,40,7931,4232,7933,7935,7936,7938,7939,7942,7943,7945,7946,7948],{},[22,7929,7930],{},"thread_join",[22,7932,6992],{},[22,7934,6996],{}," 无限等待，",[22,7937,1821],{}," 不等待，",[22,7940,7941],{},">0"," 等待指定毫秒数。返回 ",[22,7944,1821],{}," 表示线程已结束，",[22,7947,6996],{}," 表示超时。",[18,7950,7951,7954],{},[22,7952,7953],{},"thread_detach"," 分离线程，线程结束后自动释放资源。",[50,7956,7958],{"className":52,"code":7957,"language":54,"meta":55,"style":55},"void my_worker(void *arg) {\n    thread_sleep(1000);\n    \u002F\u002F 做点事...\n}\n\nthread_t *t = thread_run(my_worker, NULL);\n\u002F\u002F ... 干别的 ...\nthread_join(t, 5000);  \u002F\u002F 等最多5秒\nthread_detach(t);       \u002F\u002F 释放资源\n",[22,7959,7960,7965,7970,7975,7979,7983,7988,7993,7998],{"__ignoreMap":55},[59,7961,7962],{"class":61,"line":62},[59,7963,7964],{},"void my_worker(void *arg) {\n",[59,7966,7967],{"class":61,"line":88},[59,7968,7969],{},"    thread_sleep(1000);\n",[59,7971,7972],{"class":61,"line":134},[59,7973,7974],{},"    \u002F\u002F 做点事...\n",[59,7976,7977],{"class":61,"line":140},[59,7978,149],{},[59,7980,7981],{"class":61,"line":146},[59,7982,174],{"emptyLinePlaceholder":173},[59,7984,7985],{"class":61,"line":281},[59,7986,7987],{},"thread_t *t = thread_run(my_worker, NULL);\n",[59,7989,7990],{"class":61,"line":286},[59,7991,7992],{},"\u002F\u002F ... 干别的 ...\n",[59,7994,7995],{"class":61,"line":292},[59,7996,7997],{},"thread_join(t, 5000);  \u002F\u002F 等最多5秒\n",[59,7999,8000],{"class":61,"line":600},[59,8001,8002],{},"thread_detach(t);       \u002F\u002F 释放资源\n",[46,8004,8006],{"id":8005},"线程安全-ui-更新","线程安全 UI 更新",[50,8008,8010],{"className":52,"code":8009,"language":54,"meta":55,"style":55},"void execute_on_main(HWND hwnd, void (*cb)(void *), void *arg);\n",[22,8011,8012],{"__ignoreMap":55},[59,8013,8014],{"class":61,"line":62},[59,8015,8009],{},[18,8017,8018,624,8021,8024],{},[69,8019,8020],{},"可在任意线程中调用。",[22,8022,8023],{},"cb(arg)"," 会在主线程的消息循环中被执行。如果调用时已经位于主线程，直接执行不走队列。",[18,8026,8027],{},[69,8028,8029],{},"工作原理：",[50,8031,8034],{"className":8032,"code":8033,"language":358},[356],"子线程 ──→ execute_on_main(hwnd, callback, arg)\n                ↓ 打包为 main_call_t\n                ↓ PostMessage\n      隐藏 dispatch 窗口 ──→ WM_DISPATCH_CALL ──→ 主线程执行 callback(arg)\n",[22,8035,8033],{"__ignoreMap":55},[18,8037,8038,8039,8042],{},"无需子类化窗口过程，无需自定义 ",[22,8040,8041],{},"WM_USER"," 消息号。",[50,8044,8046],{"className":52,"code":8045,"language":54,"meta":55,"style":55},"static void on_log(void *msg) {\n    llog((const WCHAR*)msg);\n    free(msg);\n}\n\nvoid my_thread(void *arg) {\n    execute_on_main(g_win, on_log, _wcsdup(L\"线程更新了 UI\"));\n}\n",[22,8047,8048,8053,8058,8063,8067,8071,8076,8081],{"__ignoreMap":55},[59,8049,8050],{"class":61,"line":62},[59,8051,8052],{},"static void on_log(void *msg) {\n",[59,8054,8055],{"class":61,"line":88},[59,8056,8057],{},"    llog((const WCHAR*)msg);\n",[59,8059,8060],{"class":61,"line":134},[59,8061,8062],{},"    free(msg);\n",[59,8064,8065],{"class":61,"line":140},[59,8066,149],{},[59,8068,8069],{"class":61,"line":146},[59,8070,174],{"emptyLinePlaceholder":173},[59,8072,8073],{"class":61,"line":281},[59,8074,8075],{},"void my_thread(void *arg) {\n",[59,8077,8078],{"class":61,"line":286},[59,8079,8080],{},"    execute_on_main(g_win, on_log, _wcsdup(L\"线程更新了 UI\"));\n",[59,8082,8083],{"class":61,"line":292},[59,8084,149],{},[46,8086,8087],{"id":8087},"互斥锁",[50,8089,8091],{"className":52,"code":8090,"language":54,"meta":55,"style":55},"typedef struct mutex_s mutex_t;\n\nmutex_t* mutex_create(void);\nvoid mutex_lock(mutex_t *m);\nint  mutex_trylock(mutex_t *m);    \u002F\u002F 0=锁定成功\nvoid mutex_unlock(mutex_t *m);\nvoid mutex_free(mutex_t *m);\n",[22,8092,8093,8098,8102,8107,8112,8117,8122],{"__ignoreMap":55},[59,8094,8095],{"class":61,"line":62},[59,8096,8097],{},"typedef struct mutex_s mutex_t;\n",[59,8099,8100],{"class":61,"line":88},[59,8101,174],{"emptyLinePlaceholder":173},[59,8103,8104],{"class":61,"line":134},[59,8105,8106],{},"mutex_t* mutex_create(void);\n",[59,8108,8109],{"class":61,"line":140},[59,8110,8111],{},"void mutex_lock(mutex_t *m);\n",[59,8113,8114],{"class":61,"line":146},[59,8115,8116],{},"int  mutex_trylock(mutex_t *m);    \u002F\u002F 0=锁定成功\n",[59,8118,8119],{"class":61,"line":281},[59,8120,8121],{},"void mutex_unlock(mutex_t *m);\n",[59,8123,8124],{"class":61,"line":286},[59,8125,8126],{},"void mutex_free(mutex_t *m);\n",[18,8128,8129,8130,8133],{},"基于 ",[22,8131,8132],{},"CRITICAL_SECTION","（用户态锁，无内核切换，比 Windows Mutex 对象轻量）。",[50,8135,8137],{"className":52,"code":8136,"language":54,"meta":55,"style":55},"static int g_counter = 0;\nstatic mutex_t *g_mutex = NULL;\n\nvoid counter_thread(void *arg) {\n    mutex_lock(g_mutex);\n    g_counter++;\n    mutex_unlock(g_mutex);\n}\n\nint main(void) {\n    thread_init();\n    g_mutex = mutex_create();\n    \u002F\u002F ... 启动若干线程 ...\n    mutex_free(g_mutex);\n    thread_cleanup();\n}\n",[22,8138,8139,8144,8149,8153,8158,8163,8168,8173,8177,8181,8186,8191,8196,8201,8206,8211],{"__ignoreMap":55},[59,8140,8141],{"class":61,"line":62},[59,8142,8143],{},"static int g_counter = 0;\n",[59,8145,8146],{"class":61,"line":88},[59,8147,8148],{},"static mutex_t *g_mutex = NULL;\n",[59,8150,8151],{"class":61,"line":134},[59,8152,174],{"emptyLinePlaceholder":173},[59,8154,8155],{"class":61,"line":140},[59,8156,8157],{},"void counter_thread(void *arg) {\n",[59,8159,8160],{"class":61,"line":146},[59,8161,8162],{},"    mutex_lock(g_mutex);\n",[59,8164,8165],{"class":61,"line":281},[59,8166,8167],{},"    g_counter++;\n",[59,8169,8170],{"class":61,"line":286},[59,8171,8172],{},"    mutex_unlock(g_mutex);\n",[59,8174,8175],{"class":61,"line":292},[59,8176,149],{},[59,8178,8179],{"class":61,"line":600},[59,8180,174],{"emptyLinePlaceholder":173},[59,8182,8183],{"class":61,"line":917},[59,8184,8185],{},"int main(void) {\n",[59,8187,8188],{"class":61,"line":922},[59,8189,8190],{},"    thread_init();\n",[59,8192,8193],{"class":61,"line":928},[59,8194,8195],{},"    g_mutex = mutex_create();\n",[59,8197,8198],{"class":61,"line":933},[59,8199,8200],{},"    \u002F\u002F ... 启动若干线程 ...\n",[59,8202,8203],{"class":61,"line":939},[59,8204,8205],{},"    mutex_free(g_mutex);\n",[59,8207,8208],{"class":61,"line":1320},[59,8209,8210],{},"    thread_cleanup();\n",[59,8212,8213],{"class":61,"line":1326},[59,8214,149],{},[46,8216,298],{"id":298},[18,8218,301,8219,8222,8223,8226],{},[22,8220,8221],{},"samples\u002Fthread_demo\u002F","（3 个并发线程 + 互斥锁计数器 + ",[22,8224,8225],{},"execute_on_main"," 更新 UI）。",[362,8228,364],{},{"title":55,"searchDepth":88,"depth":88,"links":8230},[8231,8232,8233,8234,8235],{"id":1960,"depth":88,"text":1961},{"id":7868,"depth":88,"text":7868},{"id":8005,"depth":88,"text":8006},{"id":8087,"depth":88,"text":8087},{"id":298,"depth":88,"text":298},{},"\u002Fdocs\u002Fapi\u002Feduwin-thread-api",{"title":7774,"description":55},"docs\u002Fapi\u002Feduwin-thread-api","JJgz-EFfkyP0O1fXS4mEflIJ8_dy8veihZnbX94Qosk",{"id":8242,"title":8243,"body":8244,"description":55,"extension":374,"meta":9564,"navigation":173,"path":9565,"seo":9566,"stem":9567,"__hash__":9568},"content\u002Fdocs\u002Fapi\u002Feduwin-util-api.md","eduwin 工具 API",{"type":8,"value":8245,"toc":9546},[8246,8249,8252,8261,8268,8277,8287,8296,8305,8314,8320,8340,8355,8377,8410,8412,8415,8424,8427,8429,8433,8442,8460,8475,8482,8484,8487,8490,8505,8519,8533,8548,8551,8566,8582,8596,8599,8680,8730,8734,8743,8759,8762,8796,8798,8801,8804,8842,8853,8855,8858,8883,8909,8915,8927,8933,9005,9008,9041,9056,9094,9097,9160,9170,9210,9213,9243,9257,9301,9304,9319,9329,9353,9356,9420,9426,9446,9449,9474,9491,9504,9510,9535,9538,9544],[11,8247,8243],{"id":8248},"eduwin-工具-api",[46,8250,8251],{"id":8251},"数字与字符串工具",[50,8253,8255],{"className":52,"code":8254,"language":54,"meta":55,"style":55},"int rand_int(int min, int max);\n",[22,8256,8257],{"__ignoreMap":55},[59,8258,8259],{"class":61,"line":62},[59,8260,8254],{},[18,8262,8263,8264,8267],{},"返回 ",[22,8265,8266],{},"[min, max]"," 闭区间的随机整数。",[50,8269,8271],{"className":52,"code":8270,"language":54,"meta":55,"style":55},"const WCHAR *itow(int value);\n",[22,8272,8273],{"__ignoreMap":55},[59,8274,8275],{"class":61,"line":62},[59,8276,8270],{},[18,8278,8279,8280,8283,8284,75],{},"整数 → 宽字符串。如 ",[22,8281,8282],{},"itow(42)"," 返回 ",[22,8285,8286],{},"L\"42\"",[50,8288,8290],{"className":52,"code":8289,"language":54,"meta":55,"style":55},"const WCHAR *ftow(double value);\n",[22,8291,8292],{"__ignoreMap":55},[59,8293,8294],{"class":61,"line":62},[59,8295,8289],{},[18,8297,8298,8299,8283,8302,75],{},"小数 → 宽字符串。如 ",[22,8300,8301],{},"ftow(3.14)",[22,8303,8304],{},"L\"3.14\"",[50,8306,8308],{"className":52,"code":8307,"language":54,"meta":55,"style":55},"void str_concat(WCHAR *dest, int size, ...);\n",[22,8309,8310],{"__ignoreMap":55},[59,8311,8312],{"class":61,"line":62},[59,8313,8307],{},[18,8315,8316,8317,8319],{},"拼接多个宽字符串，最后一个参数必须是 ",[22,8318,477],{}," 表示结束。",[50,8321,8323],{"className":52,"code":8322,"language":54,"meta":55,"style":55},"WCHAR buf[64];\nstr_concat(buf, 64, L\"你点击了 \", itow(count), L\" 次\", NULL);\nset_text(label, buf);\n",[22,8324,8325,8330,8335],{"__ignoreMap":55},[59,8326,8327],{"class":61,"line":62},[59,8328,8329],{},"WCHAR buf[64];\n",[59,8331,8332],{"class":61,"line":88},[59,8333,8334],{},"str_concat(buf, 64, L\"你点击了 \", itow(count), L\" 次\", NULL);\n",[59,8336,8337],{"class":61,"line":134},[59,8338,8339],{},"set_text(label, buf);\n",[50,8341,8343],{"className":52,"code":8342,"language":54,"meta":55,"style":55},"void str_format(WCHAR *buf, int size, const WCHAR *fmt, ...);\nvoid str_format_a(char *buf, int size, const char *fmt, ...);\n",[22,8344,8345,8350],{"__ignoreMap":55},[59,8346,8347],{"class":61,"line":62},[59,8348,8349],{},"void str_format(WCHAR *buf, int size, const WCHAR *fmt, ...);\n",[59,8351,8352],{"class":61,"line":88},[59,8353,8354],{},"void str_format_a(char *buf, int size, const char *fmt, ...);\n",[18,8356,8357,8358,8360,8361,8363,8364,8366,8367,6708,8370,8373,8374,1090],{},"格式化字符串到 ",[22,8359,3639],{},"，用法类似 ",[22,8362,3542],{},"，自动截断不会溢出。",[22,8365,6058],{}," 输出到宽字符串（",[22,8368,8369],{},"WCHAR",[22,8371,8372],{},"str_format_a"," 输出到 UTF-8 字符串（",[22,8375,8376],{},"char",[50,8378,8380],{"className":52,"code":8379,"language":54,"meta":55,"style":55},"WCHAR buf[64];\nstr_format(buf, 64, L\"Client: %d x %d\", w, h);  \u002F* buf = L\"Client: 682 x 453\" *\u002F\nstr_format(buf, 64, L\"%.1f%%\", 95.7f);          \u002F* buf = L\"95.7%\" *\u002F\n\nchar buf_a[64];\nstr_format_a(buf_a, 64, \"{\\\"result\\\":%d}\", 42); \u002F* buf_a = {\"result\":42} *\u002F\n",[22,8381,8382,8386,8391,8396,8400,8405],{"__ignoreMap":55},[59,8383,8384],{"class":61,"line":62},[59,8385,8329],{},[59,8387,8388],{"class":61,"line":88},[59,8389,8390],{},"str_format(buf, 64, L\"Client: %d x %d\", w, h);  \u002F* buf = L\"Client: 682 x 453\" *\u002F\n",[59,8392,8393],{"class":61,"line":134},[59,8394,8395],{},"str_format(buf, 64, L\"%.1f%%\", 95.7f);          \u002F* buf = L\"95.7%\" *\u002F\n",[59,8397,8398],{"class":61,"line":140},[59,8399,174],{"emptyLinePlaceholder":173},[59,8401,8402],{"class":61,"line":146},[59,8403,8404],{},"char buf_a[64];\n",[59,8406,8407],{"class":61,"line":281},[59,8408,8409],{},"str_format_a(buf_a, 64, \"{\\\"result\\\":%d}\", 42); \u002F* buf_a = {\"result\":42} *\u002F\n",[432,8411],{},[46,8413,8414],{"id":8414},"剪贴板",[50,8416,8418],{"className":52,"code":8417,"language":54,"meta":55,"style":55},"void clipboard_copy(const WCHAR *text);\n",[22,8419,8420],{"__ignoreMap":55},[59,8421,8422],{"class":61,"line":62},[59,8423,8417],{},[18,8425,8426],{},"将文本复制到系统剪贴板。",[432,8428],{},[46,8430,8432],{"id":8431},"base64-解码","Base64 解码",[50,8434,8436],{"className":52,"code":8435,"language":54,"meta":55,"style":55},"int base64_decode(const char *in, int in_len, unsigned char *out);\n",[22,8437,8438],{"__ignoreMap":55},[59,8439,8440],{"class":61,"line":62},[59,8441,8435],{},[18,8443,8444,8445,8448,8449,8452,8453,8456,8457,1090],{},"解码 Base64 字符串为原始字节。",[22,8446,8447],{},"in_len"," 传入输入字符串长度（用 ",[22,8450,8451],{},"strlen","），返回值是输出的字节数。",[22,8454,8455],{},"out"," 缓冲区需足够大（最大约为 ",[22,8458,8459],{},"in_len * 3 \u002F 4",[50,8461,8463],{"className":52,"code":8462,"language":54,"meta":55,"style":55},"unsigned char data[1024];\nint len = base64_decode(\"SGVsbG8=\", 8, data);  \u002F\u002F len=5, data=\"Hello\"\n",[22,8464,8465,8470],{"__ignoreMap":55},[59,8466,8467],{"class":61,"line":62},[59,8468,8469],{},"unsigned char data[1024];\n",[59,8471,8472],{"class":61,"line":88},[59,8473,8474],{},"int len = base64_decode(\"SGVsbG8=\", 8, data);  \u002F\u002F len=5, data=\"Hello\"\n",[18,8476,8477,8478,8481],{},"内部在 ",[22,8479,8480],{},"eduwin_d3d"," 的 glTF 加载器中使用，解码 data URI 中的 base64 数据。",[432,8483],{},[46,8485,8486],{"id":8486},"文件读写",[1020,8488,8489],{"id":8489},"文本文件读写",[50,8491,8493],{"className":52,"code":8492,"language":54,"meta":55,"style":55},"int read_text_file(const WCHAR *filepath, WCHAR *buf, int size);\nint write_text_file(const WCHAR *filepath, const WCHAR *text);\n",[22,8494,8495,8500],{"__ignoreMap":55},[59,8496,8497],{"class":61,"line":62},[59,8498,8499],{},"int read_text_file(const WCHAR *filepath, WCHAR *buf, int size);\n",[59,8501,8502],{"class":61,"line":88},[59,8503,8504],{},"int write_text_file(const WCHAR *filepath, const WCHAR *text);\n",[18,8506,8507,8510,8511,8513,8514,8516,8517,75],{},[22,8508,8509],{},"read_text_file"," 读取文本文件内容到宽字符串缓冲区。自动以 ",[69,8512,5056],{}," 编码读取（兼容含 BOM 的文件）。成功返回读取的字符数（不含末尾 ",[22,8515,3651],{},"），失败返回 ",[22,8518,6996],{},[18,8520,8521,8524,8525,8527,8528,8530,8531,75],{},[22,8522,8523],{},"write_text_file"," 将宽字符串写入文本文件，编码为 ",[69,8526,5056],{},"（含 BOM）。成功返回 ",[22,8529,1817],{},"，失败返回 ",[22,8532,1821],{},[50,8534,8536],{"className":52,"code":8535,"language":54,"meta":55,"style":55},"WCHAR buf[1024];\nint n = read_text_file(L\"data.txt\", buf, 1024);\n",[22,8537,8538,8543],{"__ignoreMap":55},[59,8539,8540],{"class":61,"line":62},[59,8541,8542],{},"WCHAR buf[1024];\n",[59,8544,8545],{"class":61,"line":88},[59,8546,8547],{},"int n = read_text_file(L\"data.txt\", buf, 1024);\n",[1020,8549,8550],{"id":8550},"二进制文件读写",[50,8552,8554],{"className":52,"code":8553,"language":54,"meta":55,"style":55},"int read_bin_file(const WCHAR *filepath, void *buf, int elem_size, int max_count);\nint write_bin_file(const WCHAR *filepath, void *buf, int elem_size, int count);\n",[22,8555,8556,8561],{"__ignoreMap":55},[59,8557,8558],{"class":61,"line":62},[59,8559,8560],{},"int read_bin_file(const WCHAR *filepath, void *buf, int elem_size, int max_count);\n",[59,8562,8563],{"class":61,"line":88},[59,8564,8565],{},"int write_bin_file(const WCHAR *filepath, void *buf, int elem_size, int count);\n",[18,8567,8568,8571,8572,8575,8576,8579,8580,75],{},[22,8569,8570],{},"read_bin_file"," 从二进制文件中读取数据到缓冲区。",[22,8573,8574],{},"elem_size"," 为每个元素的字节数，",[22,8577,8578],{},"max_count"," 为最多读取多少个元素。成功返回实际读取的元素数，失败返回 ",[22,8581,6996],{},[18,8583,8584,8587,8588,8591,8592,8530,8594,75],{},[22,8585,8586],{},"write_bin_file"," 将缓冲区数据写入二进制文件。",[22,8589,8590],{},"count"," 为要写入的元素个数。成功返回 ",[22,8593,1817],{},[22,8595,1821],{},[18,8597,8598],{},"常用于读写结构体或结构体数组：",[50,8600,8602],{"className":52,"code":8601,"language":54,"meta":55,"style":55},"typedef struct {\n    int id;\n    WCHAR name[32];\n    int score;\n} Student;\n\nStudent students[3] = {\n    {1001, L\"张三\", 85},\n    {1002, L\"李四\", 92},\n    {1003, L\"王五\", 78}\n};\n\nwrite_bin_file(L\"students.bin\", students, sizeof(Student), 3);\n\nStudent buf[3];\nint n = read_bin_file(L\"students.bin\", buf, sizeof(Student), 3);\n",[22,8603,8604,8608,8613,8618,8623,8628,8632,8637,8642,8647,8652,8657,8661,8666,8670,8675],{"__ignoreMap":55},[59,8605,8606],{"class":61,"line":62},[59,8607,3881],{},[59,8609,8610],{"class":61,"line":88},[59,8611,8612],{},"    int id;\n",[59,8614,8615],{"class":61,"line":134},[59,8616,8617],{},"    WCHAR name[32];\n",[59,8619,8620],{"class":61,"line":140},[59,8621,8622],{},"    int score;\n",[59,8624,8625],{"class":61,"line":146},[59,8626,8627],{},"} Student;\n",[59,8629,8630],{"class":61,"line":281},[59,8631,174],{"emptyLinePlaceholder":173},[59,8633,8634],{"class":61,"line":286},[59,8635,8636],{},"Student students[3] = {\n",[59,8638,8639],{"class":61,"line":292},[59,8640,8641],{},"    {1001, L\"张三\", 85},\n",[59,8643,8644],{"class":61,"line":600},[59,8645,8646],{},"    {1002, L\"李四\", 92},\n",[59,8648,8649],{"class":61,"line":917},[59,8650,8651],{},"    {1003, L\"王五\", 78}\n",[59,8653,8654],{"class":61,"line":922},[59,8655,8656],{},"};\n",[59,8658,8659],{"class":61,"line":928},[59,8660,174],{"emptyLinePlaceholder":173},[59,8662,8663],{"class":61,"line":933},[59,8664,8665],{},"write_bin_file(L\"students.bin\", students, sizeof(Student), 3);\n",[59,8667,8668],{"class":61,"line":939},[59,8669,174],{"emptyLinePlaceholder":173},[59,8671,8672],{"class":61,"line":1320},[59,8673,8674],{},"Student buf[3];\n",[59,8676,8677],{"class":61,"line":1326},[59,8678,8679],{},"int n = read_bin_file(L\"students.bin\", buf, sizeof(Student), 3);\n",[15,8681,8682,8687],{},[18,8683,8684],{},[69,8685,8686],{},"注意事项：",[5250,8688,8689,8695,8711,8717],{},[318,8690,8691,8694],{},[69,8692,8693],{},"不可跨平台","——结构体内存布局（对齐、填充字节）在不同编译器下可能不同。在 eduwin（MSVC x64）环境下读写无问题。",[318,8696,8697,8700,8701,2338,8704,8707,8708,1090],{},[69,8698,8699],{},"不能包含指针","——结构体里有 ",[22,8702,8703],{},"char *",[22,8705,8706],{},"WCHAR *"," 指针时，只会写入指针地址值而不是指向的字符串内容。应改用固定大小的字符数组（如 ",[22,8709,8710],{},"WCHAR name[32]",[318,8712,8713,8716],{},[69,8714,8715],{},"路径用宽字符串","——与 eduwin 其他文件 API 一致。",[318,8718,8719,8722,8723,8725,8726,8729],{},[69,8720,8721],{},"防止缓冲区溢出","——",[22,8724,3639],{}," 必须足够大以容纳 ",[22,8727,8728],{},"elem_size * max_count"," 字节。",[1020,8731,8733],{"id":8732},"二进制文件读取动态分配","二进制文件读取（动态分配）",[50,8735,8737],{"className":52,"code":8736,"language":54,"meta":55,"style":55},"unsigned char* read_bin_file_alloc(const char *path, int *out_len);\n",[22,8738,8739],{"__ignoreMap":55},[59,8740,8741],{"class":61,"line":62},[59,8742,8736],{},[18,8744,8745,8746,8749,8750,8753,8754,8756,8757,75],{},"将整个文件一次性读入堆内存，自动以 ",[22,8747,8748],{},"malloc"," 分配缓冲区。",[22,8751,8752],{},"out_len"," 返回文件字节数。调用方需用 ",[22,8755,5974],{}," 释放返回的缓冲区。失败返回 ",[22,8758,477],{},[18,8760,8761],{},"常用于需要将整个文件加载到内存的场景，如图片文件加载、glTF 二进制数据处理等。",[50,8763,8765],{"className":52,"code":8764,"language":54,"meta":55,"style":55},"int len;\nunsigned char *data = read_bin_file_alloc(\"image.bmp\", &len);\nif (data) {\n    \u002F\u002F 使用 data...\n    free(data);\n}\n",[22,8766,8767,8772,8777,8782,8787,8792],{"__ignoreMap":55},[59,8768,8769],{"class":61,"line":62},[59,8770,8771],{},"int len;\n",[59,8773,8774],{"class":61,"line":88},[59,8775,8776],{},"unsigned char *data = read_bin_file_alloc(\"image.bmp\", &len);\n",[59,8778,8779],{"class":61,"line":134},[59,8780,8781],{},"if (data) {\n",[59,8783,8784],{"class":61,"line":140},[59,8785,8786],{},"    \u002F\u002F 使用 data...\n",[59,8788,8789],{"class":61,"line":146},[59,8790,8791],{},"    free(data);\n",[59,8793,8794],{"class":61,"line":281},[59,8795,149],{},[432,8797],{},[46,8799,8800],{"id":8800},"数据库",[18,8802,8803],{},"eduwin 已内置 SQLite 嵌入式数据库引擎，无需任何额外配置：",[50,8805,8807],{"className":52,"code":8806,"language":54,"meta":55,"style":55},"#include \"eduwin.h\"\n#include \"sqlite3.h\"\n\nsqlite3 *db;\nsqlite3_open(\"test.db\", &db);\nsqlite3_exec(db, \"CREATE TABLE IF NOT EXISTS ...\", NULL, NULL, NULL);\nsqlite3_close(db);\n",[22,8808,8809,8813,8818,8822,8827,8832,8837],{"__ignoreMap":55},[59,8810,8811],{"class":61,"line":62},[59,8812,5287],{},[59,8814,8815],{"class":61,"line":88},[59,8816,8817],{},"#include \"sqlite3.h\"\n",[59,8819,8820],{"class":61,"line":134},[59,8821,174],{"emptyLinePlaceholder":173},[59,8823,8824],{"class":61,"line":140},[59,8825,8826],{},"sqlite3 *db;\n",[59,8828,8829],{"class":61,"line":146},[59,8830,8831],{},"sqlite3_open(\"test.db\", &db);\n",[59,8833,8834],{"class":61,"line":281},[59,8835,8836],{},"sqlite3_exec(db, \"CREATE TABLE IF NOT EXISTS ...\", NULL, NULL, NULL);\n",[59,8838,8839],{"class":61,"line":286},[59,8840,8841],{},"sqlite3_close(db);\n",[18,8843,8844,8845,75,8849,8852],{},"详见 ",[745,8846,8848],{"href":8847},"..\u002Ftutorial\u002Feduwin-sqlite","eduwin-sqlite.md",[22,8850,8851],{},"samples\\sqlite_demo\\sqlite_demo.c"," 是一个完整的用户信息增删改查演示。",[432,8854],{},[46,8856,8857],{"id":8857},"进程管理",[50,8859,8861],{"className":52,"code":8860,"language":54,"meta":55,"style":55},"HANDLE run_process(const WCHAR *exe_path, const WCHAR *cmdline, int show);\nint    kill_process(HANDLE proc);\nint    kill_process_pid(DWORD pid);\nint    wait_process(HANDLE proc);\n",[22,8862,8863,8868,8873,8878],{"__ignoreMap":55},[59,8864,8865],{"class":61,"line":62},[59,8866,8867],{},"HANDLE run_process(const WCHAR *exe_path, const WCHAR *cmdline, int show);\n",[59,8869,8870],{"class":61,"line":88},[59,8871,8872],{},"int    kill_process(HANDLE proc);\n",[59,8874,8875],{"class":61,"line":134},[59,8876,8877],{},"int    kill_process_pid(DWORD pid);\n",[59,8879,8880],{"class":61,"line":140},[59,8881,8882],{},"int    wait_process(HANDLE proc);\n",[18,8884,8885,8888,8889,8892,8893,8896,8897,6708,8899,8902,8903,8906,8907,75],{},[22,8886,8887],{},"run_process"," 创建新进程。",[22,8890,8891],{},"exe_path"," 为可执行文件路径，",[22,8894,8895],{},"cmdline"," 为命令行参数（可为 ",[22,8898,477],{},[22,8900,8901],{},"show=1"," 显示窗口，",[22,8904,8905],{},"show=0"," 隐藏窗口。成功返回进程句柄，失败返回 ",[22,8908,477],{},[18,8910,8911,8914],{},[22,8912,8913],{},"kill_process"," 强制终止进程并关闭句柄，成功返回 0。",[18,8916,8917,8920,8921,464,8924,8926],{},[22,8918,8919],{},"kill_process_pid"," 按进程 PID 终止进程（内部调用 ",[22,8922,8923],{},"OpenProcess",[22,8925,8913],{},"），成功返回 0。",[18,8928,8929,8932],{},[22,8930,8931],{},"wait_process"," 等待进程结束，返回进程退出码（内部会自动关闭句柄）。",[50,8934,8936],{"className":52,"code":8935,"language":54,"meta":55,"style":55},"\u002F\u002F 启动记事本，等待退出\nHANDLE h = run_process(L\"C:\\\\Windows\\\\System32\\\\notepad.exe\", NULL, 1);\nif (h) {\n    int code = wait_process(h);\n    log_info(L\"记事本已退出\");\n}\n\n\u002F\u002F 后台启动 cmd\nHANDLE h2 = run_process(L\"C:\\\\Windows\\\\System32\\\\cmd.exe\", NULL, 1);\n\u002F\u002F 稍后终止\nkill_process(h2);\n\n\u002F\u002F 按 PID 终止进程\nkill_process_pid(1234);\n",[22,8937,8938,8943,8948,8953,8958,8963,8967,8971,8976,8981,8986,8991,8995,9000],{"__ignoreMap":55},[59,8939,8940],{"class":61,"line":62},[59,8941,8942],{},"\u002F\u002F 启动记事本，等待退出\n",[59,8944,8945],{"class":61,"line":88},[59,8946,8947],{},"HANDLE h = run_process(L\"C:\\\\Windows\\\\System32\\\\notepad.exe\", NULL, 1);\n",[59,8949,8950],{"class":61,"line":134},[59,8951,8952],{},"if (h) {\n",[59,8954,8955],{"class":61,"line":140},[59,8956,8957],{},"    int code = wait_process(h);\n",[59,8959,8960],{"class":61,"line":146},[59,8961,8962],{},"    log_info(L\"记事本已退出\");\n",[59,8964,8965],{"class":61,"line":281},[59,8966,149],{},[59,8968,8969],{"class":61,"line":286},[59,8970,174],{"emptyLinePlaceholder":173},[59,8972,8973],{"class":61,"line":292},[59,8974,8975],{},"\u002F\u002F 后台启动 cmd\n",[59,8977,8978],{"class":61,"line":600},[59,8979,8980],{},"HANDLE h2 = run_process(L\"C:\\\\Windows\\\\System32\\\\cmd.exe\", NULL, 1);\n",[59,8982,8983],{"class":61,"line":917},[59,8984,8985],{},"\u002F\u002F 稍后终止\n",[59,8987,8988],{"class":61,"line":922},[59,8989,8990],{},"kill_process(h2);\n",[59,8992,8993],{"class":61,"line":928},[59,8994,174],{"emptyLinePlaceholder":173},[59,8996,8997],{"class":61,"line":933},[59,8998,8999],{},"\u002F\u002F 按 PID 终止进程\n",[59,9001,9002],{"class":61,"line":939},[59,9003,9004],{},"kill_process_pid(1234);\n",[46,9006,9007],{"id":9007},"进程枚举",[50,9009,9011],{"className":52,"code":9010,"language":54,"meta":55,"style":55},"typedef struct {\n    DWORD pid;\n    WCHAR name[260];\n} process_entry_t;\n\nint enum_processes(process_entry_t **out, int *count);\n",[22,9012,9013,9017,9022,9027,9032,9036],{"__ignoreMap":55},[59,9014,9015],{"class":61,"line":62},[59,9016,3881],{},[59,9018,9019],{"class":61,"line":88},[59,9020,9021],{},"    DWORD pid;\n",[59,9023,9024],{"class":61,"line":134},[59,9025,9026],{},"    WCHAR name[260];\n",[59,9028,9029],{"class":61,"line":140},[59,9030,9031],{},"} process_entry_t;\n",[59,9033,9034],{"class":61,"line":146},[59,9035,174],{"emptyLinePlaceholder":173},[59,9037,9038],{"class":61,"line":281},[59,9039,9040],{},"int enum_processes(process_entry_t **out, int *count);\n",[18,9042,9043,9046,9047,9049,9050,9052,9053,9055],{},[22,9044,9045],{},"enum_processes"," 枚举所有系统进程，动态分配数组并通过 ",[22,9048,8455],{}," 返回。",[22,9051,8590],{}," 返回进程数量。成功返回 0。调用方需 ",[22,9054,5974],{}," 释放返回的数组。",[50,9057,9059],{"className":52,"code":9058,"language":54,"meta":55,"style":55},"process_entry_t *procs = NULL;\nint count = 0;\nif (enum_processes(&procs, &count) == 0) {\n    for (int i = 0; i \u003C count; i++)\n        wprintf(L\"%u  %s\\n\", procs[i].pid, procs[i].name);\n    free(procs);\n}\n",[22,9060,9061,9066,9071,9076,9080,9085,9090],{"__ignoreMap":55},[59,9062,9063],{"class":61,"line":62},[59,9064,9065],{},"process_entry_t *procs = NULL;\n",[59,9067,9068],{"class":61,"line":88},[59,9069,9070],{},"int count = 0;\n",[59,9072,9073],{"class":61,"line":134},[59,9074,9075],{},"if (enum_processes(&procs, &count) == 0) {\n",[59,9077,9078],{"class":61,"line":140},[59,9079,4806],{},[59,9081,9082],{"class":61,"line":146},[59,9083,9084],{},"        wprintf(L\"%u  %s\\n\", procs[i].pid, procs[i].name);\n",[59,9086,9087],{"class":61,"line":281},[59,9088,9089],{},"    free(procs);\n",[59,9091,9092],{"class":61,"line":286},[59,9093,149],{},[46,9095,9096],{"id":9096},"系统信息",[50,9098,9100],{"className":52,"code":9099,"language":54,"meta":55,"style":55},"typedef struct {\n    WCHAR computer_name[64];\n    WCHAR user_name[64];\n    WCHAR os_version[64];\n    int   cpu_cores;\n    WCHAR cpu_name[128];\n    unsigned long long mem_total_mb;\n    unsigned long long mem_avail_mb;\n    WCHAR windows_dir[MAX_PATH];\n} sys_info_t;\n\nint get_sys_info(sys_info_t *info);\n",[22,9101,9102,9106,9111,9116,9121,9126,9131,9136,9141,9146,9151,9155],{"__ignoreMap":55},[59,9103,9104],{"class":61,"line":62},[59,9105,3881],{},[59,9107,9108],{"class":61,"line":88},[59,9109,9110],{},"    WCHAR computer_name[64];\n",[59,9112,9113],{"class":61,"line":134},[59,9114,9115],{},"    WCHAR user_name[64];\n",[59,9117,9118],{"class":61,"line":140},[59,9119,9120],{},"    WCHAR os_version[64];\n",[59,9122,9123],{"class":61,"line":146},[59,9124,9125],{},"    int   cpu_cores;\n",[59,9127,9128],{"class":61,"line":281},[59,9129,9130],{},"    WCHAR cpu_name[128];\n",[59,9132,9133],{"class":61,"line":286},[59,9134,9135],{},"    unsigned long long mem_total_mb;\n",[59,9137,9138],{"class":61,"line":292},[59,9139,9140],{},"    unsigned long long mem_avail_mb;\n",[59,9142,9143],{"class":61,"line":600},[59,9144,9145],{},"    WCHAR windows_dir[MAX_PATH];\n",[59,9147,9148],{"class":61,"line":917},[59,9149,9150],{},"} sys_info_t;\n",[59,9152,9153],{"class":61,"line":922},[59,9154,174],{"emptyLinePlaceholder":173},[59,9156,9157],{"class":61,"line":928},[59,9158,9159],{},"int get_sys_info(sys_info_t *info);\n",[18,9161,9162,9165,9166,9169],{},[22,9163,9164],{},"get_sys_info"," 填充系统信息到 ",[22,9167,9168],{},"sys_info_t"," 结构体。成功返回 0。",[50,9171,9173],{"className":52,"code":9172,"language":54,"meta":55,"style":55},"sys_info_t info;\nget_sys_info(&info);\n\u002F\u002F info.computer_name → 计算机名\n\u002F\u002F info.os_version → \"Windows 10 Pro (Build 19045, 22H2)\"\n\u002F\u002F info.cpu_cores → 8\n\u002F\u002F info.cpu_name → \"Intel(R) Core(TM) i7-10700\"\n\u002F\u002F info.mem_total_mb → 16384\n",[22,9174,9175,9180,9185,9190,9195,9200,9205],{"__ignoreMap":55},[59,9176,9177],{"class":61,"line":62},[59,9178,9179],{},"sys_info_t info;\n",[59,9181,9182],{"class":61,"line":88},[59,9183,9184],{},"get_sys_info(&info);\n",[59,9186,9187],{"class":61,"line":134},[59,9188,9189],{},"\u002F\u002F info.computer_name → 计算机名\n",[59,9191,9192],{"class":61,"line":140},[59,9193,9194],{},"\u002F\u002F info.os_version → \"Windows 10 Pro (Build 19045, 22H2)\"\n",[59,9196,9197],{"class":61,"line":146},[59,9198,9199],{},"\u002F\u002F info.cpu_cores → 8\n",[59,9201,9202],{"class":61,"line":281},[59,9203,9204],{},"\u002F\u002F info.cpu_name → \"Intel(R) Core(TM) i7-10700\"\n",[59,9206,9207],{"class":61,"line":286},[59,9208,9209],{},"\u002F\u002F info.mem_total_mb → 16384\n",[46,9211,9212],{"id":9212},"注册表读写",[50,9214,9216],{"className":52,"code":9215,"language":54,"meta":55,"style":55},"int reg_read_str(HKEY root, const WCHAR *key, const WCHAR *value, WCHAR *buf, int size);\nint reg_read_dword(HKEY root, const WCHAR *key, const WCHAR *value, DWORD *out);\nint reg_write_str(HKEY root, const WCHAR *key, const WCHAR *value, const WCHAR *data);\nint reg_write_dword(HKEY root, const WCHAR *key, const WCHAR *value, DWORD data);\nint reg_delete_value(HKEY root, const WCHAR *key, const WCHAR *value);\n",[22,9217,9218,9223,9228,9233,9238],{"__ignoreMap":55},[59,9219,9220],{"class":61,"line":62},[59,9221,9222],{},"int reg_read_str(HKEY root, const WCHAR *key, const WCHAR *value, WCHAR *buf, int size);\n",[59,9224,9225],{"class":61,"line":88},[59,9226,9227],{},"int reg_read_dword(HKEY root, const WCHAR *key, const WCHAR *value, DWORD *out);\n",[59,9229,9230],{"class":61,"line":134},[59,9231,9232],{},"int reg_write_str(HKEY root, const WCHAR *key, const WCHAR *value, const WCHAR *data);\n",[59,9234,9235],{"class":61,"line":140},[59,9236,9237],{},"int reg_write_dword(HKEY root, const WCHAR *key, const WCHAR *value, DWORD data);\n",[59,9239,9240],{"class":61,"line":146},[59,9241,9242],{},"int reg_delete_value(HKEY root, const WCHAR *key, const WCHAR *value);\n",[18,9244,9245,9246,9249,9250,1090,9253,9256],{},"注册表读写操作。",[22,9247,9248],{},"root"," 为根键（如 ",[22,9251,9252],{},"HKEY_LOCAL_MACHINE",[22,9254,9255],{},"key"," 为键路径。成功返回 0。写操作会自动创建不存在的键。",[50,9258,9260],{"className":52,"code":9259,"language":54,"meta":55,"style":55},"WCHAR cpu_name[128];\nreg_read_str(HKEY_LOCAL_MACHINE,\n    L\"HARDWARE\\\\DESCRIPTION\\\\System\\\\CentralProcessor\\\\0\",\n    L\"ProcessorNameString\", cpu_name, 128);\n\nDWORD value = 1;\nreg_write_dword(HKEY_CURRENT_USER,\n    L\"Software\\\\MyApp\", L\"Setting\", value);\n",[22,9261,9262,9267,9272,9277,9282,9286,9291,9296],{"__ignoreMap":55},[59,9263,9264],{"class":61,"line":62},[59,9265,9266],{},"WCHAR cpu_name[128];\n",[59,9268,9269],{"class":61,"line":88},[59,9270,9271],{},"reg_read_str(HKEY_LOCAL_MACHINE,\n",[59,9273,9274],{"class":61,"line":134},[59,9275,9276],{},"    L\"HARDWARE\\\\DESCRIPTION\\\\System\\\\CentralProcessor\\\\0\",\n",[59,9278,9279],{"class":61,"line":140},[59,9280,9281],{},"    L\"ProcessorNameString\", cpu_name, 128);\n",[59,9283,9284],{"class":61,"line":146},[59,9285,174],{"emptyLinePlaceholder":173},[59,9287,9288],{"class":61,"line":281},[59,9289,9290],{},"DWORD value = 1;\n",[59,9292,9293],{"class":61,"line":286},[59,9294,9295],{},"reg_write_dword(HKEY_CURRENT_USER,\n",[59,9297,9298],{"class":61,"line":292},[59,9299,9300],{},"    L\"Software\\\\MyApp\", L\"Setting\", value);\n",[46,9302,9303],{"id":9303},"环境变量",[50,9305,9307],{"className":52,"code":9306,"language":54,"meta":55,"style":55},"int get_env(const WCHAR *name, WCHAR *buf, int size);\nint set_env(const WCHAR *name, const WCHAR *value);\n",[22,9308,9309,9314],{"__ignoreMap":55},[59,9310,9311],{"class":61,"line":62},[59,9312,9313],{},"int get_env(const WCHAR *name, WCHAR *buf, int size);\n",[59,9315,9316],{"class":61,"line":88},[59,9317,9318],{},"int set_env(const WCHAR *name, const WCHAR *value);\n",[18,9320,9321,9324,9325,9328],{},[22,9322,9323],{},"get_env"," 读取环境变量，成功返回 0。",[22,9326,9327],{},"set_env"," 设置环境变量（仅影响当前进程），成功返回 0。",[50,9330,9332],{"className":52,"code":9331,"language":54,"meta":55,"style":55},"WCHAR path_val[4096];\nget_env(L\"PATH\", path_val, 4096);\n\nset_env(L\"MY_VAR\", L\"hello\");\n",[22,9333,9334,9339,9344,9348],{"__ignoreMap":55},[59,9335,9336],{"class":61,"line":62},[59,9337,9338],{},"WCHAR path_val[4096];\n",[59,9340,9341],{"class":61,"line":88},[59,9342,9343],{},"get_env(L\"PATH\", path_val, 4096);\n",[59,9345,9346],{"class":61,"line":134},[59,9347,174],{"emptyLinePlaceholder":173},[59,9349,9350],{"class":61,"line":140},[59,9351,9352],{},"set_env(L\"MY_VAR\", L\"hello\");\n",[46,9354,9355],{"id":9355},"特殊文件夹",[50,9357,9359],{"className":52,"code":9358,"language":54,"meta":55,"style":55},"#define FOLDER_DESKTOP      0\n#define FOLDER_DOCUMENTS    1\n#define FOLDER_DOWNLOADS    2\n#define FOLDER_PICTURES     3\n#define FOLDER_MUSIC        4\n#define FOLDER_VIDEOS       5\n#define FOLDER_APPDATA      6\n#define FOLDER_LOCALAPPDATA 7\n#define FOLDER_PROGRAMFILES 8\n#define FOLDER_STARTUP      9\n\nint get_special_folder(int folder_id, WCHAR *buf, int size);\n",[22,9360,9361,9366,9371,9376,9381,9386,9391,9396,9401,9406,9411,9415],{"__ignoreMap":55},[59,9362,9363],{"class":61,"line":62},[59,9364,9365],{},"#define FOLDER_DESKTOP      0\n",[59,9367,9368],{"class":61,"line":88},[59,9369,9370],{},"#define FOLDER_DOCUMENTS    1\n",[59,9372,9373],{"class":61,"line":134},[59,9374,9375],{},"#define FOLDER_DOWNLOADS    2\n",[59,9377,9378],{"class":61,"line":140},[59,9379,9380],{},"#define FOLDER_PICTURES     3\n",[59,9382,9383],{"class":61,"line":146},[59,9384,9385],{},"#define FOLDER_MUSIC        4\n",[59,9387,9388],{"class":61,"line":281},[59,9389,9390],{},"#define FOLDER_VIDEOS       5\n",[59,9392,9393],{"class":61,"line":286},[59,9394,9395],{},"#define FOLDER_APPDATA      6\n",[59,9397,9398],{"class":61,"line":292},[59,9399,9400],{},"#define FOLDER_LOCALAPPDATA 7\n",[59,9402,9403],{"class":61,"line":600},[59,9404,9405],{},"#define FOLDER_PROGRAMFILES 8\n",[59,9407,9408],{"class":61,"line":917},[59,9409,9410],{},"#define FOLDER_STARTUP      9\n",[59,9412,9413],{"class":61,"line":922},[59,9414,174],{"emptyLinePlaceholder":173},[59,9416,9417],{"class":61,"line":928},[59,9418,9419],{},"int get_special_folder(int folder_id, WCHAR *buf, int size);\n",[18,9421,9422,9425],{},[22,9423,9424],{},"get_special_folder"," 获取系统特殊文件夹路径。成功返回 0。",[50,9427,9429],{"className":52,"code":9428,"language":54,"meta":55,"style":55},"WCHAR path[MAX_PATH];\nget_special_folder(FOLDER_DOWNLOADS, path, MAX_PATH);\n\u002F\u002F path → \"C:\\Users\\xxx\\Downloads\"\n",[22,9430,9431,9436,9441],{"__ignoreMap":55},[59,9432,9433],{"class":61,"line":62},[59,9434,9435],{},"WCHAR path[MAX_PATH];\n",[59,9437,9438],{"class":61,"line":88},[59,9439,9440],{},"get_special_folder(FOLDER_DOWNLOADS, path, MAX_PATH);\n",[59,9442,9443],{"class":61,"line":134},[59,9444,9445],{},"\u002F\u002F path → \"C:\\Users\\xxx\\Downloads\"\n",[46,9447,9448],{"id":9448},"日志",[50,9450,9452],{"className":52,"code":9451,"language":54,"meta":55,"style":55},"void log_info(const WCHAR *text);\nvoid log_warning(const WCHAR *text);\nvoid log_error(const WCHAR *title);\nvoid log_clear(void);\n",[22,9453,9454,9459,9464,9469],{"__ignoreMap":55},[59,9455,9456],{"class":61,"line":62},[59,9457,9458],{},"void log_info(const WCHAR *text);\n",[59,9460,9461],{"class":61,"line":88},[59,9462,9463],{},"void log_warning(const WCHAR *text);\n",[59,9465,9466],{"class":61,"line":134},[59,9467,9468],{},"void log_error(const WCHAR *title);\n",[59,9470,9471],{"class":61,"line":140},[59,9472,9473],{},"void log_clear(void);\n",[18,9475,9476,9477,9480,9481,735,9484,735,9487,9490],{},"将文本写入 exe 所在目录下的 ",[22,9478,9479],{},"log.txt"," 文件，自动在行首添加日期时间戳（精确到毫秒）和标签（",[22,9482,9483],{},"[INFO]",[22,9485,9486],{},"[WARN]",[22,9488,9489],{},"[ERROR]","）。日志以追加方式写入。",[18,9492,9493,9496,9497,9499,9500,9503],{},[22,9494,9495],{},"log_error"," 除了写入时间戳和 ",[22,9498,9489],{}," 标签外，还会自动追加 ",[22,9501,9502],{},"GetLastError()"," 的错误码和对应的系统错误描述。",[18,9505,9506,9509],{},[22,9507,9508],{},"log_clear"," 清空日志文件。",[50,9511,9513],{"className":52,"code":9512,"language":54,"meta":55,"style":55},"log_info(L\"程序启动完成\");\nlog_warning(L\"纹理加载失败，使用默认纹理\");\nlog_error(L\"CreateDevice\");  \u002F\u002F 自动追加 err=87 - 参数错误\nlog_clear();\n",[22,9514,9515,9520,9525,9530],{"__ignoreMap":55},[59,9516,9517],{"class":61,"line":62},[59,9518,9519],{},"log_info(L\"程序启动完成\");\n",[59,9521,9522],{"class":61,"line":88},[59,9523,9524],{},"log_warning(L\"纹理加载失败，使用默认纹理\");\n",[59,9526,9527],{"class":61,"line":134},[59,9528,9529],{},"log_error(L\"CreateDevice\");  \u002F\u002F 自动追加 err=87 - 参数错误\n",[59,9531,9532],{"class":61,"line":140},[59,9533,9534],{},"log_clear();\n",[18,9536,9537],{},"输出示例：",[50,9539,9542],{"className":9540,"code":9541,"language":358},[356],"2026-06-23 14:30:01.123 [INFO] 程序启动完成\n2026-06-23 14:30:02.456 [WARN] 纹理加载失败，使用默认纹理\n2026-06-23 14:30:03.789 [ERROR] CreateDevice (err=87 - 参数错误)\n",[22,9543,9541],{"__ignoreMap":55},[362,9545,364],{},{"title":55,"searchDepth":88,"depth":88,"links":9547},[9548,9549,9550,9551,9556,9557,9558,9559,9560,9561,9562,9563],{"id":8251,"depth":88,"text":8251},{"id":8414,"depth":88,"text":8414},{"id":8431,"depth":88,"text":8432},{"id":8486,"depth":88,"text":8486,"children":9552},[9553,9554,9555],{"id":8489,"depth":134,"text":8489},{"id":8550,"depth":134,"text":8550},{"id":8732,"depth":134,"text":8733},{"id":8800,"depth":88,"text":8800},{"id":8857,"depth":88,"text":8857},{"id":9007,"depth":88,"text":9007},{"id":9096,"depth":88,"text":9096},{"id":9212,"depth":88,"text":9212},{"id":9303,"depth":88,"text":9303},{"id":9355,"depth":88,"text":9355},{"id":9448,"depth":88,"text":9448},{},"\u002Fdocs\u002Fapi\u002Feduwin-util-api",{"title":8243,"description":55},"docs\u002Fapi\u002Feduwin-util-api","QdGasvXE2cGMTs9c51NnjfIYExWNNySthJCWyZtmKig",{"id":9570,"title":9571,"body":9572,"description":55,"extension":374,"meta":10644,"navigation":173,"path":10645,"seo":10646,"stem":10647,"__hash__":10648},"content\u002Fdocs\u002Fapi\u002Feduwin-webview-api.md","eduwin WebView2 模块 API",{"type":8,"value":9573,"toc":10628},[9574,9577,9589,9592,9597,9650,9692,9694,9714,9722,9736,9744,9826,9830,9839,9854,9865,9880,9884,9899,9912,9921,9928,9957,9960,9975,9986,9990,9993,9997,10017,10029,10043,10096,10099,10205,10209,10216,10225,10235,10250,10264,10293,10351,10361,10364,10384,10387,10390,10405,10412,10436,10440,10447,10456,10469,10498,10507,10509,10512,10526,10529,10536,10545,10609,10612,10618,10625],[11,9575,9571],{"id":9576},"eduwin-webview2-模块-api",[15,9578,9579],{},[18,9580,9581,9582,9584,9585,9588],{},"头文件：通过 ",[22,9583,28],{}," 统一引用（",[22,9586,9587],{},"@public_api"," 已自动合并）",[18,9590,9591],{},"封装 Microsoft Edge WebView2，在 Eduwin 窗口中嵌入现代浏览器引擎。纯 C 实现，通过 COM lpVtbl + 手写 vtable 回调接入 WebView2 的三层 API（Loader flat 入口 \u002F ICoreWebView2 接口调用 \u002F 异步 CompletedHandler 回调）。",[18,9593,9594],{},[69,9595,9596],{},"两种 Runtime 模式：",[4903,9598,9599,9615],{},[4906,9600,9601],{},[4909,9602,9603,9606,9609,9612],{},[4912,9604,9605],{},"模式",[4912,9607,9608],{},"Runtime 来源",[4912,9610,9611],{},"包体",[4912,9613,9614],{},"客户端要求",[4919,9616,9617,9634],{},[4909,9618,9619,9625,9628,9631],{},[4924,9620,9621,9624],{},[69,9622,9623],{},"Evergreen","（默认）",[4924,9626,9627],{},"系统共享",[4924,9629,9630],{},"—",[4924,9632,9633],{},"需预装 Runtime",[4909,9635,9636,9641,9644,9647],{},[4924,9637,9638],{},[69,9639,9640],{},"Fixed Version",[4924,9642,9643],{},"应用本地打包",[4924,9645,9646],{},"~150MB",[4924,9648,9649],{},"零依赖",[15,9651,9652,9661,9689],{},[18,9653,9654,624,9657,9660],{},[69,9655,9656],{},"⚠️ Runtime 获取：",[22,9658,9659],{},"eduwin_pub.zip"," 不含 WebView2 Runtime（体积过大）。",[315,9662,9663,9676],{},[318,9664,9665,9668,9669,9675],{},[69,9666,9667],{},"Evergreen（推荐开发\u002F测试用）","：下载 ",[745,9670,9674],{"href":9671,"rel":9672},"https:\u002F\u002Fdeveloper.microsoft.com\u002Fmicrosoft-edge\u002Fwebview2\u002F",[9673],"nofollow","Evergreen Bootstrapper","（约 2MB），双击安装，重启即可",[318,9677,9678,9668,9681,9685,9686],{},[69,9679,9680],{},"Fixed Version（推荐分发用）",[745,9682,9684],{"href":9671,"rel":9683},[9673],"Fixed Version Runtime","（x64，约 150MB），解压后目录传给 ",[22,9687,9688],{},"webview_set_runtime_path()",[18,9690,9691],{},"两种下载均在同一页面，选择 \"Evergreen Bootstrapper\" 或 \"Fixed Version\" → 架构选 x64。",[46,9693,1961],{"id":1960},[50,9695,9697],{"className":52,"code":9696,"language":54,"meta":55,"style":55},"void webview_set_runtime_path(const WCHAR *path);  \u002F* 必须在 webview_init() 之前 *\u002F\nint  webview_init(void);\nvoid webview_cleanup(void);\n",[22,9698,9699,9704,9709],{"__ignoreMap":55},[59,9700,9701],{"class":61,"line":62},[59,9702,9703],{},"void webview_set_runtime_path(const WCHAR *path);  \u002F* 必须在 webview_init() 之前 *\u002F\n",[59,9705,9706],{"class":61,"line":88},[59,9707,9708],{},"int  webview_init(void);\n",[59,9710,9711],{"class":61,"line":134},[59,9712,9713],{},"void webview_cleanup(void);\n",[18,9715,9716,9718,9719,9721],{},[22,9717,9688],{}," 设置 Runtime 路径。",[22,9720,477],{}," = Evergreen 模式（默认），传路径 = Fixed Version 模式。",[18,9723,9724,7824,9727,624,9729,9732,9733,9735],{},[22,9725,9726],{},"webview_init()",[22,9728,1985],{},[69,9730,9731],{},"之前","调用。返回值 ",[22,9734,1821],{}," 成功，非零失败。",[18,9737,9738,7831,9741,9743],{},[22,9739,9740],{},"webview_cleanup()",[22,9742,7834],{}," 返回后调用，释放全局 COM 环境。",[50,9745,9747],{"className":52,"code":9746,"language":54,"meta":55,"style":55},"\u002F* Fixed Version：客户端零依赖 *\u002F\nwebview_set_runtime_path(L\"..\u002Fruntime\u002F\");\nif (webview_init() != 0) {\n    init_app();\n    alert(L\"错误\", L\"WebView2 Runtime 初始化失败\");\n    return 1;\n}\n\u002F*\n * Evergreen（默认）：\n *   不调 webview_set_runtime_path，webview_init() 自动用系统 Runtime。\n *   Windows 11 已预装，Windows 10 需手动安装。\n *\u002F\ninit_app();\n\u002F\u002F ... 创建 WebView 窗口 ...\nrun_app();\nwebview_cleanup();\n",[22,9748,9749,9754,9759,9764,9769,9774,9779,9783,9788,9793,9798,9803,9808,9812,9817,9821],{"__ignoreMap":55},[59,9750,9751],{"class":61,"line":62},[59,9752,9753],{},"\u002F* Fixed Version：客户端零依赖 *\u002F\n",[59,9755,9756],{"class":61,"line":88},[59,9757,9758],{},"webview_set_runtime_path(L\"..\u002Fruntime\u002F\");\n",[59,9760,9761],{"class":61,"line":134},[59,9762,9763],{},"if (webview_init() != 0) {\n",[59,9765,9766],{"class":61,"line":140},[59,9767,9768],{},"    init_app();\n",[59,9770,9771],{"class":61,"line":146},[59,9772,9773],{},"    alert(L\"错误\", L\"WebView2 Runtime 初始化失败\");\n",[59,9775,9776],{"class":61,"line":281},[59,9777,9778],{},"    return 1;\n",[59,9780,9781],{"class":61,"line":286},[59,9782,149],{},[59,9784,9785],{"class":61,"line":292},[59,9786,9787],{},"\u002F*\n",[59,9789,9790],{"class":61,"line":600},[59,9791,9792],{}," * Evergreen（默认）：\n",[59,9794,9795],{"class":61,"line":917},[59,9796,9797],{}," *   不调 webview_set_runtime_path，webview_init() 自动用系统 Runtime。\n",[59,9799,9800],{"class":61,"line":922},[59,9801,9802],{}," *   Windows 11 已预装，Windows 10 需手动安装。\n",[59,9804,9805],{"class":61,"line":928},[59,9806,9807],{}," *\u002F\n",[59,9809,9810],{"class":61,"line":933},[59,9811,7845],{},[59,9813,9814],{"class":61,"line":939},[59,9815,9816],{},"\u002F\u002F ... 创建 WebView 窗口 ...\n",[59,9818,9819],{"class":61,"line":1320},[59,9820,7860],{},[59,9822,9823],{"class":61,"line":1326},[59,9824,9825],{},"webview_cleanup();\n",[46,9827,9829],{"id":9828},"创建-webview","创建 WebView",[50,9831,9833],{"className":52,"code":9832,"language":54,"meta":55,"style":55},"HWND create_webview(HWND parent, int x, int y, int w, int h);\n",[22,9834,9835],{"__ignoreMap":55},[59,9836,9837],{"class":61,"line":62},[59,9838,9832],{},[18,9840,9841,9842,735,9844,9846,9847,9849,9850,9853],{},"在父窗口中创建 WebView2 子控件。参数与 ",[22,9843,6026],{},[22,9845,6029],{}," 一致：",[22,9848,6474],{}," 左上角坐标，",[22,9851,9852],{},"(w, h)"," 宽高。",[18,9855,9856,9857,9860,9861,9864],{},"WebView 的 Environment 和 Controller 是",[69,9858,9859],{},"异步创建","的，函数返回后 WebView 尚未就绪——需配合 ",[22,9862,9863],{},"on_webview_ready"," 回调使用。",[50,9866,9868],{"className":52,"code":9867,"language":54,"meta":55,"style":55},"HWND win = create_window(L\"WebView Demo\", 960, 640);\nHWND wv = create_webview(win, 0, 0, 960, 600);\n",[22,9869,9870,9875],{"__ignoreMap":55},[59,9871,9872],{"class":61,"line":62},[59,9873,9874],{},"HWND win = create_window(L\"WebView Demo\", 960, 640);\n",[59,9876,9877],{"class":61,"line":88},[59,9878,9879],{},"HWND wv = create_webview(win, 0, 0, 960, 600);\n",[46,9881,9883],{"id":9882},"导航-内容","导航 & 内容",[50,9885,9887],{"className":52,"code":9886,"language":54,"meta":55,"style":55},"void webview_navigate(HWND hwv, const WCHAR *url);\nvoid webview_load_html(HWND hwv, const WCHAR *html);\n",[22,9888,9889,9894],{"__ignoreMap":55},[59,9890,9891],{"class":61,"line":62},[59,9892,9893],{},"void webview_navigate(HWND hwv, const WCHAR *url);\n",[59,9895,9896],{"class":61,"line":88},[59,9897,9898],{},"void webview_load_html(HWND hwv, const WCHAR *html);\n",[18,9900,9901,9904,9905,9911],{},[22,9902,9903],{},"webview_navigate"," 导航到指定 URL。",[69,9906,9907,9908,9910],{},"通常在 ",[22,9909,9863],{}," 回调中调用","，确保 WebView 已就绪。",[18,9913,9914,9917,9918,1090],{},[22,9915,9916],{},"webview_load_html"," 加载 HTML 字符串（",[22,9919,9920],{},"\u003Chtml>...\u003C\u002Fhtml>",[18,9922,9923,9924,9927],{},"如果在 ",[22,9925,9926],{},"ready"," 之前调用，请求会自动排队，WebView 就绪后执行。",[50,9929,9931],{"className":52,"code":9930,"language":54,"meta":55,"style":55},"static void on_ready(HWND hwv, void *data) {\n    webview_navigate(hwv, L\"https:\u002F\u002Fwww.bing.com\");\n    \u002F\u002F 或：\n    \u002F\u002F webview_load_html(hwv, L\"\u003Chtml>\u003Cbody>\u003Ch1>Hello\u003C\u002Fh1>\u003C\u002Fbody>\u003C\u002Fhtml>\");\n}\n",[22,9932,9933,9938,9943,9948,9953],{"__ignoreMap":55},[59,9934,9935],{"class":61,"line":62},[59,9936,9937],{},"static void on_ready(HWND hwv, void *data) {\n",[59,9939,9940],{"class":61,"line":88},[59,9941,9942],{},"    webview_navigate(hwv, L\"https:\u002F\u002Fwww.bing.com\");\n",[59,9944,9945],{"class":61,"line":134},[59,9946,9947],{},"    \u002F\u002F 或：\n",[59,9949,9950],{"class":61,"line":140},[59,9951,9952],{},"    \u002F\u002F webview_load_html(hwv, L\"\u003Chtml>\u003Cbody>\u003Ch1>Hello\u003C\u002Fh1>\u003C\u002Fbody>\u003C\u002Fhtml>\");\n",[59,9954,9955],{"class":61,"line":146},[59,9956,149],{},[46,9958,9959],{"id":9959},"就绪回调",[50,9961,9963],{"className":52,"code":9962,"language":54,"meta":55,"style":55},"typedef void (*webview_ready_fn)(HWND hwv, void *data);\nvoid on_webview_ready(HWND hwv, webview_ready_fn cb, void *data);\n",[22,9964,9965,9970],{"__ignoreMap":55},[59,9966,9967],{"class":61,"line":62},[59,9968,9969],{},"typedef void (*webview_ready_fn)(HWND hwv, void *data);\n",[59,9971,9972],{"class":61,"line":88},[59,9973,9974],{},"void on_webview_ready(HWND hwv, webview_ready_fn cb, void *data);\n",[18,9976,9977,9978,735,9980,735,9982,9985],{},"WebView2 异步初始化完成后触发。这是安全调用 ",[22,9979,9903],{},[22,9981,9916],{},[22,9983,9984],{},"webview_post_message"," 的时机。",[46,9987,9989],{"id":9988},"js-双向通信","JS 双向通信",[18,9991,9992],{},"C 与 WebView 中页面可通过以下两种方式进行双向通信。",[1020,9994,9996],{"id":9995},"方式一原始-postmessage","方式一：原始 postMessage",[50,9998,10000],{"className":52,"code":9999,"language":54,"meta":55,"style":55},"typedef void (*webview_message_fn)(HWND hwv, const char *json, void *data);\nvoid on_webview_message(HWND hwv, webview_message_fn cb, void *data);\nvoid webview_post_message(HWND hwv, const char *json);\n",[22,10001,10002,10007,10012],{"__ignoreMap":55},[59,10003,10004],{"class":61,"line":62},[59,10005,10006],{},"typedef void (*webview_message_fn)(HWND hwv, const char *json, void *data);\n",[59,10008,10009],{"class":61,"line":88},[59,10010,10011],{},"void on_webview_message(HWND hwv, webview_message_fn cb, void *data);\n",[59,10013,10014],{"class":61,"line":134},[59,10015,10016],{},"void webview_post_message(HWND hwv, const char *json);\n",[18,10018,10019,624,10022,10024,10025,10028],{},[69,10020,10021],{},"C → JS：",[22,10023,9984],{}," 向 WebView 中页面发送 JSON 字符串。页面通过 ",[22,10026,10027],{},"window.chrome.webview.addEventListener('message', ...)"," 接收。",[18,10030,10031,10034,10035,10038,10039,10042],{},[69,10032,10033],{},"JS → C："," 页面通过 ",[22,10036,10037],{},"window.chrome.webview.postMessage(json)"," 发送消息，C 端通过 ",[22,10040,10041],{},"on_webview_message"," 注册的回调接收。",[50,10044,10046],{"className":52,"code":10045,"language":54,"meta":55,"style":55},"\u002F\u002F C 端接收 JS 消息\nstatic void on_msg(HWND hwv, const char *json, void *data) {\n    \u002F\u002F json 是从 JS 发来的字符串\n    log_info(L\"收到 JS 消息\");\n    \u002F\u002F 回复消息给 JS\n    webview_post_message(hwv, \"{\\\"reply\\\": \\\"你好 JS\\\"}\");\n}\n\n\u002F\u002F 注册\non_webview_message(wv, on_msg, NULL);\n",[22,10047,10048,10053,10058,10063,10068,10073,10078,10082,10086,10091],{"__ignoreMap":55},[59,10049,10050],{"class":61,"line":62},[59,10051,10052],{},"\u002F\u002F C 端接收 JS 消息\n",[59,10054,10055],{"class":61,"line":88},[59,10056,10057],{},"static void on_msg(HWND hwv, const char *json, void *data) {\n",[59,10059,10060],{"class":61,"line":134},[59,10061,10062],{},"    \u002F\u002F json 是从 JS 发来的字符串\n",[59,10064,10065],{"class":61,"line":140},[59,10066,10067],{},"    log_info(L\"收到 JS 消息\");\n",[59,10069,10070],{"class":61,"line":146},[59,10071,10072],{},"    \u002F\u002F 回复消息给 JS\n",[59,10074,10075],{"class":61,"line":281},[59,10076,10077],{},"    webview_post_message(hwv, \"{\\\"reply\\\": \\\"你好 JS\\\"}\");\n",[59,10079,10080],{"class":61,"line":286},[59,10081,149],{},[59,10083,10084],{"class":61,"line":292},[59,10085,174],{"emptyLinePlaceholder":173},[59,10087,10088],{"class":61,"line":600},[59,10089,10090],{},"\u002F\u002F 注册\n",[59,10092,10093],{"class":61,"line":917},[59,10094,10095],{},"on_webview_message(wv, on_msg, NULL);\n",[18,10097,10098],{},"页面端示例：",[50,10100,10104],{"className":10101,"code":10102,"language":10103,"meta":55,"style":55},"language-js shiki shiki-themes github-light github-dark","\u002F\u002F JS 端发送消息到 C\nwindow.chrome.webview.postMessage(JSON.stringify({ type: \"click\", value: 42 }));\n\n\u002F\u002F JS 端接收 C 消息\nwindow.chrome.webview.addEventListener(\"message\", (e) => {\n    console.log(\"C 发来:\", e.data);\n});\n","js",[22,10105,10106,10111,10146,10150,10155,10184,10200],{"__ignoreMap":55},[59,10107,10108],{"class":61,"line":62},[59,10109,10110],{"class":1902},"\u002F\u002F JS 端发送消息到 C\n",[59,10112,10113,10116,10119,10122,10125,10128,10131,10134,10137,10140,10143],{"class":61,"line":88},[59,10114,10115],{"class":5076},"window.chrome.webview.",[59,10117,10118],{"class":1908},"postMessage",[59,10120,10121],{"class":5076},"(",[59,10123,10124],{"class":1926},"JSON",[59,10126,10127],{"class":5076},".",[59,10129,10130],{"class":1908},"stringify",[59,10132,10133],{"class":5076},"({ type: ",[59,10135,10136],{"class":1933},"\"click\"",[59,10138,10139],{"class":5076},", value: ",[59,10141,10142],{"class":1926},"42",[59,10144,10145],{"class":5076}," }));\n",[59,10147,10148],{"class":61,"line":134},[59,10149,174],{"emptyLinePlaceholder":173},[59,10151,10152],{"class":61,"line":140},[59,10153,10154],{"class":1902},"\u002F\u002F JS 端接收 C 消息\n",[59,10156,10157,10159,10162,10164,10167,10170,10174,10177,10181],{"class":61,"line":146},[59,10158,10115],{"class":5076},[59,10160,10161],{"class":1908},"addEventListener",[59,10163,10121],{"class":5076},[59,10165,10166],{"class":1933},"\"message\"",[59,10168,10169],{"class":5076},", (",[59,10171,10173],{"class":10172},"s4XuR","e",[59,10175,10176],{"class":5076},") ",[59,10178,10180],{"class":10179},"szBVR","=>",[59,10182,10183],{"class":5076}," {\n",[59,10185,10186,10189,10192,10194,10197],{"class":61,"line":281},[59,10187,10188],{"class":5076},"    console.",[59,10190,10191],{"class":1908},"log",[59,10193,10121],{"class":5076},[59,10195,10196],{"class":1933},"\"C 发来:\"",[59,10198,10199],{"class":5076},", e.data);\n",[59,10201,10202],{"class":61,"line":286},[59,10203,10204],{"class":5076},"});\n",[1020,10206,10208],{"id":10207},"方式二bridge推荐","方式二：Bridge（推荐）",[18,10210,10211,10212,10215],{},"Bridge 在页面启动时自动注入 ",[22,10213,10214],{},"window.eduwin"," 对象，封装了原始 postMessage，提供更简洁的双向调用方式。",[50,10217,10219],{"className":52,"code":10218,"language":54,"meta":55,"style":55},"void webview_bridge_init(HWND hwv);\n",[22,10220,10221],{"__ignoreMap":55},[59,10222,10223],{"class":61,"line":62},[59,10224,10218],{},[18,10226,10227,10228,2730,10231,10234],{},"启用 Bridge。调用后 WebView 中所有页面自动获得 ",[22,10229,10230],{},"window.eduwin.on_message(cb)",[22,10232,10233],{},"window.eduwin.call(func, params, callback)"," 方法。",[50,10236,10238],{"className":52,"code":10237,"language":54,"meta":55,"style":55},"typedef void (*webview_bridge_fn)(HWND hwv, const char *func_name, const char *params_json, void *data);\nvoid webview_bridge_register(const char *func_name, webview_bridge_fn cb, void *data);\n",[22,10239,10240,10245],{"__ignoreMap":55},[59,10241,10242],{"class":61,"line":62},[59,10243,10244],{},"typedef void (*webview_bridge_fn)(HWND hwv, const char *func_name, const char *params_json, void *data);\n",[59,10246,10247],{"class":61,"line":88},[59,10248,10249],{},"void webview_bridge_register(const char *func_name, webview_bridge_fn cb, void *data);\n",[18,10251,10252,10253,10256,10257,10260,10261,75],{},"注册一个 C 端 bridge 处理函数，供 JS 通过 ",[22,10254,10255],{},"eduwin.call(func_name, params)"," 调用。",[22,10258,10259],{},"params_json"," 是参数字符串（JSON 格式），如 ",[22,10262,10263],{},"{\"name\":\"eduwin\"}",[50,10265,10267],{"className":52,"code":10266,"language":54,"meta":55,"style":55},"\u002F* C 端注册 *\u002F\nstatic void on_hello(HWND hwv, const char *func, const char *params, void *d) {\n    webview_post_message(hwv, \"{\\\"reply\\\":\\\"你好！\\\"}\");\n}\nwebview_bridge_register(\"hello\", on_hello, NULL);\n",[22,10268,10269,10274,10279,10284,10288],{"__ignoreMap":55},[59,10270,10271],{"class":61,"line":62},[59,10272,10273],{},"\u002F* C 端注册 *\u002F\n",[59,10275,10276],{"class":61,"line":88},[59,10277,10278],{},"static void on_hello(HWND hwv, const char *func, const char *params, void *d) {\n",[59,10280,10281],{"class":61,"line":134},[59,10282,10283],{},"    webview_post_message(hwv, \"{\\\"reply\\\":\\\"你好！\\\"}\");\n",[59,10285,10286],{"class":61,"line":140},[59,10287,149],{},[59,10289,10290],{"class":61,"line":146},[59,10291,10292],{},"webview_bridge_register(\"hello\", on_hello, NULL);\n",[50,10294,10296],{"className":10101,"code":10295,"language":10103,"meta":55,"style":55},"\u002F* JS 端调用 *\u002F\neduwin.call(\"hello\", { name: \"eduwin\" }, function (r) {\n    console.log(r.reply); \u002F\u002F \"你好！\"\n});\n",[22,10297,10298,10303,10335,10347],{"__ignoreMap":55},[59,10299,10300],{"class":61,"line":62},[59,10301,10302],{"class":1902},"\u002F* JS 端调用 *\u002F\n",[59,10304,10305,10308,10311,10313,10315,10318,10321,10324,10327,10330,10332],{"class":61,"line":88},[59,10306,10307],{"class":5076},"eduwin.",[59,10309,10310],{"class":1908},"call",[59,10312,10121],{"class":5076},[59,10314,5111],{"class":1933},[59,10316,10317],{"class":5076},", { name: ",[59,10319,10320],{"class":1933},"\"eduwin\"",[59,10322,10323],{"class":5076}," }, ",[59,10325,10326],{"class":10179},"function",[59,10328,10329],{"class":5076}," (",[59,10331,734],{"class":10172},[59,10333,10334],{"class":5076},") {\n",[59,10336,10337,10339,10341,10344],{"class":61,"line":134},[59,10338,10188],{"class":5076},[59,10340,10191],{"class":1908},[59,10342,10343],{"class":5076},"(r.reply); ",[59,10345,10346],{"class":1902},"\u002F\u002F \"你好！\"\n",[59,10348,10349],{"class":61,"line":140},[59,10350,10204],{"class":5076},[18,10352,10353,10354,10357,10358,10360],{},"Bridge 的底层仍基于 postMessage，消息类型为 ",[22,10355,10356],{},"{ type: \"bridge_call\", func, params, id }","。C 端自动路由到已注册的处理函数，未被识别为 bridge 的消息仍会传给 ",[22,10359,10041],{}," 回调。",[46,10362,10363],{"id":10363},"导航控制",[50,10365,10367],{"className":52,"code":10366,"language":54,"meta":55,"style":55},"void webview_back(HWND hwv);\nvoid webview_forward(HWND hwv);\nvoid webview_reload(HWND hwv);\n",[22,10368,10369,10374,10379],{"__ignoreMap":55},[59,10370,10371],{"class":61,"line":62},[59,10372,10373],{},"void webview_back(HWND hwv);\n",[59,10375,10376],{"class":61,"line":88},[59,10377,10378],{},"void webview_forward(HWND hwv);\n",[59,10380,10381],{"class":61,"line":134},[59,10382,10383],{},"void webview_reload(HWND hwv);\n",[18,10385,10386],{},"浏览器的后退、前进、刷新操作。仅在 WebView 就绪后有效。",[46,10388,10389],{"id":10389},"导航完成回调",[50,10391,10393],{"className":52,"code":10392,"language":54,"meta":55,"style":55},"typedef void (*webview_nav_complete_fn)(HWND hwv, int success, int status_code, void *data);\nvoid on_navigation_completed(HWND hwv, webview_nav_complete_fn cb, void *data);\n",[22,10394,10395,10400],{"__ignoreMap":55},[59,10396,10397],{"class":61,"line":62},[59,10398,10399],{},"typedef void (*webview_nav_complete_fn)(HWND hwv, int success, int status_code, void *data);\n",[59,10401,10402],{"class":61,"line":88},[59,10403,10404],{},"void on_navigation_completed(HWND hwv, webview_nav_complete_fn cb, void *data);\n",[18,10406,10407,10408,10411],{},"注册导航完成回调。页面加载完成后触发，",[22,10409,10410],{},"success"," 为 1 表示成功，0 表示失败。",[50,10413,10415],{"className":52,"code":10414,"language":54,"meta":55,"style":55},"static void on_nav(HWND hwv, int success, int code, void *d) {\n    set_text(g_status, success ? L\"Done\" : L\"Failed\");\n}\non_navigation_completed(wv, on_nav, NULL);\n",[22,10416,10417,10422,10427,10431],{"__ignoreMap":55},[59,10418,10419],{"class":61,"line":62},[59,10420,10421],{},"static void on_nav(HWND hwv, int success, int code, void *d) {\n",[59,10423,10424],{"class":61,"line":88},[59,10425,10426],{},"    set_text(g_status, success ? L\"Done\" : L\"Failed\");\n",[59,10428,10429],{"class":61,"line":134},[59,10430,149],{},[59,10432,10433],{"class":61,"line":140},[59,10434,10435],{},"on_navigation_completed(wv, on_nav, NULL);\n",[46,10437,10439],{"id":10438},"virtual-host-加载本地网页文件","Virtual Host — 加载本地网页文件",[18,10441,10442,10443,10446],{},"将本地文件夹映射为虚拟域名，绕过 ",[22,10444,10445],{},"file:\u002F\u002F\u002F"," 的限制，正常加载外部 HTML\u002FCSS\u002FJS。",[50,10448,10450],{"className":52,"code":10449,"language":54,"meta":55,"style":55},"void webview_set_virtual_host(HWND hwv, const WCHAR *host_name, const WCHAR *folder_path);\n",[22,10451,10452],{"__ignoreMap":55},[59,10453,10454],{"class":61,"line":62},[59,10455,10449],{},[18,10457,10458,10461,10462,6708,10465,10468],{},[22,10459,10460],{},"host_name"," 为虚拟域名（如 ",[22,10463,10464],{},"L\"eduwin.local\"",[22,10466,10467],{},"folder_path"," 为本地文件夹路径（相对路径基于 exe 所在目录，也支持绝对路径）。",[50,10470,10472],{"className":52,"code":10471,"language":54,"meta":55,"style":55},"\u002F* 映射当前 exe 目录到 eduwin.local *\u002F\nwebview_set_virtual_host(hwv, L\"eduwin.local\", L\".\");\n\n\u002F* 然后通过 https 访问本地文件 *\u002F\nwebview_navigate(hwv, L\"https:\u002F\u002Feduwin.local\u002Findex.html\");\n",[22,10473,10474,10479,10484,10488,10493],{"__ignoreMap":55},[59,10475,10476],{"class":61,"line":62},[59,10477,10478],{},"\u002F* 映射当前 exe 目录到 eduwin.local *\u002F\n",[59,10480,10481],{"class":61,"line":88},[59,10482,10483],{},"webview_set_virtual_host(hwv, L\"eduwin.local\", L\".\");\n",[59,10485,10486],{"class":61,"line":134},[59,10487,174],{"emptyLinePlaceholder":173},[59,10489,10490],{"class":61,"line":140},[59,10491,10492],{},"\u002F* 然后通过 https 访问本地文件 *\u002F\n",[59,10494,10495],{"class":61,"line":146},[59,10496,10497],{},"webview_navigate(hwv, L\"https:\u002F\u002Feduwin.local\u002Findex.html\");\n",[15,10499,10500],{},[18,10501,10502,10503,10506],{},"Virtual Host 的访问权限为 ",[22,10504,10505],{},"COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_DENY_CORS","，禁止跨域请求但允许正常加载同域资源。",[46,10508,298],{"id":298},[18,10510,10511],{},"参见：",[315,10513,10514,10520],{},[318,10515,10516,10519],{},[22,10517,10518],{},"samples\u002Fwebview_browser\u002F"," —— 简易浏览器（工具栏 + 地址栏 + 状态栏）",[318,10521,10522,10525],{},[22,10523,10524],{},"samples\u002Fwebview_bridge\u002F"," —— Bridge 演示（C ↔ JS 双向通信 + 外部 HTML\u002FCSS\u002FJS 加载）",[46,10527,10528],{"id":10528},"链接库",[18,10530,10531,10532,10535],{},"使用 WebView2 模块时，",[22,10533,10534],{},"build.bat"," 需额外链接：",[50,10537,10539],{"className":7634,"code":10538,"language":7636,"meta":55,"style":55},"link ... WebView2LoaderStatic.lib ole32.lib oleaut32.lib version.lib advapi32.lib\n",[22,10540,10541],{"__ignoreMap":55},[59,10542,10543],{"class":61,"line":62},[59,10544,10538],{},[4903,10546,10547,10557],{},[4906,10548,10549],{},[4909,10550,10551,10554],{},[4912,10552,10553],{},"库",[4912,10555,10556],{},"用途",[4919,10558,10559,10569,10579,10589,10599],{},[4909,10560,10561,10566],{},[4924,10562,10563],{},[22,10564,10565],{},"WebView2LoaderStatic.lib",[4924,10567,10568],{},"WebView2 环境加载器",[4909,10570,10571,10576],{},[4924,10572,10573],{},[22,10574,10575],{},"ole32.lib",[4924,10577,10578],{},"COM 基础（CoInitializeEx 等）",[4909,10580,10581,10586],{},[4924,10582,10583],{},[22,10584,10585],{},"oleaut32.lib",[4924,10587,10588],{},"COM 自动化",[4909,10590,10591,10596],{},[4924,10592,10593],{},[22,10594,10595],{},"version.lib",[4924,10597,10598],{},"版本信息查询",[4909,10600,10601,10606],{},[4924,10602,10603],{},[22,10604,10605],{},"advapi32.lib",[4924,10607,10608],{},"注册表（检测 WebView2 Runtime）",[46,10610,10611],{"id":10611},"实现架构",[50,10613,10616],{"className":10614,"code":10615,"language":358},[356],"eduwin_webview.c (纯 C)\n├── 模式 A: Flat API ── CreateCoreWebView2EnvironmentWithOptions(...)\n├── 模式 B: lpVtbl ──── controller->lpVtbl->put_Bounds(...)\n│                       webview->lpVtbl->Navigate(...)\n└── 模式 C: 回调 vtable ─ env_handler_t \u002F ctrl_handler_t \u002F msg_handler_t\n                         (手写 QueryInterface \u002F AddRef \u002F Release \u002F Invoke)\n",[22,10617,10615],{"__ignoreMap":55},[18,10619,10620,10621,10624],{},"三种 COM 互操作模式全部在纯 C 中完成，未引入 ",[22,10622,10623],{},".cpp"," 文件。",[362,10626,10627],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":55,"searchDepth":88,"depth":88,"links":10629},[10630,10631,10632,10633,10634,10638,10639,10640,10641,10642,10643],{"id":1960,"depth":88,"text":1961},{"id":9828,"depth":88,"text":9829},{"id":9882,"depth":88,"text":9883},{"id":9959,"depth":88,"text":9959},{"id":9988,"depth":88,"text":9989,"children":10635},[10636,10637],{"id":9995,"depth":134,"text":9996},{"id":10207,"depth":134,"text":10208},{"id":10363,"depth":88,"text":10363},{"id":10389,"depth":88,"text":10389},{"id":10438,"depth":88,"text":10439},{"id":298,"depth":88,"text":298},{"id":10528,"depth":88,"text":10528},{"id":10611,"depth":88,"text":10611},{},"\u002Fdocs\u002Fapi\u002Feduwin-webview-api",{"title":9571,"description":55},"docs\u002Fapi\u002Feduwin-webview-api","CethnAkaGczUX39tfUygDsrORxHeg95Udju0uuDw8J4",{"id":10650,"title":10651,"body":10652,"description":55,"extension":374,"meta":11726,"navigation":173,"path":11727,"seo":11728,"stem":11729,"__hash__":11730},"content\u002Fdocs\u002Fsamples\u002Fai_browser_demo.md","ai_browser_demo — AI Browser 启动入口",{"type":8,"value":10653,"toc":11706},[10654,10657,10674,10676,10680,10690,10771,10774,10780,10782,10786,10795,10799,10816,10820,10823,10828,10860,10865,10891,10896,10939,10944,10973,10977,10983,10987,11023,11025,11029,11125,11194,11196,11200,11204,11210,11253,11256,11270,11274,11334,11338,11353,11355,11359,11362,11405,11438,11440,11444,11519,11525,11527,11531,11703],[11,10655,10651],{"id":10656},"ai_browser_demo-ai-browser-启动入口",[15,10658,10659],{},[18,10660,10661,624,10664,10666,10669,10670,10673],{},[69,10662,10663],{},"示例位置：",[22,10665,7618],{},[69,10667,10668],{},"核心演示："," 命令行参数解析 + WebView2 集成 + WS Hub pub\u002Fsub 消息枢纽 + 日志面板\n",[69,10671,10672],{},"最后更新："," 2026-06-26",[432,10675],{},[46,10677,10679],{"id":10678},"一概述","一、概述",[18,10681,10682,10685,10686,10689],{},[22,10683,10684],{},"ai_browser_demo"," 是 ",[69,10687,10688],{},"eduwin AI Browser"," 的启动入口骨架，将框架多个模块集成到一个可执行程序中：",[4903,10691,10692,10701],{},[4906,10693,10694],{},[4909,10695,10696,10699],{},[4912,10697,10698],{},"模块",[4912,10700,10556],{},[4919,10702,10703,10713,10732,10742,10752,10762],{},[4909,10704,10705,10710],{},[4924,10706,10707],{},[22,10708,10709],{},"eduwin_app",[4924,10711,10712],{},"窗口创建、消息循环、窗口关闭\u002F缩放回调",[4909,10714,10715,10720],{},[4924,10716,10717],{},[22,10718,10719],{},"eduwin_cmd",[4924,10721,10722,10723,1086,10725,1086,10728,1086,10730,1494],{},"命令行参数解析（",[22,10724,2054],{},[22,10726,10727],{},"--port",[22,10729,2035],{},[22,10731,2057],{},[4909,10733,10734,10739],{},[4924,10735,10736],{},[22,10737,10738],{},"eduwin_webview",[4924,10740,10741],{},"WebView2 浏览器容器",[4909,10743,10744,10749],{},[4924,10745,10746],{},[22,10747,10748],{},"eduwin_net（ws_hub）",[4924,10750,10751],{},"通用 pub\u002Fsub WebSocket 消息枢纽",[4909,10753,10754,10759],{},[4924,10755,10756],{},[22,10757,10758],{},"eduwin_logview",[4924,10760,10761],{},"右侧日志面板（三列：时间 \u002F 级别 \u002F 消息）",[4909,10763,10764,10768],{},[4924,10765,10766],{},[22,10767,39],{},[4924,10769,10770],{},"后台线程运行 WS 服务器",[1020,10772,10773],{"id":10773},"架构",[50,10775,10778],{"className":10776,"code":10777,"language":358},[356],"┌──────────────────────────────────────────────────────┐\n│                   ai_browser_demo                     │\n│                                                      │\n│  ┌─ WebView2 ─────────────────────────────┐          │\n│  │  （左侧内容区）                         │          │\n│  └────────────────────────────────────────┘          │\n│                                                      │\n│  ┌─ LogView ──────────────────────────────┐          │\n│  │  时间  │  级别  │  消息                 │          │\n│  │  14:30 │  ⓘ    │  日志面板已初始化     │          │\n│  │  14:30 │  ⓘ    │  WS Hub 已启动        │          │\n│  │  14:31 │  ⓘ    │  conn_id=0 订阅 'js'  │          │\n│  │  14:31 │  ⚠    │  客户端断开           │          │\n│  │  14:32 │  ✕    │  无效 JSON            │          │\n│  └────────────────────────────────────────┘          │\n│                                                      │\n│  ┌─ ws_hub（后台线程）───────────────────┐           │\n│  │  通用 pub\u002Fsub，多个 WS 客户端自由路由   │          │\n│  └────────────────────────────────────────┘          │\n└──────────────────────────────────────────────────────┘\n",[22,10779,10777],{"__ignoreMap":55},[432,10781],{},[46,10783,10785],{"id":10784},"二通信协议-通用-pubsub","二、通信协议 — 通用 pub\u002Fsub",[18,10787,10788,10790,10791,10794],{},[22,10789,10684],{}," 使用 ",[69,10792,10793],{},"ws_hub（话题驱动的 pub\u002Fsub 模式）","，类似 MQTT。",[1020,10796,10798],{"id":10797},"_21-设计思路","2.1 设计思路",[315,10800,10801,10810,10813],{},[318,10802,10803,10806,10807],{},[69,10804,10805],{},"不再区分 Agent 连接和 JS 连接"," — 两者都是",[69,10808,10809],{},"对等 WS 客户端",[318,10811,10812],{},"客户端通过 JSON 指令自由订阅\u002F发布话题",[318,10814,10815],{},"消息路由不经过 C 层逻辑，服务端只做透传",[1020,10817,10819],{"id":10818},"_22-协议格式","2.2 协议格式",[18,10821,10822],{},"所有消息为 JSON 格式，UTF-8 编码：",[18,10824,10825],{},[69,10826,10827],{},"订阅话题：",[50,10829,10831],{"className":5067,"code":10830,"language":5069,"meta":55,"style":55},"{\"type\":\"subscribe\",\"topic\":\"agent\"}\n",[22,10832,10833],{"__ignoreMap":55},[59,10834,10835,10838,10841,10844,10847,10850,10853,10855,10858],{"class":61,"line":62},[59,10836,10837],{"class":5076},"{",[59,10839,10840],{"class":1926},"\"type\"",[59,10842,10843],{"class":5076},":",[59,10845,10846],{"class":1933},"\"subscribe\"",[59,10848,10849],{"class":5076},",",[59,10851,10852],{"class":1926},"\"topic\"",[59,10854,10843],{"class":5076},[59,10856,10857],{"class":1933},"\"agent\"",[59,10859,149],{"class":5076},[18,10861,10862],{},[69,10863,10864],{},"取消订阅：",[50,10866,10868],{"className":5067,"code":10867,"language":5069,"meta":55,"style":55},"{\"type\":\"unsubscribe\",\"topic\":\"agent\"}\n",[22,10869,10870],{"__ignoreMap":55},[59,10871,10872,10874,10876,10878,10881,10883,10885,10887,10889],{"class":61,"line":62},[59,10873,10837],{"class":5076},[59,10875,10840],{"class":1926},[59,10877,10843],{"class":5076},[59,10879,10880],{"class":1933},"\"unsubscribe\"",[59,10882,10849],{"class":5076},[59,10884,10852],{"class":1926},[59,10886,10843],{"class":5076},[59,10888,10857],{"class":1933},[59,10890,149],{"class":5076},[18,10892,10893],{},[69,10894,10895],{},"发布消息：",[50,10897,10899],{"className":5067,"code":10898,"language":5069,"meta":55,"style":55},"{\"type\":\"publish\",\"topic\":\"agent\",\"data\":{\"action\":\"describe\"}}\n",[22,10900,10901],{"__ignoreMap":55},[59,10902,10903,10905,10907,10909,10912,10914,10916,10918,10920,10922,10925,10928,10931,10933,10936],{"class":61,"line":62},[59,10904,10837],{"class":5076},[59,10906,10840],{"class":1926},[59,10908,10843],{"class":5076},[59,10910,10911],{"class":1933},"\"publish\"",[59,10913,10849],{"class":5076},[59,10915,10852],{"class":1926},[59,10917,10843],{"class":5076},[59,10919,10857],{"class":1933},[59,10921,10849],{"class":5076},[59,10923,10924],{"class":1926},"\"data\"",[59,10926,10927],{"class":5076},":{",[59,10929,10930],{"class":1926},"\"action\"",[59,10932,10843],{"class":5076},[59,10934,10935],{"class":1933},"\"describe\"",[59,10937,10938],{"class":5076},"}}\n",[18,10940,10941],{},[69,10942,10943],{},"收到推送（服务端 → 非发送者的所有订阅者）：",[50,10945,10947],{"className":5067,"code":10946,"language":5069,"meta":55,"style":55},"{\"topic\":\"agent\",\"data\":{\"action\":\"describe\"}}\n",[22,10948,10949],{"__ignoreMap":55},[59,10950,10951,10953,10955,10957,10959,10961,10963,10965,10967,10969,10971],{"class":61,"line":62},[59,10952,10837],{"class":5076},[59,10954,10852],{"class":1926},[59,10956,10843],{"class":5076},[59,10958,10857],{"class":1933},[59,10960,10849],{"class":5076},[59,10962,10924],{"class":1926},[59,10964,10927],{"class":5076},[59,10966,10930],{"class":1926},[59,10968,10843],{"class":5076},[59,10970,10935],{"class":1933},[59,10972,10938],{"class":5076},[1020,10974,10976],{"id":10975},"_23-典型对话流程","2.3 典型对话流程",[50,10978,10981],{"className":10979,"code":10980,"language":358},[356],"Python Agent                     ai_browser_demo              WebView JS\n    │                                  │                          │\n    │── WS connect ──────────────────►  │                          │\n    │                                  │◄── WS connect ──────────│\n    │                                  │                          │\n    │── subscribe \"agent\" ───────────►  │                          │\n    │                                  │◄── subscribe \"js\" ──────│\n    │                                  │                          │\n    │── publish topic=\"agent\" ───────►  │                          │\n    │   {\"action\":\"describe\"}          │── 转发给 \"js\" 订阅者 ──► │\n    │                                  │     （排除发送者）        │\n    │                                  │◄── publish topic=\"js\" ──│\n    │◄── 转发给 \"agent\" 订阅者 ───────│    DOM tree              │\n    │                                  │                          │\n",[22,10982,10980],{"__ignoreMap":55},[1020,10984,10986],{"id":10985},"_24-服务端限制","2.4 服务端限制",[4903,10988,10989,10997],{},[4906,10990,10991],{},[4909,10992,10993,10995],{},[4912,10994,7441],{},[4912,10996,7444],{},[4919,10998,10999,11005,11011,11017],{},[4909,11000,11001,11003],{},[4924,11002,7451],{},[4924,11004,7454],{},[4909,11006,11007,11009],{},[4924,11008,7459],{},[4924,11010,7462],{},[4909,11012,11013,11015],{},[4924,11014,7467],{},[4924,11016,7470],{},[4909,11018,11019,11021],{},[4924,11020,7475],{},[4924,11022,7478],{},[432,11024],{},[46,11026,11028],{"id":11027},"三命令行参数","三、命令行参数",[4903,11030,11031,11045],{},[4906,11032,11033],{},[4909,11034,11035,11038,11040,11043],{},[4912,11036,11037],{},"参数",[4912,11039,5585],{},[4912,11041,11042],{},"默认值",[4912,11044,4917],{},[4919,11046,11047,11062,11079,11095,11112],{},[4909,11048,11049,11053,11056,11059],{},[4924,11050,11051],{},[22,11052,2054],{},[4924,11054,11055],{},"开关",[4924,11057,11058],{},"有窗口",[4924,11060,11061],{},"隐身模式（无界面，WebView 挂傀儡窗口）",[4909,11063,11064,11068,11071,11076],{},[4924,11065,11066],{},[22,11067,10727],{},[4924,11069,11070],{},"值",[4924,11072,11073],{},[22,11074,11075],{},"9099",[4924,11077,11078],{},"WS 服务器端口",[4909,11080,11081,11085,11087,11092],{},[4924,11082,11083],{},[22,11084,2035],{},[4924,11086,11070],{},[4924,11088,11089],{},[22,11090,11091],{},"about:blank",[4924,11093,11094],{},"起始导航 URL",[4909,11096,11097,11102,11104,11109],{},[4924,11098,11099],{},[22,11100,11101],{},"--title",[4924,11103,11070],{},[4924,11105,11106],{},[22,11107,11108],{},"AI Browser",[4924,11110,11111],{},"窗口标题",[4909,11113,11114,11118,11120,11122],{},[4924,11115,11116],{},[22,11117,2057],{},[4924,11119,11055],{},[4924,11121,9630],{},[4924,11123,11124],{},"显示帮助信息",[50,11126,11128],{"className":1893,"code":11127,"language":1895,"meta":55,"style":55},"# 默认有头模式\nai_browser_demo.exe\n\n# 隐身模式，自定义端口\nai_browser_demo.exe --headless --port 9090\n\n# 指定起始 URL\nai_browser_demo.exe --url https:\u002F\u002Fwww.bing.com\n\n# 查看帮助\nai_browser_demo.exe --help\n",[22,11129,11130,11135,11140,11144,11149,11162,11166,11171,11179,11183,11188],{"__ignoreMap":55},[59,11131,11132],{"class":61,"line":62},[59,11133,11134],{"class":1902},"# 默认有头模式\n",[59,11136,11137],{"class":61,"line":88},[59,11138,11139],{"class":1908},"ai_browser_demo.exe\n",[59,11141,11142],{"class":61,"line":134},[59,11143,174],{"emptyLinePlaceholder":173},[59,11145,11146],{"class":61,"line":140},[59,11147,11148],{"class":1902},"# 隐身模式，自定义端口\n",[59,11150,11151,11154,11157,11159],{"class":61,"line":146},[59,11152,11153],{"class":1908},"ai_browser_demo.exe",[59,11155,11156],{"class":1926}," --headless",[59,11158,1947],{"class":1926},[59,11160,11161],{"class":1926}," 9090\n",[59,11163,11164],{"class":61,"line":281},[59,11165,174],{"emptyLinePlaceholder":173},[59,11167,11168],{"class":61,"line":286},[59,11169,11170],{"class":1902},"# 指定起始 URL\n",[59,11172,11173,11175,11177],{"class":61,"line":292},[59,11174,11153],{"class":1908},[59,11176,1930],{"class":1926},[59,11178,1934],{"class":1933},[59,11180,11181],{"class":61,"line":600},[59,11182,174],{"emptyLinePlaceholder":173},[59,11184,11185],{"class":61,"line":917},[59,11186,11187],{"class":1902},"# 查看帮助\n",[59,11189,11190,11192],{"class":61,"line":922},[59,11191,11153],{"class":1908},[59,11193,1957],{"class":1926},[432,11195],{},[46,11197,11199],{"id":11198},"四核心-api-使用","四、核心 API 使用",[1020,11201,11203],{"id":11202},"_41-日志面板","4.1 日志面板",[18,11205,11206,11207,11209],{},"通过框架 ",[22,11208,10758],{}," 组件创建和使用：",[50,11211,11213],{"className":52,"code":11212,"language":54,"meta":55,"style":55},"#include \"eduwin.h\"\n\nstatic HWND g_logview;\n\ng_logview = create_logview(g_win, x, y, w, h);\n\nlogview_info(g_logview, L\"应用已启动，端口 %d\", 9099);\nlogview_warn(g_logview, L\"连接超时 (conn_id=%d)\", 5);\nlogview_error(g_logview, L\"网络错误\");\n",[22,11214,11215,11219,11223,11228,11232,11237,11241,11245,11249],{"__ignoreMap":55},[59,11216,11217],{"class":61,"line":62},[59,11218,5287],{},[59,11220,11221],{"class":61,"line":88},[59,11222,174],{"emptyLinePlaceholder":173},[59,11224,11225],{"class":61,"line":134},[59,11226,11227],{},"static HWND g_logview;\n",[59,11229,11230],{"class":61,"line":140},[59,11231,174],{"emptyLinePlaceholder":173},[59,11233,11234],{"class":61,"line":146},[59,11235,11236],{},"g_logview = create_logview(g_win, x, y, w, h);\n",[59,11238,11239],{"class":61,"line":281},[59,11240,174],{"emptyLinePlaceholder":173},[59,11242,11243],{"class":61,"line":286},[59,11244,6127],{},[59,11246,11247],{"class":61,"line":292},[59,11248,6132],{},[59,11250,11251],{"class":61,"line":600},[59,11252,6137],{},[18,11254,11255],{},"面板自动：",[315,11257,11258,11261,11264,11267],{},[318,11259,11260],{},"附加时间戳",[318,11262,11263],{},"限制行数（默认 500，超限删最旧）",[318,11265,11266],{},"自动滚底",[318,11268,11269],{},"双击行复制消息内容到剪贴板",[1020,11271,11273],{"id":11272},"_42-ws-hub","4.2 WS Hub",[50,11275,11277],{"className":52,"code":11276,"language":54,"meta":55,"style":55},"ws_hub_t *hub = ws_hub_create(port);\n\n\u002F\u002F 主循环\nwhile (g_running) {\n    int cid = ws_hub_accept(hub, 200);          \u002F\u002F 接受新连接\n    int ready = ws_hub_poll(hub, 200);           \u002F\u002F 轮询消息\n    ws_hub_subscribe(hub, cid, \"agent\");         \u002F\u002F 订阅话题\n    int n = ws_hub_publish(hub, sender, \"agent\", payload, len);  \u002F\u002F 发布\n    ws_hub_disconnect(hub, cid);                 \u002F\u002F 断开连接\n}\n\nws_hub_close(hub);\n",[22,11278,11279,11284,11288,11293,11297,11302,11307,11312,11317,11322,11326,11330],{"__ignoreMap":55},[59,11280,11281],{"class":61,"line":62},[59,11282,11283],{},"ws_hub_t *hub = ws_hub_create(port);\n",[59,11285,11286],{"class":61,"line":88},[59,11287,174],{"emptyLinePlaceholder":173},[59,11289,11290],{"class":61,"line":134},[59,11291,11292],{},"\u002F\u002F 主循环\n",[59,11294,11295],{"class":61,"line":140},[59,11296,7504],{},[59,11298,11299],{"class":61,"line":146},[59,11300,11301],{},"    int cid = ws_hub_accept(hub, 200);          \u002F\u002F 接受新连接\n",[59,11303,11304],{"class":61,"line":281},[59,11305,11306],{},"    int ready = ws_hub_poll(hub, 200);           \u002F\u002F 轮询消息\n",[59,11308,11309],{"class":61,"line":286},[59,11310,11311],{},"    ws_hub_subscribe(hub, cid, \"agent\");         \u002F\u002F 订阅话题\n",[59,11313,11314],{"class":61,"line":292},[59,11315,11316],{},"    int n = ws_hub_publish(hub, sender, \"agent\", payload, len);  \u002F\u002F 发布\n",[59,11318,11319],{"class":61,"line":600},[59,11320,11321],{},"    ws_hub_disconnect(hub, cid);                 \u002F\u002F 断开连接\n",[59,11323,11324],{"class":61,"line":917},[59,11325,149],{},[59,11327,11328],{"class":61,"line":922},[59,11329,174],{"emptyLinePlaceholder":173},[59,11331,11332],{"class":61,"line":928},[59,11333,7608],{},[1020,11335,11337],{"id":11336},"_43-傀儡窗口","4.3 傀儡窗口",[50,11339,11341],{"className":52,"code":11340,"language":54,"meta":55,"style":55},"\u002F\u002F headless 模式下 WebView2 需要有效父窗口\nHWND hwnd = create_hidden_window(1200, 800);\n",[22,11342,11343,11348],{"__ignoreMap":55},[59,11344,11345],{"class":61,"line":62},[59,11346,11347],{},"\u002F\u002F headless 模式下 WebView2 需要有效父窗口\n",[59,11349,11350],{"class":61,"line":88},[59,11351,11352],{},"HWND hwnd = create_hidden_window(1200, 800);\n",[432,11354],{},[46,11356,11358],{"id":11357},"五测试","五、测试",[18,11360,11361],{},"Python 测试脚本位于 demo 目录下：",[4903,11363,11364,11373],{},[4906,11365,11366],{},[4909,11367,11368,11371],{},[4912,11369,11370],{},"脚本",[4912,11372,10556],{},[4919,11374,11375,11385,11395],{},[4909,11376,11377,11382],{},[4924,11378,11379],{},[22,11380,11381],{},"pub.py",[4924,11383,11384],{},"DIP 指令测试客户端（单条模式）",[4909,11386,11387,11392],{},[4924,11388,11389],{},[22,11390,11391],{},"test_ws_pubsub.py",[4924,11393,11394],{},"基础双向通信测试",[4909,11396,11397,11402],{},[4924,11398,11399],{},[22,11400,11401],{},"test_ws_pubsub2.py",[4924,11403,11404],{},"多发进阶测试",[50,11406,11408],{"className":1893,"code":11407,"language":1895,"meta":55,"style":55},"# 单条指令\npython samples\\ai_browser_demo\\pub.py '{\"action\":\"describe\"}'\n",[22,11409,11410,11415],{"__ignoreMap":55},[59,11411,11412],{"class":61,"line":62},[59,11413,11414],{"class":1902},"# 单条指令\n",[59,11416,11417,11420,11423,11426,11429,11432,11435],{"class":61,"line":88},[59,11418,11419],{"class":1908},"python",[59,11421,11422],{"class":1933}," samples",[59,11424,11425],{"class":1926},"\\a",[59,11427,11428],{"class":1933},"i_browser_demo",[59,11430,11431],{"class":1926},"\\p",[59,11433,11434],{"class":1933},"ub.py",[59,11436,11437],{"class":1933}," '{\"action\":\"describe\"}'\n",[432,11439],{},[46,11441,11443],{"id":11442},"六构建","六、构建",[50,11445,11447],{"className":1893,"code":11446,"language":1895,"meta":55,"style":55},"cd D:\\XTU\\c-train\\eduwin\nbuild_lib.bat                 # 先编译框架库\n\ncd D:\\XTU\\c-train\\samples\\ai_browser_demo\nbuild.bat                     # 编译 demo\n",[22,11448,11449,11475,11483,11487,11512],{"__ignoreMap":55},[59,11450,11451,11454,11457,11460,11463,11466,11469,11472],{"class":61,"line":62},[59,11452,11453],{"class":1926},"cd",[59,11455,11456],{"class":1933}," D:",[59,11458,11459],{"class":1926},"\\X",[59,11461,11462],{"class":1933},"TU",[59,11464,11465],{"class":1926},"\\c",[59,11467,11468],{"class":1933},"-train",[59,11470,11471],{"class":1926},"\\e",[59,11473,11474],{"class":1933},"duwin\n",[59,11476,11477,11480],{"class":61,"line":88},[59,11478,11479],{"class":1908},"build_lib.bat",[59,11481,11482],{"class":1902},"                 # 先编译框架库\n",[59,11484,11485],{"class":61,"line":134},[59,11486,174],{"emptyLinePlaceholder":173},[59,11488,11489,11491,11493,11495,11497,11499,11501,11504,11507,11509],{"class":61,"line":140},[59,11490,11453],{"class":1926},[59,11492,11456],{"class":1933},[59,11494,11459],{"class":1926},[59,11496,11462],{"class":1933},[59,11498,11465],{"class":1926},[59,11500,11468],{"class":1933},[59,11502,11503],{"class":1926},"\\s",[59,11505,11506],{"class":1933},"amples",[59,11508,11425],{"class":1926},[59,11510,11511],{"class":1933},"i_browser_demo\n",[59,11513,11514,11516],{"class":61,"line":146},[59,11515,10534],{"class":1908},[59,11517,11518],{"class":1902},"                     # 编译 demo\n",[18,11520,11521,11522],{},"依赖链接库：",[22,11523,11524],{},"eduwin.lib user32.lib gdi32.lib gdiplus.lib comctl32.lib comdlg32.lib shell32.lib winmm.lib ole32.lib oleaut32.lib version.lib advapi32.lib ws2_32.lib",[432,11526],{},[46,11528,11530],{"id":11529},"七已集成的框架模块","七、已集成的框架模块",[4903,11532,11533,11545],{},[4906,11534,11535],{},[4909,11536,11537,11539,11542],{},[4912,11538,10698],{},[4912,11540,11541],{},"文件",[4912,11543,11544],{},"@public_api 函数",[4919,11546,11547,11581,11601,11621,11652,11675],{},[4909,11548,11549,11553,11558],{},[4924,11550,11551],{},[22,11552,10719],{},[4924,11554,11555],{},[22,11556,11557],{},"eduwin_cmd.h\u002Fc",[4924,11559,11560,3980,11563,3980,11566,3980,11569,3980,11572,3980,11575,3980,11578],{},[22,11561,11562],{},"cmd_init",[22,11564,11565],{},"cmd_has",[22,11567,11568],{},"cmd_get",[22,11570,11571],{},"cmd_get_def",[22,11573,11574],{},"cmd_is",[22,11576,11577],{},"cmd_print_help",[22,11579,11580],{},"cmd_cleanup",[4909,11582,11583,11587,11592],{},[4924,11584,11585],{},[22,11586,35],{},[4924,11588,11589],{},[22,11590,11591],{},"eduwin_net.h\u002Fc",[4924,11593,11594,3980,11597,11600],{},[22,11595,11596],{},"ws_server_*",[22,11598,11599],{},"ws_hub_*","（含 ws_hub pub\u002Fsub 全套）",[4909,11602,11603,11607,11612],{},[4924,11604,11605],{},[22,11606,39],{},[4924,11608,11609],{},[22,11610,11611],{},"eduwin_thread.h\u002Fc",[4924,11613,11614,3980,11616,3980,11618],{},[22,11615,43],{},[22,11617,7930],{},[22,11619,11620],{},"thread_cleanup",[4909,11622,11623,11627,11632],{},[4924,11624,11625],{},[22,11626,10709],{},[4924,11628,11629],{},[22,11630,11631],{},"eduwin_app.h\u002Fc",[4924,11633,11634,3980,11637,3980,11640,3980,11643,3980,11645,3980,11648,3980,11650],{},[22,11635,11636],{},"init_app",[22,11638,11639],{},"create_window",[22,11641,11642],{},"create_hidden_window",[22,11644,686],{},[22,11646,11647],{},"run_app",[22,11649,631],{},[22,11651,615],{},[4909,11653,11654,11658,11663],{},[4924,11655,11656],{},[22,11657,10758],{},[4924,11659,11660],{},[22,11661,11662],{},"eduwin_logview.h\u002Fc",[4924,11664,11665,3980,11667,3980,11669,3980,11672],{},[22,11666,3615],{},[22,11668,3515],{},[22,11670,11671],{},"logview_clear",[22,11673,11674],{},"logview_set_max_rows",[4909,11676,11677,11681,11686],{},[4924,11678,11679],{},[22,11680,10738],{},[4924,11682,11683],{},[22,11684,11685],{},"eduwin_webview.h\u002Fc",[4924,11687,11688,3980,11691,3980,11694,3980,11696,3980,11698,3980,11701],{},[22,11689,11690],{},"webview_init",[22,11692,11693],{},"create_webview",[22,11695,9903],{},[22,11697,9863],{},[22,11699,11700],{},"on_navigation_completed",[22,11702,10041],{},[362,11704,11705],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":55,"searchDepth":88,"depth":88,"links":11707},[11708,11711,11717,11718,11723,11724,11725],{"id":10678,"depth":88,"text":10679,"children":11709},[11710],{"id":10773,"depth":134,"text":10773},{"id":10784,"depth":88,"text":10785,"children":11712},[11713,11714,11715,11716],{"id":10797,"depth":134,"text":10798},{"id":10818,"depth":134,"text":10819},{"id":10975,"depth":134,"text":10976},{"id":10985,"depth":134,"text":10986},{"id":11027,"depth":88,"text":11028},{"id":11198,"depth":88,"text":11199,"children":11719},[11720,11721,11722],{"id":11202,"depth":134,"text":11203},{"id":11272,"depth":134,"text":11273},{"id":11336,"depth":134,"text":11337},{"id":11357,"depth":88,"text":11358},{"id":11442,"depth":88,"text":11443},{"id":11529,"depth":88,"text":11530},{},"\u002Fdocs\u002Fsamples\u002Fai_browser_demo",{"title":10651,"description":55},"docs\u002Fsamples\u002Fai_browser_demo","2IdQk2SnTzwBPAwP67XnZZH_koTw2_tScSv49ub2S1w",{"id":11732,"title":11733,"body":11734,"description":11835,"extension":374,"meta":11836,"navigation":173,"path":11837,"seo":11838,"stem":11839,"__hash__":11840},"content\u002Fdocs\u002Fsamples\u002Fai_chat_demo.md","ai_chat_demo —— AI 对话示例",{"type":8,"value":11735,"toc":11829},[11736,11739,11745,11748,11759,11762,11767,11770,11802,11805,11820,11827],[11,11737,11733],{"id":11738},"ai_chat_demo-ai-对话示例",[18,11740,11741,11742,11744],{},"调用大模型的入门示例，展示如何使用 ",[22,11743,312],{}," 模块在 C 程序中集成 AI 对话能力。",[46,11746,11747],{"id":11747},"功能",[315,11749,11750,11753,11756],{},[318,11751,11752],{},"在文本框中输入问题，点击按钮发送给大模型",[318,11754,11755],{},"同步调用，在单独的线程中执行，不阻塞窗口 UI",[318,11757,11758],{},"显示请求耗时",[46,11760,11761],{"id":11761},"源码位置",[18,11763,11764],{},[22,11765,11766],{},"d:\\XTU\\c-train\\samples\\ai_chat_demo\\ai_chat_demo.c",[46,11768,11769],{"id":11769},"涉及模块",[4903,11771,11772,11780],{},[4906,11773,11774],{},[4909,11775,11776,11778],{},[4912,11777,10698],{},[4912,11779,10556],{},[4919,11781,11782,11792],{},[4909,11783,11784,11789],{},[4924,11785,11786],{},[22,11787,11788],{},"ai",[4924,11790,11791],{},"AI 大模型调用",[4909,11793,11794,11799],{},[4924,11795,11796],{},[22,11797,11798],{},"net",[4924,11800,11801],{},"HTTP 传输层",[46,11803,11804],{"id":11804},"编译",[50,11806,11808],{"className":7634,"code":11807,"language":7636,"meta":55,"style":55},"cd samples\\ai_chat_demo\nbuild.bat\n",[22,11809,11810,11815],{"__ignoreMap":55},[59,11811,11812],{"class":61,"line":62},[59,11813,11814],{},"cd samples\\ai_chat_demo\n",[59,11816,11817],{"class":61,"line":88},[59,11818,11819],{},"build.bat\n",[18,11821,11822,11823,11826],{},"编译后需确保 ",[22,11824,11825],{},"api_key.txt"," 文件存在（与 exe 同目录），内容为 DeepSeek API Key。",[362,11828,364],{},{"title":55,"searchDepth":88,"depth":88,"links":11830},[11831,11832,11833,11834],{"id":11747,"depth":88,"text":11747},{"id":11761,"depth":88,"text":11761},{"id":11769,"depth":88,"text":11769},{"id":11804,"depth":88,"text":11804},"调用大模型的入门示例，展示如何使用 eduwin_ai 模块在 C 程序中集成 AI 对话能力。",{},"\u002Fdocs\u002Fsamples\u002Fai_chat_demo",{"title":11733,"description":11835},"docs\u002Fsamples\u002Fai_chat_demo","Bb_7-mJjUKyZEJ__dtfNeZBdX3o0txxv8zGnYuNVh8E",{"id":11842,"title":11843,"body":11844,"description":11851,"extension":374,"meta":11904,"navigation":173,"path":11905,"seo":11906,"stem":11907,"__hash__":11908},"content\u002Fdocs\u002Fsamples\u002Fcanvas_demo.md","canvas_demo —— 画布综合示例",{"type":8,"value":11845,"toc":11899},[11846,11849,11852,11854,11874,11876,11881,11883,11897],[11,11847,11843],{"id":11848},"canvas_demo-画布综合示例",[18,11850,11851],{},"展示 eduwin 画布的核心绘图能力：矩形、椭圆、直线、文字、局部清除等。",[46,11853,11747],{"id":11747},[315,11855,11856,11859,11862,11865,11868],{},[318,11857,11858],{},"矩形填充 + 轮廓绘制",[318,11860,11861],{},"椭圆填充 + 轮廓绘制",[318,11863,11864],{},"直线绘制（不同颜色和线宽）",[318,11866,11867],{},"文字绘制（中文 + 英文）",[318,11869,11870,11873],{},[22,11871,11872],{},"clear_rect"," 局部清除演示",[46,11875,11761],{"id":11761},[18,11877,11878],{},[22,11879,11880],{},"d:\\XTU\\c-train\\samples\\canvas_demo\\canvas_demo.c",[46,11882,11804],{"id":11804},[50,11884,11886],{"className":7634,"code":11885,"language":7636,"meta":55,"style":55},"cd samples\\canvas_demo\nbuild.bat\n",[22,11887,11888,11893],{"__ignoreMap":55},[59,11889,11890],{"class":61,"line":62},[59,11891,11892],{},"cd samples\\canvas_demo\n",[59,11894,11895],{"class":61,"line":88},[59,11896,11819],{},[362,11898,364],{},{"title":55,"searchDepth":88,"depth":88,"links":11900},[11901,11902,11903],{"id":11747,"depth":88,"text":11747},{"id":11761,"depth":88,"text":11761},{"id":11804,"depth":88,"text":11804},{},"\u002Fdocs\u002Fsamples\u002Fcanvas_demo",{"title":11843,"description":11851},"docs\u002Fsamples\u002Fcanvas_demo","fd_1fcyHtUol0wo5xTXkV9352xuYcqsWgkEVLbxTXbI",{"id":11910,"title":11911,"body":11912,"description":55,"extension":374,"meta":12134,"navigation":173,"path":12135,"seo":12136,"stem":12137,"__hash__":12138},"content\u002Fdocs\u002Fsamples\u002Fcmd_demo.md","cmd_demo — 命令行参数解析",{"type":8,"value":11913,"toc":12126},[11914,11917,11931,11933,11935,11944,11947,11953,11955,11959,11965,11967,11971,12058,12060,12064,12123],[11,11915,11911],{"id":11916},"cmd_demo-命令行参数解析",[15,11918,11919],{},[18,11920,11921,624,11923,11926,624,11928,11930],{},[69,11922,10663],{},[22,11924,11925],{},"samples\u002Fcmd_demo\u002F",[69,11927,10668],{},[22,11929,10719],{}," 命令行参数解析模块",[432,11932],{},[46,11934,10679],{"id":10678},[18,11936,11937,11940,11941,11943],{},[22,11938,11939],{},"cmd_demo"," 演示如何使用 ",[22,11942,10719],{}," 模块解析命令行参数。窗口逐行展示各参数的值（检测到即显示 ✔ + 值，未检测到显示默认值）。",[1020,11945,11946],{"id":11946},"支持的参数格式",[50,11948,11951],{"className":11949,"code":11950,"language":358},[356],"--key value          \u002F\u002F 带值参数\n--flag               \u002F\u002F 开关参数\n--key=\"value with space\"  \u002F\u002F 带空格的参数\n--help               \u002F\u002F 内置帮助\n",[22,11952,11950],{"__ignoreMap":55},[432,11954],{},[46,11956,11958],{"id":11957},"二运行效果","二、运行效果",[50,11960,11963],{"className":11961,"code":11962,"language":358},[356],"> cmd_demo.exe\n    起始 URL: about:blank (默认)\n    WS 端口: 9099 (默认)\n    窗口标题: AI Browser (默认)\n    应用名称: (未设置) (默认)\n    显示模式: (未设置) (默认)\n    隐身模式: (未设置) (默认)\n\n> cmd_demo.exe --visible --url https:\u002F\u002Fwww.bing.com\n✔  起始 URL: https:\u002F\u002Fwww.bing.com\n    WS 端口: 9099 (默认)\n    窗口标题: AI Browser (默认)\n    应用名称: (未设置) (默认)\n✔  显示模式: 已启用\n    隐身模式: (未设置) (默认)\n",[22,11964,11962],{"__ignoreMap":55},[432,11966],{},[46,11968,11970],{"id":11969},"三核心代码","三、核心代码",[50,11972,11974],{"className":52,"code":11973,"language":54,"meta":55,"style":55},"#include \"eduwin.h\"\n\n\u002F\u002F 1. 解析命令行参数\ncmd_init();\n\n\u002F\u002F 2. 初始化 eduwin\ninit_app();\nHWND win = create_window(L\"cmd_demo — 命令行参数\", 500, 360);\n\n\u002F\u002F 3. 查询参数\nint headless = cmd_is(L\"--headless\");\nconst WCHAR *url = cmd_get_def(L\"--url\", L\"about:blank\");\nint port = _wtoi(cmd_get_def(L\"--port\", L\"9099\"));\n\n\u002F\u002F 4. 显示窗口\nshow_window(win);\nrun_app();\n\ncmd_cleanup();\n",[22,11975,11976,11980,11984,11989,11993,11997,12002,12006,12011,12015,12020,12024,12028,12032,12036,12041,12046,12050,12054],{"__ignoreMap":55},[59,11977,11978],{"class":61,"line":62},[59,11979,5287],{},[59,11981,11982],{"class":61,"line":88},[59,11983,174],{"emptyLinePlaceholder":173},[59,11985,11986],{"class":61,"line":134},[59,11987,11988],{},"\u002F\u002F 1. 解析命令行参数\n",[59,11990,11991],{"class":61,"line":140},[59,11992,2068],{},[59,11994,11995],{"class":61,"line":146},[59,11996,174],{"emptyLinePlaceholder":173},[59,11998,11999],{"class":61,"line":281},[59,12000,12001],{},"\u002F\u002F 2. 初始化 eduwin\n",[59,12003,12004],{"class":61,"line":286},[59,12005,7845],{},[59,12007,12008],{"class":61,"line":292},[59,12009,12010],{},"HWND win = create_window(L\"cmd_demo — 命令行参数\", 500, 360);\n",[59,12012,12013],{"class":61,"line":600},[59,12014,174],{"emptyLinePlaceholder":173},[59,12016,12017],{"class":61,"line":917},[59,12018,12019],{},"\u002F\u002F 3. 查询参数\n",[59,12021,12022],{"class":61,"line":922},[59,12023,2110],{},[59,12025,12026],{"class":61,"line":928},[59,12027,2100],{},[59,12029,12030],{"class":61,"line":933},[59,12031,2105],{},[59,12033,12034],{"class":61,"line":939},[59,12035,174],{"emptyLinePlaceholder":173},[59,12037,12038],{"class":61,"line":1320},[59,12039,12040],{},"\u002F\u002F 4. 显示窗口\n",[59,12042,12043],{"class":61,"line":1326},[59,12044,12045],{},"show_window(win);\n",[59,12047,12048],{"class":61,"line":1331},[59,12049,7860],{},[59,12051,12052],{"class":61,"line":1336},[59,12053,174],{"emptyLinePlaceholder":173},[59,12055,12056],{"class":61,"line":1342},[59,12057,2119],{},[432,12059],{},[46,12061,12063],{"id":12062},"四构建","四、构建",[50,12065,12067],{"className":1893,"code":12066,"language":1895,"meta":55,"style":55},"cd D:\\XTU\\c-train\\eduwin\nbuild_lib.bat\n\ncd D:\\XTU\\c-train\\samples\\cmd_demo\nbuild.bat\n",[22,12068,12069,12087,12092,12096,12119],{"__ignoreMap":55},[59,12070,12071,12073,12075,12077,12079,12081,12083,12085],{"class":61,"line":62},[59,12072,11453],{"class":1926},[59,12074,11456],{"class":1933},[59,12076,11459],{"class":1926},[59,12078,11462],{"class":1933},[59,12080,11465],{"class":1926},[59,12082,11468],{"class":1933},[59,12084,11471],{"class":1926},[59,12086,11474],{"class":1933},[59,12088,12089],{"class":61,"line":88},[59,12090,12091],{"class":1908},"build_lib.bat\n",[59,12093,12094],{"class":61,"line":134},[59,12095,174],{"emptyLinePlaceholder":173},[59,12097,12098,12100,12102,12104,12106,12108,12110,12112,12114,12116],{"class":61,"line":140},[59,12099,11453],{"class":1926},[59,12101,11456],{"class":1933},[59,12103,11459],{"class":1926},[59,12105,11462],{"class":1933},[59,12107,11465],{"class":1926},[59,12109,11468],{"class":1933},[59,12111,11503],{"class":1926},[59,12113,11506],{"class":1933},[59,12115,11465],{"class":1926},[59,12117,12118],{"class":1933},"md_demo\n",[59,12120,12121],{"class":61,"line":146},[59,12122,11819],{"class":1908},[362,12124,12125],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":55,"searchDepth":88,"depth":88,"links":12127},[12128,12131,12132,12133],{"id":10678,"depth":88,"text":10679,"children":12129},[12130],{"id":11946,"depth":134,"text":11946},{"id":11957,"depth":88,"text":11958},{"id":11969,"depth":88,"text":11970},{"id":12062,"depth":88,"text":12063},{},"\u002Fdocs\u002Fsamples\u002Fcmd_demo",{"title":11911,"description":55},"docs\u002Fsamples\u002Fcmd_demo","HF6ti80m6-JYkaEk4_-8oVbHCVYr-8ypOb7ep9rgg3s",{"id":12140,"title":12141,"body":12142,"description":12286,"extension":374,"meta":12287,"navigation":173,"path":12288,"seo":12289,"stem":12290,"__hash__":12291},"content\u002Fdocs\u002Fsamples\u002Fd3d_gltf_demo.md","gltf_demo —— glTF 模型加载",{"type":8,"value":12143,"toc":12279},[12144,12147,12157,12159,12170,12172,12177,12180,12237,12241,12277],[11,12145,12141],{"id":12146},"gltf_demo-gltf-模型加载",[18,12148,12149,12150,12153,12154,12156],{},"加载外部 glTF 模型（",[22,12151,12152],{},".glb"," 格式），展示 eduwin 的 ",[22,12155,4778],{}," API 用法。",[46,12158,11747],{"id":11747},[315,12160,12161,12164,12167],{},[318,12162,12163],{},"加载三个 glTF 模型横排展示",[318,12165,12166],{},"鼠标拖拽旋转视角、滚轮缩放",[318,12168,12169],{},"六个预设视角快捷键（顶\u002F底\u002F前\u002F后\u002F左\u002F右）",[46,12171,11761],{"id":11761},[18,12173,12174],{},[22,12175,12176],{},"d:\\XTU\\c-train\\samples\\d3d_gltf_demo\\",[1020,12178,12179],{"id":12179},"关键代码",[50,12181,12183],{"className":52,"code":12182,"language":54,"meta":55,"style":55},"\u002F* 加载模型 *\u002F\nint count;\ndx_geometry **geos = dx_load_gltf(\"Soldier.glb\", &count);\n\n\u002F* 创建网格对象 *\u002F\nfor (int i = 0; i \u003C count; i++)\n    g_objs[i] = dx_create_mesh(geos[i], NULL);\n\n\u002F* 每帧渲染 *\u002F\nfor (int i = 0; i \u003C g_obj_count; i++)\n    dx_mesh_draw(g_objs[i]);\n",[22,12184,12185,12190,12194,12199,12203,12208,12213,12218,12222,12227,12232],{"__ignoreMap":55},[59,12186,12187],{"class":61,"line":62},[59,12188,12189],{},"\u002F* 加载模型 *\u002F\n",[59,12191,12192],{"class":61,"line":88},[59,12193,4791],{},[59,12195,12196],{"class":61,"line":134},[59,12197,12198],{},"dx_geometry **geos = dx_load_gltf(\"Soldier.glb\", &count);\n",[59,12200,12201],{"class":61,"line":140},[59,12202,174],{"emptyLinePlaceholder":173},[59,12204,12205],{"class":61,"line":146},[59,12206,12207],{},"\u002F* 创建网格对象 *\u002F\n",[59,12209,12210],{"class":61,"line":281},[59,12211,12212],{},"for (int i = 0; i \u003C count; i++)\n",[59,12214,12215],{"class":61,"line":286},[59,12216,12217],{},"    g_objs[i] = dx_create_mesh(geos[i], NULL);\n",[59,12219,12220],{"class":61,"line":292},[59,12221,174],{"emptyLinePlaceholder":173},[59,12223,12224],{"class":61,"line":600},[59,12225,12226],{},"\u002F* 每帧渲染 *\u002F\n",[59,12228,12229],{"class":61,"line":917},[59,12230,12231],{},"for (int i = 0; i \u003C g_obj_count; i++)\n",[59,12233,12234],{"class":61,"line":922},[59,12235,12236],{},"    dx_mesh_draw(g_objs[i]);\n",[46,12238,12240],{"id":12239},"使用的-api","使用的 API",[315,12242,12243,12258,12264,12271],{},[318,12244,12245,12247,12248,735,12250,12253,12254,12257],{},[22,12246,4778],{}," — 加载 ",[22,12249,12152],{},[22,12251,12252],{},".gltf"," 格式的 3D 模型，支持 data URI 和外部 ",[22,12255,12256],{},".bin"," 文件",[318,12259,12260,12263],{},[22,12261,12262],{},"dx_free_geometries"," — 释放整个 geometry 数组",[318,12265,12266,735,12268,12270],{},[22,12267,4336],{},[22,12269,4310],{}," — 网格封装渲染",[318,12272,12273,12276],{},[22,12274,12275],{},"dx_orbit"," — 轨道控制器",[362,12278,364],{},{"title":55,"searchDepth":88,"depth":88,"links":12280},[12281,12282,12285],{"id":11747,"depth":88,"text":11747},{"id":11761,"depth":88,"text":11761,"children":12283},[12284],{"id":12179,"depth":134,"text":12179},{"id":12239,"depth":88,"text":12240},"加载外部 glTF 模型（.glb 格式），展示 eduwin 的 dx_load_gltf API 用法。",{},"\u002Fdocs\u002Fsamples\u002Fd3d_gltf_demo",{"title":12141,"description":12286},"docs\u002Fsamples\u002Fd3d_gltf_demo","uNz88oWEQpT2flo844a8FVxl8oJwckZSNFiRA1kZSe0",{"id":12293,"title":12294,"body":12295,"description":12302,"extension":374,"meta":12349,"navigation":173,"path":12350,"seo":12351,"stem":12352,"__hash__":12353},"content\u002Fdocs\u002Fsamples\u002Fd3d_light_demo.md","d3d_light_demo —— 动态光照演示",{"type":8,"value":12296,"toc":12344},[12297,12300,12303,12305,12319,12321,12326,12328,12342],[11,12298,12294],{"id":12299},"d3d_light_demo-动态光照演示",[18,12301,12302],{},"展示 eduwin D3D 的动态光照效果：点光源绕物体旋转，立方体表面光影随之变化。",[46,12304,11747],{"id":11747},[315,12306,12307,12310,12313,12316],{},[318,12308,12309],{},"地面 + 旋转立方体",[318,12311,12312],{},"环境光恒定照明",[318,12314,12315],{},"方向光从固定方向照射",[318,12317,12318],{},"点光源围绕立方体旋转，颜色为暖色（橙红）",[46,12320,11761],{"id":11761},[18,12322,12323],{},[22,12324,12325],{},"d:\\XTU\\c-train\\samples\\d3d_light_demo\\main.c",[46,12327,11804],{"id":11804},[50,12329,12331],{"className":7634,"code":12330,"language":7636,"meta":55,"style":55},"cd samples\\d3d_light_demo\nbuild.bat\n",[22,12332,12333,12338],{"__ignoreMap":55},[59,12334,12335],{"class":61,"line":62},[59,12336,12337],{},"cd samples\\d3d_light_demo\n",[59,12339,12340],{"class":61,"line":88},[59,12341,11819],{},[362,12343,364],{},{"title":55,"searchDepth":88,"depth":88,"links":12345},[12346,12347,12348],{"id":11747,"depth":88,"text":11747},{"id":11761,"depth":88,"text":11761},{"id":11804,"depth":88,"text":11804},{},"\u002Fdocs\u002Fsamples\u002Fd3d_light_demo",{"title":12294,"description":12302},"docs\u002Fsamples\u002Fd3d_light_demo","a5cSSTA2IB7d2gU_WyvMk-cS3ZO2ooSjJUOJgZgfWa8",{"id":12355,"title":12356,"body":12357,"description":12364,"extension":374,"meta":12408,"navigation":173,"path":12409,"seo":12410,"stem":12411,"__hash__":12412},"content\u002Fdocs\u002Fsamples\u002Fd3d_material_demo.md","d3d_material_demo —— 纹理材质演示",{"type":8,"value":12358,"toc":12403},[12359,12362,12365,12367,12378,12380,12385,12387,12401],[11,12360,12356],{"id":12361},"d3d_material_demo-纹理材质演示",[18,12363,12364],{},"展示程序生成 BMP 纹理并加载为材质的流程：C 代码生成棋盘格、圆点、条纹、渐变等纹理图案，然后贴到球体上展示。",[46,12366,11747],{"id":11747},[315,12368,12369,12372,12375],{},[318,12370,12371],{},"程序生成 5 种纹理 BMP（棋盘格、迷彩、圆点、条纹、渐变）",[318,12373,12374],{},"将纹理加载为材质，贴到球体表面",[318,12376,12377],{},"多个球体并排展示不同材质效果",[46,12379,11761],{"id":11761},[18,12381,12382],{},[22,12383,12384],{},"d:\\XTU\\c-train\\samples\\d3d_material_demo\\main.c",[46,12386,11804],{"id":11804},[50,12388,12390],{"className":7634,"code":12389,"language":7636,"meta":55,"style":55},"cd samples\\d3d_material_demo\nbuild.bat\n",[22,12391,12392,12397],{"__ignoreMap":55},[59,12393,12394],{"class":61,"line":62},[59,12395,12396],{},"cd samples\\d3d_material_demo\n",[59,12398,12399],{"class":61,"line":88},[59,12400,11819],{},[362,12402,364],{},{"title":55,"searchDepth":88,"depth":88,"links":12404},[12405,12406,12407],{"id":11747,"depth":88,"text":11747},{"id":11761,"depth":88,"text":11761},{"id":11804,"depth":88,"text":11804},{},"\u002Fdocs\u002Fsamples\u002Fd3d_material_demo",{"title":12356,"description":12364},"docs\u002Fsamples\u002Fd3d_material_demo","UmGq8Q6wkms_DFNdOsUiR1Aowxos5NOaFO1-LOsyMVk",{"id":12414,"title":12415,"body":12416,"description":12470,"extension":374,"meta":12471,"navigation":173,"path":12472,"seo":12473,"stem":12474,"__hash__":12475},"content\u002Fdocs\u002Fsamples\u002Fd3d_shape_demo.md","d3d_shape_demo —— 基本体形状展示",{"type":8,"value":12417,"toc":12465},[12418,12421,12427,12429,12440,12442,12447,12449,12463],[11,12419,12415],{"id":12420},"d3d_shape_demo-基本体形状展示",[18,12422,349,12423,12426],{},[22,12424,12425],{},"d3d_light_demo"," 类似，展示地面 + 四种基本体的组合渲染，旋转速度微调后做对比。",[46,12428,11747],{"id":11747},[315,12430,12431,12434,12437],{},[318,12432,12433],{},"地面 + 长方体、球体、圆柱体、棱锥",[318,12435,12436],{},"环境光 + 方向光 + 点光源",[318,12438,12439],{},"每种基本体以不同速度旋转",[46,12441,11761],{"id":11761},[18,12443,12444],{},[22,12445,12446],{},"d:\\XTU\\c-train\\samples\\d3d_shape_demo\\main.c",[46,12448,11804],{"id":11804},[50,12450,12452],{"className":7634,"code":12451,"language":7636,"meta":55,"style":55},"cd samples\\d3d_shape_demo\nbuild.bat\n",[22,12453,12454,12459],{"__ignoreMap":55},[59,12455,12456],{"class":61,"line":62},[59,12457,12458],{},"cd samples\\d3d_shape_demo\n",[59,12460,12461],{"class":61,"line":88},[59,12462,11819],{},[362,12464,364],{},{"title":55,"searchDepth":88,"depth":88,"links":12466},[12467,12468,12469],{"id":11747,"depth":88,"text":11747},{"id":11761,"depth":88,"text":11761},{"id":11804,"depth":88,"text":11804},"与 d3d_light_demo 类似，展示地面 + 四种基本体的组合渲染，旋转速度微调后做对比。",{},"\u002Fdocs\u002Fsamples\u002Fd3d_shape_demo",{"title":12415,"description":12470},"docs\u002Fsamples\u002Fd3d_shape_demo","Wyxe3xMIfUFzx1J-UICbMwy7lV5nsqKL8lU-bSuVhEY",{"id":12477,"title":12478,"body":12479,"description":12486,"extension":374,"meta":12539,"navigation":173,"path":12540,"seo":12541,"stem":12542,"__hash__":12543},"content\u002Fdocs\u002Fsamples\u002Ffighter_demo.md","fighter_demo —— 飞机射击游戏",{"type":8,"value":12480,"toc":12534},[12481,12484,12487,12489,12509,12511,12516,12518,12532],[11,12482,12478],{"id":12483},"fighter_demo-飞机射击游戏",[18,12485,12486],{},"一个完整的 2D 射击游戏，展示 eduwin 精灵系统、键盘输入、碰撞检测、音效等能力。",[46,12488,11747],{"id":11747},[315,12490,12491,12494,12497,12500,12503,12506],{},[318,12492,12493],{},"WASD 控制英雄移动",[318,12495,12496],{},"J 键发射子弹",[318,12498,12499],{},"敌机随机生成，带不同颜色叠加",[318,12501,12502],{},"子弹与敌机碰撞检测",[318,12504,12505],{},"得分和生命值 HUD",[318,12507,12508],{},"背景音乐和音效",[46,12510,11761],{"id":11761},[18,12512,12513],{},[22,12514,12515],{},"d:\\XTU\\c-train\\samples\\fighter_demo\\fighter_demo.c",[46,12517,11804],{"id":11804},[50,12519,12521],{"className":7634,"code":12520,"language":7636,"meta":55,"style":55},"cd samples\\fighter_demo\nbuild.bat\n",[22,12522,12523,12528],{"__ignoreMap":55},[59,12524,12525],{"class":61,"line":62},[59,12526,12527],{},"cd samples\\fighter_demo\n",[59,12529,12530],{"class":61,"line":88},[59,12531,11819],{},[362,12533,364],{},{"title":55,"searchDepth":88,"depth":88,"links":12535},[12536,12537,12538],{"id":11747,"depth":88,"text":11747},{"id":11761,"depth":88,"text":11761},{"id":11804,"depth":88,"text":11804},{},"\u002Fdocs\u002Fsamples\u002Ffighter_demo",{"title":12478,"description":12486},"docs\u002Fsamples\u002Ffighter_demo","diGjiYuOiVAxswJAPGYeksncVhZ7PsfHpDPxUt7fk6s",{"id":12545,"title":12546,"body":12547,"description":12554,"extension":374,"meta":12595,"navigation":173,"path":12596,"seo":12597,"stem":12598,"__hash__":12599},"content\u002Fdocs\u002Fsamples\u002Fhello_demo.md","hello_demo —— 第一个程序",{"type":8,"value":12548,"toc":12590},[12549,12552,12555,12557,12565,12567,12572,12574,12588],[11,12550,12546],{"id":12551},"hello_demo-第一个程序",[18,12553,12554],{},"eduwin 的入门示例，展示窗口创建、标签和按钮等基础控件的使用。",[46,12556,11747],{"id":11747},[315,12558,12559,12562],{},[318,12560,12561],{},"创建一个窗口，包含一个文本框、一个标签和一个按钮",[318,12563,12564],{},"点击按钮时，将文本框的内容显示到标签上",[46,12566,11761],{"id":11761},[18,12568,12569],{},[22,12570,12571],{},"d:\\XTU\\c-train\\samples\\hello_demo\\hello.c",[46,12573,11804],{"id":11804},[50,12575,12577],{"className":7634,"code":12576,"language":7636,"meta":55,"style":55},"cd samples\\hello_demo\nbuild.bat\n",[22,12578,12579,12584],{"__ignoreMap":55},[59,12580,12581],{"class":61,"line":62},[59,12582,12583],{},"cd samples\\hello_demo\n",[59,12585,12586],{"class":61,"line":88},[59,12587,11819],{},[362,12589,364],{},{"title":55,"searchDepth":88,"depth":88,"links":12591},[12592,12593,12594],{"id":11747,"depth":88,"text":11747},{"id":11761,"depth":88,"text":11761},{"id":11804,"depth":88,"text":11804},{},"\u002Fdocs\u002Fsamples\u002Fhello_demo",{"title":12546,"description":12554},"docs\u002Fsamples\u002Fhello_demo","XsDzCNU_iBKbKr-C_vkQ1N2aK4rB0SH6D9qbxA1USbM",{"id":12601,"title":12602,"body":12603,"description":12742,"extension":374,"meta":12743,"navigation":173,"path":12744,"seo":12745,"stem":12746,"__hash__":12747},"content\u002Fdocs\u002Fsamples\u002Fi18n_demo.md","i18n_demo —— 国际化示例",{"type":8,"value":12604,"toc":12736},[12605,12608,12615,12617,12632,12636,12708,12710,12718,12720,12734],[11,12606,12602],{"id":12607},"i18n_demo-国际化示例",[18,12609,12610,12611,12614],{},"展示 eduwin 内置的 i18n 多语言支持功能，基于 JSON 翻译文件和 ",[22,12612,12613],{},"i18n_get_str"," 实现界面文字国际化。",[46,12616,11747],{"id":11747},[315,12618,12619,12626,12629],{},[318,12620,12621,12622,12625],{},"加载 ",[22,12623,12624],{},"lang.json"," 翻译文件，支持中文、英文、日文三种语言",[318,12627,12628],{},"点击按钮即时切换界面语言，无需重启",[318,12630,12631],{},"翻译 id 查找不到时自动回退到中文",[46,12633,12635],{"id":12634},"json-翻译文件格式","JSON 翻译文件格式",[50,12637,12639],{"className":5067,"code":12638,"language":5069,"meta":55,"style":55},"{\n    \"current\": \"zh\",\n    \"labels\": [\n        {\"id\": \"hello\", \"zh\": \"你好，世界！\", \"en\": \"Hello, World!\", \"jp\": \"こんにちは、世界！\"}\n    ]\n}\n",[22,12640,12641,12645,12655,12661,12700,12704],{"__ignoreMap":55},[59,12642,12643],{"class":61,"line":62},[59,12644,574],{"class":5076},[59,12646,12647,12649,12651,12653],{"class":61,"line":88},[59,12648,5081],{"class":1926},[59,12650,5084],{"class":5076},[59,12652,5087],{"class":1933},[59,12654,5090],{"class":5076},[59,12656,12657,12659],{"class":61,"line":134},[59,12658,5095],{"class":1926},[59,12660,5098],{"class":5076},[59,12662,12663,12665,12667,12669,12671,12673,12675,12677,12680,12682,12684,12686,12689,12691,12693,12695,12698],{"class":61,"line":140},[59,12664,5103],{"class":5076},[59,12666,5106],{"class":1926},[59,12668,5084],{"class":5076},[59,12670,5111],{"class":1933},[59,12672,3980],{"class":5076},[59,12674,5087],{"class":1926},[59,12676,5084],{"class":5076},[59,12678,12679],{"class":1933},"\"你好，世界！\"",[59,12681,3980],{"class":5076},[59,12683,5125],{"class":1926},[59,12685,5084],{"class":5076},[59,12687,12688],{"class":1933},"\"Hello, World!\"",[59,12690,3980],{"class":5076},[59,12692,5134],{"class":1926},[59,12694,5084],{"class":5076},[59,12696,12697],{"class":1933},"\"こんにちは、世界！\"",[59,12699,149],{"class":5076},[59,12701,12702],{"class":61,"line":146},[59,12703,5177],{"class":5076},[59,12705,12706],{"class":61,"line":281},[59,12707,149],{"class":5076},[46,12709,11761],{"id":11761},[18,12711,12712,12715],{},[22,12713,12714],{},"d:\\XTU\\c-train\\samples\\i18n_demo\\main.c",[22,12716,12717],{},"d:\\XTU\\c-train\\samples\\i18n_demo\\lang.json",[46,12719,11804],{"id":11804},[50,12721,12723],{"className":7634,"code":12722,"language":7636,"meta":55,"style":55},"cd samples\\i18n_demo\nbuild.bat\n",[22,12724,12725,12730],{"__ignoreMap":55},[59,12726,12727],{"class":61,"line":62},[59,12728,12729],{},"cd samples\\i18n_demo\n",[59,12731,12732],{"class":61,"line":88},[59,12733,11819],{},[362,12735,5382],{},{"title":55,"searchDepth":88,"depth":88,"links":12737},[12738,12739,12740,12741],{"id":11747,"depth":88,"text":11747},{"id":12634,"depth":88,"text":12635},{"id":11761,"depth":88,"text":11761},{"id":11804,"depth":88,"text":11804},"展示 eduwin 内置的 i18n 多语言支持功能，基于 JSON 翻译文件和 i18n_get_str 实现界面文字国际化。",{},"\u002Fdocs\u002Fsamples\u002Fi18n_demo",{"title":12602,"description":12742},"docs\u002Fsamples\u002Fi18n_demo","IcJaHj6Nrky6Jqlq-Q1nauZpFZlklyVngZ6MxPJ2UXk",{"id":12749,"title":12750,"body":12751,"description":12758,"extension":374,"meta":12932,"navigation":173,"path":12933,"seo":12934,"stem":12935,"__hash__":12936},"content\u002Fdocs\u002Fsamples\u002Fjson_demo.md","json_demo —— JSON 读写示例",{"type":8,"value":12752,"toc":12926},[12753,12756,12759,12761,12785,12789,12898,12900,12908,12910,12924],[11,12754,12750],{"id":12755},"json_demo-json-读写示例",[18,12757,12758],{},"演示 eduwin JSON API 的完整流程：读取文件 → 解析 JSON → 显示内容 → 构建新 JSON → 写回文件。",[46,12760,11747],{"id":11747},[315,12762,12763,12770],{},[318,12764,12765,12766,12769],{},"「加载 JSON」按钮：读取 ",[22,12767,12768],{},"data.json","，解析后显示格式化的 JSON 文本，并在状态栏显示各字段值",[318,12771,12772,12773,735,12776,12779,12780,12782,12783],{},"「修改并保存」按钮：用 ",[22,12774,12775],{},"json_create_*",[22,12777,12778],{},"json_add_*"," 构建新 JSON 对象，通过 ",[22,12781,8523],{}," 写回 ",[22,12784,12768],{},[46,12786,12788],{"id":12787},"涉及-api","涉及 API",[4903,12790,12791,12800],{},[4906,12792,12793],{},[4909,12794,12795,12798],{},[4912,12796,12797],{},"分类",[4912,12799,6071],{},[4919,12801,12802,12812,12825,12836,12852,12868,12889],{},[4909,12803,12804,12806],{},[4924,12805,8486],{},[4924,12807,12808,735,12810],{},[22,12809,8509],{},[22,12811,8523],{},[4909,12813,12814,12817],{},[4924,12815,12816],{},"编码转换",[4924,12818,12819,735,12822],{},[22,12820,12821],{},"wtou8",[22,12823,12824],{},"u8tow",[4909,12826,12827,12830],{},[4924,12828,12829],{},"JSON 解析",[4924,12831,12832,735,12834],{},[22,12833,5623],{},[22,12835,5631],{},[4909,12837,12838,12841],{},[4924,12839,12840],{},"JSON 读取",[4924,12842,12843,735,12846,735,12849],{},[22,12844,12845],{},"json_get_str",[22,12847,12848],{},"json_get_int",[22,12850,12851],{},"json_get_double",[4909,12853,12854,12857],{},[4924,12855,12856],{},"JSON 构建",[4924,12858,12859,735,12862,735,12865],{},[22,12860,12861],{},"json_create_obj",[22,12863,12864],{},"json_create_arr",[22,12866,12867],{},"json_create_str",[4909,12869,12870,12873],{},[4924,12871,12872],{},"JSON 添加",[4924,12874,12875,735,12878,735,12881,735,12884,735,12886],{},[22,12876,12877],{},"json_add_str",[22,12879,12880],{},"json_add_int",[22,12882,12883],{},"json_add_double",[22,12885,5866],{},[22,12887,12888],{},"json_arr_add",[4909,12890,12891,12894],{},[4924,12892,12893],{},"JSON 输出",[4924,12895,12896],{},[22,12897,5966],{},[46,12899,11761],{"id":11761},[18,12901,12902,12905],{},[22,12903,12904],{},"d:\\XTU\\c-train\\samples\\json_demo\\main.c",[22,12906,12907],{},"d:\\XTU\\c-train\\samples\\json_demo\\data.json",[46,12909,11804],{"id":11804},[50,12911,12913],{"className":7634,"code":12912,"language":7636,"meta":55,"style":55},"cd samples\\json_demo\nbuild.bat\n",[22,12914,12915,12920],{"__ignoreMap":55},[59,12916,12917],{"class":61,"line":62},[59,12918,12919],{},"cd samples\\json_demo\n",[59,12921,12922],{"class":61,"line":88},[59,12923,11819],{},[362,12925,364],{},{"title":55,"searchDepth":88,"depth":88,"links":12927},[12928,12929,12930,12931],{"id":11747,"depth":88,"text":11747},{"id":12787,"depth":88,"text":12788},{"id":11761,"depth":88,"text":11761},{"id":11804,"depth":88,"text":11804},{},"\u002Fdocs\u002Fsamples\u002Fjson_demo",{"title":12750,"description":12758},"docs\u002Fsamples\u002Fjson_demo","l_TFgbk1Ly1d_29brjUWpnVE5awQi4Ps_Xl5ZJWzbNQ",{"id":12938,"title":12939,"body":12940,"description":12947,"extension":374,"meta":12994,"navigation":173,"path":12995,"seo":12996,"stem":12997,"__hash__":12998},"content\u002Fdocs\u002Fsamples\u002Fmenu_demo.md","menu_demo —— 菜单演示",{"type":8,"value":12941,"toc":12989},[12942,12945,12948,12950,12964,12966,12971,12973,12987],[11,12943,12939],{"id":12944},"menu_demo-菜单演示",[18,12946,12947],{},"展示 eduwin 菜单系统的完整用法，包括菜单栏、右键快捷菜单、状态栏等。",[46,12949,11747],{"id":11747},[315,12951,12952,12955,12958,12961],{},[318,12953,12954],{},"菜单栏（文件、编辑、视图等菜单）",[318,12956,12957],{},"右键上下文菜单",[318,12959,12960],{},"状态栏实时显示操作提示",[318,12962,12963],{},"菜单项 ID 管理",[46,12965,11761],{"id":11761},[18,12967,12968],{},[22,12969,12970],{},"d:\\XTU\\c-train\\samples\\menu_demo\\menu_demo.c",[46,12972,11804],{"id":11804},[50,12974,12976],{"className":7634,"code":12975,"language":7636,"meta":55,"style":55},"cd samples\\menu_demo\nbuild.bat\n",[22,12977,12978,12983],{"__ignoreMap":55},[59,12979,12980],{"class":61,"line":62},[59,12981,12982],{},"cd samples\\menu_demo\n",[59,12984,12985],{"class":61,"line":88},[59,12986,11819],{},[362,12988,364],{},{"title":55,"searchDepth":88,"depth":88,"links":12990},[12991,12992,12993],{"id":11747,"depth":88,"text":11747},{"id":11761,"depth":88,"text":11761},{"id":11804,"depth":88,"text":11804},{},"\u002Fdocs\u002Fsamples\u002Fmenu_demo",{"title":12939,"description":12947},"docs\u002Fsamples\u002Fmenu_demo","FApOPtL1P1GZf2Jl5jKmk8NWViE0FvlxrxNIC7MLZss",{"id":13000,"title":13001,"body":13002,"description":13108,"extension":374,"meta":13109,"navigation":173,"path":13110,"seo":13111,"stem":13112,"__hash__":13113},"content\u002Fdocs\u002Fsamples\u002Fnetwork_info_demo.md","network_info_demo —— 网络功能示例",{"type":8,"value":13003,"toc":13102},[13004,13007,13012,13014,13044,13046,13051,13053,13084,13086,13100],[11,13005,13001],{"id":13006},"network_info_demo-网络功能示例",[18,13008,13009,13011],{},[22,13010,35],{}," 网络模块的功能演示，展示 TCP \u002F UDP \u002F HTTP \u002F DNS 四种网络能力。",[46,13013,11747],{"id":11747},[315,13015,13016,13022,13031,13041],{},[318,13017,13018,13021],{},[69,13019,13020],{},"HTTP 请求"," — 访问 httpbin.org\u002Fip 获取公网 IP",[318,13023,13024,13026,13027,13030],{},[69,13025,6855],{}," — 解析 ",[22,13028,13029],{},"www.xtu.edu.cn"," 的 IP 地址",[318,13032,13033,13036,13037,13040],{},[69,13034,13035],{},"TCP 连接"," — 连接 ",[22,13038,13039],{},"time.nist.gov:13"," 获取网络时间",[318,13042,13043],{},"所有网络操作在独立线程中执行，不阻塞窗口 UI",[46,13045,11761],{"id":11761},[18,13047,13048],{},[22,13049,13050],{},"d:\\XTU\\c-train\\samples\\network_info_demo\\network_info_demo.c",[46,13052,11769],{"id":11769},[4903,13054,13055,13063],{},[4906,13056,13057],{},[4909,13058,13059,13061],{},[4912,13060,10698],{},[4912,13062,10556],{},[4919,13064,13065,13074],{},[4909,13066,13067,13071],{},[4924,13068,13069],{},[22,13070,11798],{},[4924,13072,13073],{},"TCP \u002F HTTP \u002F DNS 网络操作",[4909,13075,13076,13081],{},[4924,13077,13078],{},[22,13079,13080],{},"thread",[4924,13082,13083],{},"线程执行 + 安全 UI 更新",[46,13085,11804],{"id":11804},[50,13087,13089],{"className":7634,"code":13088,"language":7636,"meta":55,"style":55},"cd samples\\network_info_demo\nbuild.bat\n",[22,13090,13091,13096],{"__ignoreMap":55},[59,13092,13093],{"class":61,"line":62},[59,13094,13095],{},"cd samples\\network_info_demo\n",[59,13097,13098],{"class":61,"line":88},[59,13099,11819],{},[362,13101,364],{},{"title":55,"searchDepth":88,"depth":88,"links":13103},[13104,13105,13106,13107],{"id":11747,"depth":88,"text":11747},{"id":11761,"depth":88,"text":11761},{"id":11769,"depth":88,"text":11769},{"id":11804,"depth":88,"text":11804},"eduwin_net 网络模块的功能演示，展示 TCP \u002F UDP \u002F HTTP \u002F DNS 四种网络能力。",{},"\u002Fdocs\u002Fsamples\u002Fnetwork_info_demo",{"title":13001,"description":13108},"docs\u002Fsamples\u002Fnetwork_info_demo","4fWQooFm0Wkm3Rzrq0B1Jxlcnxan1LfQaML0zxU8orU",{"id":13115,"title":13116,"body":13117,"description":13175,"extension":374,"meta":13176,"navigation":173,"path":13177,"seo":13178,"stem":13179,"__hash__":13180},"content\u002Fdocs\u002Fsamples\u002Fprogress_demo.md","progress_demo —— 进度条与滑块",{"type":8,"value":13118,"toc":13170},[13119,13122,13132,13134,13145,13147,13152,13154,13168],[11,13120,13116],{"id":13121},"progress_demo-进度条与滑块",[18,13123,13124,13125,2730,13128,13131],{},"展示 ",[22,13126,13127],{},"create_slider",[22,13129,13130],{},"create_progressbar"," 控件的配合使用。",[46,13133,11747],{"id":11747},[315,13135,13136,13139,13142],{},[318,13137,13138],{},"滑块（Slider）调节数值",[318,13140,13141],{},"进度条（ProgressBar）实时反映滑块位置",[318,13143,13144],{},"标签显示当前数值",[46,13146,11761],{"id":11761},[18,13148,13149],{},[22,13150,13151],{},"d:\\XTU\\c-train\\samples\\progress_demo\\progress_demo.c",[46,13153,11804],{"id":11804},[50,13155,13157],{"className":7634,"code":13156,"language":7636,"meta":55,"style":55},"cd samples\\progress_demo\nbuild.bat\n",[22,13158,13159,13164],{"__ignoreMap":55},[59,13160,13161],{"class":61,"line":62},[59,13162,13163],{},"cd samples\\progress_demo\n",[59,13165,13166],{"class":61,"line":88},[59,13167,11819],{},[362,13169,364],{},{"title":55,"searchDepth":88,"depth":88,"links":13171},[13172,13173,13174],{"id":11747,"depth":88,"text":11747},{"id":11761,"depth":88,"text":11761},{"id":11804,"depth":88,"text":11804},"展示 create_slider 和 create_progressbar 控件的配合使用。",{},"\u002Fdocs\u002Fsamples\u002Fprogress_demo",{"title":13116,"description":13175},"docs\u002Fsamples\u002Fprogress_demo","vm6t_lqXkBgqwsN-MGYxUrUFxPdVcntLRb4EAWXZ6g0",{"id":13182,"title":13183,"body":13184,"description":13191,"extension":374,"meta":13238,"navigation":173,"path":13239,"seo":13240,"stem":13241,"__hash__":13242},"content\u002Fdocs\u002Fsamples\u002Fshooting_demo.md","shooting_demo —— 打靶游戏",{"type":8,"value":13185,"toc":13233},[13186,13189,13192,13194,13208,13210,13215,13217,13231],[11,13187,13183],{"id":13188},"shooting_demo-打靶游戏",[18,13190,13191],{},"一个鼠标点击打靶游戏，展示画布的帧循环、鼠标事件、随机数等能力。",[46,13193,11747],{"id":11747},[315,13195,13196,13199,13202,13205],{},[318,13197,13198],{},"随机位置\u002F大小\u002F颜色的靶子（矩形或椭圆）",[318,13200,13201],{},"鼠标点击靶子得分",[318,13203,13204],{},"实时计分显示",[318,13206,13207],{},"游戏结束判定",[46,13209,11761],{"id":11761},[18,13211,13212],{},[22,13213,13214],{},"d:\\XTU\\c-train\\samples\\shooting_demo\\shooting_demo.c",[46,13216,11804],{"id":11804},[50,13218,13220],{"className":7634,"code":13219,"language":7636,"meta":55,"style":55},"cd samples\\shooting_demo\nbuild.bat\n",[22,13221,13222,13227],{"__ignoreMap":55},[59,13223,13224],{"class":61,"line":62},[59,13225,13226],{},"cd samples\\shooting_demo\n",[59,13228,13229],{"class":61,"line":88},[59,13230,11819],{},[362,13232,364],{},{"title":55,"searchDepth":88,"depth":88,"links":13234},[13235,13236,13237],{"id":11747,"depth":88,"text":11747},{"id":11761,"depth":88,"text":11761},{"id":11804,"depth":88,"text":11804},{},"\u002Fdocs\u002Fsamples\u002Fshooting_demo",{"title":13183,"description":13191},"docs\u002Fsamples\u002Fshooting_demo","5_AQYfehMZPQLpTLQhTOEjE8k_qzGwVtC90qF04b2mI",{"id":13244,"title":13245,"body":13246,"description":13253,"extension":374,"meta":13300,"navigation":173,"path":13301,"seo":13302,"stem":13303,"__hash__":13304},"content\u002Fdocs\u002Fsamples\u002Fsqlite_demo.md","sqlite_demo —— SQLite 数据库",{"type":8,"value":13247,"toc":13295},[13248,13251,13254,13256,13270,13272,13277,13279,13293],[11,13249,13245],{"id":13250},"sqlite_demo-sqlite-数据库",[18,13252,13253],{},"展示 eduwin 集成 SQLite 的完整增删改查（CRUD）操作。",[46,13255,11747],{"id":11747},[315,13257,13258,13261,13264,13267],{},[318,13259,13260],{},"用户信息表（id、姓名、电话）",[318,13262,13263],{},"添加、编辑、删除用户",[318,13265,13266],{},"详细列表（ListView）展示数据",[318,13268,13269],{},"状态栏提示",[46,13271,11761],{"id":11761},[18,13273,13274],{},[22,13275,13276],{},"d:\\XTU\\c-train\\samples\\sqlite_demo\\sqlite_demo.c",[46,13278,11804],{"id":11804},[50,13280,13282],{"className":7634,"code":13281,"language":7636,"meta":55,"style":55},"cd samples\\sqlite_demo\nbuild.bat\n",[22,13283,13284,13289],{"__ignoreMap":55},[59,13285,13286],{"class":61,"line":62},[59,13287,13288],{},"cd samples\\sqlite_demo\n",[59,13290,13291],{"class":61,"line":88},[59,13292,11819],{},[362,13294,364],{},{"title":55,"searchDepth":88,"depth":88,"links":13296},[13297,13298,13299],{"id":11747,"depth":88,"text":11747},{"id":11761,"depth":88,"text":11761},{"id":11804,"depth":88,"text":11804},{},"\u002Fdocs\u002Fsamples\u002Fsqlite_demo",{"title":13245,"description":13253},"docs\u002Fsamples\u002Fsqlite_demo","fPlORvCw-VBDxVnkfobEIW4m_Iw5GSd2ndBxZvfPtUM",{"id":13306,"title":13307,"body":13308,"description":13315,"extension":374,"meta":13362,"navigation":173,"path":13363,"seo":13364,"stem":13365,"__hash__":13366},"content\u002Fdocs\u002Fsamples\u002Fstack.md","stack —— 栈动画演示",{"type":8,"value":13309,"toc":13357},[13310,13313,13316,13318,13332,13334,13339,13341,13355],[11,13311,13307],{"id":13312},"stack-栈动画演示",[18,13314,13315],{},"通过画布帧循环演示栈数据结构的 push\u002Fpop 操作动画。",[46,13317,11747],{"id":11747},[315,13319,13320,13323,13326,13329],{},[318,13321,13322],{},"Push：新元素从上方飞入栈顶",[318,13324,13325],{},"Pop：栈顶元素飞出消失",[318,13327,13328],{},"60fps 帧循环驱动动画",[318,13330,13331],{},"点击按钮操作",[46,13333,11761],{"id":11761},[18,13335,13336],{},[22,13337,13338],{},"d:\\XTU\\c-train\\samples\\stack\\stack_animation.c",[46,13340,11804],{"id":11804},[50,13342,13344],{"className":7634,"code":13343,"language":7636,"meta":55,"style":55},"cd samples\\stack\nbuild.bat\n",[22,13345,13346,13351],{"__ignoreMap":55},[59,13347,13348],{"class":61,"line":62},[59,13349,13350],{},"cd samples\\stack\n",[59,13352,13353],{"class":61,"line":88},[59,13354,11819],{},[362,13356,364],{},{"title":55,"searchDepth":88,"depth":88,"links":13358},[13359,13360,13361],{"id":11747,"depth":88,"text":11747},{"id":11761,"depth":88,"text":11761},{"id":11804,"depth":88,"text":11804},{},"\u002Fdocs\u002Fsamples\u002Fstack",{"title":13307,"description":13315},"docs\u002Fsamples\u002Fstack","aW_RuP-CtAsUQqQqFri7D5D0T-nvtN3coMZLylRcEeE",{"id":13368,"title":13369,"body":13370,"description":13498,"extension":374,"meta":13499,"navigation":173,"path":13500,"seo":13501,"stem":13502,"__hash__":13503},"content\u002Fdocs\u002Fsamples\u002Fthread_demo.md","thread_demo —— 线程与并发示例",{"type":8,"value":13371,"toc":13492},[13372,13375,13380,13382,13385,13438,13444,13446,13451,13453,13474,13476,13490],[11,13373,13369],{"id":13374},"thread_demo-线程与并发示例",[18,13376,13377,13379],{},[22,13378,39],{}," 线程模块的完整演示，展示线程创建、互斥锁、线程安全 UI 更新三种核心能力。",[46,13381,11747],{"id":11747},[18,13383,13384],{},"点击「启动 3 个线程」按钮，同时运行三个不同类型的线程：",[4903,13386,13387,13400],{},[4906,13388,13389],{},[4909,13390,13391,13394,13397],{},[4912,13392,13393],{},"线程",[4912,13395,13396],{},"行为",[4912,13398,13399],{},"演示能力",[4919,13401,13402,13416,13427],{},[4909,13403,13404,13407,13410],{},[4924,13405,13406],{},"计数器线程",[4924,13408,13409],{},"每 300ms 递增一次共享计数器",[4924,13411,13412,13413],{},"互斥锁保护共享数据 + ",[22,13414,13415],{},"eduwin_execute_on_main",[4909,13417,13418,13421,13424],{},[4924,13419,13420],{},"工作线程",[4924,13422,13423],{},"模拟 5 步耗时任务（每步 500ms）",[4924,13425,13426],{},"多步进度更新",[4909,13428,13429,13432,13435],{},[4924,13430,13431],{},"心跳线程",[4924,13433,13434],{},"每 400ms 输出一次心跳",[4924,13436,13437],{},"独立并发运行",[18,13439,13440,13441,13443],{},"所有 UI 更新通过 ",[22,13442,7797],{}," 安全投递，无需手动 PostMessage 或子类化。",[46,13445,11761],{"id":11761},[18,13447,13448],{},[22,13449,13450],{},"d:\\XTU\\c-train\\samples\\thread_demo\\thread_demo.c",[46,13452,11769],{"id":11769},[4903,13454,13455,13463],{},[4906,13456,13457],{},[4909,13458,13459,13461],{},[4912,13460,10698],{},[4912,13462,10556],{},[4919,13464,13465],{},[4909,13466,13467,13471],{},[4924,13468,13469],{},[22,13470,13080],{},[4924,13472,13473],{},"线程创建、互斥锁、主线程调度",[46,13475,11804],{"id":11804},[50,13477,13479],{"className":7634,"code":13478,"language":7636,"meta":55,"style":55},"cd samples\\thread_demo\nbuild.bat\n",[22,13480,13481,13486],{"__ignoreMap":55},[59,13482,13483],{"class":61,"line":62},[59,13484,13485],{},"cd samples\\thread_demo\n",[59,13487,13488],{"class":61,"line":88},[59,13489,11819],{},[362,13491,364],{},{"title":55,"searchDepth":88,"depth":88,"links":13493},[13494,13495,13496,13497],{"id":11747,"depth":88,"text":11747},{"id":11761,"depth":88,"text":11761},{"id":11769,"depth":88,"text":11769},{"id":11804,"depth":88,"text":11804},"eduwin_thread 线程模块的完整演示，展示线程创建、互斥锁、线程安全 UI 更新三种核心能力。",{},"\u002Fdocs\u002Fsamples\u002Fthread_demo",{"title":13369,"description":13498},"docs\u002Fsamples\u002Fthread_demo","m1ooDlRoNp0OSLK-QqKej8YwcD3DDbf1m8sCWJ7DYNM",{"id":13505,"title":13506,"body":13507,"description":13514,"extension":374,"meta":13561,"navigation":173,"path":13562,"seo":13563,"stem":13564,"__hash__":13565},"content\u002Fdocs\u002Fsamples\u002Ftree_demo.md","tree_demo —— 树形列表",{"type":8,"value":13508,"toc":13556},[13509,13512,13515,13517,13531,13533,13538,13540,13554],[11,13510,13506],{"id":13511},"tree_demo-树形列表",[18,13513,13514],{},"展示 TreeView 控件的节点增删改操作。",[46,13516,11747],{"id":11747},[315,13518,13519,13522,13525,13528],{},[318,13520,13521],{},"树形节点展示",[318,13523,13524],{},"添加子节点 \u002F 编辑节点 \u002F 删除节点",[318,13526,13527],{},"文本输入框编辑节点名称",[318,13529,13530],{},"选中项提示",[46,13532,11761],{"id":11761},[18,13534,13535],{},[22,13536,13537],{},"d:\\XTU\\c-train\\samples\\tree_demo\\tree_demo.c",[46,13539,11804],{"id":11804},[50,13541,13543],{"className":7634,"code":13542,"language":7636,"meta":55,"style":55},"cd samples\\tree_demo\nbuild.bat\n",[22,13544,13545,13550],{"__ignoreMap":55},[59,13546,13547],{"class":61,"line":62},[59,13548,13549],{},"cd samples\\tree_demo\n",[59,13551,13552],{"class":61,"line":88},[59,13553,11819],{},[362,13555,364],{},{"title":55,"searchDepth":88,"depth":88,"links":13557},[13558,13559,13560],{"id":11747,"depth":88,"text":11747},{"id":11761,"depth":88,"text":11761},{"id":11804,"depth":88,"text":11804},{},"\u002Fdocs\u002Fsamples\u002Ftree_demo",{"title":13506,"description":13514},"docs\u002Fsamples\u002Ftree_demo","n3zHs6_vnkuoPuD5fS7itpAwEy6G3hqTr69S-AnB9fU",{"id":13567,"title":13568,"body":13569,"description":13769,"extension":374,"meta":13770,"navigation":173,"path":13771,"seo":13772,"stem":13773,"__hash__":13774},"content\u002Fdocs\u002Fsamples\u002Fwebview_bridge.md","webview_bridge —— WebView2 Bridge 演示",{"type":8,"value":13570,"toc":13763},[13571,13574,13579,13581,13643,13645,13649,13708,13710,13732,13734,13748,13761],[11,13572,13568],{"id":13573},"webview_bridge-webview2-bridge-演示",[18,13575,13124,13576,13578],{},[22,13577,10738],{}," Bridge 机制的示例。HTML\u002FCSS\u002FJS 以独立文件加载（通过 Virtual Host 映射），C 端通过 Bridge 注册函数供 JS 调用。",[46,13580,11747],{"id":11747},[315,13582,13583,13597,13627,13640],{},[318,13584,13585,13588,13589,13592,13593,13596],{},[69,13586,13587],{},"加载外部 HTML\u002FCSS\u002FJS","：使用 ",[22,13590,13591],{},"webview_set_virtual_host()"," 将 exe 目录映射为 ",[22,13594,13595],{},"eduwin.local"," 虚拟域名",[318,13598,13599,13602,13603],{},[69,13600,13601],{},"Bridge 通信","：\n",[315,13604,13605,13616],{},[318,13606,13607,13608,13611,13612,13615],{},"JS 侧：",[22,13609,13610],{},"eduwin.on_message(cb)"," 接收 C 消息，",[22,13613,13614],{},"eduwin.call(func, params, callback)"," 调用 C 函数",[318,13617,13618,13619,13622,13623,13626],{},"C 侧：",[22,13620,13621],{},"webview_bridge_register()"," 注册处理函数，JS 调 ",[22,13624,13625],{},"eduwin.call()"," 自动路由到对应 C 函数",[318,13628,13629,4232,13632,13635,13636,13639],{},[69,13630,13631],{},"内置演示",[22,13633,13634],{},"hello","（返回问候语）、",[22,13637,13638],{},"get_time","（返回当前时间）",[318,13641,13642],{},"链接在 WebView 内部打开（已拦截 NewWindowRequested，防止弹出系统浏览器）",[46,13644,11761],{"id":11761},[18,13646,13647],{},[22,13648,10524],{},[4903,13650,13651,13659],{},[4906,13652,13653],{},[4909,13654,13655,13657],{},[4912,13656,11541],{},[4912,13658,10556],{},[4919,13660,13661,13671,13681,13691],{},[4909,13662,13663,13668],{},[4924,13664,13665],{},[22,13666,13667],{},"main.c",[4924,13669,13670],{},"C 端：窗口、工具栏、Bridge 注册",[4909,13672,13673,13678],{},[4924,13674,13675],{},[22,13676,13677],{},"index.html",[4924,13679,13680],{},"页面结构",[4909,13682,13683,13688],{},[4924,13684,13685],{},[22,13686,13687],{},"style.css",[4924,13689,13690],{},"页面样式",[4909,13692,13693,13698],{},[4924,13694,13695],{},[22,13696,13697],{},"script.js",[4924,13699,13700,13701,735,13704,13707],{},"JS 端：",[22,13702,13703],{},"eduwin.on_message",[22,13705,13706],{},"eduwin.call"," 使用",[46,13709,11769],{"id":11769},[4903,13711,13712,13720],{},[4906,13713,13714],{},[4909,13715,13716,13718],{},[4912,13717,10698],{},[4912,13719,10556],{},[4919,13721,13722],{},[4909,13723,13724,13729],{},[4924,13725,13726],{},[22,13727,13728],{},"webview",[4924,13730,13731],{},"WebView2 嵌入、Bridge、Virtual Host",[46,13733,11804],{"id":11804},[50,13735,13737],{"className":7634,"code":13736,"language":7636,"meta":55,"style":55},"cd samples\\webview_bridge\nbuild.bat\n",[22,13738,13739,13744],{"__ignoreMap":55},[59,13740,13741],{"class":61,"line":62},[59,13742,13743],{},"cd samples\\webview_bridge\n",[59,13745,13746],{"class":61,"line":88},[59,13747,11819],{},[18,13749,13750,13751,624,13753,624,13755,624,13757,624,13759,75],{},"需要链接的额外库：",[22,13752,10565],{},[22,13754,10575],{},[22,13756,10585],{},[22,13758,10595],{},[22,13760,10605],{},[362,13762,364],{},{"title":55,"searchDepth":88,"depth":88,"links":13764},[13765,13766,13767,13768],{"id":11747,"depth":88,"text":11747},{"id":11761,"depth":88,"text":11761},{"id":11769,"depth":88,"text":11769},{"id":11804,"depth":88,"text":11804},"展示 eduwin_webview Bridge 机制的示例。HTML\u002FCSS\u002FJS 以独立文件加载（通过 Virtual Host 映射），C 端通过 Bridge 注册函数供 JS 调用。",{},"\u002Fdocs\u002Fsamples\u002Fwebview_bridge",{"title":13568,"description":13769},"docs\u002Fsamples\u002Fwebview_bridge","1P7KfXoJcyKLl1bB0cyAAg--dUhb5eOTi2zeIR-qZOE",{"id":13776,"title":13777,"body":13778,"description":13871,"extension":374,"meta":13872,"navigation":173,"path":13873,"seo":13874,"stem":13875,"__hash__":13876},"content\u002Fdocs\u002Fsamples\u002Fwebview_browser.md","webview_browser —— 简易 WebView2 浏览器",{"type":8,"value":13779,"toc":13865},[13780,13783,13790,13792,13805,13807,13812,13814,13835,13837,13851,13863],[11,13781,13777],{"id":13782},"webview_browser-简易-webview2-浏览器",[18,13784,13785,13786,13789],{},"基础的 WebView2 浏览器示例，带工具栏（后退\u002F前进\u002F刷新）、地址栏、Go 按钮和状态栏。",[69,13787,13788],{},"不含 JS Bridge","，适合作为普通浏览器控件使用。",[46,13791,11747],{"id":11747},[315,13793,13794,13797,13800,13802],{},[318,13795,13796],{},"地址栏输入 URL，点 Go 或回车导航",[318,13798,13799],{},"后退\u002F前进\u002F刷新按钮",[318,13801,13642],{},[318,13803,13804],{},"状态栏显示加载状态",[46,13806,11761],{"id":11761},[18,13808,13809],{},[22,13810,13811],{},"samples\u002Fwebview_browser\u002Fmain.c",[46,13813,11769],{"id":11769},[4903,13815,13816,13824],{},[4906,13817,13818],{},[4909,13819,13820,13822],{},[4912,13821,10698],{},[4912,13823,10556],{},[4919,13825,13826],{},[4909,13827,13828,13832],{},[4924,13829,13830],{},[22,13831,13728],{},[4924,13833,13834],{},"WebView2 嵌入、导航、链接拦截",[46,13836,11804],{"id":11804},[50,13838,13840],{"className":7634,"code":13839,"language":7636,"meta":55,"style":55},"cd samples\\webview_browser\nbuild.bat\n",[22,13841,13842,13847],{"__ignoreMap":55},[59,13843,13844],{"class":61,"line":62},[59,13845,13846],{},"cd samples\\webview_browser\n",[59,13848,13849],{"class":61,"line":88},[59,13850,11819],{},[18,13852,13750,13853,624,13855,624,13857,624,13859,624,13861,75],{},[22,13854,10565],{},[22,13856,10575],{},[22,13858,10585],{},[22,13860,10595],{},[22,13862,10605],{},[362,13864,364],{},{"title":55,"searchDepth":88,"depth":88,"links":13866},[13867,13868,13869,13870],{"id":11747,"depth":88,"text":11747},{"id":11761,"depth":88,"text":11761},{"id":11769,"depth":88,"text":11769},{"id":11804,"depth":88,"text":11804},"基础的 WebView2 浏览器示例，带工具栏（后退\u002F前进\u002F刷新）、地址栏、Go 按钮和状态栏。不含 JS Bridge，适合作为普通浏览器控件使用。",{},"\u002Fdocs\u002Fsamples\u002Fwebview_browser",{"title":13777,"description":13871},"docs\u002Fsamples\u002Fwebview_browser","IXTZjVx7TUhykFI0rTu0UtvQWGQhpD2BpE8NvsYYymU",{"id":13878,"title":13879,"body":13880,"description":13887,"extension":374,"meta":14166,"navigation":173,"path":14167,"seo":14168,"stem":14169,"__hash__":14170},"content\u002Fdocs\u002Fsamples\u002Fwin-helper.md","win_helper —— Windows 小助手",{"type":8,"value":13881,"toc":14156},[13882,13885,13888,13891,13970,13974,13978,14035,14039,14088,14090,14095,14097,14111,14114,14117,14154],[11,13883,13879],{"id":13884},"win_helper-windows-小助手",[18,13886,13887],{},"Windows 小助手是一个综合性的系统工具面板，集成了工具启动、进程管理、系统信息查看、环境变量读写、快捷文件夹等常用功能。",[46,13889,13890],{"id":13890},"功能模块",[4903,13892,13893,13901],{},[4906,13894,13895],{},[4909,13896,13897,13899],{},[4912,13898,10698],{},[4912,13900,11747],{},[4919,13902,13903,13913,13923,13933,13943,13952,13961],{},[4909,13904,13905,13910],{},[4924,13906,13907],{},[69,13908,13909],{},"系统工具",[4924,13911,13912],{},"记事本、计算器、命令提示符、注册表编辑器、画图、任务管理器",[4909,13914,13915,13920],{},[4924,13916,13917],{},[69,13918,13919],{},"管理工具",[4924,13921,13922],{},"设备管理器、磁盘管理、服务管理、事件查看器",[4909,13924,13925,13930],{},[4924,13926,13927],{},[69,13928,13929],{},"磁盘工具",[4924,13931,13932],{},"磁盘清理、磁盘碎片整理",[4909,13934,13935,13940],{},[4924,13936,13937],{},[69,13938,13939],{},"快捷文件夹",[4924,13941,13942],{},"桌面、文档、下载、图片、音乐、视频、AppData 一键直达",[4909,13944,13945,13949],{},[4924,13946,13947],{},[69,13948,8857],{},[4924,13950,13951],{},"列出所有进程（PID + 名称）、终止选定进程",[4909,13953,13954,13958],{},[4924,13955,13956],{},[69,13957,9096],{},[4924,13959,13960],{},"计算机名、用户名、OS 版本、CPU 核心\u002F型号、物理内存、Windows 目录",[4909,13962,13963,13967],{},[4924,13964,13965],{},[69,13966,9303],{},[4924,13968,13969],{},"查看常用环境变量值、新增\u002F修改变量（当前进程）",[46,13971,13973],{"id":13972},"演示的-api","演示的 API",[1020,13975,13977],{"id":13976},"控件-api","控件 API",[315,13979,13980,13990,13996,14016,14021,14030],{},[318,13981,13982,735,13985,735,13987,13989],{},[22,13983,13984],{},"create_tabbar",[22,13986,3346],{},[22,13988,3356],{}," — 标签页容器，切换不同功能面板",[318,13991,13992,13995],{},[22,13993,13994],{},"create_groupbox"," — 分组框，组织每类功能的控件",[318,13997,13998,735,14001,735,14003,735,14005,735,14007,735,14010,735,14013,14015],{},[22,13999,14000],{},"create_listview",[22,14002,3230],{},[22,14004,3234],{},[22,14006,3240],{},[22,14008,14009],{},"listview_delete_all",[22,14011,14012],{},"listview_get_text",[22,14014,3270],{}," — 详细列表，展示进程和环境变量",[318,14017,14018,14020],{},[22,14019,3008],{}," — 状态栏提示",[318,14022,14023,735,14026,14029],{},[22,14024,14025],{},"show",[22,14027,14028],{},"hide"," — 面板显隐切换",[318,14031,14032,14034],{},[22,14033,6058],{}," — 格式化字符串",[1020,14036,14038],{"id":14037},"工具-api","工具 API",[315,14040,14041,14048,14053,14061,14066,14073,14082],{},[318,14042,14043,735,14045,14047],{},[22,14044,8887],{},[22,14046,8931],{}," — 启动系统工具",[318,14049,14050,14052],{},[22,14051,8919],{}," — 按 PID 终止进程",[318,14054,14055,464,14057,14060],{},[22,14056,9045],{},[22,14058,14059],{},"free"," — 枚举系统进程",[318,14062,14063,14065],{},[22,14064,9164],{}," — 系统信息展示",[318,14067,14068,735,14070,14072],{},[22,14069,9323],{},[22,14071,9327],{}," — 环境变量读写",[318,14074,14075,14077,14078,14081],{},[22,14076,9424],{}," 配合 ",[22,14079,14080],{},"explorer.exe"," — 打开系统文件夹",[318,14083,14084,14087],{},[22,14085,14086],{},"log_info"," — 日志记录",[46,14089,11761],{"id":11761},[18,14091,14092],{},[22,14093,14094],{},"d:\\XTU\\c-train\\samples\\win_helper\\main.c",[46,14096,11804],{"id":11804},[50,14098,14100],{"className":7634,"code":14099,"language":7636,"meta":55,"style":55},"cd samples\\win_helper\nbuild.bat\n",[22,14101,14102,14107],{"__ignoreMap":55},[59,14103,14104],{"class":61,"line":62},[59,14105,14106],{},"cd samples\\win_helper\n",[59,14108,14109],{"class":61,"line":88},[59,14110,11819],{},[46,14112,14113],{"id":14113},"使用说明",[18,14115,14116],{},"窗口顶部为标签页容器，包含 7 个分类，点击切换不同功能面板：",[5250,14118,14119,14124,14129,14134,14139,14144,14149],{},[318,14120,14121,14123],{},[69,14122,13909],{}," — 点击按钮直接启动对应程序（等待退出后状态栏提示）",[318,14125,14126,14128],{},[69,14127,13919],{}," — 启动 MMC 管理控制台",[318,14130,14131,14133],{},[69,14132,13929],{}," — 启动磁盘维护工具",[318,14135,14136,14138],{},[69,14137,13939],{}," — 点击打开资源管理器并定位到对应目录",[318,14140,14141,14143],{},[69,14142,8857],{}," — 点击\"刷新列表\"获取所有进程，选中后点击\"终止进程\"",[318,14145,14146,14148],{},[69,14147,9096],{}," — 自动读取并显示本机硬件和系统信息",[318,14150,14151,14153],{},[69,14152,9303],{}," — 显示常见环境变量，可在输入框中自定义变量名和值后点击\"设置\"",[362,14155,364],{},{"title":55,"searchDepth":88,"depth":88,"links":14157},[14158,14159,14163,14164,14165],{"id":13890,"depth":88,"text":13890},{"id":13972,"depth":88,"text":13973,"children":14160},[14161,14162],{"id":13976,"depth":134,"text":13977},{"id":14037,"depth":134,"text":14038},{"id":11761,"depth":88,"text":11761},{"id":11804,"depth":88,"text":11804},{"id":14113,"depth":88,"text":14113},{},"\u002Fdocs\u002Fsamples\u002Fwin-helper",{"title":13879,"description":13887},"docs\u002Fsamples\u002Fwin-helper","7I5u4diBkLNcYQSYoRJNCtY1xEMzS17cV7CXki6DcLw",{"id":14172,"title":14173,"body":14174,"description":14181,"extension":374,"meta":15222,"navigation":173,"path":15223,"seo":15224,"stem":15225,"__hash__":15226},"content\u002Fdocs\u002Ftutorial\u002Feduwin-canvas.md","eduwin 画布绘图教程",{"type":8,"value":14175,"toc":15209},[14176,14179,14182,14184,14188,14211,14217,14232,14234,14238,14318,14320,14324,14344,14365,14368,14397,14399,14403,14438,14467,14470,14514,14517,14537,14539,14543,14558,14580,14614,14616,14620,14623,14673,14676,14753,14755,14759,14772,14794,14833,14835,14839,14853,14886,14888,14892,14905,14917,14963,14965,14969,14981,14987,15007,15009,15011,15195,15198,15207],[11,14177,14173],{"id":14178},"eduwin-画布绘图教程",[18,14180,14181],{},"eduwin 的画布（Canvas）组件提供完整的 2D 绘图能力，基于 GDI+ 实现，支持线、矩形、椭圆、多边形、扇形、贝塞尔曲线、路径、文本、图像等绘制，还内置双缓冲离屏渲染，天然无闪烁。",[432,14183],{},[46,14185,14187],{"id":14186},"_1-创建画布","1. 创建画布",[50,14189,14191],{"className":52,"code":14190,"language":54,"meta":55,"style":55},"#include \"eduwin.h\"\n\nHWND win = create_window(L\"画布演示\", 640, 480);\nHWND canvas = create_canvas(win, 0, 0, 640, 480);\n",[22,14192,14193,14197,14201,14206],{"__ignoreMap":55},[59,14194,14195],{"class":61,"line":62},[59,14196,5287],{},[59,14198,14199],{"class":61,"line":88},[59,14200,174],{"emptyLinePlaceholder":173},[59,14202,14203],{"class":61,"line":134},[59,14204,14205],{},"HWND win = create_window(L\"画布演示\", 640, 480);\n",[59,14207,14208],{"class":61,"line":140},[59,14209,14210],{},"HWND canvas = create_canvas(win, 0, 0, 640, 480);\n",[18,14212,14213,14216],{},[22,14214,14215],{},"create_canvas"," 在窗口中创建一个画布控件，参数依次为父窗口、x、y、宽度、高度。",[15,14218,14219],{},[18,14220,14221,14226,14228,14229,14231],{},[69,14222,14223,14224,687],{},"注意：所有绘图操作必须在 ",[22,14225,686],{},[22,14227,686],{}," 触发 ",[22,14230,695],{}," 消息，画布会重新创建离屏位图，之前绘制的内容会被清空。",[432,14233],{},[46,14235,14237],{"id":14236},"_2-基本绘图流程","2. 基本绘图流程",[50,14239,14241],{"className":52,"code":14240,"language":54,"meta":55,"style":55},"show_window(win);\n\n\u002F\u002F 1. 清空画布背景\ncanvas_clear(canvas, 240, 240, 240, 255);  \u002F\u002F 灰色不透明背景\n\n\u002F\u002F 2. 设置画笔（轮廓）\ncanvas_set_pen(canvas, 0, 0, 255, 255, 3);  \u002F\u002F 蓝色 3px 不透明\n\n\u002F\u002F 3. 设置画刷（填充）\ncanvas_set_brush(canvas, 255, 200, 200, 255); \u002F\u002F 粉色不透明\n\n\u002F\u002F 4. 绘图\ncanvas_draw_rect(canvas, 20, 20, 100, 80);    \u002F\u002F 矩形边框\ncanvas_fill_ellipse(canvas, 140, 20, 100, 80); \u002F\u002F 填充椭圆\ncanvas_draw_line(canvas, 20, 120, 300, 120);   \u002F\u002F 直线\ncanvas_draw_text(canvas, L\"Hello 画布\", 20, 140, 0, 0, 0, 255, 20); \u002F\u002F 文字\n",[22,14242,14243,14247,14251,14256,14261,14265,14270,14275,14279,14284,14289,14293,14298,14303,14308,14313],{"__ignoreMap":55},[59,14244,14245],{"class":61,"line":62},[59,14246,12045],{},[59,14248,14249],{"class":61,"line":88},[59,14250,174],{"emptyLinePlaceholder":173},[59,14252,14253],{"class":61,"line":134},[59,14254,14255],{},"\u002F\u002F 1. 清空画布背景\n",[59,14257,14258],{"class":61,"line":140},[59,14259,14260],{},"canvas_clear(canvas, 240, 240, 240, 255);  \u002F\u002F 灰色不透明背景\n",[59,14262,14263],{"class":61,"line":146},[59,14264,174],{"emptyLinePlaceholder":173},[59,14266,14267],{"class":61,"line":281},[59,14268,14269],{},"\u002F\u002F 2. 设置画笔（轮廓）\n",[59,14271,14272],{"class":61,"line":286},[59,14273,14274],{},"canvas_set_pen(canvas, 0, 0, 255, 255, 3);  \u002F\u002F 蓝色 3px 不透明\n",[59,14276,14277],{"class":61,"line":292},[59,14278,174],{"emptyLinePlaceholder":173},[59,14280,14281],{"class":61,"line":600},[59,14282,14283],{},"\u002F\u002F 3. 设置画刷（填充）\n",[59,14285,14286],{"class":61,"line":917},[59,14287,14288],{},"canvas_set_brush(canvas, 255, 200, 200, 255); \u002F\u002F 粉色不透明\n",[59,14290,14291],{"class":61,"line":922},[59,14292,174],{"emptyLinePlaceholder":173},[59,14294,14295],{"class":61,"line":928},[59,14296,14297],{},"\u002F\u002F 4. 绘图\n",[59,14299,14300],{"class":61,"line":933},[59,14301,14302],{},"canvas_draw_rect(canvas, 20, 20, 100, 80);    \u002F\u002F 矩形边框\n",[59,14304,14305],{"class":61,"line":939},[59,14306,14307],{},"canvas_fill_ellipse(canvas, 140, 20, 100, 80); \u002F\u002F 填充椭圆\n",[59,14309,14310],{"class":61,"line":1320},[59,14311,14312],{},"canvas_draw_line(canvas, 20, 120, 300, 120);   \u002F\u002F 直线\n",[59,14314,14315],{"class":61,"line":1326},[59,14316,14317],{},"canvas_draw_text(canvas, L\"Hello 画布\", 20, 140, 0, 0, 0, 255, 20); \u002F\u002F 文字\n",[432,14319],{},[46,14321,14323],{"id":14322},"_3-绘图状态","3. 绘图状态",[50,14325,14326],{"className":52,"code":707,"language":54,"meta":55,"style":55},[22,14327,14328,14332,14336,14340],{"__ignoreMap":55},[59,14329,14330],{"class":61,"line":62},[59,14331,714],{},[59,14333,14334],{"class":61,"line":88},[59,14335,719],{},[59,14337,14338],{"class":61,"line":134},[59,14339,724],{},[59,14341,14342],{"class":61,"line":140},[59,14343,729],{},[315,14345,14346,14351,14356,14361],{},[318,14347,14348,14350],{},[22,14349,753],{}," 用指定颜色填充整个画布（清除原有内容），参数为 RGBA，范围 0–255",[318,14352,14353,14355],{},[22,14354,759],{}," 设置轮廓画笔的颜色、透明度和线宽（默认黑色 1px 不透明）",[318,14357,14358,14360],{},[22,14359,765],{}," 设置填充画刷的颜色和透明度（默认白色不透明）",[318,14362,14363,772],{},[22,14364,771],{},[18,14366,14367],{},"绘图状态是全局的——设置后影响后续所有绘制，直到再次修改。",[50,14369,14371],{"className":52,"code":14370,"language":54,"meta":55,"style":55},"canvas_set_pen(canvas, 255, 0, 0, 255, 5);   \u002F\u002F 红色 5px 画笔\ncanvas_draw_rect(canvas, 10, 10, 100, 100);  \u002F\u002F 红色5px边框\n\ncanvas_set_pen(canvas, 0, 128, 0, 200, 1);   \u002F\u002F 绿色半透明 1px 画笔\ncanvas_draw_rect(canvas, 130, 10, 100, 100); \u002F\u002F 绿色1px边框\n",[22,14372,14373,14378,14383,14387,14392],{"__ignoreMap":55},[59,14374,14375],{"class":61,"line":62},[59,14376,14377],{},"canvas_set_pen(canvas, 255, 0, 0, 255, 5);   \u002F\u002F 红色 5px 画笔\n",[59,14379,14380],{"class":61,"line":88},[59,14381,14382],{},"canvas_draw_rect(canvas, 10, 10, 100, 100);  \u002F\u002F 红色5px边框\n",[59,14384,14385],{"class":61,"line":134},[59,14386,174],{"emptyLinePlaceholder":173},[59,14388,14389],{"class":61,"line":140},[59,14390,14391],{},"canvas_set_pen(canvas, 0, 128, 0, 200, 1);   \u002F\u002F 绿色半透明 1px 画笔\n",[59,14393,14394],{"class":61,"line":146},[59,14395,14396],{},"canvas_draw_rect(canvas, 130, 10, 100, 100); \u002F\u002F 绿色1px边框\n",[432,14398],{},[46,14400,14402],{"id":14401},"_4-基本几何图形","4. 基本几何图形",[50,14404,14406],{"className":52,"code":14405,"language":54,"meta":55,"style":55},"void canvas_draw_line(HWND canvas, int x1, int y1, int x2, int y2);\nvoid canvas_draw_rect(HWND canvas, int x, int y, int w, int h);\nvoid canvas_fill_rect(HWND canvas, int x, int y, int w, int h);\nvoid canvas_draw_ellipse(HWND canvas, int x, int y, int w, int h);\nvoid canvas_fill_ellipse(HWND canvas, int x, int y, int w, int h);\nvoid canvas_fill_polygon(HWND canvas, const PointF *pts, int count);\nvoid canvas_fill_pie(HWND canvas, int x, int y, int w, int h, float start_angle, float sweep_angle);\n",[22,14407,14408,14412,14416,14420,14424,14428,14433],{"__ignoreMap":55},[59,14409,14410],{"class":61,"line":62},[59,14411,787],{},[59,14413,14414],{"class":61,"line":88},[59,14415,792],{},[59,14417,14418],{"class":61,"line":134},[59,14419,797],{},[59,14421,14422],{"class":61,"line":140},[59,14423,802],{},[59,14425,14426],{"class":61,"line":146},[59,14427,807],{},[59,14429,14430],{"class":61,"line":281},[59,14431,14432],{},"void canvas_fill_polygon(HWND canvas, const PointF *pts, int count);\n",[59,14434,14435],{"class":61,"line":286},[59,14436,14437],{},"void canvas_fill_pie(HWND canvas, int x, int y, int w, int h, float start_angle, float sweep_angle);\n",[315,14439,14440,14452],{},[318,14441,14442,735,14445,735,14448,14451],{},[22,14443,14444],{},"draw_line",[22,14446,14447],{},"draw_rect",[22,14449,14450],{},"draw_ellipse"," 用当前画笔绘制轮廓",[318,14453,14454,735,14457,735,14460,735,14463,14466],{},[22,14455,14456],{},"fill_rect",[22,14458,14459],{},"fill_ellipse",[22,14461,14462],{},"fill_polygon",[22,14464,14465],{},"fill_pie"," 用当前画刷填充",[18,14468,14469],{},"填充多边形示例：",[50,14471,14473],{"className":52,"code":14472,"language":54,"meta":55,"style":55},"\u002F* 绘制一个蓝色三角形 *\u002F\nPointF pts[3] = {\n    {200, 50},  \u002F* 顶点 *\u002F\n    {150, 150}, \u002F* 左下 *\u002F\n    {250, 150}  \u002F* 右下 *\u002F\n};\ncanvas_set_brush(canvas, 50, 100, 200, 200);  \u002F\u002F 半透明蓝色\ncanvas_fill_polygon(canvas, pts, 3);\n",[22,14474,14475,14480,14485,14490,14495,14500,14504,14509],{"__ignoreMap":55},[59,14476,14477],{"class":61,"line":62},[59,14478,14479],{},"\u002F* 绘制一个蓝色三角形 *\u002F\n",[59,14481,14482],{"class":61,"line":88},[59,14483,14484],{},"PointF pts[3] = {\n",[59,14486,14487],{"class":61,"line":134},[59,14488,14489],{},"    {200, 50},  \u002F* 顶点 *\u002F\n",[59,14491,14492],{"class":61,"line":140},[59,14493,14494],{},"    {150, 150}, \u002F* 左下 *\u002F\n",[59,14496,14497],{"class":61,"line":146},[59,14498,14499],{},"    {250, 150}  \u002F* 右下 *\u002F\n",[59,14501,14502],{"class":61,"line":281},[59,14503,8656],{},[59,14505,14506],{"class":61,"line":286},[59,14507,14508],{},"canvas_set_brush(canvas, 50, 100, 200, 200);  \u002F\u002F 半透明蓝色\n",[59,14510,14511],{"class":61,"line":292},[59,14512,14513],{},"canvas_fill_polygon(canvas, pts, 3);\n",[18,14515,14516],{},"填充扇形示例：",[50,14518,14520],{"className":52,"code":14519,"language":54,"meta":55,"style":55},"\u002F* 绘制一个从 0° 到 90° 的扇形 *\u002F\ncanvas_set_brush(canvas, 200, 100, 50, 220);\ncanvas_fill_pie(canvas, 300, 50, 120, 120, 0, 90);\n",[22,14521,14522,14527,14532],{"__ignoreMap":55},[59,14523,14524],{"class":61,"line":62},[59,14525,14526],{},"\u002F* 绘制一个从 0° 到 90° 的扇形 *\u002F\n",[59,14528,14529],{"class":61,"line":88},[59,14530,14531],{},"canvas_set_brush(canvas, 200, 100, 50, 220);\n",[59,14533,14534],{"class":61,"line":134},[59,14535,14536],{},"canvas_fill_pie(canvas, 300, 50, 120, 120, 0, 90);\n",[432,14538],{},[46,14540,14542],{"id":14541},"_5-弧形与贝塞尔曲线","5. 弧形与贝塞尔曲线",[50,14544,14546],{"className":52,"code":14545,"language":54,"meta":55,"style":55},"void canvas_draw_arc(HWND canvas, int x, int y, int w, int h, float start_angle, float sweep_angle);\nvoid canvas_draw_bezier(HWND canvas, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);\n",[22,14547,14548,14553],{"__ignoreMap":55},[59,14549,14550],{"class":61,"line":62},[59,14551,14552],{},"void canvas_draw_arc(HWND canvas, int x, int y, int w, int h, float start_angle, float sweep_angle);\n",[59,14554,14555],{"class":61,"line":88},[59,14556,14557],{},"void canvas_draw_bezier(HWND canvas, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);\n",[315,14559,14560,14574],{},[318,14561,14562,14565,14566,14569,14570,14573],{},[22,14563,14564],{},"draw_arc"," 绘制椭圆弧线段，",[22,14567,14568],{},"start_angle"," 为起始角度（度），",[22,14571,14572],{},"sweep_angle"," 为扫过的角度（逆时针为正）",[318,14575,14576,14579],{},[22,14577,14578],{},"draw_bezier"," 绘制三次贝塞尔曲线，4 个点依次为起点、控制点 1、控制点 2、终点",[50,14581,14583],{"className":52,"code":14582,"language":54,"meta":55,"style":55},"\u002F* 弧线：从 0° 到 180° *\u002F\ncanvas_set_pen(canvas, 200, 50, 50, 255, 3);\ncanvas_draw_arc(canvas, 50, 200, 100, 80, 0, 180);\n\n\u002F* 贝塞尔曲线 *\u002F\ncanvas_draw_bezier(canvas, 200, 200, 250, 100, 350, 300, 400, 200);\n",[22,14584,14585,14590,14595,14600,14604,14609],{"__ignoreMap":55},[59,14586,14587],{"class":61,"line":62},[59,14588,14589],{},"\u002F* 弧线：从 0° 到 180° *\u002F\n",[59,14591,14592],{"class":61,"line":88},[59,14593,14594],{},"canvas_set_pen(canvas, 200, 50, 50, 255, 3);\n",[59,14596,14597],{"class":61,"line":134},[59,14598,14599],{},"canvas_draw_arc(canvas, 50, 200, 100, 80, 0, 180);\n",[59,14601,14602],{"class":61,"line":140},[59,14603,174],{"emptyLinePlaceholder":173},[59,14605,14606],{"class":61,"line":146},[59,14607,14608],{},"\u002F* 贝塞尔曲线 *\u002F\n",[59,14610,14611],{"class":61,"line":281},[59,14612,14613],{},"canvas_draw_bezier(canvas, 200, 200, 250, 100, 350, 300, 400, 200);\n",[432,14615],{},[46,14617,14619],{"id":14618},"_6-路径系统","6. 路径系统",[18,14621,14622],{},"路径（Path）允许你将多条线段、弧线、椭圆、矩形组合成一个图形，然后统一绘制或填充。",[50,14624,14626],{"className":52,"code":14625,"language":54,"meta":55,"style":55},"void *canvas_create_path(void);\nvoid canvas_add_path_line(void *path, float x1, float y1, float x2, float y2);\nvoid canvas_add_path_arc(void *path, float x, float y, float w, float h, float start_angle, float sweep_angle);\nvoid canvas_add_path_ellipse(void *path, float x, float y, float w, float h);\nvoid canvas_add_path_rect(void *path, float x, float y, float w, float h);\nvoid canvas_close_path(void *path);\nvoid canvas_draw_path(HWND canvas, void *path);\nvoid canvas_fill_path(HWND canvas, void *path);\nvoid canvas_delete_path(void *path);\n",[22,14627,14628,14633,14638,14643,14648,14653,14658,14663,14668],{"__ignoreMap":55},[59,14629,14630],{"class":61,"line":62},[59,14631,14632],{},"void *canvas_create_path(void);\n",[59,14634,14635],{"class":61,"line":88},[59,14636,14637],{},"void canvas_add_path_line(void *path, float x1, float y1, float x2, float y2);\n",[59,14639,14640],{"class":61,"line":134},[59,14641,14642],{},"void canvas_add_path_arc(void *path, float x, float y, float w, float h, float start_angle, float sweep_angle);\n",[59,14644,14645],{"class":61,"line":140},[59,14646,14647],{},"void canvas_add_path_ellipse(void *path, float x, float y, float w, float h);\n",[59,14649,14650],{"class":61,"line":146},[59,14651,14652],{},"void canvas_add_path_rect(void *path, float x, float y, float w, float h);\n",[59,14654,14655],{"class":61,"line":281},[59,14656,14657],{},"void canvas_close_path(void *path);\n",[59,14659,14660],{"class":61,"line":286},[59,14661,14662],{},"void canvas_draw_path(HWND canvas, void *path);\n",[59,14664,14665],{"class":61,"line":292},[59,14666,14667],{},"void canvas_fill_path(HWND canvas, void *path);\n",[59,14669,14670],{"class":61,"line":600},[59,14671,14672],{},"void canvas_delete_path(void *path);\n",[18,14674,14675],{},"使用模式：创建路径 → 添加图形 → 绘制\u002F填充 → 销毁路径。",[50,14677,14679],{"className":52,"code":14678,"language":54,"meta":55,"style":55},"\u002F* 创建一个心形路径 *\u002F\nvoid *path = canvas_create_path();\ncanvas_add_path_ellipse(path, 0, 0, 80, 80);    \u002F\u002F 左圆\ncanvas_add_path_ellipse(path, 60, 0, 80, 80);   \u002F\u002F 右圆\ncanvas_add_path_line(path, 0, 50, 70, 140);      \u002F\u002F 左侧边\ncanvas_add_path_line(path, 70, 140, 140, 50);     \u002F\u002F 右侧边\ncanvas_close_path(path);\n\ncanvas_set_brush(canvas, 220, 60, 80, 255);\ncanvas_fill_path(canvas, path);\n\ncanvas_set_pen(canvas, 180, 30, 50, 255, 2);\ncanvas_draw_path(canvas, path);\n\ncanvas_delete_path(path);\n",[22,14680,14681,14686,14691,14696,14701,14706,14711,14716,14720,14725,14730,14734,14739,14744,14748],{"__ignoreMap":55},[59,14682,14683],{"class":61,"line":62},[59,14684,14685],{},"\u002F* 创建一个心形路径 *\u002F\n",[59,14687,14688],{"class":61,"line":88},[59,14689,14690],{},"void *path = canvas_create_path();\n",[59,14692,14693],{"class":61,"line":134},[59,14694,14695],{},"canvas_add_path_ellipse(path, 0, 0, 80, 80);    \u002F\u002F 左圆\n",[59,14697,14698],{"class":61,"line":140},[59,14699,14700],{},"canvas_add_path_ellipse(path, 60, 0, 80, 80);   \u002F\u002F 右圆\n",[59,14702,14703],{"class":61,"line":146},[59,14704,14705],{},"canvas_add_path_line(path, 0, 50, 70, 140);      \u002F\u002F 左侧边\n",[59,14707,14708],{"class":61,"line":281},[59,14709,14710],{},"canvas_add_path_line(path, 70, 140, 140, 50);     \u002F\u002F 右侧边\n",[59,14712,14713],{"class":61,"line":286},[59,14714,14715],{},"canvas_close_path(path);\n",[59,14717,14718],{"class":61,"line":292},[59,14719,174],{"emptyLinePlaceholder":173},[59,14721,14722],{"class":61,"line":600},[59,14723,14724],{},"canvas_set_brush(canvas, 220, 60, 80, 255);\n",[59,14726,14727],{"class":61,"line":917},[59,14728,14729],{},"canvas_fill_path(canvas, path);\n",[59,14731,14732],{"class":61,"line":922},[59,14733,174],{"emptyLinePlaceholder":173},[59,14735,14736],{"class":61,"line":928},[59,14737,14738],{},"canvas_set_pen(canvas, 180, 30, 50, 255, 2);\n",[59,14740,14741],{"class":61,"line":933},[59,14742,14743],{},"canvas_draw_path(canvas, path);\n",[59,14745,14746],{"class":61,"line":939},[59,14747,174],{"emptyLinePlaceholder":173},[59,14749,14750],{"class":61,"line":1320},[59,14751,14752],{},"canvas_delete_path(path);\n",[432,14754],{},[46,14756,14758],{"id":14757},"_7-绘制文本","7. 绘制文本",[50,14760,14762],{"className":52,"code":14761,"language":54,"meta":55,"style":55},"void canvas_set_font(HWND canvas, const WCHAR *family, int size);\nvoid canvas_draw_text(HWND canvas, const WCHAR *text, int x, int y, int r, int g, int b, int a, int size);\n",[22,14763,14764,14768],{"__ignoreMap":55},[59,14765,14766],{"class":61,"line":62},[59,14767,729],{},[59,14769,14770],{"class":61,"line":88},[59,14771,812],{},[315,14773,14774,14782],{},[318,14775,14776,14778,14779,14781],{},[22,14777,771],{}," 设置全局字体和字号（",[22,14780,853],{}," 为 0 时不影响后续单次绘制）",[318,14783,14784,40,14786,850,14788,14790,14791,14793],{},[22,14785,845],{},[22,14787,849],{},[22,14789,853],{}," 传 0 使用 ",[22,14792,771],{}," 设定的字号",[50,14795,14797],{"className":52,"code":14796,"language":54,"meta":55,"style":55},"\u002F* 设置全局字体 *\u002F\ncanvas_set_font(canvas, L\"SimSun\", 24);     \u002F\u002F 宋体 24 号\ncanvas_draw_text(canvas, L\"你好，eduwin\", 50, 300, 50, 50, 50, 255, 0);  \u002F\u002F 用全局设置\n\n\u002F* 单次绘制指定字号 *\u002F\ncanvas_draw_text(canvas, L\"大标题\", 50, 340, 200, 50, 50, 255, 36);\ncanvas_draw_text(canvas, L\"小标注\", 50, 390, 128, 128, 128, 255, 12);\n",[22,14798,14799,14804,14809,14814,14818,14823,14828],{"__ignoreMap":55},[59,14800,14801],{"class":61,"line":62},[59,14802,14803],{},"\u002F* 设置全局字体 *\u002F\n",[59,14805,14806],{"class":61,"line":88},[59,14807,14808],{},"canvas_set_font(canvas, L\"SimSun\", 24);     \u002F\u002F 宋体 24 号\n",[59,14810,14811],{"class":61,"line":134},[59,14812,14813],{},"canvas_draw_text(canvas, L\"你好，eduwin\", 50, 300, 50, 50, 50, 255, 0);  \u002F\u002F 用全局设置\n",[59,14815,14816],{"class":61,"line":140},[59,14817,174],{"emptyLinePlaceholder":173},[59,14819,14820],{"class":61,"line":146},[59,14821,14822],{},"\u002F* 单次绘制指定字号 *\u002F\n",[59,14824,14825],{"class":61,"line":281},[59,14826,14827],{},"canvas_draw_text(canvas, L\"大标题\", 50, 340, 200, 50, 50, 255, 36);\n",[59,14829,14830],{"class":61,"line":286},[59,14831,14832],{},"canvas_draw_text(canvas, L\"小标注\", 50, 390, 128, 128, 128, 255, 12);\n",[432,14834],{},[46,14836,14838],{"id":14837},"_8-绘制图像","8. 绘制图像",[50,14840,14842],{"className":52,"code":14841,"language":54,"meta":55,"style":55},"void *load_image_from_file(const WCHAR *filepath, UINT *width, UINT *height);\nvoid canvas_draw_image(HWND canvas, void *image, int x, int y, int w, int h);\n",[22,14843,14844,14849],{"__ignoreMap":55},[59,14845,14846],{"class":61,"line":62},[59,14847,14848],{},"void *load_image_from_file(const WCHAR *filepath, UINT *width, UINT *height);\n",[59,14850,14851],{"class":61,"line":88},[59,14852,817],{},[50,14854,14856],{"className":52,"code":14855,"language":54,"meta":55,"style":55},"UINT iw, ih;\nvoid *img = load_image_from_file(L\"photo.png\", &iw, &ih);\nif (img)\n{\n    canvas_draw_image(canvas, img, 10, 10, (int)iw, (int)ih);\n}\n",[22,14857,14858,14863,14868,14873,14877,14882],{"__ignoreMap":55},[59,14859,14860],{"class":61,"line":62},[59,14861,14862],{},"UINT iw, ih;\n",[59,14864,14865],{"class":61,"line":88},[59,14866,14867],{},"void *img = load_image_from_file(L\"photo.png\", &iw, &ih);\n",[59,14869,14870],{"class":61,"line":134},[59,14871,14872],{},"if (img)\n",[59,14874,14875],{"class":61,"line":140},[59,14876,574],{},[59,14878,14879],{"class":61,"line":146},[59,14880,14881],{},"    canvas_draw_image(canvas, img, 10, 10, (int)iw, (int)ih);\n",[59,14883,14884],{"class":61,"line":281},[59,14885,149],{},[432,14887],{},[46,14889,14891],{"id":14890},"_9-局部清除与文件输出","9. 局部清除与文件输出",[50,14893,14895],{"className":52,"code":14894,"language":54,"meta":55,"style":55},"void canvas_clear_rect(HWND canvas, int x, int y, int w, int h, int r, int g, int b, int a);\nvoid canvas_save_image(HWND canvas, const WCHAR *filepath);\n",[22,14896,14897,14901],{"__ignoreMap":55},[59,14898,14899],{"class":61,"line":62},[59,14900,950],{},[59,14902,14903],{"class":61,"line":88},[59,14904,995],{},[18,14906,14907,14910,14911,962,14913,14916],{},[22,14908,14909],{},"canvas_clear_rect"," 用颜色填充指定矩形区域（局部清除），",[22,14912,745],{},[22,14914,14915],{},"canvas_save_image"," 将画布当前内容保存为图像文件（根据扩展名自动选择编码器，支持 png\u002Fjpg\u002Fbmp）。",[50,14918,14920],{"className":52,"code":14919,"language":54,"meta":55,"style":55},"\u002F* 在灰色区域中挖一个矩形 *\u002F\ncanvas_set_brush(canvas, 180, 180, 180, 255);\ncanvas_fill_rect(canvas, 10, 10, 200, 100);\n\n\u002F* 局部清除并填充绿色 *\u002F\ncanvas_clear_rect(canvas, 50, 30, 100, 40, 100, 200, 50, 255);\n\n\u002F* 保存为文件 *\u002F\ncanvas_save_image(canvas, L\"output.png\");\n",[22,14921,14922,14927,14932,14936,14940,14945,14949,14953,14958],{"__ignoreMap":55},[59,14923,14924],{"class":61,"line":62},[59,14925,14926],{},"\u002F* 在灰色区域中挖一个矩形 *\u002F\n",[59,14928,14929],{"class":61,"line":88},[59,14930,14931],{},"canvas_set_brush(canvas, 180, 180, 180, 255);\n",[59,14933,14934],{"class":61,"line":134},[59,14935,982],{},[59,14937,14938],{"class":61,"line":140},[59,14939,174],{"emptyLinePlaceholder":173},[59,14941,14942],{"class":61,"line":146},[59,14943,14944],{},"\u002F* 局部清除并填充绿色 *\u002F\n",[59,14946,14947],{"class":61,"line":281},[59,14948,987],{},[59,14950,14951],{"class":61,"line":286},[59,14952,174],{"emptyLinePlaceholder":173},[59,14954,14955],{"class":61,"line":292},[59,14956,14957],{},"\u002F* 保存为文件 *\u002F\n",[59,14959,14960],{"class":61,"line":600},[59,14961,14962],{},"canvas_save_image(canvas, L\"output.png\");\n",[432,14964],{},[46,14966,14968],{"id":14967},"_10-画布尺寸","10. 画布尺寸",[50,14970,14971],{"className":52,"code":1228,"language":54,"meta":55,"style":55},[22,14972,14973,14977],{"__ignoreMap":55},[59,14974,14975],{"class":61,"line":62},[59,14976,1235],{},[59,14978,14979],{"class":61,"line":88},[59,14980,1240],{},[18,14982,14983,14984,14986],{},"获取画布的实际像素尺寸，通常配合 ",[22,14985,686],{}," 之后使用：",[50,14988,14990],{"className":52,"code":14989,"language":54,"meta":55,"style":55},"int cw = canvas_get_width(canvas);\nint ch = canvas_get_height(canvas);\ncanvas_draw_text(canvas, L\"居中文字\", cw \u002F 2 - 60, ch \u002F 2, 0, 0, 0, 255, 20);\n",[22,14991,14992,14997,15002],{"__ignoreMap":55},[59,14993,14994],{"class":61,"line":62},[59,14995,14996],{},"int cw = canvas_get_width(canvas);\n",[59,14998,14999],{"class":61,"line":88},[59,15000,15001],{},"int ch = canvas_get_height(canvas);\n",[59,15003,15004],{"class":61,"line":134},[59,15005,15006],{},"canvas_draw_text(canvas, L\"居中文字\", cw \u002F 2 - 60, ch \u002F 2, 0, 0, 0, 255, 20);\n",[432,15008],{},[46,15010,298],{"id":298},[50,15012,15014],{"className":52,"code":15013,"language":54,"meta":55,"style":55},"#include \"eduwin.h\"\n\nint main(void)\n{\n    init_app();\n\n    HWND win = create_window(L\"画布绘图教程\", 400, 400);\n    HWND canvas = create_canvas(win, 0, 0, 400, 400);\n\n    show_window(win);\n\n    \u002F* 白色背景 *\u002F\n    canvas_clear(canvas, 255, 255, 255, 255);\n\n    \u002F* 红色矩形边框 *\u002F\n    canvas_set_pen(canvas, 220, 50, 50, 255, 3);\n    canvas_draw_rect(canvas, 20, 20, 160, 100);\n\n    \u002F* 蓝色填充椭圆 *\u002F\n    canvas_set_brush(canvas, 60, 120, 200, 200);\n    canvas_fill_ellipse(canvas, 220, 20, 160, 100);\n\n    \u002F* 绿色直线 *\u002F\n    canvas_set_pen(canvas, 60, 160, 60, 255, 2);\n    canvas_draw_line(canvas, 20, 180, 380, 180);\n\n    \u002F* 半透明填充三角形 *\u002F\n    canvas_set_brush(canvas, 200, 160, 60, 180);\n    PointF pts[3] = {{200, 220}, {100, 350}, {300, 350}};\n    canvas_fill_polygon(canvas, pts, 3);\n\n    \u002F* 灰色文字 *\u002F\n    canvas_draw_text(canvas, L\"eduwin 画布\", 140, 370, 80, 80, 80, 255, 16);\n\n    run_app();\n    return 0;\n}\n",[22,15015,15016,15020,15024,15028,15032,15036,15040,15045,15050,15054,15058,15062,15067,15072,15076,15081,15086,15091,15095,15100,15105,15110,15114,15119,15124,15129,15134,15140,15146,15152,15158,15163,15169,15175,15180,15185,15190],{"__ignoreMap":55},[59,15017,15018],{"class":61,"line":62},[59,15019,5287],{},[59,15021,15022],{"class":61,"line":88},[59,15023,174],{"emptyLinePlaceholder":173},[59,15025,15026],{"class":61,"line":134},[59,15027,5296],{},[59,15029,15030],{"class":61,"line":140},[59,15031,574],{},[59,15033,15034],{"class":61,"line":146},[59,15035,9768],{},[59,15037,15038],{"class":61,"line":281},[59,15039,174],{"emptyLinePlaceholder":173},[59,15041,15042],{"class":61,"line":286},[59,15043,15044],{},"    HWND win = create_window(L\"画布绘图教程\", 400, 400);\n",[59,15046,15047],{"class":61,"line":292},[59,15048,15049],{},"    HWND canvas = create_canvas(win, 0, 0, 400, 400);\n",[59,15051,15052],{"class":61,"line":600},[59,15053,174],{"emptyLinePlaceholder":173},[59,15055,15056],{"class":61,"line":917},[59,15057,5365],{},[59,15059,15060],{"class":61,"line":922},[59,15061,174],{"emptyLinePlaceholder":173},[59,15063,15064],{"class":61,"line":928},[59,15065,15066],{},"    \u002F* 白色背景 *\u002F\n",[59,15068,15069],{"class":61,"line":933},[59,15070,15071],{},"    canvas_clear(canvas, 255, 255, 255, 255);\n",[59,15073,15074],{"class":61,"line":939},[59,15075,174],{"emptyLinePlaceholder":173},[59,15077,15078],{"class":61,"line":1320},[59,15079,15080],{},"    \u002F* 红色矩形边框 *\u002F\n",[59,15082,15083],{"class":61,"line":1326},[59,15084,15085],{},"    canvas_set_pen(canvas, 220, 50, 50, 255, 3);\n",[59,15087,15088],{"class":61,"line":1331},[59,15089,15090],{},"    canvas_draw_rect(canvas, 20, 20, 160, 100);\n",[59,15092,15093],{"class":61,"line":1336},[59,15094,174],{"emptyLinePlaceholder":173},[59,15096,15097],{"class":61,"line":1342},[59,15098,15099],{},"    \u002F* 蓝色填充椭圆 *\u002F\n",[59,15101,15102],{"class":61,"line":1348},[59,15103,15104],{},"    canvas_set_brush(canvas, 60, 120, 200, 200);\n",[59,15106,15107],{"class":61,"line":5377},[59,15108,15109],{},"    canvas_fill_ellipse(canvas, 220, 20, 160, 100);\n",[59,15111,15112],{"class":61,"line":7589},[59,15113,174],{"emptyLinePlaceholder":173},[59,15115,15116],{"class":61,"line":7595},[59,15117,15118],{},"    \u002F* 绿色直线 *\u002F\n",[59,15120,15121],{"class":61,"line":7600},[59,15122,15123],{},"    canvas_set_pen(canvas, 60, 160, 60, 255, 2);\n",[59,15125,15126],{"class":61,"line":7605},[59,15127,15128],{},"    canvas_draw_line(canvas, 20, 180, 380, 180);\n",[59,15130,15132],{"class":61,"line":15131},26,[59,15133,174],{"emptyLinePlaceholder":173},[59,15135,15137],{"class":61,"line":15136},27,[59,15138,15139],{},"    \u002F* 半透明填充三角形 *\u002F\n",[59,15141,15143],{"class":61,"line":15142},28,[59,15144,15145],{},"    canvas_set_brush(canvas, 200, 160, 60, 180);\n",[59,15147,15149],{"class":61,"line":15148},29,[59,15150,15151],{},"    PointF pts[3] = {{200, 220}, {100, 350}, {300, 350}};\n",[59,15153,15155],{"class":61,"line":15154},30,[59,15156,15157],{},"    canvas_fill_polygon(canvas, pts, 3);\n",[59,15159,15161],{"class":61,"line":15160},31,[59,15162,174],{"emptyLinePlaceholder":173},[59,15164,15166],{"class":61,"line":15165},32,[59,15167,15168],{},"    \u002F* 灰色文字 *\u002F\n",[59,15170,15172],{"class":61,"line":15171},33,[59,15173,15174],{},"    canvas_draw_text(canvas, L\"eduwin 画布\", 140, 370, 80, 80, 80, 255, 16);\n",[59,15176,15178],{"class":61,"line":15177},34,[59,15179,174],{"emptyLinePlaceholder":173},[59,15181,15183],{"class":61,"line":15182},35,[59,15184,5370],{},[59,15186,15188],{"class":61,"line":15187},36,[59,15189,2087],{},[59,15191,15193],{"class":61,"line":15192},37,[59,15194,149],{},[18,15196,15197],{},"编译命令：",[50,15199,15201],{"className":7634,"code":15200,"language":7636,"meta":55,"style":55},"cl canvas_demo.c \u002FFeoutput.exe \u002FI include \u002Flink eduwin.lib lib\\*.lib\n",[22,15202,15203],{"__ignoreMap":55},[59,15204,15205],{"class":61,"line":62},[59,15206,15200],{},[362,15208,364],{},{"title":55,"searchDepth":88,"depth":88,"links":15210},[15211,15212,15213,15214,15215,15216,15217,15218,15219,15220,15221],{"id":14186,"depth":88,"text":14187},{"id":14236,"depth":88,"text":14237},{"id":14322,"depth":88,"text":14323},{"id":14401,"depth":88,"text":14402},{"id":14541,"depth":88,"text":14542},{"id":14618,"depth":88,"text":14619},{"id":14757,"depth":88,"text":14758},{"id":14837,"depth":88,"text":14838},{"id":14890,"depth":88,"text":14891},{"id":14967,"depth":88,"text":14968},{"id":298,"depth":88,"text":298},{},"\u002Fdocs\u002Ftutorial\u002Feduwin-canvas",{"title":14173,"description":14181},"docs\u002Ftutorial\u002Feduwin-canvas","hIYP7sIEtJw9iSbaWOl8Jk6MvjGU5Wgo7GrIAngjimU",{"id":15228,"title":15229,"body":15230,"description":15237,"extension":374,"meta":16789,"navigation":173,"path":16790,"seo":16791,"stem":16792,"__hash__":16793},"content\u002Fdocs\u002Ftutorial\u002Feduwin-d3d.md","eduwin 3D 渲染教程",{"type":8,"value":15231,"toc":16765},[15232,15235,15238,15240,15243,15269,15271,15275,15279,15290,15296,15303,15307,15313,15316,15349,15353,15360,15366,15372,15375,15379,15386,15441,15448,15459,15473,15477,15483,15489,15494,15505,15530,15534,15541,15580,15644,15648,15659,15699,15775,15779,15785,15788,15790,15794,15798,15990,15994,16089,16098,16102,16115,16117,16135,16138,16151,16155,16160,16220,16227,16245,16248,16287,16289,16293,16300,16304,16569,16573,16576,16580,16661,16665,16673,16726,16732,16734,16738,16763],[11,15233,15229],{"id":15234},"eduwin-3d-渲染教程",[18,15236,15237],{},"本文从零讲解 3D 渲染的基础概念，然后步进到 eduwin D3D 封装的使用。",[432,15239],{},[46,15241,15242],{"id":15242},"目录",[5250,15244,15245,15251,15257,15263],{},[318,15246,15247],{},[745,15248,15250],{"href":15249},"#1-3d-%E6%B8%B2%E6%9F%93%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5","3D 渲染的基本概念",[318,15252,15253],{},[745,15254,15256],{"href":15255},"#2-eduwin-%E7%9A%84-d3d-%E5%B0%81%E8%A3%85","eduwin 的 D3D 封装",[318,15258,15259],{},[745,15260,15262],{"href":15261},"#3-%E6%9E%84%E5%BB%BA%E7%AC%AC%E4%B8%80%E4%B8%AA-3d-%E7%A8%8B%E5%BA%8F","构建第一个 3D 程序",[318,15264,15265],{},[745,15266,15268],{"href":15267},"#4-%E8%BF%9B%E4%B8%80%E6%AD%A5%E9%98%85%E8%AF%BB","进一步阅读",[432,15270],{},[46,15272,15274],{"id":15273},"_1-3d-渲染的基本概念","1. 3D 渲染的基本概念",[1020,15276,15278],{"id":15277},"_11-坐标系","1.1 坐标系",[18,15280,15281,15282,15285,15286,15289],{},"3D 渲染使用",[69,15283,15284],{},"三维坐标系","来表示空间中的位置。每个点用 ",[22,15287,15288],{},"(x, y, z)"," 三个坐标表示。",[50,15291,15294],{"className":15292,"code":15293,"language":358},[356],"  y\n  |\n  |___ x\n \u002F\nz\n",[22,15295,15293],{"__ignoreMap":55},[18,15297,15298,15299,15302],{},"D3D11 使用",[69,15300,15301],{},"左手系","：伸出左手，食指朝上（Y），拇指朝右（X），中指朝前（Z）。Z 正方向指向屏幕内部。",[1020,15304,15306],{"id":15305},"_12-顶点","1.2 顶点",[18,15308,15309,15312],{},[69,15310,15311],{},"顶点","是 3D 模型的基本单元。一个顶点包含位置信息，还可以包含颜色、法线、纹理坐标等属性。",[18,15314,15315],{},"在 eduwin 中，顶点包含位置 + 法线 + UV + 顶点色：",[50,15317,15319],{"className":52,"code":15318,"language":54,"meta":55,"style":55},"typedef struct {\n    float x, y, z;       \u002F\u002F 位置坐标\n    float nx, ny, nz;    \u002F\u002F 法线\n    float u, v;          \u002F\u002F 纹理坐标\n    float r, g, b, a;    \u002F\u002F 顶点色 (红绿蓝透明度，范围 0~1)\n} dx_vertex;\n",[22,15320,15321,15325,15330,15335,15340,15345],{"__ignoreMap":55},[59,15322,15323],{"class":61,"line":62},[59,15324,3881],{},[59,15326,15327],{"class":61,"line":88},[59,15328,15329],{},"    float x, y, z;       \u002F\u002F 位置坐标\n",[59,15331,15332],{"class":61,"line":134},[59,15333,15334],{},"    float nx, ny, nz;    \u002F\u002F 法线\n",[59,15336,15337],{"class":61,"line":140},[59,15338,15339],{},"    float u, v;          \u002F\u002F 纹理坐标\n",[59,15341,15342],{"class":61,"line":146},[59,15343,15344],{},"    float r, g, b, a;    \u002F\u002F 顶点色 (红绿蓝透明度，范围 0~1)\n",[59,15346,15347],{"class":61,"line":281},[59,15348,3906],{},[1020,15350,15352],{"id":15351},"_13-三角形和索引","1.3 三角形和索引",[18,15354,15355,15356,15359],{},"三个顶点构成一个",[69,15357,15358],{},"三角形","（3D 渲染的最小图元）。一堆三角形拼起来就是 3D 模型。",[18,15361,15362,15365],{},[69,15363,15364],{},"索引","的作用是复用顶点。例如一个矩形需要 2 个三角形（6 个顶点），但如果用索引，只需 4 个顶点 + 6 个索引：",[50,15367,15370],{"className":15368,"code":15369,"language":358},[356],"顶点: [A, B, C, D]\n索引: [0,1,2,  0,2,3]  →  三角形1: A-B-C, 三角形2: A-C-D\n",[22,15371,15369],{"__ignoreMap":55},[18,15373,15374],{},"eduwin 使用 16 位无符号整数索引。",[1020,15376,15378],{"id":15377},"_14-变换矩阵","1.4 变换矩阵",[18,15380,15381,15382,15385],{},"3D 中的位置变换通过 ",[69,15383,15384],{},"4×4 矩阵"," 计算。最基本的变换有三种：",[4903,15387,15388,15400],{},[4906,15389,15390],{},[4909,15391,15392,15395,15398],{},[4912,15393,15394],{},"变换",[4912,15396,15397],{},"作用",[4912,15399,1890],{},[4919,15401,15402,15415,15428],{},[4909,15403,15404,15409,15412],{},[4924,15405,15406],{},[69,15407,15408],{},"平移 (Translation)",[4924,15410,15411],{},"移动位置",[4924,15413,15414],{},"把物体从原点移到 (10, 0, 0)",[4909,15416,15417,15422,15425],{},[4924,15418,15419],{},[69,15420,15421],{},"旋转 (Rotation)",[4924,15423,15424],{},"绕轴旋转",[4924,15426,15427],{},"物体绕 Y 轴转 45°",[4909,15429,15430,15435,15438],{},[4924,15431,15432],{},[69,15433,15434],{},"缩放 (Scale)",[4924,15436,15437],{},"放大缩小",[4924,15439,15440],{},"把物体放大 2 倍",[18,15442,15443,15444,15447],{},"矩阵可以",[69,15445,15446],{},"相乘组合","：先旋转再平移 = 旋转矩阵 × 平移矩阵。",[18,15449,15450,15451,15453,15454,3821,15456,15458],{},"eduwin 使用 ",[69,15452,4836],{}," 矩阵存储，与 ",[22,15455,3820],{},[22,15457,3824],{}," 约定一致：",[50,15460,15462],{"className":52,"code":15461,"language":54,"meta":55,"style":55},"typedef struct { float m[16]; } dx_mat4;\n\u002F\u002F m[row*4+col] — m[0]=_11, m[1]=_12, m[4]=_21, m[12]=_41\n",[22,15463,15464,15468],{"__ignoreMap":55},[59,15465,15466],{"class":61,"line":62},[59,15467,3846],{},[59,15469,15470],{"class":61,"line":88},[59,15471,15472],{},"\u002F\u002F m[row*4+col] — m[0]=_11, m[1]=_12, m[4]=_21, m[12]=_41\n",[1020,15474,15476],{"id":15475},"_15-世界-视图-投影","1.5 世界 → 视图 → 投影",[18,15478,15479,15480,4232],{},"3D 渲染的核心管线需要",[69,15481,15482],{},"三个矩阵",[50,15484,15487],{"className":15485,"code":15486,"language":358},[356],"顶点位置 → 世界矩阵 → 视图矩阵 → 投影矩阵 → 屏幕坐标\n         (模型摆哪)  (相机看哪)  (透视效果)\n",[22,15488,15486],{"__ignoreMap":55},[15490,15491,15493],"h4",{"id":15492},"世界矩阵-world-matrix","世界矩阵 (World Matrix)",[18,15495,15496,15497,15500,15501,15504],{},"把模型从",[69,15498,15499],{},"模型坐标系","（原点在模型中心）变换到",[69,15502,15503],{},"世界坐标系","（模型在场景中的位置）。",[50,15506,15508],{"className":52,"code":15507,"language":54,"meta":55,"style":55},"\u002F\u002F 物体在原点绕 Y 轴旋转\ndx_mat4 world;\ndx_mat4_rotation_y(&world, angle);\ndx_set_world(world);\n",[22,15509,15510,15515,15520,15525],{"__ignoreMap":55},[59,15511,15512],{"class":61,"line":62},[59,15513,15514],{},"\u002F\u002F 物体在原点绕 Y 轴旋转\n",[59,15516,15517],{"class":61,"line":88},[59,15518,15519],{},"dx_mat4 world;\n",[59,15521,15522],{"class":61,"line":134},[59,15523,15524],{},"dx_mat4_rotation_y(&world, angle);\n",[59,15526,15527],{"class":61,"line":140},[59,15528,15529],{},"dx_set_world(world);\n",[15490,15531,15533],{"id":15532},"视图矩阵-view-matrix","视图矩阵 (View Matrix)",[18,15535,15536,15537,15540],{},"把世界坐标变换到",[69,15538,15539],{},"相机坐标系","（以相机为原点）。",[50,15542,15544],{"className":52,"code":15543,"language":54,"meta":55,"style":55},"float eye[] = {0, 3, -5};  \u002F\u002F 相机位置\nfloat at[]  = {0, 0, 0};   \u002F\u002F 观察目标\nfloat up[]  = {0, 1, 0};   \u002F\u002F 上方向\n\ndx_mat4 view;\ndx_mat4_look_at_lh(&view, eye, at, up);\ndx_set_view(view);\n",[22,15545,15546,15551,15556,15561,15565,15570,15575],{"__ignoreMap":55},[59,15547,15548],{"class":61,"line":62},[59,15549,15550],{},"float eye[] = {0, 3, -5};  \u002F\u002F 相机位置\n",[59,15552,15553],{"class":61,"line":88},[59,15554,15555],{},"float at[]  = {0, 0, 0};   \u002F\u002F 观察目标\n",[59,15557,15558],{"class":61,"line":134},[59,15559,15560],{},"float up[]  = {0, 1, 0};   \u002F\u002F 上方向\n",[59,15562,15563],{"class":61,"line":140},[59,15564,174],{"emptyLinePlaceholder":173},[59,15566,15567],{"class":61,"line":146},[59,15568,15569],{},"dx_mat4 view;\n",[59,15571,15572],{"class":61,"line":281},[59,15573,15574],{},"dx_mat4_look_at_lh(&view, eye, at, up);\n",[59,15576,15577],{"class":61,"line":286},[59,15578,15579],{},"dx_set_view(view);\n",[4903,15581,15582,15595],{},[4906,15583,15584],{},[4909,15585,15586,15589,15592],{},[4912,15587,15588],{},"参数名",[4912,15590,15591],{},"含义",[4912,15593,15594],{},"示例值",[4919,15596,15597,15613,15629],{},[4909,15598,15599,15604,15607],{},[4924,15600,15601],{},[22,15602,15603],{},"eye",[4924,15605,15606],{},"相机坐标",[4924,15608,15609,15612],{},[22,15610,15611],{},"{0, 3, -5}"," 即头顶稍后",[4909,15614,15615,15620,15623],{},[4924,15616,15617],{},[22,15618,15619],{},"at",[4924,15621,15622],{},"观察目标点",[4924,15624,15625,15628],{},[22,15626,15627],{},"{0, 0, 0}"," 看向原点",[4909,15630,15631,15635,15638],{},[4924,15632,15633],{},[22,15634,1705],{},[4924,15636,15637],{},"世界\"向上\"方向",[4924,15639,15640,15643],{},[22,15641,15642],{},"{0, 1, 0}"," Y 轴向上",[15490,15645,15647],{"id":15646},"投影矩阵-projection-matrix","投影矩阵 (Projection Matrix)",[18,15649,15650,15651,15654,15655,15658],{},"把 3D 坐标变换到 ",[69,15652,15653],{},"2D 屏幕","（Clip Space），实现",[69,15656,15657],{},"近大远小","的透视效果。",[50,15660,15662],{"className":52,"code":15661,"language":54,"meta":55,"style":55},"dx_mat4 proj;\ndx_mat4_perspective_fov_lh(&proj,\n    DX_PI\u002F3,           \u002F\u002F FOV: 60°\n    800.0f\u002F600.0f,     \u002F\u002F Aspect: 宽\u002F高\n    0.1f,              \u002F\u002F Near: 近裁剪面\n    100.0f);           \u002F\u002F Far: 远裁剪面\ndx_set_projection(proj);\n",[22,15663,15664,15669,15674,15679,15684,15689,15694],{"__ignoreMap":55},[59,15665,15666],{"class":61,"line":62},[59,15667,15668],{},"dx_mat4 proj;\n",[59,15670,15671],{"class":61,"line":88},[59,15672,15673],{},"dx_mat4_perspective_fov_lh(&proj,\n",[59,15675,15676],{"class":61,"line":134},[59,15677,15678],{},"    DX_PI\u002F3,           \u002F\u002F FOV: 60°\n",[59,15680,15681],{"class":61,"line":140},[59,15682,15683],{},"    800.0f\u002F600.0f,     \u002F\u002F Aspect: 宽\u002F高\n",[59,15685,15686],{"class":61,"line":146},[59,15687,15688],{},"    0.1f,              \u002F\u002F Near: 近裁剪面\n",[59,15690,15691],{"class":61,"line":281},[59,15692,15693],{},"    100.0f);           \u002F\u002F Far: 远裁剪面\n",[59,15695,15696],{"class":61,"line":286},[59,15697,15698],{},"dx_set_projection(proj);\n",[4903,15700,15701,15712],{},[4906,15702,15703],{},[4909,15704,15705,15707,15709],{},[4912,15706,15588],{},[4912,15708,15591],{},[4912,15710,15711],{},"典型值",[4919,15713,15714,15730,15745,15760],{},[4909,15715,15716,15721,15724],{},[4924,15717,15718],{},[22,15719,15720],{},"fov_y",[4924,15722,15723],{},"Y 方向视场角（弧度）",[4924,15725,15726,15729],{},[22,15727,15728],{},"π\u002F3","（60°）",[4909,15731,15732,15737,15740],{},[4924,15733,15734],{},[22,15735,15736],{},"aspect",[4924,15738,15739],{},"屏幕宽高比",[4924,15741,15742],{},[22,15743,15744],{},"800\u002F600 = 1.333",[4909,15746,15747,15752,15755],{},[4924,15748,15749],{},[22,15750,15751],{},"zn",[4924,15753,15754],{},"近裁剪面",[4924,15756,15757],{},[22,15758,15759],{},"0.1f",[4909,15761,15762,15767,15770],{},[4924,15763,15764],{},[22,15765,15766],{},"zf",[4924,15768,15769],{},"远裁剪面",[4924,15771,15772],{},[22,15773,15774],{},"100.0f",[1020,15776,15778],{"id":15777},"_16-渲染管线","1.6 渲染管线",[50,15780,15783],{"className":15781,"code":15782,"language":358},[356],"顶点输入 ─→ 顶点着色器 ─→ 光栅化 ─→ 像素着色器 ─→ 输出合并\n              (VS)                 (PS)\n",[22,15784,15782],{"__ignoreMap":55},[18,15786,15787],{},"eduwin 内嵌的 HLSL 顶点着色器用世界\u002F视图\u002F投影三个矩阵变换顶点位置，像素着色器计算顶点色 + 光照结果。",[432,15789],{},[46,15791,15793],{"id":15792},"_2-eduwin-的-d3d-封装","2. eduwin 的 D3D 封装",[1020,15795,15797],{"id":15796},"_21-主要函数一览","2.1 主要函数一览",[4903,15799,15800,15808],{},[4906,15801,15802],{},[4909,15803,15804,15806],{},[4912,15805,6071],{},[4912,15807,10556],{},[4919,15809,15810,15820,15830,15840,15850,15860,15870,15880,15890,15900,15910,15920,15930,15940,15950,15960,15970,15980],{},[4909,15811,15812,15817],{},[4924,15813,15814],{},[22,15815,15816],{},"dx_init(hwnd, w, h)",[4924,15818,15819],{},"初始化 D3D11",[4909,15821,15822,15827],{},[4924,15823,15824],{},[22,15825,15826],{},"dx_shutdown()",[4924,15828,15829],{},"销毁所有资源",[4909,15831,15832,15837],{},[4924,15833,15834],{},[22,15835,15836],{},"dx_begin(r,g,b,a)",[4924,15838,15839],{},"开始一帧",[4909,15841,15842,15847],{},[4924,15843,15844],{},[22,15845,15846],{},"dx_end()",[4924,15848,15849],{},"结束一帧，交换缓冲",[4909,15851,15852,15857],{},[4924,15853,15854],{},[22,15855,15856],{},"dx_set_world(m)",[4924,15858,15859],{},"设置世界矩阵",[4909,15861,15862,15867],{},[4924,15863,15864],{},[22,15865,15866],{},"dx_set_view(m)",[4924,15868,15869],{},"设置视图矩阵",[4909,15871,15872,15877],{},[4924,15873,15874],{},[22,15875,15876],{},"dx_set_projection(m)",[4924,15878,15879],{},"设置投影矩阵",[4909,15881,15882,15887],{},[4924,15883,15884],{},[22,15885,15886],{},"dx_create_geometry(v,c,i,c)",[4924,15888,15889],{},"创建自定义几何体",[4909,15891,15892,15897],{},[4924,15893,15894],{},[22,15895,15896],{},"dx_create_box(w,h,d, r,g,b,a)",[4924,15898,15899],{},"创建立方体",[4909,15901,15902,15907],{},[4924,15903,15904],{},[22,15905,15906],{},"dx_create_sphere(r, r,g,b,a)",[4924,15908,15909],{},"创建球体",[4909,15911,15912,15917],{},[4924,15913,15914],{},[22,15915,15916],{},"dx_create_cylinder(r,h, r,g,b,a)",[4924,15918,15919],{},"创建圆柱体",[4909,15921,15922,15927],{},[4924,15923,15924],{},[22,15925,15926],{},"dx_create_pyramid(w,h,d, r,g,b,a)",[4924,15928,15929],{},"创建棱锥",[4909,15931,15932,15937],{},[4924,15933,15934],{},[22,15935,15936],{},"dx_create_plane(w,h, r,g,b,a)",[4924,15938,15939],{},"创建平面",[4909,15941,15942,15947],{},[4924,15943,15944],{},[22,15945,15946],{},"dx_draw_geometry(g,c,s,b)",[4924,15948,15949],{},"绘制几何体",[4909,15951,15952,15957],{},[4924,15953,15954],{},[22,15955,15956],{},"dx_free_geometry(g)",[4924,15958,15959],{},"释放几何体",[4909,15961,15962,15967],{},[4924,15963,15964],{},[22,15965,15966],{},"dx_set_ambient_light(r,g,b,i)",[4924,15968,15969],{},"设置环境光",[4909,15971,15972,15977],{},[4924,15973,15974],{},[22,15975,15976],{},"dx_set_directional_light(dx,dy,dz, r,g,b,i)",[4924,15978,15979],{},"设置方向光",[4909,15981,15982,15987],{},[4924,15983,15984],{},[22,15985,15986],{},"dx_set_point_light(px,py,pz, r,g,b,i, range)",[4924,15988,15989],{},"设置点光源",[1020,15991,15993],{"id":15992},"_22-矩阵辅助函数","2.2 矩阵辅助函数",[4903,15995,15996,16004],{},[4906,15997,15998],{},[4909,15999,16000,16002],{},[4912,16001,6071],{},[4912,16003,10556],{},[4919,16005,16006,16016,16029,16039,16049,16059,16069,16079],{},[4909,16007,16008,16013],{},[4924,16009,16010],{},[22,16011,16012],{},"dx_mat4_identity(&m)",[4924,16014,16015],{},"单位矩阵",[4909,16017,16018,16023],{},[4924,16019,16020],{},[22,16021,16022],{},"dx_mat4_mul(&o,&a,&b)",[4924,16024,16025,16026],{},"矩阵乘法 ",[22,16027,16028],{},"o = a × b",[4909,16030,16031,16036],{},[4924,16032,16033],{},[22,16034,16035],{},"dx_mat4_rotation_x(&m, a)",[4924,16037,16038],{},"绕 X 轴旋转",[4909,16040,16041,16046],{},[4924,16042,16043],{},[22,16044,16045],{},"dx_mat4_rotation_y(&m, a)",[4924,16047,16048],{},"绕 Y 轴旋转",[4909,16050,16051,16056],{},[4924,16052,16053],{},[22,16054,16055],{},"dx_mat4_rotation_z(&m, a)",[4924,16057,16058],{},"绕 Z 轴旋转",[4909,16060,16061,16066],{},[4924,16062,16063],{},[22,16064,16065],{},"dx_mat4_translation(&m, x,y,z)",[4924,16067,16068],{},"平移",[4909,16070,16071,16076],{},[4924,16072,16073],{},[22,16074,16075],{},"dx_mat4_look_at_lh(&m, e,a,u)",[4924,16077,16078],{},"观察矩阵（左手系）",[4909,16080,16081,16086],{},[4924,16082,16083],{},[22,16084,16085],{},"dx_mat4_perspective_fov_lh(&m, f,a,n,f)",[4924,16087,16088],{},"透视投影（左手系）",[15,16090,16091],{},[18,16092,16093,16094],{},"更多细节见 ",[745,16095,16097],{"href":16096},"\u002Fcontent\u002Fdocs\u002Fapi\u002Feduwin-d3d-api.md","eduwin D3D API 文档",[1020,16099,16101],{"id":16100},"_23-相机封装","2.3 相机封装",[50,16103,16105],{"className":52,"code":16104,"language":54,"meta":55,"style":55},"void dx_camera_init(dx_camera *cam, float px, float py, float pz, float aspect);\nvoid dx_camera_update(dx_camera *cam);\n",[22,16106,16107,16111],{"__ignoreMap":55},[59,16108,16109],{"class":61,"line":62},[59,16110,4169],{},[59,16112,16113],{"class":61,"line":88},[59,16114,4174],{},[18,16116,4177],{},[50,16118,16120],{"className":52,"code":16119,"language":54,"meta":55,"style":55},"dx_camera cam;\ndx_camera_init(&cam, 0, 3, -5, 800.0f \u002F 600.0f);  \u002F\u002F 位置 + 宽高比\ndx_camera_update(&cam);                             \u002F\u002F 每帧更新\n",[22,16121,16122,16126,16131],{"__ignoreMap":55},[59,16123,16124],{"class":61,"line":62},[59,16125,4187],{},[59,16127,16128],{"class":61,"line":88},[59,16129,16130],{},"dx_camera_init(&cam, 0, 3, -5, 800.0f \u002F 600.0f);  \u002F\u002F 位置 + 宽高比\n",[59,16132,16133],{"class":61,"line":134},[59,16134,4197],{},[18,16136,16137],{},"窗口 resize 时改 aspect：",[50,16139,16141],{"className":52,"code":16140,"language":54,"meta":55,"style":55},"cam.aspect = 1024.0f \u002F 768.0f;\ndx_camera_update(&cam);\n",[22,16142,16143,16147],{"__ignoreMap":55},[59,16144,16145],{"class":61,"line":62},[59,16146,4211],{},[59,16148,16149],{"class":61,"line":88},[59,16150,4216],{},[1020,16152,16154],{"id":16153},"_24-网格mesh-geometry-material-transform","2.4 网格（Mesh = Geometry + Material + Transform）",[18,16156,16157,16159],{},[22,16158,4227],{}," 把几何体 + 材质 + 变换打包，隐藏矩阵操作，仿 Three.js Mesh：",[50,16161,16162],{"className":52,"code":4235,"language":54,"meta":55,"style":55},[22,16163,16164,16168,16172,16176,16180,16184,16188,16192,16196,16200,16204,16208,16212,16216],{"__ignoreMap":55},[59,16165,16166],{"class":61,"line":62},[59,16167,3881],{},[59,16169,16170],{"class":61,"line":88},[59,16171,4246],{},[59,16173,16174],{"class":61,"line":134},[59,16175,4251],{},[59,16177,16178],{"class":61,"line":140},[59,16179,4256],{},[59,16181,16182],{"class":61,"line":146},[59,16183,4261],{},[59,16185,16186],{"class":61,"line":281},[59,16187,4266],{},[59,16189,16190],{"class":61,"line":286},[59,16191,4271],{},[59,16193,16194],{"class":61,"line":292},[59,16195,174],{"emptyLinePlaceholder":173},[59,16197,16198],{"class":61,"line":600},[59,16199,4280],{},[59,16201,16202],{"class":61,"line":917},[59,16203,4285],{},[59,16205,16206],{"class":61,"line":922},[59,16207,4290],{},[59,16209,16210],{"class":61,"line":928},[59,16211,4295],{},[59,16213,16214],{"class":61,"line":933},[59,16215,4300],{},[59,16217,16218],{"class":61,"line":939},[59,16219,4305],{},[18,16221,16222,4311,16224,16226],{},[22,16223,4310],{},[22,16225,4314],{},"，绑定材质，绘制几何体。\n可直接读写字段：",[50,16228,16230],{"className":52,"code":16229,"language":54,"meta":55,"style":55},"mesh->position[0] = 2;    \u002F* X 位置 *\u002F\nmesh->rotation[1] = 0.5f; \u002F* Y 旋转 *\u002F\nmesh->scale[0] = 2.0f;    \u002F* 水平拉长 2 倍 *\u002F\n",[22,16231,16232,16236,16240],{"__ignoreMap":55},[59,16233,16234],{"class":61,"line":62},[59,16235,4325],{},[59,16237,16238],{"class":61,"line":88},[59,16239,4330],{},[59,16241,16242],{"class":61,"line":134},[59,16243,16244],{},"mesh->scale[0] = 2.0f;    \u002F* 水平拉长 2 倍 *\u002F\n",[18,16246,16247],{},"位置是固有属性，初始化设一次，每帧只改变化的旋转：",[50,16249,16251],{"className":52,"code":16250,"language":54,"meta":55,"style":55},"\u002F\u002F 初始化（一次）\ndx_mesh *obj = dx_create_mesh(geo, NULL);\ndx_mesh_set_position(obj, 0, 0.5f, 0);\n\n\u002F\u002F 每帧\nobj->rotation[1] = angle;\ndx_mesh_draw(obj);\n",[22,16252,16253,16258,16263,16268,16272,16277,16282],{"__ignoreMap":55},[59,16254,16255],{"class":61,"line":62},[59,16256,16257],{},"\u002F\u002F 初始化（一次）\n",[59,16259,16260],{"class":61,"line":88},[59,16261,16262],{},"dx_mesh *obj = dx_create_mesh(geo, NULL);\n",[59,16264,16265],{"class":61,"line":134},[59,16266,16267],{},"dx_mesh_set_position(obj, 0, 0.5f, 0);\n",[59,16269,16270],{"class":61,"line":140},[59,16271,174],{"emptyLinePlaceholder":173},[59,16273,16274],{"class":61,"line":146},[59,16275,16276],{},"\u002F\u002F 每帧\n",[59,16278,16279],{"class":61,"line":281},[59,16280,16281],{},"obj->rotation[1] = angle;\n",[59,16283,16284],{"class":61,"line":286},[59,16285,16286],{},"dx_mesh_draw(obj);\n",[432,16288],{},[46,16290,16292],{"id":16291},"_3-构建第一个-3d-程序","3. 构建第一个 3D 程序",[18,16294,16295,16296,16299],{},"下面以 ",[22,16297,16298],{},"samples\u002Fd3d_light_demo\u002F"," 为蓝本，一步步构建一个带光照的 3D 场景。",[1020,16301,16303],{"id":16302},"_31-完整代码","3.1 完整代码",[50,16305,16307],{"className":52,"code":16306,"language":54,"meta":55,"style":55},"#include \"eduwin.h\"\n#include \u003Cmath.h>\n\nstatic dx_mesh *g_box, *g_ground;\nstatic dx_camera g_cam;\nstatic float g_light_angle = 0;\n\nstatic void on_frame(void)\n{\n    float px, py, pz;\n\n    if (g_box) g_box->rotation[1] += 0.01f;      \u002F* 立方体自转 *\u002F\n\n    \u002F* 点光源绕立方体旋转 *\u002F\n    g_light_angle += 0.015f;\n    px = 2.5f * cosf(g_light_angle);\n    pz = 2.5f * sinf(g_light_angle);\n    py = 1.5f;\n\n    dx_camera_update(&g_cam);\n    dx_begin(0.12f, 0.12f, 0.2f, 1);     \u002F* 深蓝背景 *\u002F\n\n    \u002F* 设置光照 *\u002F\n    dx_set_ambient_light(0.4f, 0.4f, 0.4f, 1);\n    dx_set_directional_light(0.5f, -0.707f, 0.5f, 1, 1, 1, 1);\n    dx_set_point_light(px, py, pz, 1, 0.6f, 0.1f, 0.8f, 5);\n\n    dx_mesh_draw(g_ground);               \u002F* 地面 *\u002F\n    dx_mesh_draw(g_box);                  \u002F* 立方体 *\u002F\n\n    dx_end();\n}\n\nint main(void)\n{\n    if (init_app() != 0) { log_error(L\"init失败\"); return 1; }\n    HWND win = create_window(L\"3D 光照演示\", 800, 600);\n    if (!win) return 1;\n    show_window(win);\n    if (!dx_init(win, 800, 600)) return 1;\n\n    \u002F* 创建物体 *\u002F\n    g_box    = dx_create_mesh(dx_create_box(1.2f, 1.2f, 1.2f, 0.85f, 0.15f, 0.15f, 1), NULL);\n    g_ground = dx_create_mesh(dx_create_plane(8, 8, 0.5f, 0.5f, 0.5f, 1), NULL);\n    dx_mesh_set_position(g_box,    0, 0, 0);\n    dx_mesh_set_position(g_ground, 0, -0.8f, 0);\n\n    dx_camera_init(&g_cam, 0, 1, -3.5f, 800.0f \u002F 600.0f);\n\n    run_app_frame(on_frame);\n    dx_shutdown();\n    return 0;\n}\n",[22,16308,16309,16313,16318,16322,16327,16332,16337,16341,16346,16350,16355,16359,16364,16368,16373,16378,16383,16388,16393,16397,16402,16407,16411,16416,16421,16426,16431,16435,16440,16445,16449,16454,16458,16462,16466,16470,16475,16480,16485,16490,16496,16501,16507,16513,16519,16525,16531,16536,16542,16547,16553,16559,16564],{"__ignoreMap":55},[59,16310,16311],{"class":61,"line":62},[59,16312,5287],{},[59,16314,16315],{"class":61,"line":88},[59,16316,16317],{},"#include \u003Cmath.h>\n",[59,16319,16320],{"class":61,"line":134},[59,16321,174],{"emptyLinePlaceholder":173},[59,16323,16324],{"class":61,"line":140},[59,16325,16326],{},"static dx_mesh *g_box, *g_ground;\n",[59,16328,16329],{"class":61,"line":146},[59,16330,16331],{},"static dx_camera g_cam;\n",[59,16333,16334],{"class":61,"line":281},[59,16335,16336],{},"static float g_light_angle = 0;\n",[59,16338,16339],{"class":61,"line":286},[59,16340,174],{"emptyLinePlaceholder":173},[59,16342,16343],{"class":61,"line":292},[59,16344,16345],{},"static void on_frame(void)\n",[59,16347,16348],{"class":61,"line":600},[59,16349,574],{},[59,16351,16352],{"class":61,"line":917},[59,16353,16354],{},"    float px, py, pz;\n",[59,16356,16357],{"class":61,"line":922},[59,16358,174],{"emptyLinePlaceholder":173},[59,16360,16361],{"class":61,"line":928},[59,16362,16363],{},"    if (g_box) g_box->rotation[1] += 0.01f;      \u002F* 立方体自转 *\u002F\n",[59,16365,16366],{"class":61,"line":933},[59,16367,174],{"emptyLinePlaceholder":173},[59,16369,16370],{"class":61,"line":939},[59,16371,16372],{},"    \u002F* 点光源绕立方体旋转 *\u002F\n",[59,16374,16375],{"class":61,"line":1320},[59,16376,16377],{},"    g_light_angle += 0.015f;\n",[59,16379,16380],{"class":61,"line":1326},[59,16381,16382],{},"    px = 2.5f * cosf(g_light_angle);\n",[59,16384,16385],{"class":61,"line":1331},[59,16386,16387],{},"    pz = 2.5f * sinf(g_light_angle);\n",[59,16389,16390],{"class":61,"line":1336},[59,16391,16392],{},"    py = 1.5f;\n",[59,16394,16395],{"class":61,"line":1342},[59,16396,174],{"emptyLinePlaceholder":173},[59,16398,16399],{"class":61,"line":1348},[59,16400,16401],{},"    dx_camera_update(&g_cam);\n",[59,16403,16404],{"class":61,"line":5377},[59,16405,16406],{},"    dx_begin(0.12f, 0.12f, 0.2f, 1);     \u002F* 深蓝背景 *\u002F\n",[59,16408,16409],{"class":61,"line":7589},[59,16410,174],{"emptyLinePlaceholder":173},[59,16412,16413],{"class":61,"line":7595},[59,16414,16415],{},"    \u002F* 设置光照 *\u002F\n",[59,16417,16418],{"class":61,"line":7600},[59,16419,16420],{},"    dx_set_ambient_light(0.4f, 0.4f, 0.4f, 1);\n",[59,16422,16423],{"class":61,"line":7605},[59,16424,16425],{},"    dx_set_directional_light(0.5f, -0.707f, 0.5f, 1, 1, 1, 1);\n",[59,16427,16428],{"class":61,"line":15131},[59,16429,16430],{},"    dx_set_point_light(px, py, pz, 1, 0.6f, 0.1f, 0.8f, 5);\n",[59,16432,16433],{"class":61,"line":15136},[59,16434,174],{"emptyLinePlaceholder":173},[59,16436,16437],{"class":61,"line":15142},[59,16438,16439],{},"    dx_mesh_draw(g_ground);               \u002F* 地面 *\u002F\n",[59,16441,16442],{"class":61,"line":15148},[59,16443,16444],{},"    dx_mesh_draw(g_box);                  \u002F* 立方体 *\u002F\n",[59,16446,16447],{"class":61,"line":15154},[59,16448,174],{"emptyLinePlaceholder":173},[59,16450,16451],{"class":61,"line":15160},[59,16452,16453],{},"    dx_end();\n",[59,16455,16456],{"class":61,"line":15165},[59,16457,149],{},[59,16459,16460],{"class":61,"line":15171},[59,16461,174],{"emptyLinePlaceholder":173},[59,16463,16464],{"class":61,"line":15177},[59,16465,5296],{},[59,16467,16468],{"class":61,"line":15182},[59,16469,574],{},[59,16471,16472],{"class":61,"line":15187},[59,16473,16474],{},"    if (init_app() != 0) { log_error(L\"init失败\"); return 1; }\n",[59,16476,16477],{"class":61,"line":15192},[59,16478,16479],{},"    HWND win = create_window(L\"3D 光照演示\", 800, 600);\n",[59,16481,16483],{"class":61,"line":16482},38,[59,16484,5333],{},[59,16486,16488],{"class":61,"line":16487},39,[59,16489,5365],{},[59,16491,16493],{"class":61,"line":16492},40,[59,16494,16495],{},"    if (!dx_init(win, 800, 600)) return 1;\n",[59,16497,16499],{"class":61,"line":16498},41,[59,16500,174],{"emptyLinePlaceholder":173},[59,16502,16504],{"class":61,"line":16503},42,[59,16505,16506],{},"    \u002F* 创建物体 *\u002F\n",[59,16508,16510],{"class":61,"line":16509},43,[59,16511,16512],{},"    g_box    = dx_create_mesh(dx_create_box(1.2f, 1.2f, 1.2f, 0.85f, 0.15f, 0.15f, 1), NULL);\n",[59,16514,16516],{"class":61,"line":16515},44,[59,16517,16518],{},"    g_ground = dx_create_mesh(dx_create_plane(8, 8, 0.5f, 0.5f, 0.5f, 1), NULL);\n",[59,16520,16522],{"class":61,"line":16521},45,[59,16523,16524],{},"    dx_mesh_set_position(g_box,    0, 0, 0);\n",[59,16526,16528],{"class":61,"line":16527},46,[59,16529,16530],{},"    dx_mesh_set_position(g_ground, 0, -0.8f, 0);\n",[59,16532,16534],{"class":61,"line":16533},47,[59,16535,174],{"emptyLinePlaceholder":173},[59,16537,16539],{"class":61,"line":16538},48,[59,16540,16541],{},"    dx_camera_init(&g_cam, 0, 1, -3.5f, 800.0f \u002F 600.0f);\n",[59,16543,16545],{"class":61,"line":16544},49,[59,16546,174],{"emptyLinePlaceholder":173},[59,16548,16550],{"class":61,"line":16549},50,[59,16551,16552],{},"    run_app_frame(on_frame);\n",[59,16554,16556],{"class":61,"line":16555},51,[59,16557,16558],{},"    dx_shutdown();\n",[59,16560,16562],{"class":61,"line":16561},52,[59,16563,2087],{},[59,16565,16567],{"class":61,"line":16566},53,[59,16568,149],{},[1020,16570,16572],{"id":16571},"_32-运行效果","3.2 运行效果",[18,16574,16575],{},"运行后你会看到一个灰色地面 + 一个红色立方体在旋转。一个暖色（橙红）点光源围绕立方体旋转，立方体表面光影随之变化，同时有一个方向光从固定方向照射。",[1020,16577,16579],{"id":16578},"_33-关键点回顾","3.3 关键点回顾",[4903,16581,16582,16591],{},[4906,16583,16584],{},[4909,16585,16586,16589],{},[4912,16587,16588],{},"知识点",[4912,16590,4917],{},[4919,16592,16593,16607,16622,16637,16651],{},[4909,16594,16595,16598],{},[4924,16596,16597],{},"几何体创建",[4924,16599,16600,735,16603,16606],{},[22,16601,16602],{},"dx_create_box",[22,16604,16605],{},"dx_create_plane"," 自动生成顶点",[4909,16608,16609,16612],{},[4924,16610,16611],{},"对象系统",[4924,16613,16614,464,16616,464,16619],{},[22,16615,4336],{},[22,16617,16618],{},"dx_mesh_set_position",[22,16620,16621],{},"mesh->rotation[1]",[4909,16623,16624,16626],{},[4924,16625,4520],{},[4924,16627,16628,464,16631,464,16634],{},[22,16629,16630],{},"dx_set_ambient_light",[22,16632,16633],{},"dx_set_directional_light",[22,16635,16636],{},"dx_set_point_light",[4909,16638,16639,16642],{},[4924,16640,16641],{},"相机",[4924,16643,16644,16647,16648],{},[22,16645,16646],{},"dx_camera_init"," + 每帧 ",[22,16649,16650],{},"dx_camera_update",[4909,16652,16653,16655],{},[4924,16654,1022],{},[4924,16656,16657,16660],{},[22,16658,16659],{},"run_app_frame(on_frame)"," 非阻塞消息循环",[1020,16662,16664],{"id":16663},"_34-调试技巧","3.4 调试技巧",[18,16666,16667,16668,16670,16671,4232],{},"eduwin D3D 的日志通过 ",[22,16669,9495],{}," 写入 exe 同目录的 ",[22,16672,9479],{},[4903,16674,16675,16684],{},[4906,16676,16677],{},[4909,16678,16679,16682],{},[4912,16680,16681],{},"日志内容",[4912,16683,15591],{},[4919,16685,16686,16696,16706,16716],{},[4909,16687,16688,16693],{},[4924,16689,16690],{},[22,16691,16692],{},"[d3d] VS: ...",[4924,16694,16695],{},"顶点着色器编译错误",[4909,16697,16698,16703],{},[4924,16699,16700],{},[22,16701,16702],{},"[d3d] PS: ...",[4924,16704,16705],{},"像素着色器编译错误",[4909,16707,16708,16713],{},[4924,16709,16710],{},[22,16711,16712],{},"[d3d] CreateDevice",[4924,16714,16715],{},"D3D11 设备创建失败",[4909,16717,16718,16723],{},[4924,16719,16720],{},[22,16721,16722],{},"[main] D3D11 初始化失败",[4924,16724,16725],{},"dx_init 返回 0",[18,16727,16728,16729,16731],{},"运行时查看 ",[22,16730,9479],{},"，无内容 = 一切正常。",[432,16733],{},[46,16735,16737],{"id":16736},"_4-进一步阅读","4. 进一步阅读",[315,16739,16740,16745,16751,16757],{},[318,16741,16742,16744],{},[745,16743,16097],{"href":16096}," — 函数参考",[318,16746,16747],{},[745,16748,16750],{"href":16749},"\u002Fcontent\u002Fdocs\u002Fsamples\u002Fd3d_light_demo.md","samples\u002Fd3d_light_demo — 动态光照演示",[318,16752,16753],{},[745,16754,16756],{"href":16755},"\u002Fcontent\u002Fdocs\u002Fsamples\u002Fd3d_shape_demo.md","samples\u002Fd3d_shape_demo — 基本体形状展示",[318,16758,16759],{},[745,16760,16762],{"href":16761},"\u002Fcontent\u002Fdocs\u002Fsamples\u002Fd3d_material_demo.md","samples\u002Fd3d_material_demo — 纹理材质演示",[362,16764,364],{},{"title":55,"searchDepth":88,"depth":88,"links":16766},[16767,16768,16776,16782,16788],{"id":15242,"depth":88,"text":15242},{"id":15273,"depth":88,"text":15274,"children":16769},[16770,16771,16772,16773,16774,16775],{"id":15277,"depth":134,"text":15278},{"id":15305,"depth":134,"text":15306},{"id":15351,"depth":134,"text":15352},{"id":15377,"depth":134,"text":15378},{"id":15475,"depth":134,"text":15476},{"id":15777,"depth":134,"text":15778},{"id":15792,"depth":88,"text":15793,"children":16777},[16778,16779,16780,16781],{"id":15796,"depth":134,"text":15797},{"id":15992,"depth":134,"text":15993},{"id":16100,"depth":134,"text":16101},{"id":16153,"depth":134,"text":16154},{"id":16291,"depth":88,"text":16292,"children":16783},[16784,16785,16786,16787],{"id":16302,"depth":134,"text":16303},{"id":16571,"depth":134,"text":16572},{"id":16578,"depth":134,"text":16579},{"id":16663,"depth":134,"text":16664},{"id":16736,"depth":88,"text":16737},{},"\u002Fdocs\u002Ftutorial\u002Feduwin-d3d",{"title":15229,"description":15237},"docs\u002Ftutorial\u002Feduwin-d3d","Bjye55_goubyyOcUuWRG6h3XYOAdprkq-BBP9j72lMM",{"id":16795,"title":16796,"body":16797,"description":17111,"extension":374,"meta":17112,"navigation":173,"path":17113,"seo":17114,"stem":17115,"__hash__":17116},"content\u002Fdocs\u002Ftutorial\u002Feduwin-debug.md","VS Code 断点调试",{"type":8,"value":16798,"toc":17100},[16799,16802,16809,16811,16815,16839,16843,16860,16866,16870,16876,16892,16897,16910,16914,16928,16943,16961,16968,16972,16975,16978,16992,16995,17000,17092,17095,17098],[11,16800,16796],{"id":16801},"vs-code-断点调试",[18,16803,16804,16805,16808],{},"eduwin 支持在 VS Code 中直接按 ",[22,16806,16807],{},"F5"," 进行断点调试。本教程将介绍如何配置和使用。",[432,16810],{},[46,16812,16814],{"id":16813},"_1-前提条件","1. 前提条件",[315,16816,16817,16829],{},[318,16818,16819,464,16822,16825,16826,1494],{},[69,16820,16821],{},"VS Code",[69,16823,16824],{},"C\u002FC++ 扩展","（发布者 Microsoft，扩展面板搜索 ",[22,16827,16828],{},"C\u002FC++",[318,16830,16831,16834,16835,1494],{},[69,16832,16833],{},"Visual Studio Build Tools"," 已安装调试器组件（安装教程见",[745,16836,16838],{"href":16837},"\u002Fdownload","下载页面",[46,16840,16842],{"id":16841},"_2-配置说明","2. 配置说明",[18,16844,16845,16848,16849,2730,16852,16855,16856,16859],{},[22,16846,16847],{},".vscode\\"," 目录位于项目根目录下，其中已提供 ",[22,16850,16851],{},"launch.json",[22,16853,16854],{},"tasks.json","（",[22,16857,16858],{},"pub.bat"," 打包时已包含），解压即可用。结构如下：",[50,16861,16864],{"className":16862,"code":16863,"language":358},[356],"your_project\u002F\n├── .vscode\u002F\n│   ├── launch.json      ← 调试器配置\n│   └── tasks.json       ← 编译任务配置\n├── samples\u002F\n│   └── menu_demo\u002F\n│       ├── menu_demo.c\n│       └── build_debug.bat   ← 调试专用编译脚本\n├── eduwin\u002F\n│   ├── include\u002Feduwin.h\n│   └── lib\u002Feduwin.lib\n",[22,16865,16863],{"__ignoreMap":55},[46,16867,16869],{"id":16868},"_3-使用步骤","3. 使用步骤",[18,16871,16872,16875],{},[69,16873,16874],{},"第一步","：在 VS Code 中打开项目根目录。",[18,16877,16878,16881,16882,16888,16889,1090],{},[69,16879,16880],{},"第二步","：在左侧文件浏览器中",[69,16883,16884,16885,12257],{},"点选你要调试的 ",[22,16886,16887],{},".c","（如 ",[22,16890,16891],{},"samples\\menu_demo\\menu_demo.c",[15,16893,16894],{},[18,16895,16896],{},"这一步很重要——调试器会根据当前选中的文件来确定编译哪个程序。",[18,16898,16899,16902,16903,16905,16906,16909],{},[69,16900,16901],{},"第三步","：按 ",[22,16904,16807],{},"（或点左侧 Run and Debug → 绿色播放按钮），VS Code 会自动调用 ",[22,16907,16908],{},"build_debug.bat"," 编译，然后启动调试。",[46,16911,16913],{"id":16912},"_4-build_debugbat-怎么用","4. build_debug.bat 怎么用",[18,16915,16916,16917,16920,16921,16923,16924,16927],{},"目前只有 ",[22,16918,16919],{},"samples\\menu_demo\\"," 下提供了 ",[22,16922,16908],{},"。如果你想给其他示例加调试支持，复制 ",[22,16925,16926],{},"samples\\menu_demo\\build_debug.bat"," 到对应示例目录，只改两行：",[50,16929,16931],{"className":7634,"code":16930,"language":7636,"meta":55,"style":55},"set SOURCE=你的源文件.c\nset PROGRAM=你的程序_debug.exe\n",[22,16932,16933,16938],{"__ignoreMap":55},[59,16934,16935],{"class":61,"line":62},[59,16936,16937],{},"set SOURCE=你的源文件.c\n",[59,16939,16940],{"class":61,"line":88},[59,16941,16942],{},"set PROGRAM=你的程序_debug.exe\n",[315,16944,16945,16951],{},[318,16946,16947,16950],{},[22,16948,16949],{},"SOURCE"," 改为你要调试的源文件名",[318,16952,16953,16956,16957,16960],{},[22,16954,16955],{},"PROGRAM"," 随意，加 ",[22,16958,16959],{},"_debug"," 后缀即可",[18,16962,16963,16964,16967],{},"其他内容不用动（",[22,16965,16966],{},"\u002FZi \u002FOd \u002FDEBUG"," 都是调试必需的编译选项）。",[46,16969,16971],{"id":16970},"_5-调试技巧","5. 调试技巧",[1020,16973,16974],{"id":16974},"设置断点",[18,16976,16977],{},"在 VS Code 中，点击代码行号左侧的空白处即可设置断点（红点）。建议在以下位置设断点：",[315,16979,16980,16986,16989],{},[318,16981,16982,16983,1494],{},"回调函数入口（如 ",[22,16984,16985],{},"on_click",[318,16987,16988],{},"事件处理逻辑的关键分支",[318,16990,16991],{},"需要观察变量的位置",[1020,16993,16994],{"id":16994},"调试面板",[18,16996,5245,16997,16999],{},[22,16998,16807],{}," 启动调试后，可以使用：",[4903,17001,17002,17013],{},[4906,17003,17004],{},[4909,17005,17006,17009,17011],{},[4912,17007,17008],{},"操作",[4912,17010,6683],{},[4912,17012,4917],{},[4919,17014,17015,17027,17040,17053,17066,17079],{},[4909,17016,17017,17020,17024],{},[4924,17018,17019],{},"继续",[4924,17021,17022],{},[22,17023,16807],{},[4924,17025,17026],{},"运行到下一个断点",[4909,17028,17029,17032,17037],{},[4924,17030,17031],{},"单步跳过",[4924,17033,17034],{},[22,17035,17036],{},"F10",[4924,17038,17039],{},"执行当前行，不进入函数",[4909,17041,17042,17045,17050],{},[4924,17043,17044],{},"单步进入",[4924,17046,17047],{},[22,17048,17049],{},"F11",[4924,17051,17052],{},"进入函数内部",[4909,17054,17055,17058,17063],{},[4924,17056,17057],{},"单步跳出",[4924,17059,17060],{},[22,17061,17062],{},"Shift+F11",[4924,17064,17065],{},"跳出当前函数",[4909,17067,17068,17071,17076],{},[4924,17069,17070],{},"重启",[4924,17072,17073],{},[22,17074,17075],{},"Ctrl+Shift+F5",[4924,17077,17078],{},"重新启动调试",[4909,17080,17081,17084,17089],{},[4924,17082,17083],{},"停止",[4924,17085,17086],{},[22,17087,17088],{},"Shift+F5",[4924,17090,17091],{},"停止调试",[1020,17093,17094],{"id":17094},"查看变量",[18,17096,17097],{},"调试时，将鼠标悬停在变量名上即可查看当前值。也可以在左侧「变量」面板中查看所有局部变量、监视表达式和调用堆栈。",[362,17099,364],{},{"title":55,"searchDepth":88,"depth":88,"links":17101},[17102,17103,17104,17105,17106],{"id":16813,"depth":88,"text":16814},{"id":16841,"depth":88,"text":16842},{"id":16868,"depth":88,"text":16869},{"id":16912,"depth":88,"text":16913},{"id":16970,"depth":88,"text":16971,"children":17107},[17108,17109,17110],{"id":16974,"depth":134,"text":16974},{"id":16994,"depth":134,"text":16994},{"id":17094,"depth":134,"text":17094},"eduwin 支持在 VS Code 中直接按 F5 进行断点调试。本教程将介绍如何配置和使用。",{},"\u002Fdocs\u002Ftutorial\u002Feduwin-debug",{"title":16796,"description":17111},"docs\u002Ftutorial\u002Feduwin-debug","5OK4182rhAOKOHaZXw39II5UR-ALqBPhkrkJEq3UUv8",{"id":17118,"title":17119,"body":17120,"description":18447,"extension":374,"meta":18448,"navigation":173,"path":18449,"seo":18450,"stem":18451,"__hash__":18452},"content\u002Fdocs\u002Ftutorial\u002Feduwin-game.md","eduwin 游戏开发指南",{"type":8,"value":17121,"toc":18434},[17122,17125,17132,17210,17231,17249,17255,17257,17261,17265,17277,17298,17340,17342,17354,17357,17405,17408,17516,17518,17538,17541,17547,17564,17568,17579,17727,17930,17933,17957,17963,17970,18013,18016,18024,18027,18055,18066,18068,18071,18091,18094,18177,18180,18183,18204,18220,18222,18242,18247,18252,18257,18267,18270,18285,18287,18290,18293,18421,18427,18432],[11,17123,17119],{"id":17124},"eduwin-游戏开发指南",[18,17126,17127,17128,17131],{},"eduwin 的画布组件（Canvas）不仅仅是绘图工具——它是一套完整的 ",[69,17129,17130],{},"2D 游戏引擎","，内置了游戏开发所需的一切核心机制：",[4903,17133,17134,17143],{},[4906,17135,17136],{},[4909,17137,17138,17141],{},[4912,17139,17140],{},"能力",[4912,17142,4917],{},[4919,17144,17145,17154,17163,17172,17181,17190,17200],{},[4909,17146,17147,17151],{},[4924,17148,17149],{},[69,17150,1022],{},[4924,17152,17153],{},"定时器驱动，帧率可设（30\u002F60fps 等）",[4909,17155,17156,17160],{},[4924,17157,17158],{},[69,17159,1057],{},[4924,17161,17162],{},"实时按键查询 + 按下\u002F释放事件回调",[4909,17164,17165,17169],{},[4924,17166,17167],{},[69,17168,1097],{},[4924,17170,17171],{},"实时坐标查询 + 移动\u002F按下\u002F释放回调",[4909,17173,17174,17178],{},[4924,17175,17176],{},[69,17177,1356],{},[4924,17179,17180],{},"图像\u002F纯色精灵，支持平移、缩放、旋转、克隆、碰撞检测",[4909,17182,17183,17187],{},[4924,17184,17185],{},[69,17186,1666],{},[4924,17188,17189],{},"一键注册精灵的 click、down、up、hover 回调，无需手动处理坐标",[4909,17191,17192,17197],{},[4924,17193,17194],{},[69,17195,17196],{},"音效",[4924,17198,17199],{},"WAV 播放、蜂鸣器",[4909,17201,17202,17207],{},[4924,17203,17204],{},[69,17205,17206],{},"双缓冲",[4924,17208,17209],{},"GDI+ 离屏位图，所有绘制先在位图上完成再一次性显示到屏幕，天然无闪烁",[18,17211,17212,17213,17215,17216,17218,17219,17222,17223,17226,17227,17230],{},"其中 ",[69,17214,17206],{}," 是一个很大的优势：eduwin 的 Canvas 内部使用 ",[22,17217,1362],{}," 作为离屏画布，所有 ",[22,17220,17221],{},"canvas_draw_*"," 操作都在位图上进行，仅在 ",[22,17224,17225],{},"InvalidateRect"," 时才触发 ",[22,17228,17229],{},"WM_PAINT"," 刷新屏幕——不会有传统的闪烁问题，也无需手动管理缓冲切换。",[315,17232,17233,17240,17246],{},[318,17234,17235,17236,17239],{},"打靶等低速游戏：",[69,17237,17238],{},"30fps"," 即可",[318,17241,17242,17243],{},"动作游戏、像素滚动：",[69,17244,17245],{},"60fps",[318,17247,17248],{},"不建议超过 60fps，Windows 定时器精度有限",[18,17250,17251,17254],{},[22,17252,17253],{},"samples\\shooting_demo\\shooting_demo.c"," 是一个完整的打靶游戏示例，编译运行就能玩。",[432,17256],{},[46,17258,17260],{"id":17259},"游戏开发核心-api","游戏开发核心 API",[1020,17262,17264],{"id":17263},"帧循环游戏主循环","帧循环（游戏主循环）",[50,17266,17267],{"className":52,"code":1025,"language":54,"meta":55,"style":55},[22,17268,17269,17273],{"__ignoreMap":55},[59,17270,17271],{"class":61,"line":62},[59,17272,1032],{},[59,17274,17275],{"class":61,"line":88},[59,17276,1037],{},[315,17278,17279,17290,17293],{},[318,17280,17281,17283,17284,17286,17287,17289],{},[22,17282,1042],{}," 启动一个定时器，每秒调用 ",[22,17285,1046],{}," 次 ",[22,17288,534],{},"。通常设为 60（60fps）",[318,17291,17292],{},"回调中执行游戏逻辑：更新状态 → 清理画布 → 重新绘制",[318,17294,17295,17297],{},[22,17296,1053],{}," 停止帧循环",[50,17299,17301],{"className":52,"code":17300,"language":54,"meta":55,"style":55},"void game_loop(void *data)\n{\n    \u002F\u002F 1. 处理输入\n    \u002F\u002F 2. 更新游戏状态\n    \u002F\u002F 3. 绘制画面\n}\n\ncanvas_start_timer(canvas, 60, game_loop, NULL);\n",[22,17302,17303,17308,17312,17317,17322,17327,17331,17335],{"__ignoreMap":55},[59,17304,17305],{"class":61,"line":62},[59,17306,17307],{},"void game_loop(void *data)\n",[59,17309,17310],{"class":61,"line":88},[59,17311,574],{},[59,17313,17314],{"class":61,"line":134},[59,17315,17316],{},"    \u002F\u002F 1. 处理输入\n",[59,17318,17319],{"class":61,"line":140},[59,17320,17321],{},"    \u002F\u002F 2. 更新游戏状态\n",[59,17323,17324],{"class":61,"line":146},[59,17325,17326],{},"    \u002F\u002F 3. 绘制画面\n",[59,17328,17329],{"class":61,"line":281},[59,17330,149],{},[59,17332,17333],{"class":61,"line":286},[59,17334,174],{"emptyLinePlaceholder":173},[59,17336,17337],{"class":61,"line":292},[59,17338,17339],{},"canvas_start_timer(canvas, 60, game_loop, NULL);\n",[1020,17341,1057],{"id":1057},[50,17343,17344],{"className":52,"code":1060,"language":54,"meta":55,"style":55},[22,17345,17346,17350],{"__ignoreMap":55},[59,17347,17348],{"class":61,"line":62},[59,17349,1067],{},[59,17351,17352],{"class":61,"line":88},[59,17353,1072],{},[18,17355,17356],{},"两种方式获取键盘状态：",[4903,17358,17359,17372],{},[4906,17360,17361],{},[4909,17362,17363,17366,17369],{},[4912,17364,17365],{},"方式",[4912,17367,17368],{},"适用场景",[4912,17370,17371],{},"用法",[4919,17373,17374,17390],{},[4909,17375,17376,17381,17384],{},[4924,17377,17378],{},[69,17379,17380],{},"实时查询",[4924,17382,17383],{},"WASD 移动、持续按住",[4924,17385,17386,17387],{},"每帧调用 ",[22,17388,17389],{},"canvas_is_key_down(canvas, 'W')",[4909,17391,17392,17397,17400],{},[4924,17393,17394],{},[69,17395,17396],{},"事件回调",[4924,17398,17399],{},"跳跃（按下一次）、菜单",[4924,17401,17402],{},[22,17403,17404],{},"canvas_on_key(canvas, on_down, NULL)",[18,17406,17407],{},"常用虚拟键码：",[4903,17409,17410,17423],{},[4906,17411,17412],{},[4909,17413,17414,17417,17420],{},[4912,17415,17416],{},"键",[4912,17418,17419],{},"常量",[4912,17421,17422],{},"数值",[4919,17424,17425,17437,17450,17463,17476,17489,17502],{},[4909,17426,17427,17430,17434],{},[4924,17428,17429],{},"方向键左",[4924,17431,17432],{},[22,17433,1085],{},[4924,17435,17436],{},"37",[4909,17438,17439,17442,17447],{},[4924,17440,17441],{},"方向键上",[4924,17443,17444],{},[22,17445,17446],{},"VK_UP",[4924,17448,17449],{},"38",[4909,17451,17452,17455,17460],{},[4924,17453,17454],{},"方向键右",[4924,17456,17457],{},[22,17458,17459],{},"VK_RIGHT",[4924,17461,17462],{},"39",[4909,17464,17465,17468,17473],{},[4924,17466,17467],{},"方向键下",[4924,17469,17470],{},[22,17471,17472],{},"VK_DOWN",[4924,17474,17475],{},"40",[4909,17477,17478,17481,17486],{},[4924,17479,17480],{},"空格",[4924,17482,17483],{},[22,17484,17485],{},"VK_SPACE",[4924,17487,17488],{},"32",[4909,17490,17491,17494,17499],{},[4924,17492,17493],{},"ESC",[4924,17495,17496],{},[22,17497,17498],{},"VK_ESCAPE",[4924,17500,17501],{},"27",[4909,17503,17504,17507,17513],{},[4924,17505,17506],{},"A-Z",[4924,17508,17509,1814,17511],{},[22,17510,1089],{},[22,17512,6704],{},[4924,17514,17515],{},"65—90",[1020,17517,1097],{"id":1097},[50,17519,17520],{"className":52,"code":1100,"language":54,"meta":55,"style":55},[22,17521,17522,17526,17530,17534],{"__ignoreMap":55},[59,17523,17524],{"class":61,"line":62},[59,17525,1107],{},[59,17527,17528],{"class":61,"line":88},[59,17529,1112],{},[59,17531,17532],{"class":61,"line":134},[59,17533,1117],{},[59,17535,17536],{"class":61,"line":140},[59,17537,1122],{},[18,17539,17540],{},"鼠标回调参数约定：",[50,17542,17545],{"className":17543,"code":17544,"language":358},[356],"void on_down(int button, int x, int y);\n",[22,17546,17544],{"__ignoreMap":55},[315,17548,17549,17555],{},[318,17550,17551,17554],{},[22,17552,17553],{},"button","：0=左键, 1=右键",[318,17556,17557,3980,17560,17563],{},[22,17558,17559],{},"x",[22,17561,17562],{},"y","：鼠标在画布客户区的坐标",[1020,17565,17567],{"id":17566},"精灵系统sprite","精灵系统（Sprite）",[18,17569,17570,17571,17574,17575,17578],{},"eduwin 提供了完整的精灵系统，支持",[69,17572,17573],{},"图像精灵","和",[69,17576,17577],{},"形状精灵","，所有精灵内部统一为位图处理。内置平移、缩放、旋转、克隆、碰撞检测：",[50,17580,17582],{"className":52,"code":17581,"language":54,"meta":55,"style":55},"typedef struct Sprite Sprite;\n\n\u002F* 形状类型 *\u002F\n#define SHAPE_RECT      0\n#define SHAPE_ELLIPSE   1\n#define SHAPE_TRIANGLE  2\n#define SHAPE_DIAMOND   3\n#define SHAPE_PENTAGON  4\n#define SHAPE_HEXAGON   5\n#define SHAPE_STAR      6\n#define SHAPE_ARROW     7\n#define SHAPE_PIE       8\n\nSprite *sprite_create_from_shape(int type, int w, int h);\nSprite *sprite_create_from_file(const WCHAR *filepath);\nSprite *sprite_create_from_image(void *image);\nvoid sprite_destroy(Sprite *s);\nvoid sprite_set_pos(Sprite *s, float x, float y);\nvoid sprite_move(Sprite *s, float dx, float dy);\nvoid sprite_set_scale(Sprite *s, float sx, float sy);\nvoid sprite_set_rotation(Sprite *s, float degrees);\nvoid sprite_set_color(Sprite *s, int r, int g, int b, int a);\nvoid sprite_set_image(Sprite *s, void *image);\nfloat sprite_get_x(Sprite *s);\nfloat sprite_get_y(Sprite *s);\nfloat sprite_get_w(Sprite *s);\nfloat sprite_get_h(Sprite *s);\nfloat sprite_get_sx(Sprite *s);\nfloat sprite_get_sy(Sprite *s);\nSprite *sprite_clone(Sprite *s);\nvoid sprite_draw(HWND canvas, Sprite *s);\nint sprite_collide(Sprite *a, Sprite *b);\nint sprite_hit_test(Sprite *s, float px, float py);\n",[22,17583,17584,17589,17593,17598,17603,17608,17613,17618,17623,17628,17633,17638,17643,17647,17651,17655,17659,17663,17667,17671,17675,17679,17683,17687,17691,17695,17699,17703,17707,17711,17715,17719,17723],{"__ignoreMap":55},[59,17585,17586],{"class":61,"line":62},[59,17587,17588],{},"typedef struct Sprite Sprite;\n",[59,17590,17591],{"class":61,"line":88},[59,17592,174],{"emptyLinePlaceholder":173},[59,17594,17595],{"class":61,"line":134},[59,17596,17597],{},"\u002F* 形状类型 *\u002F\n",[59,17599,17600],{"class":61,"line":140},[59,17601,17602],{},"#define SHAPE_RECT      0\n",[59,17604,17605],{"class":61,"line":146},[59,17606,17607],{},"#define SHAPE_ELLIPSE   1\n",[59,17609,17610],{"class":61,"line":281},[59,17611,17612],{},"#define SHAPE_TRIANGLE  2\n",[59,17614,17615],{"class":61,"line":286},[59,17616,17617],{},"#define SHAPE_DIAMOND   3\n",[59,17619,17620],{"class":61,"line":292},[59,17621,17622],{},"#define SHAPE_PENTAGON  4\n",[59,17624,17625],{"class":61,"line":600},[59,17626,17627],{},"#define SHAPE_HEXAGON   5\n",[59,17629,17630],{"class":61,"line":917},[59,17631,17632],{},"#define SHAPE_STAR      6\n",[59,17634,17635],{"class":61,"line":922},[59,17636,17637],{},"#define SHAPE_ARROW     7\n",[59,17639,17640],{"class":61,"line":928},[59,17641,17642],{},"#define SHAPE_PIE       8\n",[59,17644,17645],{"class":61,"line":933},[59,17646,174],{"emptyLinePlaceholder":173},[59,17648,17649],{"class":61,"line":939},[59,17650,1429],{},[59,17652,17653],{"class":61,"line":1320},[59,17654,1434],{},[59,17656,17657],{"class":61,"line":1326},[59,17658,1439],{},[59,17660,17661],{"class":61,"line":1331},[59,17662,1449],{},[59,17664,17665],{"class":61,"line":1336},[59,17666,1513],{},[59,17668,17669],{"class":61,"line":1342},[59,17670,1518],{},[59,17672,17673],{"class":61,"line":1348},[59,17674,1523],{},[59,17676,17677],{"class":61,"line":5377},[59,17678,1528],{},[59,17680,17681],{"class":61,"line":7589},[59,17682,1533],{},[59,17684,17685],{"class":61,"line":7595},[59,17686,1538],{},[59,17688,17689],{"class":61,"line":7600},[59,17690,1593],{},[59,17692,17693],{"class":61,"line":7605},[59,17694,1598],{},[59,17696,17697],{"class":61,"line":15131},[59,17698,1603],{},[59,17700,17701],{"class":61,"line":15136},[59,17702,1608],{},[59,17704,17705],{"class":61,"line":15142},[59,17706,1613],{},[59,17708,17709],{"class":61,"line":15148},[59,17710,1618],{},[59,17712,17713],{"class":61,"line":15154},[59,17714,1444],{},[59,17716,17717],{"class":61,"line":15160},[59,17718,1631],{},[59,17720,17721],{"class":61,"line":15165},[59,17722,1636],{},[59,17724,17725],{"class":61,"line":15171},[59,17726,1641],{},[4903,17728,17729,17737],{},[4906,17730,17731],{},[4909,17732,17733,17735],{},[4912,17734,6071],{},[4912,17736,4917],{},[4919,17738,17739,17748,17758,17771,17781,17791,17800,17810,17820,17830,17840,17850,17860,17870,17880,17890,17900,17910,17920],{},[4909,17740,17741,17745],{},[4924,17742,17743],{},[22,17744,1456],{},[4924,17746,17747],{},"创建形状精灵（矩形\u002F椭圆\u002F三角等），自动渲染为位图",[4909,17749,17750,17755],{},[4924,17751,17752],{},[22,17753,17754],{},"sprite_create_from_file(filepath)",[4924,17756,17757],{},"从图片文件创建精灵，尺寸自动匹配",[4909,17759,17760,17765],{},[4924,17761,17762],{},[22,17763,17764],{},"sprite_create_from_image(image)",[4924,17766,17767,17768,17770],{},"从 ",[22,17769,866],{}," 返回的句柄创建精灵（内部克隆副本）",[4909,17772,17773,17778],{},[4924,17774,17775],{},[22,17776,17777],{},"sprite_destroy(s)",[4924,17779,17780],{},"销毁精灵，引用计数归零时释放资源",[4909,17782,17783,17788],{},[4924,17784,17785],{},[22,17786,17787],{},"sprite_set_pos(s, x, y)",[4924,17789,17790],{},"设置位置（左上角）",[4909,17792,17793,17798],{},[4924,17794,17795],{},[22,17796,17797],{},"sprite_move(s, dx, dy)",[4924,17799,16068],{},[4909,17801,17802,17807],{},[4924,17803,17804],{},[22,17805,17806],{},"sprite_set_scale(s, sx, sy)",[4924,17808,17809],{},"缩放因子（1.0=原大，2.0=放大一倍）",[4909,17811,17812,17817],{},[4924,17813,17814],{},[22,17815,17816],{},"sprite_set_rotation(s, deg)",[4924,17818,17819],{},"设置旋转角度（度），绕原点（默认中心）旋转",[4909,17821,17822,17827],{},[4924,17823,17824],{},[22,17825,17826],{},"sprite_set_color(s, r, g, b, a)",[4924,17828,17829],{},"正片叠底混色，透明像素不受影响，多次调用不累积",[4909,17831,17832,17837],{},[4924,17833,17834],{},[22,17835,17836],{},"sprite_set_image(s, image)",[4924,17838,17839],{},"替换精灵图像，内部克隆为新副本",[4909,17841,17842,17847],{},[4924,17843,17844],{},[22,17845,17846],{},"sprite_get_x\u002Fw\u002Fh\u002F...",[4924,17848,17849],{},"读取精灵坐标、尺寸、缩放值",[4909,17851,17852,17857],{},[4924,17853,17854],{},[22,17855,17856],{},"sprite_clone(s)",[4924,17858,17859],{},"克隆精灵（从原始图克隆独立工作副本）",[4909,17861,17862,17867],{},[4924,17863,17864],{},[22,17865,17866],{},"sprite_draw(canvas, s)",[4924,17868,17869],{},"在画布上绘制精灵，自动处理旋转\u002F缩放\u002F位置",[4909,17871,17872,17877],{},[4924,17873,17874],{},[22,17875,17876],{},"sprite_collide(a, b)",[4924,17878,17879],{},"AABB 碰撞检测，返回 1 表示碰撞",[4909,17881,17882,17887],{},[4924,17883,17884],{},[22,17885,17886],{},"sprite_hit_test(s, px, py)",[4924,17888,17889],{},"检测点是否在精灵范围内（含缩放）",[4909,17891,17892,17897],{},[4924,17893,17894],{},[22,17895,17896],{},"on_sprite_click(canvas, s, cb, d)",[4924,17898,17899],{},"注册精灵点击事件回调",[4909,17901,17902,17907],{},[4924,17903,17904],{},[22,17905,17906],{},"on_sprite_down(canvas, s, cb, d)",[4924,17908,17909],{},"注册精灵按下事件回调",[4909,17911,17912,17917],{},[4924,17913,17914],{},[22,17915,17916],{},"on_sprite_up(canvas, s, cb, d)",[4924,17918,17919],{},"注册精灵释放事件回调",[4909,17921,17922,17927],{},[4924,17923,17924],{},[22,17925,17926],{},"on_sprite_hover(canvas, s, cb, d)",[4924,17928,17929],{},"注册精灵悬停事件回调",[18,17931,17932],{},"Sprite 结构体字段可以直接读写：",[50,17934,17936],{"className":52,"code":17935,"language":54,"meta":55,"style":55},"sprite->x = 100;       \u002F\u002F 直接设坐标\nsprite->visible = 0;   \u002F\u002F 隐藏\nsprite->ox = 0;        \u002F\u002F 原点移到左上角\nsprite->oy = 0;\n",[22,17937,17938,17943,17948,17953],{"__ignoreMap":55},[59,17939,17940],{"class":61,"line":62},[59,17941,17942],{},"sprite->x = 100;       \u002F\u002F 直接设坐标\n",[59,17944,17945],{"class":61,"line":88},[59,17946,17947],{},"sprite->visible = 0;   \u002F\u002F 隐藏\n",[59,17949,17950],{"class":61,"line":134},[59,17951,17952],{},"sprite->ox = 0;        \u002F\u002F 原点移到左上角\n",[59,17954,17955],{"class":61,"line":140},[59,17956,1789],{},[18,17958,17959,17960,17962],{},"Sprite 内部引用计数机制：克隆时共享同一份 ",[22,17961,5419],{},"，最后一个精灵销毁时才释放图像内存。",[18,17964,17965,14077,17967,17969],{},[22,17966,866],{},[22,17968,1476],{}," 可以实现图像复用：",[50,17971,17973],{"className":52,"code":17972,"language":54,"meta":55,"style":55},"void *img = load_image_from_file(L\"player.png\", NULL, NULL);  \u002F\u002F 加载一次\n\nSprite *player1 = sprite_create_from_image(img);  \u002F\u002F 创建两个精灵共享同一张图\nSprite *player2 = sprite_create_from_image(img);\nsprite_set_pos(player1, 50, 50);\nsprite_set_pos(player2, 200, 50);\n\n\u002F\u002F 每个精灵独立销毁，但图像在最后一个销毁时才释放\n",[22,17974,17975,17980,17984,17989,17994,17999,18004,18008],{"__ignoreMap":55},[59,17976,17977],{"class":61,"line":62},[59,17978,17979],{},"void *img = load_image_from_file(L\"player.png\", NULL, NULL);  \u002F\u002F 加载一次\n",[59,17981,17982],{"class":61,"line":88},[59,17983,174],{"emptyLinePlaceholder":173},[59,17985,17986],{"class":61,"line":134},[59,17987,17988],{},"Sprite *player1 = sprite_create_from_image(img);  \u002F\u002F 创建两个精灵共享同一张图\n",[59,17990,17991],{"class":61,"line":140},[59,17992,17993],{},"Sprite *player2 = sprite_create_from_image(img);\n",[59,17995,17996],{"class":61,"line":146},[59,17997,17998],{},"sprite_set_pos(player1, 50, 50);\n",[59,18000,18001],{"class":61,"line":281},[59,18002,18003],{},"sprite_set_pos(player2, 200, 50);\n",[59,18005,18006],{"class":61,"line":286},[59,18007,174],{"emptyLinePlaceholder":173},[59,18009,18010],{"class":61,"line":292},[59,18011,18012],{},"\u002F\u002F 每个精灵独立销毁，但图像在最后一个销毁时才释放\n",[1020,18014,18015],{"id":18015},"碰撞检测",[50,18017,18018],{"className":52,"code":1636,"language":54,"meta":55,"style":55},[22,18019,18020],{"__ignoreMap":55},[59,18021,18022],{"class":61,"line":62},[59,18023,1636],{},[18,18025,18026],{},"AABB 轴对齐矩形碰撞检测，自动考虑缩放：",[50,18028,18030],{"className":52,"code":18029,"language":54,"meta":55,"style":55},"if (sprite_collide(player, enemy))\n{\n    player->visible = 0;          \u002F\u002F 玩家消失\n    sprite_set_pos(player, 0, 0); \u002F\u002F 重置位置\n}\n",[22,18031,18032,18037,18041,18046,18051],{"__ignoreMap":55},[59,18033,18034],{"class":61,"line":62},[59,18035,18036],{},"if (sprite_collide(player, enemy))\n",[59,18038,18039],{"class":61,"line":88},[59,18040,574],{},[59,18042,18043],{"class":61,"line":134},[59,18044,18045],{},"    player->visible = 0;          \u002F\u002F 玩家消失\n",[59,18047,18048],{"class":61,"line":140},[59,18049,18050],{},"    sprite_set_pos(player, 0, 0); \u002F\u002F 重置位置\n",[59,18052,18053],{"class":61,"line":146},[59,18054,149],{},[18,18056,18057,18058,18061,18062,18065],{},"在游戏中，精灵本身就充当按钮——用 ",[22,18059,18060],{},"on_sprite_click"," 处理点击，用 ",[22,18063,18064],{},"on_sprite_hover"," 做悬停反馈，完全不需要额外的按钮控件。详见下文「精灵事件（推荐）」。",[1020,18067,1666],{"id":1666},[18,18069,18070],{},"更简洁的方式是直接注册精灵事件回调，无需手动处理鼠标坐标：",[50,18072,18073],{"className":52,"code":1669,"language":54,"meta":55,"style":55},[22,18074,18075,18079,18083,18087],{"__ignoreMap":55},[59,18076,18077],{"class":61,"line":62},[59,18078,1676],{},[59,18080,18081],{"class":61,"line":88},[59,18082,1681],{},[59,18084,18085],{"class":61,"line":134},[59,18086,1686],{},[59,18088,18089],{"class":61,"line":140},[59,18090,1691],{},[18,18092,18093],{},"注册后，框架自动检测精灵上的鼠标事件并调用回调：",[50,18095,18097],{"className":52,"code":18096,"language":54,"meta":55,"style":55},"Sprite *btn = sprite_create_from_shape(SHAPE_RECT, 120, 40);\nsprite_set_pos(btn, 100, 100);\nsprite_set_color(btn, 80, 160, 240, 255);\n\nvoid on_btn_click(Sprite *s, void *data)\n{\n    sprite_set_color(s, 0, 200, 80, 255);    \u002F\u002F 点击变绿\n    play_beep(1000, 100);\n}\n\nvoid on_btn_hover(Sprite *s, void *data)\n{\n    sprite_set_color(s, 120, 200, 255, 255);  \u002F\u002F 悬停高亮\n}\n\non_sprite_click(canvas, btn, on_btn_click, NULL);\non_sprite_hover(canvas, btn, on_btn_hover, NULL);\n",[22,18098,18099,18104,18109,18114,18118,18123,18127,18132,18137,18141,18145,18150,18154,18159,18163,18167,18172],{"__ignoreMap":55},[59,18100,18101],{"class":61,"line":62},[59,18102,18103],{},"Sprite *btn = sprite_create_from_shape(SHAPE_RECT, 120, 40);\n",[59,18105,18106],{"class":61,"line":88},[59,18107,18108],{},"sprite_set_pos(btn, 100, 100);\n",[59,18110,18111],{"class":61,"line":134},[59,18112,18113],{},"sprite_set_color(btn, 80, 160, 240, 255);\n",[59,18115,18116],{"class":61,"line":140},[59,18117,174],{"emptyLinePlaceholder":173},[59,18119,18120],{"class":61,"line":146},[59,18121,18122],{},"void on_btn_click(Sprite *s, void *data)\n",[59,18124,18125],{"class":61,"line":281},[59,18126,574],{},[59,18128,18129],{"class":61,"line":286},[59,18130,18131],{},"    sprite_set_color(s, 0, 200, 80, 255);    \u002F\u002F 点击变绿\n",[59,18133,18134],{"class":61,"line":292},[59,18135,18136],{},"    play_beep(1000, 100);\n",[59,18138,18139],{"class":61,"line":600},[59,18140,149],{},[59,18142,18143],{"class":61,"line":917},[59,18144,174],{"emptyLinePlaceholder":173},[59,18146,18147],{"class":61,"line":922},[59,18148,18149],{},"void on_btn_hover(Sprite *s, void *data)\n",[59,18151,18152],{"class":61,"line":928},[59,18153,574],{},[59,18155,18156],{"class":61,"line":933},[59,18157,18158],{},"    sprite_set_color(s, 120, 200, 255, 255);  \u002F\u002F 悬停高亮\n",[59,18160,18161],{"class":61,"line":939},[59,18162,149],{},[59,18164,18165],{"class":61,"line":1320},[59,18166,174],{"emptyLinePlaceholder":173},[59,18168,18169],{"class":61,"line":1326},[59,18170,18171],{},"on_sprite_click(canvas, btn, on_btn_click, NULL);\n",[59,18173,18174],{"class":61,"line":1331},[59,18175,18176],{},"on_sprite_hover(canvas, btn, on_btn_hover, NULL);\n",[18,18178,18179],{},"事件生命周期自动管理：销毁精灵时会自动清理注册的事件。同一个精灵可以同时注册 click、down、up、hover 中的任意组合。",[1020,18181,18182],{"id":18182},"辅助工具",[50,18184,18186],{"className":52,"code":18185,"language":54,"meta":55,"style":55},"int canvas_get_width(HWND canvas);\nint canvas_get_height(HWND canvas);\nvoid canvas_clear_rect(HWND canvas, int x, int y, int w, int h, int r, int g, int b, int a);\nint rand_int(int min, int max);\n",[22,18187,18188,18192,18196,18200],{"__ignoreMap":55},[59,18189,18190],{"class":61,"line":62},[59,18191,1235],{},[59,18193,18194],{"class":61,"line":88},[59,18195,1240],{},[59,18197,18198],{"class":61,"line":134},[59,18199,950],{},[59,18201,18202],{"class":61,"line":140},[59,18203,8254],{},[315,18205,18206,18211],{},[318,18207,18208,18210],{},[22,18209,14909],{}," 局部清除并填充颜色，适合需要保留背景的游戏场景",[318,18212,18213,18216,18217,18219],{},[22,18214,18215],{},"rand_int"," 生成 ",[22,18218,8266],{}," 闭区间随机整数",[1020,18221,17196],{"id":17196},[50,18223,18224],{"className":52,"code":7681,"language":54,"meta":55,"style":55},[22,18225,18226,18230,18234,18238],{"__ignoreMap":55},[59,18227,18228],{"class":61,"line":62},[59,18229,7688],{},[59,18231,18232],{"class":61,"line":88},[59,18233,7693],{},[59,18235,18236],{"class":61,"line":134},[59,18237,7698],{},[59,18239,18240],{"class":61,"line":140},[59,18241,7703],{},[18,18243,18244,18246],{},[22,18245,7710],{}," 播放音频文件（异步，不阻塞游戏循环），成功返回 1，失败返回 0。",[18,18248,18249,18251],{},[22,18250,7716],{}," 循环播放音频文件。",[18,18253,18254,18256],{},[22,18255,7722],{}," 停止当前正在播放的音频。",[18,18258,18259,18261,18262,7733,18264,18266],{},[22,18260,7728],{}," 通过 PC 喇叭发出指定频率和时长的蜂鸣声。",[22,18263,7732],{},[22,18265,7736],{}," 为时长（毫秒）。成功返回 1。",[18,18268,18269],{},"适合游戏的用法：",[50,18271,18273],{"className":52,"code":18272,"language":54,"meta":55,"style":55},"play_sound(L\"hit.wav\");           \u002F\u002F 击中音效（异步，不卡帧）\nplay_beep(1200, 80);               \u002F\u002F 高音短促蜂鸣代替\n",[22,18274,18275,18280],{"__ignoreMap":55},[59,18276,18277],{"class":61,"line":62},[59,18278,18279],{},"play_sound(L\"hit.wav\");           \u002F\u002F 击中音效（异步，不卡帧）\n",[59,18281,18282],{"class":61,"line":88},[59,18283,18284],{},"play_beep(1200, 80);               \u002F\u002F 高音短促蜂鸣代替\n",[432,18286],{},[46,18288,18289],{"id":18289},"游戏模板",[18,18291,18292],{},"创建一个新游戏的最简模板：",[50,18294,18296],{"className":52,"code":18295,"language":54,"meta":55,"style":55},"#include \"eduwin.h\"\n\nstatic HWND canvas;\n\nstatic void game_tick(void *data)\n{\n    int cw = canvas_get_width(canvas);\n    int ch = canvas_get_height(canvas);\n\n    canvas_clear(canvas, 0, 0, 0);          \u002F\u002F 黑色背景\n\n    \u002F* 游戏逻辑写在这里 *\u002F\n    canvas_draw_text(canvas, L\"Hello Game!\", 10, 10, 255, 255, 255, 16);\n}\n\nint main(void)\n{\n    init_app();\n    HWND win = create_window(L\"我的游戏\", 800, 600);\n    canvas = create_canvas(win, 0, 0, 800, 600);\n\n    canvas_start_timer(canvas, 60, game_tick, NULL);\n    canvas_on_key(canvas, NULL, NULL);       \u002F\u002F 让画布获得键盘焦点\n\n    show_window(win);\n    run_app();\n    return 0;\n}\n",[22,18297,18298,18302,18306,18311,18315,18320,18324,18329,18334,18338,18343,18347,18352,18357,18361,18365,18369,18373,18377,18382,18387,18391,18396,18401,18405,18409,18413,18417],{"__ignoreMap":55},[59,18299,18300],{"class":61,"line":62},[59,18301,5287],{},[59,18303,18304],{"class":61,"line":88},[59,18305,174],{"emptyLinePlaceholder":173},[59,18307,18308],{"class":61,"line":134},[59,18309,18310],{},"static HWND canvas;\n",[59,18312,18313],{"class":61,"line":140},[59,18314,174],{"emptyLinePlaceholder":173},[59,18316,18317],{"class":61,"line":146},[59,18318,18319],{},"static void game_tick(void *data)\n",[59,18321,18322],{"class":61,"line":281},[59,18323,574],{},[59,18325,18326],{"class":61,"line":286},[59,18327,18328],{},"    int cw = canvas_get_width(canvas);\n",[59,18330,18331],{"class":61,"line":292},[59,18332,18333],{},"    int ch = canvas_get_height(canvas);\n",[59,18335,18336],{"class":61,"line":600},[59,18337,174],{"emptyLinePlaceholder":173},[59,18339,18340],{"class":61,"line":917},[59,18341,18342],{},"    canvas_clear(canvas, 0, 0, 0);          \u002F\u002F 黑色背景\n",[59,18344,18345],{"class":61,"line":922},[59,18346,174],{"emptyLinePlaceholder":173},[59,18348,18349],{"class":61,"line":928},[59,18350,18351],{},"    \u002F* 游戏逻辑写在这里 *\u002F\n",[59,18353,18354],{"class":61,"line":933},[59,18355,18356],{},"    canvas_draw_text(canvas, L\"Hello Game!\", 10, 10, 255, 255, 255, 16);\n",[59,18358,18359],{"class":61,"line":939},[59,18360,149],{},[59,18362,18363],{"class":61,"line":1320},[59,18364,174],{"emptyLinePlaceholder":173},[59,18366,18367],{"class":61,"line":1326},[59,18368,5296],{},[59,18370,18371],{"class":61,"line":1331},[59,18372,574],{},[59,18374,18375],{"class":61,"line":1336},[59,18376,9768],{},[59,18378,18379],{"class":61,"line":1342},[59,18380,18381],{},"    HWND win = create_window(L\"我的游戏\", 800, 600);\n",[59,18383,18384],{"class":61,"line":1348},[59,18385,18386],{},"    canvas = create_canvas(win, 0, 0, 800, 600);\n",[59,18388,18389],{"class":61,"line":5377},[59,18390,174],{"emptyLinePlaceholder":173},[59,18392,18393],{"class":61,"line":7589},[59,18394,18395],{},"    canvas_start_timer(canvas, 60, game_tick, NULL);\n",[59,18397,18398],{"class":61,"line":7595},[59,18399,18400],{},"    canvas_on_key(canvas, NULL, NULL);       \u002F\u002F 让画布获得键盘焦点\n",[59,18402,18403],{"class":61,"line":7600},[59,18404,174],{"emptyLinePlaceholder":173},[59,18406,18407],{"class":61,"line":7605},[59,18408,5365],{},[59,18410,18411],{"class":61,"line":15131},[59,18412,5370],{},[59,18414,18415],{"class":61,"line":15136},[59,18416,2087],{},[59,18418,18419],{"class":61,"line":15142},[59,18420,149],{},[18,18422,18423,18424,6535],{},"编译命令（参考 ",[22,18425,18426],{},"samples\\shooting_demo\\build.bat",[50,18428,18430],{"className":18429,"code":13219,"language":358},[356],[22,18431,13219],{"__ignoreMap":55},[362,18433,364],{},{"title":55,"searchDepth":88,"depth":88,"links":18435},[18436,18446],{"id":17259,"depth":88,"text":17260,"children":18437},[18438,18439,18440,18441,18442,18443,18444,18445],{"id":17263,"depth":134,"text":17264},{"id":1057,"depth":134,"text":1057},{"id":1097,"depth":134,"text":1097},{"id":17566,"depth":134,"text":17567},{"id":18015,"depth":134,"text":18015},{"id":1666,"depth":134,"text":1666},{"id":18182,"depth":134,"text":18182},{"id":17196,"depth":134,"text":17196},{"id":18289,"depth":88,"text":18289},"eduwin 的画布组件（Canvas）不仅仅是绘图工具——它是一套完整的 2D 游戏引擎，内置了游戏开发所需的一切核心机制：",{},"\u002Fdocs\u002Ftutorial\u002Feduwin-game",{"title":17119,"description":18447},"docs\u002Ftutorial\u002Feduwin-game","NconF5ePeV29pmS1GqvB3ss7fbPItP7hXINsEVTp3dM",{"id":18454,"title":18455,"body":18456,"description":18463,"extension":374,"meta":19351,"navigation":173,"path":19352,"seo":19353,"stem":19354,"__hash__":19355},"content\u002Fdocs\u002Ftutorial\u002Feduwin-hello.md","Hello eduwin —— 第一个 GUI 程序",{"type":8,"value":18457,"toc":19331},[18458,18461,18464,18466,18470,18474,18477,18493,18502,18508,18521,18525,18539,18543,18546,18550,18560,18584,18588,18591,18597,18600,18604,18611,18809,18813,18823,18827,18832,18841,18848,18912,18925,18929,18936,18940,18955,18974,18978,18983,18998,19010,19035,19060,19064,19087,19102,19114,19118,19124,19130,19153,19167,19173,19177,19186,19192,19198,19202,19205,19216,19219,19223,19316,19318,19329],[11,18459,18455],{"id":18460},"hello-eduwin-第一个-gui-程序",[18,18462,18463],{},"本教程将带你从零开始，编写并运行第一个 eduwin 程序。",[432,18465],{},[46,18467,18469],{"id":18468},"_1-准备工作","1. 准备工作",[1020,18471,18473],{"id":18472},"_11-下载与解压","1.1 下载与解压",[18,18475,18476],{},"eduwin 只需两个条件：",[315,18478,18479,18485],{},[318,18480,18481,18484],{},[69,18482,18483],{},"Windows 7\u002F8\u002F10\u002F11"," 操作系统",[318,18486,18487,18490,18491,1494],{},[69,18488,18489],{},"MSVC 编译器","（Visual Studio 2022 Build Tools，安装教程见",[745,18492,16838],{"href":16837},[18,18494,18495,18496,18498,18499,6535],{},"从下载页面获取 ",[22,18497,9659],{},"，解压后目录结构如下（",[69,18500,18501],{},"请先看清这个结构，后面所有路径都与此相关",[50,18503,18506],{"className":18504,"code":18505,"language":358},[356],"eduwin_pub\u002F                  ← 下载包根目录\n├── eduwin\u002F                  ← eduwin 库文件\n│   ├── include\u002F             #   头文件（eduwin.h 在这里）\n│   ├── lib\u002F                 #   静态库（eduwin.lib 在这里）\n│   └── eduwin.manifest      #   Windows 视觉样式清单文件\n├── samples\u002F                 ← 示例项目\n│   ├── hello_demo\u002F          #   本教程对应的示例\n│   ├── sqlite_demo\u002F\n│   └── ...\n└── ...\n",[22,18507,18505],{"__ignoreMap":55},[15,18509,18510],{},[18,18511,18512,18513,18516,18517,18520],{},"本文中，我们假设 ",[22,18514,18515],{},"eduwin_pub\u002F"," 解压到了 ",[22,18518,18519],{},"D:\\XTU\\c-train\\eduwin_pub\\","，后文以此为例。",[1020,18522,18524],{"id":18523},"_12-关于编译器","1.2 关于编译器",[18,18526,18527,18528,18531,18532,18535,18536],{},"eduwin ",[69,18529,18530],{},"仅官方支持 MSVC","（Visual Studio 编译器）。MinGW（GCC 的 Windows 移植版）理论上可以编译 C 代码，但因为 eduwin 使用了 MSVC 特有的资源编译（",[22,18533,18534],{},".rc"," 文件）和链接器配置，用 MinGW 需要额外写大量的兼容脚本，非常繁琐，不推荐。",[69,18537,18538],{},"请直接使用 MSVC。",[46,18540,18542],{"id":18541},"_2-创建项目","2. 创建项目",[18,18544,18545],{},"有两种方式，推荐初学者使用方式一。",[1020,18547,18549],{"id":18548},"方式一直接复制-samples推荐","方式一：直接复制 samples（推荐）",[18,18551,18552,18555,18556,18559],{},[22,18553,18554],{},"samples\u002F"," 目录下有多个可直接编译运行的示例项目。以 ",[22,18557,18558],{},"hello_demo"," 为例：",[5250,18561,18562,18572,18578],{},[318,18563,18564,18565,624,18568,18571],{},"把 ",[22,18566,18567],{},"samples\u002Fhello_demo\u002F",[69,18569,18570],{},"整个文件夹","复制出来",[318,18573,18574,18575,1494],{},"放到你的工作目录，可重命名为你的项目名（比如 ",[22,18576,18577],{},"my_project\u002F",[318,18579,18580,18581,18583],{},"修改里面的 ",[22,18582,16887],{}," 文件开始编程",[1020,18585,18587],{"id":18586},"方式二从零搭建","方式二：从零搭建",[18,18589,18590],{},"你也可以在工作目录下手动创建项目文件：",[50,18592,18595],{"className":18593,"code":18594,"language":358},[356],"my_project\u002F\n├── app.ico           # 程序图标（可以从 samples 复制）\n├── app.rc            # 资源文件（从 samples 复制并修改）\n├── build.bat         # 编译脚本（从 samples 复制并修改）\n├── hello.c           # 我们的代码\n",[22,18596,18594],{"__ignoreMap":55},[18,18598,18599],{},"接下来的讲解以方式二为例（两种方式的原理完全一样，方式一只是省去了文件复制步骤）。",[46,18601,18603],{"id":18602},"_3-编写代码","3. 编写代码",[18,18605,18606,18607,18610],{},"创建 ",[22,18608,18609],{},"hello.c","，写入以下内容：",[50,18612,18614],{"className":52,"code":18613,"language":54,"meta":55,"style":55},"#include \"eduwin.h\"\n\nstatic HWND text;   \u002F* 文本框 *\u002F\nstatic HWND label;  \u002F* 标签 *\u002F\n\n\u002F* 点击按钮时，把文本框内容显示到标签上 *\u002F\nstatic void on_click(void *data)\n{\n    const WCHAR *s = get_text(text);\n    set_text(label, s);\n    set_text(text, L\"\");\n}\n\nint main(void)\n{\n    if (init_app() != 0)\n    {\n        alert(L\"错误\", L\"eduwin 初始化失败\");\n        return 1;\n    }\n\n    HWND win = create_window(L\"Hello eduwin\", 400, 200);\n    if (!win)\n    {\n        alert(L\"错误\", L\"创建窗口失败\");\n        return 1;\n    }\n\n    \u002F* 文本框 *\u002F\n    text = create_text(win, L\"请输入文字\", 20, 20, 250, 24);\n\n    \u002F* 按钮 *\u002F\n    HWND btn = create_button(win, L\"显示\", 280, 20, 80, 24);\n\n    \u002F* 标签（用来显示输入的文字） *\u002F\n    label = create_label(win, L\"\", 20, 60, 340, 80);\n\n    on_button_click(btn, on_click, NULL);\n\n    show_window(win);\n    run_app();\n    return 0;\n}\n",[22,18615,18616,18620,18624,18629,18634,18638,18643,18648,18652,18657,18662,18667,18671,18675,18679,18683,18688,18693,18698,18703,18707,18711,18716,18721,18725,18730,18734,18738,18742,18747,18752,18756,18761,18766,18770,18775,18780,18784,18789,18793,18797,18801,18805],{"__ignoreMap":55},[59,18617,18618],{"class":61,"line":62},[59,18619,5287],{},[59,18621,18622],{"class":61,"line":88},[59,18623,174],{"emptyLinePlaceholder":173},[59,18625,18626],{"class":61,"line":134},[59,18627,18628],{},"static HWND text;   \u002F* 文本框 *\u002F\n",[59,18630,18631],{"class":61,"line":140},[59,18632,18633],{},"static HWND label;  \u002F* 标签 *\u002F\n",[59,18635,18636],{"class":61,"line":146},[59,18637,174],{"emptyLinePlaceholder":173},[59,18639,18640],{"class":61,"line":281},[59,18641,18642],{},"\u002F* 点击按钮时，把文本框内容显示到标签上 *\u002F\n",[59,18644,18645],{"class":61,"line":286},[59,18646,18647],{},"static void on_click(void *data)\n",[59,18649,18650],{"class":61,"line":292},[59,18651,574],{},[59,18653,18654],{"class":61,"line":600},[59,18655,18656],{},"    const WCHAR *s = get_text(text);\n",[59,18658,18659],{"class":61,"line":917},[59,18660,18661],{},"    set_text(label, s);\n",[59,18663,18664],{"class":61,"line":922},[59,18665,18666],{},"    set_text(text, L\"\");\n",[59,18668,18669],{"class":61,"line":928},[59,18670,149],{},[59,18672,18673],{"class":61,"line":933},[59,18674,174],{"emptyLinePlaceholder":173},[59,18676,18677],{"class":61,"line":939},[59,18678,5296],{},[59,18680,18681],{"class":61,"line":1320},[59,18682,574],{},[59,18684,18685],{"class":61,"line":1326},[59,18686,18687],{},"    if (init_app() != 0)\n",[59,18689,18690],{"class":61,"line":1331},[59,18691,18692],{},"    {\n",[59,18694,18695],{"class":61,"line":1336},[59,18696,18697],{},"        alert(L\"错误\", L\"eduwin 初始化失败\");\n",[59,18699,18700],{"class":61,"line":1342},[59,18701,18702],{},"        return 1;\n",[59,18704,18705],{"class":61,"line":1348},[59,18706,7534],{},[59,18708,18709],{"class":61,"line":5377},[59,18710,174],{"emptyLinePlaceholder":173},[59,18712,18713],{"class":61,"line":7589},[59,18714,18715],{},"    HWND win = create_window(L\"Hello eduwin\", 400, 200);\n",[59,18717,18718],{"class":61,"line":7595},[59,18719,18720],{},"    if (!win)\n",[59,18722,18723],{"class":61,"line":7600},[59,18724,18692],{},[59,18726,18727],{"class":61,"line":7605},[59,18728,18729],{},"        alert(L\"错误\", L\"创建窗口失败\");\n",[59,18731,18732],{"class":61,"line":15131},[59,18733,18702],{},[59,18735,18736],{"class":61,"line":15136},[59,18737,7534],{},[59,18739,18740],{"class":61,"line":15142},[59,18741,174],{"emptyLinePlaceholder":173},[59,18743,18744],{"class":61,"line":15148},[59,18745,18746],{},"    \u002F* 文本框 *\u002F\n",[59,18748,18749],{"class":61,"line":15154},[59,18750,18751],{},"    text = create_text(win, L\"请输入文字\", 20, 20, 250, 24);\n",[59,18753,18754],{"class":61,"line":15160},[59,18755,174],{"emptyLinePlaceholder":173},[59,18757,18758],{"class":61,"line":15165},[59,18759,18760],{},"    \u002F* 按钮 *\u002F\n",[59,18762,18763],{"class":61,"line":15171},[59,18764,18765],{},"    HWND btn = create_button(win, L\"显示\", 280, 20, 80, 24);\n",[59,18767,18768],{"class":61,"line":15177},[59,18769,174],{"emptyLinePlaceholder":173},[59,18771,18772],{"class":61,"line":15182},[59,18773,18774],{},"    \u002F* 标签（用来显示输入的文字） *\u002F\n",[59,18776,18777],{"class":61,"line":15187},[59,18778,18779],{},"    label = create_label(win, L\"\", 20, 60, 340, 80);\n",[59,18781,18782],{"class":61,"line":15192},[59,18783,174],{"emptyLinePlaceholder":173},[59,18785,18786],{"class":61,"line":16482},[59,18787,18788],{},"    on_button_click(btn, on_click, NULL);\n",[59,18790,18791],{"class":61,"line":16487},[59,18792,174],{"emptyLinePlaceholder":173},[59,18794,18795],{"class":61,"line":16492},[59,18796,5365],{},[59,18798,18799],{"class":61,"line":16498},[59,18800,5370],{},[59,18802,18803],{"class":61,"line":16503},[59,18804,2087],{},[59,18806,18807],{"class":61,"line":16509},[59,18808,149],{},[46,18810,18812],{"id":18811},"_4-编译运行","4. 编译运行",[18,18814,18815,18816,18818,18819,18822],{},"打开 ",[22,18817,10534],{},"（从 ",[22,18820,18821],{},"samples\u002Fhello_demo\u002Fbuild.bat"," 复制而来），其中有 4 个地方需要你理解和修改。",[1020,18824,18826],{"id":18825},"_41-修改-vcvars-路径","4.1 修改 VCVARS 路径",[18,18828,18829,18831],{},[22,18830,10534],{}," 中有这样一行：",[50,18833,18835],{"className":7634,"code":18834,"language":7636,"meta":55,"style":55},"set \"VCVARS=C:\\Program Files (x86)\\Microsoft Visual Studio\\18\\BuildTools\\VC\\Auxiliary\\Build\\vcvars64.bat\"\n",[22,18836,18837],{"__ignoreMap":55},[59,18838,18839],{"class":61,"line":62},[59,18840,18834],{},[18,18842,18843,18844,18847],{},"你需要根据自己安装的 VS 版本，把路径中 ",[22,18845,18846],{},"\\18\\"," 那一段换成对应的文件夹名。下表列出了常见版本：",[4903,18849,18850,18860],{},[4906,18851,18852],{},[4909,18853,18854,18857],{},[4912,18855,18856],{},"你安装的版本",[4912,18858,18859],{},"对应的路径（复制后替换即可）",[4919,18861,18862,18872,18882,18892,18902],{},[4909,18863,18864,18867],{},[4924,18865,18866],{},"VS 2026 Build Tools",[4924,18868,18869],{},[22,18870,18871],{},"C:\\Program Files (x86)\\Microsoft Visual Studio\\18\\BuildTools\\VC\\Auxiliary\\Build\\vcvars64.bat",[4909,18873,18874,18877],{},[4924,18875,18876],{},"VS 2022 Build Tools",[4924,18878,18879],{},[22,18880,18881],{},"C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\BuildTools\\VC\\Auxiliary\\Build\\vcvars64.bat",[4909,18883,18884,18887],{},[4924,18885,18886],{},"VS 2022 Community",[4924,18888,18889],{},[22,18890,18891],{},"C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat",[4909,18893,18894,18897],{},[4924,18895,18896],{},"VS 2019 Build Tools",[4924,18898,18899],{},[22,18900,18901],{},"C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools\\VC\\Auxiliary\\Build\\vcvars64.bat",[4909,18903,18904,18907],{},[4924,18905,18906],{},"VS 2019 Community",[4924,18908,18909],{},[22,18910,18911],{},"C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat",[18,18913,18914,18915,18918,18919],{},"找到自己对应的那一行，复制整条路径，粘贴替换掉 ",[22,18916,18917],{},"VCVARS="," 后面的内容即可。",[69,18920,18921,18922,18924],{},"即使你直接用 ",[22,18923,18554],{}," 里的例子，也必须先改这里。",[15490,18926,18928],{"id":18927},"找不到-vcvars64bat-怎么办","找不到 vcvars64.bat 怎么办？",[18,18930,18931,18932,18935],{},"如果用 Everything 搜索 ",[22,18933,18934],{},"vcvars64.bat","，能直接找到真实路径，比对着表格猜更快。",[1020,18937,18939],{"id":18938},"_42-修改-source-和-program","4.2 修改 SOURCE 和 PROGRAM",[50,18941,18943],{"className":7634,"code":18942,"language":7636,"meta":55,"style":55},"set SOURCE=hello.c     ← 你的 C 源文件名\nset PROGRAM=hello.exe  ← 编译后输出的 exe 文件名\n",[22,18944,18945,18950],{"__ignoreMap":55},[59,18946,18947],{"class":61,"line":62},[59,18948,18949],{},"set SOURCE=hello.c     ← 你的 C 源文件名\n",[59,18951,18952],{"class":61,"line":88},[59,18953,18954],{},"set PROGRAM=hello.exe  ← 编译后输出的 exe 文件名\n",[315,18956,18957,18965],{},[318,18958,18959,18961,18962,18964],{},[22,18960,16949],{},"：你写了哪个 ",[22,18963,16887],{}," 文件，这里就填哪个文件名",[318,18966,18967,18969,18970,18973],{},[22,18968,16955],{},"：你想生成什么名字的 ",[22,18971,18972],{},".exe","，随便取",[1020,18975,18977],{"id":18976},"_43-理解编译命令中的相对路径重点","4.3 理解编译命令中的相对路径（重点）",[18,18979,18980,18982],{},[22,18981,18821],{}," 中的编译命令如下：",[50,18984,18986],{"className":7634,"code":18985,"language":7636,"meta":55,"style":55},"cl \u002Fnologo \u002FW3 \u002Futf-8 \u002Fstd:c17 \u002FI..\\..\\eduwin\\include \u002Fc \"%SOURCE%\"\nlink \u002Fnologo \"%SOURCE:.c=.obj%\" app.res ..\\..\\eduwin\\lib\\eduwin.lib ...\n",[22,18987,18988,18993],{"__ignoreMap":55},[59,18989,18990],{"class":61,"line":62},[59,18991,18992],{},"cl \u002Fnologo \u002FW3 \u002Futf-8 \u002Fstd:c17 \u002FI..\\..\\eduwin\\include \u002Fc \"%SOURCE%\"\n",[59,18994,18995],{"class":61,"line":88},[59,18996,18997],{},"link \u002Fnologo \"%SOURCE:.c=.obj%\" app.res ..\\..\\eduwin\\lib\\eduwin.lib ...\n",[18,18999,19000,19001,2730,19004,1177,19007,75],{},"这里的 ",[22,19002,19003],{},"..\\..\\eduwin\\include",[22,19005,19006],{},"..\\..\\eduwin\\lib\\eduwin.lib",[69,19008,19009],{},"相对路径",[18,19011,19012,19015,19016,7831,19018,19020,19021,19024,19025,19027,19028,2338,19031,19034],{},[22,19013,19014],{},".."," 表示\"退回上一级目录\"。",[22,19017,10534],{},[22,19019,18567],{}," 里，",[22,19022,19023],{},"..\\..\\"," 就是从那里往上退两级，到达 ",[22,19026,18515],{}," 根目录，然后再进 ",[22,19029,19030],{},"eduwin\\include\\",[22,19032,19033],{},"eduwin\\lib\\","。所以：",[315,19036,19037,19049],{},[318,19038,19039,19042,19043,19046,19047],{},[22,19040,19041],{},"\u002FI..\\..\\eduwin\\include"," 告诉编译器去 ",[22,19044,19045],{},"eduwin_pub\u002Feduwin\u002Finclude\u002F"," 找 ",[22,19048,28],{},[318,19050,19051,19053,19054,19046,19057],{},[22,19052,19006],{}," 告诉链接器去 ",[22,19055,19056],{},"eduwin_pub\u002Feduwin\u002Flib\u002F",[22,19058,19059],{},"eduwin.lib",[15490,19061,19063],{"id":19062},"如果项目位置变了呢","如果项目位置变了呢？",[18,19065,19066,19067,19070,19071,7831,19073,19075,19076,19086],{},"假如你把项目复制到了 ",[22,19068,19069],{},"D:\\my_project\\","，而 ",[22,19072,18515],{},[22,19074,18519],{},"，那么相对路径就变了。你需要根据实际情况调整——核心原则是：",[69,19077,19078,19079,19081,19082,19085],{},"确保 ",[22,19080,19003],{}," 能正确指向 ",[22,19083,19084],{},"eduwin\u002Finclude\u002F"," 目录","。如果不确定，可以用绝对路径替代，比如：",[50,19088,19090],{"className":7634,"code":19089,"language":7636,"meta":55,"style":55},"cl \u002Fnologo \u002FW3 \u002Futf-8 \u002Fstd:c17 \u002FID:\\XTU\\c-train\\eduwin_pub\\eduwin\\include \u002Fc \"%SOURCE%\"\nlink \u002Fnologo \"%SOURCE:.c=.obj%\" app.res D:\\XTU\\c-train\\eduwin_pub\\eduwin\\lib\\eduwin.lib ...\n",[22,19091,19092,19097],{"__ignoreMap":55},[59,19093,19094],{"class":61,"line":62},[59,19095,19096],{},"cl \u002Fnologo \u002FW3 \u002Futf-8 \u002Fstd:c17 \u002FID:\\XTU\\c-train\\eduwin_pub\\eduwin\\include \u002Fc \"%SOURCE%\"\n",[59,19098,19099],{"class":61,"line":88},[59,19100,19101],{},"link \u002Fnologo \"%SOURCE:.c=.obj%\" app.res D:\\XTU\\c-train\\eduwin_pub\\eduwin\\lib\\eduwin.lib ...\n",[15,19103,19104],{},[18,19105,19106,19107,19110,19111,19113],{},"绝对路径就是完整的路径（如 ",[22,19108,19109],{},"D:\\...\\eduwin.lib","），不依赖于\"当前在哪个目录\"，不容易搞混。缺点是如果移动了 ",[22,19112,18515],{}," 的位置或换到别人电脑上，就需要重新改。",[1020,19115,19117],{"id":19116},"_44-apprc-中的路径","4.4 app.rc 中的路径",[18,19119,19120,19123],{},[22,19121,19122],{},"app.rc"," 是资源文件，用记事本打开会看到：",[50,19125,19128],{"className":19126,"code":19127,"language":358},[356],"1 ICON \"app.ico\"\n1 24 \"..\\\\..\\\\eduwin\\\\eduwin.manifest\"\n",[22,19129,19127],{"__ignoreMap":55},[315,19131,19132,19142],{},[318,19133,19134,19135,19138,19139,19141],{},"第 1 行：指定程序图标为 ",[22,19136,19137],{},"app.ico","（跟 ",[22,19140,19122],{}," 同目录）",[318,19143,19144,19145,19148,19149,19152],{},"第 2 行：引用 ",[22,19146,19147],{},"eduwin.manifest","（Windows 视觉样式清单），这里的 ",[22,19150,19151],{},"..\\\\..\\\\eduwin\\\\eduwin.manifest"," 同样是相对路径，原理与上一节相同",[15,19154,19155],{},[18,19156,154,19157,19159,19160,19163,19164,75],{},[22,19158,18534],{}," 文件中路径使用双反斜杠 ",[22,19161,19162],{},"\\\\","，这是资源编译器的语法要求，意思等同于单反斜杠 ",[22,19165,19166],{},"\\",[18,19168,19169,19170,19172],{},"如果 ",[22,19171,18515],{}," 的相对位置变了，这里也要同步修改。",[1020,19174,19176],{"id":19175},"_45-编译","4.5 编译",[18,19178,19179,19180,19182,19183,4232],{},"确认以上路径都正确后，双击 ",[22,19181,10534],{},"，等待编译完成。目录下会生成 ",[22,19184,19185],{},"hello.exe",[50,19187,19190],{"className":19188,"code":19189,"language":358},[356],"my_project\u002F\n├── hello.c\n├── build.bat\n├── hello.obj\n└── hello.exe    ← 这就是我们的程序\n",[22,19191,19189],{"__ignoreMap":55},[18,19193,19194,19195,19197],{},"双击 ",[22,19196,19185],{}," 运行！",[46,19199,19201],{"id":19200},"_5-效果展示","5. 效果展示",[18,19203,19204],{},"运行后会看到一个 400×200 的窗口：",[315,19206,19207,19210,19213],{},[318,19208,19209],{},"上边是一个文本框，可以输入文字",[318,19211,19212],{},"右边是一个\"显示\"按钮",[318,19214,19215],{},"下边是一个标签区域",[18,19217,19218],{},"在文本框输入文字后点击\"显示\"，标签会显示你输入的内容，同时文本框清空。",[46,19220,19222],{"id":19221},"_6-代码说明","6. 代码说明",[4903,19224,19225,19233],{},[4906,19226,19227],{},[4909,19228,19229,19231],{},[4912,19230,6071],{},[4912,19232,4917],{},[4919,19234,19235,19244,19254,19264,19274,19284,19294,19303],{},[4909,19236,19237,19241],{},[4924,19238,19239],{},[22,19240,1985],{},[4924,19242,19243],{},"初始化 eduwin 框架，必须最先调用",[4909,19245,19246,19251],{},[4924,19247,19248],{},[22,19249,19250],{},"create_window()",[4924,19252,19253],{},"创建一个窗口，返回窗口句柄",[4909,19255,19256,19261],{},[4924,19257,19258],{},[22,19259,19260],{},"create_text()",[4924,19262,19263],{},"创建文本框控件",[4909,19265,19266,19271],{},[4924,19267,19268],{},[22,19269,19270],{},"create_button()",[4924,19272,19273],{},"创建按钮控件，最后一个参数是回调函数",[4909,19275,19276,19281],{},[4924,19277,19278],{},[22,19279,19280],{},"on_button_click()",[4924,19282,19283],{},"注册按钮的点击事件回调",[4909,19285,19286,19291],{},[4924,19287,19288],{},[22,19289,19290],{},"show_window()",[4924,19292,19293],{},"显示窗口",[4909,19295,19296,19300],{},[4924,19297,19298],{},[22,19299,7834],{},[4924,19301,19302],{},"进入消息循环，阻塞运行",[4909,19304,19305,19313],{},[4924,19306,19307,735,19310],{},[22,19308,19309],{},"get_text()",[22,19311,19312],{},"set_text()",[4924,19314,19315],{},"获取\u002F设置控件的文字内容",[432,19317],{},[18,19319,19320,19321,19325,19326,75],{},"现在你已经成功跑通了第一个 eduwin 程序！接下来可以查看更完整的",[745,19322,19324],{"href":19323},"#","示例项目","，或阅读完整的 ",[745,19327,19328],{"href":650},"API 参考",[362,19330,364],{},{"title":55,"searchDepth":88,"depth":88,"links":19332},[19333,19337,19341,19342,19349,19350],{"id":18468,"depth":88,"text":18469,"children":19334},[19335,19336],{"id":18472,"depth":134,"text":18473},{"id":18523,"depth":134,"text":18524},{"id":18541,"depth":88,"text":18542,"children":19338},[19339,19340],{"id":18548,"depth":134,"text":18549},{"id":18586,"depth":134,"text":18587},{"id":18602,"depth":88,"text":18603},{"id":18811,"depth":88,"text":18812,"children":19343},[19344,19345,19346,19347,19348],{"id":18825,"depth":134,"text":18826},{"id":18938,"depth":134,"text":18939},{"id":18976,"depth":134,"text":18977},{"id":19116,"depth":134,"text":19117},{"id":19175,"depth":134,"text":19176},{"id":19200,"depth":88,"text":19201},{"id":19221,"depth":88,"text":19222},{},"\u002Fdocs\u002Ftutorial\u002Feduwin-hello",{"title":18455,"description":18463},"docs\u002Ftutorial\u002Feduwin-hello","5q2sDS3vV7U_l1VvPC9CECH7OZTubYL8T_Kam0k_v3E",{"id":19357,"title":19358,"body":19359,"description":20018,"extension":374,"meta":20019,"navigation":173,"path":20020,"seo":20021,"stem":20022,"__hash__":20023},"content\u002Fdocs\u002Ftutorial\u002Feduwin-sqlite.md","eduwin + SQLite 数据库编程",{"type":8,"value":19360,"toc":20007},[19361,19364,19374,19376,19379,19530,19532,19540,19552,19554,19558,19561,19565,19580,19600,19604,19624,19629,19644,19648,19779,19785,19791,19846,19849,19853,19862,19864,19867,19877,19892,19913,19923,19925,19969,19971,19975,19984,19987,20004],[11,19362,19358],{"id":19363},"eduwin-sqlite-数据库编程",[18,19365,19366,19367,19369,19370,19373],{},"SQLite 是一个自包含、零配置、嵌入式的 C 语言数据库引擎。eduwin 已将 SQLite 编译进 ",[22,19368,19059],{},"，学生程序只需 ",[22,19371,19372],{},"#include \"sqlite3.h\""," 即可直接使用，无需额外配置。",[432,19375],{},[46,19377,19378],{"id":19378},"快速开始",[50,19380,19382],{"className":52,"code":19381,"language":54,"meta":55,"style":55},"#include \"eduwin.h\"\n#include \"sqlite3.h\"\n\nint main(void)\n{\n    init_app();\n\n    sqlite3 *db = NULL;\n    int rc = sqlite3_open(\"test.db\", &db);\n    if (rc != SQLITE_OK)\n    {\n        alert(L\"错误\", L\"无法打开数据库\");\n        return 1;\n    }\n\n    char *err = NULL;\n    sqlite3_exec(db, \"CREATE TABLE IF NOT EXISTS users (\"\n                     \"id INTEGER PRIMARY KEY AUTOINCREMENT, \"\n                     \"name TEXT NOT NULL, \"\n                     \"email TEXT)\", NULL, NULL, &err);\n    if (err)\n    {\n        alert(L\"错误\", L\"建表失败\");\n        sqlite3_free(err);\n    }\n\n    HWND win = create_window(L\"SQLite 测试\", 500, 300);\n    show_window(win);\n    run_app();\n\n    sqlite3_close(db);\n    return 0;\n}\n",[22,19383,19384,19388,19392,19396,19400,19404,19408,19412,19417,19422,19427,19431,19436,19440,19444,19448,19453,19458,19463,19468,19473,19478,19482,19487,19492,19496,19500,19505,19509,19513,19517,19522,19526],{"__ignoreMap":55},[59,19385,19386],{"class":61,"line":62},[59,19387,5287],{},[59,19389,19390],{"class":61,"line":88},[59,19391,8817],{},[59,19393,19394],{"class":61,"line":134},[59,19395,174],{"emptyLinePlaceholder":173},[59,19397,19398],{"class":61,"line":140},[59,19399,5296],{},[59,19401,19402],{"class":61,"line":146},[59,19403,574],{},[59,19405,19406],{"class":61,"line":281},[59,19407,9768],{},[59,19409,19410],{"class":61,"line":286},[59,19411,174],{"emptyLinePlaceholder":173},[59,19413,19414],{"class":61,"line":292},[59,19415,19416],{},"    sqlite3 *db = NULL;\n",[59,19418,19419],{"class":61,"line":600},[59,19420,19421],{},"    int rc = sqlite3_open(\"test.db\", &db);\n",[59,19423,19424],{"class":61,"line":917},[59,19425,19426],{},"    if (rc != SQLITE_OK)\n",[59,19428,19429],{"class":61,"line":922},[59,19430,18692],{},[59,19432,19433],{"class":61,"line":928},[59,19434,19435],{},"        alert(L\"错误\", L\"无法打开数据库\");\n",[59,19437,19438],{"class":61,"line":933},[59,19439,18702],{},[59,19441,19442],{"class":61,"line":939},[59,19443,7534],{},[59,19445,19446],{"class":61,"line":1320},[59,19447,174],{"emptyLinePlaceholder":173},[59,19449,19450],{"class":61,"line":1326},[59,19451,19452],{},"    char *err = NULL;\n",[59,19454,19455],{"class":61,"line":1331},[59,19456,19457],{},"    sqlite3_exec(db, \"CREATE TABLE IF NOT EXISTS users (\"\n",[59,19459,19460],{"class":61,"line":1336},[59,19461,19462],{},"                     \"id INTEGER PRIMARY KEY AUTOINCREMENT, \"\n",[59,19464,19465],{"class":61,"line":1342},[59,19466,19467],{},"                     \"name TEXT NOT NULL, \"\n",[59,19469,19470],{"class":61,"line":1348},[59,19471,19472],{},"                     \"email TEXT)\", NULL, NULL, &err);\n",[59,19474,19475],{"class":61,"line":5377},[59,19476,19477],{},"    if (err)\n",[59,19479,19480],{"class":61,"line":7589},[59,19481,18692],{},[59,19483,19484],{"class":61,"line":7595},[59,19485,19486],{},"        alert(L\"错误\", L\"建表失败\");\n",[59,19488,19489],{"class":61,"line":7600},[59,19490,19491],{},"        sqlite3_free(err);\n",[59,19493,19494],{"class":61,"line":7605},[59,19495,7534],{},[59,19497,19498],{"class":61,"line":15131},[59,19499,174],{"emptyLinePlaceholder":173},[59,19501,19502],{"class":61,"line":15136},[59,19503,19504],{},"    HWND win = create_window(L\"SQLite 测试\", 500, 300);\n",[59,19506,19507],{"class":61,"line":15142},[59,19508,5365],{},[59,19510,19511],{"class":61,"line":15148},[59,19512,5370],{},[59,19514,19515],{"class":61,"line":15154},[59,19516,174],{"emptyLinePlaceholder":173},[59,19518,19519],{"class":61,"line":15160},[59,19520,19521],{},"    sqlite3_close(db);\n",[59,19523,19524],{"class":61,"line":15165},[59,19525,2087],{},[59,19527,19528],{"class":61,"line":15171},[59,19529,149],{},[18,19531,15197],{},[50,19533,19534],{"className":1893,"code":11819,"language":1895,"meta":55,"style":55},[22,19535,19536],{"__ignoreMap":55},[59,19537,19538],{"class":61,"line":62},[59,19539,11819],{"class":1908},[18,19541,19542,19543,19545,19546,19548,19549,19551],{},"只要在 ",[22,19544,16887],{}," 文件中 ",[22,19547,19372],{},"，link 时自动从 ",[22,19550,19059],{}," 获取 SQLite 实现。",[432,19553],{},[46,19555,19557],{"id":19556},"核心-api","核心 API",[18,19559,19560],{},"SQLite 最常用的三个函数：",[1020,19562,19564],{"id":19563},"打开关闭","打开\u002F关闭",[50,19566,19568],{"className":52,"code":19567,"language":54,"meta":55,"style":55},"int sqlite3_open(const char *filename, sqlite3 **ppDb);\nint sqlite3_close(sqlite3 *db);\n",[22,19569,19570,19575],{"__ignoreMap":55},[59,19571,19572],{"class":61,"line":62},[59,19573,19574],{},"int sqlite3_open(const char *filename, sqlite3 **ppDb);\n",[59,19576,19577],{"class":61,"line":88},[59,19578,19579],{},"int sqlite3_close(sqlite3 *db);\n",[315,19581,19582,19588,19594],{},[318,19583,19584,19587],{},[22,19585,19586],{},"sqlite3_open"," 打开或创建数据库文件（UTF-8 路径）",[318,19589,19590,19593],{},[22,19591,19592],{},"sqlite3_close"," 关闭数据库，释放资源",[318,19595,8263,19596,19599],{},[22,19597,19598],{},"SQLITE_OK","（0）表示成功",[1020,19601,19603],{"id":19602},"执行-sql","执行 SQL",[50,19605,19607],{"className":52,"code":19606,"language":54,"meta":55,"style":55},"int sqlite3_exec(sqlite3 *db, const char *sql,\n                 int (*callback)(void *, int, char **, char **),\n                 void *data, char **errmsg);\n",[22,19608,19609,19614,19619],{"__ignoreMap":55},[59,19610,19611],{"class":61,"line":62},[59,19612,19613],{},"int sqlite3_exec(sqlite3 *db, const char *sql,\n",[59,19615,19616],{"class":61,"line":88},[59,19617,19618],{},"                 int (*callback)(void *, int, char **, char **),\n",[59,19620,19621],{"class":61,"line":134},[59,19622,19623],{},"                 void *data, char **errmsg);\n",[18,19625,19626,19627,4232],{},"最简单的执行方式——不需要回调时传 ",[22,19628,477],{},[50,19630,19632],{"className":52,"code":19631,"language":54,"meta":55,"style":55},"sqlite3_exec(db, \"INSERT INTO users VALUES(NULL, '张三', 'zhangsan@test.com')\",\n             NULL, NULL, &err);\n",[22,19633,19634,19639],{"__ignoreMap":55},[59,19635,19636],{"class":61,"line":62},[59,19637,19638],{},"sqlite3_exec(db, \"INSERT INTO users VALUES(NULL, '张三', 'zhangsan@test.com')\",\n",[59,19640,19641],{"class":61,"line":88},[59,19642,19643],{},"             NULL, NULL, &err);\n",[1020,19645,19647],{"id":19646},"带参数查询推荐","带参数查询（推荐）",[50,19649,19651],{"className":52,"code":19650,"language":54,"meta":55,"style":55},"\u002F* 根据学号查询用户，返回姓名，未找到返回 NULL *\u002F\nconst char *find_user_name(sqlite3 *db, int user_id)\n{\n    sqlite3_stmt *stmt = NULL;\n\n    \u002F* 1. 准备 SQL（？是占位符） *\u002F\n    int rc = sqlite3_prepare_v2(db,\n                \"SELECT name FROM users WHERE id = ?\",\n                -1, &stmt, NULL);\n    if (rc != SQLITE_OK)\n        return NULL;\n\n    \u002F* 2. 绑定参数（第 1 个 ? 绑定 user_id） *\u002F\n    sqlite3_bind_int(stmt, 1, user_id);\n\n    \u002F* 3. 执行查询 *\u002F\n    const char *result = NULL;\n    if (sqlite3_step(stmt) == SQLITE_ROW)\n    {\n        \u002F* 4. 读取结果集第 0 列 *\u002F\n        result = strdup((const char *)sqlite3_column_text(stmt, 0));\n    }\n\n    \u002F* 5. 释放语句 *\u002F\n    sqlite3_finalize(stmt);\n    return result;\n}\n",[22,19652,19653,19658,19663,19667,19672,19676,19681,19686,19691,19696,19700,19705,19709,19714,19719,19723,19728,19733,19738,19742,19747,19752,19756,19760,19765,19770,19775],{"__ignoreMap":55},[59,19654,19655],{"class":61,"line":62},[59,19656,19657],{},"\u002F* 根据学号查询用户，返回姓名，未找到返回 NULL *\u002F\n",[59,19659,19660],{"class":61,"line":88},[59,19661,19662],{},"const char *find_user_name(sqlite3 *db, int user_id)\n",[59,19664,19665],{"class":61,"line":134},[59,19666,574],{},[59,19668,19669],{"class":61,"line":140},[59,19670,19671],{},"    sqlite3_stmt *stmt = NULL;\n",[59,19673,19674],{"class":61,"line":146},[59,19675,174],{"emptyLinePlaceholder":173},[59,19677,19678],{"class":61,"line":281},[59,19679,19680],{},"    \u002F* 1. 准备 SQL（？是占位符） *\u002F\n",[59,19682,19683],{"class":61,"line":286},[59,19684,19685],{},"    int rc = sqlite3_prepare_v2(db,\n",[59,19687,19688],{"class":61,"line":292},[59,19689,19690],{},"                \"SELECT name FROM users WHERE id = ?\",\n",[59,19692,19693],{"class":61,"line":600},[59,19694,19695],{},"                -1, &stmt, NULL);\n",[59,19697,19698],{"class":61,"line":917},[59,19699,19426],{},[59,19701,19702],{"class":61,"line":922},[59,19703,19704],{},"        return NULL;\n",[59,19706,19707],{"class":61,"line":928},[59,19708,174],{"emptyLinePlaceholder":173},[59,19710,19711],{"class":61,"line":933},[59,19712,19713],{},"    \u002F* 2. 绑定参数（第 1 个 ? 绑定 user_id） *\u002F\n",[59,19715,19716],{"class":61,"line":939},[59,19717,19718],{},"    sqlite3_bind_int(stmt, 1, user_id);\n",[59,19720,19721],{"class":61,"line":1320},[59,19722,174],{"emptyLinePlaceholder":173},[59,19724,19725],{"class":61,"line":1326},[59,19726,19727],{},"    \u002F* 3. 执行查询 *\u002F\n",[59,19729,19730],{"class":61,"line":1331},[59,19731,19732],{},"    const char *result = NULL;\n",[59,19734,19735],{"class":61,"line":1336},[59,19736,19737],{},"    if (sqlite3_step(stmt) == SQLITE_ROW)\n",[59,19739,19740],{"class":61,"line":1342},[59,19741,18692],{},[59,19743,19744],{"class":61,"line":1348},[59,19745,19746],{},"        \u002F* 4. 读取结果集第 0 列 *\u002F\n",[59,19748,19749],{"class":61,"line":5377},[59,19750,19751],{},"        result = strdup((const char *)sqlite3_column_text(stmt, 0));\n",[59,19753,19754],{"class":61,"line":7589},[59,19755,7534],{},[59,19757,19758],{"class":61,"line":7595},[59,19759,174],{"emptyLinePlaceholder":173},[59,19761,19762],{"class":61,"line":7600},[59,19763,19764],{},"    \u002F* 5. 释放语句 *\u002F\n",[59,19766,19767],{"class":61,"line":7605},[59,19768,19769],{},"    sqlite3_finalize(stmt);\n",[59,19771,19772],{"class":61,"line":15131},[59,19773,19774],{},"    return result;\n",[59,19776,19777],{"class":61,"line":15136},[59,19778,149],{},[18,19780,19781,19782,75],{},"流程：",[69,19783,19784],{},"准备 → 绑定 → 单步执行 → 读列 → 销毁",[18,19786,19787,19790],{},[22,19788,19789],{},"?"," 是占位符，从 1 开始编号。多参数时：",[50,19792,19794],{"className":52,"code":19793,"language":54,"meta":55,"style":55},"\u002F* UPDATE 示例：带三个占位符 *\u002F\nsqlite3_stmt *stmt;\nsqlite3_prepare_v2(db,\n    \"UPDATE users SET name = ?, email = ? WHERE id = ?\",\n    -1, &stmt, NULL);\nsqlite3_bind_text(stmt, 1, name_utf8, -1, SQLITE_TRANSIENT);\nsqlite3_bind_text(stmt, 2, email_utf8, -1, SQLITE_TRANSIENT);\nsqlite3_bind_int(stmt,  3, user_id);\nsqlite3_step(stmt);\nsqlite3_finalize(stmt);\n",[22,19795,19796,19801,19806,19811,19816,19821,19826,19831,19836,19841],{"__ignoreMap":55},[59,19797,19798],{"class":61,"line":62},[59,19799,19800],{},"\u002F* UPDATE 示例：带三个占位符 *\u002F\n",[59,19802,19803],{"class":61,"line":88},[59,19804,19805],{},"sqlite3_stmt *stmt;\n",[59,19807,19808],{"class":61,"line":134},[59,19809,19810],{},"sqlite3_prepare_v2(db,\n",[59,19812,19813],{"class":61,"line":140},[59,19814,19815],{},"    \"UPDATE users SET name = ?, email = ? WHERE id = ?\",\n",[59,19817,19818],{"class":61,"line":146},[59,19819,19820],{},"    -1, &stmt, NULL);\n",[59,19822,19823],{"class":61,"line":281},[59,19824,19825],{},"sqlite3_bind_text(stmt, 1, name_utf8, -1, SQLITE_TRANSIENT);\n",[59,19827,19828],{"class":61,"line":286},[59,19829,19830],{},"sqlite3_bind_text(stmt, 2, email_utf8, -1, SQLITE_TRANSIENT);\n",[59,19832,19833],{"class":61,"line":292},[59,19834,19835],{},"sqlite3_bind_int(stmt,  3, user_id);\n",[59,19837,19838],{"class":61,"line":600},[59,19839,19840],{},"sqlite3_step(stmt);\n",[59,19842,19843],{"class":61,"line":917},[59,19844,19845],{},"sqlite3_finalize(stmt);\n",[18,19847,19848],{},"始终使用参数绑定而非字符串拼接，可防止 SQL 注入。",[1020,19850,19852],{"id":19851},"获取最后插入的-id","获取最后插入的 ID",[50,19854,19856],{"className":52,"code":19855,"language":54,"meta":55,"style":55},"int last_id = (int)sqlite3_last_insert_rowid(db);\n",[22,19857,19858],{"__ignoreMap":55},[59,19859,19860],{"class":61,"line":62},[59,19861,19855],{},[432,19863],{},[46,19865,19866],{"id":19866},"字符串编码",[18,19868,19869,19870,19872,19873,19876],{},"SQLite 的 API 使用 ",[69,19871,5056],{}," 编码，而 eduwin 使用 ",[69,19874,19875],{},"UTF-16（宽字符）","。eduwin 提供两个内置工具函数完成互转：",[50,19878,19880],{"className":52,"code":19879,"language":54,"meta":55,"style":55},"char *wtou8(const WCHAR *ws);\nWCHAR *u8tow(const char *us);\n",[22,19881,19882,19887],{"__ignoreMap":55},[59,19883,19884],{"class":61,"line":62},[59,19885,19886],{},"char *wtou8(const WCHAR *ws);\n",[59,19888,19889],{"class":61,"line":88},[59,19890,19891],{},"WCHAR *u8tow(const char *us);\n",[315,19893,19894,19904],{},[318,19895,19896,19898,19899,19901,19902,1494],{},[22,19897,12821],{},"：宽字符串 → UTF-8 字符串（",[22,19900,8748],{}," 分配内存，用完需 ",[22,19903,14059],{},[318,19905,19906,19908,19909,19901,19911,1494],{},[22,19907,12824],{},"：UTF-8 字符串 → 宽字符串（",[22,19910,8748],{},[22,19912,14059],{},[18,19914,19915,19916,19918,19919,19922],{},"这两个函数已编译在 ",[22,19917,19059],{}," 中，",[22,19920,19921],{},"#include \"eduwin.h\""," 即可使用。",[18,19924,4177],{},[50,19926,19928],{"className":52,"code":19927,"language":54,"meta":55,"style":55},"char *sql = wtou8(L\"INSERT INTO users VALUES(NULL, '测试用户', 'test@test.com')\");\nsqlite3_exec(db, sql, NULL, NULL, &err);\nfree(sql);\n\nconst char *name8 = (const char *)sqlite3_column_text(stmt, 1);\nWCHAR *name16 = u8tow(name8);\nset_text(label, name16);\nfree(name16);\n",[22,19929,19930,19935,19940,19945,19949,19954,19959,19964],{"__ignoreMap":55},[59,19931,19932],{"class":61,"line":62},[59,19933,19934],{},"char *sql = wtou8(L\"INSERT INTO users VALUES(NULL, '测试用户', 'test@test.com')\");\n",[59,19936,19937],{"class":61,"line":88},[59,19938,19939],{},"sqlite3_exec(db, sql, NULL, NULL, &err);\n",[59,19941,19942],{"class":61,"line":134},[59,19943,19944],{},"free(sql);\n",[59,19946,19947],{"class":61,"line":140},[59,19948,174],{"emptyLinePlaceholder":173},[59,19950,19951],{"class":61,"line":146},[59,19952,19953],{},"const char *name8 = (const char *)sqlite3_column_text(stmt, 1);\n",[59,19955,19956],{"class":61,"line":281},[59,19957,19958],{},"WCHAR *name16 = u8tow(name8);\n",[59,19960,19961],{"class":61,"line":286},[59,19962,19963],{},"set_text(label, name16);\n",[59,19965,19966],{"class":61,"line":292},[59,19967,19968],{},"free(name16);\n",[432,19970],{},[46,19972,19974],{"id":19973},"完整示例用户管理","完整示例：用户管理",[18,19976,19977,19980,19981,6535],{},[22,19978,19979],{},"demo\\sqlite_demo.c"," 是一个完整的用户信息管理程序，演示了增删改查全部操作，可直接编译运行（",[22,19982,19983],{},"demo\\build_sqlite_demo.bat",[18,19985,19986],{},"功能包括：",[315,19988,19989,19992,19995,19998,20001],{},[318,19990,19991],{},"列表显示所有用户（ListView）",[318,19993,19994],{},"添加用户（姓名、邮箱、电话）",[318,19996,19997],{},"双击列表项回填到输入框，修改后保存",[318,19999,20000],{},"选中用户后删除",[318,20002,20003],{},"实时搜索过滤",[362,20005,20006],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":55,"searchDepth":88,"depth":88,"links":20008},[20009,20010,20016,20017],{"id":19378,"depth":88,"text":19378},{"id":19556,"depth":88,"text":19557,"children":20011},[20012,20013,20014,20015],{"id":19563,"depth":134,"text":19564},{"id":19602,"depth":134,"text":19603},{"id":19646,"depth":134,"text":19647},{"id":19851,"depth":134,"text":19852},{"id":19866,"depth":88,"text":19866},{"id":19973,"depth":88,"text":19974},"SQLite 是一个自包含、零配置、嵌入式的 C 语言数据库引擎。eduwin 已将 SQLite 编译进 eduwin.lib，学生程序只需 #include \"sqlite3.h\" 即可直接使用，无需额外配置。",{},"\u002Fdocs\u002Ftutorial\u002Feduwin-sqlite",{"title":19358,"description":20018},"docs\u002Ftutorial\u002Feduwin-sqlite","2Crz_HJ9w9O9YzgATSB1f3B9FwdGw9RcrliXjf8sVk4",{"id":20025,"title":20026,"body":20027,"description":20116,"extension":374,"meta":20117,"navigation":173,"path":20118,"seo":20119,"stem":20120,"__hash__":20121},"content\u002Fversions\u002Flog\u002Feduwin_logview_2026-06-26.md","eduwin_logview — 日志面板组件",{"type":8,"value":20028,"toc":20112},[20029,20032,20037,20043,20046,20095,20098],[11,20030,20026],{"id":20031},"eduwin_logview-日志面板组件",[18,20033,20034,10673],{},[69,20035,20036],{},"日期：",[18,20038,20039,20042],{},[69,20040,20041],{},"版本："," 1.0",[46,20044,20045],{"id":20045},"新增",[315,20047,20048],{},[318,20049,20050,20052,20053],{},[22,20051,11662],{}," — 新的 LogView 自定义控件模块\n",[315,20054,20055,20060,20069,20074,20079,20086],{},[318,20056,20057,20059],{},[22,20058,3615],{}," — 创建日志面板（3 列：时间\u002F级别\u002F消息）",[318,20061,20062,735,20064,735,20066,20068],{},[22,20063,3611],{},[22,20065,6098],{},[22,20067,6111],{}," — 追加日志行",[318,20070,20071,20073],{},[22,20072,11671],{}," — 清空所有日志",[318,20075,20076,20078],{},[22,20077,11674],{}," — 设置行数上限（默认 500）",[318,20080,20081,20082,20085],{},"内部使用框架封装的 ",[22,20083,20084],{},"listview_add_text\u002Fset_text\u002Fcount"," 等 API",[318,20087,20088,20089,1086,20091,2730,20093],{},"已集成到 ",[22,20090,11479],{},[22,20092,11381],{},[22,20094,28],{},[46,20096,20097],{"id":20097},"影响",[315,20099,20100],{},[318,20101,20102,20104,20105,735,20108,20111],{},[22,20103,10684],{}," 已迁移到 LogView 组件，移除所有原生 ",[22,20106,20107],{},"SendMessageW",[22,20109,20110],{},"LVM_*"," 调用",{"title":55,"searchDepth":88,"depth":88,"links":20113},[20114,20115],{"id":20045,"depth":88,"text":20045},{"id":20097,"depth":88,"text":20097},"日期： 2026-06-26",{},"\u002Fversions\u002Flog\u002Feduwin_logview_2026-06-26",{"title":20026,"description":20116},"versions\u002Flog\u002Feduwin_logview_2026-06-26","o3sXi0QDy7KHkmoeSOuZo4baGPRK2iV0rRFtfvZgSwQ",{"id":20123,"title":20124,"body":20125,"description":20482,"extension":374,"meta":20483,"navigation":173,"path":20484,"seo":20485,"stem":20486,"__hash__":20487},"content\u002Fversions\u002Flog\u002Feduwin_webview_2026-06-23.md","eduwin_webview 模块变更日志",{"type":8,"value":20126,"toc":20473},[20127,20130,20138,20140,20171,20175,20283,20287,20299,20302,20305,20365,20367,20437,20440,20459,20461],[11,20128,20124],{"id":20129},"eduwin_webview-模块变更日志",[18,20131,20132,20134,20135,20137],{},[69,20133,20036],{}," 2026-06-23\n",[69,20136,20041],{}," v1.0",[46,20139,20045],{"id":20045},[315,20141,20142,20151,20159,20168],{},[318,20143,20144,20147,20148,20150],{},[69,20145,20146],{},"WebView2 嵌入支持。"," 新增 ",[22,20149,10738],{}," 模块。",[318,20152,20153,624,20156,20158],{},[69,20154,20155],{},"Fixed Version 支持。",[22,20157,9688],{}," 可指定本地 Runtime 目录，客户端零依赖。",[318,20160,20161,20164,20165,20167],{},[69,20162,20163],{},"Evergreen 兼容。"," 不调用 ",[22,20166,9688],{}," 时默认使用系统共享 Runtime。",[318,20169,20170],{},"纯 C 实现，COM 三种模式全覆盖。",[46,20172,20174],{"id":20173},"api","API",[4903,20176,20177,20185],{},[4906,20178,20179],{},[4909,20180,20181,20183],{},[4912,20182,6071],{},[4912,20184,4917],{},[4919,20186,20187,20197,20206,20215,20225,20235,20245,20254,20264,20274],{},[4909,20188,20189,20194],{},[4924,20190,20191],{},[22,20192,20193],{},"webview_set_runtime_path(path)",[4924,20195,20196],{},"设置 Runtime 路径（NULL=Evergreen）",[4909,20198,20199,20203],{},[4924,20200,20201],{},[22,20202,9726],{},[4924,20204,20205],{},"初始化 WebView2（COM + 注册窗口类）",[4909,20207,20208,20212],{},[4924,20209,20210],{},[22,20211,9740],{},[4924,20213,20214],{},"清理 WebView2 全局环境",[4909,20216,20217,20222],{},[4924,20218,20219],{},[22,20220,20221],{},"create_webview(parent, x, y, w, h)",[4924,20223,20224],{},"创建 WebView2 子控件",[4909,20226,20227,20232],{},[4924,20228,20229],{},[22,20230,20231],{},"webview_navigate(hwv, url)",[4924,20233,20234],{},"导航到 URL",[4909,20236,20237,20242],{},[4924,20238,20239],{},[22,20240,20241],{},"webview_load_html(hwv, html)",[4924,20243,20244],{},"加载 HTML 字符串",[4909,20246,20247,20252],{},[4924,20248,20249],{},[22,20250,20251],{},"on_webview_ready(hwv, cb, data)",[4924,20253,9959],{},[4909,20255,20256,20261],{},[4924,20257,20258],{},[22,20259,20260],{},"on_webview_message(hwv, cb, data)",[4924,20262,20263],{},"接收 JS→C 消息",[4909,20265,20266,20271],{},[4924,20267,20268],{},[22,20269,20270],{},"webview_post_message(hwv, json)",[4924,20272,20273],{},"发送 C→JS 消息",[4909,20275,20276,20281],{},[4924,20277,20278],{},[22,20279,20280],{},"webview_back\u002Fforward\u002Freload(hwv)",[4924,20282,10363],{},[46,20284,20286],{"id":20285},"打包-runtime","打包 Runtime",[18,20288,20289,20291,20292,20295,20296,20298],{},[22,20290,11381],{}," 自动将 ",[22,20293,20294],{},"eduwin\u002Fwebview2\u002Fruntime\u002F","（Fixed Version, 149.0.4022.80 x64, ~280MB）打包进 ",[22,20297,9659],{},"。发布包总大小约 252MB。",[46,20300,20301],{"id":20301},"实现",[18,20303,20304],{},"三种 COM 互操作模式全部在纯 C 中完成：",[4903,20306,20307,20318],{},[4906,20308,20309],{},[4909,20310,20311,20313,20315],{},[4912,20312,9605],{},[4912,20314,10556],{},[4912,20316,20317],{},"实现方式",[4919,20319,20320,20337,20350],{},[4909,20321,20322,20325,20328],{},[4924,20323,20324],{},"模式 A: Flat API",[4924,20326,20327],{},"WebView2 Loader 入口",[4924,20329,20330,20331,20334,20335],{},"手写 ",[22,20332,20333],{},"__stdcall"," 声明，链接 ",[22,20336,10565],{},[4909,20338,20339,20342,20345],{},[4924,20340,20341],{},"模式 B: lpVtbl",[4924,20343,20344],{},"接口调用（Navigate、put_Bounds 等）",[4924,20346,20347],{},[22,20348,20349],{},"obj->lpVtbl->Method(obj, ...)",[4909,20351,20352,20355,20358],{},[4924,20353,20354],{},"模式 C: 回调 vtable",[4924,20356,20357],{},"异步 CompletedHandler",[4924,20359,20360,20361,20364],{},"手写 struct ",[22,20362,20363],{},"{ Vtbl *lpVtbl; LONG ref; ctx }"," + 四个 STDMETHODCALLTYPE",[46,20366,11541],{"id":11541},[4903,20368,20369,20377],{},[4906,20370,20371],{},[4909,20372,20373,20375],{},[4912,20374,11541],{},[4912,20376,10556],{},[4919,20378,20379,20389,20399,20409,20419,20428],{},[4909,20380,20381,20386],{},[4924,20382,20383],{},[22,20384,20385],{},"eduwin\u002Fsrc\u002Feduwin_webview.c",[4924,20387,20388],{},"核心实现（586 行）",[4909,20390,20391,20396],{},[4924,20392,20393],{},[22,20394,20395],{},"eduwin\u002Fsrc\u002Feduwin_webview_internal.h",[4924,20397,20398],{},"内部接口",[4909,20400,20401,20406],{},[4924,20402,20403],{},[22,20404,20405],{},"eduwin\u002Finclude\u002Feduwin_webview.h",[4924,20407,20408],{},"公开 API 头",[4909,20410,20411,20416],{},[4924,20412,20413],{},[22,20414,20415],{},"eduwin\u002Fwebview2\u002F",[4924,20417,20418],{},"WebView2 SDK（头文件 + lib）",[4909,20420,20421,20425],{},[4924,20422,20423],{},[22,20424,10518],{},[4924,20426,20427],{},"简易浏览器示例",[4909,20429,20430,20434],{},[4924,20431,20432],{},[22,20433,10524],{},[4924,20435,20436],{},"Bridge 通信示例",[46,20438,20439],{"id":20439},"文档",[315,20441,20442,20448,20454],{},[318,20443,20444,20445],{},"API 参考：",[22,20446,20447],{},"www\u002Fcontent\u002Fdocs\u002Fapi\u002Feduwin-webview-api.md",[318,20449,20450,20451],{},"示例说明：",[22,20452,20453],{},"www\u002Fcontent\u002Fdocs\u002Fsamples\u002Fwebview_browser.md",[318,20455,20450,20456],{},[22,20457,20458],{},"www\u002Fcontent\u002Fdocs\u002Fsamples\u002Fwebview_bridge.md",[46,20460,10528],{"id":10528},[18,20462,20463,624,20465,624,20467,624,20469,624,20471],{},[22,20464,10565],{},[22,20466,10575],{},[22,20468,10585],{},[22,20470,10595],{},[22,20472,10605],{},{"title":55,"searchDepth":88,"depth":88,"links":20474},[20475,20476,20477,20478,20479,20480,20481],{"id":20045,"depth":88,"text":20045},{"id":20173,"depth":88,"text":20174},{"id":20285,"depth":88,"text":20286},{"id":20301,"depth":88,"text":20301},{"id":11541,"depth":88,"text":11541},{"id":20439,"depth":88,"text":20439},{"id":10528,"depth":88,"text":10528},"日期： 2026-06-23\n版本： v1.0",{},"\u002Fversions\u002Flog\u002Feduwin_webview_2026-06-23",{"title":20124,"description":20482},"versions\u002Flog\u002Feduwin_webview_2026-06-23","HBbimXomvtlOHIqIKMwXLaZyfheTNT3bh_GR38yoVlQ",{"id":20489,"title":20490,"body":20491,"description":55,"extension":374,"meta":20510,"navigation":173,"path":20529,"seo":20530,"stem":20531,"__hash__":20532},"content\u002Fversions\u002Fv1.0.md","eduwin v1.0 — 首个发布版本",{"type":8,"value":20492,"toc":20507},[20493,20496,20501,20504],[11,20494,20490],{"id":20495},"eduwin-v10-首个发布版本",[15,20497,20498],{},[18,20499,20500],{},"发布日期：2026-06-13",[46,20502,20503],{"id":20503},"概述",[18,20505,20506],{},"eduwin 的第一个公开发布版本，包含窗口系统、菜单、图片、2D 画布和 SQLite 数据库五大模块。",{"title":55,"searchDepth":88,"depth":88,"links":20508},[20509],{"id":20503,"depth":88,"text":20503},{"version":20511,"date":20512,"summary":20513,"items":20514},"v1.0","2026-06-13","首个发布版本",[20515,20518,20521,20523,20526],{"module":20516,"text":20517},"app","窗口系统：init_app \u002F create_window \u002F show_window \u002F run_app",{"module":20519,"text":20520},"menu","菜单系统：create_menu \u002F add_menu \u002F add_menu_item",{"module":1493,"text":20522},"图片模块：load_image \u002F draw_image",{"module":20524,"text":20525},"canvas","2D 画布：create_canvas \u002F sprite_* 基本精灵系统",{"module":20527,"text":20528},"sqlite","SQLite 集成：db_open \u002F db_exec \u002F db_query","\u002Fversions\u002Fv1.0",{"title":20490,"description":55},"versions\u002Fv1.0","XqILAEKvICCatFJEmCUaXD9YujcAVMOG0aN-82Zb85o",{"id":20534,"title":20535,"body":20536,"description":55,"extension":374,"meta":20554,"navigation":173,"path":20566,"seo":20567,"stem":20568,"__hash__":20569},"content\u002Fversions\u002Fv1.1.md","eduwin v1.1 — 画布 \u002F 音效",{"type":8,"value":20537,"toc":20551},[20538,20541,20546,20548],[11,20539,20535],{"id":20540},"eduwin-v11-画布-音效",[15,20542,20543],{},[18,20544,20545],{},"发布日期：2026-06-14",[46,20547,20503],{"id":20503},[18,20549,20550],{},"v1.1 重构精灵系统并新增音效模块。",{"title":55,"searchDepth":88,"depth":88,"links":20552},[20553],{"id":20503,"depth":88,"text":20503},{"version":20555,"date":20556,"summary":20557,"items":20558},"v1.1","2026-06-14","画布 \u002F 音效",[20559,20561,20563],{"module":20524,"text":20560},"精灵系统重构（结构体 + 面向对象风格）",{"module":20524,"text":20562},"精灵碰撞检测",{"module":20564,"text":20565},"sound","load_sound \u002F play_sound \u002F free_sound","\u002Fversions\u002Fv1.1",{"title":20535,"description":55},"versions\u002Fv1.1","MrX_BpcGafW5TuTKYtwzwskOUOZoW3cJRQx_eXbGsg0",{"id":20571,"title":20572,"body":20573,"description":55,"extension":374,"meta":20590,"navigation":173,"path":20606,"seo":20607,"stem":20608,"__hash__":20609},"content\u002Fversions\u002Fv1.2.md","eduwin v1.2 — 控件封装 \u002F 精灵增强",{"type":8,"value":20574,"toc":20587},[20575,20578,20582,20584],[11,20576,20572],{"id":20577},"eduwin-v12-控件封装-精灵增强",[15,20579,20580],{},[18,20581,20545],{},[46,20583,20503],{"id":20503},[18,20585,20586],{},"v1.2 系统化封装 Win32 常用控件，提供统一的创建和操作 API。",{"title":55,"searchDepth":88,"depth":88,"links":20588},[20589],{"id":20503,"depth":88,"text":20503},{"version":20591,"date":20556,"summary":20592,"items":20593},"v1.2","控件封装 \u002F 精灵增强",[20594,20596,20598,20600,20602,20604],{"module":6711,"text":20595},"create_button \u002F create_label \u002F create_text \u002F create_textarea \u002F create_password",{"module":6711,"text":20597},"create_checkbox \u002F create_radio \u002F create_listbox \u002F create_combobox",{"module":6711,"text":20599},"create_progress \u002F create_treeview \u002F create_statusbar",{"module":6711,"text":20601},"set_text \u002F get_text \u002F set_readonly \u002F set_enable \u002F show \u002F hide \u002F move \u002F set_size",{"module":6711,"text":20603},"on_button_click 回调机制",{"module":20524,"text":20605},"新增精灵动画帧率控制","\u002Fversions\u002Fv1.2",{"title":20572,"description":55},"versions\u002Fv1.2","Qa4K4bsi-Jn_f_gJOQK4vdjDDCOrnmPM900NBzSVqms",{"id":20611,"title":20612,"body":20613,"description":55,"extension":374,"meta":20631,"navigation":173,"path":20648,"seo":20649,"stem":20650,"__hash__":20651},"content\u002Fversions\u002Fv1.5.md","eduwin v1.5 — JSON \u002F i18n",{"type":8,"value":20614,"toc":20628},[20615,20618,20623,20625],[11,20616,20612],{"id":20617},"eduwin-v15-json-i18n",[15,20619,20620],{},[18,20621,20622],{},"发布日期：2026-06-17",[46,20624,20503],{"id":20503},[18,20626,20627],{},"v1.5 新增 JSON 解析模块和国际化（i18n）模块。",{"title":55,"searchDepth":88,"depth":88,"links":20629},[20630],{"id":20503,"depth":88,"text":20503},{"version":20632,"date":20633,"summary":20634,"items":20635},"v1.5","2026-06-17","JSON \u002F i18n",[20636,20638,20641,20643,20646],{"module":5069,"text":20637},"新增 eduwin_json 模块封装 cJSON，支持文件读写",{"module":20639,"text":20640},"i18n","新增 eduwin_i18n 模块",{"module":20639,"text":20642},"支持语言文件热切换",{"module":20644,"text":20645},"util","日志系统新增错误日志功能",{"module":20644,"text":20647},"窗口创建时传 ws_str 作为额外数据","\u002Fversions\u002Fv1.5",{"title":20612,"description":55},"versions\u002Fv1.5","Deh4V7-UwJuTRdpB2cgKjdPQvwSw_Mkhlj6tHO4AgNM",{"id":20653,"title":20654,"body":20655,"description":55,"extension":374,"meta":20673,"navigation":173,"path":20704,"seo":20705,"stem":20706,"__hash__":20707},"content\u002Fversions\u002Fv2.0.md","eduwin v2.0 — 3D 渲染引擎",{"type":8,"value":20656,"toc":20670},[20657,20660,20665,20667],[11,20658,20654],{"id":20659},"eduwin-v20-3d-渲染引擎",[15,20661,20662],{},[18,20663,20664],{},"发布日期：2026-06-18",[46,20666,20503],{"id":20503},[18,20668,20669],{},"v2.0 是 eduwin 的重大版本，引入基于 DirectX 11 的 3D 渲染引擎，提供完整 3D 管线。",{"title":55,"searchDepth":88,"depth":88,"links":20671},[20672],{"id":20503,"depth":88,"text":20503},{"version":20674,"date":20675,"summary":20676,"items":20677},"v2.0","2026-06-18","3D 渲染引擎",[20678,20681,20683,20685,20687,20689,20691,20693,20695,20698,20700,20702],{"module":20679,"text":20680},"d3d","新增 D3D11 3D 渲染模块（eduwin_d3d），基于 DirectX 11 轻量封装",{"module":20679,"text":20682},"提供完整 3D 管线：世界矩阵、视图矩阵、投影矩阵、顶点\u002F像素着色器",{"module":20679,"text":20684},"内置 6 种基本几何体：dx_create_box\u002Fsphere\u002Fcylinder\u002Fpyramid\u002Fplane\u002Fmesh",{"module":20679,"text":20686},"dx_obj 对象系统封装位置\u002F旋转\u002F缩放",{"module":20679,"text":20688},"光照系统：环境光 + 方向光 + 点光源",{"module":20679,"text":20690},"可交互相机 dx_camera，支持鼠标拖拽旋转视角",{"module":20679,"text":20692},"新增 3 个 3D 示例 + 教程 + API 参考文档",{"module":20679,"text":20694},"独立的材质系统支持纹理贴图",{"module":20696,"text":20697},"docs","Hello eduwin 教程全面重写（目录结构、相对路径、编译器选型）",{"module":20696,"text":20699},"3D 渲染教程全面重写",{"module":20696,"text":20701},"修复断裂链接",{"module":20679,"text":20703},"移除 eduwin\u002Flinmath\u002F 目录，合并进 eduwin_d3d","\u002Fversions\u002Fv2.0",{"title":20654,"description":55},"versions\u002Fv2.0","nNeStMNA2kr-XF7X5DxNcwbadIVX5gyQYGQSGnPSoOk",{"id":20709,"title":20710,"body":20711,"description":55,"extension":374,"meta":20729,"navigation":173,"path":20756,"seo":20757,"stem":20758,"__hash__":20759},"content\u002Fversions\u002Fv2.1.md","eduwin v2.1 — 线程\u002F网络\u002FAI 模块",{"type":8,"value":20712,"toc":20726},[20713,20716,20721,20723],[11,20714,20710],{"id":20715},"eduwin-v21-线程网络ai-模块",[15,20717,20718],{},[18,20719,20720],{},"发布日期：2026-06-19",[46,20722,20503],{"id":20503},[18,20724,20725],{},"v2.1 新增三大模块：线程、网络通信、AI 大模型，eduwin 模块数扩展至 12 个。",{"title":55,"searchDepth":88,"depth":88,"links":20727},[20728],{"id":20503,"depth":88,"text":20503},{"version":20730,"date":20731,"summary":20732,"items":20733},"v2.1","2026-06-19","线程\u002F网络\u002FAI 模块",[20734,20736,20738,20740,20742,20744,20746,20748,20750,20752,20754],{"module":13080,"text":20735},"新增线程模块：thread_init\u002Fcleanup、thread_run\u002Fjoin\u002Fdetach、execute_on_main、mutex_*",{"module":13080,"text":20737},"execute_on_main() 封装 PostMessage + 隐藏窗口调度，无需子类化",{"module":13080,"text":20739},"新增 thread_demo 示例（3 个并发线程 + 互斥锁计数器）",{"module":11798,"text":20741},"TCP 客户端\u002F服务端、UDP 通信、HTTP GET\u002FPOST（HTTPS）、DNS 解析",{"module":11798,"text":20743},"新增 network_info_demo 示例",{"module":11788,"text":20745},"同步\u002F异步\u002F窗口异步三种调用方式",{"module":11788,"text":20747},"兼容 OpenAI API 格式（DeepSeek \u002F 智谱 \u002F 通义千问等）",{"module":11788,"text":20749},"新增 ai_chat_demo 示例",{"module":20696,"text":20751},"pub.py 新增三个模块的 @public_api 提取",{"module":20696,"text":20753},"新增 3 篇 API 文档 + 3 篇示例文档",{"module":20696,"text":20755},"更新日志迁移至独立版本文件结构","\u002Fversions\u002Fv2.1",{"title":20710,"description":55},"versions\u002Fv2.1","RLrQfByVYiPz0fNRS_Ew1zbJrNoy3ULyzN0OLRPXSbU",{"id":20761,"title":20762,"body":20763,"description":55,"extension":374,"meta":20781,"navigation":173,"path":20814,"seo":20815,"stem":20816,"__hash__":20817},"content\u002Fversions\u002Fv2.2.md","eduwin v2.2 — 3D 材质纹理 \u002F glTF \u002F API 命名简化",{"type":8,"value":20764,"toc":20778},[20765,20768,20773,20775],[11,20766,20762],{"id":20767},"eduwin-v22-3d-材质纹理-gltf-api-命名简化",[15,20769,20770],{},[18,20771,20772],{},"发布日期：2026-06-22",[46,20774,20503],{"id":20503},[18,20776,20777],{},"v2.2 进一步完善 3D 模块，新增材质纹理系统、glTF 模型加载，同时完成 API 命名简化（去除 eduwin_ 前缀）。",{"title":55,"searchDepth":88,"depth":88,"links":20779},[20780],{"id":20503,"depth":88,"text":20503},{"version":20782,"date":20783,"summary":20784,"items":20785},"v2.2","2026-06-22","3D 材质纹理 \u002F glTF \u002F API 命名简化",[20786,20788,20790,20792,20794,20796,20798,20800,20802,20804,20806,20808,20810,20812],{"module":20679,"text":20787},"新增网格系统 dx_mesh，封装 geometry + material + transform，自动计算世界矩阵",{"module":20679,"text":20789},"新增独立材质类型 dx_material，支持纹理贴图漫反射",{"module":20679,"text":20791},"dx_create_texture \u002F dx_create_texture_rgba \u002F dx_bind_texture 纹理系统",{"module":20679,"text":20793},"dx_load_gltf 加载 .glb \u002F .gltf 格式 3D 模型，支持 data URI 和外部 .bin",{"module":20679,"text":20795},"新增 d3d_material_demo（纹理材质）和 d3d_gltf_demo（模型加载）示例",{"module":20679,"text":20797},"Base64 解码和文件读取工具迁移至 eduwin_util，便于多模块复用",{"module":20173,"text":20799},"所有 API 去除 eduwin_ 前缀（eduwin_net_init → net_init，eduwin_ai_setup → ai_setup 等）",{"module":20173,"text":20801},"类型名简化：eduwin_tcp_t → tcp_t，eduwin_udp_t → udp_t 等",{"module":20173,"text":20803},"eduwin_ai 异步接口改用 thread_run，ai_chat_demo 同步更新",{"module":20173,"text":20805},"所有示例（network_info_demo、ai_chat_demo 等）同步去前缀",{"module":20696,"text":20807},"首页全面改版，新增网络\u002FAI\u002F线程特性展示，统计信息更新",{"module":20696,"text":20809},"补齐 3 篇 API 文档（网络、AI、线程）+ 3 篇示例文档",{"module":20696,"text":20811},"补充 Util API 文档（Base64 解码、read_bin_file_alloc）",{"module":20696,"text":20813},"更新日志改为 JSON 配置驱动，统一由 eduwin.json 管理","\u002Fversions\u002Fv2.2",{"title":20762,"description":55},"versions\u002Fv2.2","DjA7FnUxXokjmAXg20dyG0AQl_SmP60X7EDOEn96JUc",{"id":20819,"title":20820,"body":20821,"description":55,"extension":374,"meta":21340,"navigation":173,"path":21363,"seo":21364,"stem":21365,"__hash__":21366},"content\u002Fversions\u002Fv2.8.md","eduwin v2.8 — WebView2 浏览器引擎",{"type":8,"value":20822,"toc":21325},[20823,20826,20831,20833,20848,20852,20855,20905,20909,21015,21019,21029,21109,21113,21117,21138,21141,21153,21156,21196,21200,21231,21234,21288,21291,21294,21323],[11,20824,20820],{"id":20825},"eduwin-v28-webview2-浏览器引擎",[15,20827,20828],{},[18,20829,20830],{},"发布日期：2026-06-23",[46,20832,20503],{"id":20503},[18,20834,20835,20836,20839,20840,620,20842,20845,20846,75],{},"v2.8 主打",[69,20837,20838],{},"嵌入 WebView2 浏览器引擎","。从此 C 程序可以内嵌完整浏览器，JS ↔ C 双向通信，\n支持 Fixed Version（本地打包 Runtime，客户端零安装）和 Evergreen（系统共享 Runtime）两种模式。\n同时完成了 API 全面统合：所有模块头文件合并进 ",[22,20841,28],{},[22,20843,20844],{},"common.h"," 补全常用标准库，WebView2LoaderStatic 合并入 ",[22,20847,19059],{},[46,20849,20851],{"id":20850},"webview2-模块","WebView2 模块",[1020,20853,20854],{"id":20854},"核心能力",[4903,20856,20857,20865],{},[4906,20858,20859],{},[4909,20860,20861,20863],{},[4912,20862,17140],{},[4912,20864,4917],{},[4919,20866,20867,20875,20887,20897],{},[4909,20868,20869,20872],{},[4924,20870,20871],{},"双模式",[4924,20873,20874],{},"Fixed Version（本地 Runtime 打包，~280MB）和 Evergreen（系统 WebView2 Runtime）",[4909,20876,20877,20880],{},[4924,20878,20879],{},"双向通信",[4924,20881,10033,20882,20884,20885],{},[22,20883,10041],{}," 回调；C → JS：",[22,20886,9984],{},[4909,20888,20889,20891],{},[4924,20890,10363],{},[4924,20892,20893,20894,20896],{},"URL 导航 + ",[22,20895,11700],{}," 回调",[4909,20898,20899,20902],{},[4924,20900,20901],{},"COM 三层互操作",[4924,20903,20904],{},"Flat API → lpVtbl 接口表 → 手写 vtable 回调，纯 C 实现",[1020,20906,20908],{"id":20907},"新增-api","新增 API",[4903,20910,20911,20919],{},[4906,20912,20913],{},[4909,20914,20915,20917],{},[4912,20916,20174],{},[4912,20918,4917],{},[4919,20920,20921,20933,20943,20953,20963,20976,20986,20996,21006],{},[4909,20922,20923,20927],{},[4924,20924,20925],{},[22,20926,20193],{},[4924,20928,20929,20930,20932],{},"设置 Fixed Version Runtime 路径（",[22,20931,9726],{}," 前调用）",[4909,20934,20935,20940],{},[4924,20936,20937],{},[22,20938,20939],{},"webview_init(win, id, url, w, h)",[4924,20941,20942],{},"初始化 WebView2 控件",[4909,20944,20945,20950],{},[4924,20946,20947],{},[22,20948,20949],{},"webview_navigate(hwnd, url)",[4924,20951,20952],{},"导航到新 URL",[4909,20954,20955,20960],{},[4924,20956,20957],{},[22,20958,20959],{},"webview_post_message(hwnd, json)",[4924,20961,20962],{},"C → JS 发送消息",[4909,20964,20965,20973],{},[4924,20966,20967,735,20970],{},[22,20968,20969],{},"webview_go_back(hwnd)",[22,20971,20972],{},"webview_go_forward(hwnd)",[4924,20974,20975],{},"后退\u002F前进",[4909,20977,20978,20983],{},[4924,20979,20980],{},[22,20981,20982],{},"webview_reload(hwnd)",[4924,20984,20985],{},"刷新页面",[4909,20987,20988,20993],{},[4924,20989,20990],{},[22,20991,20992],{},"webview_set_zoom(hwnd, factor)",[4924,20994,20995],{},"设置缩放因子",[4909,20997,20998,21003],{},[4924,20999,21000],{},[22,21001,21002],{},"on_webview_message(wp, lp)",[4924,21004,21005],{},"JS → C 消息回调",[4909,21007,21008,21013],{},[4924,21009,21010],{},[22,21011,21012],{},"on_navigation_completed(wp, lp)",[4924,21014,10389],{},[1020,21016,21018],{"id":21017},"示例简易浏览器","示例：简易浏览器",[18,21020,21021,21024,21025,21028],{},[22,21022,21023],{},"webview_browser"," — 简易 WebView2 浏览器，带工具栏（后退\u002F前进\u002F刷新\u002F地址栏\u002FGo）和状态栏。\n",[22,21026,21027],{},"webview_bridge"," — WebView2 Bridge 演示，C ↔ JS 双向通信，外部 HTML\u002FCSS\u002FJS 独立文件加载。",[50,21030,21032],{"className":52,"code":21031,"language":54,"meta":55,"style":55},"#include \"eduwin.h\"\n\nint main(void) {\n    init_app();\n    webview_set_runtime_path(L\"eduwin\u002Fwebview2\u002Fruntime\");\n    HWND win = create_window(L\"Browser\", 1024, 700);\n\n    \u002F\u002F 创建工具栏...\n    HWND url_bar = create_textbox(win, L\"https:\u002F\u002Feduwin.net\", 80, 8, 700, 30);\n    HWND go_btn  = create_button(win, L\"Go\", 790, 8, 60, 30);\n\n    \u002F\u002F 创建 WebView\n    webview_init(win, 0, L\"https:\u002F\u002Feduwin.net\", 0, 50);\n    show_window(win);\n    run_app();\n    return 0;\n}\n",[22,21033,21034,21038,21042,21046,21050,21055,21060,21064,21069,21074,21079,21083,21088,21093,21097,21101,21105],{"__ignoreMap":55},[59,21035,21036],{"class":61,"line":62},[59,21037,5287],{},[59,21039,21040],{"class":61,"line":88},[59,21041,174],{"emptyLinePlaceholder":173},[59,21043,21044],{"class":61,"line":134},[59,21045,8185],{},[59,21047,21048],{"class":61,"line":140},[59,21049,9768],{},[59,21051,21052],{"class":61,"line":146},[59,21053,21054],{},"    webview_set_runtime_path(L\"eduwin\u002Fwebview2\u002Fruntime\");\n",[59,21056,21057],{"class":61,"line":281},[59,21058,21059],{},"    HWND win = create_window(L\"Browser\", 1024, 700);\n",[59,21061,21062],{"class":61,"line":286},[59,21063,174],{"emptyLinePlaceholder":173},[59,21065,21066],{"class":61,"line":292},[59,21067,21068],{},"    \u002F\u002F 创建工具栏...\n",[59,21070,21071],{"class":61,"line":600},[59,21072,21073],{},"    HWND url_bar = create_textbox(win, L\"https:\u002F\u002Feduwin.net\", 80, 8, 700, 30);\n",[59,21075,21076],{"class":61,"line":917},[59,21077,21078],{},"    HWND go_btn  = create_button(win, L\"Go\", 790, 8, 60, 30);\n",[59,21080,21081],{"class":61,"line":922},[59,21082,174],{"emptyLinePlaceholder":173},[59,21084,21085],{"class":61,"line":928},[59,21086,21087],{},"    \u002F\u002F 创建 WebView\n",[59,21089,21090],{"class":61,"line":933},[59,21091,21092],{},"    webview_init(win, 0, L\"https:\u002F\u002Feduwin.net\", 0, 50);\n",[59,21094,21095],{"class":61,"line":939},[59,21096,5365],{},[59,21098,21099],{"class":61,"line":1320},[59,21100,5370],{},[59,21102,21103],{"class":61,"line":1326},[59,21104,2087],{},[59,21106,21107],{"class":61,"line":1331},[59,21108,149],{},[46,21110,21112],{"id":21111},"api-统合","API 统合",[1020,21114,21116],{"id":21115},"commonh-补全","common.h 补全",[50,21118,21120],{"className":52,"code":21119,"language":54,"meta":55,"style":55},"#include \u003Cstring.h>   \u002F\u002F ← 新增\n#include \u003Cmath.h>     \u002F\u002F ← 新增\n",[22,21121,21122,21130],{"__ignoreMap":55},[59,21123,21124,21127],{"class":61,"line":62},[59,21125,21126],{},"#include \u003Cstring.h>",[59,21128,21129],{},"   \u002F\u002F ← 新增\n",[59,21131,21132,21135],{"class":61,"line":88},[59,21133,21134],{},"#include \u003Cmath.h>",[59,21136,21137],{},"     \u002F\u002F ← 新增\n",[1020,21139,21140],{"id":21140},"学生端只需一行",[50,21142,21144],{"className":52,"code":21143,"language":54,"meta":55,"style":55},"#include \"eduwin.h\"   \u002F\u002F 覆盖全部 15 个模块\n",[22,21145,21146],{"__ignoreMap":55},[59,21147,21148,21150],{"class":61,"line":62},[59,21149,19921],{},[59,21151,21152],{},"   \u002F\u002F 覆盖全部 15 个模块\n",[18,21154,21155],{},"不再需要：",[315,21157,21158,21164,21169,21174,21178,21182,21187,21191],{},[318,21159,21160,21161],{},"❌ ",[22,21162,21163],{},"#include \"eduwin_webview.h\"",[318,21165,21160,21166],{},[22,21167,21168],{},"#include \"eduwin_ai.h\"",[318,21170,21160,21171],{},[22,21172,21173],{},"#include \"eduwin_thread.h\"",[318,21175,21160,21176],{},[22,21177,19372],{},[318,21179,21160,21180],{},[22,21181,21126],{},[318,21183,21160,21184],{},[22,21185,21186],{},"#include \u003Cstdlib.h>",[318,21188,21160,21189],{},[22,21190,21134],{},[318,21192,21193,21194],{},"❌ 单独链接 ",[22,21195,10565],{},[1020,21197,21199],{"id":21198},"pubpy-改进","pub.py 改进",[315,21201,21202,21211,21221],{},[318,21203,21204,21207,21208],{},[22,21205,21206],{},"sqlite3.h"," 路径修正为 ",[22,21209,21210],{},"eduwin\u002Fsqlite\u002Fsqlite3.h",[318,21212,21213,21216,21217,21220],{},[22,21214,21215],{},"copy_framework()"," 不再复制任何多余 ",[22,21218,21219],{},".h"," 到 pub",[318,21222,21223,40,21225,21228,21229],{},[22,21224,9659],{},[22,21226,21227],{},"include\u002F"," 下只有一个 ",[22,21230,28],{},[46,21232,21233],{"id":21233},"模块统计",[4903,21235,21236,21249],{},[4906,21237,21238],{},[4909,21239,21240,21243,21246],{},[4912,21241,21242],{},"版本",[4912,21244,21245],{},"模块数",[4912,21247,21248],{},"新增模块",[4919,21250,21251,21261,21271],{},[4909,21252,21253,21255,21258],{},[4924,21254,20730],{},[4924,21256,21257],{},"12",[4924,21259,21260],{},"thread \u002F net \u002F ai",[4909,21262,21263,21265,21268],{},[4924,21264,20782],{},[4924,21266,21267],{},"13",[4924,21269,21270],{},"d3d 网格\u002F材质\u002FglTF",[4909,21272,21273,21278,21283],{},[4924,21274,21275],{},[69,21276,21277],{},"v2.8",[4924,21279,21280],{},[69,21281,21282],{},"15",[4924,21284,21285],{},[69,21286,21287],{},"webview + 其他增强",[46,21289,21290],{"id":21290},"升级指南",[18,21292,21293],{},"从 v2.2 升级到 v2.8：",[5250,21295,21296,21303,21312,21317],{},[318,21297,21298,21299,2730,21301],{},"替换 ",[22,21300,28],{},[22,21302,19059],{},[318,21304,21305,21306,21309,21310,1494],{},"删除 demo 中多余的 ",[22,21307,21308],{},"#include","（只留 ",[22,21311,19921],{},[318,21313,21314,21315,19085],{},"如需 WebView2 Fixed Version，拷贝 ",[22,21316,20294],{},[318,21318,21319,21320,1494],{},"编译链接相同（无需额外 ",[22,21321,21322],{},".lib",[362,21324,364],{},{"title":55,"searchDepth":88,"depth":88,"links":21326},[21327,21328,21333,21338,21339],{"id":20503,"depth":88,"text":20503},{"id":20850,"depth":88,"text":20851,"children":21329},[21330,21331,21332],{"id":20854,"depth":134,"text":20854},{"id":20907,"depth":134,"text":20908},{"id":21017,"depth":134,"text":21018},{"id":21111,"depth":88,"text":21112,"children":21334},[21335,21336,21337],{"id":21115,"depth":134,"text":21116},{"id":21140,"depth":134,"text":21140},{"id":21198,"depth":134,"text":21199},{"id":21233,"depth":88,"text":21233},{"id":21290,"depth":88,"text":21290},{"version":21277,"date":21341,"summary":21342,"items":21343},"2026-06-23","WebView2 浏览器引擎 \u002F Fixed Version 支持 \u002F API 全面统合",[21344,21346,21348,21350,21352,21354,21356,21358,21360],{"module":13728,"text":21345},"新增 eduwin_webview 模块，集成 Microsoft Edge WebView2 浏览器引擎",{"module":13728,"text":21347},"支持 Fixed Version（本地打包 Runtime）和 Evergreen（系统共享 Runtime）两种模式",{"module":13728,"text":21349},"JS ↔ C 双向通信（webview_post_message \u002F on_webview_message）",{"module":13728,"text":21351},"内置 webview_set_runtime_path API，运行时环境可配置",{"module":20173,"text":21353},"所有模块头文件合并入 eduwin.h，学生只需",{"module":20173,"text":21355},"common.h 补全 \u003Cstring.h> \u003Cmath.h>，所有 demo 清除冗余 include",{"module":20173,"text":21357},"WebView2LoaderStatic.lib 合并入 eduwin.lib",{"module":20696,"text":21359},"新增 WebView2 API 文档、示例文档和模块变更日志",{"module":21361,"text":21362},"samples","新增 WebView2 浏览器示例","\u002Fversions\u002Fv2.8",{"title":20820,"description":55},"versions\u002Fv2.8","1yaKRdIvdTxbJJE2gWs6XqAiNyiAaF1eWybNUuAw8lE",{"id":21368,"title":21369,"body":21370,"description":55,"extension":374,"meta":22066,"navigation":173,"path":22108,"seo":22109,"stem":22110,"__hash__":22111},"content\u002Fversions\u002Fv2.9.md","eduwin v2.9 — Windows 小助手 + 工具 API 增强",{"type":8,"value":21371,"toc":22043},[21372,21375,21380,21382,21389,21392,21399,21458,21462,21464,21484,21489,21491,21505,21508,21510,21524,21527,21529,21553,21556,21558,21570,21573,21575,21601,21604,21607,21667,21671,21820,21824,21828,21839,21843,21856,21868,21872,21880,21886,21889,21896,21937,21940,21961,21963,22010,22012,22015,22041],[11,21373,21369],{"id":21374},"eduwin-v29-windows-小助手-工具-api-增强",[15,21376,21377],{},[18,21378,21379],{},"发布日期：2026-06-24",[46,21381,20503],{"id":20503},[18,21383,21384,21385,21388],{},"v2.9 主打 ",[69,21386,21387],{},"Windows 小助手"," 示例和配套工具 API 完善。新增进程枚举、系统信息、注册表读写、环境变量、特殊文件夹等实用 API，同时重构了 WebView 示例结构、新增 Bridge 机制。",[46,21390,21387],{"id":21391},"windows-小助手",[18,21393,21394,21395,21398],{},"新增 ",[22,21396,21397],{},"samples\u002Fwin_helper\u002F"," 示例程序，7 个分类面板、完全使用封装 API、零原生 Windows API：",[4903,21400,21401,21410],{},[4906,21402,21403],{},[4909,21404,21405,21408],{},[4912,21406,21407],{},"面板",[4912,21409,11747],{},[4919,21411,21412,21418,21424,21430,21437,21444,21451],{},[4909,21413,21414,21416],{},[4924,21415,13909],{},[4924,21417,13912],{},[4909,21419,21420,21422],{},[4924,21421,13919],{},[4924,21423,13922],{},[4909,21425,21426,21428],{},[4924,21427,13929],{},[4924,21429,13932],{},[4909,21431,21432,21434],{},[4924,21433,13939],{},[4924,21435,21436],{},"桌面、文档、下载、图片、音乐、视频、Roaming AppData",[4909,21438,21439,21441],{},[4924,21440,8857],{},[4924,21442,21443],{},"枚举所有进程、PID 显示、一键终止",[4909,21445,21446,21448],{},[4924,21447,9096],{},[4924,21449,21450],{},"计算机名、用户名、OS 版本、CPU 核心\u002F型号、内存、Windows 目录",[4909,21452,21453,21455],{},[4924,21454,9303],{},[4924,21456,21457],{},"查看常见环境变量、修改变量值",[46,21459,21461],{"id":21460},"工具-api-增强","工具 API 增强",[1020,21463,8857],{"id":8857},[50,21465,21466],{"className":52,"code":8860,"language":54,"meta":55,"style":55},[22,21467,21468,21472,21476,21480],{"__ignoreMap":55},[59,21469,21470],{"class":61,"line":62},[59,21471,8867],{},[59,21473,21474],{"class":61,"line":88},[59,21475,8872],{},[59,21477,21478],{"class":61,"line":134},[59,21479,8877],{},[59,21481,21482],{"class":61,"line":140},[59,21483,8882],{},[18,21485,21394,21486,21488],{},[22,21487,8919],{}," 按 PID 终止进程。",[1020,21490,9007],{"id":9007},[50,21492,21494],{"className":52,"code":21493,"language":54,"meta":55,"style":55},"typedef struct { DWORD pid; WCHAR name[260]; } process_entry_t;\nint enum_processes(process_entry_t **out, int *count);\n",[22,21495,21496,21501],{"__ignoreMap":55},[59,21497,21498],{"class":61,"line":62},[59,21499,21500],{},"typedef struct { DWORD pid; WCHAR name[260]; } process_entry_t;\n",[59,21502,21503],{"class":61,"line":88},[59,21504,9040],{},[18,21506,21507],{},"枚举所有系统进程，动态分配数组。",[1020,21509,9096],{"id":9096},[50,21511,21513],{"className":52,"code":21512,"language":54,"meta":55,"style":55},"typedef struct { \u002F* 计算机名\u002F用户名\u002FOS版本\u002FCPU核心\u002FCPU型号\u002F内存\u002FWindows目录 *\u002F } sys_info_t;\nint get_sys_info(sys_info_t *info);\n",[22,21514,21515,21520],{"__ignoreMap":55},[59,21516,21517],{"class":61,"line":62},[59,21518,21519],{},"typedef struct { \u002F* 计算机名\u002F用户名\u002FOS版本\u002FCPU核心\u002FCPU型号\u002F内存\u002FWindows目录 *\u002F } sys_info_t;\n",[59,21521,21522],{"class":61,"line":88},[59,21523,9159],{},[18,21525,21526],{},"获取完整的系统信息。",[1020,21528,9212],{"id":9212},[50,21530,21531],{"className":52,"code":9215,"language":54,"meta":55,"style":55},[22,21532,21533,21537,21541,21545,21549],{"__ignoreMap":55},[59,21534,21535],{"class":61,"line":62},[59,21536,9222],{},[59,21538,21539],{"class":61,"line":88},[59,21540,9227],{},[59,21542,21543],{"class":61,"line":134},[59,21544,9232],{},[59,21546,21547],{"class":61,"line":140},[59,21548,9237],{},[59,21550,21551],{"class":61,"line":146},[59,21552,9242],{},[18,21554,21555],{},"注册表完整读写支持，写操作自动创建不存在的键。",[1020,21557,9303],{"id":9303},[50,21559,21560],{"className":52,"code":9306,"language":54,"meta":55,"style":55},[22,21561,21562,21566],{"__ignoreMap":55},[59,21563,21564],{"class":61,"line":62},[59,21565,9313],{},[59,21567,21568],{"class":61,"line":88},[59,21569,9318],{},[18,21571,21572],{},"读取和设置环境变量。",[1020,21574,9355],{"id":9355},[50,21576,21578],{"className":52,"code":21577,"language":54,"meta":55,"style":55},"#define FOLDER_DESKTOP      0\n#define FOLDER_DOCUMENTS    1\n#define FOLDER_DOWNLOADS    2\n\u002F\u002F ...\nint get_special_folder(int folder_id, WCHAR *buf, int size);\n",[22,21579,21580,21584,21588,21592,21597],{"__ignoreMap":55},[59,21581,21582],{"class":61,"line":62},[59,21583,9365],{},[59,21585,21586],{"class":61,"line":88},[59,21587,9370],{},[59,21589,21590],{"class":61,"line":134},[59,21591,9375],{},[59,21593,21594],{"class":61,"line":140},[59,21595,21596],{},"\u002F\u002F ...\n",[59,21598,21599],{"class":61,"line":146},[59,21600,9419],{},[18,21602,21603],{},"获取系统特殊文件夹路径（桌面、文档、下载等 10 种）。",[1020,21605,21606],{"id":21606},"其他工具增强",[4903,21608,21609,21617],{},[4906,21610,21611],{},[4909,21612,21613,21615],{},[4912,21614,20174],{},[4912,21616,4917],{},[4919,21618,21619,21632,21642,21655],{},[4909,21620,21621,21626],{},[4924,21622,21623],{},[22,21624,21625],{},"str_format_a(buf, size, fmt, ...)",[4924,21627,21628,21629,21631],{},"UTF-8 版本格式化，与 ",[22,21630,6058],{},"（宽串）对称",[4909,21633,21634,21639],{},[4924,21635,21636],{},[22,21637,21638],{},"clipboard_copy(text)",[4924,21640,21641],{},"复制文本到系统剪贴板",[4909,21643,21644,21649],{},[4924,21645,21646],{},[22,21647,21648],{},"log_warning(text)",[4924,21650,21651,21652,21654],{},"带 ",[22,21653,9486],{}," 标签的日志",[4909,21656,21657,21662],{},[4924,21658,21659],{},[22,21660,21661],{},"log_error(title)",[4924,21663,21651,21664,21666],{},[22,21665,9489],{}," 标签+自动追加 GetLastError 描述的日志",[46,21668,21670],{"id":21669},"控件-api-增强","控件 API 增强",[4903,21672,21673,21684],{},[4906,21674,21675],{},[4909,21676,21677,21679,21682],{},[4912,21678,20174],{},[4912,21680,21681],{},"控件",[4912,21683,4917],{},[4919,21685,21686,21697,21708,21720,21731,21743,21754,21765,21776,21787,21798,21809],{},[4909,21687,21688,21692,21694],{},[4924,21689,21690],{},[22,21691,14009],{},[4924,21693,3189],{},[4924,21695,21696],{},"清空所有行",[4909,21698,21699,21703,21705],{},[4924,21700,21701],{},[22,21702,14012],{},[4924,21704,3189],{},[4924,21706,21707],{},"读取指定行列文本",[4909,21709,21710,21715,21717],{},[4924,21711,21712],{},[22,21713,21714],{},"on_listview_dblclick",[4924,21716,3189],{},[4924,21718,21719],{},"双击行回调",[4909,21721,21722,21726,21728],{},[4924,21723,21724],{},[22,21725,3356],{},[4924,21727,3315],{},[4924,21729,21730],{},"标签切换回调",[4909,21732,21733,21737,21740],{},[4924,21734,21735],{},[22,21736,2300],{},[4924,21738,21739],{},"通用",[4924,21741,21742],{},"控件可见性检测",[4909,21744,21745,21749,21751],{},[4924,21746,21747],{},[22,21748,3122],{},[4924,21750,3056],{},[4924,21752,21753],{},"添加子节点",[4909,21755,21756,21760,21762],{},[4924,21757,21758],{},[22,21759,3128],{},[4924,21761,3056],{},[4924,21763,21764],{},"获取选中节点句柄",[4909,21766,21767,21771,21773],{},[4924,21768,21769],{},[22,21770,3137],{},[4924,21772,3056],{},[4924,21774,21775],{},"获取选中节点文本",[4909,21777,21778,21782,21784],{},[4924,21779,21780],{},[22,21781,3143],{},[4924,21783,3056],{},[4924,21785,21786],{},"删除节点",[4909,21788,21789,21793,21795],{},[4924,21790,21791],{},[22,21792,3149],{},[4924,21794,3056],{},[4924,21796,21797],{},"修改节点文本",[4909,21799,21800,21804,21806],{},[4924,21801,21802],{},[22,21803,3155],{},[4924,21805,3056],{},[4924,21807,21808],{},"展开节点",[4909,21810,21811,21815,21817],{},[4924,21812,21813],{},[22,21814,3158],{},[4924,21816,3056],{},[4924,21818,21819],{},"折叠节点",[46,21821,21823],{"id":21822},"webview2-改进","WebView2 改进",[1020,21825,21827],{"id":21826},"newwindowrequested-链接在内部打开","NewWindowRequested — 链接在内部打开",[18,21829,21830,21831,21834,21835,21838],{},"注册 ",[22,21832,21833],{},"add_NewWindowRequested"," 事件处理，拦截 ",[22,21836,21837],{},"target=\"_blank\""," 等打开新窗口的链接，在当前 WebView 内导航，阻止弹出系统浏览器。",[1020,21840,21842],{"id":21841},"bridge-双向调用","Bridge 双向调用",[50,21844,21846],{"className":52,"code":21845,"language":54,"meta":55,"style":55},"void webview_bridge_init(HWND hwv);\nvoid webview_bridge_register(const char *func_name, webview_bridge_fn cb, void *data);\n",[22,21847,21848,21852],{"__ignoreMap":55},[59,21849,21850],{"class":61,"line":62},[59,21851,10218],{},[59,21853,21854],{"class":61,"line":88},[59,21855,10249],{},[18,21857,21858,21859,21861,21862,21864,21865,21867],{},"JS 侧自动注入 ",[22,21860,10214],{}," 对象，",[22,21863,13610],{}," 接收消息、",[22,21866,13614],{}," 调用 C 函数，比原始 postMessage 更简洁。",[1020,21869,21871],{"id":21870},"virtual-host","Virtual Host",[50,21873,21874],{"className":52,"code":10449,"language":54,"meta":55,"style":55},[22,21875,21876],{"__ignoreMap":55},[59,21877,21878],{"class":61,"line":62},[59,21879,10449],{},[18,21881,21882,21883,21885],{},"将本地文件夹映射为虚拟域名，正常加载外部 HTML\u002FCSS\u002FJS，绕过 ",[22,21884,10445],{}," 限制。",[1020,21887,21888],{"id":21888},"示例拆分",[18,21890,21891,21892,21895],{},"原 ",[22,21893,21894],{},"webview_demo"," 拆分为两个独立示例：",[4903,21897,21898,21909],{},[4906,21899,21900],{},[4909,21901,21902,21904,21907],{},[4912,21903,1890],{},[4912,21905,21906],{},"路径",[4912,21908,4917],{},[4919,21910,21911,21924],{},[4909,21912,21913,21917,21921],{},[4924,21914,21915],{},[22,21916,21023],{},[4924,21918,21919],{},[22,21920,10518],{},[4924,21922,21923],{},"纯浏览器，无 bridge",[4909,21925,21926,21930,21934],{},[4924,21927,21928],{},[22,21929,21027],{},[4924,21931,21932],{},[22,21933,10524],{},[4924,21935,21936],{},"Bridge 演示 + 独立 HTML\u002FCSS\u002FJS",[46,21938,21939],{"id":21939},"其他改进",[315,21941,21942,21949,21955],{},[318,21943,21944,21945,21948],{},"Groupbox 内 Label 灰色背景修复（",[22,21946,21947],{},"WM_CTLCOLORSTATIC"," 子类化）",[318,21950,21951,21952,21954],{},"Tab 切换使用全局链表分发，支持 ",[22,21953,3356],{}," 统一注册",[318,21956,21957,21958,21960],{},"各 ",[22,21959,10534],{}," 链接参数统一化",[46,21962,21233],{"id":21233},[4903,21964,21965,21975],{},[4906,21966,21967],{},[4909,21968,21969,21971,21973],{},[4912,21970,21242],{},[4912,21972,21245],{},[4912,21974,21248],{},[4919,21976,21977,21985,21994],{},[4909,21978,21979,21981,21983],{},[4924,21980,20782],{},[4924,21982,21267],{},[4924,21984,21270],{},[4909,21986,21987,21989,21991],{},[4924,21988,21277],{},[4924,21990,21282],{},[4924,21992,21993],{},"webview + API 统合",[4909,21995,21996,22001,22005],{},[4924,21997,21998],{},[69,21999,22000],{},"v2.9",[4924,22002,22003],{},[69,22004,21282],{},[4924,22006,22007],{},[69,22008,22009],{},"工具 API 大幅增强 + WebView 改进",[46,22011,21290],{"id":21290},[18,22013,22014],{},"从 v2.8 升级到 v2.9：",[5250,22016,22017,22023,22028],{},[318,22018,21298,22019,2730,22021],{},[22,22020,28],{},[22,22022,19059],{},[318,22024,22025,22026],{},"WebView2 新 API（bridge、virtual host）需更新 ",[22,22027,19059],{},[318,22029,22030,22031,22034,22035,2338,22038],{},"旧 ",[22,22032,22033],{},"samples\u002Fwebview_demo\u002F"," 已删除，迁移到 ",[22,22036,22037],{},"webview_browser\u002F",[22,22039,22040],{},"webview_bridge\u002F",[362,22042,364],{},{"title":55,"searchDepth":88,"depth":88,"links":22044},[22045,22046,22047,22056,22057,22063,22064,22065],{"id":20503,"depth":88,"text":20503},{"id":21391,"depth":88,"text":21387},{"id":21460,"depth":88,"text":21461,"children":22048},[22049,22050,22051,22052,22053,22054,22055],{"id":8857,"depth":134,"text":8857},{"id":9007,"depth":134,"text":9007},{"id":9096,"depth":134,"text":9096},{"id":9212,"depth":134,"text":9212},{"id":9303,"depth":134,"text":9303},{"id":9355,"depth":134,"text":9355},{"id":21606,"depth":134,"text":21606},{"id":21669,"depth":88,"text":21670},{"id":21822,"depth":88,"text":21823,"children":22058},[22059,22060,22061,22062],{"id":21826,"depth":134,"text":21827},{"id":21841,"depth":134,"text":21842},{"id":21870,"depth":134,"text":21871},{"id":21888,"depth":134,"text":21888},{"id":21939,"depth":88,"text":21939},{"id":21233,"depth":88,"text":21233},{"id":21290,"depth":88,"text":21290},{"version":22000,"date":22067,"summary":22068,"items":22069},"2026-06-24","Windows 小助手 \u002F 工具 API 大幅增强 \u002F WebView Bridge \u002F Virtual Host",[22070,22072,22074,22076,22078,22080,22082,22084,22086,22088,22090,22092,22094,22096,22098,22100,22102,22104,22106],{"module":21361,"text":22071},"新增 win_helper —— Windows 小助手，7 个 Tab 面板：系统工具\u002F管理工具\u002F磁盘工具\u002F快捷文件夹\u002F进程管理\u002F系统信息\u002F环境变量",{"module":20644,"text":22073},"新增 kill_process_pid — 按 PID 终止进程",{"module":20644,"text":22075},"新增 enum_processes — 枚举所有系统进程",{"module":20644,"text":22077},"新增 get_sys_info — 获取系统信息（计算机名\u002FCPU\u002F内存\u002FOS 等）",{"module":20644,"text":22079},"新增 reg_read_str\u002Freg_read_dword\u002Freg_write_str\u002Freg_write_dword\u002Freg_delete_value — 注册表完整读写",{"module":20644,"text":22081},"新增 get_env\u002Fset_env — 环境变量读写",{"module":20644,"text":22083},"新增 get_special_folder — 10 种系统特殊文件夹路径获取",{"module":20644,"text":22085},"新增 str_format_a —— UTF-8 字符串格式化",{"module":20644,"text":22087},"新增 clipboard_copy —— 复制文本到系统剪贴板",{"module":20644,"text":22089},"新增 log_warning\u002Flog_error —— 带标签和错误码的日志",{"module":6711,"text":22091},"新增 listview_delete_all\u002Flistview_get_text\u002Fon_listview_dblclick —— 列表视图增强",{"module":6711,"text":22093},"新增 on_tabbar_select —— 标签页切换回调支持",{"module":6711,"text":22095},"新增 is_visible —— 控件可见性检测",{"module":6711,"text":22097},"新增 treeview_add_child\u002Fselected\u002Fselected_text\u002Fdelete\u002Fset_text\u002Fexpand\u002Fcollapse —— 树状列表完整功能",{"module":13728,"text":22099},"新增 NewWindowRequested 事件处理，拦截链接在 WebView 内打开",{"module":13728,"text":22101},"新增 Bridge 机制：webview_bridge_init\u002Fbridge_register，JS 侧自动注入 eduwin 对象",{"module":13728,"text":22103},"新增 Virtual Host：webview_set_virtual_host，映射本地文件夹为虚拟域名",{"module":21361,"text":22105},"webview_demo 拆分为 webview_browser 和 webview_bridge 两个独立示例",{"module":20696,"text":22107},"更新 WebView API 文档（Bridge\u002FVirtual Host\u002F导航完成回调），补齐 ctrl\u002Futil API 文档","\u002Fversions\u002Fv2.9",{"title":21369,"description":55},"versions\u002Fv2.9","j4PeedKll08_DFzuzDY5GLq2Rp5BJdK4mQWVdC-axck",{"id":22113,"title":22114,"body":22115,"description":22122,"extension":374,"meta":22331,"navigation":173,"path":22361,"seo":22362,"stem":22363,"__hash__":22364},"content\u002Fversions\u002Fv3.0.md","v3.0 — 命令行参数解析 + 日志面板 + WebSocket Hub",{"type":8,"value":22116,"toc":22318},[22117,22120,22123,22125,22129,22140,22146,22149,22169,22173,22176,22201,22204,22208,22214,22220,22223,22226,22228,22233,22271,22274,22276,22279,22283,22303,22306,22316],[11,22118,22114],{"id":22119},"v30-命令行参数解析-日志面板-websocket-hub",[18,22121,22122],{},"eduwin v3.0 引入三个新模块与两个新示例，显著拓展了非 GUI 场景的能力边界。",[46,22124,21248],{"id":21248},[1020,22126,22128],{"id":22127},"命令行参数解析eduwin_cmd","命令行参数解析（eduwin_cmd）",[18,22130,22131,22133,22134,464,22137,22139],{},[22,22132,1981],{}," 自动调用 ",[22,22135,22136],{},"GetCommandLineW",[22,22138,1886],{}," 解析命令行，支持标准化参数格式：",[50,22141,22144],{"className":22142,"code":22143,"language":358},[356],"ai_browser_demo.exe --headless --port 9090 --url https:\u002F\u002Fwww.bing.com\n",[22,22145,22143],{"__ignoreMap":55},[18,22147,22148],{},"查询 API 简洁统一：",[315,22150,22151,22157,22163],{},[318,22152,22153,22156],{},[22,22154,22155],{},"cmd_is(L\"--headless\")"," — 检测开关参数",[318,22158,22159,22162],{},[22,22160,22161],{},"cmd_get(L\"--url\")"," — 获取值参数",[318,22164,22165,22168],{},[22,22166,22167],{},"cmd_get_def(L\"--port\", L\"9099\")"," — 带默认值获取",[1020,22170,22172],{"id":22171},"日志面板eduwin_logview","日志面板（eduwin_logview）",[18,22174,22175],{},"基于 ListView 封装的日志显示控件，三列布局（时间 \u002F 级别 \u002F 消息）：",[50,22177,22179],{"className":52,"code":22178,"language":54,"meta":55,"style":55},"HWND lv = create_logview(parent, x, y, w, h);\nlogview_info(lv, L\"应用已启动，端口 %d\", port);\nlogview_warn(lv, L\"连接超时\");\nlogview_error(lv, L\"网络错误\");\n",[22,22180,22181,22186,22191,22196],{"__ignoreMap":55},[59,22182,22183],{"class":61,"line":62},[59,22184,22185],{},"HWND lv = create_logview(parent, x, y, w, h);\n",[59,22187,22188],{"class":61,"line":88},[59,22189,22190],{},"logview_info(lv, L\"应用已启动，端口 %d\", port);\n",[59,22192,22193],{"class":61,"line":134},[59,22194,22195],{},"logview_warn(lv, L\"连接超时\");\n",[59,22197,22198],{"class":61,"line":140},[59,22199,22200],{},"logview_error(lv, L\"网络错误\");\n",[18,22202,22203],{},"特性：自动时间戳、行数限制（默认 500）、超限删最旧、自动滚底、双击复制。",[1020,22205,22207],{"id":22206},"websocket-hubeduwin_net","WebSocket Hub（eduwin_net）",[18,22209,22210,22211,22213],{},"在原有 TCP\u002FUDP\u002FHTTP\u002FDNS 基础上，新增 ",[22,22212,11599],{}," pub\u002Fsub 消息枢纽：",[50,22215,22218],{"className":22216,"code":22217,"language":358},[356],"ws_hub_create(port) → accept → subscribe(topic) → publish → poll → recv\n",[22,22219,22217],{"__ignoreMap":55},[18,22221,22222],{},"单线程无锁设计，最大 64 连接、16 KB 消息，纯 C 实现（零外部依赖）。",[46,22224,22225],{"id":22225},"新示例",[1020,22227,10684],{"id":10684},[18,22229,22230,22232],{},[69,22231,11108],{}," 是一个综合 demo，将框架 6 个模块集成到一个程序中：",[315,22234,22235,22240,22245,22251,22256,22261,22266],{},[318,22236,22237,22239],{},[22,22238,10719],{}," — 命令行参数解析",[318,22241,22242,22244],{},[22,22243,10738],{}," — WebView2 浏览器",[318,22246,22247,22250],{},[22,22248,22249],{},"eduwin_net (ws_hub)"," — WebSocket pub\u002Fsub 枢纽",[318,22252,22253,22255],{},[22,22254,10758],{}," — 右侧日志面板",[318,22257,22258,22260],{},[22,22259,39],{}," — 后台 WS 服务器线程",[318,22262,22263,22265],{},[22,22264,10709],{}," — 窗口\u002F事件\u002Fheadless 模式",[318,22267,22268,22270],{},[22,22269,11642],{}," — 隐形\"傀儡\"窗口",[18,22272,22273],{},"支持 --headless 隐身模式，适用于后台 AI Agent 场景。",[1020,22275,11939],{"id":11939},[18,22277,22278],{},"简洁的命令行参数解析演示，窗口展示各参数的检测状态和值。",[46,22280,22282],{"id":22281},"api-文档","API 文档",[18,22284,21394,22285,2730,22288,22291,22292,22295,22296,22299,22300,22302],{},[22,22286,22287],{},"eduwin-cmd-api.md",[22,22289,22290],{},"eduwin-logview-api.md","，\n",[22,22293,22294],{},"eduwin-net-api.md"," 补充 WebSocket Hub 完整章节，\n",[22,22297,22298],{},"eduwin-app-api.md"," 补充 ",[22,22301,11642],{}," 说明。",[46,22304,22305],{"id":22305},"版本号",[18,22307,22308,22309,17574,22312,22315],{},"v3.0 标志着 eduwin 在",[69,22310,22311],{},"工具链",[69,22313,22314],{},"后端能力","方向的重大扩展——框架不再仅仅是 GUI 控件库，开始提供命令行工具、日志框架、网络服务等基础设施。",[362,22317,364],{},{"title":55,"searchDepth":88,"depth":88,"links":22319},[22320,22325,22329,22330],{"id":21248,"depth":88,"text":21248,"children":22321},[22322,22323,22324],{"id":22127,"depth":134,"text":22128},{"id":22171,"depth":134,"text":22172},{"id":22206,"depth":134,"text":22207},{"id":22225,"depth":88,"text":22225,"children":22326},[22327,22328],{"id":10684,"depth":134,"text":10684},{"id":11939,"depth":134,"text":11939},{"id":22281,"depth":88,"text":22282},{"id":22305,"depth":88,"text":22305},{"version":22332,"date":22333,"summary":22334,"items":22335},"v3.0","2026-06-26","命令行参数解析、日志面板组件、WebSocket Hub、AI Browser 示例",[22336,22339,22341,22343,22345,22347,22349,22351,22353,22355,22357,22359],{"module":22337,"text":22338},"cmd","新增 eduwin_cmd 模块，支持 --key value \u002F --flag \u002F --help 等命令行参数格式",{"module":6711,"text":22340},"新增 eduwin_logview 日志面板控件（基于 ListView，三列：时间\u002F级别\u002F消息，自动滚动与行数限制）",{"module":11798,"text":22342},"新增 ws_hub 通用 pub\u002Fsub WebSocket 消息枢纽，支持多客户端话题订阅\u002F发布",{"module":20516,"text":22344},"新增 create_hidden_window 傀儡窗口函数，用于 headless 模式下 WebView2 挂载",{"module":21361,"text":22346},"新增 ai_browser_demo 示例：集成 WebView2 + WS Hub + LogView + Cmd + Thread 的综合 AI 浏览器入口",{"module":21361,"text":22348},"新增 cmd_demo 示例：命令行参数解析功能演示",{"module":20173,"text":22350},"新增 eduwin-cmd-api 和 eduwin-logview-api 文档",{"module":20173,"text":22352},"补充 eduwin-net-api 的 WebSocket Hub 章节",{"module":20173,"text":22354},"补充 eduwin-app-api 的 create_hidden_window 说明",{"module":20696,"text":22356},"文档中心新增 17 个 API 模块 + 23 个示例索引",{"module":20644,"text":22358},"所有 samples build.bat 统一补充 advapi32.lib 链接",{"module":13728,"text":22360},"ai_browser_demo 使用 SetVirtualHostNameToFolderMapping 加载本地 HTML\u002FJS 资源","\u002Fversions\u002Fv3.0",{"title":22114,"description":22122},"versions\u002Fv3.0","Q6XNOUZLN0nsVbEO5_rSueiAag190dye52EIieABXcg",1782488679736]