开源代码系列之Office文档读写(Excel/Word)
Office系列文档使用的是所谓的Compound File,也就是Structured Storage,简单地说,也就是在一个文件中有一个文件系统,结构化保存复合内容()。微软为了垄断,这些文件结构是不公开的,但有牛人研究出来,一个很不错的例子就是Open Office代码,里面就有大部分Office系列文档格式的java代码,包括WordExcelPowerPointOutlook等,现在市面上的商业化软件很多都在使用别人的劳动成功,明的暗的。

今天介绍的是3个开源代码,都是关于Excel的,应用.NET编写,2个是读,一个是写。

koogra:超一流的Excel文件解析类库,支持最新版本的Excel文件,包括2003版本的,在我向作者指出的错误之后,作者特别添加了对中文的支持,C#编写,(这是我介绍的所有的开源代码中第二个获得我给予的6星评价的,大家可以体验一下),用了这个类库,大家就不需要打开Excel实例来解析Excel文件了!

Smart Excel Library:这个东西是我写的,用以写BIFF2.1格式的文件,VB.NET,不支持最新版本的Excel格式,但任何版本的Excel都可以直接打开,我记得有个博客园的朋友移植到C#,大家可以搜索一下。用了这个类库,大家就可以不用Excel而直接生成标准的Excel文件,而不是那种CSV,又或者通过HTTP Header来让Excel认为是Excel的做法,因为它不支持格式。

OLE Structured Storage ClassesVB大牛EdanmoVB.NET写的结构化文档格式读取类库,不喜欢VB.NET而喜欢C#的朋友将就一下,反正是类库,编译一下便可通用:)

事实上肯定不只这些,如果大家知道,可以和别人分享一下:)
posted on 2005-02-28 00:03 奋斗中的灵感之源 阅读(4141) 评论(14)  编辑 收藏 收藏至365Key 所属分类: 3.开源项目

FeedBack:
# re: 开源代码系列之Office文档读写(Excel/Word)
2005-02-28 09:18 | 强把忧郁再掩盖
我用不开源得Aspose  回复
 
# re: 开源代码系列之Office文档读写(Excel/Word)
2005-02-28 09:22 | 灵感之源:PSP团队Guru
Aspose 不是开源的啊!  回复
 
# re: 开源代码系列之Office文档读写(Excel/Word)
2005-02-28 09:45 | HelloSnoopy开发田地
有没有word读写的??  回复
 
# re: 开源代码系列之Office文档读写(Excel/Word)
2005-02-28 10:08 | daniel
非常usefull. thanks a lot.
真的希望您能够再写一篇关于Word控件的文章。  回复
 
# re: 开源代码系列之Office文档读写(Excel/Word)
2005-03-01 14:54 | cqhydz
我想问一下,有关OpenPOP.NET除了以下地址中提到的好久没更新了wwwblogs/unruledboy/archive/2004/06/16/16143.html
我希望老大能对OpenPOP.NET开几篇幅专门讲一下。  回复
 
# re: 开源代码系列之Office文档读写(Excel/Word)
2005-03-01 18:11 | myrat
也想要word读写的:)  回复
 
# re: 开源代码系列之Office文档读写(Excel/Word)
2005-03-02 16:48 | 灵感之源:奋斗中
to:cqhydz
没有精力写。  回复
 
# re: 开源代码系列之Office文档读写(Excel/Word)
2005-04-02 16:27 | 雨田
smart Excel 怎么增加一张表页  回复
 
# re: 开源代码系列之Office文档读写(Excel/Word)
2005-05-12 09:51 | 登峰
在线编辑word文档目前有更好的解决办法吗  回复
 
# re: 开源代码系列之Office文档读写(Excel/Word)
2005-05-24 22:57 | 蓝风
红月亮工作室的 Word Excel 在线编辑控件,对个人用户免费,使用简单,功能强大,支持痕迹保留,轻松连同表单数据一起发送至服务器。
dmoon  回复
 
# re: 开源代码系列之Office文档读写(Excel/Word)
2005-05-28 09:40 | 无我境地
中文写代码软件
请问哪有,PowerPoint 的读写的代码,相关的资料也行。

  回复
 
# re: 开源代码系列之Office文档读写(Excel/Word)
2005-09-09 12:24 | zzb111222
6.0版本开始进行word操作就不是很难阿,我还是觉得不用类写出来的更灵活回复
 
# re: 开源代码系列之Office文档读写(Excel/Word)
2005-11-06 23:07 | zhebei
Smart Excel Librar做的文件koogra等其他非excel程式开不了  有没有办法  回复
 
# re: 开源代码系列之Office文档读写(Excel/Word)
2005-12-01 10:32 | huangzp
之能写入文本模式的数据.:(
有没有什么好的framework 支持向excel 中写入jpeg格式的图像回复
将Exce嵌入你的.Net程序
 这是一篇技术总结,主要说明在.Net情况下,将Excel嵌入我们自己的程序,并进行控制。
        由于微软再一次推迟了.Net版的OLE Container Control的发布(2005版本叫ActiveDocumentControl),使我们几乎没有太多的办法来完成这个任务(你可以利用Visual C++来完成你的工作,不过该实现对技术的要求太高,大部分人不具备这个能力,因此只好作罢,最好在Visual C++基础上开发一个控件,在别的环境下调用,如果你能做这个工作,请帮帮我),只能够使用重量级的解决方案,就是利用WebBrowser控件和dsoframer.OCX来完成这个工作。
    在最新的Visual Studio 2005中,为我们提供了一个WebBrowser的封装控件,这个控件隐藏了底层的axWebBrowser控件,我们就利用WebBrowser控件来完成我们的ExcelActiveDocument的嵌入和操作。在此之前,你需要将Office.Net库安装到你的机器上。
1.在你的工具栏上选择WebBrowser控件,将其放到你的Form
2.在Form上增加一个控件来使其加载一个Excel文件,如下:
Button的事件处理程序:
  private void button1_Click_1(object sender, EventArgs e)
  {
            string strFileName = @"d:\a.xls";
            Object refmissing = System.Reflection.Missing.Value;
            this.webBrowser1 .Navigate(strFileName);
            object axWebBrowser = this.webBrowser1.ActiveXInstance;
           
  }
3.增加WebBrowserDocumentComplete事件处理程序
private void webBrowser1_DocumentCompleted_1(object sender, WebBrowserDocumentCompletedEventArgs e)
{
            Object refmissing = System.Reflection.Missing.Value;
            object[] args = new object[4];
            args[0] = SHDocVw.OLECMDID.OLECMDID_HIDETOOLBARS;
            args[1] = SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER;
            args[2] = refmissing;
            args[3] = refmissing;
 
            object axWebBrowser = this.webBrowser1.ActiveXInstance;
 
 
 
            axWebBrowser.GetType().InvokeMember("ExecWB", BindingFlags.InvokeMethod, null, axWebBrowser, args);
 
 
            object oApplication = axWebBrowser.GetType().InvokeMember("Document", BindingFlags.GetProperty, null, axWebBrowser, null);
   
            Excel.Workbook wbb = (Excel.Workbook)oApplication;
            Excel.ApplicationClass excel = wbb.Application as Excel.ApplicationClass ;
            Excel.Workbook wb = excel.Workbooks[1] ;
            Excel.Worksheet ws = wb.Worksheets[1] as Excel.Worksheet;
            ws.Cells.Font.Name = "Verdana";
            ws.Cells.Font.Size = 14;
            ws.Cells.Font.Bold = true;
            Excel.Range range = ws.Cells;
 
            Excel.Range oCell = range[10, 10] as Excel.Range ;
            oCell.Value2 = "你好";
           
  }
    这里要注意,WebBrowser加载Excel文档后,并不显示工具栏,为此我们需要使用高级一点的技术,通过反射调用底层的方法来实现,首先我们需要到WebBrowser封装的AxWebBrowser控件。
                object axWebBrowser = this.webBrowser1.ActiveXInstance;
然后,我们调用反射来显示工具栏
          Object refmissing = System.Reflection.Missing.Value;
            object[] args = new object[4];
            args[0] = SHDocVw.OLECMDID.OLECMDID_HIDETOOLBARS;
            args[1] = SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER;
            args[2] = refmissing;
            args[3] = refmissing;
 
            axWebBrowser.GetType().InvokeMember("ExecWB", BindingFlags.InvokeMethod, null, axWebBrowser, args);
            注:如果你直接使用AxWebBrowser控件,那你就可以直接调用ExecWB方法来显示工具栏。如下:
    axWebBrowser1.ExecWB(SHDocVw.OLECMDID.OLECMDID_HIDETOOLBARS, SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER, refmissing, refmissing)
    好了,现在你需要获得你的Excel对象来操作Excel,此时需要使用Office.Net库来完成。代码如下:
             
object oApplication = axWebBrowser.GetType().InvokeMember("Document", BindingFlags.GetProperty, null, axWebBrowser, null);
   
          Excel.ApplicationClass excel = wbb.Application as Excel.ApplicationClass ;
            Excel.Workbook wb = excel.Workbooks[1] ;
            Excel.Worksheet ws = wb.Worksheets[1] as Excel.Worksheet;
            ws.Cells.Font.Name = "Verdana";
            ws.Cells.Font.Size = 14;
            ws.Cells.Font.Bold = true;
            Excel.Range range = ws.Cells;
 
            Excel.Range oCell = range[10, 10] as Excel.Range ;
            oCell.Value2 = "你好";
现在你可以随心所欲来控制Excel了,效果如下
   
   
 
说明:通过使用WebBrowser控件,是不能显示菜单栏的。
 
3.如果使用AxWebBrowser控件,那就需要处理NavigateComplete2事件,事件处理程序如下
    private void axWebBrowser_NavigateComplete2(object sender, AxSHDocVw.DWebBrowserEvents2_NavigateComplete2Event e)
        {
            Object refmissing = System.Reflection.Missing.Value;
            this.webBrowser1.ExecWB(SHDocVw.OLECMDID.OLECMDID_HIDETOOLBARS, SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER, ref refmissing, ref refmissing);
            Object o = e.pDisp;
                  Object oDocument = o.GetType().InvokeMember("Document",BindingFlags.GetProperty, null, o, null);
            Object oApplication = o.GetType().InvokeMember("Application", BindingFlags.GetProperty, null, oDocument, null);
                ////由于打开的是excel文件,所以这里的oApplication 其实就是Excel.Application
            Excel.Application excel = (Excel.Application)oApplication;//这样就可以象上文中所述来操作Excel了。
 
            Excel.Workbook wb = excel.Workbooks[1];
            Excel.Worksheet ws = wb.Worksheets[1] as Excel.Worksheet;
            ws.Cells.Font.Name = "Verdana";
            ws.Cells.Font.Size = 14;
            ws.Cells.Font.Bold = true;
            Excel.Range range = ws.Cells;
 
            Excel.Range oCell = range[10, 10] as Excel.Range;
            oCell.Value2 = "你好";
        }
这里操作更简单一点,缺点就是你必须直接使用AxWebBrowser控件,该控件比起已经封装好的WebBrowser可能有些我们所不知道的问题。微软推荐大家通过WebBrowser来访问。使用AxWebBrowser,有一个意外的收获,如下:
      Object o = e.pDisp;
      IOleCommandTarget cmdTarget = o as IOleCommandTarget;
      Guid guidCMDGroup = new Guid("00020820-0000-0000-C000-000000000046");
    cmdTarget.Exec(ref guidCMDGroup , 1, 0, null, null);
 
通过IoleCommandTarget可以模拟如菜单命令,这样你可以自己定制菜单,然后将菜单命令发送到Excel来处理。呵呵,模拟菜单。
  public interface IOleCommandTarget
    {
        [PreserveSig()]
        int QueryStatus([In, MarshalAs(UnmanagedType.Struct)] ref Guid
          pguidCmdGroup, [MarshalAs(UnmanagedType.U4)] int cCmds,
          [In, Out] IntPtr prgCmds, [In, Out] IntPtr pCmdText);
 
        [PreserveSig()]
        int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdExecOpt,
            object[] pvaIn, [In, Out, MarshalAs(UnmanagedType.LPArray)]
            object[] pvaOut);
    }
 
4.如果我们使用Visual C++我们可以获得一个完全的,嵌入式Excel,包括菜单,看起来就像我们自己的一样:
 
可惜我们对Visual C++都不太知道如何控制。
  使用AxWebBrowser或者WebBrowser的方法将Office嵌入我们的.Net系统问题有几个,1WebBrowser控件是一个比较重的控件,2是通过WebBrowser去控制Office如果出现问题没有办法进行调试与判断,也无法修改,3Office对应的菜单没有办法控制。为此我们决定需求新的解决方案,使用微软提供的dsoFramer控件例子,这个例子使用VC++编写,本身是可以使用的,可以参见:
   support.microsoft/default.aspx?scid=kb;en-us;311765
在这里下载dsoFramer的源码
www.microsoft/downloads/details.aspx?familyid=CE2CA4FD-2169-4FAC-82AF-770AA9B60D77&displaylang=en
下面进行我们的改造过程
1.使用VisualStudio2005打开该VC工程,按系统提示进行转换
2.转换完成后,发现该控件缺少OLB文件,这个文件是控件的对象库,在引用该控件的时候要用到,编译该对象库需要但该工具在VS2005中已经不再提供,使用MIDL代替,但MIDL又不支持这种转换过来的工程,没想到第一步就遇到了挫折,为此想了很多办法,后来从VS2003中到旧的,拷入VS2005\common\tools\bin下面,呵呵可以了,(这个应该算VS2005的一个Bug吧)
3.编译好ocx,通过Regsvr32 注册到系统中
4.创建一个C#工程,引入刚才的ocx
5.发现该控件,自带了菜单,工具栏,并且我们基本不能控制该控件,晕倒
6.决定自己将它的菜单栏放到我们的.Net  Form上,发现不能将 C++的菜单直接放过来,C++的有太多的控制了,如果要重写这些控制,就要吐血了,并且发现新的MenuStrip控件根本就不是一个菜单,传入C++后,不认识。费了好大的劲,将FormMainMenu给传了进去,呵呵,这下行了,看看运行的效果:
怎么样,感觉还不错吧,现在这个Excel就可以好好的控制了,最主要的是, 如果你想增加什么新的东西,出现什么问题就可以自己调试解决了。
如果你要在OCX中增加接口,你必须同时修改三个地方,一个是ODLOCX的接口定义文件,并且按照规范,一个是你的控件实现的对应的.H文件,一个实现的.CPP文件,这些都简单了。
6.菜单出来了,发现不能控制,又再次晕倒,不知所措了
解决办法,是通过OVerride窗体的WndProc方法,将菜单的事件,除了你自己的菜单以外的,都给传到OCX中。
OCX中增加一个接口方法:ExecuteMenuMessage,它直接调用OnMenuMessage,呵呵,这样就行了,代码如下:
        protected override void WndProc(ref Message m)
        {
            IntPtr  lWParam=IntPtr.Zero  ;
            switch (m.Msg)
            {
                case WindowsMessages.WM_INITMENU :
                case WindowsMessages.WM_ENTERIDLE:
                case WindowsMessages.WM_MENUSELECT:
                case WindowsMessages.WM_INITMENUPOPUP:
                case WindowsMessages.WM_COMMAND :                 
                    ExecuteMenuMessage(m.Msg, m.WParam, m.LParam);
                    //Console.Write(m.Msg.ToString("X"));
                    //Console.Write("    ");
                    //Console.Write(lWParam.ToString("X"));
                    //Console.Write("    ");
                    //Console.WriteLine(m.LParam.ToString("X"));
                    break;               
            }
       
            base.WndProc(ref m);     
        }
   现在菜单信息也传入了,哦,创建菜单的程序如下,酷吧:
   private void Form1_Load(object sender, EventArgs e)
        {
            //if (this.Menu == null)
            {
                this.Menu = new MainMenu();
                CreateFileMenu();
                IntPtr hMainMenu = this.Menu.Handle;
                this.axFramerControl1.MainFormMenu = hMainMenu;
                this.axFramerControl1.MainForm = this.Handle;
            }
        } 
       
     
        private void CreateFileMenu()
        {
            IntPtr pFileMenu = MenuHelper.CreatePopupMenu();
            MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_STRING, FrameMenuMessage.MNU_NEW, "新建(&N)...\tCtrl+N");
            MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_STRING, FrameMenuMessage.MNU_OPEN, "打开(&O)...\tCtrl+O");
            MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_STRING, FrameMenuMessage.MNU_CLOSE, "关闭(&C)");
            MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_SEPARATOR, 0, string.Empty);
            MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_STRING, FrameMenuMessage.MNU_SAVE, "保存(&S)\tCtrl+S");
            MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_STRING, FrameMenuMessage.MNU_SAVEAS, "另存为(&A)...");
            MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_SEPARATOR, 0, string.Empty);
            MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_STRING, FrameMenuMessage.MNU_PGSETUP, "页面设置(&U)...");
            MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_STRING, FrameMenuMessage.MNU_PRINTPV, "打印预览(&V)");
            MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_STRING, FrameMenuMessage.MNU_PRINT, "打印(&P)...");
            MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_SEPARATOR, 0, string.Empty);
            MenuHelper.AppendMenu(pFileMenu, MenuFlags.MF_STRING, FrameMenuMessage.MNU_PROPS, "属性(&I)");
            MenuHelper.AppendMenu(this.Menu.Handle, MenuFlags.MF_BYPOSITION | MenuFlags.MF_POPUP, (uint)pFileMenu.ToInt32() , "文件(&F)");
            MenuHelper.DrawMenuBar(this.Handle);
        }
7.当关闭Excel后会怎么样,靠,菜单还在,并且每个都报错,肯定报错,OLE已经卸载了,只好跑去修改OCX控件中docObjectClearMergeMenu,让它清除掉Excel的菜单,明白原理就简单了。
////////////////////////////////////////////////////////////////////////
// CDsoDocObject::ClearMergedMenu (protected)
//
//  Frees the merged menu set by host.
//
STDMETHODIMP_(void) CDsoDocObject::ClearMergedMenu()

 if (m_hMenuMerged)
 {
        int cbMenuCnt = GetMenuItemCount(m_hMenuMerged);
  int iMainMenuCnt=0;
  if(this->m_hMenuMainForm)
   iMainMenuCnt=GetMenuItemCount(m_hMenuMainForm);
  for (int i = cbMenuCnt-1; i >0; --i)
  {
   if(this->m_hMenuMainForm)
   {
    RemoveMenu(m_hMenuMainForm,--iMainMenuCnt,MF_BYPOSITION);
   }
   RemoveMenu(m_hMenuMerged, i, MF_BYPOSITION);
  }
  if(m_hwndMainForm)
   DrawMenuBar(m_hwndMainForm);
  DestroyMenu(m_hMenuMerged);
  m_hMenuMerged = NULL;
 }
}
 
还有一些遗留问题:
   1.菜单合并时候,可以指定位置,这样有利于我们控制,这个暂时没做。
   2.文件菜单中,当Excel卸载后,文件菜单的部分不能自动置为灰的,这里还没搞清楚是什么原因
 
好了如果你有兴趣,而且要使用类似的功能,可以我,我可以提供源码,不过如果你改的更好了,别忘记给我一份,呵呵。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。