已阅读5页,还剩13页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
基于基于 MFC 的插件系统开发的插件系统开发 解剖 MFC 程序 现代工业的特征之一就是分工 一旦能够分工 就会出现 术业有专攻 的局面 建筑业需要钢材 冶金需要矿石 采矿需要设备 由此带来生机勃勃的 现代文明社会 我们很难想象 建造一座楼房的时候 需要的钢材自己炼 需 要的砖瓦自己烧 需要的电梯自己造 会是一种什么感觉 MFC 编程可能就是 这样 如果 MFC 程序是一座楼 今天的 MFC 程序员必须亲历亲为 需要亲自 盖起楼的主体结构 形成层次 装修房间 完成布线等等一系列工作 因此 我们有必要对现在的 MFC 程序进行一次外科解剖手术 使 MFC 程序的 构造能够实现分工 我们从剖析一个典型的 MFC 多文档程序开始 典型的 MFC 程序 通常由一个 文档框架主窗口 CMainFrame 以及一组 文档 模板构成 程序的形态取决 于 CMainFrame 内容取决于其包含的文档模板 在代码结构上 主窗口 文 档 文档框架窗口 CChildFrame View 等类型对象耦合在一起形成了一 个通常意义下的 MFC 程序 对象耦合的过程 被 MFC 框架巧妙地封装了 因 此 多少年来绝大多数场合下人们看到的是一个近乎 永恒 的代码结构 CMultiDocTemplate pDocTemplate pDocTemplate new CMultiDocTemplate IDR MsdnPluginSamplTYPE1 RUNTIME CLASS CSampleDoc1 RUNTIME CLASS CChildFrame1 RUNTIME CLASS CUserCtrlView if pDocTemplate return FALSE AddDocTemplate pDocTemplate pDocTemplate new CMultiDocTemplate IDR MsdnPluginSamplTYPE2 RUNTIME CLASS CSampleDoc2 RUNTIME CLASS CChildFrame2 RUNTIME CLASS CUserCtrlView if pDocTemplate return FALSE AddDocTemplate pDocTemplate pDocTemplate new CMultiDocTemplate IDR MsdnPluginSamplTYPEn RUNTIME CLASS CSampleDocn RUNTIME CLASS CChildFramen RUNTIME CLASS CUserCtrlView if pDocTemplate return FALSE AddDocTemplate pDocTemplate CMainFrame pMainFrame new CMainFrame if pMainFrame pMainFrame LoadFrame IDR MAINFRAME return FALSE m pMainWnd pMainFrame pMainFrame ShowWindow m nCmdShow pMainFrame UpdateWindow 只要你用过 MFC 你一定接触过上述代码 从中你会感受到一张沧桑的面孔 这段代码贴切地显示出 MFC 框架的呆板 僵化 深入了解了这些代码背后发 生的故事 你会发现解开 MFC 框架臃肿 僵化的玄机就在这里 从上面给出 的代码中可以看出 MFC 程序在初始化阶段通过AddDocTemplate pDocTemplate 填充了一个文档模板队列 完成文档队列填充后的工作就是实例化一个主窗口 然后创建该窗口并显示出来 这个初始化过程许多年来一直在以相同的模式重 复着 就像一个物理规律 几乎时时刻刻地发生在 MFC 的世界里 每增加一 个文档模板 就需要在程序中增加一个文档类 一个文档框架窗口和一个或多 个视图类 然后在程序初始化阶段重新构造一个文档模板类 将其填充到文档 模板队列中 你的程序至少需要增加 3 个类 如果要构造支持 5 个文档类型 的 MFC 程序 得到的程序结构将十分丰满 因为保守估计该程序也得包含 15 个以上的 C 类 对初学者而言 会因此而极大地增强信心 因为他终于写出 很大的 C 程序了 然而对一个大型的综合程序而言却是一个噩梦 一个系统 如果要求 100 个用户视图 20 个文档类型 用 MFC 框架开发就是件十分 恐 怖 的事情 由此我们看到 文档模板队列 是基于文档的 MFC 程序结构臃肿之 症结所在 然而 换个角度看基于文档的 MFC 程序的结构就很简单 无外乎一个文档模 板队列 以及一个支撑文档显示的主框架窗口 许许多多其它对象均属于 亚 层次的二级结构元部件 结构臃肿的症结既然已经暴露 化解的方案也就呼之 欲出了 从程序初始化的过程中可以看到 文档模板队列的填充 是通过调用 AddDocTemplate 完成的 这个调用的参数的数据类型是 CMultiDocTemplate 因此 一个文档模板并非一定是程序内部提供的 也 并非必须在程序初始化过程中填充 认识到这一点极为重要 即使在同一个程 序内部 不同文档模板之间的关联性一般也很弱 这一点表明 文档模板完全 可以从程序结构中剥离 实现文档模板与应用程序结构剥离 意味着实现功能 与程序主体的分离 即 应用的内容可以单独开发 根据需要加载 剥离机制 面临的技术问题是 是否存在一个可接受的途径使得外部构造的模板可以根据 需要插入到一个特定程序的模板队列中 由此可见 调整文档模板队列的填充 机制 是化解 MFC 工程臃肿的良好策略 此外 MFC 程序的主线程窗口的匹 配 也是在初始化过程中通过 CMainFrame pMainFrame new CMainFrame if pMainFrame pMainFrame LoadFrame IDR MAINFRAME return FALSE m pMainWnd pMainFrame 完成的 m pMainWnd是个 CWnd 类型的指针 即 程序的主线程仅仅需要一个 同线程窗口对象的指针 因此 主窗口对象也不一定由程序主体内部提供 主 窗口的创建 同样可以从程序中剥离出来 认识到这一点同样重要 对于一个 基于文档的 MFC 程序来说 主窗口的主要责任只是为一系列文档模板形成的 体系提供可供显示的框框 我们完全没有必要将程序和一个主窗口死死的焊接 在一起 更没必要将一组固定的模板固化在一个特定的程序之中 我们看到 如果实现文档模板从程序中剥离出来 那么全部剥离出来文档模板将形成一个 社会 如果能够将程序的主窗口也剥离出来 那么剥离出来的主窗口将形成 文档社会中的一个个的建筑或组织形态 而这时候 所谓的应用 就是根据特 定的需求组织文档模板形成解决方案的一种策略 此时主窗口是特定场合下组 织模式的体现 而 文档模板 就是组织成员 因此 我们需要挖掘出一种灵活 的文档组织策略 以使得现在的 MFC 程序员摆脱僵化 臃肿的开发模式 构造最小的构造最小的 MFC 程序程序 现在 我们就开始一个精简 MFC 程序的过程吧 我们记如下类型代码段为 a CMultiDocTemplate pDocTemplate pDocTemplate new CMultiDocTemplate IDR MsdnPluginSamplTYPE RUNTIME CLASS CMsdnPluginSampleDoc RUNTIME CLASS CChildFrame RUNTIME CLASS CUserCtrlView if pDocTemplate return FALSE AddDocTemplate pDocTemplate 而将如下类型代码段记为 b CMainFrame pMainFrame new CMainFrame if pMainFrame pMainFrame LoadFrame IDR MAINFRAME return FALSE m pMainWnd pMainFrame pMainFrame ShowWindow m nCmdShow 我们的思路是 首先在常规 MFC 多文档程序中删除上述与主窗口和文档相关 的创建代码 使原生应用程序空无一物 然后 创建新的工程 在其中完成主 窗口和文档的创建工作 最后一步自然是把它们装回去 先进行第一步 我们按照常规的步骤生成一个标准的 MFC 多文档程序 将 a b 对应的代码删除掉 再删除所有与 a b 相关的所有文件 注意 保留应用程序对象对应的文件 并适当调整使这个程序能够正确编译 经过 以上处理 将得到一个最小的 MFC 程序 这个程序中除了一个应用程序对象 和一个 CAboutDlg 之外 什么都没有 因此 运行时什么都不会发生 现在 一个有意思的问题出现了 如何寻找一个合适的途径 使得 a b 中的代码合理地 回归 到我们刚刚得到的最小的 MFC 程序中 我们回顾 一下 a 的目的仅仅是要填充一个队列 如果忽略具体的软件需求 只要是 一个有效的 CMultiDocTemplate 指针 只要这个指针能够顺利地传递到 a 填充就能顺利进行 并且填充过程不具备排斥性 这意味着什么 这意 味着如果能找到合适的办法 你就可以填充任意多数量的文档模板 b 的目的是实例化一个窗口对象 创建 加载并匹配给 MFC 的主线程 但 并不局限于规定的窗口类型 虽然这个过程对每个程序的运行时只有一次 正 像一个人或者一个公司可以根据意愿选择自己的住所或办公地一样 如果找到 合适的办法 一个程序完全可以根据场合匹配适当的主窗口 为了实现 a b 回归到原来的程序 我们需要在原始程序的初始化工程中嵌入两个 回调 机制 一个用于加载 a 另一个用于加载 b 现在 可以形成策略了 我们需要两类 对象 一类用来解决 a 另一类用 来解决 b 同时 我们还希望这两类对象能够独立于程序存在 需要的时 候能够在程序的合适位置创建即可 从纯粹 MFC 的角度解决所提到的两类对 象是完全可能的 但这样会带来很大的局限 也十分可惜 因此 结合现代的 软件技术 我们应该从更广泛的意义上考虑这个问题 由此挖掘出 MFC 框架 与现在主流软件技术的强有力的结合点 我们着重考虑基于 COM NET Java 等框架考虑实现这两种对象的可能性 本文中 我们工作 的基础是 NET 动态加载原理动态加载原理 动态加载 是个应用很广泛的程序技巧 COM NET Java 均存在软件动态 加载的机制 对任何一个 NET 程序 CLR 提供两种途径检索可动态加载的对 象 一个途径是全局的 这类对象必须在全局对象缓冲区中注册 另一个途 径是局部的 通过程序的配置文件指定 NET 框架为每个程序提供一个配置文件 这个文件的名字是 程序名 exe config 这个文件实际上就是个标准的 XML 文件 一个典型的配置文件 如下 CLR 通过中指 定的信息发现当前程序的局部组件路径 可以给 privatePath 重新赋值以指定 新的局部组件检索路径 不同的路径用 分隔 也就是说 除了全局对象缓冲 区中注册的对象外 只有程序所在目录以及子目录 bin usercontrol component 中的动态链接库中包含的局部 NET 对象才 能够被该程序加载 具体的加载实现如下 Assembly m pDotNetAppAssembly Assembly Load S LibName Type m pDotNetAppType m pDotNetAppAssembly GetType S ObjectID true true MethodInfo mi m pDotNetAppType GetMethod S SomeMethod Object m pDotNetAppServer Activator CreateInstance m pDotNetAppType String p new String 1 p 0 S SomStrParam mi Invoke m pDotNetAppServer p 上述代码给出了动态加载包含于 LibName dll 中名称为 ObjectID 的 NET 对象 的一般方法 并显示了如何调用这个动态对象中的一个方法 SomeMethod 其中关于 Invoke 的更详细的信息 请参考 NET FrameWork SDK 技术文档 插件的一般理论插件的一般理论 插件 通常是指具有相同 虚 行为规范的一类动态可加载对象 插件事实上是 一种委托行为 当程序需要一种可以预见 粗略 轮廓而无法确定具体功能的行 为或对象描述时 通常将类似行为委托给一个后期介入的对象进行解析 此时 就有了插件 随着软件技术的不断进步 人们已经不再满足于开发仅有有限静 态功能的软件 一个更合理的软件构思是 使得软件在一定的意义下可以与来 自用户的新的需求对接 这一点客观上要求软件主体必须有一个可 暴露 的对 象模型 同时这个模型应该能够接受来自用户的新的软件拓展对象 而且这个 模型具有一个虚拟的拓展规范 我们以 MFC 程序为例对此进行简要的说明 如果希望一个基于文档的 MFC MDI 程序支持虚拟拓展 首先必须对拓展的目的进行清晰的描述 其次是给拓 展行为制定规范 假设计划该 MDI 程序支持一组新的可加载文档类型 那么这 组新的文档应该具备什么样的 虚 的行为规范 所谓 虚 的行为规范 本质上 相当于一个 C 类对象中的一组虚函数 或者相当于一个虚接口中的一组方法 当一个对象重载了对应的 C 类或重新实现了给定的虚接口 这个对象就有可 能被另一个系统接纳 如果我们计划为给定的 MDI 程序实现一组新的可加载文 档 并且希望这类文档加载后 能够与原程序无缝地集成并在指令上能够与原 模型实现对接 例如 主窗口可以显示一组菜单 菜单中能够显示一些常规的 指令与新文档进行交互等等 那么我们必须在主程序中虚设这些指令的执行 例如 用一个 虚 指针调用一个指令 只要确保运行时指针是有效的 而 在真正的运行时产生新的对象与虚的交互进行对接 即将一个虚的指针有效化 为实现这个目的 主程序必须维护一个加载对象的字典 或与之相当的机制 使得程序主体能够判断出新加载对象的属性 这样就能够正确地定位虚的行为 发生的时间以便将其对应到一个确实的对象之中 具体地说 假设一组扩展行为的名称为 Action1 Action2 Action3 那么 我们需要一个类 或一个 COM Java 接口等等 class SomePlugInObj Overrides virtual void Action1 0 virtual void Action2 0 virtual void Action3 0 我们希望每个新加载的文档能够包含一个 SomePlugInObj 对象的指针 根据 需要 这个指针指向的对象可以是 SomePlugInObj 的派生对象 这一点可以 确保扩展行为的多态性 运行时程序可以通过加载的文档得到这个指针 并在 适当的时刻 例如选择菜单 关闭文档等等 以公共的方式触发 SomePlugInObj 的关联行为 如果是这样的话 新的文档就自然地被主系统 接纳了 现在的问题是 我们必须在给定的程序之外实现 SomePlugInObj 及其宿主文 档 根据动态加载原理 我们可以实现一个基于 NET 的组件库 在这个库中 创建一个 NET 对象 使得该对象中维护一个 SomePlugInObj 对象的实例 然后根据需要实现运行时加载该 NET 对象 使得需要的 SomePlugInObj 能 够正确传递到原生程序 一般来说 你可以在新的组件库中派生 SomePlugInObj 重载其中的全部方法 这样用户的定制化功能就可以自然 地导入到原生程序之中 采用 NET 技术的优势在于 你可以将 SomePlugInObj 的方法进一步 虚化 到一个 NET 对象中 例如 如果基于 NET 的组件库为 SomeLib dll 我们可以在其中派生新的类 class DotNetSomePlugInObj public SomePlugInObj gcroot m pDotNetObj virtual void Action1 pDotNetObj Action1 virtual void Action2 pDotNetObj Action2 virtual void Action3 pDotNetObj Action3 此时 我们可以构造一个 SomeLib dll 中的 NET 对象 SomeLibObject 其 中包含三个可重载的虚方法 Action1 Action2 Action3 那么我们就 实现了 SomePlugInObj 对象的 NET 虚化 进一步 用户就可以基于其它 NET 语言 VB NET C 等等 利用 NET 框架的重载机制 在其它 NET 支 持的语言中以 SomeLib SomeLibObject 为基类派生新的 NET 对象 提 供不同版本的 m pDotNetObj 从而为原生软件带来更加广泛 灵活的扩展 途径 检索 管理可加载组件是支持插件的至关重要的环节 为此我们必须对主程序 构造提出必要的先决条件 也就是说 程序主体必须具备接纳机制 通常程序 主体必须明确地具备检索可加载对象的能力 这是程序具备可拓展性的最起 码的要求 COM 机制通常利用 COM 组件的范畴 Category 机制实现组件 接口的枚举 检索 NET 框架还可以提供更灵活的检索机制 由于 NET 程序 的组件是可配置的 即我们可以在程序所属的目录中建立子目录用来存储可加 载对象的包容库 以此方式可以灵活地进行组件分配 部署 因此 NET 非 常适合实现可加载插件的检索机制 值得考虑的方案是通过 NET 程序的配置 文件指定插件对象的包容组件库存储的目录 进而制定插件的管理方法 此时 如果插件包容库已经进入指定的目录 则其中的组件就可以被原始程序自然地 检索了 描述基于 动态插入目的 开发的组件的常规做法是用 XML 文件技术 这一做法的好处是使得描述局部化了 避开了复杂的 注册表 而且容易实现 我们可以为每个组件提供一个相关的 XML 文件 其中描述组件所属的组件库 程序集 对象名以及其它相关信息 然后将全部这类的 XML 文件存储在同一个 目录中 只要能够合理的检索 枚举这些同目录下的 XML 文件 就可以开发出 灵活 强大的插件管理器 一个典型的插件管理器的实现可以参考我们提供的 样板程序 根据这个样板 你完全可以开发自己的插件管理器 一旦明确了插件的工作原理 那么插件的运行时管理 调度就是一个关键的课 题了 事实上 主程序系统就是一个插件的调度 管理枢纽中心 程序对象模型 如何虚 实结合是个艺术性的问题 如果你希望一个有价值的软件行为有延续 的必要 或局部调整的可能 再或者打算在此留下一些 头绪 以便其他人续写 此时一个巧妙的做法是调用一个 虚函数 或触发一个事件 因此 支持插件的 代价也就暴露了 程序中会因插件而出现许多虚的实现和虚的调用 程序的危 险因素增加了 健壮 稳定就成了程序成败的关键因素 解决上述问题的办法 是 使程序主体能够识别所有虚拟行为的描述 无论有多么 虚 都不能失去 规范 因此 维护一个动态虚拟对象的字典是绝对必要的 只有这样 你才能 在一个运行时刻确保正确地解析虚拟对象的虚拟行为 并将这些 虚 的指令行 为委托给一个 实 的对象 这就是虚实结合 构造第一个可动态加载的主窗口插件构造第一个可动态加载的主窗口插件 我们现在的目的是 将主窗口的匹配过程实现为一种插件机制 为此 我们需 要建立一个支持托管扩展的 MFC 动态链接库工程 在这个工程中 添加一个 名为 AppObject 的托管 C 对象 在其中添加一个名为 ConnectMainFrame 的方法 根据动态加载原理 可以在最小 MFC 程序的 初始化过程中实现对该对象的动态加载 注意 你的最小 MFC 程序必须修改 使其支持托管扩展 对象的动态加载实现的位置是前述的 b 对应的代码位 置 第一步是在 ConnectMainFrame 中写些简单的代码 然后看看程序运行 的效果 例如 写一个最简单的实现过程 voidAppObject ConnectMainFrame AfxMessageBox T Hello MFC 如果你成功地完成了上述步骤 你就已经走完了正确的一步并且可以进行下一 步了 现在 在创建的动态链接库中添加一个 CMainFrame 对象 一般的方 法是 建立一个辅助的 MFC 工程 然后将其中有关 CMainFrame 的代码以及 相关的资源文件全部添加到你的动态链接库工程之中 经过适当的调整 使得 这个工程能够被正确地编译 这是个比较可行的手工方法 但相对繁琐且容易 出错 一个行之有效的办法是实现一个集成在 Visual Studio IDE 中的 Wizard 自动的完成上述工作 相关的 Wizard 在附赠的光盘中 在 ConnectMainFrame 方法实现中 我们可以将代码 b 嵌入到其中 这样 最小的 MFC 程序就成功地与一个 CMainFrame 对接了 void AppObject ConnectMainFrame CWinApp pApp AfxGetApp AfxSetResourceHandle theApp m hInstance CMainFrame pMainFrame new CMainFrame theApp m pAppObj this if pMainFrame pMainFrame LoadFrame IDR MAINFRAME return pMainFrame ShowWindow pApp m nCmdShow pMainFrame UpdateWindow 我们可以看到 主窗口插件的目的就是将一个无窗口的程序通过一个实体表现 出来 使得运行时具有程序交互的基础 构造可动态加载的文档模型构造可动态加载的文档模型 与实现主窗口插件的原理一样 我们还可以构造另一类基于 NET 的插件对象 用于加载文档模板 一个包含文档模板的 NET 组件库与主窗口一样 也是一 个基于 MFC 框架的动态链接库 手工建立这个库的过程与主窗口工程的建立 类似 第一 需要一个支持托管扩展的 MFC 动态链接库工程 第二 需要一 个辅助的基于文档的 MFC 程序工程 第三 将辅助工程中与文档相关的代码 全部添加到动态链接库工程中 这个过程同样可以实现为一个集成的 Wizard 在光盘中提供 实现文档模板动态插入的代码如下 void ActiveDocTemplate String strExtImpl if g pDotNetExtImpl CWinApp pApp AfxGetApp AfxSetResourceHandle theApp m hInstance CMultiDocTemplate pDocTemplate pDocTemplate new CMultiDocTemplate IDR DocumentXDocTemplate3TYPE RUNTIME CLASS CDocumentXDocTemplate3Doc custom MDI child frame RUNTIME CLASS CChildFrame RUNTIME CLASS CDocumentXDocTemplate3View if pDocTemplate return CString strFileInfo strFileExt pDocTemplate GetDocString strFileInfo CDocTemplate filterName pDocTemplate GetDocString strFileExt CDocTemplate filterExt g pDotNetExtImpl m DocInfoDictionary strFileInfo strFileExt g pDotNetExtImpl m pCurDocTemplate pDocTemplate pApp AddDocTemplate pDocTemplate 从上述代码中可以看出 文档模板插件的目的就是实现一个从外部构造的模板 自然地插入到应用程序对象的文档模板队列以使得程序支持新的文档类型 构造基于插件的应用系统构造基于插件的应用系统 回到最小的 MFC 程序构造环节 我们看到 我们并不是要刻意挖空一个程序 的内容 这样做的目的是寻找一个解决 MFC 机制臃肿的方案 结果 我们得 到的是一些碎片 现在我们找到理由了 根据插件系统的一般原理 我们看到 了这些碎片重新合成的可能性 有了一个最小的 MFC 程序 一类主窗口插件 一类文档模板插件 问题就很清晰了 那就是 如何将这些东西拼装起来 以 形成一个真正的程序 现在 让我们将剥离后的碎片重新合成 以形成一个系统吧 首先 我们需要 一个具有组织 加载能力的应用程序 这样一个完整的例子可以参考我们提供 的案例代码 彻底的方案是将上述机制形成一系列 Wizard 并与 Visual Studio 集成 为此我们提供了所有这些相关的工作 在随机光盘中提供 一 旦有了程序 合成工作的基础就建立了 第二步 我们需要一个配置文件以便指定特定应用需要的主窗口插件以及文档 模板队列的检索位置 为此 一个最好的做法就是直接利用 NET 程序的默认 配置文件 对 NET 程序而言 配置文件相当于一个局部的注册表 不仅可以 指定组件的局部检索路径 而且可以指定其他的定制化信息 为了合成工作的 需要 在配置文件中增加如下结点 经过上述调整后 程序的配置文件事实上为合成工作描述了一个合成方案 一 个程序可以有任意多个描述方案 当面对不同需求的时候 用户可以形成不同 的方案 由于每个 NET 程序可以存储在不同的本地目录中 因此 你可以为 每个本地目录匹配一个不同的合成方案以此实现应用系统的多态性 第三步 系统通过MainAssemblyLib testmainframe 指定主窗口插件所在的 NET 程 序集名称 MainFrameAssembly testmainframe AppObject 指定加载主窗口的 NET 对 象的 ID 值 这个方法与 JavaBean 采用的技术十分相似 给软件配置带来了 十分灵活的机制 由于主窗口的实现已经与程序分离了 因此 开发人员可以 根据需要提供多个主窗口 理论上可以实现任意多个主窗口 甚至直接使用 已存在的主窗口对象 而且 由于实现主窗口的动态链接库是基于 NET 框架 的 因此 你甚至可以用其他 NET 语言重载相关的对象 例如 如果负责加 载主窗口的 NET 对象中存在有针对性的 虚函数 那么其他开发者甚至用户就 可以重载这个对象 以实现进一步的程序定制化 一般来说 我们可以将主窗 口与对应负责加载主窗口的 NET 对象匹配在一起形成一个对偶对 就可以实 现 通过相应的 NET 对象驱动主窗口 这一驱动模型 一旦运行时
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- XX建筑工程有限公司安全科长岗位职责
- 铁路安全议题探讨讲解
- 游戏行业职业发展规划书
- 韩国社工就业前景解析
- 2026年中国糖塑工艺师考试冲刺模拟
- 2026年证券从业资格模拟题库
- 医院健康宣教工作定义
- 校园消防安全培训内容
- (正式版)DB41∕T 2352-2022 《在用公用管道绘图及信息标识》
- 医院医保工作会议制度
- 机关单位工会内控制度
- 2026年腾讯市场营销岗位面试题及解析
- 病理学练习题库
- 塑料注射成型多段射胶技术资料
- 丰田汽车钣金培训大纲
- LNG充装标准操作流程详解
- 上海民办上宝中学七年级下册期末生物期末试卷(带答案)-全册
- 2025年青海省西宁市城区中考英语试卷附答案
- 2023-2025年中考语文试题分类汇编:病句辨识(原卷版)
- 2025年低压电工理论考试试题1000题
- 欠薪法律宣传课件
评论
0/150
提交评论