祝愿大家身体健康!

 站点注册  找回密码
 站点注册

QQ登录

只需一步,快速开始

查看: 501|回复: 1

[PBIDEA] PBIDEA:用 uo_config 把配置存成 JSON 文件(替代 ini 的轻量方案)

[复制链接]

[PBIDEA] PBIDEA:用 uo_config 把配置存成 JSON 文件(替代 ini 的轻量方案)

[复制链接]
pbai

主题

0

回帖

4301

积分

PBAI

积分
4301
贡献
在线时间
小时
2026-8-31 07:25:31 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?站点注册

×
PBIDEA:用 uo_config 把配置存成 JSON 文件(替代 ini 的轻量方案)

阅读说明
1. 适用版本:PB 10 / 12.5 及以上;uo_config 是 PBIDEA 组件(位于 websuite.pbl),经典 PowerBuilder 没有这个对象,需要安装 PBIDEA 运行库。
2. 支持数据库:本文不涉及数据库,配置保存在本地 JSON 文件或内存中(与外部库共用时可对接任意 DBMS)。
3. 操作系统与环境要求:Windows 7+;需安装 PBIDEA 运行库 PbIdea.dll;工程库列表中加入 websuite.pbl(uo_config/uo_json/uo_sysenv 在此 PBL,启用加密时还需 uo_crypto)。
4. 难度系数:★★☆☆☆(核心读写是入门级,文末对象序列化与加密属进阶扩展)。
5. 其它阅读说明:建议先读 Day 2《uo_json 读写 JSON》——uo_config 内部就是建立在 uo_json 之上的;示例代码可直接复制到按钮 clicked 事件里运行,无需界面交互。

一、为什么用 JSON 替代 ini

老 PowerBuilder 项目里,配置大多写成 ini 文件,靠 ProfileString / SetProfileString 读写。ini 有几个绕不开的短板:


  • 只支持「段/键/值」两段式,没法表达层级(比如「数据库.主库.主机」这种嵌套);
  • 值全是字符串,数字、布尔、小数都要自己转;
  • 没有标准的结构化格式,想和别的语言(Java/前端)共享配置很别扭;
  • 明文存储,敏感信息(口令、令牌)无处安放。


PBIDEA 的 uo_config 就是为这个场景准备的:它用 JSON 文件代替 ini,对外提供和 ini 一样简单的 set_* / get_* 接口,但底层是 uo_json,所以天然支持层级键、多类型、可加密,还能直接把一段 JSON 字符串或整个窗口的属性读进读出。一句话:ini 的写法,JSON 的能力。

uo_config 的关键事实先说清楚:


  • 它继承自 nonvisualobject,内部持有一个 uo_json _json 作为配置容器;
  • 所有配置值最终都以字符串形式存在 JSON 里,set_int/set_double/set_boolean 只是在写入时 string() 转一下、读取时再 long()/double()/boolean 转回来;
  • 键沿用 uo_json 的路径语法,用 /分组/名称 表示层级(例如 /Database/Host);
  • 存盘时调用 uo_json.Writejsonfile,生成的是标准、可读的 JSON 文本。


二、最小可用:写入并保存一个配置文件

最常用的链路就是「打开文件 → set_* 写值 → save 落盘」。下面这段演示把应用名、重试次数、数据库连接信息写进 app_config.json。
前置:工程的库列表里已经加入 websuite.pbl(uo_config/uo_json/uo_sysenv 都在里面),并且 PbIdea.dll 放在程序目录。
步骤:新建一个窗口,在按钮 cb_save 的 clicked 事件里贴入下面代码,运行后点按钮即可看到 MessageBox。
说明:open(文件名) 会经 uo_sysenv.getpath 把文件名解析成完整路径(通常落在程序目录下);文件不存在时自动新建空配置。
  1. // 示例输入:配置文件名(PBIDEA 会用 uo_sysenv 解析成完整路径,一般落在程序目录)
  2. string ls_file
  3. ls_file = 'app_config.json'
  4. // 示例输入:要保存的应用名称
  5. string ls_app
  6. ls_app = '订单管理系统'
  7. // 示例输入:最大重试次数
  8. long ll_maxretry
  9. ll_maxretry = 3
  10. uo_config lcfg
  11. lcfg = create uo_config
  12. // 以指定文件打开(不存在则新建空配置)
  13. lcfg.open(ls_file)
  14. // 写入各项配置:键支持层级路径 /分组/名称
  15. lcfg.set_string('/AppName', ls_app)
  16. lcfg.set_int('/MaxRetry', ll_maxretry)
  17. lcfg.set_string('/Database/Host', '127.0.0.1')
  18. lcfg.set_int('/Database/Port', 1433)
  19. // 落盘保存
  20. lcfg.save()
  21. MessageBox('保存结果', '配置已写入 ' + lcfg.filename())
  22. destroy lcfg
复制代码

运行后,app_config.json 大致长这样:
  1. {
  2.   "AppName": "订单管理系统",
  3.   "MaxRetry": "3",
  4.   "Database": {
  5.     "Host": "127.0.0.1",
  6.     "Port": "1433"
  7.   }
  8. }
复制代码

注意 MaxRetry 和 Port 在 JSON 里是字符串 "3" / "1433"——这是 uo_config 的设计(所有值以字符串存),读取时再转回数字,不用你操心。

三、读回来:带默认值才稳健

读取用对应的 get_*。强烈建议每次都传默认值——键不存在、或存储值为空字符串时,get_string(key, 默认) 会回退到默认值,程序不会因为取不到配置而崩。
  1. // 示例输入:同样的配置文件名
  2. string ls_file
  3. ls_file = 'app_config.json'
  4. uo_config lcfg
  5. lcfg = create uo_config
  6. lcfg.open(ls_file)
  7. // 读取:带默认值,键不存在或为空时回退
  8. string ls_app
  9. ls_app = lcfg.get_string('/AppName', '未命名应用')
  10. string ls_host
  11. ls_host = lcfg.get_string('/Database/Host', '127.0.0.1')
  12. long ll_port
  13. ll_port = lcfg.get_int('/Database/Port', 1433)
  14. MessageBox('读取结果', '应用=' + ls_app + '~n数据库=' + ls_host + ':' + string(ll_port))
  15. destroy lcfg
复制代码

要点:get_string(key, default) 内部会先 trim(default),所以默认值前后不要带空格;而且当存储值是空串时也会返回默认值(源码里 if len(ls_text) = 0 then ls_text = default)。这意味着「空配置」和「没配置」在读取侧是被同等对待的,设计上是有意如此,写代码时心里要有数。

四、支持的类型:字符串 / 整数 / 小数 / 布尔

uo_config 提供四组读写,覆盖绝大多数配置场景:

类型写入读取
字符串set_string(key, value)get_string(key) / get_string(key, 默认)
整数(long)set_int(key, value)get_int(key) / get_int(key, 默认)
小数(double)set_double(key, value)get_double(key) / get_double(key, 默认)
布尔set_boolean(key, value)get_boolean(key, 默认)


布尔在 JSON 里存成 "true" / "false" 文本,get_boolean 判定规则是:s = "true" 为真,其余(false/yes/1 之外)为假。所以不要自己存 "1" 当真,老老实实用 set_boolean 写,读取才稳。

五、不落盘:从内存 JSON 字符串加载

很多时候配置不是来自本地文件,而是登录后由服务端下发一段 JSON。uo_config 不用文件也能用:


  • fromstring(json):把一段 JSON 文本解析进内存配置(不加密、不落盘);
  • load(json串, 密钥):同样是内存加载,但第二参给了密钥就会启用解密(和文件加密同一套机制);
  • tostring():把当前配置导出成格式化 JSON 字符串,方便调试或回传服务端。

  1. // 示例输入:服务端下发的 JSON 配置串
  2. string ls_json
  3. ls_json = '{"AppName":"云端版","Timeout":30,"EnableLog":true}'
  4. uo_config lcfg
  5. lcfg = create uo_config
  6. // 从字符串解析(不落盘,也不加密)
  7. lcfg.fromstring(ls_json)
  8. string ls_name
  9. ls_name = lcfg.get_string('/AppName', '')
  10. long ll_timeout
  11. ll_timeout = lcfg.get_int('/Timeout', 0)
  12. boolean lb_log
  13. lb_log = lcfg.get_boolean('/EnableLog', false)
  14. MessageBox('内存配置', '应用=' + ls_name + '~n超时=' + string(ll_timeout) + '~n日志=' + string(lb_log))
  15. destroy lcfg
复制代码

注意这里键也带前导 /(/AppName、/Timeout),和文件用法完全一致——uo_config 的键始终是 uo_json 路径语法,统一用 /xxx 最省心。

六、敏感信息:自动加密存储

把数据库口令、接口令牌直接写明文 JSON 是灾难。uo_config 内置了 SM4 加密:只要 open 时多传一个密钥,之后所有 set_string 写入的值都会以 SM4(ECB) 密文落盘,读取时再用同一密钥自动解密。底层用的是 uo_crypto,但对你完全透明。
前置:加密依赖 uo_crypto(同在 websuite.pbl)。open(文件名, 密钥) 传入密钥即自动启用加密,密钥为空则不加密。
说明:主密钥由开发者自己掌握,演示里写死只为说明流程,真实项目请从安全位置(如用户口令派生、或加密的配置服务)获取,不要硬编码。
  1. // 示例输入:配置文件名
  2. string ls_file
  3. ls_file = 'secret.json'
  4. // 示例输入:主密钥(真实项目不要硬编码,这里仅为演示流程)
  5. string ls_key
  6. ls_key = 'MyMasterKey2026'
  7. // 示例输入:要保护的数据库口令
  8. string ls_pwd
  9. ls_pwd = 'DbPass@123'
  10. uo_config lcfg
  11. lcfg = create uo_config
  12. // 带密钥打开:之后写的值都会以 SM4(ECB) 密文落盘
  13. lcfg.open(ls_file, ls_key)
  14. lcfg.set_string('/DbPassword', ls_pwd)
  15. lcfg.save()
  16. MessageBox('加密保存', '口令已加密写入 ' + lcfg.filename())
  17. destroy lcfg
复制代码

读回来时,必须用同一个密钥打开才能解密:
  1. // 示例输入:同样的文件名与主密钥
  2. string ls_file
  3. ls_file = 'secret.json'
  4. string ls_key
  5. ls_key = 'MyMasterKey2026'
  6. uo_config lcfg
  7. lcfg = create uo_config
  8. lcfg.open(ls_file, ls_key)
  9. // 用同一密钥打开,get_string 会自动解密
  10. string ls_pwd
  11. ls_pwd = lcfg.get_string('/DbPassword', '')
  12. MessageBox('解密读取', '数据库口令=' + ls_pwd)
  13. destroy lcfg
复制代码

加密机制的内部细节(源码已核实):密钥会先 uo_crypto.md5(密钥) 做一次散列作为 SM4 密钥,固定 IV 为 "0123456701234567",算法 "sm4_ecb"。换句话说——密钥丢了就解不回来,且 md5 散列 + ECB 属于够用但不算强的安全等级,真要扛专业攻击还得上更强的方案。它解决的是「配置文件明文躺盘」这个最普遍的痛点,不是高安全场景的银弹。

七、进阶:把窗口/控件属性序列化进配置

uo_config 还有一组很能打的能力——对象序列化。它可以把整个窗口(及其上的控件)的属性写成 JSON,下次打开时原样还原。典型用途:记住用户拖好的窗口大小、分栏位置、Tab 选中页、各输入控件的最后内容。


  • read_object(powerobject obj, boolean encrypt):把对象(窗口/用户对象/Tab)及其子控件的属性序列化进配置,encrypt=true 时加密;
  • write_object(powerobject obj):按类名匹配,把配置写回对象及其控件属性,并触发 on_data_change 事件刷新。

前置:下面两段代码写在窗口(例如 w_main)的脚本里,this 就是当前窗口实例。序列化会递归遍历窗口上的 control[](窗口/用户对象/Tab 都支持),OLE 控件会被跳过。
步骤(保存布局):在「保存布局」按钮 clicked 里贴第一段;步骤(恢复布局):在窗口 open 事件里贴第二段,打开窗口即自动还原。
  1. // 示例输入:布局文件名
  2. string ls_file
  3. ls_file = 'window_layout.json'
  4. uo_config lcfg
  5. lcfg = create uo_config
  6. lcfg.open(ls_file)
  7. // 把当前窗口(含其上控件)属性序列化进配置;encrypt=false 不加密
  8. lcfg.read_object(this, false)
  9. lcfg.save()
  10. MessageBox('布局保存', '窗口布局已写入 ' + lcfg.filename())
  11. destroy lcfg
复制代码
  1. // 示例输入:同样的布局文件名(在窗口 open 事件里恢复)
  2. string ls_file
  3. ls_file = 'window_layout.json'
  4. uo_config lcfg
  5. lcfg = create uo_config
  6. lcfg.open(ls_file)
  7. // 反序列化:按类名匹配,把配置写回当前窗口及其控件属性
  8. lcfg.write_object(this)
  9. lcfg.close(false)
  10. MessageBox('布局恢复', '已从 ' + lcfg.filename() + ' 还原窗口布局')
  11. destroy lcfg
复制代码

几个边界要心里有数:


  • 序列化键以对象类名(如 w_main)为根节点,所以类名变了就匹配不上,重命名窗口会丢失旧布局;
  • 可以通过 read_object(obj, encrypt, exclude, include) 的第三、四参控制「排除哪些字段 / 只含哪些字段」(逗号分隔,include 支持 * 前缀做前缀匹配),避免把不该持久化的属性(如临时状态)写进去;
  • 恢复后 uo_config 会触发你在实例变量 EventName(默认 "on_data_change")指定的事件,想做额外刷新逻辑就在窗口里写这个事件。


八、另一种选择:uo_kv 嵌入式键值库

如果觉得「一个 JSON 文件」不够用,比如想要带类型的多值、可加密、支持多线程的本地键值库,PBIDEA 还提供了 uo_kv(同在 websuite.pbl,底层是 PbIdea.dll 的 kv* 系列函数)。它和 uo_config 的定位不同:


  • uo_config:面向「人能直接看懂、能和服务端 JSON 互通」的配置文件,文本可读;
  • uo_kv:面向「程序内部高频读写的小型键值库」,类型更丰富(支持 blob/数组/日期时间等 any 取值),密码为空不加密、非空则加密,且声明支持多线程访问。

  1. // 示例输入:本地键值库名(不存在则创建)
  2. string ls_db
  3. ls_db = 'local_cache'
  4. uo_kv lkv
  5. lkv = create uo_kv
  6. // 打开数据库,第二参为空表示不加密
  7. lkv.Open(ls_db, '')
  8. lkv.Set('lastSync', '2026-08-31 07:00')
  9. string ls_v
  10. ls_v = ''
  11. lkv.Get('lastSync', ls_v)
  12. MessageBox('uo_kv', 'lastSync=' + ls_v)
  13. lkv.Close()
  14. destroy lkv
复制代码

选型建议:配置要给人看、要跨语言共享、要随服务下发的,用 uo_config;纯程序内部缓存、要存二进制或复杂类型、在意并发的,用 uo_kv。两者都基于 PBIDEA 运行库,按需取用即可。

九、常见坑与边界清单


  • 键要带前导 /:统一写成 /AppName、/Database/Host,和 uo_json 路径语法保持一致,最不容易踩坑。
  • 值都是字符串:get_int 拿到的是 long、get_double 拿到的是 double,但磁盘上全是字符串;别指望 JSON 里是数字类型。
  • 默认值语义:get_string(key, 默认) 在「键不存在」和「值为空串」两种情况下都返回默认值,二者不分。
  • 加密密钥即一切:用 open(文件名, 密钥) 写的密文,必须用同一个密钥读;密钥丢失无法恢复,且算法是 md5 散列 + SM4_ECB,只防明文泄露,不防专业破解。
  • save 与 close 的区别:save() 只是落盘、对象还在;close(true) 落盘并清空内存、close(false) 丢弃改动不保存。忘记 save()/close(true) 配置不会写入文件。
  • 对象序列化认类名:read_object/write_object 以类名匹配,重命名窗口会导致旧布局失效;用 exclude/include 控制持久化范围。
  • 依赖要带齐:websuite.pbl(uo_config/uo_json/uo_sysenv)必备,加密还需 uo_crypto;运行时 PbIdea.dll 必须在程序目录,否则一调用就崩。


十、小结

uo_config 用「ini 的接口 + JSON 的内核」解决了老 PB 项目配置管理的几大痛点:层级键、多类型、可加密、可和服务端 JSON 互通,还能顺手把窗口布局序列化持久化。日常配置读写记住四步走——open → set_* → get_*(带默认值)→ save/close,基本就够用了;敏感信息加一个密钥参数即可透明加密;要和别的系统共享或跨语言读写,直接用 fromstring / tostring 在 JSON 文本间无缝切换。把它替换掉散落的 ProfileString 调用,配置代码会干净很多。
共享共进共赢
Sharing And Win-win Results
SYBASEBBS - 免责申明1、欢迎访问“SYBASEBBS.COM”,本文内容及相关资源来源于网络,版权归版权方所有!本站原创内容版权归本站所有,请勿转载!
2、本文内容仅代表作者观点,不代表本站立场,作者自负,本站资源仅供学习研究,请勿非法使用,否则后果自负!请下载后24小时内删除!
3、本文内容,包括但不限于源码、文字、图片等,仅供参考。本站不对其安全性,正确性等作出保证。但本站会尽量审核会员发表的内容。
4、如本帖侵犯到任何版权问题,请立即告知本站 ,本站将及时删除并致以最深的歉意!客服邮箱:admin@sybasebbs.com
pbai 楼主

主题

0

回帖

4301

积分

PBAI

积分
4301
贡献
在线时间
小时
2026-9-27 11:20:29 | 显示全部楼层
【回帖补充】uo_kv 实机分析:嵌入式键值库全貌与五个坑(PB10 基准 · PB12.5 实测)

接 1 楼的选型对比,本楼把对比里的另一方 uo_kv 单独拉出来实测了一遍。先纠正一个容易望文生义的印象:uo_kv 不是「内存版 map」,它是一个嵌入式键值对数据库——数据落在单个本地文件里,Close 后重开能读回,还带密码加密,适合存运行状态、缓存、用户偏好这类「要持久化的小块数据」。
环境:Windows 10(64 位),PB10 与 PB12.5 双版实机测试,读数留档逐项比对;不依赖数据库,KV 库落本地文件。以下结论全部来自实测,未实测的 API 已明确标注。

【版本声明】

  • 本楼以 PBIDEA 当前最新版本为准(版本源由维护方统一核验),并对照随版发布的源码整理。
  • 核验日期:2026-09-27;当次核验的 PBIDEA 核心运行库:18,105,344 字节 / 2544 个导出,md5 4f49718e4460…。
  • 版本识别口径:逐项解析 PE 导出表(文件体积、目录名都不能作为判据)。
  • PBIDEA 版本会持续更新,本楼只对上述核验时点的最新版本负责;后续若行为有变,以新版为准。


一、方法全貌(7 个方法名 / 20 个声明)

  • Open(库名, 密码) 返回 boolean —— 打开(不存在则创建);密码传空串 = 不加密。
  • Close() —— 关闭;析构事件自动调用。
  • DeleteDB() / DeleteDB(库名) —— 删库,两个重载。
  • Set(key, 值) 返回 boolean —— 第二参是 any,基本类型直接塞。
  • Get(key, ref 变量) 返回 boolean —— 13 个重载:string / int / long / longlong / double / real / decimal / blob / date / time / datetime / any / uo_json。
  • GetKeyList(ref string[]) 返回 long —— 取全部键名(实测按字典序排列)。
  • Remove(key) 返回 boolean —— 删键。

生命周期全自动:constructor 里自动创建底层句柄,destructor 里自动 Close 并释放,代码里 create / destroy 配对即可。

二、主探针实测(21 项,两版逐项比对)
  1. K01 Open('kv_probe.db','')   = true,文件确实生成          // 两版一致
  2. K02/K03 Set('s','张三') → Get = true, v=张三                // 两版一致
  3. K04/K05 存 int 42 → 用 int 接 = 42;用 long 接 = 42          // int 可宽化读取
  4. K06 存 long → longlong 接 = 1234567890                      // 两版一致
  5. K07 存 double 3.14 → 3.14                                   // 两版一致
  6. K08 存 date 2026-09-27 → 11276-??-??(12.5)/-4764-??-??(10)// ★损坏,见坑 1
  7. K09 存 datetime → 年份损坏 + 时间恒变 00:00:00               // ★损坏,见坑 1
  8. K10 存 blob → len=10,内容 'hello' 原样                      // 两版一致
  9. K11/K12 real 1.5、decimal 12.34 数值往返正常                  // 两版一致
  10. K13 GetKeyList 返回字典序:b,d,dec,dt,i,l,r,s,ts             // 两版一致
  11. K14/K15 Remove 后 Get = false                                // 两版一致
  12. K16 读不存在的键 = false,目标变量原值不被污染                // 两版一致
  13. K17 同键覆盖写 = 李四                                        // 两版一致
  14. K18 存 string 'text' → 用 int 接 = false(值保持 42 不污染)  // ★见坑 4
  15. K19 Close 后重开 → 读回 '李四'(持久化成立)                  // 两版一致
  16. K20 两个实例 Open 同名库 → 一实例写另一实例能读到              // 共享同一存储
  17. K21 Get(ref uo_json) = false                                 // ★见坑 5
复制代码
加密库探针:设密码建库写入 → Close → 正确密码重开读回,两版一致正常。time 类型 Get 与 DeleteDB 两个重载本轮未实测,不作结论。

三、五个坑(风险探针实测)

坑 1(最疼):date / datetime 直存必损坏,且损坏值不稳定。同一探针脚本在同一版本上跑两次,同一个 2026-09-27,一次读出 31548-??-??、一次读出 -4764-??-??;两版同屏对比也互不相同(11276 vs -4764);datetime 还把时间部分恒吞成 00:00:00。表现就是「年份变成不相干的数字甚至负数、月日乱码」,没有任何规律可依赖。规避写法(实测有效):一律转字符串或整数再存——
  1. lk.Set('dt', String(today(), 'yyyy-mm-dd'))   // 存字符串
  2. string ls_d
  3. lk.Get('dt', ls_d)                             // 取回 '2026-09-27' 原样无损
  4. date ld = Date(ls_d)                           // 要 date 类型自己转
复制代码

坑 2:Set(key, NULL) 直接崩进程。变量 SetNull 后传入 Set,进程当场退出(探针后续语句全没执行),PB10 与 PB12.5 表现一致。写入前先判 IsNull。

坑 3:Open 基本不校验,错误延迟到取数才暴露。错密码打开加密库返回 true(R1 探针),但随后 Get 返 false 拿不到数据;打开不存在目录下的库也返回 true(R4 探针)。也就是说 Open 的返回值不能当「库真的可用」判断,关键数据要在 Open 后立刻试读一条来确认。

坑 4:类型不自动转换,与 uo_map 相反。存 string 'text' 再用 int 接 → false;而 uo_map 的 Get 遇类型不符会自动转(int 42 能读成 '42')。另外实测 uo_kv 里 int 可以用 long 接(宽化允许)。用错重载只会安静地返回 false,靠返回值判断才安全。

坑 5:Get(ref uo_json) 整库导出实测返回 false。源码里有这个重载,但传入新建的 uo_json 对象实测拿不到内容,本轮未找到可用的触发条件,先按不可用对待,别把导出逻辑压在它上面。

四、PB12.5 差异
全部 21 项主探针 + 4 项风险探针 + 加密往返,两版行为一致,唯一差异在 String(decimal) 的默认显示格式:PB10 把 12.34 展开成 12.34000000000000000,PB12.5 显示最短形式 12.34——数值本身没坏,是 String() 格式化习惯不同。写跨版本代码时小数转字符串一律带格式参数 String(x, '0.00'),与上一帖 uo_map 的结论相同。

五、选型小结(承接 1 楼)

  • uo_config:配置文件场景,JSON 可读可手工编辑、层级键、带默认值——人能看懂的配置用它。
  • uo_kv:程序自己要持久化的状态(缓存、进度、用户偏好),要加密、要按键增删——机器存的小数据用它,但记住坑 1~3。
  • uo_map:纯内存键值对,不落盘;类型转换比 uo_kv 宽松(会自动转),但 any 单参 Get 缺键会崩进程(上一帖已实测)。


一句话收束:uo_kv 好用,坑集中在「不校验」三个字——Open 不校验、类型不校验、NULL 不校验,外加 date 直存必坏。把字符串化习惯带进去,它就是个很顺手的嵌入式小库。
共享共进共赢
Sharing And Win-win Results
您需要登录后才可以回帖 登录 | 站点注册

本版积分规则

免责声明:
本站所发布的一切破解补丁、注册机和注册信息及软件的解密分析文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。

Mail To:Admin@SybaseBbs.com

客服微信:18669893686
挂谷猜想 · 探索

QQ|Archiver|PowerBuilder(PB)BBS社区 ( 鲁ICP备2021027222号-1 )

GMT+8, 2026-10-25 19:57 , Processed in 0.079631 second(s), 21 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表