VB6+SQL Server销售管理系统源码拆解:ADO、事务与库存扣减

发布时间:2026/10/31 22:31:14

VB6+SQL Server销售管理系统源码拆解:ADO、事务与库存扣减 简介面向VB6初学者与数据库入门开发者的销售管理系统源码实例围绕VB6.0与SQL Server 2000的联合开发展开可用于学习窗体界面、数据库连接及销售业务数据处理的基本流程。压缩包共51个文件约1.37MB包含10个frm窗体与10个frx窗体资源、3个ocx控件及oca授权文件、3个bas标准模块、2个cls类模块另有vbp工程、sql数据库脚本和db数据文件等结构上覆盖界面、报表与业务逻辑等部分。已有442人学习适合作为课程设计、毕业设计或自学练手参考。功能涉及产品信息录入、产品管理、查询打印、金额汇总和简单报表可帮助读者理解VB与SQL Server之间的数据读写、窗体事件处理和基础报表输出思路并借助随包SQL脚本还原运行环境、按模块拆解学习。1. 一份能编译的 VB6 SQL Server 销售管理系统源码比十篇教程都值得拆接手一套老系统做二次开发最耗时间的不是写代码而是猜上一任作者把单据号生成、库存扣减、权限判断分别藏在了哪个窗体里。这份 VB6 销售管理系统完整源代码带着 SQL Server 建库脚本一起给到商品、客户、销售开单、退货、库存、统计报表这几条主线在工程里是通的能编译、能连库、能出单。它适合三类人要给小型客户交付进销存的外包开发者、在老旧系统上改需求的维护人员、以及想弄明白 ADO 事件驱动写法到底长什么样的新手。下面按“先跑起来、再看结构、最后改得动”的顺序拆。2. 环境与连接把 VB6、SQL Server 与 ADO 凑成能跑的第一版2.1 三件套版本对齐VB6 SP6、SQL Server 与 32 位运行时这套工程的骨架非常传统VB6 标准 EXE ADO 数据访问 SQL Server 关系库。它的优点是很轻缺点也很直接——VB6 只能产出 32 位程序所有和它打交道的组件都必须是 32 位一旦有人在中途混进 64 位驱动报错信息往往和真正的原因毫无关系。IDE 侧建议装 VB6 SP6装在独立机器或虚拟机里不要和日常办公环境混用。VB6 的安装程序对新系统识别很差安装过程中弹出的组件选择框全部保持默认即可装完补一次 SP6 补丁否则某些控件在保存窗体时会崩溃。数据库侧从 SQL Server 2008 R2 到 2019 都能跑这套脚本因为它只用到基础表、视图和存储过程。真正需要留意的是数据访问层选谁提供程序连接串写法适用场景注意事项SQLOLEDBProviderSQLOLEDB.1老机器、老系统迁移随 MDAC 分发不支持 TLS 1.2服务端强制加密时握手失败MSOLEDBSQLProviderMSOLEDBSQL新版 SQL Server、需要加密需单独安装驱动工程里要重新引用MSDASQL ODBCProviderMSDASQL.1只能用 DSN 的老代码32 位 DSN 要用 SysWOW64 下的 odbcad32 建工程引用里只需要一个Microsoft ActiveX Data Objects 2.8 Library。如果打开工程时看到 2.5 和 2.8 同时被勾选直接取消 2.5两个版本共存会让ADODB.Recordset在运行期变成“未知类型”编译期不报错一跑就崩。2.2 建库把 .sql 脚本跑成中文不乱码的表脚本不要一次性全选执行。先翻到最前面看有没有CREATE DATABASE和USE很多源码包的脚本默认库名是SalesDB而机器上已经有一个同名库直接跑会把老数据盖掉。第一步确认库的排序规则和字符类型-- 先单独执行确认服务端排序规则 SELECT SERVERPROPERTY(Collation) AS ServerCollation; -- 库里如果已经有中文数据排序规则不要随便改 CREATE DATABASE SalesDB COLLATE Chinese_PRC_CI_AS; GO USE SalesDB; GO第二步按“基础资料表 → 单据头表 → 单据明细表 → 视图和存储过程”的顺序分批执行。带外键的明细表必须等主表建完脚本里如果有GO说明它是一个批次分隔符SSMS 会分段提交中途报错不会自动回滚前面的表。第三步把存中文的列统一成nvarchar。原脚本里常见的varchar(50)存客户名称在Chinese_PRC_CI_AS下能存进去但一旦换到别的排序规则或换台机器还原备份就会出现问号。改法是在建表阶段就定死CREATE TABLE Customer( CustomerID int IDENTITY(1,1) NOT NULL PRIMARY KEY, CustName nvarchar(100) NOT NULL, LinkMan nvarchar(30) NULL, Tel varchar(30) NULL, Address nvarchar(200) NULL, IsActive bit NOT NULL DEFAULT 1 ); GOCustomerID用IDENTITY而不是手工取号是因为主数据表并发低让数据库自己发号最省事单据号则完全相反业务上要求“前缀 日期 流水”必须自己控制这个放到第 3 章讲。建完之后跑一遍行数校验确认脚本里的初始化数据真的进去了SELECT Customer AS Tbl, COUNT(*) AS Cnt FROM Customer UNION ALL SELECT Product, COUNT(*) FROM Product UNION ALL SELECT SaleBill, COUNT(*) FROM SaleBill;2.3 连接串集中管理一个模块管住所有窗体源码包里最常见的问题是连接串被硬编码在十几个窗体的Form_Load里密码还是sa。换台机器部署得全文搜索改十几处漏一处就报“登录失败”。我一般的做法是抽一个modConn.bas把连接串和连接对象都收口 modConn.bas —— 连接串集中管理换机器只改 config.ini 一处 Option Explicit Private Declare Function GetPrivateProfileString Lib kernel32 Alias GetPrivateProfileStringA _ (ByVal lpAppName As String, ByVal lpKeyName As String, ByVal lpDefault As String, _ ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long Public gConn As ADODB.Connection Public Function ConnString() As String Dim sServer As String, sDb As String, sUser As String, sPwd As String sServer ReadIni(db, server, 127.0.0.1,1433) sDb ReadIni(db, database, SalesDB) sUser ReadIni(db, user, salesapp) sPwd ReadIni(db, pwd, ) 端口直接写在 Data Source 里避免命名实例解析不到 ConnString ProviderSQLOLEDB.1;Data Source sServer _ ;Initial Catalog sDb _ ;User ID sUser ;Password sPwd _ ;Persist Security InfoFalse;Connect Timeout10 End Function Public Function DBOpen() As Boolean On Error GoTo EH If Not gConn Is Nothing Then If gConn.State adStateOpen Then DBOpen True: Exit Function End If Set gConn New ADODB.Connection gConn.CursorLocation adUseClient 客户端游标界面拿到的记录集可断开使用 gConn.CommandTimeout 30 gConn.Open ConnString() DBOpen True Exit Function EH: MsgBox 连接数据库失败 Err.Description, vbCritical, 数据库 End Function Private Function ReadIni(ByVal Section As String, ByVal Key As String, ByVal DefVal As String) As String Dim buf As String, n As Long buf String$(512, vbNullChar) n GetPrivateProfileString(Section, Key, DefVal, buf, 512, App.Path \config.ini) ReadIni Left$(buf, n) End Function几个参数值得单独说。CursorLocation adUseClient决定记录集在客户端内存里维护好处是窗体可以拿着断开后的Recordset慢慢翻页、重新排序不占用服务端游标代价是大结果集会吃内存报表查询必须自己加TOP或者分页。Connect Timeout10是连接阶段的超时网络不通时能让用户十秒内看到提示而不是界面假死。Persist Security InfoFalse表示连接打开后不再把密码留在连接对象里排查日志时不会把明文密码打出来。config.ini放在程序目录下内容就四行[db] server192.168.1.10,1433 databaseSalesDB usersalesapp pwdChangeMe_2024应用账号不要用sa。在 SSMS 里给它单独建登录只授予db_datareader、db_datawriter和必要存储过程的EXECUTE出问题时能一眼看出是权限问题还是代码问题这比事后翻日志省事得多。3. 销售单据主线头表、明细表与库存扣减在代码里怎么落地3.1 单据号生成靠一张序列表和行锁而不是取 MAX1销售单据号是这套系统里最容易翻车的地方。原代码里我见过两种写法SELECT MAX(BillNo)1和用时间戳拼随机数。前者在两个人同时开单时必然撞号后者会在数据库里留下无法排序的脏数据月底对账时对不上。正确做法是把发号动作交给数据库用一张序列表加UPDLOCKCREATE TABLE BillSeq( Prefix varchar(10) NOT NULL, BillDate char(8) NOT NULL, -- yyyymmdd Seq int NOT NULL, CONSTRAINT PK_BillSeq PRIMARY KEY(Prefix, BillDate) ); GO CREATE PROCEDURE usp_NextBillNo Prefix varchar(10), BillNo varchar(20) OUTPUT AS BEGIN SET NOCOUNT ON; DECLARE d char(8) CONVERT(char(8), GETDATE(), 112); UPDATE BillSeq WITH (UPDLOCK, ROWLOCK) SET BillNo Prefix d RIGHT(0000 CAST(Seq AS varchar(4)), 4), Seq Seq 1 WHERE Prefix Prefix AND BillDate d; IF ROWCOUNT 0 BEGIN BEGIN TRY INSERT INTO BillSeq(Prefix, BillDate, Seq) VALUES(Prefix, d, 1); SET BillNo Prefix d 0001; END TRY BEGIN CATCH -- 两个会话同时插入时让后到的那个重走一次 UPDATE EXEC usp_NextBillNo Prefix, BillNo OUTPUT; END CATCH END ENDUPDLOCK保证同一行的更新被串行化ROWLOCK避免升级成表锁把整张序列表锁住。日期前缀每天换一次Seq从 1 重来业务上看起来干净。这里把SET NOCOUNT ON放在存储过程里是为了减少网络往返的数据包但要注意调用方如果靠RecordsAffected判断成功NOCOUNT ON会让它返回 -1这个坑放到第 5 章细说。3.2 主从表保存一个事务里写完单据头和明细行销售开单界面上是一张主表加一个网格保存时必须一口气写完SaleBill和SaleDetail中间任何一行失败都要整体回滚否则会留下“有单头没明细”的废单月底统计金额直接错。 保存一张销售单头 N 行明细全程一个事务 Public Function SaveBill(ByVal BillNo As String, ByVal CustID As Long, _ ByVal dtBill As Date, ByRef Lines As Collection) As Boolean Dim cmd As ADODB.Command, vLine As Variant, i As Long Dim curTotal As Currency On Error GoTo EH gConn.BeginTrans 1) 先算总额避免明细写完之后再回头 UPDATE 单头 For i 1 To Lines.Count curTotal curTotal Lines(i)(2) * Lines(i)(3) Qty * Price Next i 2) 写单头 Set cmd New ADODB.Command Set cmd.ActiveConnection gConn cmd.CommandType adCmdText cmd.CommandText INSERT INTO SaleBill(BillNo,BillDate,CustomerID,TotalAmount,Operator,Status) _ VALUES(?,?,?,?,?,0) cmd.Parameters.Append cmd.CreateParameter(BillNo, adVarWChar, adParamInput, 20, BillNo) cmd.Parameters.Append cmd.CreateParameter(BillDate, adDBTimeStamp, adParamInput, , dtBill) cmd.Parameters.Append cmd.CreateParameter(CustID, adInteger, adParamInput, , CustID) cmd.Parameters.Append cmd.CreateParameter(Total, adCurrency, adParamInput, , curTotal) cmd.Parameters.Append cmd.CreateParameter(Op, adVarWChar, adParamInput, 20, CurrUser) cmd.Execute , , adExecuteNoRecords 3) 逐行写明细同时扣库存 For i 1 To Lines.Count vLine Lines(i) Array(ProductID, RowNo, Qty, Price) Set cmd New ADODB.Command Set cmd.ActiveConnection gConn cmd.CommandType adCmdText cmd.CommandText INSERT INTO SaleDetail(BillNo,RowNo,ProductID,Qty,Price) VALUES(?,?,?,?,?) cmd.Parameters.Append cmd.CreateParameter(b, adVarWChar, adParamInput, 20, BillNo) cmd.Parameters.Append cmd.CreateParameter(r, adInteger, adParamInput, , CLng(vLine(1))) cmd.Parameters.Append cmd.CreateParameter(p, adInteger, adParamInput, , CLng(vLine(0))) cmd.Parameters.Append cmd.CreateParameter(q, adDouble, adParamInput, , CDbl(vLine(2))) cmd.Parameters.Append cmd.CreateParameter(pr, adCurrency, adParamInput, , CCur(vLine(3))) cmd.Execute , , adExecuteNoRecords If Not CutStock(CLng(vLine(0)), CDbl(vLine(2))) Then Err.Raise vbObjectError 1001, , 商品 vLine(0) 库存不足 End If Next i gConn.CommitTrans SaveBill True Exit Function EH: On Error Resume Next gConn.RollbackTrans MsgBox 保存失败 Err.Description, vbCritical, 销售开单 SaveBill False End Function这段代码里有三个刻意的设计。总额在写库之前就算好避免“先插 0 再 UPDATE”的两次写明细行号RowNo由界面维护而不是数据库自增是因为用户会在网格里插行、删行行号必须跟着界面视觉顺序走库存扣减放在事务内扣失败就用Err.Raise抛出去让统一的错误分支回滚而不是在每个调用点写一堆If。Lines用Collection装数组是 VB6 里最省事的容器写法比定义自定义类型数组少写一堆ReDim Preserve。数组下标按ProductID, RowNo, Qty, Price固定顺序读的时候必须和界面填充时严格一致这类“隐式约定”在老代码里遍地都是改的时候建议在注释里写清楚。3.3 库存扣减把“先查后改”换成带条件的 UPDATE原代码里库存扣减常见这样写先SELECT Qty FROM Stock WHERE ProductID?在 VB 里比较一下再UPDATE Stock SET Qty?。两个人同时卖最后一件商品都能查到 1都能通过判断最后库存变成 -1 或者被覆盖。干净的写法是把判断塞进WHERE 原子扣减库存够才扣返回 True 表示本次确实扣掉了 1 行 Public Function CutStock(ByVal ProductID As Long, ByVal Qty As Double) As Boolean Dim cmd As ADODB.Command, n As Long Set cmd New ADODB.Command Set cmd.ActiveConnection gConn cmd.CommandType adCmdText cmd.CommandText UPDATE Stock SET Qty Qty - ? WHERE ProductID ? AND Qty ? cmd.Parameters.Append cmd.CreateParameter(q, adDouble, adParamInput, , Qty) cmd.Parameters.Append cmd.CreateParameter(p, adInteger, adParamInput, , ProductID) cmd.Parameters.Append cmd.CreateParameter(chk, adDouble, adParamInput, , Qty) cmd.Execute n, , adExecuteNoRecords CutStock (n 1) End Functioncmd.Execute n的第一个参数是受影响行数的出参n 1说明这一行确实被扣了n 0说明库存不够或者商品不存在。整个判断和更新由数据库一条语句完成不存在中间被别人插队的窗口。如果业务允许负库存比如先发货后补货那就把这句换成不带Qty ?的版本但仍然要走UPDATE ... SET Qty Qty - ?的增量写法绝不可以在 VB 里先算好绝对值再写回。3.4 查询列表MSFlexGrid 手工填充比数据绑定更可控这套界面用的是MSFlexGrid不少人图省事接ADODC做数据绑定结果一改列宽、一加隐藏列绑定就乱。手工填充反而清楚 把断开式记录集填进 MSFlexGrid空值统一显示为空白 Public Sub FillGrid(rs As ADODB.Recordset, grd As MSFlexGrid) Dim r As Long, c As Long grd.Clear grd.Cols rs.Fields.Count grd.Rows 1 For c 0 To rs.Fields.Count - 1 grd.TextMatrix(0, c) rs.Fields(c).Name Next c Do Until rs.EOF grd.AddItem r grd.Rows - 1 For c 0 To rs.Fields.Count - 1 If IsNull(rs.Fields(c).Value) Then grd.TextMatrix(r, c) Else grd.TextMatrix(r, c) CStr(rs.Fields(c).Value) End If Next c rs.MoveNext Loop grd.ColWidth(0) 0 隐藏第一列用来存主键 End Sub先设Cols再设Rows 1顺序反了会多出一行空白。空值必须单独判IsNullCStr(Null)会直接抛“无效使用 Null”的错误这是老代码里最高频的崩溃点之一。第一列藏起来存BillNo或ProductID双击行的时候用grd.TextMatrix(grd.Row, 0)取主键比在Tag里拼字符串可读得多。4. 换掉字符串拼接ADO 参数化、事务模板与存储过程调用4.1 拼接 SQL 的两个真实代价“SELECT * FROM Customer WHERE CustName txtName.Text ”这种写法在老 VB 代码里几乎是标配它的代价不只是安全。第一条代价是执行计划复用每次文本不同SQL Server 都要重新编译几百个客户名查下来计划缓存里全是只执行过一次的短命计划内存被挤占别人的查询跟着变慢。第二条代价是格式解析。日期在拼接里以本地格式传过去客户端是yyyy/m/d服务端DATEFORMAT是mdy2024/03/05就被解析成 3 月 5 日还是 5 月 3 日完全看运气。字符串里的单引号不转义就直接语法错误。参数化一次解决这三件事顺带让代码可读性上一个台阶。4.2 CreateParameter 的五个必填细节用ADODB.Command传参时CreateParameter的参数顺序是Name, Type, Direction, Size, Value每一项写错都表现为不同的怪现象Dim cmd As ADODB.Command Set cmd New ADODB.Command Set cmd.ActiveConnection gConn 注意adCmdText 下 SQLOLEDB 只认 ? 占位不认 名字 cmd.CommandType adCmdText cmd.CommandText SELECT BillNo,BillDate,TotalAmount FROM SaleBill _ WHERE CustomerID ? AND BillDate ? AND BillDate ? ORDER BY BillNo cmd.Parameters.Append cmd.CreateParameter(CustID, adInteger, adParamInput, , lngCustID) cmd.Parameters.Append cmd.CreateParameter(D1, adDBTimeStamp, adParamInput, , dtFrom) cmd.Parameters.Append cmd.CreateParameter(D2, adDBTimeStamp, adParamInput, , DateAdd(d, 1, dtTo)) Dim rs As ADODB.Recordset Set rs cmd.Execute第一adVarWChar一定要给Size比如 20 或 50不给Size在某些驱动下会被当成varchar(1)查出来全是空。第二文本类用adVarWChar而不是adVarChar否则中文在varchar列上又是一场乱码纠纷。第三金额用adCurrency别用adDouble0.1 0.2这类浮点误差累积到几百张单据上报表和财务对不上。第四adCmdText加SQLOLEDB时只能用?占位符按添加顺序匹配写了CustID不会报错但也不会被替换最后变成语法错误。第五日期用adDBTimeStamp传Date类型让驱动自己处理格式别在 VB 里Format成字符串再塞进 SQL。4.3 事务模板把 BeginTrans 和 Rollback 收在一个出口VB6 没有try...finally靠On Error GoTo跳转很容易出现“开了事务某条分支直接Exit Sub”的漏回滚。我一般把写操作全塞进一个模板函数里业务逻辑用一个独立的过程实现返回值表示成功与否 统一事务模板业务过程返回 False 就回滚 Public Function RunInTrans(ByVal ProcName As String, ParamArray Args() As Variant) As Boolean On Error GoTo EH gConn.BeginTrans If Not CallByName(Me, ProcName, VbMethod, Args) Then gConn.RollbackTrans Exit Function End If gConn.CommitTrans RunInTrans True Exit Function EH: On Error Resume Next gConn.RollbackTrans MsgBox 操作失败已回滚 Err.Description, vbExclamation, 提示 End FunctionCallByName在这里只是让调用点统一实际项目里如果嫌它慢直接写两个过程也可以一个负责BeginTrans/Commit/Rollback一个只负责业务 SQL。关键点是事务的开启和结束必须在同一个函数里成对出现不要出现“界面层BeginTrans、数据层CommitTrans”这种跨层写法那是维护期最难查的一类 bug。还有一点连接对象必须是全局同一个。VB6 里New ADODB.Connection每次都会开一个新连接如果一个事务里用了两个连接对象RollbackTrans只回滚其中一个另一部分写操作已经落地了。4.4 存储过程调用命名参数与输出参数怎么拿adCmdStoredProc和adCmdText在参数上的规则完全不同——前者支持名字由驱动转成 RPC 调用性能也更好 调用发号存储过程取回输出参数 Public Function NextBillNo(ByVal Prefix As String) As String Dim cmd As ADODB.Command Set cmd New ADODB.Command Set cmd.ActiveConnection gConn cmd.CommandType adCmdStoredProc cmd.CommandText usp_NextBillNo cmd.Parameters.Append cmd.CreateParameter(Prefix, adVarWChar, adParamInput, 10, Prefix) cmd.Parameters.Append cmd.CreateParameter(BillNo, adVarWChar, adParamOutput, 20) cmd.Execute , , adExecuteNoRecords NextBillNo cmd.Parameters(BillNo).Value End Function输出参数的Size必须给足BillNo是 20 位就给 20给小了返回值会被截断还是静默截断、不报错。取值用cmd.Parameters(BillNo).Value按名字取比按下标稳后面加参数不会把取值位置弄错。另外存储过程里如果写了SET NOCOUNT ON调用方用cmd.Execute n拿到的n会是 -1。这时候判断成功与否只能靠业务字段比如返回一个Result int输出参数不能靠受影响行数。5. 避坑排查这套源码在新机器上跑不起来的五个现场5.1 一打开工程就弹“找不到工程或库”现象.vbp双击打开编译菜单是灰的引用列表里有一项前面打着“丢失”。原因工程引用的某个组件在机器上没注册或者注册的是别的版本。老项目常见的是引用了开发机上的第三方控件拷到新机器就找不到。解决先打开“工程 → 引用”把打叉的项取消重新勾选同名的可用版本如果列表里确实没有说明缺控件文件得先把 OCX/DLL 拷到System3232 位系统或SysWOW64并注册。别急着全选取消MSADO15.dll这类核心引用去掉之后代码里所有ADODB.前缀都会报“用户定义类型未定义”。5.2 编译能过运行时提示 MSCOMCTL.OCX 或 MSFLXGRD.OCX 未注册现象开发机好好的拷到客户机上双击就报“Component MSCOMCTL.OCX or one of its dependencies not correctly registered”。原因VB6 的窗体控件是独立的 32 位 OCX安装程序没带上或者被 Office 的补丁覆盖成了不兼容的版本。解决用管理员身份开命令行跑regsvr32 C:\Windows\SysWOW64\MSCOMCTL.OCX。注意路径必须是SysWOW64因为 VB6 程序是 32 位System32下放的是 64 位版本。如果注册报 0x80004005先确认文件本身没被替换成精简版把原版文件拷进去再注册一次。5.3 连接串看着没错就是连不上现象gConn.Open直接抛“SQL Server 不存在或访问被拒绝”但用 SSMS 在同一台机器能连。原因三种可能——SQL Server 的 TCP/IP 协议没启用只开了共享内存连的是命名实例端口是动态的客户端和服务端不在同一网段防火墙拦了 1433。解决在 SQL Server 配置管理器里把TCP/IP启用IPAll的 TCP 端口固定成 1433重启服务连接串里写成Data Source192.168.1.10,1433用 IP 加端口不要用机器名加实例名防火墙放行入站 1433。命名实例如果一定要用名字连得保证 SQL Server Browser 服务在跑。5.4 查询条件里日期和中文一起出问题现象按日期段查明明有单子却查不出来按客户名模糊查带中文的查不到、英文的能查到。原因日期是拼接进去的字符串2024/03/05在服务端按mdy解析成 3 月 5 日还是 5 月 3 日取决于登录语言中文则是varchar列存nvarchar常量时发生的隐式转换。解决日期一律走adDBTimeStamp参数或者至少用yyyymmdd这种在任何DATEFORMAT下都无歧义的格式中文列统一改nvarchar查询参数用adVarWChar。改完之后记得在 SSMS 里用SET LANGUAGE切成不同语言各跑一遍确认结果一致。5.5 更新语句执行了数据没变现象UPDATE没报错界面刷新后数值还是旧的。原因第一种存储过程里有SET NOCOUNT ONExecute返回的受影响行数是 -1代码里If n 0 Then的判断永远不成立走了“失败”分支但没提示第二种连接对象的CursorLocation是adUseClient记录集是断开式的改完rs.Update只改了内存没回写数据库第三种另一个会话持着行锁UPDATE在等锁而CommandTimeout到了之后被静默取消。解决先看存储过程的NOCOUNT设置判断成功改用业务返回值断开式记录集批量回写要用UpdateBatch并且必须重新打开连接把CommandTimeout从 30 调到 60 观察是否变慢同时用sp_who2看有没有阻塞源。6. 改得动才算拿到手加一个字段、打一次包二次开发最常接到的需求是“销售单上要加一列备注”。在这套结构里一个字段最少要动六处漏一处就是各种“保存后不见了”。改动位置具体内容漏改的表现表结构ALTER TABLE SaleBill ADD Remark nvarchar(200) NULL保存报无效列名单头保存语句INSERT 的列清单和?占位各加一个备注永远为空参数列表CreateParameter(Remark, adVarWChar, adParamInput, 200, ...)类型不匹配或截断查询语句SELECT 列表加字段FillGrid自动多一列界面看不到编辑回填txtRemark.Text rs(Remark) 打开旧单备注被清空打印/报表DataReport的DataMember和文本框绑定打印出来是空白ALTER TABLE加可空列在千万级以内的表上是秒级的但如果表上有一堆索引建议放在业务低峰做。加完列之后把SELECT *的写法全部改成显式列名——这套代码里SELECT *出现得非常多多一个字段就会让FillGrid的列顺序和打印模板的字段序号全部错位那是一种查起来非常痛苦的“玄学”错位。打包交付时用 VB6 自带的打包向导依赖列表里重点是MSCOMCTL.OCX、MSFLXGRD.OCX、MSADO15.DLL和msvbvm60.dll。前三者注册最后一个不注册只拷到目标目录即可。setup.lst里的版本号要手工核对一遍如果客户机上已经有一个更高版本的同名 OCX安装程序会跳过复制结果就是“装完了还是报未注册”这时候要么提升安装包的版本号要么在安装脚本里强制覆盖后重注册。血泪经验是每次交付前我一定在一台从没装过 VB6 的干净机器上完整走一遍“装库 → 装程序 → 开单 → 打印 → 卸载重装”。开发机上永远不会暴露的控件依赖、权限、区域设置问题只有在那台干净机器上才会一起冒出来。从那以后我再也不敢只在自己机器上点两下就说“没问题”。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/31 22:26:14

OpenStack 计算管理:实例锁定后被普通用户解锁了?避坑实操

给实例上了锁,转身普通用户登录就把锁解了、机删了——这不是 bug,是 policy.json 里 unlock 那条规则默认对 owner 开放。这篇把计算管理走一遍完整闭环:主机聚合与可用分区、规格的 Public/Private、密钥对与服务器组、发放实例&#xff0c…

2026/10/31 22:26:14

Neon safekeeper任务

safekeeper 的运行时与任务架构safekeeper 使用 5 个独立的 Tokio Runtime&#xff08;通过 Lazy<Runtime> 静态变量定义&#xff09;&#xff0c;每个 runtime 运行在独立的 OS 线程池上&#xff0c;从而把 WAL 服务、HTTP 管理接口、Broker 通信、备份导出和后台维护任…

2026/10/31 23:31:23

OIer必备网站集:用角色分工和打开顺序替代链接囤积

我的浏览器书签里曾经塞过三百多个链接&#xff0c;分类文件夹从“题库”一直建到“待看”&#xff0c;结果那半年成绩纹丝不动&#xff0c;连省二都卡得死死的。后来我把整个书签栏清空重来&#xff0c;只留下每天真的会点的五六个入口&#xff0c;剩下的靠搜索和记忆去找&…

2026/10/31 23:31:23

三数之和算法解析与双指针优化实践

1. 三数之和问题解析三数之和&#xff08;3Sum&#xff09;是LeetCode上经典的算法问题&#xff0c;编号为第15题。给定一个整数数组nums&#xff0c;要求找出所有不重复的三元组[nums[i], nums[j], nums[k]]&#xff0c;使得i≠j≠k且nums[i]nums[j]nums[k]0。这个问题看似简单…

2026/10/31 23:31:23

混沌序列扩频评估指南:从四种映射到MATLAB仿真避坑手册

简介&#xff1a;这是一套面向无线通信与信息安全方向学习者的混沌扩频序列Matlab程序包&#xff0c;聚焦利用Logistic、Tent、Henon、Chebyshev等常见混沌映射生成扩频序列&#xff0c;并通过比特量化、门限量化和模2量化得到离散序列&#xff0c;再计算自相关、互相关以及游程…

2026/10/31 23:31:23

逆向工程实战:UPX脱壳与混合加密算法分析

1. 题目背景与核心思路这道来自2021年长城杯政企组的逆向工程题目"魔鬼凯撒的RC4茶室"结合了多种经典加密算法&#xff0c;考察选手对混合加密模式的识别与破解能力。题目名称中提到的"RC4茶室"暗示了核心加密逻辑&#xff0c;而"魔鬼凯撒"则提示…

2026/10/31 23:31:23

AI学术专著高效写作工具链与实践指南

1. AI学术专著写作的核心挑战学术专著写作向来是件耗时费力的工作&#xff0c;尤其是当涉及AI这样的前沿领域时。我最近完成了一本关于深度学习的专著&#xff0c;深刻体会到传统写作方式在效率和质量上的局限性。AI专著写作面临三大核心挑战&#xff1a;首先&#xff0c;技术更…

2026/10/31 23:26:21

大模型Agent开发入门:从核心原理到实战避坑指南

这些年聊大模型&#xff0c;大家从最早的“能聊天”逐渐过渡到了“能不能帮我干点正事”。而“让大模型自己干活”这件事&#xff0c;最直接的落地形态就是Agent。这个标题“大模型Agent开发入门”看起来很直白&#xff0c;但真正接触过的朋友都知道&#xff0c;从概念到能跑通…

2026/10/30 20:55:10

全国计算机二级C语言真题PDF高效刷题指南

简介&#xff1a;这份PDF是全国计算机二级C语言考试的试题与答案解析&#xff0c;专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/30 20:55:10

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介&#xff1a;一份以实战为导向的Python爬虫入门资源&#xff0c;面向具备基本编程基础、希望快速掌握网络数据抓取的初学者&#xff0c;可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档&#xff0c;完整演示从网页中提取 标题…

2026/10/31 22:14:20

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介&#xff1a;这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档&#xff0c;压缩包大小七十八KB&#xff0c;内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开&#xff1a;先构造一个极点位于单位圆内…

2026/10/31 0:02:41

猫狗目标检测数据集:VOC/COCO/YOLO三格式一体化实战包

简介&#xff1a;本资源是一套面向计算机视觉初学者与YOLO目标检测实践者的猫狗图像数据集及配套开发工具包&#xff0c;专为快速上手目标检测模型训练而设计。资源包含1000张真实场景高清猫狗图片&#xff0c;标注质量高&#xff0c;同时提供VOC&#xff08;XML&#xff09;、…

2026/10/31 0:02:41

HDU2017多校标程复现:算法竞赛工程化真题沙盒

简介&#xff1a;本资源为2017年HDU多校联合训练第一场官方配套资料&#xff0c;面向ACM程序设计竞赛备赛学生、算法爱好者及高校编程集训队成员&#xff0c;旨在提供权威解题参考与完整测试验证环境。压缩包共40个文件&#xff0c;含15份C标程&#xff08;覆盖全部赛题&#x…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑