webweb前端开发插件的后台插件化web前端开发插件是怎么实现的

Winform开发框架之插件化应用框架实现 - 伍华聪 - 博客园
随笔 - 558, 文章 - 20, 评论 - 7600, 引用 - 20
支持插件化应用的开发框架能给程序带来无穷的生命力,也是目前很多系统、程序追求的重要方向之一,插件化的模块,在遵循一定的接口标准的基础上,可以实现快速集成,也就是所谓的热插拔操作,可以无限对已经开发好系统进行扩展,而且不会影响已有的功能,不在需要的模块,通过修改配置移除即可。我的Winform开发框架一直以来,来源于多年的项目积累以及客户的反馈,已经具备了众多很好的特性以及相关的模块组合,为了更好拥抱变化,提高基于Winform开发框架基础上开发新系统的效率,以及为框架融入更多好的特性,故此把我的Winform开发框架在原来的基础上进行扩展,实现基于插件化应用的框架特性。
为了引入插件化的应用框架特点,我在上一篇随笔《》已经对我的通用权限管理系统进行了改进,其中增加了菜单管理模块就是为了做插件化做准备的,我们通过权限管理系统配置好菜单的相关信息,然后在应用框架中动态加载菜单功能即可实现。这个菜单模块,是用来配置基于Web开发框架或者Winform开发框架、WCF开发框架的菜单,通过预先的配置,框架程序的动态加载解析,就能实现插件模块的热插拔功能了。实际插件化框架的菜单配置界面效果如下所示。
最终在Winform开发框架的程序中,实现基于插件化的应用,如下所示。
先来看看我改造Winform开发框架,最终形成的框架界面效果,然后在逐一进行介绍,整个开发框架的实现过程。
1、框架的项目工程规划
为了减少框架整体的复杂性以及提高重用,对插件化的应用框架的项目工程进行了划分,包括&框架基础界面模块&、&插件应用框架启动模块&、仓库管理系统模块业务逻辑、仓库管理系统模块窗体界面等几个部分。前面两个部分是插件化框架的核心,可以认为是不需要变化的模块,提供所有插件应用动态创建以及使用的框架支撑;后面两个是具体的主业务模块,这里以WInform开发框架中的仓库管理系统作为主业务模块,它本身也是插件应用之一,具体的项目工程结构以及说明如下所示。
WHC.Framework.BaseUIDx
&框架基础界面模块,定义窗体界面基类、通用Excel导入模块、通用高级查询模块等
WHC.Framework.StarterDx
&插件应用框架启动模块,集成权限登录、动态菜单创建、插件应用动态加载、基础框架功能等
WHC.WareHouseMis
&仓库管理系统模块的业务逻辑
WHC.Framework.WareHouseDx
&&仓库管理系统模块的窗体界面
从上面的表格说明中,我们可以看到&WHC.Framework.StarterDx&项目工程,是&插件应用框架启动模块&,它基本上只和权限管理系统模块有关联关系,因为权限系统是框架底层支撑的模块,包括用户登录、菜单管理、权限控制等都需要从权限管理系统中获取数据,具体的主要业务功能如下所示。
2、框架的菜单动态加载
&本文第一张图片里面,介绍了菜单的定义信息,其中包括了图标的配置,这些图片为了方便管理,以及插件需要动态添加菜单图标,我把它放置在了程序目录的相对路径下面,如下所示,动态创建菜单的时候,从指定的路径去获取图标并加载即可。
动态加载菜单是指在插件化应用框架启动,用户登录后进入主界面后,在主界面中动态创建相应的菜单(菜单在权限管理系统中进行配置管理),如下代码所示。
其中是RibbonPageHelper为了方便动态创建菜单而创建的辅助类,部分代码如下所示。
/// &summary&
/// 动态创建RibbonPage和其下面的按钮项目辅助类
/// &/summary&
public class RibbonPageHelper
private RibbonC
public MainForm mainF
public RibbonPageHelper(MainForm mainForm, ref RibbonControl control)
this.mainForm = mainF
this.control =
public void AddPages()
//约定菜单共有3级,第一级为大的类别,第二级为小模块分组,第三级为具体的菜单
List&MenuNodeInfo& menuList = WHC.Security.BLL.BLLFactory&SysMenu&.Instance.GetTree(Portal.gc.SystemType);
if (menuList.Count == 0) return;
int i = 0;
foreach(MenuNodeInfo firstInfo in menuList)
//如果没有菜单的权限,则跳过
if (!Portal.gc.HasFunction(firstInfo.FunctionId)) continue;
//添加页面(一级菜单)
RibbonPage page = new DevExpress.XtraBars.Ribbon.RibbonPage();
page.Text = firstInfo.N
page.Name = firstInfo.ID;
this.control.Pages.Insert(i++, page);
if(firstInfo.Children.Count == 0) continue;
foreach(MenuNodeInfo secondInfo in firstInfo.Children)
//如果没有菜单的权限,则跳过
if (!Portal.gc.HasFunction(secondInfo.FunctionId)) continue;
//添加RibbonPageGroup(二级菜单)
RibbonPageGroup group = new RibbonPageGroup();
group.Text = secondInfo.N
group.Name = secondInfo.ID;
page.Groups.Add(group);
if(secondInfo.Children.Count == 0) continue;
foreach (MenuNodeInfo thirdInfo in secondInfo.Children)
//如果没有菜单的权限,则跳过
if (!Portal.gc.HasFunction(thirdInfo.FunctionId)) continue;
//添加功能按钮(三级菜单)
BarButtonItem button = new BarButtonItem();
button.PaintStyle = BarItemPaintStyle.CaptionG
button.LargeGlyph = LoadIcon(thirdInfo.Icon);
button.Glyph = LoadIcon(thirdInfo.Icon);
button.Name = thirdInfo.ID;
button.Caption = thirdInfo.N
..................
group.ItemLinks.Add(button);
...............
菜单为了方便管理,约定分为3级菜单,三个层级的菜单示意图如下所示。
启动顶部的选项卡级别为第一级,下面的Ribbon分组为第二级,具体的功能菜单(或者按钮)为第三级,以上就是通过菜单数据动态创建的菜单界面图。
3、框架的用户信息和权限控制
基础框架需要传统的登录进行验证,登录成功后,把用户关联的具有的权限下载到本地,然后由系统逻辑统一判断即可。
插件应用框架系统的登录代码和普通的差别不大,登录后把相关信息存储在框架变量中,如下所示。
private void btLogin_Click(object sender, EventArgs e)
.................
string ip = NetworkUtil.GetLocalIP();
string macAddr = HardwareInfoHelper.GetMacAddress();
string loginName = this.cmbzhanhao.Text.Trim();
string identity = WHC.Security.BLL.BLLFactory&WHC.Security.BLL.User&.Instance.VerifyUser(loginName, this.tbPass.Text, Portal.gc.SystemType, ip, macAddr);
if (!string.IsNullOrEmpty(identity))
UserInfo info = WHC.Security.BLL.BLLFactory&WHC.Security.BLL.User&.Instance.GetUserByName(loginName);
if (info != null)
#region 获取用户的功能列表
List&FunctionInfo& list = WHC.Security.BLL.BLLFactory&WHC.Security.BLL.Function&.Instance.GetFunctionsByUser(info.ID, Portal.gc.SystemType);
if (list != null && list.Count & 0)
foreach (FunctionInfo functionInfo in list)
if (!Portal.gc.FunctionDict.ContainsKey(functionInfo.ControlID))
Portal.gc.FunctionDict.Add(functionInfo.ControlID, functionInfo.ControlID);
#endregion
bLogin = true;
Portal.gc.UserInfo =
Portal.gc.LoginUserInfo = ConvertToLoginUser(info);
this.DialogResult = DialogResult.OK;
MessageDxUtil.ShowTips("用户帐号密码不正确");
this.tbPass.Text = ""; //设置密码为空
catch (Exception err)
MessageDxUtil.ShowError(err.Message);
为了使框架记录的权限信息、用户数据、以及系统的一些配置信息能够传递到每个插件应用的窗体中,设计了一个插件应用界面需要实现的接口,放在了BaseUI项目工程中。
namespace WHC.Framework.BaseUI
/// &summary&
/// 父窗体实现的权限控制接口
/// &/summary&
public interface IFunction
/// &summary&
/// 初始化权限控制信息
/// &/summary&
void InitFunction(LoginUserInfo userInfo, Dictionary&string, string& functionDict);
/// &summary&
/// 是否具有访问指定控制ID的权限
/// &/summary&
/// &param name="controlId"&功能控制ID&/param&
/// &returns&&/returns&
bool HasFunction(string controlId);
/// &summary&
/// 登陆用户基础信息
/// &/summary&
LoginUserInfo LoginUserInfo { get; set; }
/// &summary&
/// 登录用户具有的功能字典集合
/// &/summary&
Dictionary&string, string& FunctionDict { get; set; }
/// &summary&
/// 应用程序基础信息
/// &/summary&
AppInfo AppInfo { get; set; }
然后在BaseUI的项目中,界面基类BaseForm实现这个接口。
namespace WHC.Framework.BaseUI
public partial class BaseForm : DevExpress.XtraEditors.XtraForm, IFunction
public BaseForm()
InitializeComponent();
...................
最后,就是我们如何传递用户信息以及权限信息到窗体本身,传递到窗体作为其本身的变量后,就可以很方便使用这些关键的信息了。
在我们动态加载插件应用的后,我们会创建对应的Form对象,然后转换为IFunction接口,赋予该接口相关的变量属性即可实现用户信息及权限信息的传递,如下代码所示。
Form tableForm = (Form)Activator.CreateInstance(formType);
//如果窗体集成了IFunction接口(第一次创建需要设置)
IFunction function = tableForm as IF
if (function != null)
//初始化权限控制信息
function.InitFunction(Portal.gc.LoginUserInfo, Portal.gc.FunctionDict);
//记录程序的相关信息
function.AppInfo = new AppInfo(Portal.gc.AppUnit, Portal.gc.AppName, Portal.gc.AppWholeName, Portal.gc.SystemType);
&4、插件应用的动态加载
上面我们说到,只要是实现基于Form的,我们都可以动态创建方式调用显示插件的界面出来,而如果界面实现了IFucntion的权限控制接口,那么我们就能够传递给它响应的数据,实现更加完善的控制功能。
在第一张关于权限系统的菜单管理图片中,我们看到了有个Winform的窗体类型的字段,里面就是用来动态构造插件的配置信息,我们主要是用来构造插件的窗体,并传递给它相关数据即可,下图是菜单管理里面的 &Winform窗体类型& 信息的具体内容。
但我们完成菜单的动态创建后,菜单按钮的响应事件就是触发动态加载插件的事件。
我们添加菜单的时候,对它的响应事件也做了处理,具体代码如下所示。
//添加功能按钮(三级菜单)
BarButtonItem button = new BarButtonItem();
.................
button.Caption = thirdInfo.N
button.Tag = thirdInfo.WinformT
button.ItemClick += (sender, e) =&
if (button.Tag != null && !string.IsNullOrEmpty(button.Tag.ToString()))
LoadPlugInForm(button.Tag.ToString());
MessageDxUtil.ShowTips(button.Caption);
group.ItemLinks.Add(button);
单击事件的响应处理就是动态构建插件应用的事件,其中就是根据&Winform窗体类型&的数据进行解析的。
string dllFullPath = Path.Combine(Application.StartupPath, filePath);
Assembly tempAssembly = System.Reflection.Assembly.LoadFrom(dllFullPath);
if (tempAssembly != null)
Type objType = tempAssembly.GetType(type);
if (objType != null)
LoadMdiForm(this.mainForm, objType, isShowDialog);
通过动态创建菜单模块,动态加载插件应用,以及权限控制等管理,我们就能隔离框架本身和插件应用模块之间的耦合性关联,所有后续开发或者别人开发的业务模块,都可以很方便的通过权限管理系统配置数据、自动更新模块更新程序应用的方式,把一个高效、易于扩展、动态管理的系统应用弄得丰富多彩,有声有色。
基于插件化应用框架的Winform开发框架改造,使得今后开发业务系统,只是基于一定的接口协议,开发插件应用即可,整体性的框架本身可以有专门的人员进行维护,提高团队对业务模块的横向切割和快速开发的效率,更好、统一、高效完成企业化应用框架的搭建和使用。
下面的图形是之前Winform开发框架的相关功能点集合,加上目前框架的&支持插件化框架应用,能快速开发插件、支持动态扩展&的特点,就显得更加丰富完善了。拒绝访问 | www.jigao616.com | 百度云加速
请打开cookies.
此网站 (www.jigao616.com) 的管理员禁止了您的访问。原因是您的访问包含了非浏览器特征(42d49d0b841f436a-ua98).
重新安装浏览器,或使用别的浏览器动态加载与插件化
  插件化备忘
一、 &概述
  当一个软件项目开发结束并交互使用后,需要添加一些新的功能,我们通常希望在不修改原有的应用程序情况下,将新添加的功能植入到中,这就是所谓的插件化,新增加的功能模块就叫插件。插件化能大大的降低模块间的耦合性,有利于各模块的独立维护,加快项目的维护更新。这里记录了下,主流的集中语言,实现插件化的方法。
二、 &、Net和c/c++ 动态加载方式
  Java的动态加载主要是靠classloader实现的.顾名思义,ClassLoader就是用来Load Class的,当一个Class被加载的时候,这个Class所引用到的所有Class也会被加载,而且这种加载是递归的,也就是说,如果A引用到B,B 引用到C,那么当A被加载的时候,B也会被加载,而B被加载的时候,C也会加载。如此递归直到所有需要的Class都加载好。特别注意ClassLoader双亲委派机制:ClassLoader总是先请求其父ClassLoader查找Class在自身查找Class。具体参考:https://yiminghe.iteye.com/blog/267959
所以可以实现ClassLoader来实现Class的动态加载,不过java已经提供了现成的ClassLoader实现类:URLClassLoader可以加载URL所指定的jar。
有一个plugin.jar里面有一个实现IPlugin接口的类com.test.TestPlugin
ClassLoader loader = new URLClassLoader(new URL[]{new URL(&file://jar路劲\plugin.jar&)});
Class&?& pluginCls = loader.loadClass(&com.test.TestPlugin&);
IPlugin plugin = (IPlugin)pluginCls. newInstance();
plugin.load();
PS:这只是范例代码还有很多注意点,比如jar中不能包含IPlugin接口,确保URLClassLoader的parent中包含IPlugin。如何导出jar,就不叙述了。上诉就是java的动态加载,在这基础上添加插件管理,版本控制(如在zip文件结构中打入插件信息,在android那块会具体说明)。
  Net的代码是以exe与dll的组织形式发布的,而我们常用的插件加载形式为exe和dll。果开发进程级别的插件可以考虑以exe的形式组织插件(java也一样),你可以以创建,终止进程的方式,随意加载,卸载所需插件。不过这种方式要多考虑多进程通信方面的问题,这里不讨论这种形式,主要关注进程内dll形式的插件实现使用的方法。先看下net的一些基本概念,如应用程序域,程序集。
应用程序域:
  应用程序域是一种边界,它由公共语言运行库围绕同一应用程序范围内创建的对象建立(即,从应用程序入口点开始,沿着对象激活的序列的任何位置)。应用程序域有助于将在一个应用程序中创建的对象与在其他应用程序中创建的对象隔离,以使运行时行为可以预知。在一个单独的进程中可以存在多个应用程序域。在net中加载到应用程序域中的dll是不能卸载,我们可以每次创建一个新的应用程序域加载dll,完成逻辑后卸载这个应用程序域。
应用程序集:
  一个.NET应用程序可以由多个程序集拼装而成的。程序集,简单来说,就是一个以公共语言运行库(CLR)为宿主的、版本化的、自描述的二进制文件。尽管显示中.NET程序集和以往Win32二进制文件(包括遗留的COM服务对象)的文件扩展名(*.exe或*.dll)完全相同,但是两者的内部构成几乎完全不同。程序集可以促进代码重用、确定类型边界、可版本化的单元、自描述的、可配置的。
Net主要靠Assembly实现dll的动态加载, Assembly由多个加载dll的static方法,这边只关注加载指定路径下dll的方法LoadFile
有一个plugin.dll 里面有一个实现IPlugin接口的类Test.Plugins.TestPlugin
Assembly assembly = Assembly.LoadFile(&dll的路径\plugin.dll&);
Type type = Assembly.GetType(&Test.Plugins.TestPlugin&);
IPlugin plugin = Activator. CreateInstance(type) as IP
plugin.Load();
PS:如果单纯这样加载的dll实在默认的应用程序域,无法被卸载的,如果需要卸载dll可以新建一个应用程序域去加载,不用时卸载这个应用程序域。
  c/c++就没有标准的动态加载的实现方式了,依平台而定这边记录了下Liunx与windows。程序有两种链接方式,一种是静态链接,一种是动态链接。了解c/c++的动态加载前,先看下这两个概念。
  所谓静态链接就是在编译链接时直接将需要的执行代码拷贝到调用处,优点就是在程序发布的时候就不需要的依赖库,也就是不再需要带着库一块发布,程序可以独立执行,但是体积可能会相对大一些。(所谓库就是一些功能代码经过编译连接后的可执行形式。)
  所谓动态链接就是在编译的时候不直接拷贝可执行代码,而是通过记录一系列符号和参数,在程序运行或加载时将这些信息传递给操作系统,操作系统负责将需要的动态库加载到内存中,然后程序在运行到指定的代码时,去共享执行内存中已经加载的动态库可执行代码,最终达到运行时连接的目的。优点是多个程序可以共享同一段代码,而不需要在磁盘上存储多个拷贝,缺点是由于是运行时加载,可能会影响程序的前期执行性能。
  Windows下是以dll形式的(和net的dll有一定的区别),而liunx则是以so的形式。虽然Windows和Liunx的API有一点差异,但是大体的机制还是一致。都是分加载动态链接库,查找指定符号地址,调用指定过程,完成释放动态链接库。
  下面先来看下windows的,windows提供了多个API来实现动态加载动态链接库和调用里面的方法。
  LoadLibrary 加载指定的动态链接库获得对应的模块句柄(还有其他高级版本)
  GetProcAddress 按照指定的符号名称在加载的动态链接库中查找函数地址
  FreeLibrary 释放加载的动态链接库
  有一个动态链接库plugin.dll,里面有一个插件接口IPlugin、一个插件实现类TestPlugin和一个导出函数int CreatePlugin(IPlugin** outPlugin)
typedef int (*CreatePlugin)(IPlugin** outPlugin);
HMODULE handle = LoadLibrary(L&dll所在目录\plugin.dll&);
if (handle == nullptr)
& & printf_s(&load dll fail&);
& & return -1;
CreatePlugin create = (CreatePlugin)GetProcAddress(handle, &CreatePlugin&);
if (create == nullptr)
& & printf_s(&get proc address fail&);
& & return -1;
IPlugin* plugin =
Int res = create(&plugin);
if (res!=0 || plugin == nullptr)
& & &printf_s(&load plugin fail&);
& & &return -1;
plugin-&Load();
FreeLibrary(handle);
PS:一般dll都是导出函数,所以都是先导出指定函数,作为创建导出类的入口点,后续类的创建都依赖这个入口点。Dll如何实现函数如何导出这里就不说了。
说完windows我们在看下liunx,其实和加载调用过程和windows差不多的,liunx提供了:
dlopen 按指定模式加载指定路径下的动态库。
dlsym按照指定的符号名称在加载的动态链接库中查找函数地址
dlclose 关闭释放动态链接库
和windows一样的情况,有一个动态链接库plugin.so,里面有一个插件接口IPlugin、一个插件实现类TestPlugin和一个导出函数int CreatePlugin(IPlugin** outPlugin)
typedef int (*CreatePlugin)(IPlugin** outPlugin);
void* handle = dlopen(&so所在目录/plugin.so&, RTLD_LAZY);
if (handle ==NULL)
& & &printf(&load dll fail&);
& & &return -1;
CreatePlugin create = (CreatePlugin)dlsym(handle, &CreatePlugin&);
if (create == NULL)
& & &printf(&get proc sym fail&);
& & &return -1;
IPlugin* plugin = NULL;
Int res = create(&plugin);
if (res!=0 || plugin == NULL)
& & printf(&load plugin fail&);
& & return -1;
plugin-&Load();
dlclose(handle);
PS:liunx和windows动态加载动态库的流程还是很相似的,但是由于平台的差异,中间还是有许多细节需要注意的,这边只记录大体的流程,细节后续的在讨论。
三、 & & 与Windows Phone插件化
Android插件化
  看完上面这些,我们再来看看,两个移动平台的情况,首先看下android,由于android的上层开发一般性基于java(那些游戏引擎暂时除外),所以动态加载和java还是基本上一样的,但是由于Dalvik与java的差异,实质还是有一些差距,流程上大致是一致。
  Dalvik虚拟机和java虚拟机的差异致使,无法直接加载class文件,必须将class转化为dex,Dalvik才能去加载。所以在开发插件的步骤上我们要多一步jar到dex的转化。在动态加载的api上,android提供两个加载dex的类DexClassLoader和PathClassLoader。
  DexClassLoader顾名思义就是加载Dex的ClassLoader,它可以加载dex文件或包含dex的jar和apk(dex必须是classes.dex这样命名并在根目录下),但是实际使用时发现有些机型无法加载直接dex形式,所以使用时建议用jar或apk形式。
  PathClassLoader看名字可能认为它就是加载指定路径下的模块,实际上PathClassLoader加载的路径已经被限定死,只能加载/data/app路径下的dex。所以一般情况下,都是使用DexClassLoader实现插件机制。
有一个plugin.jar里面有一个实现IPlugin接口的类com.test.TestPlugin
ClassLoader loader = (IPlugin) new DexClassLoader(&jar所在的目录/plugin.jar&,&dextop目录&,&so的查找目录&,父ClassLoader);
Class&?& pluginCls = loader.loadClass(&com.test.TestPlugin&);
IPlugin plugin = (IPlugin)pluginCls. newInstance();
plugin.load();
  到这里android初步的插件化是可以实现了,可是在使用的时候,会发现这样插件化,无法支持android的res机制,每次实现插件里面的UI只能靠code实现,真心不是一般的烦。那接下我来看下,有什么办法能将res也集成到插件里面。
  首先我们先分析下,无法使用res的原因,android项目在编译的时候会res进行一次编译,对资源进行优化,并为每一个res下的资源都生成一个对应的id。在使用的时候靠这个id在运行时android会靠这个id和资源映射表arsc找到相应资源,所这样造成我们在开发插件时,及时把res放到jar,android也无法按照id找到对应资源。这里的关键是Resource对象,在应用的那个Resource里面是找不到插件内带的那些资源的,所以如果我们能构造一个和插件里面res关联的Resource,那么利用这个Resource(其实还包括Theme)我们就能顺利访问到插件里的资源。
那如何创建插件的Resources呢,这里有两个方法:
  1.直接构造Resources我们可以看下Resources的定义可以发现它有这么一个构造Resources(AssetManager, DisplayMetrics, Configuration)这里可以看出创建Resources所需要的三个参数,先看下后面两个参数这两个参数一个和显示分辨率相关一个是单纯的配置,自然我们可以直接使用app的Resources里的这两个参数,那么AssetManager呢,这个就与资源相关了,显然我们要创建一个与插件的资源相关联的的AssetManager,那我们在看下AssetManager的定义可以发现一个主要的方法addAssetPath(String),看它的实现,这个String应该是对应资源所在的apk路径(即插件位置)这样我们就能构建一个自己的AssetManager了。
  现在三个参数都全了,是不是可以构建出插件的Resources了,拿到这个Resources我们就能访问里面的大部分资源了,对了时大部分资源,不是全部,这个后续会说明的,接下来先讲下下一个方法。
  2.通过某个方法直接加在apk获得这个apk的Context,那样我能就能拿到所有与这个Context相关的资源。这里我们可以观察activity的启动过程,可以发现当某个apk中的第一被加载的时候都会去查找这个组件所在的Application是否已经被创建,如果没有被创建都会,先加载apk创建Application,这里android以Package Name为标识查找Application,查看这部分我们会发现Application是由LoadedApk创建的,而LoadedApk是从ActivityThread的getPackageInfo开头的几个方法里,再仔细浏览的话,发现有个getPackageInfoNoCheck方法,需要获得一个applicationinfo为参数(还有别的参数,这个4以下与以上不同),于似乎我们只要拿到Apk的ApplicationInfo就能创建Application并获得Resources,实际上也很接近了。
  但是是还有些问题要解决,如果我们直接PackageManager().getPackageArchiveInfo获得PackageInfo获得里面的ApplicationInfo,进行上述操作,会坑爹发现报了个某某路劲没有访问权限的错误,这是由于LoadedApk在makeApplication时候,需要加载apk里的dex,这时所指定的dexopt路径是应用是没有权限访问。是不是感觉到这里就坑了,只能默默的回去继续看源码,看着这茫茫的代码晕了一段时间后,会发现LoadedApk在创建加载dex前会判断mClassLoader是不是为null,如果不是就不会去加载dex,现在好了我们只要自己去加载dex,再通过反射去设置mClassLoader这一步就过了。
  再拿到这个Application后,其实我们基本是用它的Resources,而不是直接用这个Application去创建View,由于这个Application是未安装的,如果你用这个Application创建EditText它去访问剪切板服务就GameOver了。
Windows Phone
  过完android,再来看下Windows Phone,由于Windows Phone生态系统的性质,微软为了杜绝未经审核的应用在商店上线,防止恶意的程序对Windows Phone的破坏,动态加载这部分API是被严格限制的。
  先来看下Windows Phone 下的net,实际上和PC上的net一样也是由Assembly的Load方法进行动态加载,不过Windows Phone公开了一个带AssemblyName参数的重载,只能加载应用安装目录和GAC下的DLL流程上和net基本没差。
  再看下Windows Phone下的win32 API,Windows Phone的win32有公开动态加载动态库的API但是也和net一样做了限制LoadLibrary被LoadPackagedLibrary取代,只能加载安装目录下的动态库,而另外两个API与PC的一致。
  到这里可以发现微软已经隐藏了所有可以加载外部代码的API,那就没办法加载外部的代码了吗,如果我们去查看Windows Phone的System32下的dll会发现,其实答案是否定的,还是有办法加载的,虽然说微软只公开的LoadPackagedLibrary,但是LoadLibrary还是存在的,这里我们就看看怎么去访问这个未公开的LoadLibrary。
在这之前我们先看几个和后续操作有关的结构体
//线程环境块
typedef struct _TEB
& & //BYTE fill[0x30]; x86
& & NT_TIB nt_
& & PVOID EnvironmentP
& & CLIENT_ID
& & PVOID ActiveRpcH
& & PVOID ThreadLocalStorageP
& & PEB* currentPEB;
//进程环境块
typedef struct _PEB
// &BYTE fill[0x0c]; x86
& & BYTE Reserved1[2];
& & BYTE BeingD
& & BYTE Reserved2[1];
& & PVOID Reserved3[2];
& & PEB_LDR_DATA*
//进程加载的模块信息
typedef struct _PEB_LDR_DATA
// &BYTE fill[0x1c]; x86
& & ULONG L
& & BOOLEAN I
& & PVOID SsH
& & LIST_ENTRY InLoadOrderModuleL
& & LIST_ENTRY InMemoryOrderModuleL
& & MODULE_LIST_ENTRY* initModuleL
}PEB_LDR_DATA;
//模块链表实体
typedef struct _MODULE_LIST_ENTRY
& & struct &_MODULE_LIST_ENTRY* F
& & struct &_MODULE_LIST_ENTRY* B
& & DWORD* baseA
}MODULE_LIST_ENTRY;
在这几个结构体里面就包含了我要找的LoadLibrary所在的模块句柄,既PEB的ldr这个链表里面。所以只要我们能获得PEB就能在ldr里面找的已经加载的模块,那怎么获得PEB,看TEB我们可以发现TEB里面包含PEB那又变成获得TEB,而获得TEB的API是公开的NtCurrentTeb,有了这个就好办了
HMODULE NativeInterop::getKernelModule()
& TEB* teb = NtCurrentTeb();
& return (HMODULE) teb-&currentPEB-&ldr-&initModuleList-&Flink-&baseA
这样就获取到LoadLibrary的所在模块的句柄了,不过这是默认这个模块加载顺序确定的情况下,所以建议在加载模块名称的判断来确定模块位置。

我要回帖

更多关于 java web 插件式开发 的文章

 

随机推荐