开源代码系列之Office文档读写(Excel/Word) | |
Office系列文档使用的是所谓的Compound File,也就是Structured Storage,简单地说,也就是在一个文件中有一个文件系统,结构化保存复合内容(流)。微软为了垄断,这些文件结构是不公开的,但有牛人研究出来,一个很不错的例子就是Open Office代码,里面就有大部分Office系列文档格式的java代码,包括Word、Excel、PowerPoint、Outlook等,现在市面上的商业化软件很多都在使用别人的劳动成功,明的暗的。 今天介绍的是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 Classes,VB大牛Edanmo用VB.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控件来完成我们的Excel等ActiveDocument的嵌入和操作。在此之前,你需要将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.增加WebBrowser的DocumentComplete事件处理程序 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系统问题有几个,1是WebBrowser控件是一个比较重的控件,2是通过WebBrowser去控制Office如果出现问题没有办法进行调试与判断,也无法修改,3是Office对应的菜单没有办法控制。为此我们决定需求新的解决方案,使用微软提供的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++后,不认识。费了好大的劲,将Form的MainMenu给传了进去,呵呵,这下行了,看看运行的效果: 怎么样,感觉还不错吧,现在这个Excel就可以好好的控制了,最主要的是, 如果你想增加什么新的东西,出现什么问题就可以自己调试解决了。 如果你要在OCX中增加接口,你必须同时修改三个地方,一个是ODL,OCX的接口定义文件,并且按照规范,一个是你的控件实现的对应的.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控件中docObject的ClearMergeMenu,让它清除掉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小时内删除。
发表评论