在很多场合,我们需要在已有软件程序上增加一些新的功能,几乎所有原因是因为原有软件功能不能满足我们的需要,我们平时做的插件就属于这种情况,最常见的是VS IDE的插件开发,网上老外写的一篇关于插件开发的文章,很详细(网址)。如果我们要给一个已有软件扩展新的功能,一般我们必须知道原有软件提供给二次开发人员的接口,也就是说,如果原有软件在设计的时候,压根儿就没有考虑到后续可能存在的二次开发,也不提供任何接口,那么通常情况下,是很难在它的基础上扩展出新功能的(除非是原有软件开发者)。

还有一种可以扩展已有程序功能的方式,网址,利用windows消息、windows hook技术,理论上可以给任何一个桌面应用程序扩展出新功能,而不需要任何接口,但是,这种方式很有局限性,扩展出来的功能几乎停留在操作系统级别上,比如UI外观样式等,并不能真正的去与已有软件程序进行交互。老外这篇文章其实重点是在讲Windows hooks和Windows Message。

这篇文章不是讲怎么去开发VS插件,更不是谈哪个具体软件比如CAD、PROE的二次开发,我只是想将看似复杂的东西简单化地解释一下,看看“给已有软件扩展新功能”到底是怎么回事。以插件为例:

首先,宿主程序和插件之间一定要有交互的,不然的话,插件是不会知道什么时候该干什么事情;其次,宿主程序一定会传递某些数据信息给插件,否则你叫插件拿什么原材料干活?最后,宿主程序一定是要有所准备的,什么叫有所准备?也就是说,在开发宿主程序的时候,一定要为以后的功能扩展留有接口,所有插件必须遵守这个接口给出的规范,知道应该在什么时候跟插件通讯,了解插件的任何一个行为将会导致什么样的结果,并且作出相应的反应。综上所述,给已有程序扩展新功能,关键还是在这个“已有程序”身上,如果一个程序出生的时候就没想着将来别人要给自己增加功能,那你不用再想着去给它扩展功能了。也就是我文章刚开始说到的,并不是你可以在任何一个程序基础上扩展新功能。

图1

如上图所示,宿主程序与插件之间通过某一协议进行通信,这个跟上一篇最后讲到的“框架和客户端代码之间的关系”很相似,你可以把宿主程序看做是框架,而插件则是客户端代码(参见上一篇文章图6)。

图2

如上图,在宿主程序中应该提前设计好该在什么时候与插件通信,以及给它传递对应数据信息,接着返回交互结果。宿主程序应该考虑所有与插件交互的地方和时间,然而插件不一定处处都会有所反应,也就是说,一个宿主程序设计好100个与插件交互的地方(插件最多可以在这100个地方大做文章),但是你开发一个插件时,根据具体需要,完全可以只响应其中的某几个。

图3

文章后面我附上一个简单的画图Demo,实现简单的画板、保存(默认可以保存JPG图片格式和PIC可编辑格式)等功能,然后自己又做了一个插件,插件主要新增了以下功能:

  • 增加一个“关于”菜单,点击弹出关于对话框;
  • 已有画板程序只能绘制圆形和正方形,增加了一个三角形图形;
  • 将画图保存成JPG格式时,在图片上添加水印;
  • 增加一种全新的文件格式(newpic格式),可以将画图保存为newpic格式的文件,这个有点类似photoshop的ico插件,安装后,PS可以将图片保存为ico格式。

整个项目源码分为以下三个部分:

  1. PluginDemo:宿主程序,在它的基础上扩展新的功能;
  2. PluginHelper:扩展功能时必须遵守的规范(接口),随宿主程序一起开发,通常就是我们常说的“二次开发包”,理论上应该还有二次开发说明文档之类的东西;
  3. Plugin:我自己开发的一个插件。

正常情况下,1和2由已有软件开发商提供,3由二次开发人员开发。

下面主要说明一下PluginHelper中的两个接口,其余的源码诸位可以自己下下来看看。

IPlugin接口:

     /// <summary>
/// 插件接口 所有插件必须实现该接口
/// </summary>
public interface IPlugin
{
void ApplicationLoaded(PluginApplication pluginApplication); //应用加载后
void FileSavingAsJPG(Bitmap bitmap,string filepath); //文件保存为JPG
void FileSavingAsPIC(PluginApplication pluginApplication); //文件保存为PIC
void BeforeSave(Dictionary<string,SaveFileHandler> extensions); //保存文件之前
void BeforeOpen(Dictionary<string,OpenFileHandler> extensions); //打开文件之前
void ApplicationExiting(); //应用退出时
}

如接口代码所示,在固定时候固定地方,宿主程序都会调用对应方法与插件通信。

IObject接口:

    /// <summary>
/// 图形接口 所有的图形都必须实现该接口
/// </summary>
public interface IObject
{
int X
{
get;
set;
}
int Y
{
get;
set;
}
void Draw(Graphics g);
}

所有新扩展图形都必须实现该接口。

如果想要开发自己的插件,只需要知道二次开发包(PluginHelper.dll),定义一个类实现IPlugin接口就行。

图4

最后上几张效果图,没插件之前的宿主程序:

图5

安装插件后的宿主程序:

图6

如上图所示,安装插件后,菜单多了“关于”菜单项,工具栏多了“三角形”按钮,可以保存另外一种“newpic”格式的文件,另外,在保存为JPG格式图片时,已有软件保存图片为:

图7

安装插件后,保存为JPG格式文件如下:

图8

如上图,安装插件后,保存的JPG图有水印。

下载源码:http://files.cnblogs.com/xiaozhi_5638/PluginDemo.rar

将开发的插件放在宿主程序的plugins目录下,重启宿主程序就可以。希望对各位有帮助!

.Net开发笔记(十七) 应用程序扩展的更多相关文章

  1. Java开发笔记(八十七)随机访问文件的读写

    前面介绍了字符流读写文件的两种方式,包括文件字符流和缓存字符流,但是它们的写操作都存在一个问题:不管是write方法还是append方法,都只能从文件开头写入,而不能追加到文件末尾或者在文件中间某个位 ...

  2. Java开发笔记(二十七)数值包装类型

    方法的出现缘起优化代码结构,但它的意义并不局限于此,正因为有了方法定义,编程语言才更像一门能解决实际问题的工具,而不仅仅是只能用于加减乘除的计算器.在数学的发展过程中,为了表示四则运算,人们创造了加减 ...

  3. Java开发笔记(三十七)利用正则串分割字符串

    前面介绍了处理字符串的常用方法,还有一种分割字符串的场景也很常见,也就是按照某个规则将字符串切割为若干子串.分割规则通常是指定某个分隔符,根据字符串内部的分隔符将字符串进行分割,例如逗号.空格等等都可 ...

  4. Java开发笔记(四十七)关键字this的用法

    前面介绍了类的基本定义,包括成员属性.成员方法.构造方法几个组成要素,可谓是具备了类的完整封装形态.不过在进行下一阶段的学习之前,有必要梳理一下前述的类定义代码,看看是否存在哪些需要优化的地方.首先观 ...

  5. Java开发笔记(五十七)因抽象方法而产生的抽象类

    前面介绍了类的常见用法,令人感叹面向对象的强大,几乎日常生活中的所有事物,都可以抽象成Java的基类及其子类.然而抽象操作也有副作用,就是某个抽象而来的行为可能是不确定的,比如半夜鸡叫,如果是公鸡则必 ...

  6. Java开发笔记(六十七)清单:ArrayList和LinkedList

    前面介绍了集合与映射两类容器,它们的共同特点是每个元素都是唯一的,并且采用二叉树方式的类型还自带有序性.然而这两个特点也存在弊端:其一,为啥内部元素必须是唯一的呢?像手机店卖出了两部Mate20,虽然 ...

  7. Java开发笔记(七十七)使用Optional规避空指针异常

    前面在介绍清单用法的时候,讲到了既能使用for循环遍历清单,也能通过stream流式加工清单.譬如从一个苹果清单中挑选出红苹果清单,采取for循环和流式处理都可以实现.下面是通过for循环挑出红苹果清 ...

  8. Java开发笔记(九十七)利用Runnable启动线程

    前面介绍了线程的基本用法,按理说足够一般的场合使用了,只是每次开辟新线程,都得单独定义专门的线程类,着实开销不小.注意到新线程内部真正需要开发者重写的仅有run方法,其实就是一段代码块,分线程启动之后 ...

  9. Java开发笔记(一百四十七)通过JDBC管理数据库

    前面介绍了如何通过JDBC获取数据库连接,可是Connection对象不能直接执行SQL语句,需要引入Statement报告对象才能操作SQL.Statement对象由Connection的creat ...

  10. Java开发笔记(一百一十七)AWT窗口

    前面介绍的所有Java代码,都只能通过日志观察运行情况,就算编译成class文件,也必须在命令行下面运行,这样的程序无疑只能给开发者做调试用,不能拿给一般人使用.因为普通用户早已习惯在窗口界面上操作, ...

随机推荐

  1. C++ 系列:静态库与动态库

    转载自http://www.cnblogs.com/skynet/p/3372855.html 这次分享的宗旨是——让大家学会创建与使用静态库.动态库,知道静态库与动态库的区别,知道使用的时候如何选择 ...

  2. 【leetcode】Isomorphic Strings

    题目简述: Given two strings s and t, determine if they are isomorphic. Two strings are isomorphic if the ...

  3. 设置CentOS不启用图形界面

    由于linux服务器放在机房长时间运行,或者只是在后台运行,一般通过secureCRT连接服务器: 所以在虚拟机上都不需要图形界面,而且图形界面占用内存. 所以,我们可以通过简单的设置,让linux只 ...

  4. VadRoot枚举进程模块在Windows7下的完整实现

      原理小伟的小伟在http://bbs.pediy.com/showthread.php?t=66886说的挺清楚了,Windows7下有一些变化,使用NtQueryVirtualMemory来枚举 ...

  5. 使用手机展示axure

    UC浏览器全屏,去地址栏 发布axure选项设置一下:参考http://sowm.cn/yixieshi/article/A752C3309457FBE32A99CA3A6A50B993.html 启 ...

  6. 【CentOS】LAMP

    文章需要整合,学习需要归纳,博主把一连四篇的LAMP合并成为一片长篇的大部头,并梳理了一下他们的关系,希望对各位有所帮助 最近一次更新:2016年12月21日21:38:31 本文为博主JerryCh ...

  7. javaweb学习笔记之servlet01

    一.Servlet概述 A servlet is a small Java program that runs within a Web server. Servlets receive and re ...

  8. 消息队列之ZeroMQ(C++)

    ZMQ是什么? 这是个类似于Socket的一系列接口,他跟Socket的区别是:普通 的socket是端到端的(1:1的关系),而ZMQ却是可以N:M 的关系,人们对BSD套接字的了解较多的是点对点的 ...

  9. C语言中使用系统自带的快排函数

    题目 . 德才论 () 宋代史学家司马光在<资治通鉴>中有一段著名的"德才论":"是故才德全尽谓之圣人,才德兼亡谓之愚人,德胜才谓之君子,才胜德谓之小人.凡取 ...

  10. java-并发-线程对象

    浏览以下内容前,请点击并阅读 声明 每个线程都和类Thread的实例相关,有两种基本的使用Thread对象来创建并发应用的方法: 直接控制线程的创建和管理,每次需要开始一个异步任务使简单地实例化Thr ...