有个需求是,程序导出一份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. 【模板】【P3402】可持久化并查集

    (题面来自洛谷) 题目描述 n个集合 m个操作 操作: 1 a b 合并a,b所在集合 2 k 回到第k次操作之后的状态(查询算作操作) 3 a b 询问a,b是否属于同一集合,是则输出1否则输出0 ...

  2. 做IT需要掌握的电力基础知识

    电流 损耗 直流电的传输损耗大,所以不适合长距离传输, 交流电的传输损耗小,所以适合长距离传输, 使用 直流电电压稳定,无白躁声,故适於电子产品使用(例如电视机,收音机电脑等), 交流电要经过整流/开 ...

  3. How tomcat works(深入剖析tomcat)生命周期Lifecycle

    How Tomcat Works (6)生命周期Lifecycle 总体概述 这一章讲的是tomcat的组件之一,LifeCycle组件,通过这个组件可以统一管理其他组件,可以达到统一启动/关闭组件的 ...

  4. SAD DNS--新型DNS缓存中毒攻击

    一.DNS基础知识: 1.DNS简介: DNS 域名服务,用于建立 域名与 ip地址的 一对一 映射.DNS 将域名转换为 IP地址,以便浏览器能够加载 Internet 资源. 类似于一个翻译系统, ...

  5. unittest框架中读取有特殊符号的配置文件内容的方法-configparser的RawConfigParser类应用

    在搭建Unittest框架中,出现了一个问题,配置文件.ini中,出现了特殊字符如何处理? 通过 1.configparser的第三方库对应的ConfigParser类,无法完成对特殊字符的读取: # ...

  6. 20190713_发布网站的时候报错:无法完成向远程代理 URL 发送请求 基础连接已经关闭 发送时发生错误

    环境介绍: Vs2017 IIS 7.5 服务器: windows 2008 R2 overflow上有一篇帖子讲了关于问题的解决办法: 链接: https://stackoverflow.com/q ...

  7. 对称加密之---AES加密

    工作中常会需要让数据传输前进行加密处理.这次用到的是AES加密.AES加密中,需要注意到坑还是挺多的.对AES也进行了一番了解,发现里面的东西真的是注意的太多了.今天只是整理了一种简单的加密格式,工作 ...

  8. Python爬虫实战案例:取喜马拉雅音频数据详解

    前言 喜马拉雅是专业的音频分享平台,汇集了有声小说,有声读物,有声书,FM电台,儿童睡前故事,相声小品,鬼故事等数亿条音频,我最喜欢听民间故事和德云社相声集,你呢? 今天带大家爬取喜马拉雅音频数据,一 ...

  9. moviepy音视频剪辑:使用fl_time进行时间特效处理报错ValueError: Attribute duration not set

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt+moviepy音视频剪辑实战 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 在使 ...

  10. CSDN-markdown编辑器使用方法

    这里写自定义目录标题 欢迎使用Markdown编辑器 新的改变 功能快捷键 合理的创建标题,有助于目录的生成 如何改变文本的样式 插入链接与图片 如何插入一段漂亮的代码片 生成一个适合你的列表 创建一 ...