CAB 默认模块装载器中的Bug

        前段时间在弄CAB (Composite UI Application Block), 按着书上写的方法写了一个模块workitem extension 扩展类。当被扩展的workitem的run方法被调用的是时候,扩展类所重写的方法老是没有被调用。书上说可以我测试却不可以,搞得那天好心烦。设断点调试,明明看到workitemExtensionService 里面注册了我的那个extension。 extension 里面的OnRunStarted就是没有被调用到,我崩溃了。 后来没有办法去看CAB的源码,检查了一个晚上终于发现了一个天大的秘密, 这是一个BUG!!!!!!!!
        我们来看一下CAB的默认moduleloader :
        private void InnerLoad(WorkItem workItem, IModuleInfo[] modules)
        
{
            
if (modules.Length == 0)
                
return;

            LoadAssemblies(modules);
            List
<ModuleMetadata> loadOrder = GetLoadOrder();

            
foreach (ModuleMetadata module in loadOrder)
                module.LoadServices(workItem);

            
foreach (ModuleMetadata module in loadOrder)
                module.InitializeModuleClasses(workItem);

            
foreach (ModuleMetadata module in loadOrder)
                module.InitializeWorkItemExtensions(workItem);

            
foreach (ModuleMetadata module in loadOrder)
                module.NotifyOfLoadedModule(OnModuleLoaded);
        }
       
        模块装载的时候Load方法会先检查一下传进来的参数有没有null值这些问题, 然后转而调用到上面的这个 InnerLoad方法来装载模块。这个方法依次的 装载服务、 初始化模块、 初始化workitem extension、 然后触发模块装载完毕事件....

        起初我怀疑我的workitemextension 挂钩到workitem里面的时机晚了, 果然不出我所料。 我们看一下 ModuleMetaData 类的 InitializeModuleClasses 方法
            public void InitializeModuleClasses(WorkItem workItem)
            
{
                
if (modulesInitialzed)
                    
return;

                modulesInitialzed 
= true;
                EnsureModuleClassesExist(workItem);

                
try
                
{
                    
foreach (IModule module in moduleClasses)
                    
{
                        module.Load();

                        
if (traceSource != null)
                            traceSource.TraceInformation(Properties.Resources.ModuleStartCalled, module.GetType());
                    }

                }

                
catch (FileNotFoundException ex) { ThrowModuleReferenceException(ex); }
                
catch (Exception ex) { ThrowModuleLoadException(ex); }
            }
       而模块的InitializeModuleClasses 方法竟然调用了 IModule.Load() 方法!! 也就是说如果我在所 重写的 ModuleInit.Load() 方法里面开始我的逻辑的话, 此时workitemextension 还没有被初始化,还没有挂钩到所扩展的workitem里面!!!     而CAB附带的几个quick start 却恰恰在重写的ModuleInit.Load()方法里面开始逻辑,也就是说在模块装载还没有完全完成的时候就已经开始逻辑了!! 严重误导,严重浪费了我的时间......

       看来要自己找方法解决了。那个NotifyOfLoadedModule 是最后的步骤, 这个步骤会触发 ModuleLoader.Loaded 事件。 这才是真正的装载完毕! 于是我改为在 ModuleLoaded 的loaded 事件发生的时候开始我写的逻辑。
       贴一下我写的代码:
    public class PressureExplorerModuleInit : ModuleInit
    
{
        
private WorkItem shellRootWorkItem;

        
// the root work item of the shell will be given by constructor injection
        [InjectionConstructor]
        
public PressureExplorerModuleInit(
            [ServiceDependency] 
            WorkItem shellRootWorkItem)
        
{
            
this.shellRootWorkItem = shellRootWorkItem;
            shellRootWorkItem.Services.Get
<IModuleLoaderService>().ModuleLoaded += new EventHandler<Microsoft.Practices.CompositeUI.Utility.DataEventArgs<LoadedModuleInfo>>(PressureExplorerModuleInit_ModuleLoaded);
        }


        
private void PressureExplorerModuleInit_ModuleLoaded(object sender, Microsoft.Practices.CompositeUI.Utility.DataEventArgs<LoadedModuleInfo> e)
        
{
            
if (e.Data.Assembly == System.Reflection.Assembly.GetExecutingAssembly())
                Loaded();
        }


        
private void Loaded()
        
{
            
// start the peWorkItem after the module is fully loaded by calling run method
            PEWorkItem peWorkItem = shellRootWorkItem.WorkItems.AddNew<PEWorkItem>("PEWorkItem");
            peWorkItem.Run();
        }

    }

       至此还没有结束,虽然那我的问题解决了, 但是我还是想到网上别人有什么更好的解决方法。 然后我找到了一篇文章有关新的 new module loader for CAB, 此模块装载器修正了我所说的这个bug, 还提供了一些很有用的新功能:

Towards the end of the Smart Client Software Factory project, we wanted to be able to support a more comprehensive module loader system for CAB applications. Our goals were:

    1. Support grouping modules together into logical "sections" which represented groups of functionality. These groups might be things like "layout", "services", "applications", etc.
    2. Allow dependencies to be expressed between these sections (i.e., make sure you load all the layout modules before you load the application modules).
    3. Allow dependencies to be expressed in an external place instead of forcing them to be expressed in attributes on the assembly.
    4. Make it easier to provide transports for the profile catalog XML without having to write an entirely new enumerator.
    5. If possible, preserve backward compatibility with the new loader and old enumerators, so that we could preserve effort people had putting into writing their own enumerators.
    6. Fix a bug in the module loader that causes WorkItem extensions to be registered too late to see WorkItems that were being created in the module's Load method.


    1.  支持把模块按功能分组, 每一组模块(模组section)  代表一系列有关的功能 。 比如 “排版” 模组,“服务” 模组, “应用” 模组等等。
    2.  允许表达 section 之间的关系依赖性(耦合), 比如你可以确定 排版section 会在 应用section 装载完毕之后再装载。
    3.  允许在外部定义 关系依赖性,突破原先的只能在assembly attribute里面定义的局限。
    4.  不需要写整个 模块枚举器来实现 profile catalog XML 的传输。
    5.  向后兼容, 可以使用之前写好的 模块枚举器。
    6.  修正了我上面所描述的那个bug, 这个bug 使得 WorkItem extension 注册晚了 ( 注册聆听workitem的RunStarted, Initialized等事件晚了 )。

posted @ 2008-06-16 14:34  Yang Ching  阅读(1621)  评论(6编辑  收藏