基于 .NET 的插件/附加组件框架,带动态工具栏和菜单






4.74/5 (39投票s)
2006年5月2日
6分钟阅读

181447

2230
一篇关于基于 .NET 的插件/附加组件框架的文章,带有动态工具栏和菜单。
引言
构建一个能够满足客户未来需求的软件产品始终是一项具有挑战性的任务。当软件进入增强/维护阶段时,情况尤其如此。在此阶段,最初创建软件的团队成员可能已经离开了公司,或者他们可能在其他项目团队中。如果你能找到最初在开发团队中帮助你的成员,那将是你的幸运。如果一个对产品不熟悉的新程序员参与到产品的增强中,那么他犯错并破坏现有功能的可能性就会更大,这些错误在客户现场发生之前将无法识别。如果软件设计不佳且没有可用的正确文档,情况会更糟。此外,为现有产品添加新功能的开发成本也非常高,仅仅是因为整个系统需要再次测试并作为另一个版本发布。这就是附加组件项目框架获胜的地方。在我之前的文章中,我向您展示了如何使用VC++、MFC、ATL 和 COM 编写带有动态工具栏和菜单的插件框架。在本文中,我将向您展示如何使用 .NET 框架库做同样的事情。
尽管微软正在推动他们的客户转向 .NET 技术,但他们的产品 VS.NET 2005 产品框架仍然是用旧的 Win32、MFC 和 COM 技术编写的。这迫使我思考当前的 .NET 框架库可能存在一些问题,导致微软不在他们的主要产品(如 VS.NET 2005)中使用它(或者他们没有在他们的产品中使用 .NET 技术还有其他原因吗?)。本文是这种思考的成果。本文只是出于好奇而撰写,旨在了解使用 .NET 编写带有动态工具栏和菜单的插件框架有多困难。令我惊讶的是,编写一个类似于我用 VC++、MFC 和 ATL 编写的 .NET 插件框架非常容易,并且所需时间仅为 VC++、MFC 和 ATL 的一半。除了 .NET 中键盘快捷键自定义等一些小限制(.NET 库仅提供预定义的快捷键)之外,我没有看到任何差异,就实现而言,它更高效。
在本文中,我将解释这个基于 .NET 的附加组件项目框架架构、插件如何加载以及它与框架的交互、如何从菜单和工具栏调用附加组件函数等。我还会讨论这两种方法(基于 .NET 的方法和基于 VC++、MFC 和 ATL/COM 的方法)的优缺点,我认为这将对您未来的项目有所帮助。CodeProject 上还有其他基于 .NET 框架的插件框架。与这些框架相比,本框架的优势在于它非常简单且易于理解。一旦您了解了它的工作原理,就可以扩展此框架以满足您的需求。
背景
本文假设您具有 C# 编程背景,熟悉一些常见的命名空间和库,并且对构建 .NET 组件、XML、接口等有所了解。本框架的核心依赖于 `System.Reflection` 命名空间,并假设您对此命名空间有很好的理解。
简要架构
下面展示了 .NET 插件框架模型的简要架构
图 1 .NET 插件框架架构.
该架构包含一个名为 *AddinInterfaces.dll* 的接口 DLL,其中包含每个框架 (`IProjectFrameworkApp`) 和插件 (`IProjectFrameworkAddin`) 都应实现的必要接口。每个接口的基本接口定义如下
public interface IProjectFrameworkAddin
{
void InitializeAddin(ref IProjectFrameworkApp ref
ProjectFrameworkApp, long lSession,bool bFirstLoad);
void UnInitializeAddin(long lSession);
}
这是每个插件都应实现的接口,用于建立插件与框架之间的连接。框架会在适当的时间调用此接口中的函数。
public interface IProjectFrameworkApp
{
void AddCommandsInfo(string strXMLMenuInfo, long lSession,
object lInstanceHandle, object lToolbarInfo);
void SendMessage(string strMessage);
}
此接口由框架实现,供插件用于向框架发送信息和消息。
您可以根据需要在此 DLL 中添加其他接口,这些接口将由框架或附加组件实现。
XML 插件菜单格式
该框架使用以下 XML 结构来交换有关附加组件中的菜单、工具栏和其他信息。
<ProjectFrameworkAddin>
<AppVer>1</AppVer>
<AddinName>Report Addin</AddinName>
<ToobarButtonCount>1</ToobarButtonCount>
<MainMenu>
<Name>Bar Code</Name>
<ShortcutKeyIndex>1</ShortcutKeyIndex>
<SubMenu>
<Name>Bar Code</Name>
<ShortcutKeyIndex>1</ShortcutKeyIndex>
<LeafMenu>
<Name>Test Menu</Name>
<FunctionName>AddinFunctionName</FunctionName>
<HelpString>Some Status bar text</HelpString>
<ToolTip>Some tool tip text</ToolTip>
<ToolBarIndex>Addin2Settings.ico</ToolBarIndex>
<ShortCutKey>Ctrl + H</ShortCutKey>
</LeafMenu>
<LeafMenu>
........
........
</LeafMenu>
</SubMenu>
<SubMenu>
..............
..............
</SubMenu>
</MainMenu>
<MainMenu>
......
......
</MainMenu>
</ProjectFrameworkAddin>
程序流程
该框架的基本程序流程在图 2 中显示为高级时序图。
图 2 程序流程
使用代码
示例源代码包含四个项目,即 *ProjectFramework*、*AddinInterfaces*、*Addin1* 和 *Addin2*。ProjectFramework 是核心应用程序,它加载附加组件目录中找到的所有附加组件。AddinInterfaces 是由框架和附加组件引用的 DLL,其中包含所有接口定义。Addin1 和 Addin2 是两个示例附加组件。请参阅源代码了解更多详细信息。下面列出了构建应用程序的重要代码块。
在 `AddinProjectFramework` 构造函数中,请注意加粗的代码并查看高级程序流程图(图 2)。
public AddinProjectFramework()
{
// // Required for Windows Form Designer support //
m_PluginManager= new PluginManager();
m_FrameworkApp = new ProjectFrameworkApp();
m_AddinToolbarArray= new ArrayList();
System.Configuration.AppSettingsReader
configurationAppSettings = new
System.Configuration.AppSettingsReader();
m_strPluginFile=((string)
(configurationAppSettings.GetValue(
"PluginFile", typeof(string))));
InitializeComponent();
m_PluginManager.ProjectFramework=this;
m_FrameworkApp.ProjectFramework=this;
//Set the IProjectFrameworkApp reference
m_PluginManager.m_FrameworkApp =
(IProjectFrameworkApp)m_FrameworkApp;
//Load all addins
if(CheckAddinSettings())
{
LoadAllAddins(m_bLoadAllAddins);
m_PluginManager.LoadAddinMenus();
m_PluginManager.LoadAddinToolbars();
m_PluginManager.LoadAddinToolbarMenus();
//Remove the about box from
//the Middle and add it towards the end.
//Help menu is here
mainProjectMenu.MenuItems.RemoveAt(3);
mainProjectMenu.MenuItems.Add(menuItemHelp);
}
}
框架的核心依赖于一个名为 `PluginManager` 的类。插件管理器的骨架如下
namespace ProjectFramework
{
/// This class handles all the plugin
/// related activities their
///loading, saving etc
public struct AddinCommadInfo
{
//All Menus before coming to the leaf node
public ArrayList MenuStringsArray;
public int iCommandID;
public string strMenuString;
public string strHelpString;
public string strToolTip;
public string strFunction;
public string strShortCutKey;
public string strToolbarIndexName;
public int iSeparator;
public MenuItem Menu;
void Initialize()...
};
public struct AddinInfo
{
public string strAddinName;
public string strAddinDllName;
public string strAddinVersion;
public object lInstanceHandle;
public object lToobarRes;
public bool bLoadAddin;
public int lToolbarButtonCount;
public Assembly AddinAssembly;
public Type AddinType;
public IProjectFrameworkAddin FrameworkAddin;
public string strAddinInterfaceName;
public ArrayList AddinCommadInfoArray;
void Initialize() ....
};
public class PluginManager
{
public AddinInfo[] AddinInfoArray;
public AddinProjectFramework ProjectFramework;
public IProjectFrameworkApp m_FrameworkApp;
public bool m_bLoadAddinsOnStartup;
private string m_strXMLFileName;
public PluginManager()....
public bool LoadPluginDetailsFromXML(string
strXMLFileName)..
public bool SavePluginDetailsToXML()...
public bool LoadAddAddinAssemblyInfo(string
strAddinFolderName)...
public bool UnloadAllAddins()...
public bool InvokeAddinMember(int iAddinIndex,
string strFunctionName)...
public string GetMainInterfaceName(string strDllName)...
public int GetAddinIndex(string strDllName)...
public bool InvokeMenu(ref object AddinMenuItem)...
public bool UpdateAddinMenuStatus(int iAddinIndex,
bool bEnable)...
public bool LoadAddinMenus()...
public bool LoadAddinToolbars()...
public bool LoadAddinToolbarMenus()...
public void GetMainMenuItem(string strMenuName,
out MenuItem Item)....
public void GetMenuItem(string strMenuName,
ref MenuItem ItemParent,
out MenuItem Item)...
}
基于 .NET 的插件框架与基于 MFC、ATL、COM 的插件框架的比较
功能 | 基于 .NET 的插件框架 | 基于 MFC、ATL、COM 的插件框架 |
易用性 | 非常易于使用。 | 难以使用。需要了解许多 COM 的复杂细节。 |
灵活性 | 对于数据库应用程序,此插件框架很好。不适用于电子表格和图形软件包等高端应用程序。 | 适用于需要高度灵活性的各种框架。 |
模块化 | .NET 库是松散耦合的。因此,易于设计和开发面向模式的应用程序。 | MFC 库是紧密耦合的。难以设计和开发面向模式的应用程序。 |
速度 | 一旦 IL 转换为机器代码,性能可与本地代码媲美。 | 性能最佳。有许多优化技术可用。 |
内存 | 与原生代码相比,内存消耗较高。此外,CLR 决定了未使用对象的生命周期。在内存优化方面不是确定性的。 | 对内存有更多控制。可以进行很好的内存优化。 |
支持 | .NET 是一项新技术。未来有望获得较好的支持。 | 迄今为止支持良好。时间会证明微软未来是否会支持 MFC。 |
开发成本 | 低功耗 | 高。 |
数据类型支持 | .NET 框架支持的任何类型。 | 仅支持 COM 特定数据类型、接口和自定义接口。 |
关注点
此版本实现的功能有
- 动态菜单。
- 动态工具栏。
- 帮助字符串和工具提示支持。
- 从附加组件菜单和工具栏调用插件中的方法。
- 使用从某个插件调用的对话框(报告附加组件 -> 销售报告 -> 今日销售报告,Ctrl + B 菜单)解释自动化支持。
- 插件的键盘加速器支持。
- 附加组件的加载/卸载。
- 支持获取来自附加组件的通知事件。
链接
历史
- 首次发布:2006 年 5 月 2 日。