前言
VSTO https://en.wikipedia.org/wiki/Visual_Studio_Tools_for_Office 是一套用于创建自定义 Office 应用程序的 Visual Studio 工具包, 通过 Interop 提供的增强 Office 对象, 可以对 Word 文档进行编程操作. Range https://docs.microsoft.com/en-us/dotnet/api/microsoft.office.interop.word.range?view=word-pia 是 Word 中执行操作的一个单元, 可以理解成文档中一个选中的部分或者区域, 针对这个选中部分, 可以应用格式, 修改文字和颜色等功能. 在相同的文档和不同的文档之间, 将 Range 的内容从原处复制到其他的 Range 中, 是一种常见的业务需求. 本文总结了常见的几种 Range 复制方式, 对其特点进行了讨论分析, 并提出一些改良的地方.
剪贴板复制
剪贴板是 windows 进程间通信 https://blog.csdn.net/microzone/article/details/7044266 的一种方式. 原理是这样的, Range.Copy() 将内容复制到剪贴板中, 然后
AnotherRange.Paste()
从剪贴板中获取数据, 然后复制到自己的区域内.
- // 原理示意
- /// <summary>
- /// range 间的复制
- /// </summary>
- /// <param name="source"> 源 range</param>
- /// <param name="target"> 目标 range</param>
- public void Copy(Word.Range source, Word.Range target)
- {
- source.Copy();
- target.Paste();
- }
这种方式可靠性低, 速度慢. 就像薛定谔的猫一样, 有时候可用, 有时候不可用. 原因在于使用了剪贴板作为中介, 许多进程都在使用剪贴板, 显而易见, 使用剪贴板有许多未知的问题.
改良
为了缓解这种弊端, 我们稍微做下修改, 在剪贴板失败的时候, 进行重试, 大于一定次数就抛出异常. 假设剪贴板失败是独立不相干的事件, 可以在概率上提高成功的可能性. 连续多次失败的概率将会变得很小, 除非存在显著的剪贴板问题. 依据的原理是贝努利分布, 这里不予证明.
- /// <summary>
- /// range 间的复制
- /// </summary>
- /// <param name="source"> 源 range</param>
- /// <param name="target"> 目标 range</param>
- public void Copy(Word.Range source, Word.Range target)
- {
- int num = 0;
- // 重试总次数
- int limitNum = 5;
- retry:
- try
- {
- source.Copy();
- target.Paste();
- }
- catch(Exception)
- {
- num++;
- // 连续多次失败, 就抛出异常
- if (num> limitNum)
- {
- throw;
- }
- goto retry;
- }
- }
XML 复制
我们知道 Word 文档其实是一个 OpenXml 的结构树, 一个复杂无比的 XML, 所以 Word 中的元素是 XML 的一部分, 也是一个 XML. 参考 HMTL 来说, 两个相同的标签内容就是相同的两个标签.
同理通过 Range.XML 获取到 Range 的 XML 文本结构, 再通过
AnotherRange.InsertXML()
方法插入到目标 range 的区域, 就完成了复制.
- /// <summary>
- /// range 间的复制
- /// </summary>
- /// <param name="source"> 源 range</param>
- /// <param name="target"> 目标 range</param>
- public void Copy(Word.Range source, Word.Range target)
- {
- target.InsertXML(source.XML);
- }
这种方式的稳定性比剪贴板的强, 不存在中间过程的通信转换, 速度也快一些. 但是这种方式, 也不一定完全就能成功, 推测是 Word 的格式兼容性, 有些 Word 文档的 XML 不能复制, 或许是旧版本的 Word 文档, 或许是某个版本 Wps 编辑的文档, 原因很难探究, 毕竟 Word 是世界上最复杂的软件之一了.
混合复制
通过上面的两种复制方式的陈述, 我们知道这两种方式都有各自的特点.
方式 | 速度 | 可靠性 | 格式兼容性 | |
---|---|---|---|---|
剪贴板 | 慢 | 低 | 高 | |
XML | 快 | 中 | 中 |
这两种复制方式都有可能失败. 为了避免这种情况, 可以设计一个方案, 优先使用 XML 进行复制, 如果失败了, 退化到使用剪贴板复制; 重试若干次, 直到成功或者超过重试次数.
- /// <summary>
- /// range 间的复制
- /// </summary>
- /// <param name="source"> 源 range</param>
- /// <param name="target"> 目标 range</param>
- public void Copy(Word.Range source, Word.Range target)
- {
- int num = 0;
- int limitNum = 5;
- retryCopy:
- // 偶数次使用 XML 复制
- if (num % 2 == 0)
- {
- //no
- }
- // 奇数次使用剪贴板复制
- else
- {
- source.Copy();
- }
- try
- {
- if (num % 2 == 0)
- {
- target.InsertXML(source.XML);
- }
- else
- {
- target.Paste();
- }
- }
- catch (Exception)
- {
- // 失败了进行重试
- num++;
- // 超过重试次数, 抛出异常
- if (num> limitNum)
- {
- throw;
- }
- goto retryCopy;
- }
- }
来源: https://www.cnblogs.com/senhtry/p/9335265.html