第一版为纯导出库:用窗口、默认代码视图。打开工程不会进设计画布。
写法:先写函数,再导出
导出名默认可与 Y++ 函数名相同(含中文)。也可用别名让 PE 导出为 ASCII,Y++ 仍用中文函数名:
' —— math.yc ——
函数 相加(整数 a, 整数 b) 整数 {
返回(a + b)
}
' —— exports.yex ——
导出 "相加" ' 导出名 = 函数名
导出 "Add" = 相加 ' PE 导出名 Add,函数仍是 相加
导出 ["相减"] ' 批量(仅同名)
也支持不带引号:导出 相加。
调用约定
项目设置 → DLL 调用约定:stdcall(默认,WinAPI / 习惯)或 cdecl(多数 C 库)。改后须重编 DLL 与宿主。
生成的 .yp 会在别名后写上约定(stdcall / cdecl),宿主 thunk 按该约定生成。
仅 type=dll 工程可用 导出。
DllMain 钩子
函数 DLL.加载 {
' LoadLibrary 成功、本 DLL 初始化时(PROCESS_ATTACH)
}
函数 DLL.卸载 {
' FreeLibrary / 进程退出时(PROCESS_DETACH)
}
新建 DLL 工程会自带 main.yc + exports.yex。菜单「添加新文件 → 导出清单 (.yex)」可再建。
右侧「导出」面板
打开 DLL 工程后,右侧栏有 导出 页:
| 操作 | 作用 |
|---|---|
| 刷新 | 按当前 .yc / .yex(含未保存编辑器)重扫导出表 |
| 勾选 | 多选后生成 .yp / 复制片段只含勾选项 |
| 双击行 | 跳转到函数定义源码 |
| Y++片段 | 复制 加载DLL / 取导出函数 / 调用函数Ex |
| C#片段 | 复制 [DllImport](含 CallingConvention) |
| 生成 .yp | 写入本工程 源代码/{工程名}.yp(带约定),并复制全文 |
| 调用 | 填参后跑同级宿主,无信息框;结果写回面板(临时文件) |
| 一键宿主 | 创建/更新同级 {工程名}_host,弹出信息框后自动关闭 |
| 读PE | 解析发布目录 .dll 的导出表(无需 dumpbin) |
| 表右键 | 跳转定义 / 复制导出名或「导出」行 / 片段 / 调用 / 宿主 / 读PE |
| .yex 中 Ctrl+E | 一键插入尚未导出的函数(导出 "函数名") |
自动 .yp 示例
@"MyLib.dll"
整数 相加(整数 a, 整数 b) 相加 stdcall
整数 Add(整数 a, 整数 b) 相加 stdcall ' 若写了 导出 "Add" = 相加
中文导出名经 DLL 内 YplDll_ResolveExport 解析。生成 .yp 时:
- 简单整数/文本:原样声明
- 字节集:写成
长整数 xxx_ptr, 整数 xxx_len(对齐导出 C-ABI) &结构/ 数组 /子程序指针:按.yp语法写出,不再整行注释跳过- 一键「调用」测试仍仅支持无字节集/回调/数组的简单签名
同级测试宿主
父目录/
MyLib/ ← DLL 工程
MyLib_host/ ← 一键测试 / 调用测试创建的窗口工程
F5 编译 DLL 不会自动弹宿主。若尚无 .dll 会先编译本 DLL 再继续测试。
依赖清单 / 版本资源
- 项目设置 → DLL 依赖清单:每行一个同级工程名或
xxx.dll;宿主 F5 / 发布时只拉清单中的项,以及源码 /.yp里出现的"xxx.dll"。不会把同目录下其它无关 DLL 工程产物一并拷来。 - VERSIONINFO:勾选「编译时写入」后,
.dll使用VFT_DLL;资源管理器「属性 → 详细信息」可见版本字段。
导出 C-ABI(给 C# / C 用)
Y++ DLL 的 extern "C" 导出层按稳定 C 约定编组(内部函数仍是 C++):
| Y++ | 导出 ABI | 说明 |
|---|---|---|
| 文本 入参 | LPCWSTR / const wchar_t* |
空指针按 L"" |
| 文本 返回 | LPCWSTR |
TLS 环形缓冲驻留,调用方须立即拷贝 |
| 字节集 入参 | const unsigned char *ptr, int len |
两参数;不要只传指针 |
| 字节集 返回 | const unsigned char * + 末尾 int *out_len |
TLS 驻留;out_len 写长度 |
| 整数/逻辑/小数 | 与内部一致 | long long / int / double 等 |
「一键调用」暂只覆盖无字节集的简单签名;含字节集的导出请用 C# 片段或手写 .yp(长整数+整数)。
加载DLL 对磁盘路径会先读 PE 头做 x86/x64 位数预检,不一致时 DLL_取错误() 有明确提示。
结构体 / 数组 / 字节集(与 .yt、.yp 对齐)
写导出或 .yp 声明时,按下列约定可少踩坑:
| 场景 | 写法 | 说明 |
|---|---|---|
| 简单整数/逻辑/文本 | 整数 a、文本 s |
与普通函数一致 |
.yt 结构体指针 |
.yp:&矩形 框;Y++ 内先定义类型 |
嵌套 / 定长数组≤256 / 字节集入参已编组;见 dll-pointers.md §3.5 |
| 字节缓冲 | 字节 缓冲[] 或 字节集 |
传 BYTE*;空则 nullptr |
| 整数数组 | 整数 表[] |
元素为 64 位;若 DLL 要 32 位 int*,用 DLL_整数指针32 |
| 回调 | 子程序指针 cb |
不要加 & 类型;约定与工程 stdcall/cdecl 一致 |
更细的指针边界见 dll-pointers.md、dllapi.md。
宿主调用
长整数 p = 取导出函数(模块, "相加") ' 或 "Add"
整数 和 = 到整数(调用函数Ex(p, 2, 3))
或 .yp 别名。
产物与分发
- F5 / 编译:只编译不启动 →
发布/{arch}/{cfg}/项目名.dll(及同名.lib) - 发布打包:选目标文件夹 →「开始打包」;拷贝
.dll(及.lib)。DLL 工程不生成安装包。 - Debug 启动位置(窗口工程 / 测试宿主):项目设置可勾选 「Debug 也从发布/ 目录启动」,少纠缠
%TEMP%\ystudio-build;默认仍从临时缓存启动以避杀软二次扫描。
宿主怎么找到 DLL?
宿主 F5 时按「依赖清单 + 源码/.yp 引用」拉取同级已发布的 xxx.dll;一键宿主会自动把本 DLL 工程名写入宿主的依赖清单。
长整数 模块 = 加载DLL(取程序目录() + "\\你的库.dll")
中文导出名由 YplDll_ResolveExport 解析;ASCII 别名可直接 GetProcAddress。