具体实现步骤如下:
一、开发web用户控件
这一步和以前的开发没有区别。
1、首先新建一个web应用程序(需要VS2005 sp1支持)
2、然后在里面开发几个web用户控件
3、在ascx文件上右键-〉属性-〉生成操作选择嵌入的资源
4、生成dll就可以了(dll的名字为:Test.Control.dll,后面会用到)
二、开发一个虚拟文件系统提供类
这一步是最重要的一步。
具体思路就是:在系统中注册这个类,然后在每访问一个文件/资源的时候会自动调用这个类,在这个类中判断文件的路径是否是我们定义的,如果是就用我们的逻辑来处理,即从dll中取出资源。
首先把类的代码贴出来,我想可能许多人应该和我一样,喜欢直接先看代码:)

 DllVirtualPathProvider
DllVirtualPathProvider public class DllVirtualPathProvider : System.Web.Hosting.VirtualPathProvider
    public class DllVirtualPathProvider : System.Web.Hosting.VirtualPathProvider
 
     {
{ public DllVirtualPathProvider()
        public DllVirtualPathProvider()
 
         {
{ }
        }
 public override string CombineVirtualPaths(string basePath, string relativePath)
        public override string CombineVirtualPaths(string basePath, string relativePath)
 
         {
{ if (IsAppResourcePath(basePath))
            if (IsAppResourcePath(basePath))
 
             {
{ return null;
                return null; }
            }
 return Previous.CombineVirtualPaths(basePath, relativePath);
            return Previous.CombineVirtualPaths(basePath, relativePath); }
        }
 public override System.Runtime.Remoting.ObjRef CreateObjRef(Type requestedType)
        public override System.Runtime.Remoting.ObjRef CreateObjRef(Type requestedType)
 
         {
{ return Previous.CreateObjRef(requestedType);
            return Previous.CreateObjRef(requestedType); }
        }
 public override bool DirectoryExists(string virtualDir)
        public override bool DirectoryExists(string virtualDir)
 
         {
{ if (IsAppResourcePath(virtualDir))
            if (IsAppResourcePath(virtualDir))
 
             {
{ return true;
                return true; }
            } else
            else
 
             {
{ return Previous.DirectoryExists(virtualDir);
                return Previous.DirectoryExists(virtualDir); }
            }
 }
        }
 public override string GetCacheKey(string virtualPath)
        public override string GetCacheKey(string virtualPath)
 
         {
{ if (IsAppResourcePath(virtualPath))
            if (IsAppResourcePath(virtualPath))
 
             {
{ return null;
                return null; }
            } else
            else
 
             {
{ return Previous.GetCacheKey(virtualPath);
                return Previous.GetCacheKey(virtualPath); }
            } }
        }
 public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies)
        public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies)
 
         {
{ if (IsAppResourcePath(virtualPath))
            if (IsAppResourcePath(virtualPath))
 
             {
{ return null;
                return null; }
            } else
            else
 
             {
{ return Previous.GetFileHash(virtualPath, virtualPathDependencies);
                return Previous.GetFileHash(virtualPath, virtualPathDependencies); }
            } }
        }
 private bool IsAppResourcePath(string virtualPath)
        private bool IsAppResourcePath(string virtualPath)
 
         {
{ String checkPath = VirtualPathUtility.ToAppRelative(virtualPath);
            String checkPath = VirtualPathUtility.ToAppRelative(virtualPath); return checkPath.StartsWith("~/MyUserControl/Test.Control.dll/", StringComparison.InvariantCultureIgnoreCase);
            return checkPath.StartsWith("~/MyUserControl/Test.Control.dll/", StringComparison.InvariantCultureIgnoreCase); }
        }
 public override bool FileExists(string virtualPath)
        public override bool FileExists(string virtualPath)
 
         {
{ return (IsAppResourcePath(virtualPath) || Previous.FileExists(virtualPath));
            return (IsAppResourcePath(virtualPath) || Previous.FileExists(virtualPath)); }
        }
 public override VirtualFile GetFile(string virtualPath)
        public override VirtualFile GetFile(string virtualPath)
 
         {
{ if (IsAppResourcePath(virtualPath))
            if (IsAppResourcePath(virtualPath))
 
             {
{ return new AssemblyResourceVirtualFile(virtualPath);
                return new AssemblyResourceVirtualFile(virtualPath); }
            } else
            else
 
             {
{ return Previous.GetFile(virtualPath);
                return Previous.GetFile(virtualPath); }
            } }
        }
 public override System.Web.Caching.CacheDependency GetCacheDependency(string virtualPath,
        public override System.Web.Caching.CacheDependency GetCacheDependency(string virtualPath, System.Collections.IEnumerable virtualPathDependencies, DateTime utcStart)
               System.Collections.IEnumerable virtualPathDependencies, DateTime utcStart)
 
         {
{ if (IsAppResourcePath(virtualPath))
            if (IsAppResourcePath(virtualPath))
 
             {
{ string path = HttpRuntime.AppDomainAppPath + virtualPath.Substring(1);
                string path = HttpRuntime.AppDomainAppPath + virtualPath.Substring(1);
 return new System.Web.Caching.CacheDependency(path);
                return new System.Web.Caching.CacheDependency(path); }
            } else
            else
 
             {
{ return Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
                return Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart); }
            } }
        } }
    }1、必须从VirtualPathProvider类继承
2、IsAppResourcePath方法是用来判断是否为我们定义的路径格式:~/MyUserControl/Test.Control.dll/,下面调用的时候就使用这个路径
3、注意GetCacheKey方法:
 public override string GetCacheKey(string virtualPath)
        public override string GetCacheKey(string virtualPath)
 
         {
{ if (IsAppResourcePath(virtualPath))
            if (IsAppResourcePath(virtualPath))
 
             {
{ return null;
                return null; }
            } else
            else
 
             {
{ return Previous.GetCacheKey(virtualPath);
                return Previous.GetCacheKey(virtualPath); }
            } }
        }另外所有的方法当不符合我们的条件时一定要调用 Previous.**** 因为系统中可能有多个虚拟文件提供程序的。
4、GetCacheDependency方法:
 if (IsAppResourcePath(virtualPath))
if (IsAppResourcePath(virtualPath))
 
             {
{ string path = HttpRuntime.AppDomainAppPath + virtualPath.Substring(1);
                string path = HttpRuntime.AppDomainAppPath + virtualPath.Substring(1);
 return new System.Web.Caching.CacheDependency(path);
                return new System.Web.Caching.CacheDependency(path); }
            }5、在GetFile方法中我们返回的是一个AssemblyResourceVirtualFile类:
 class AssemblyResourceVirtualFile : VirtualFile
    class AssemblyResourceVirtualFile : VirtualFile
 
     {
{ string path;
        string path; public AssemblyResourceVirtualFile(string virtualPath)
        public AssemblyResourceVirtualFile(string virtualPath) : base(virtualPath)
            : base(virtualPath)
 
         {
{ path = VirtualPathUtility.ToAppRelative(virtualPath);
            path = VirtualPathUtility.ToAppRelative(virtualPath); }
        }
 public override System.IO.Stream Open()
        public override System.IO.Stream Open()
 
         {
{ string[] parts = path.Split('/');
            string[] parts = path.Split('/'); string assemblyName = parts[2];
            string assemblyName = parts[2]; string resourceName = parts[3];
            string resourceName = parts[3]; assemblyName = Path.Combine(HttpRuntime.BinDirectory, assemblyName);
            assemblyName = Path.Combine(HttpRuntime.BinDirectory, assemblyName); System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFrom(assemblyName);
            System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFrom(assemblyName); if (assembly != null)
            if (assembly != null)
 
             {
{ return assembly.GetManifestResourceStream(resourceName);
                return assembly.GetManifestResourceStream(resourceName); }
            } return null;
            return null; }
        } }
    }三、注册这个虚拟文件提供程序
这一个很简单,在global.asax中注册:
 protected void Application_Start(object sender, EventArgs e)
        protected void Application_Start(object sender, EventArgs e)
 
         {
{ System.Web.Hosting.HostingEnvironment.RegisterVirtualPathProvider(new DllVirtualPathProvider());
            System.Web.Hosting.HostingEnvironment.RegisterVirtualPathProvider(new DllVirtualPathProvider()); }
        }四、调用dll中的用户控件
 Control control1 = this.LoadControl("/MyUserControl/Test.Control.dll/Test.Control.Sample.List.ascx");
                Control control1 = this.LoadControl("/MyUserControl/Test.Control.dll/Test.Control.Sample.List.ascx"); Control control2 = this.LoadControl("/MyUserControl/Test.Control.dll/Test.Control.Sample.Sample.ascx");
                Control control2 = this.LoadControl("/MyUserControl/Test.Control.dll/Test.Control.Sample.Sample.ascx");
 form1.Controls.Add(control1);
                form1.Controls.Add(control1); form1.Controls.Add(control2);
                form1.Controls.Add(control2);后记:
首先感谢 Leepy 同志认真踏实的精神,找出了我这篇文章中的一个BUG。
原来我的第四步:调用dll中的用户控件,是一个web应用程序,如果是一个web站点的话就会出现问题。因为在VS2005中调试时这两种方式呈现出来的Url是不一样的:
web应用程序:http://localhost:****/Default.aspx
web站点 :http://localhost:****/WebSite1/Default.aspx
也就是说我原来的程序没有考虑到非根目录部署的情况,针对这个BUG要修改的地方有(为了BUG的原始记录,我就不在原文中修改了,把修改点列在下面):
1、GetCacheDependency方法:

 GetCacheDependency
GetCacheDependency public override System.Web.Caching.CacheDependency GetCacheDependency(string virtualPath,
    public override System.Web.Caching.CacheDependency GetCacheDependency(string virtualPath, System.Collections.IEnumerable virtualPathDependencies, DateTime utcStart)
           System.Collections.IEnumerable virtualPathDependencies, DateTime utcStart)
 
     {
{ if (IsAppResourcePath(virtualPath))
        if (IsAppResourcePath(virtualPath))
 
         {
{ string path = HttpRuntime.BinDirectory;
            string path = HttpRuntime.BinDirectory;
 return new System.Web.Caching.CacheDependency(path);
            return new System.Web.Caching.CacheDependency(path); }
        } else
        else
 
         {
{ return Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
            return Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart); }
        } }
    }2、在调用的地方修改成以下方式:
 Control control1 = this.LoadControl("~/MyUserControl/MyWebApplication.dll/MyWebApplication.Sample1.ascx");
        Control control1 = this.LoadControl("~/MyUserControl/MyWebApplication.dll/MyWebApplication.Sample1.ascx"); Control control2 = this.LoadControl("~/MyUserControl/MyWebApplication.dll/MyWebApplication.Sample2.ascx");
        Control control2 = this.LoadControl("~/MyUserControl/MyWebApplication.dll/MyWebApplication.Sample2.ascx");
 form1.Controls.Add(control1);
        form1.Controls.Add(control1); form1.Controls.Add(control2);
        form1.Controls.Add(control2);