迁移建议:如果现有系统中有 User Exit 或直接修改的代码,建议在版本升级时逐步迁移到 Extension。
三、Extension 类型详解
3.1 四种 Extension 类型
类型
执行时机
典型用途
适用场景
Before DAL
DAL 操作(insert/update/delete)之前
字段校验、默认值设置、数据补全
需在数据保存前介入
After DAL
DAL 操作之后
联动更新其他表、记录日志、发送通知
需在数据保存后处理
Before Logic
标准 UI 逻辑之前
拦截/修改输入参数、条件性阻止操作
需在标准逻辑执行前控制流程
After Logic
标准 UI 逻辑之后
补充处理、显示额外信息
需在标准逻辑执行后追加处理
3.2 类型选择指南
需要校验/修改数据? ─── 是 ─── Before DAL
需要保存后联动? ─── 是 ─── After DAL
需要控制 UI 流程? ─── 是 ─── Before Logic
需要追加 UI 逻辑? ─── 是 ─── After Logic
3.3 执行顺序
Session UI 操作
↓
Before Logic Extension
↓
标准 UI 逻辑(choice / field sections)
↓
After Logic Extension
↓
Before DAL Extension(如涉及 DAL 操作)
↓
DAL 执行(含 Hook 链)
↓
After DAL Extension
↓
事务提交
四、Extension DLL 创建流程
4.1 命名约定
项目
约定
DLL 名称
标准格式为 <module>dllcustom(如 tdslsdllcustom)
也可自定义
<prefix>dll<suffix>(如 myextdll001)
4.2 完整创建步骤
Step 1:创建 Extension DLL 源文件
| Extension: ZSSO001
| 用途: 销售订单行保存后自动更新订单备注
| DLL: tdslsdllcustom
| 扩展点: After Save Sales Order Line (After DAL)
#include <bic_dam>
declaration:
| 声明外部变量和函数原型
extern long Common.ConvertAmount(
domain tccmp, domain tcamnt, domain tcccur,
domain tcrtyp, domain tcdate, domain tcccur,
ref domain tcamnt, ref domain tcmcs.s999m, ref long )
extern domain tcmcs.s999m oExcMsg mb
extern long oExcID
| 变量声明
domain tcmcs.s999m remark.text
long log.count
before.program:
| 初始化
log.count = 0
after.save.object:
| 在 DAL after.save.object Hook 中处理
if action = DAL_NEW then
handle_new_line()
elif action = DAL_UPDATE then
handle_update_line()
endif
| 自定义函数
functions:
function void handle_new_line()
| 新建行后的处理逻辑
log.count = log.count + 1
| 示例:记录日志
endfunction
function void handle_update_line()
| 修改行后的处理逻辑
| 检查数量变更
if old.whinh215.sqty <> whinh215.sqty then
| 数量变更,联动处理
endif
endfunction
Step 2:在 LN 开发工具中注册
打开 LN Development 扩展管理工具
创建新 Extension,指定 DLL 名称和扩展点
设置优先级(数字越小优先级越高)
启用 Extension
Step 3:编译和部署
使用 bic6.2 或 LN Studio 编译
将编译后的 .dll6 / .o 文件部署到 $BSE/lib 目录
重启 bshell 或重新加载
4.3 Extension 模板(通用)
| ==================================================
| Extension: [名称]
| 用途: [描述]
| DLL: [DLL名称]
| 扩展点: [扩展点名称和类型]
| 作者: [作者]
| 日期: [日期]
| ==================================================
#include <bic_dam>
declaration:
| 外部变量
#pragma used dll [其他DLL] | 引用其他 DLL
extern long ...
| 内部变量
long ...
before.program:
| 初始化(可选)
...
| === Before DAL Hooks ===
before.save.object:
...
before.new.object:
...
before.change.object:
...
before.destroy.object:
...
| === After DAL Hooks ===
after.save.object:
if action = DAL_NEW then
...
elif action = DAL_UPDATE then
...
endif
after.new.object:
...
after.destroy.object:
...
| === Field Hooks ===
whinh215.item.update:
...
whinh215.item.make.valid:
...
| === Before Logic ===
| (UI 逻辑相关 sections)
choice.cont.process:
on.choice:
...
| === 自定义函数 ===
functions:
function long my_function(...)
...
return(0)
endfunction
五、Hook 编写实战
5.1 After DAL Hook —— 保存后联动
场景:销售订单行保存后,自动更新订单头上的总备注。
after.save.object:
if action = DAL_NEW or action = DAL_UPDATE then
| 检查金额是否超过阈值
if tdsls401.amnt(1) > 100000 then
| 调用公共接口记录大额订单备注
update_order_remark(tdsls401.orno, "大额订单,金额: " & str$(tdsls401.amnt(1)))
endif
endif
functions:
function void update_order_remark(domain tcorno i.order, string i.remark)
long ret
string dal.name(20)
dal.name = "tdsls400"
| 读取订单头
tdsls400.orno = i.order
ret = dal.find.object(dal.name)
if ret <> 0 then
return
endif
| 加锁 + 修改
ret = db.bind(tdsls400, db.FIND.BY.KEYS, db.LOCK)
if ret <> 0 then
return
endif
ret = dal.change.object(dal.name)
if ret <> 0 then
db.release(tdsls400)
return
endif
dal.set.field("tdsls400.rema", i.remark)
ret = dal.save.object(dal.name)
db.release(tdsls400)
endfunction
5.2 Before DAL Hook —— 保存前校验
场景:保存采购订单行前,校验交货日期不能是过去。
before.save.object:
if action = DAL_NEW or action = DAL_UPDATE then
if tdpur401.ddtb < utc.num() then
| 交货日期在过去,阻止保存
dal.set.error.message("tddel00101") | 引用错误消息代码
endif
endif
5.3 Field Hook —— 字段联动
场景:当物料编码改变时,自动填充物料描述和默认仓库。
tdsls401.item.update:
if dal.any.parent.changed() then
| 清空当前值
dal.set.field("tdsls401.dsca", "")
dal.set.field("tdsls401.cwar", "")
| 查询物料信息
select tccom001.dsca:desc, twhinh200.cwar:war
from tccom001
left outer join twhinh200 on twhinh200.item = tccom001.item
and twhinh200.defa = tcyesno.yes
where tccom001.item = :tdsls401.item
as set with 1 rows
selectdo
dal.set.field("tdsls401.dsca", desc)
dal.set.field("tdsls401.cwar", war)
selectempty
message("Item %s not found", tdsls401.item)
endselect
endif
long ret
domain tcmcs.s999m oMsg mb
long oID
ret = SomePublicInterface( fixed_args..., oMsg, oID )
if ret = 0 then
Exception.Delete(exception.id) | 成功:清理异常
else
Exception.GetMessage(oID, 1, oMsg)
message("错误: " & oMsg)
raise error | 失败:抛出错误
endif