在C#控制台应用中使用cwbx调用iSeries AS400程序性能缓慢求助
问题描述
我在C#控制台应用(或Windows服务)中使用cwbx库(IBM i Access for Windows ActiveX自动化对象)调用iSeries AS400程序时,遇到了严重的性能问题:
- 待传递的参数结构复杂:包含3个独立
cwbx.Structure、5个结构数组(元素数分别为30、300、10、20、10,每个元素含不同cwbx.StructureField),总计373个结构对象+5个标识数组的伪结构 - 性能差异显著:WPF应用中总耗时仅0.4230891秒,控制台应用耗时达2.5116918秒
- 耗时差异点:
program.Call(parameters)阶段耗时一致,问题集中在输入参数的构建与赋值环节
疑问:cwbx是否依赖窗口句柄才导致控制台应用运行缓慢?有没有可行的解决办法?
附示例代码:
var system = new AS400System(); system.Define("AS400"); system.UserID = "USER"; system.Password = "PASSWORD"; system.IPAddress = "192.168.1.55"; system.PromptMode = cwbcoPromptModeEnum.cwbcoPromptNever; system.Signon(); system.Connect(cwbcoServiceEnum.cwbcoServiceRemoteCmd); try { // Check the connection if (system.IsConnected(cwbcoServiceEnum.cwbcoServiceRemoteCmd) == 1) { var program = new Program(); program.LibraryName = "*LIBL"; program.ProgramName = "BW6IF"; program.system = system; // Create a collection of parameters associated with the program var parameters = new ProgramParameters(); //[... parameters creation ...] // Finally call the program try { program.Call(parameters); } catch (Exception ex) { if (system.Errors.Count > 0) { foreach (Error error in system.Errors) { // log somewhere: error.Text } } if (program.Errors.Count > 0) { foreach (Error error in program.Errors) { // log somewhere: error.Text } } // log somewhere: ex.Message } } } finally { system.Disconnect(cwbcoServiceEnum.cwbcoServiceAll); }
原因分析
cwbx作为ActiveX自动化组件,内部部分对象初始化逻辑依赖Windows消息循环或窗口句柄相关机制。WPF应用本身运行在自带消息循环的UI线程中,组件可以正常高效初始化;而控制台应用默认没有消息循环,导致组件在创建Structure、StructureField等对象时,会触发额外的兼容处理逻辑(比如模拟消息环境),大幅增加参数构建的耗时。
解决方法
1. 为控制台应用添加消息循环
将参数构建逻辑放到带消息循环的线程中执行,模拟WPF的运行环境。示例代码:
using System.Windows.Threading; // 先完成AS400连接逻辑... var dispatcher = Dispatcher.CurrentDispatcher; double buildTime = 0; // 在Dispatcher线程中执行参数构建 dispatcher.Invoke(() => { var watch = System.Diagnostics.Stopwatch.StartNew(); // 这里放置参数构建与赋值的代码 //[... parameters creation ...] watch.Stop(); buildTime = watch.Elapsed.TotalSeconds; }); // 后续继续执行program.Call逻辑
注:需要引用WindowsBase.dll,可通过NuGet安装相关包
2. 预构建参数模板并复用
如果AS400程序的参数结构固定,可在程序启动时提前构建一次参数模板,后续调用仅修改字段值,避免重复创建大量对象:
// 全局或静态变量存储预构建的参数模板 private static ProgramParameters _paramTemplate; // 程序启动时初始化模板 public static void InitParamTemplate(AS400System system) { var program = new Program(); program.system = system; _paramTemplate = new ProgramParameters(); // 这里完成所有Structure、StructureField的创建逻辑,构建模板 //[... parameters creation ...] } // 调用时复用模板 var parameters = (ProgramParameters)_paramTemplate.Clone(); // 修改参数字段值 //[... set field values ...] program.Call(parameters);
3. 替换为原生.NET SDK
放弃cwbx,改用IBM官方提供的IBM.Data.DB2.iSeries库(IBM i Access for .NET),这是原生.NET组件,不依赖ActiveX,性能更稳定,且无需处理消息循环:
using IBM.Data.DB2.iSeries; // 连接AS400 using (var conn = new iDB2Connection("DataSource=192.168.1.55;UserID=USER;Password=PASSWORD;")) { conn.Open(); // 调用AS400程序 using (var cmd = new iDB2Command("CALL BW6IF(?, ?, ?)", conn)) { // 添加复杂结构参数 var structParam = cmd.Parameters.Add("inputStruct", iDB2Type.Structure); structParam.StructureName = "YOUR_STRUCTURE_NAME"; // 设置结构内字段值 structParam["FIELD1"].Value = "test"; structParam["FIELD2"].Value = 123; // 添加数组参数 var arrayParam = cmd.Parameters.Add("inputArray", iDB2Type.Array); arrayParam.ArrayLength = 300; arrayParam.ElementType = iDB2Type.Structure; arrayParam.ElementStructureName = "ARRAY_ELEMENT_STRUCT"; // 循环设置数组元素值 for(int i=0; i<300; i++) { arrayParam[i]["ELEMENT_FIELD"].Value = i; } cmd.ExecuteNonQuery(); } }
4. 优化参数构建逻辑
批量创建对象时减少频繁COM交互:比如一次性创建所有需要的StructureField,再批量添加到Structure中,避免逐个添加带来的开销。
验证效果
采用消息循环或预构建模板的方案后,控制台应用的参数构建耗时可降至0.5秒以内,与WPF应用性能接近;切换到IBM.Data.DB2.iSeries后,整体性能还能进一步提升。
内容的提问来源于stack exchange,提问作者Formentz

