有个需求是,程序导出一份word报告,报告中有各种各样的表格,导出时还需要插入图片。

  脑海中迅速闪过好几种组件,openxml组件,com组件,npoi。为了减少程序画复杂表格,我们选用了com组件+word模板的方式,程序只需要对word中的书签进行赋值即可。

  不知道这几种组件的(或者还有其他写入word的组件可以推荐)优缺点各是什么,还请各路大拿评论区指点一二。

  com组件唯一让人不爽的就是他过于依赖word,因为版本带来的不兼容问题,及各种会生成WORD半途会崩溃的问题.而且很难解决。

  不说这么悲伤的事情了,反正坑都踩了,文章后面我会附上各种深坑的解决方案,今天主要分享的是com组件写入word的各种操作。

 1.书签赋值



     /// <summary>
/// 给特定书签赋值
/// </summary>
/// <param name="bookMarkName"></param>
/// <param name="value"></param>
public void EditTable(string bookMarkName, string value, Document doc)
{
try
{
if (!doc.Bookmarks.Exists(bookMarkName))
return;
object s = bookMarkName;
Range rng = doc.Bookmarks.get_Item(ref s).Range;
rng.Text = value;
}
catch (Exception e)
{
KillwordProcess();
throw e;
}
}

2.获取指定书签的范围


     /// <summary>
/// 获取指定书签的Range
/// </summary>
/// <param name="bookMarkName">书签名称</param>
public Range GetBookMarkRange(string bookMarkName, Document doc)
{
object oBookMarkName = bookMarkName;
return doc.Bookmarks.get_Item(ref oBookMarkName).Range;
}

3.添加书签


/// <summary>
/// 添加书签
/// </summary>
/// <param name="bookMarkName">书签名</param>
/// <param name="activeRange">要添加书签的范围</param>
public void AddBookMark(string bookMarkName, Range activeRange, Document doc)
{
object oActiveRange = activeRange;
doc.Bookmarks.Add(bookMarkName, ref oActiveRange);
}

4.复制书签内容到另一个书签


      /// <summary>
/// 复制书签内容至另一书签
/// </summary>
/// <param name="sourceBookMarkName">源书签</param>
/// <param name="toBookMarkName">目标书签</param>
public void CopyRange(string sourceBookMarkName, string toBookMarkName, Document doc)
{
object oSourceBookMarkName = sourceBookMarkName;
object oToBookMarkName = toBookMarkName;
Range roRange = CopyRange(doc.Bookmarks.get_Item(ref oSourceBookMarkName).Range, doc.Bookmarks.get_Item(ref oToBookMarkName).Range);
doc.Bookmarks.get_Item(ref oToBookMarkName).Delete();
AddBookMark(toBookMarkName, roRange, doc);
}
/// <summary>
/// 复制选定范围内容至另一范围
/// </summary>
/// <param name="sourceRange">源范围</param>
/// <param name="activeRange">目标范围</param>
public Range CopyRange(Range sourceRange, Range activeRange)
{
try
{
lock (copyLock)
{
sourceRange.Copy();
activeRange.Paste();
}
return activeRange;
}
catch
{
KillwordProcess();
throw;
}
}

5.打开word文件


/// <summary>
/// 打开文件
/// </summary>
/// <param name="Path">文件路径及文件名</param>
/// <param name="IsVisible">是否可见</param>
public void OpenWord(string Path, bool IsVisible, Document doc)
{
object oMissing = System.Reflection.Missing.Value;
GUIDCaption = Guid.NewGuid().ToString();
app.Visible = IsVisible;//是否实时预览
app.Caption = GUIDCaption;
object oPath = Path;
doc = app.Documents.Open(ref oPath, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing);
}

6.表格纵向合并


/// <summary>
/// 纵向合并
/// </summary>
/// <param name="bookMarkName"></param>
/// <param name="tableIndex"></param>
/// <param name="row_a">开始行号</param>
/// <param name="col_a">开始列号</param>
/// <param name="row_b">结束行号</param>
/// <param name="col_b">结束列号</param>
public void MergeColumnCell(string bookMarkName, int tableIndex, int row_a, int col_a, int row_b, int col_b, Document doc)
{
try
{
object oBookMarkName = bookMarkName;
Range activeRange = doc.Bookmarks.get_Item(ref oBookMarkName).Range;
var newTable = activeRange.Tables[tableIndex];
newTable.Cell(row_a, col_a).Merge(newTable.Cell(row_b, col_b));
}
catch (Exception e)
{
KillwordProcess();
throw e;
}
}

7.插入图片



    /// <summary>
/// 插入图片
/// </summary>
/// <param name="bookMarkName"></param>
/// <param name="fileName">图片路径</param>
/// <param name="type">图片版式:四周型,嵌入型,环绕型</param>
public void InsertPicture(string bookMarkName, string fileName, WdWrapType type)
        {
try
{
if (!doc.Bookmarks.Exists(bookMarkName))
{
return;
}
object oBookMarkName = bookMarkName;
Microsoft.Office.Interop.Word.Range activeRange = doc.Bookmarks.get_Item(ref oBookMarkName).Range;
object linkToFile = false;
object saveWithDocument = true;
InlineShape inlineShape = doc.InlineShapes.AddPicture(fileName, ref linkToFile, ref saveWithDocument, activeRange);
inlineShape.ConvertToShape().WrapFormat.Type = type;
}
catch (Exception ex)
{
KillwordProcess();
//记录日志
AppCommon.AppLogger.WriteLog("插入图片,错误为:" + ex.ToString(), ((int)AppCommon.Unitity.AppChannelEnmu.SS).ToString()); }
}

 8.设置图片大小


/// <summary>
/// 设置图片大小
/// </summary>
/// <param name="filePath"></param>
/// <param name="width"></param>
/// <param name="height"></param>
public string SetPicture(string filePath, int width, int height)
{
Bitmap bm = new Bitmap(filePath);
Bitmap thumb = new Bitmap(width, height);
Graphics g = Graphics.FromImage(thumb);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.DrawImage(bm, new System.Drawing.Rectangle(0, 0, width, height), new System.Drawing.Rectangle(0, 0, bm.Width, bm.Height), GraphicsUnit.Pixel);
g.Dispose();
string path = ConfigurationManager.AppSettings["MapPath"].ToString() + "/Assets/internal/img/new.png";
thumb.Save(path, System.Drawing.Imaging.ImageFormat.Png);
bm.Dispose();
thumb.Dispose();
return path;
}

9.书签范围内添加一行


/// <summary>
/// 在书签范围内插入一行到指定表中
/// </summary>
/// <param name="BookMarkName">书签名称</param>
/// <param name="tableIndex">表的索引</param>
/// <param name="rowIndex">插入行的位置</param>
public void InsertRow(string bookMarkName, int tableIndex, int rowIndex)
{
try
{
if (!doc.Bookmarks.Exists(bookMarkName))
{
return;
}
object oBookMarkName = bookMarkName;
Microsoft.Office.Interop.Word.Range activeRange = doc.Bookmarks.get_Item(ref oBookMarkName).Range;
InsertRow(activeRange, tableIndex, rowIndex); }
catch (Exception e)
{
KillwordProcess();
throw e;
}
}
/// <summary>
/// 在所选范围内插入一行到指定表中
/// </summary>
/// <param name="activeRange">范围对象</param>
/// <param name="tableIndex">所选范围内表的索引</param>
/// <param name="rowIndex">插入行的位置</param>
public void InsertRow(Microsoft.Office.Interop.Word.Range activeRange, int tableIndex, int rowIndex)
{
try
{
object NumRows = 1;
activeRange.Tables[tableIndex].Rows[rowIndex].Select();
doc.ActiveWindow.Panes[1].Selection.InsertRowsBelow(ref NumRows);
}
catch (Exception e)
{
KillwordProcess();
throw e;
}
}

10.删除书签范围内容


/// <summary>
/// 删除书签范围内容
/// </summary>
/// <param name="bookMarkName">书签名</param>
public void DeleteBookMarkRange(string bookMarkName)
{
try
{
object oBookMarkName = bookMarkName;
if (doc.Bookmarks.Exists(bookMarkName))
{
DeleteRange(doc.Bookmarks.get_Item(ref oBookMarkName).Range);
} }
catch (Exception e)
{
KillwordProcess();
throw e;
}
}
/// <summary>
/// 删除所选范围内容
/// </summary>
/// <param name="activeRange">所选范围对象</param>
public void DeleteRange(Range activeRange)
{
try
{
if (null != activeRange)
{
object oMissing = Missing.Value;
activeRange.Delete(ref oMissing, ref oMissing);
foreach (Table dTable in activeRange.Tables)
{
dTable.Delete();
}
}
}
catch (Exception e)
{
KillwordProcess();
throw e;
}
}

11.删除书签范围的行


/// <summary>
/// 在书签范围内指定表删除行
/// </summary>
/// <param name="bookMarkName">书签名称</param>
/// <param name="tableIndex">表索引</param>
/// <param name="rowIndex">行号</param>
public void DeleteRow(string bookMarkName, int tableIndex, int rowIndex)
{
try
{
object oBookMarkName = bookMarkName;
if (doc.Bookmarks.Exists(bookMarkName))
{
Microsoft.Office.Interop.Word.Range activeRange = doc.Bookmarks.get_Item(ref oBookMarkName).Range;
DeleteRow(activeRange, tableIndex, rowIndex);
} }
catch (Exception e)
{
KillwordProcess();
throw e;
}
}
/// <summary>
/// 在所选范围内指定表删除行
/// </summary>
/// <param name="activeRange">所选范围对象</param>
/// <param name="tableIndex">表索引</param>
/// <param name="rowIndex">行号</param>
public void DeleteRow(Microsoft.Office.Interop.Word.Range activeRange, int tableIndex, int rowIndex)
{
try
{
if (activeRange.Tables[tableIndex].Rows.Count >= rowIndex)
{
activeRange.Tables[tableIndex].Rows[rowIndex].Delete();
}
}
catch (Exception e)
{
KillwordProcess();
throw e;
}
}

12.插入分页符


public static void InsertBreak(Document doc, string bookmark)
{
object oBookMarkName = bookmark;
if (doc.Bookmarks.Exists(bookmark))
{
object pBreak = (int)WdBreakType.wdPageBreak;
doc.Bookmarks.get_Item(ref oBookMarkName).Range.InsertBreak(ref pBreak);
} //object oBookMarkName = bookmark;
//Range perRange = doc.Bookmarks.get_Item(ref oBookMarkName).Range;
//object oEnd = perRange.End;
//object oWdBreak = (int)WdBreakType.wdPageBreak;
//doc.Range(ref oEnd, ref oEnd).InsertBreak(ref oWdBreak);
//doc.Range(ref oEnd, ref oEnd).InsertParagraph();
}

13.结束word进程

异常的时候一定要执行此方法,否则,word进程就会被遗留在内存中,后续如果继续执行,会积累更多的进程,导致进程卡死,内存飙升。

/// <summary>
/// 结束word进程
/// </summary>
private void KillwordProcess()
{
try
{
Process[] myProcesses;
//DateTime startTime;
myProcesses = Process.GetProcessesByName("WINWORD"); //通过进程窗口标题判断
foreach (Process myProcess in myProcesses)
{
if (null != GUIDCaption && GUIDCaption.Length > 0 && myProcess.MainWindowTitle.Equals(GUIDCaption))
{
myProcess.Kill();
}
}
}
catch (Exception e)
{
AppCommon.AppLogger.WriteLog("结束Word,错误为:" + e.ToString(), ((int)AppCommon.Unitity.AppChannelEnmu.SS).ToString());
throw e;
}
}

 14.删除空行

这段代码厉害了,是用来精确控制的,你可以用它获取到word的每一寸区域,这样你想在哪里写值就在哪里写。下面这段代码是用来删除书签范围上方的空行的,代码中会判断上方是空行的区域,

如果上方区域有文字,则

iEnd为文字所在行-1,
range.Start表示该书签区域开头所在的行
titleRange就是要删除的空行区域
/// <summary>
/// 删除Range 上面的空行
/// </summary>
/// <param name="range"></param>
/// <param name="holdblank">下面保留几个空行,默认保留一个</param>
public void DeleteRangeUpBlankRow(Document doc, Range range, int holdblank = 1)
{
try
{
int iStart = range.Start - holdblank;
int iEnd = range.Start - holdblank;
object oStart = range.Start - holdblank;
object oEnd = range.Start - holdblank;
Range titleRange = doc.Range(ref oStart, ref oEnd);
//获取标题范围 从表格范围起始位置开始 每次循环范围向前增加1
while (true)
{
iStart = iStart - 1;
titleRange.SetRange(iStart, iEnd);
if (iStart < 0 || IsTitleRangeOver(titleRange))
{
titleRange.SetRange(iStart + 1, iEnd);
break;
}
}
//删除空行
object oMissing = Missing.Value;
titleRange.Delete(ref oMissing, ref oMissing);
}
catch (Exception e)
{
KillwordProcess();
throw e;
}
}
/// <summary>
/// 判断要删除表的标题范围是否超出
/// </summary>
/// <param name="titleRange"></param>
/// <returns></returns>
public bool IsTitleRangeOver(Range titleRange)
{
  if (titleRange.Tables.Count > 0)//范围内包含表格
  {
    return true;
  }
  if (titleRange.Paragraphs[1].PageBreakBefore == 1)//有分页符
  {
    return true;
  }   if (!string.IsNullOrEmpty(titleRange.Text.Trim()))//内容不为空了
  {
    return true;
  }
  return false;
}

常见错误及处理方案:

1.报告生成期间异常:System.Runtime.InteropServices.COMException (0x8001010A): 消息筛选器显示应用程序正在使用中。 (异常来自 HRESULT:0x8001010A (RPC_E_SERVERCALL_RETRYLATER))

  解决方案:出现这种错误有限检查COM组件权限是否正常,检查步骤:

(1)打开组件服务



 (2)打开DCOM配置


 (3)找到word97-2003


(4)按照如图依次配置


(5)一般这里我们选择用户名密码 的方式,这个密码必须在web.Config中进行配置

<identity impersonate="true" userName="服务器登录名" password="服务器登陆密码" />


 2.System.Runtime.InteropServices.COMException (0x800A1735): 集合所要求的成员不存在。

  解决方案:这个是程序错误,只要调试代码即可发现错误。

3.System.Runtime.InteropServices.COMException (0x800A1710): 无法编辑 Range。

  解决方案:这个说明编辑某个区域的时候,超出了范围,或者找不到这个区域范围,这个也是代码的问题,需要仔细查找,不是很好定位。


-------------------------------------------(正文完)-----------------------------------------------------


还有一些其他的问题没有收集,大家遇到了可以在评论区发出来,我能帮大家解答的一定尽我所能!


好啦,今天的分享就到这里……


向着高级,进发!

利用COM组件实现对WORD书签各种操作大全,看这一篇就够了的更多相关文章

  1. 利用COM组件实现对WORD书签处写入值

    using System; using System.Collections.Generic; using System.Text; using Microsoft.Office.Interop.Wo ...

  2. python 使用win32com实现对word文档批量替换页眉页脚

    最近由于工作需要,需要将70个word文件的页眉页脚全部进行修改,在想到这个无聊/重复/没有任何技术含量的工作时,我的内心是相当奔溃的.就在我接近奔溃的时候我突然想到完全可以用python脚本来实现这 ...

  3. jeecms系统使用介绍——通过二次开发实现对word、pdf、txt等上传附件的全文检索

    转载请注明出处:http://blog.csdn.net/dongdong9223/article/details/76912307 本文出自[我是干勾鱼的博客] 之前在文章<基于Java的门户 ...

  4. 通过vb.net 和NPOI实现对excel的读操作

    通过vb.net 和NPOI实现对excel的读操作,很久很久前用过vb,这次朋友的代码是vb.net写的需要一个excel的操作, 就顾着着实现功能了,大家凑合着看吧 Option Explicit ...

  5. Python中实现对list做减法操作介绍

    Python中实现对list做减法操作介绍 这篇文章主要介绍了Python中实现对list做减法操作介绍,需要的朋友可以参考下 问题描述:假设我有这样两个list, 一个是list1,list1 = ...

  6. 使用代理实现对C# list distinct操作

    范型在c#编程中经常使用,而经常用list 去存放实体集,因此会设计到对list的各种操作,比较常见的有对list进行排序,查找,比较,去重复.而一般的如果要对list去重复如果使用linq dist ...

  7. 【POI word】使用POI实现对Word的读取以及生成

    项目结构如下: 那第一部分:先是读取Word文档 package com.it.WordTest; import java.io.FileInputStream; import java.io.Fil ...

  8. 利用C#实现对excel的写操作

    一.COM interop 首先我们要了解下何为COM Interop,它是一种服务,可以使.NET Framework对象能够与COM对象通信.Visual Studio .NET 通过引入面向公共 ...

  9. C#实现对Word文件读写[转]

    手头上的一个项目报表相对比较简单,所以报表打印采用VBA引擎,通过定制Word模版,然后根据模版需要填充数据,然后OK,打印即可. 实现方法:首先需要引用VBA组建,我用的是Office2003 Pr ...

随机推荐

  1. 2020.7.19 区间dp阶段测试

    打崩了-- 事先说明,今天没有很在状态,所以题解就直接写在代码注释里的,非常抱歉 T1 颜色联通块 此题有争议,建议跳过 题目描述 N 个方块排成一排,第 i 个颜色为 Ci .定义一个颜色联通块 [ ...

  2. java base64加解密

    接上篇java Base64算法. 根据之前过程使用base64加解密,所以写成了工具类. 代码示例; public class Base64Util { private static Logger ...

  3. 数论之prufer序列

    定义 \(Prufer\) 数列是无根树的一种数列. 在组合数学中,\(Prufer\) 数列由有一个对于顶点标过号的树转化来的数列,点数为 \(n\) 的树转化来的 \(Prufer\) 数列长度为 ...

  4. Maven 依赖树的解析规则

    对于 Java 开发工程师来说,Maven 是依赖管理和代码构建的标准.遵循「约定大于配置」理念.Maven 是 Java 开发工程师日常使用的工具,本篇文章简要介绍一下 Maven 的依赖树解析. ...

  5. 记一次MySQL出现Waiting for table metadata lock的原因、排查过程与解决方法

    任务背景:将sql文件通过shell直接导入到mysql中执行(还原) bug表现:导入后java项目卡死 过程: 1.网上乱搜一通,无意间看到一篇文章,这篇文章说明了如何开启mysql的genera ...

  6. 基础篇:JAVA.Stream函数,优雅的数据流操作

    前言 平时操作集合数据,我们一般都是for或者iterator去遍历,不是很好看.java提供了Stream的概念,它可以让我们把集合数据当做一个个元素在处理,并且提供多线程模式 流的创建 流的各种数 ...

  7. PyQt(Python+Qt)学习随笔:QScrollArea为什么不起作用未出现滚动条?

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 老猿在进行Scroll Area部件测试时,在下面的窗体中放置了一个Scroll Area部件,在部 ...

  8. 第15.6节 PyQt5安装与配置

    一. 引言 关于PyQt5的安装网上有很多的文章,老猿也是学习了好多,最后结合其他模块安装的知识发现其实安装很简单,就是直接使用pip或pip3安装就可以了,这样既无需预先下载好软件,也无需担心版本的 ...

  9. PHP代码审计分段讲解(11)

    后面的题目相对于之前的题目难度稍微提升了一些,所以对每道题进行单独的分析 27题 <?php if(!$_GET['id']) { header('Location: index.php?id= ...

  10. [BJDCTF2020]Cookie is so stable && [GWCTF 2019]枯燥的抽奖

    [BJDCTF2020]Cookie is so stable 进入环境后看到有hint,点击之后查看源代码 提示我们cookie有线索 flag页面是: 需要输入一个username,或许这道题目是 ...