Yee的博客 http://www.smt18.com/blog/yee zh-hans Excel 插件開發(fā)經(jīng)驗分享 http://www.smt18.com/blog/excel-%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB <div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even" property="content:encoded"><h1>&nbsp;</h1> <h1><strong>1. </strong><strong>這個文檔寫的是些什么?</strong></h1> <p>&nbsp;</p> <p><em>這個文檔分享了我在Excel插件開發(fā)中所遇到的問題和對應(yīng)的解決方案。希望對要進行Excel插件開發(fā)或者想自己寫插件增加效率的朋友有所幫助。</em></p> <p>&nbsp;</p> <p>首先描述一下我的Excel開發(fā)遇到的場景:</p> <p>我需要開發(fā)一個Excel的插件可以從后臺Web Service獲取數(shù)據(jù)然后顯示到Excel中,那么就存在一個問題:Web Service的數(shù)據(jù)是通過什么方式寫到Excel中呢?答案是Function!通過函數(shù)的方式寫入Excel中。請看下面這幅圖:</p> <p><img src="/sites/default/files/clip_image001.png" /></p> <p>首先我會在C3 – C8 輸入年份 2005 - 2010,這時候我需要在D3 - D8 使用函數(shù) <strong>=GetPriceByYear()</strong>,參數(shù)就是C3 – C8的年份,然后從Web Service獲取相應(yīng)的數(shù)據(jù)寫入其中??吹竭@里你可能會說用VBA就好了啊?VBA也可以調(diào)用Web Service啊,問什么這么麻煩用插件?</p> <p>&nbsp;</p> <h1><strong>2. </strong><strong>為什么要使用插件這個東西?</strong></h1> <p>&nbsp;</p> <p>下面我來說一下為什么要用插件。</p> <p>之前這個功能的確是用VBA實現(xiàn)了的,但是VBA是和Excel綁定著的,換句話說你用VBA寫了這個功能保存到Excel中,只有保存的文檔能用這個函數(shù)功能,如果你用Excel新開一個文檔,那你之前寫的VBA用不到新的空白文檔里面去。這就造成了麻煩。</p> <p>那如果用插件呢?任何一個用文檔不管是Excel新建的,還是從別的地方拷貝過來的文檔,只要你的Excel安裝了插件,并且插件啟用了,那么就可以在任何一個文檔使用這個功能。是不是比之前用VBA的方式更通用更方便呢?有.NET強大的類庫支持,插件的功能可以寫得更強大更實用。</p> <p>那么就開始動手開發(fā)插件吧!等等,好像需要先調(diào)查一下網(wǎng)上有沒有什么好的方案,存不存在什么問題。</p> <p>&nbsp;</p> <h1><strong>3. </strong><strong>用什么開發(fā)插件?</strong></h1> <p>&nbsp;</p> <p>.NET程序員傷不起,開發(fā)環(huán)境當然首選Visual Studio了,接下來就有兩種選擇:</p> <p>一種是<strong>VSTO</strong><strong>(Visual Studio Tools for Office</strong><strong>)</strong></p> <p><img src="/sites/default/files/clip_image003.jpg" /></p> <p>還有一種就是:<strong>Shared Add-in</strong></p> <p><img src="/sites/default/files/clip_image005.jpg" /></p> <p>這兩種有什么異同呢?相同的點不用說了,都可以開發(fā)Excel Add-in,不同的就體現(xiàn)在Shared這個詞上,VSTO開發(fā)的插件是針對某一個Office產(chǎn)品的,也就是Excel,Word,Outlook需要用VSTO開發(fā)三個插件才能在這三個產(chǎn)品中使用,而Shard Add-in只需要開發(fā)一個,就可以在這三個產(chǎn)品上使用,很強大是吧?所以根據(jù)你的需求選擇最適合你的一種方式開發(fā)就好了。</p> <p>如果你想深入了解他們之間的區(qū)別可以去這里看下:</p> <p><a href="http://social.msdn.microsoft.com/Forums/vstudio/en-US/3f97705a-6052-4296-a10a-bfa3a39ab4e7/shared-addin-vs-vsto-addin-whats-the-difference-betweenhow-can-i-tell-if-im-developing?forum=vsto">Shared Add-in vs. VSTO Add-in: What's the difference between/how can I tell if I'm developing?</a></p> <p>這時你可能興奮得馬上開始打開VS創(chuàng)建一個項目試試,試試歸試試,你回頭看到周圍電腦大家用的操作系統(tǒng)和Office都不一樣的時候,你突然開始思考了。。。</p> <p>&nbsp;</p> <h1><strong>4.</strong><strong>插件在Office 2000 – 2013</strong><strong>上都能用嗎?</strong></h1> <p>&nbsp;</p> <p>答案是否定的,首先看Wiki 上的VSTO怎么說:</p> <p>The developer editions of Office have been discontinued after Office XP and VSTO is available for Office 2003 and later versions only.</p> <p>很遺憾Office 2000 還有 Office XP不被支持了,其實現(xiàn)在沒看到用這兩個版本的了,最低版本也就是 2003了,但是看到這句話我心有余悸,微軟的這種拋棄式做法,那估計想同時兼容2003-2013也懸了,因為你看下Office2003和Office2007的界面差距有多大你就清楚了。</p> <p>結(jié)果正如我所料,請看下圖:</p> <p><img src="/sites/default/files/clip_image007.jpg" /></p> <p>當我看到這個圖我凌亂了~哦難道要一個Office版本開發(fā)一個?</p> <p>經(jīng)過我的不懈Google。。。和虛擬機中的不懈測試。。。</p> <p>終于得出了結(jié)論:</p> <ul> <li><strong>如果開發(fā)Office 2003</strong><strong>的Add-in </strong><strong>使用 VS2008 + .NET 3.5 + VSTO 3.0</strong></li> <li><strong>如果開發(fā)Office 2007</strong><strong>以上的Add-in</strong><strong>使用(VS2010</strong><strong>或者VS2012 </strong><strong>)+ .NET 4.0 + VSTO 4.0</strong></li> </ul> <h1>&nbsp;</h1> <h1><strong>5. </strong><strong>那我寫個小Demo</strong><strong>發(fā)布一下試試?</strong></h1> <p>&nbsp;</p> <p>“開發(fā)機上運行完美,客戶機上運行不起來!”</p> <p>&nbsp;</p> <p>這個是很多人發(fā)布插件到客戶機安裝后得出的結(jié)論!是這么多人都SB嗎?腫么可能?是因為發(fā)布程序的陷阱太多了,換句話說,官方給了你發(fā)布程序的20個步驟,你看了之后說:你當我沒發(fā)布過程序嗎?傻啊,我就5步搞定!當你說出這話你就會說上面那句很多人都說的話了。。。</p> <p>因為你的確需要做20步,所以發(fā)布的確很讓人頭疼,不過你想想發(fā)布成功之后插件啟動了之后。。。多么美好?。?lt;/p> <ul> <li>如果你開發(fā)的是Office 2003的插件那么請按照下面的步驟發(fā)布:</li> </ul> <p><a href="http://msdn.microsoft.com/en-us/library/bb332051(v=office.12).aspx">Deploying Visual Studio 2005 Tools for the Office System SE Solutions Using Windows Installer (Part 1 of 2)</a></p> <p><a href="http://msdn.microsoft.com/en-us/library/bb332052(v=office.12).aspx">Deploying Visual Studio 2005 Tools for the Office System SE Solutions Using Windows Installer (Part 2 of 2)</a></p> <p>做完P(guān)art 1之后是Part 2,都要按步驟做哦!</p> <p>另外給一個寫的不錯中文文章:</p> <p><a href="http://blog.csdn.net/v_jzho/article/details/1954454">如何簡單部署用VSTO SE 2005開發(fā)的Excel,Word插件</a></p> <p>&nbsp;</p> <ul> <li>如果你開發(fā)的是Office 2007以上的插件,請看這里的步驟:</li> </ul> <p><a href="http://www.cnblogs.com/brooks-dotnet/archive/2011/11/04/2236609.html">http://www.cnblogs.com/brooks-dotnet/archive/2011/11/04/2236609.html</a></p> <p>&nbsp;</p> <h1><strong>6. Office 2007</strong><strong>有問題怎么辦?</strong></h1> <p>&nbsp;</p> <p>開發(fā)和發(fā)布都沒有什么問題,應(yīng)為 Office 2007 版本以上的插件發(fā)布安裝都比 2003 不知道方便了多少,但是如果你發(fā)現(xiàn)調(diào)試沒問題,發(fā)布后也能成功安裝但是就是運行出錯,首先確認你的系統(tǒng)是不是 windows XP,因為 Windows 7 以上不會出現(xiàn)這種情況,windows 7 是自帶了 .NET Framework 2.0 的,但是XP沒有自帶,我一直奇怪用 .NET Framework 4.0 開發(fā)的插件為什么還要用 2.0 的 Framework?不過就是因為這個問題弄了我好久!</p> <p>所以記住讓你額客戶端無論如何都裝一個Framework 2.0比較保險一點。</p> <p>&nbsp;</p> <h1><strong>7. </strong><strong>怎么讓函數(shù)參數(shù)列表自動提示?</strong></h1> <p>&nbsp;</p> <p>一開始寫了個模塊定義了一些函數(shù),結(jié)果在Excel里面用的時候只是提示函數(shù)名參數(shù)都不提供,給其他人使用鬼曉得輸入什么參數(shù)???</p> <p><img alt="I=sho I" src="/sites/default/files/clip_image008.png" /></p> <p><img alt="I=ShowMeJheParameter(" src="/sites/default/files/clip_image009.png" /></p> <p>完全看不到參數(shù)提示,你說我該輸入什么?</p> <p>這時候如果按 Shift + Ctrl + A,下面就是見證奇跡的時刻!</p> <p><img alt="I=ShowMeTheParameter(IDIrName,Year,Month)" src="/sites/default/files/clip_image010.png" /></p> <p>參數(shù)列表出來了,這時候選擇參數(shù)去關(guān)聯(lián)Excel里面的單元格就可以了</p> <p><img alt="I=ShowMeTheparameter(C6,D6,E6,F4. . . .? ?.I I I :. . . .. ." src="/sites/default/files/clip_image011.png" /></p> <p>&nbsp;</p> <h1><strong>8. C# </strong><strong>如何動態(tài)調(diào)用Web Service</strong><strong>?</strong></h1> <p>&nbsp;</p> <p>查了網(wǎng)上很多文章都不靠譜,自己寫了一個,支持 Soapheader。</p> <pre class="brush: csharp"> using System; using System.Collections.Generic; using System.Text; using System.Net; using System.IO; using System.Web.Services.Description; using System.CodeDom; using Microsoft.CSharp; using System.CodeDom.Compiler; using System.Reflection; namespace ExcelAddIn { /// <summary> /// Call WebService Dynamically (support SoapHeader) /// </summary> public class WebServiceHelper { /// <summary> /// Get WebService Class Name /// </summary> /// <param name="wsUrl">WebService URL</param> /// <returns>WebService Class Name</returns> private static string GetWsClassName(string wsUrl) { string[] parts = wsUrl.Split('/'); string[] pps = parts[parts.Length - 1].Split('.'); return pps[0]; } /// <summary> /// Call WebService Without SoapHeader /// </summary> /// <param name="wsUrl">WebService URL</param> /// <param name="methodName">Method Name</param> /// <param name="args">Arguments</param> /// <returns>Method Result</returns> public static object InvokeWebService(string wsUrl, string methodName, object[] args) { return InvokeWebService(wsUrl, null, methodName, null, args); } /// <summary> /// Call WebService With SoapHeader /// </summary> /// <param name="wsUrl">WebService URL</param> /// <param name="methodName">Method Name</param> /// <param name="soapHeader">Soap Header</param> /// <param name="args">Arguments</param> /// <returns>Method Result</returns> public static object InvokeWebService(string wsUrl, string methodName, SoapHeader soapHeader, object[] args) { return InvokeWebService(wsUrl, null, methodName, soapHeader, args); } /// <summary> /// Call WebService /// </summary> /// <param name="wsUrl">WebService URL</param> /// <param name="className">Class Name</param> /// <param name="methodName">Method Name</param> /// <param name="soapHeader">Soap Header</param> /// <param name="args">Arguments</param> /// <returns>Method Result</returns> public static object InvokeWebService(string wsUrl, string className, string methodName, SoapHeader soapHeader, object[] args) { string @namespace = ""; if ((className == null) || (className == "")) { className = GetWsClassName(wsUrl); } WebClient wc = new WebClient(); Stream stream = wc.OpenRead(wsUrl + "?wsdl"); ServiceDescription sd = ServiceDescription.Read(stream); ServiceDescriptionImporter sdi = new ServiceDescriptionImporter(); sdi.AddServiceDescription(sd, "", ""); CodeNamespace cn = new CodeNamespace(@namespace); CodeCompileUnit ccu = new CodeCompileUnit(); ccu.Namespaces.Add(cn); sdi.Import(cn, ccu); CSharpCodeProvider csc = new CSharpCodeProvider(); ICodeCompiler icc = csc.CreateCompiler(); CompilerParameters cplist = new CompilerParameters(); cplist.GenerateExecutable = false; cplist.GenerateInMemory = true; cplist.ReferencedAssemblies.Add("System.dll"); cplist.ReferencedAssemblies.Add("System.XML.dll"); cplist.ReferencedAssemblies.Add("System.Web.Services.dll"); cplist.ReferencedAssemblies.Add("System.Data.dll"); CompilerResults cr = icc.CompileAssemblyFromDom(cplist, ccu); if (true == cr.Errors.HasErrors) { System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (System.CodeDom.Compiler.CompilerError ce in cr.Errors) { sb.Append(ce.ToString()); sb.Append(System.Environment.NewLine); } throw new Exception(sb.ToString()); } System.Reflection.Assembly assembly = cr.CompiledAssembly; Type t = assembly.GetType(@namespace + "." + className, true, true); FieldInfo[] arry = t.GetFields(); FieldInfo client = null; object clientkey = null; if (soapHeader != null) { client = t.GetField(soapHeader.ClassName + "Value"); Type typeClient = assembly.GetType(@namespace + "." + soapHeader.ClassName); clientkey = Activator.CreateInstance(typeClient); foreach (KeyValuePair<string, object> property in soapHeader.Properties) { typeClient.GetField(property.Key).SetValue(clientkey, property.Value); } } object obj = Activator.CreateInstance(t); if (soapHeader != null) { client.SetValue(obj, clientkey); } System.Reflection.MethodInfo mi = t.GetMethod(methodName); return mi.Invoke(obj, args); } /// <summary> /// Soap Header /// </summary> public class SoapHeader { /// <summary> /// SoapHeader /// </summary> public SoapHeader() { this.Properties = new Dictionary<string, object>(); } /// <summary> /// SoapHeader /// </summary> /// <param name="className">Soap Header Class Name</param> public SoapHeader(string className) { this.ClassName = className; this.Properties = new Dictionary<string, object>(); } /// <summary> /// SoapHeader /// </summary> /// <param name="className">Soap Header Class Name</param> /// <param name="properties">Soap Header Properties</param> public SoapHeader(string className, Dictionary<string, object> properties) { this.ClassName = className; this.Properties = properties; } /// <summary> /// Soap Header Class Name /// </summary> public string ClassName; /// <summary> /// Soap Header Properties /// </summary> public Dictionary<string, object> Properties; /// <summary> /// Add a Property to SoapHeader /// </summary> /// <param name="name">Soap Property Name</param> /// <param name="value">Soap Property Value</param> public void AddProperty(string name, object value) { if (this.Properties == null) { this.Properties = new Dictionary<string, object>(); } Properties.Add(name, value); } } } } </pre> <p>&nbsp;</p> <h1><em><strong>9. </strong></em><em><strong>待續(xù)</strong></em></h1> </div></div></div><div id="comment-wrapper-nid-472"></div><div class="field field-name-field-nuova-blogtag field-type-taxonomy-term-reference field-label-above"><div class="field-label">諾懷博客標簽:&nbsp;</div><div class="field-items"><div class="field-item even"><a href="/blogtag/excel" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Excel</a></div><div class="field-item odd"><a href="/blogtag/%E5%BC%80%E5%8F%A3%E5%8D%8F%E8%AE%AE" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">開口協(xié)議</a></div><div class="field-item even"><a href="/blogtag/web-service" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Web Service</a></div></div></div> Fri, 13 Dec 2013 06:19:12 +0000 Yee 472 at http://www.smt18.com http://www.smt18.com/blog/excel-%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB#comments