wps vba 问题

BUG反馈 · 32 次浏览
Qiu Cool 创建于 1小时38分钟前

WPS 里,通过Quicker运行简单vba正常,运行复杂一点的就报错。若把这段复杂vba粘贴到wps vba编辑器里运行又正常,请问有办法解决吗

 

报错内容:

异常来自 HRESULT:0x800A03EC  StackTrace:   在 System.Dynamic.ComRuntimeHelpers.CheckThrowException(Int32 hresult, ExcepInfo& excepInfo, UInt32 argErr, String message)
   在 CallSite.Target(Closure , CallSite , ComObject , String )
   在 rhqtumo6Q8hFbw9ibm.UZkyEYqdyyKVUCV6JO.L5Fk4cJJt(Command  )
   在 LPAgent.Program.xFlnyFxF5(NamedPipeConnection`2  , String  )
步骤(sys:officehelper)执行失败,原因:命令返回失败,错误:异常来自 HRESULT:0x800A03EC
停止动作:命令返回失败,错误:异常来自 HRESULT:0x800A03EC

▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰

代码内容(通过Quicker在wps运行报错,在wps里粘贴运行正常,在office里也运行正常):

Sub 统计选中区域单元格出现频次()
    Dim selectedRng As Range
    Dim cell As Range
    Dim dict As Object
    Dim wsResult As Worksheet
    
    Application.ScreenUpdating = False
    
    ' 校验选中对象是否为单元格区域
    If TypeName(Selection) <> "Range" Then
        MsgBox "请先选中需要统计的单元格区域!", vbExclamation, "提示"
        Application.ScreenUpdating = True
        Exit Sub
    End If
    
    Set selectedRng = Selection
    Set dict = CreateObject("Scripting.Dictionary") ' 后期绑定,无需手动引用库
    
    ' 遍历选中区域,累计频次
    For Each cell In selectedRng
        If cell.Value <> "" Then ' 跳过空单元格
            dict(cell.Value) = dict(cell.Value) + 1
        End If
    Next
    
    If dict.Count = 0 Then
        MsgBox "选中区域内无有效数据!", vbInformation, "提示"
        Application.ScreenUpdating = True
        Exit Sub
    End If
    
    ' 新建工作表存放结果
    Set wsResult = Worksheets.Add(after:=Sheets(Sheets.Count))
    wsResult.Name = "频次统计_" & Format(Now, "hhmmss") ' 带时间戳避免重名
    
    ' 写入表头与统计数据
    wsResult.Range("A1:B1") = Array("单元格内容", "出现频次")
    wsResult.Range("A2").Resize(dict.Count, 1) = Application.Transpose(dict.Keys)
    wsResult.Range("B2").Resize(dict.Count, 1) = Application.Transpose(dict.Items)
    
    ' 按频次降序排序 + 自动适配列宽
    wsResult.Range("A1").CurrentRegion.Sort _
        Key1:=wsResult.Range("B1"), _
        Order1:=xlDescending, _
        Header:=xlYes
    wsResult.Columns("A:B").AutoFit
    
    Application.ScreenUpdating = True
    MsgBox "统计完成,结果已生成至新工作表。", vbInformation, "完成"
End Sub

 

 

其他背景:公司电脑装了奇安信安全软件,限制其他软件的安装,用不了我是让it部解除了对wps vba 模块的限制才装上,不知道是不是这个软件在底层搞得鬼

Qiu Cool 最后更新于 2026/6/27

回复内容
Qiu Cool 1小时24分钟前
#1

不得不说国外大模型claude、gpt确实强,gpt给我解决了


以下是在WPS里也能运行的代码 ↓ 


Sub FreqCountMain()
    On Error GoTo EH
    
    Dim selectedRng As Range
    Dim cell As Range
    Dim dict As Object
    Dim wb As Workbook
    Dim wsResult As Worksheet
    Dim v As Variant
    Dim k As Variant
    Dim arr() As Variant
    Dim i As Long, j As Long
    Dim tmp1 As Variant, tmp2 As Variant
    Dim sheetName As String
    
    Application.ScreenUpdating = False
    
    If TypeName(Application.Selection) <> "Range" Then
        MsgBox "请先选中需要统计的单元格区域!", vbExclamation, "提示"
        GoTo CleanExit
    End If
    
    Set selectedRng = Application.Selection
    Set wb = selectedRng.Worksheet.Parent
    Set dict = CreateObject("Scripting.Dictionary")
    
    ' 统计频次
    For Each cell In selectedRng.Cells
        v = cell.Value
        
        If Not IsError(v) Then
            If Len(CStr(v)) > 0 Then
                k = CStr(v)
                
                If dict.Exists(k) Then
                    dict(k) = CLng(dict(k)) + 1
                Else
                    dict.Add k, 1
                End If
            End If
        End If
    Next cell
    
    If dict.Count = 0 Then
        MsgBox "选中区域内无有效数据!", vbInformation, "提示"
        GoTo CleanExit
    End If
    
    ' 写入数组
    ReDim arr(1 To dict.Count, 1 To 2)
    
    i = 0
    For Each k In dict.Keys
        i = i + 1
        arr(i, 1) = k
        arr(i, 2) = dict(k)
    Next k
    
    ' 在数组中按出现频次降序排列,避免调用 WPS 的 Range.Sort
    For i = 1 To UBound(arr, 1) - 1
        For j = i + 1 To UBound(arr, 1)
            If CLng(arr(j, 2)) > CLng(arr(i, 2)) Then
                tmp1 = arr(i, 1)
                tmp2 = arr(i, 2)
                
                arr(i, 1) = arr(j, 1)
                arr(i, 2) = arr(j, 2)
                
                arr(j, 1) = tmp1
                arr(j, 2) = tmp2
            End If
        Next j
    Next i
    
    ' 新建结果工作表
    Set wsResult = wb.Worksheets.Add(After:=wb.Worksheets(wb.Worksheets.Count))
    
    sheetName = "频次统计_" & Format$(Now, "hhmmss")
    
    On Error Resume Next
    wsResult.Name = sheetName
    If Err.Number <> 0 Then
        Err.Clear
        wsResult.Name = "Freq_" & Format$(Now, "hhmmss")
    End If
    On Error GoTo EH
    
    ' 写入结果
    wsResult.Range("A1").Value = "单元格内容"
    wsResult.Range("B1").Value = "出现频次"
    wsResult.Range("A2").Resize(dict.Count, 2).Value = arr
    
    wsResult.Columns("A:B").AutoFit
    
    'MsgBox "统计完成,结果已生成至新工作表。", vbInformation, "完成"

CleanExit:
    Application.ScreenUpdating = True
    Exit Sub

EH:
    Application.ScreenUpdating = True
    MsgBox "执行失败:" & Err.Number & vbCrLf & Err.Description, vbExclamation, "VBA错误"
End Sub


▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰


chat gpt 完整分析过程


我先按“Quicker 调 WPS 的 COM/宏执行链路”和“VBA 代码本身在外部调用时的兼容性”两条线排查。你这个现象很像不是 VBA 逻辑错,而是 Quicker 把整段代码/命令传给 WPS 时某个语法或参数被 WPS COM 层拒绝。

我查到 Quicker 官方文档里有几条与你情况高度相关:WPS 需要安装 VBA 模块并开启“信任对于 Visual Basic 项目的访问”,而且 WPS 版本/组件差异会影响兼容性;同时 Quicker 的 Office 辅助是低权限方式,遇到提权启动、多个同名进程时也容易抓错对象或控制失败。

可以解决。你这个更像 Quicker → WPS 的 COM/VBA 注入执行链路问题,不是这段 VBA 逻辑本身错。因为同一段代码粘到 WPS VBA 编辑器里能正常跑,说明 WPS 的 VBA 运行环境基本没问题。

Quicker 官方文档也提示了几个关键点:WPS 需要安装 VBA 模块并开启“信任对于 Visual Basic 项目的访问”;WPS 版本和组件差异较大,不一定都能稳定运行;多个同名 WPS/Office 进程时,底层 COM 可能抓到错误的活动文档;Quicker 的 Office 辅助是低权限方式,不能控制管理员权限启动的 Office/WPS。

优先推荐的解决方案

方案 1:不要让 Quicker 直接执行整段复杂 VBA,只让它“调用已有宏”

这是最稳的。

把下面这段宏保存到 WPS VBA 模块里,例如目标文件的普通模块中。然后 Quicker 的“VBA宏名称或代码”里只填:

FreqCountMain

Quicker 文档明确支持“填写文档中已有宏的名称”,否则才填写完整宏代码。

这样做的好处是:Quicker 不再需要把整段代码注入 WPS,只是调用一个现成宏,能绕开很多 0x800A03EC、编码、注入、权限、VBProject 访问的问题。

我建议你改用这个版本

这个版本专门规避了几个 Quicker/WPS 外部调用时容易出问题的点:中文过程名、未限定 Workbook/Worksheet、Application.Transpose(dict.Keys)、错误值单元格、ScreenUpdating 未恢复等。

Sub FreqCountMain()
On Error GoTo EH

Dim selectedRng As Range
Dim cell As Range
Dim dict As Object
Dim wb As Workbook
Dim wsResult As Worksheet
Dim v As Variant
Dim k As Variant
Dim arr() As Variant
Dim i As Long
Dim sheetName As String

Application.ScreenUpdating = False

If TypeName(Application.Selection) <> "Range" Then
MsgBox "请先选中需要统计的单元格区域!", vbExclamation, "提示"
GoTo CleanExit
End If

Set selectedRng = Application.Selection
Set wb = selectedRng.Worksheet.Parent
Set dict = CreateObject("Scripting.Dictionary")

For Each cell In selectedRng.Cells
v = cell.Value

If Not IsError(v) Then
If Len(CStr(v)) > 0 Then
k = CStr(v)

If dict.Exists(k) Then
dict(k) = CLng(dict(k)) + 1
Else
dict.Add k, 1
End If
End If
End If
Next cell

If dict.Count = 0 Then
MsgBox "选中区域内无有效数据!", vbInformation, "提示"
GoTo CleanExit
End If

Set wsResult = wb.Worksheets.Add(After:=wb.Worksheets(wb.Worksheets.Count))

sheetName = "频次统计_" & Format$(Now, "hhmmss")
On Error Resume Next
wsResult.Name = sheetName
If Err.Number <> 0 Then
Err.Clear
wsResult.Name = "Freq_" & Format$(Now, "hhmmss")
End If
On Error GoTo EH

wsResult.Range("A1").Value = "单元格内容"
wsResult.Range("B1").Value = "出现频次"

ReDim arr(1 To dict.Count, 1 To 2)

i = 0
For Each k In dict.Keys
i = i + 1
arr(i, 1) = k
arr(i, 2) = dict(k)
Next k

wsResult.Range("A2").Resize(dict.Count, 2).Value = arr

wsResult.Range("A1").CurrentRegion.Sort _
Key1:=wsResult.Range("B1"), _
Order1:=-4121, _
Header:=1

wsResult.Columns("A:B").AutoFit

MsgBox "统计完成,结果已生成至新工作表。", vbInformation, "完成"

CleanExit:
Application.ScreenUpdating = True
Exit Sub

EH:
Application.ScreenUpdating = True
MsgBox "执行失败:" & Err.Number & vbCrLf & Err.Description, vbExclamation, "VBA错误"
End Sub

若还想在 Quicker 里直接放整段代码

可以用下面格式,过程名尽量用英文。Quicker 文档说新版支持用 'main:宏名 指定入口;但如果你的 Quicker 版本较旧,就删掉第一行,让 Sub FreqCountMain() 成为第一行。

'main:FreqCountMain
Sub FreqCountMain()
'这里放上面完整代码
End Sub

排查顺序建议

先按这个顺序来,成功率最高:

  1. 任务管理器里结束所有 WPS 相关进程,尤其是 et.exewps.exe
  2. 不要通过 Quicker 启动 WPS,改成双击表格文件或从开始菜单打开。
  3. 确保 WPS 不是“以管理员身份运行”,Quicker 也不要和 WPS 权限级别不一致。
  4. WPS 打开后,先点一下其他窗口,再切回 WPS,或最小化/还原一次,再运行 Quicker。
  5. Quicker 里优先只填宏名 FreqCountMain,不要直接放整段复杂代码。
  6. WPS 设置里确认已开启“信任对于 Visual Basic 项目的访问”。

第 4 点也有依据:Office 自动化里,外部程序用 GetObject/GetActiveObject 连接已运行的 Office 实例时,可能遇到 ROT 注册延迟;Office 应用通常在失去焦点后才把运行对象注册到 ROT,切换焦点/最小化还原可触发注册。 Quicker 社区也有人用类似思路解决过 0x800A03EC。

奇安信有没有可能是原因?

有可能,但不是第一嫌疑。

更准确地说:如果公司安全软件限制了 跨进程 COM 调用、VBProject 注入、脚本组件 CreateObject,Quicker 这种外部自动化工具就可能报 0x800A03EC;但你在 WPS VBA 编辑器里直接运行正常,只能证明 WPS 内部宏能跑,不能证明外部程序注入宏也没被拦。

可以用这个小宏在 Quicker 里测试:

Sub TestDict()
Dim d As Object
Set d = CreateObject("Scripting.Dictionary")
d.Add "a", 1
MsgBox d("a")
End Sub

如果这个也报错,重点查安全策略、脚本组件、WPS VBA 信任设置。
如果这个正常,再测:

Sub TestAddSheet()
Dim ws As Worksheet
Set ws = ActiveWorkbook.Worksheets.Add
ws.Range("A1").Value = "ok"
End Sub

如果 TestDict 正常但 TestAddSheet 报错,多半是 Quicker 抓错 WPS 实例、活动工作簿不对、多个 WPS 进程、权限级别不一致。Microsoft 对类似 Excel 自动化错误也说明,COMException/0x800A03EC 这类错误常和外部自动化环境、文件/进程状态、Office 安装配置有关,不一定是 VBA 语法本身。

我的建议:最终采用“宏存在 WPS 里,Quicker 只调用宏名”的方式。这对公司电脑、WPS、Quicker、安全软件组合来说最稳。

你这个代码确实成功了,但还是有个小报错,如图,并且结果没有按降序排列。

确实是我上一版代码里 排序常量写错了

我写的是:

Order1:=-4121

-4121 不是“降序”,它更接近 xlDown 这类方向常量。
真正的降序应该是:

Order1:=2

也就是 xlDescending = 2

所以你看到的现象刚好对得上:前面统计和生成工作表成功了,执行到排序时报“无效的过程调用”,因此结果没有降序排列。


最小修改版

如果你想保留原结构,只改这一段:

wsResult.Range("A1").CurrentRegion.Sort _
Key1:=wsResult.Range("B1"), _
Order1:=2, _
Header:=1

也就是把:

Order1:=-4121

改成:

Order1:=2

更推荐:完全绕开 WPS 的 Sort 方法

WPS 通过 Quicker 调 VBA 时,Range.Sort 有时比 Excel 更容易抽风。下面这个版本直接在 VBA 数组里排好序,再写入表格,不依赖 WPS 的排序功能,更稳。

你可以直接替换之前的 FreqCountMain

Sub FreqCountMain()
On Error GoTo EH

Dim selectedRng As Range
Dim cell As Range
Dim dict As Object
Dim wb As Workbook
Dim wsResult As Worksheet
Dim v As Variant
Dim k As Variant
Dim arr() As Variant
Dim i As Long, j As Long
Dim tmp1 As Variant, tmp2 As Variant
Dim sheetName As String

Application.ScreenUpdating = False

If TypeName(Application.Selection) <> "Range" Then
MsgBox "请先选中需要统计的单元格区域!", vbExclamation, "提示"
GoTo CleanExit
End If

Set selectedRng = Application.Selection
Set wb = selectedRng.Worksheet.Parent
Set dict = CreateObject("Scripting.Dictionary")

' 统计频次
For Each cell In selectedRng.Cells
v = cell.Value

If Not IsError(v) Then
If Len(CStr(v)) > 0 Then
k = CStr(v)

If dict.Exists(k) Then
dict(k) = CLng(dict(k)) + 1
Else
dict.Add k, 1
End If
End If
End If
Next cell

If dict.Count = 0 Then
MsgBox "选中区域内无有效数据!", vbInformation, "提示"
GoTo CleanExit
End If

' 写入数组
ReDim arr(1 To dict.Count, 1 To 2)

i = 0
For Each k In dict.Keys
i = i + 1
arr(i, 1) = k
arr(i, 2) = dict(k)
Next k

' 在数组中按出现频次降序排列,避免调用 WPS 的 Range.Sort
For i = 1 To UBound(arr, 1) - 1
For j = i + 1 To UBound(arr, 1)
If CLng(arr(j, 2)) > CLng(arr(i, 2)) Then
tmp1 = arr(i, 1)
tmp2 = arr(i, 2)

arr(i, 1) = arr(j, 1)
arr(i, 2) = arr(j, 2)

arr(j, 1) = tmp1
arr(j, 2) = tmp2
End If
Next j
Next i

' 新建结果工作表
Set wsResult = wb.Worksheets.Add(After:=wb.Worksheets(wb.Worksheets.Count))

sheetName = "频次统计_" & Format$(Now, "hhmmss")

On Error Resume Next
wsResult.Name = sheetName
If Err.Number <> 0 Then
Err.Clear
wsResult.Name = "Freq_" & Format$(Now, "hhmmss")
End If
On Error GoTo EH

' 写入结果
wsResult.Range("A1").Value = "单元格内容"
wsResult.Range("B1").Value = "出现频次"
wsResult.Range("A2").Resize(dict.Count, 2).Value = arr

wsResult.Columns("A:B").AutoFit

MsgBox "统计完成,结果已生成至新工作表。", vbInformation, "完成"

CleanExit:
Application.ScreenUpdating = True
Exit Sub

EH:
Application.ScreenUpdating = True
MsgBox "执行失败:" & Err.Number & vbCrLf & Err.Description, vbExclamation, "VBA错误"
End Sub

你这次这个报错不是奇安信导致的,主要是我上一版排序常量写错了。
建议你直接用上面这个“数组内排序版”,以后通过 Quicker 调 WPS 会更稳。



Qiu Cool 最后更新于 17 分钟前
CL 回复 Qiu Cool 1小时23分钟前 :

咋解决的?

Qiu Cool 回复 CL 18 分钟前 :

就是换了一串代码,原来那代码在 office 本身也能运行,但在WPS环境下有问题。ChatGPT诊断后改了一下,在WPS里面就可以运行了

回复主贴