WinForms股票模拟器打开Excel文件不稳定,报'call was rejected by callee'错误
解决Excel互操作"call was rejected by callee"错误的代码修改方案
针对你WinForms股票模拟器读取Excel时的稳定性问题,以下是具体的代码修改点:
1. 修复COM对象释放的逻辑错误
你的Finally块里对象判断有问题,会导致Workbook和Excel实例没法正确释放,残留的COM进程容易引发后续访问冲突,改成这样:
Finally xl.DisplayAlerts = True xl.ScreenUpdating = True ' 先释放Worksheet If ws IsNot Nothing Then Marshal.ReleaseComObject(ws) ws = Nothing End If ' 再关闭并释放Workbook,判断条件改成wb是否存在 If wb IsNot Nothing Then wb.Close(False) Marshal.ReleaseComObject(wb) wb = Nothing End If ' 最后退出并释放Excel实例 If xl IsNot Nothing Then xl.Quit() Marshal.ReleaseComObject(xl) xl = Nothing End If End Try
2. 别依赖ActiveSheet,直接用已获取的Worksheet
原代码里xl.ActiveSheet.UsedRange.Rows.Count靠当前激活的工作表,可能因为Excel内部状态变化出错,直接用已经拿到的ws就行:
' 替换原totalRow的获取方式 Dim totalRow As Integer = ws.UsedRange.Rows.Count
遍历单元格的时候也直接用ws,别重复调用xl.Worksheets("Sheet1"):
For i = 1 To totalRow With ws ticker = .Range("B" & i.ToString).Value name = .Range("C" & i.ToString).Value val = .Range("D" & i.ToString).Value vol = .Range("E" & i.ToString).Value mktcap = .Range("I" & i.ToString).Value owned = 0 ' 原代码里owned是Long类型,False是布尔值,这里修正类型匹配 End With ' ... 后面的逻辑不变 Next
3. 给Excel后台操作加等待,避免请求被拒绝
wb.RefreshAll()之后Excel可能在后台刷新数据,这时候直接读会触发"call was rejected by callee",得等刷新完:
wb.RefreshAll() ' 等所有异步刷新操作完成 xl.CalculateUntilAsyncQueriesDone() ' 加个短暂延迟,确保Excel处理完 System.Threading.Thread.Sleep(500)
同时给Excel实例加个禁用事件的设置,减少后台干扰:
xl.DisplayAlerts = False xl.ScreenUpdating = False xl.EnableEvents = False ' 新增:禁用Excel事件,避免触发不必要的操作
4. 优化文件打开参数,加重试机制
原打开逻辑没重试,而且部分参数可以优化,加个重试逻辑应对临时的文件访问冲突:
' 替换原wb = xl.Workbooks.Open(...)的代码,加重试 Dim retryCount As Integer = 3 Dim openSuccess As Boolean = False While retryCount > 0 AndAlso Not openSuccess Try wb = xl.Workbooks.Open( Filename:=file, ReadOnly:=True, UpdateLinks:=False, IgnoreReadOnlyRecommended:=True, ' 新增:忽略只读推荐提示 Editable:=False ' 新增:明确设为不可编辑,减少锁冲突 ) openSuccess = True Catch ex As Exception retryCount -= 1 If retryCount > 0 Then System.Threading.Thread.Sleep(1000) ' 等1秒再重试 End If End Try End While If Not openSuccess Then MessageBox.Show("无法打开Excel文件,请稍后重试") Application.Exit() Return End If
5. 批量读取单元格数据,减少COM交互次数
逐单元格读会增加COM调用次数,容易出错,改成一次性把整个数据区域读到数组里:
' 替换原遍历逻辑,先读整个数据区域 Dim dataRange As Excel.Range = ws.UsedRange Dim dataArray As Object(,) = dataRange.Value ' 遍历数组(注意数组索引从1开始) For i = 1 To totalRow ticker = dataArray(i, 2).ToString ' B列是第2列 name = dataArray(i, 3).ToString ' C列是第3列 val = CDbl(dataArray(i, 4)) ' D列是第4列 vol = CLng(dataArray(i, 5)) ' E列是第5列 mktcap = CDbl(dataArray(i, 9)) ' I列是第9列 owned = 0 name = FormatStockName(name) ' ... 后面创建Stock对象并加进列表的逻辑不变 Next ' 释放数据区域的COM对象 If dataRange IsNot Nothing Then Marshal.ReleaseComObject(dataRange) dataRange = Nothing End If
6. 修正IsFileLocked检查的局限性
原检查完到打开文件之间有时间差,文件可能被其他进程锁定,建议把检查逻辑整合到重试流程里,或者用更可靠的检查方式:
' 可以替换原IsFileLocked函数,改成这样 Private Function IsFileLocked(filePath As String) As Boolean Try Using fs As New FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None) fs.Close() End Using Return False Catch Return True End Try End Function
然后在重试循环里先检查:
While retryCount > 0 AndAlso Not openSuccess If IsFileLocked(file) Then retryCount -= 1 System.Threading.Thread.Sleep(1000) Continue While End If ' ... 尝试打开文件的代码 End While
内容的提问来源于stack exchange,提问作者wizard
相关产品推荐
相关产品推荐

