Home
Nova Software
Nova SoftwareNova Software
  • 标准化 SaaS 产品

    • 诺怀云物业管理系统全场景智慧物业数字化方案
    • 诺怀云汽修管理系统汽修连锁业务全链路运营管理

    定制开发服务

    • 企业信息化系统定制
    • 移动 APP 开发
    • 微信 / 小程序开发
    • 电商系统
    • 企业官网建设
    SaaS 产品 + 软件定制双重能力

    匠心数字化服务

    SaaS 产品 + 软件定制双重能力

    成熟 SaaS 系统与软件定制开发,满足企业不同数字化需求

    new-arrownew-arrow
    数字化干货与案例分享

    行业洞察

    数字化干货与案例分享

    行业白皮书、项目案例解析、技术实践文章

    new-arrownew-arrow
  • 行业解决方案

    • 智慧物业
    • 智慧社区
    • 智慧园区
    • 智慧商业
    • 学校后勤
    • 医院后勤
    • 企事业后勤
    • 社区电商

    场景解决方案

    • 医院中央运送
    • 医院医废管理
    • 医院布草洗涤
    • 医院陪护管理
    • 设备管理
    • 维修工单管理
    • 保安巡更巡逻
    • 保洁管理
    • 物料管理
    • 场所租赁预约
    • 食堂管理
    • 考勤管理
    • 宿舍管理
    • 资产管理
    • 招商租赁管理
    • 收支管理
  • 客户案例
  • 公司介绍

    • 关于诺怀了解诺怀软件发展历程与企业概况
    • 我们的产品了解我们的产品框架
    • 新闻动态
    • 招贤纳士 加入我们,查看在招岗位
    • 选择诺怀我们的核心优势与服务特色
    • 联系我们商务咨询、技术对接与来访地址

    客户见证

    • 合作客户服务客户名录与行业覆盖
    • 客户评价来自合作企业真实客户心声

    合作模式

    • SaaS化标准系统开箱即用,快速落地
    • 定制开发模式你提需求我来实现,主打个性化
    • 数字化咨询服务业务诊断与数字化方案规划
试用申请
Home
重庆诺怀软件重庆诺怀软件

Menu

    • 诺怀云物业管理系统
    • 诺怀云汽修管理系统
    • 企业信息化系统定制
    • 移动 APP 开发
    • 微信 / 小程序开发
    • 电商系统
    • 企业官网建设
    • SaaS 产品 + 软件定制双重能力
    • 数字化干货与案例分享
    • 智慧物业
    • 智慧社区
    • 智慧园区
    • 智慧商业
    • 学校后勤
    • 医院后勤
    • 企事业后勤
    • 社区电商
    • 医院中央运送
    • 医院医废管理
    • 医院布草洗涤
    • 医院陪护管理
    • 设备管理
    • 维修工单管理
    • 保安巡更巡逻
    • 保洁管理
    • 物料管理
    • 场所租赁预约
    • 食堂管理
    • 考勤管理
    • 宿舍管理
    • 资产管理
    • 招商租赁管理
    • 收支管理
    • 关于诺怀
    • 我们的产品
    • 新闻动态
    • 招贤纳士
    • 选择诺怀
    • 联系我们
    • 合作客户
    • 客户评价
    • SaaS化标准系统
    • 定制开发模式
    • 数字化咨询服务

Excel 插件开发经验分享

Nova Software

2013-12-13
blog-details-cover

1. 这个文档写的是些什么?

这个文档分享了我在Excel插件开发中所遇到的问题和对应的解决方案。希望对要进行Excel插件开发或者想自己写插件增加效率的朋友有所帮助。

首先描述一下我的Excel开发遇到的场景:

我需要开发一个Excel的插件可以从后台Web Service获取数据然后显示到Excel中,那么就存在一个问题:Web Service的数据是通过什么方式写到Excel中呢?答案是Function!通过函数的方式写入Excel中。请看下面这幅图:

首先我会在C3 – C8 输入年份 2005 - 2010,这时候我需要在D3 - D8 使用函数 =GetPriceByYear(),参数就是C3 – C8的年份,然后从Web Service获取相应的数据写入其中。看到这里你可能会说用VBA就好了啊?VBA也可以调用Web Service啊,问什么这么麻烦用插件?

2. 为什么要使用插件这个东西?

下面我来说一下为什么要用插件。

之前这个功能的确是用VBA实现了的,但是VBA是和Excel绑定着的,换句话说你用VBA写了这个功能保存到Excel中,只有保存的文档能用这个函数功能,如果你用Excel新开一个文档,那你之前写的VBA用不到新的空白文档里面去。这就造成了麻烦。

那如果用插件呢?任何一个用文档不管是Excel新建的,还是从别的地方拷贝过来的文档,只要你的Excel安装了插件,并且插件启用了,那么就可以在任何一个文档使用这个功能。是不是比之前用VBA的方式更通用更方便呢?有.NET强大的类库支持,插件的功能可以写得更强大更实用。

那么就开始动手开发插件吧!等等,好像需要先调查一下网上有没有什么好的方案,存不存在什么问题。

3. 用什么开发插件?

.NET程序员伤不起,开发环境当然首选Visual Studio了,接下来就有两种选择:

一种是VSTO****(Visual Studio Tools for Office****)

还有一种就是:Shared Add-in

这两种有什么异同呢?相同的点不用说了,都可以开发Excel Add-in,不同的就体现在Shared这个词上,VSTO开发的插件是针对某一个Office产品的,也就是Excel,Word,Outlook需要用VSTO开发三个插件才能在这三个产品中使用,而Shard Add-in只需要开发一个,就可以在这三个产品上使用,很强大是吧?所以根据你的需求选择最适合你的一种方式开发就好了。

如果你想深入了解他们之间的区别可以去这里看下:

Shared Add-in vs. VSTO Add-in: What's the difference between/how can I tell if I'm developing?

这时你可能兴奋得马上开始打开VS创建一个项目试试,试试归试试,你回头看到周围电脑大家用的操作系统和Office都不一样的时候,你突然开始思考了。。。

4.插件在Office 2000 – 2013上都能用吗?

答案是否定的,首先看Wiki 上的VSTO怎么说:

The developer editions of Office have been discontinued after Office XP and VSTO is available for Office 2003 and later versions only.

很遗憾Office 2000 还有 Office XP不被支持了,其实现在没看到用这两个版本的了,最低版本也就是 2003了,但是看到这句话我心有余悸,微软的这种抛弃式做法,那估计想同时兼容2003-2013也悬了,因为你看下Office2003和Office2007的界面差距有多大你就清楚了。

结果正如我所料,请看下图:

当我看到这个图我凌乱了~哦难道要一个Office版本开发一个?

经过我的不懈Google。。。和虚拟机中的不懈测试。。。

终于得出了结论:

  • 如果开发Office 2003****的Add-in 使用 VS2008 + .NET 3.5 + VSTO 3.0
  • 如果开发Office 2007以上的Add-in使用(VS2010****或者VS2012 )+ .NET 4.0 + VSTO 4.0

5. 那我写个小Demo****发布一下试试?

“开发机上运行完美,客户机上运行不起来!”

这个是很多人发布插件到客户机安装后得出的结论!是这么多人都SB吗?肿么可能?是因为发布程序的陷阱太多了,换句话说,官方给了你发布程序的20个步骤,你看了之后说:你当我没发布过程序吗?傻啊,我就5步搞定!当你说出这话你就会说上面那句很多人都说的话了。。。

因为你的确需要做20步,所以发布的确很让人头疼,不过你想想发布成功之后插件启动了之后。。。多么美好啊!

  • 如果你开发的是Office 2003的插件那么请按照下面的步骤发布:

Deploying Visual Studio 2005 Tools for the Office System SE Solutions Using Windows Installer (Part 1 of 2)

Deploying Visual Studio 2005 Tools for the Office System SE Solutions Using Windows Installer (Part 2 of 2)

做完Part 1之后是Part 2,都要按步骤做哦!

另外给一个写的不错中文文章:

如何简单部署用VSTO SE 2005开发的Excel,Word插件

  • 如果你开发的是Office 2007以上的插件,请看这里的步骤:

http://www.cnblogs.com/brooks-dotnet/archive/2011/11/04/2236609.html

6. Office 2007****有问题怎么办?

开发和发布都没有什么问题,应为 Office 2007 版本以上的插件发布安装都比 2003 不知道方便了多少,但是如果你发现调试没问题,发布后也能成功安装但是就是运行出错,首先确认你的系统是不是 windows XP,因为 Windows 7 以上不会出现这种情况,windows 7 是自带了 .NET Framework 2.0 的,但是XP没有自带,我一直奇怪用 .NET Framework 4.0 开发的插件为什么还要用 2.0 的 Framework?不过就是因为这个问题弄了我好久!

所以记住让你额客户端无论如何都装一个Framework 2.0比较保险一点。

7. 怎么让函数参数列表自动提示?

一开始写了个模块定义了一些函数,结果在Excel里面用的时候只是提示函数名参数都不提供,给其他人使用鬼晓得输入什么参数啊?

I=sho I

I=ShowMeJheParameter(

完全看不到参数提示,你说我该输入什么?

这时候如果按 Shift + Ctrl + A,下面就是见证奇迹的时刻!

I=ShowMeTheParameter(IDIrName,Year,Month)

参数列表出来了,这时候选择参数去关联Excel里面的单元格就可以了

I=ShowMeTheparameter(C6,D6,E6,F4. . . .? ?.I I I :. . . .. .

8. C# 如何动态调用Web Service**?**

查了网上很多文章都不靠谱,自己写了一个,支持 Soapheader。

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 { /// /// Call WebService Dynamically (support SoapHeader) /// public class WebServiceHelper { ///
/// Get WebService Class Name ///
/// WebService URL
/// WebService Class Name
private static string GetWsClassName(string wsUrl) { string[] parts = wsUrl.Split('/'); string[] pps = parts[parts.Length - 1].Split('.'); return pps[0]; }

    ///    
    /// Call WebService Without SoapHeader
    ///    
    /// WebService URL   
    /// Method Name   
    /// Arguments   
    /// Method Result   
    public static object InvokeWebService(string wsUrl, string methodName, object\[\] args)
    {
        return InvokeWebService(wsUrl, null, methodName, null, args);
    }

    ///    
    /// Call WebService With SoapHeader
    ///    
    /// WebService URL   
    /// Method Name   
    /// Soap Header   
    /// Arguments   
    /// Method Result
    public static object InvokeWebService(string wsUrl, string methodName, SoapHeader soapHeader, object\[\] args)
    {
        return InvokeWebService(wsUrl, null, methodName, soapHeader, args);
    }

    ///    
    /// Call WebService
    ///    
    /// WebService URL   
    /// Class Name   
    /// Method Name   
    /// Soap Header
    /// Arguments   
    /// Method Result   
    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 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);
    }

    /// /// Soap Header
    /// 
    public class SoapHeader
    {
        /// /// SoapHeader
        /// 
        public SoapHeader()
        {
            this.Properties = new Dictionary();
        }

        /// /// SoapHeader
        /// 
        /// Soap Header Class Name
        public SoapHeader(string className)
        {
            this.ClassName = className;
            this.Properties = new Dictionary();
        }

        /// /// SoapHeader
        /// 
        /// Soap Header Class Name
        /// Soap Header Properties
        public SoapHeader(string className, Dictionary properties)
        {
            this.ClassName = className;
            this.Properties = properties;
        }

        /// /// Soap Header Class Name
        /// 
        public string ClassName;

        /// /// Soap Header Properties
        /// 
        public Dictionary Properties;

        /// /// Add a Property to SoapHeader
        /// 
        /// Soap Property Name
        /// Soap Property Value
        public void AddProperty(string name, object value)
        {
            if (this.Properties == null)
            {
                this.Properties = new Dictionary();
            }
            Properties.Add(name, value);
        }
    }
}

}

9. 待续

留言
诺怀软件

诺怀软件

Oct 26, 2026

诺怀软件自 2005 年投身软件开发领域,是国家高新技术、重庆市专精特新企业,积累了 20 多年行业数字化落地经验,自主研发的诺怀云物管平台获评重庆重点软件产品。我们服务全国大量物业、园区、医疗机构客户,以可追溯、透明化的数字化方案,帮助客户解决账目混乱、协同低效、监管难落地等痛点,赋能物业规范化运营,重建业主信任。欢迎留言,获取适配您项目的专属方案.

回复
预约咨询

获取定制开发解决方案

如果您也想了解软件定制开发、落地智慧物业、后勤数字化管理等,可预约顾问一对一沟通方案.

  • 20+开发经验

  • 30+全球区域客户

  • 15000+成功项目

footer-four-gradient
nova software logo

专注企业软件定制开发,提供物业、汽修等行业整体解决方案,助力企业业务数字化升级

抖音抖音
小红书小红书
微信服务号微信服务号
今日头条今日头条
搜狐号搜狐号
视频号视频号

关于诺怀

  • 公司介绍
  • 新闻动态
  • 客户案例
  • 联系我们
  • 常见问答

产品服务

  • 软件定制开发
  • 移动 APP 开发
  • 小程序定制开发
  • 物业SaaS系统
  • 汽修SaaS系统
  • 网站开发
  • 电商开发

案例及解决方案

  • 智慧物业解决方案合集
  • 汽修管理解决方案
  • 物业客户案例合集
  • 定制开发案例合集

咨询热线:4008310286

渝ICP备14000366号-1© 2026 重庆诺怀软件有限公司