You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

COM异常:文档提示锁定无法编辑但实际未锁定的问题排查

Word COM异常:添加注释时文档锁定错误(由ContentControlOnEnter事件触发)

问题背景

在Word文档设置为WdProtectionType.wdAllowOnlyComments保护模式下,调用代码添加注释时,偶尔会触发文档被锁定无法编辑的COM异常:

public IWordComment AddComment(int scopeStart, int scopeEnd, string text)
{
    var scopeRange = _document.Range(scopeStart, scopeEnd);
    object cText = text;
    Comment newComment = null;
    try
    {
        newComment = _document.Comments.Add(scopeRange, cText);
    }
    catch (Exception ex)
    {
        var itisntlockedthough = _document.ProtectionType;
        // 异常处理逻辑
        ...
    }
    // 后续逻辑
}
  • 异常触发无规律:同一组参数调用5次,会随机出现2次错误,输入参数无明显共性。
  • 重试可成功但遗留空注释:立即用相同参数重试操作能成功,但目标位置会留下一条空注释,且catch块中无法直接在Comments集合中检测到该空注释。
  • 已排除常规锁定因素:文档保护模式未改动,无受保护区域或锁定的内容控件。

问题定位

经过排查,确认异常由Document_ContentControlOnEnter事件处理程序触发。推测当添加注释的范围位于当前光标所在的内容控件之外时,Word会自动切换焦点到目标内容控件,触发该事件,进而引发锁定异常。

事件处理代码如下:

private void Document_ContentControlOnEnter(ContentControl cc)
{
    // 同步选中ReviewViewModel中对应的项
    if (cc.ParentContentControl == null )
    {
        var tag = cc.Tag;
        if (tag == null)
        {
            return;
        }
        if (tag.StartsWith("tbl:"))
        {
            var id = int.Parse(cc.Tag.Substring(4));
            _reviewViewModel.SelectTable(id);
        }
        else if (!tag.StartsWith("var:"))
        {
            Guid id;
            if (Guid.TryParse(cc.Tag, out id))
            {
                _reviewViewModel.SelectItem(id);
            }
        }
    }
} 

原因分析

虽然事件处理程序中没有显式执行文档锁定操作,但Word对象模型的操作存在状态竞争:

  1. 添加注释时,Word内部会调整文档焦点到目标内容控件,触发ContentControlOnEnter事件。
  2. 事件中对ViewModel的操作(SelectTable/SelectItem)可能间接引发UI状态更新,导致Word对象模型的内部状态与添加注释的操作产生冲突。
  3. 这种冲突会被Word判定为文档锁定,抛出COM异常。第一次调用失败时,Word可能已经创建了空注释对象但未完成文本写入,导致重试后出现重复注释。

解决方案

在添加注释的前后,临时解绑并重新绑定ContentControlOnEnter事件,避免事件触发干扰注释添加操作:

修改AddComment方法如下:

public IWordComment AddComment(int scopeStart, int scopeEnd, string text)
{
    var scopeRange = _document.Range(scopeStart, scopeEnd);
    object cText = text;
    Comment newComment = null;
    // 临时解绑事件
    _document.ContentControlOnEnter -= Document_ContentControlOnEnter;
    try
    {
        newComment = _document.Comments.Add(scopeRange, cText);
    }
    catch (Exception ex)
    {
        var itisntlockedthough = _document.ProtectionType;
        // 清理目标位置的空注释后重试
        foreach (Comment existingComment in _document.Comments)
        {
            if (existingComment.Range.Start == scopeStart && existingComment.Range.End == scopeEnd && string.IsNullOrEmpty(existingComment.Range.Text))
            {
                existingComment.Delete();
                break;
            }
        }
        // 重试操作
        newComment = _document.Comments.Add(scopeRange, cText);
    }
    finally
    {
        // 无论成功或失败,重新绑定事件
        _document.ContentControlOnEnter += Document_ContentControlOnEnter;
    }
    return newComment;
}

额外验证点

  1. 空注释检测:在catch块中遍历_document.Comments集合,根据注释的范围和文本内容判断是否存在空注释,若存在则删除后再重试。
  2. 事件操作优化:若ViewModel的SelectTable/SelectItem操作涉及UI线程同步,可考虑改为异步执行,避免阻塞Word对象模型的操作。

内容的提问来源于stack exchange,提问作者Peter Dongan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 10:45:20