Add your own alternative version
Stats
98.8K views 3.3K downloads 45 bookmarked
Posted
18 Oct 2008
|
Comments and Discussions
|
|
In the main web application, I keep getting the ascx failed to load.
|
|
|
|
|
I have created a Web User Control library using your tool. Here's the issue that I'm having: I have 4 user controls - Head, Header, Footer, and Foot. I use these to build Master Pages in multiple projects which have near-identical HTML. However, here's what I'm getting when I include these user controls in the order Head - Header - Footer - Foot
All of Head
(where Header should be) A portion of Head mashed together with a portion of Header
All of Footer
All of Foot
When I remove Head from the page, I get the following:
All of Header
(where Footer should be) A portion of Head mashed together with a portion of Footer
(where Foot should be) A portion of Foot
All of the UCs except Header are straight HTML and simple .NET controls with no code-behind (Header has two OnClick events). Something strange is going on with the OnRender methods of these UCs but I'm not sure what. Please let me know if you have any insight.
Thanks.
|
|
|
|
|
Is there allready a solution for the public member problem?
I was starting to make base classes for all my controls but the job is just too much work.
I recompiled the WebLibraryMaker to framework 4.0 with succes
I also tried to compile it while referencing the latest ilmerge 2.12.803.0 but that completely locks the program for some reason.
Im still looking for a transparent way to make libraries for web user controls.
Or is there another solution?
thanks guys!
|
|
|
|
|
Hi Alexey
I downloaded the source example and I succeed doing the process with your example.
Unfortunately, when it comes to do it from scracth I have a problem.
Here it is : I can't see my aspComponent that I put in aspx files, like if it doesn't initiate correctly, But when I add a control in code behind in the page_load, the control appears.
I really spend all my day to fix this trying to compare my project and yours and I can't reach the solution. I noticed that when I add a control from your project it succeed but not in my project. Can you tell more about the class ControlsLoader and FastActivator, I am sure this is related to my problem.
Thanxs for your reply.
|
|
|
|
|
I guess you have a namespace problem.
if u add the prefix in the page or web.conf make sure u mention the whole namespace containing the control.
|
|
|
|
|
Please can you let me know if this issue got resolved?
"•You can see "Could not load type [strange type name] from assembly [assembly name]" error in runtime. It occures if you try to use members of an user control outside of the control. I.e. you can't create public properties/methods within your user controls directly. It is problem of the IlMerge tool. I can't fix it myself but i will try to notify ILMerge author."
Thank you.
|
|
|
|
|
As soon as I switch the framework to Framework 4 on the control project I get the error below.
Do you know how to resolve this?
thanks
Greg
Error 1 The command ""c:\Users\Greg\Documents\Visual Studio 2010\Projects\WebLibraryMakerExample\SignedLibrary\..\WebLibraryMaker\WebLibraryMaker.exe" /net " c:\Windows\Microsoft.NET\Framework\v4.0.30319 " /name " SignedLibrary " /prj " c:\Users\Greg\Documents\Visual Studio 2010\Projects\WebLibraryMakerExample\SignedLibrary " /obj " obj\Debug\ " /out " bin\ " /debug true /key " key.snk "" exited with code 1. SignedLibrary
|
|
|
|
|
I have recompiled web maker as framework 4.0 and all is well.
|
|
|
|
|
|
Ok I have this fixed. Not easy to do. Basically I had to modify the final dll by updating the offset for CreateResourceBasedLiteralControl based on how many resources got added and in the correct order. This worked. I will post the code on Monday.
|
|
|
|
|
I've tried to do merge unmabnaged resourced with the same way as aspnet_merge utility does it. I didn't notice that aspnet_merge changes the offset, but, seems, it was my error. It would be great to see the correct code.
|
|
|
|
|
Been working on this for awhile and this is the best I could come up with as of yet. Originally tried to do this using mono.cecil but they have some issues with generating a valid pdb in 3.5 right now. Basically I stored the offsets as we grab them from the original dlls and modified the offsets in the final dll then resaved. This might be the long term solution but like I said cecil has some issues right now. So here is my 2nd attempt.
As we load resources calculate the offsets. Stored them off into a second byte array list. Pass that into the interfacebuilder and gen up a special class that looks like the code below marked #1 Example. Then have a base class that you inherit all your usercontrols from that call this method inside of AddParsedSubObject if it is either a ResourceBasedLiteralControl or a HtmlGenericControl. Using reflection we call into the method created in example #1 and viola the method updates the ResourceBasedLiteralControls private var _offset with the correct calculated value see #2 Example below. Again this would be simpler if we could modify the dll directly but no luck on that front yet. Example #3 contains the code in interfacebuilder to gen the method in #1. If u want I can give u the actual modified files. Just let me know where to put em. Also if you can think of a better way to do this let me know.
#1 Example
public class ResourceBasedLiteralUpdater
{
// Methods
public static void UpdateResourceBasedLiteralControl_Offset(UserControl userControl, object obj)
{
int num = 0;
FieldInfo field = obj.GetType().GetField("_offset", BindingFlags.NonPublic | BindingFlags.Instance);
if (userControl is search_search_controller_ascx)
{
num = 0;
num += (int) field.GetValue(obj);
field.SetValue(obj, num);
}
if (userControl is details_details_controller_ascx)
{
num = 0x1e9;
num += (int) field.GetValue(obj);
field.SetValue(obj, num);
}
if (userControl is results_results_controller_ascx)
{
num = 0x385;
num += (int) field.GetValue(obj);
field.SetValue(obj, num);
}
}
}
#2 Example (inside a base class of all implemented usercontrols)
protected override void AddParsedSubObject(object obj)
{
if ( (obj.ToString().Contains("ResourceBasedLiteralControl")) || (obj is System.Web.UI.HtmlControls.HtmlGenericControl) )
{
CallUpdate(obj);
}
base.AddParsedSubObject(obj);
}
private void CallUpdate(object resourceLiteral)
{
if (resourceLiteral is System.Web.UI.HtmlControls.HtmlGenericControl)
{
object newResourceControl = null;
//need to find the real literal control inside of this guy.
System.Web.UI.HtmlControls.HtmlGenericControl control = resourceLiteral as System.Web.UI.HtmlControls.HtmlGenericControl;
if (control != null)
{
foreach (Control ctrl in control.Controls)
{
if (ctrl.ToString().Contains("ResourceBasedLiteralControl"))
{
newResourceControl = ctrl;
break;
}
}
}
if (newResourceControl != null)
resourceLiteral = newResourceControl;
else
return;
}
Assembly ass = this.GetType().Assembly;
Type ctrlType = ass.GetType(ass.GetName().Name + ".ResourceBasedLiteralUpdater");
if (ctrlType != null)
{
MethodInfo methodInfo = ctrlType.GetMethod("UpdateResourceBasedLiteralControl_Offset");
if (methodInfo != null)
{
object[] param = new object[2];
param[0] = this;
param[1] = resourceLiteral;
object instance = methodInfo.Invoke(this, param);
}
}
}
#3 Example
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.IO;
namespace WebLibraryMaker
{
class InterfaceBuilder
{
public static string Build(Pathes pathes, string[] assemblyPathes, List<byte[]> byteArray)
{
List<Assembly> assemblies = new List<Assembly>();
foreach (string assemblyPath in assemblyPathes)
{
assemblies.Add(Assembly.LoadFrom(assemblyPath));
}
Assembly mainAssembly = assemblies[0];
string mainName = mainAssembly.GetName().Name;
CompilerParameters compilerParameters = new CompilerParameters();
Dictionary<string, string> providerOptions = new Dictionary<string, string>();
providerOptions.Add("CompilerVersion", "v3.5");
CSharpCodeProvider CSharpProvider = new CSharpCodeProvider(providerOptions);
//Create a new Compiler parameter object.
compilerParameters.GenerateExecutable = false;
compilerParameters.GenerateInMemory = false;
compilerParameters.OutputAssembly = pathes.CompiledFileBillet + ".Interface.dll";
//reference to System.dll is resuired in any case
compilerParameters.ReferencedAssemblies.Add("System.dll");
compilerParameters.ReferencedAssemblies.Add("System.Core.dll");
//reference to each App_Web_*.dll assembly
foreach (string assemblyPath in assemblyPathes)
{
compilerParameters.ReferencedAssemblies.Add(assemblyPath);
}
//reference to each file used by the initial library
foreach (AssemblyName assemblyName in mainAssembly.GetReferencedAssemblies())
{
Assembly assembly;
try
{
//standard assembly or GAC assembly
assembly = Assembly.Load(assemblyName);
}
catch
{
//custom assembly
assembly = Assembly.LoadFrom(assemblyName.Name + ".DLL");
}
if (String.Compare(Path.GetFileNameWithoutExtension(assembly.Location), "system", true) != 0)
compilerParameters.ReferencedAssemblies.Add(assembly.Location);
}
StringBuilder code = new StringBuilder();
foreach (Assembly assembly in assemblies)
{
foreach (Type type in assembly.GetTypes())
{
if (type.Namespace == "ASP" && !type.BaseType.Namespace.StartsWith("System."))
{
//create child class for each ASP.XX one
code.AppendLine("namespace " + type.BaseType.Namespace + " {");
code.AppendLine("public class " + type.BaseType.Name);
code.AppendLine(": " + type.Name);
code.AppendLine("{");
code.AppendLine("}");
code.AppendLine("}");
}
}
}
//Create static method that we can use to update the offsets.
if (byteArray.Count > 0)
{
//create extension method
//create child class for each ASP.XX one
code.AppendLine("namespace " + pathes.ProjectName + " {");
code.AppendLine("public class ResourceBasedLiteralUpdater");
code.AppendLine("{");
code.AppendLine("public static void UpdateResourceBasedLiteralControl_Offset( System.Web.UI.UserControl userControl, object obj)");
code.AppendLine("{");
code.AppendLine(@"
int iOffSet = 0;
System.Reflection.FieldInfo field = obj.GetType().GetField(""_offset"", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
");
int iCnt = 0;
int offSet=0;
foreach (Assembly assembly in assemblies)
{
if (byteArray[iCnt] != null)
{
foreach (Type type in assembly.GetTypes())
{
if (type.Namespace == "ASP" && !type.BaseType.Namespace.StartsWith("System."))
{
code.AppendLine(@"
if ( userControl is " + type.Name + @" )
{
iOffSet = " + offSet + @";
iOffSet += (int)field.GetValue(obj);
field.SetValue(obj, iOffSet);
}");
}
}
}
if (byteArray[iCnt] != null)
offSet += byteArray[iCnt].Length;
iCnt++;
}
code.AppendLine("}");
code.AppendLine("}");
code.AppendLine("}");
}
if (code.Length != 0)
{
code.Insert(0, "using ASP;\r\n");
}
//compile the code
CompilerResults compilerResult = CSharpProvider.CompileAssemblyFromSource(compilerParameters, code.ToString());
if (compilerResult.Errors.Count > 0)
{
Console.WriteLine("Interface library creation error(s):");
foreach (CompilerError error in compilerResult.Errors)
{
Console.WriteLine(error.ErrorText);
}
return null;
}
return compilerParameters.OutputAssembly;
}
}
}
|
|
|
|
|
Here is the code from program.cs and resources.cs as well.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
namespace WebLibraryMaker
{
public static class Resources
{
const UInt32 RESOURCE_ID = 101;
const UInt32 RESOURCE_TYPE = 3771;
const UInt32 LOAD_LIBRARY_AS_DATAFILE = 2;
[DllImport("KERNEL32.DLL", EntryPoint = "BeginUpdateResourceW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public extern static IntPtr BeginUpdateResource(string pFileName, bool bDeleteExistingResources);
[DllImport("KERNEL32.DLL", EntryPoint = "UpdateResourceW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public extern static bool UpdateResource(IntPtr hUpdate, UInt32 pType, UInt32 pName, UInt16 wLanguage, byte[] pData, UInt32 cbData);
[DllImport("KERNEL32.DLL", EntryPoint = "UpdateResourceW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public extern static bool UpdateResource(IntPtr hUpdate, string pType, UInt32 pName, UInt16 wLanguage, byte[] pData, UInt32 cbData);
[DllImport("KERNEL32.DLL", EntryPoint = "EndUpdateResourceW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public extern static bool EndUpdateResource(IntPtr hUpdate, bool bDiscard);
[DllImport("KERNEL32.DLL", EntryPoint = "LoadLibraryExW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public extern static IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, UInt32 dwFlags);
[DllImport("KERNEL32.DLL", EntryPoint = "FreeLibrary", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public extern static bool FreeLibrary(IntPtr hLib);
[DllImport("KERNEL32.DLL", EntryPoint = "FindResourceW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public extern static IntPtr FindResource(IntPtr hModule, UInt32 lpID, UInt32 lpType);
[DllImport("KERNEL32.DLL", EntryPoint = "LoadResource", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public extern static IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo);
[DllImport("KERNEL32.DLL", EntryPoint = "SizeofResource", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public extern static UInt32 SizeofResource(IntPtr hModule, IntPtr hResInfo);
public static List<byte[]> LoadResources(string[] assemblies)
{
List<byte[]> byteArray = new List<byte[]>();
List<byte> bytes = new List<byte>();
foreach (string assembly in assemblies)
{
//int currentOffset = 0;
IntPtr module = LoadLibraryEx(assembly, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE);
IntPtr resource = FindResource(module, RESOURCE_ID, RESOURCE_TYPE);
if (resource != IntPtr.Zero)
{
UInt32 size = SizeofResource(module, resource);
IntPtr resourcePointer = LoadResource(module, resource);
byte[] temporaryBytes = new byte[Convert.ToInt32(size)];
byteArray.Add(temporaryBytes);
Marshal.Copy(resourcePointer, temporaryBytes, 0, Convert.ToInt32(size));
bytes.AddRange(temporaryBytes);
}
else {
byteArray.Add(null);
}
FreeLibrary(module);
}
int cnt = 0;
foreach (byte[] myBytes in byteArray)
{
if (myBytes != null)
{
FileStream stream = new FileStream("c:\\temp\\data.da" + cnt.ToString(), FileMode.Create, FileAccess.ReadWrite);
stream.Write(myBytes, 0, myBytes.Length);
cnt++;
stream.Close();
}
}
return byteArray;
}
public static void InsertResources(string path, byte[] content)
{
IntPtr resource = BeginUpdateResource(path, false);
if (resource != IntPtr.Zero)
{
bool success = UpdateResource(resource, RESOURCE_TYPE, RESOURCE_ID, Convert.ToUInt16(1049), content, Convert.ToUInt32(content.Length));
EndUpdateResource(resource, !success);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security.Cryptography;
using System.Diagnostics;
using System.Reflection;
using WebLibraryMaker.Properties;
namespace WebLibraryMaker
{
class Program
{
/// <summary>
/// The tool converts given WebApplication to user controls library.
/// It does following steps
/// 1) depending of hash of the Dll: backup unchanged Dll OR restore it before processing (in order to prevent double processing of the same file)
/// 2) call aspnet_compiler
/// 3) gather names of newly created Dlls
/// 4) create "interface" Dll containing classes inherited from ASP.XX ones. These classes have names similiar to grandparents' ones.
/// 5) load special unmanaged resources from Dlls created by aspnet_compiler
/// 6) merge all these Dlls into one file (interface Dll + Dlls created by aspnet_compiler + initial Dll) using ILMerge tool
/// 7) write special unmanaged resource into resulting Dll
/// 8) overwrite Dll files within output and intermediate folders
/// 9) calculate hash code of the Dll
/// </summary>
/// <param name="args">
/// the appliaction supports following parameters:
/// /net [installation directory of .NET 2.0]
/// /name [name of the assembly]
/// /prj [full path of the project directory]
/// /obj [relative path of the intermediate directory]
/// /out [relative path of the output directory]
/// /debug [true|false]
/// /key [name of key file]
/// /ds [true|false] (= delaySign)
/// all parameters are required
/// you can use something like "$(MSBuildProjectDirectory)\..\WebLibraryMaker\WebLibraryMaker.exe" /net " $(Framework20Dir) " /name " $(MSBuildProjectName) " /prj " $(MSBuildProjectDirectory) " /obj " $(IntermediateOutputPath) " /out " $(OutDir) " /debug $(DebugSymbols)
/// </param>
/// <returns>
/// 0 in case of successful processing
/// error code in case of error
/// </returns>
static int Main(string[] args)
{
try
{
//there are a lot of different pathes to process
//I've stored them into a nice container
Pathes pathes = new Pathes();
bool debug;
bool delaySign;
// first of all - to parse command line parameters
ArgumentParser.ParseArgs(args, pathes, out debug, out delaySign);
// compare hashes; create/restore backups
Backup.CheckBackup(pathes);
//precompile the application
int compileResult = AspnetCompiler.PrecompileWebApplication(pathes, debug);
if (compileResult != 0) return compileResult;
//gather newly created Dll files
List<string> aspCompiledPathes = new List<string>(Directory.GetFiles(pathes.CompiledDirectory, "App_Web_*.dll"));
if (aspCompiledPathes.Count == 0) return 0;
//insert initial Dll into the collection
aspCompiledPathes.Insert(0, pathes.CompiledFile);
List<byte[]> byteArray = Resources.LoadResources(aspCompiledPathes.ToArray());
//build interface library
string interfaceAsm = InterfaceBuilder.Build(pathes, aspCompiledPathes.ToArray(), byteArray);
if (interfaceAsm != null)
{
//load special unmamaged resources
//for (int i = 0; i < aspCompiledPathes.Count; i++)
//{
// aspCompiledPathes[i] = aspCompiledPathes[i] + ".mod";
//}
byte[] resource = null;
if (byteArray.Count > 0)
{
List<byte> bytes = new List<byte>();
foreach (byte[] myBytes in byteArray)
{
if (myBytes != null)
{
bytes.AddRange(myBytes);
}
}
resource = bytes.ToArray();
}
aspCompiledPathes.Insert(0, interfaceAsm);
//merge all Dlls
Merge.DoMerge(pathes.OutputDll, pathes.CompiledFile, aspCompiledPathes.ToArray(), pathes.KeyFileBillet, delaySign);
//insert special unmanaged resource into resulting Dll
if (resource != null) Resources.InsertResources(pathes.OutputDll, resource);
//UpdateResourceOffSet.Update(pathes.OutputDll, byteArray);
//File.Delete(pathes.OutputDll);
//File.Delete(pathes.OutputDll.Replace(".dll", ".pdb"));
//Console.WriteLine("Writing out dll : " + pathes.OutputDll);
//Console.WriteLine("Original : " + pathes.OutputDll + ".tmp");
//File.Copy(pathes.OutputDll + ".tmp", pathes.OutputDll);
//Console.WriteLine("Writing out pdb : " + pathes.OutputDll.Replace(".dll", ".pdb"));
//Console.WriteLine("Original : " + pathes.OutputDll + ".pdb");
//File.Copy(pathes.OutputDll + ".pdb", pathes.OutputDll.Replace(".dll", ".pdb"));
Console.WriteLine("Creating Backup");
//calculate hash code, copy result into intermediate Dll
Backup.CreateBackup(pathes);
}
else
{
throw new Exception("No interface library created");
}
}
catch (Exception ex)
{
CoStar.Core.Common.EventLogWriter.LogError(ex.Message, ex);
Console.Write(ex.ToString());
return 1;
}
return 0;
}
}
}
|
|
|
|
|
I created a solution with an empty class library project, then added a UserControl with codebehind.
I added the postbuild commandline and everything worked as expected.
I then tried an existing solution which also contains a class library project with some usercontrols, but with a dozen or so references to custom framework dlls.
This time, WebLibraryMaker gave me the following exception:
System.ArgumentOutOfRangeException: Requested range extends past the end of the array.
at System.Runtime.InteropServices.Marshal.CopyToManaged(IntPtr source, Object destination, Int32 startIndex, Int32 length)
at WebLibraryMaker.Resources.LoadResources(String[] assemblies)
at WebLibraryMaker.Program.Main(String[] args)
The library builds fine otherwise, if I build it with aspnet_compiler.exe I get no exceptions.
Does anyone know what the problem is and how I can get it to work?
|
|
|
|
|
The project was written in VB initially. This error occures because of VB -> C# conversion. I will fix it soon. Thank you for your message.
|
|
|
|
|
Hi Zuber. It’s me, Zauberer. I was asking you some time ago for a trouble that I see now is solved with the workaround of a base class for the public properties and methods. Now I am having the same problem that this post refers. I don’t understand what you mean with VB to C # because my project is fully written in C# as the WebLibraryMaker so that where the VB part fits? Thank you in advance.
|
|
|
|
|
I've fixed this error in the last release. Just download new version.
This project was written in VB initially. Then I converted it to C#, and the problem occured because of different arrays definition syntax in VB and C#.
|
|
|
|
|
Could not load type 'LibraryB.LibraryB2108.ControlC' from assembly 'LibraryB, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Hit this if we added the following code to ControlC.ascx.cs
private string _testing = "Default";
public string Testing
{
get { return _testing; }
set { _testing = value; }
}
and then try to access it via code behind in the Default.aspx onload (or anywhere).
protected void Page_Load(object sender, EventArgs e)
{
ControlC.Testing = "test";
}
You can access anything that is NOT custom with no problems ie Controls.ID. You can even set it but nothing that has been defined in the code behind of the ascx. This happens regardless of if the ascx is loaded dynamically or via markup.
|
|
|
|
|
Ok got it to work IF you add the following to the ascx markup
ClassName="LibraryB.Test.ControlC"
so it would look like this
<![CDATA[<%@ Control Language="C#" ClassName="LibraryB.Test.ControlC" AutoEventWireup="true" CodeBehind="ControlC.ascx.cs" Inherits="LibraryB.ControlC" %>]]>
However now you must refer to ControlC as LibraryB.Test.ControlC not LibraryB.ControlC (which is still available but will not load correctly).
ControlC.Testing = "test"; will now work however there is an issue.
INTELLISENSE no longer works on ControlC and it does not recognize the made up Test namespace. It will compile however so this might be ok.
|
|
|
|
|
It is really significally issue. I'm working to fix it.
|
|
|
|
|
I've found that the problem occures because of a bug of ILMerge tool. I can't fix it myself. But you can use following workaround: Create a base class for your user control and place all public members into the base class. See LibraryB.ControlC for the example.
|
|
|
|
|
|
i have to mention the strong name( key value ) in assembly. for example:
[assembly:AssemblyKeyFileAttribute(@"sgKey.snk")]
Can not be compiled
|
|
|
|
|
Strong names are not supported for now. I will research opportunities to produce this feature.
|
|
|
|
|
You can use regular functionality of VS to sign your library (project properties -> signing) in version 0.6. Don't forget to include /key " $(AssemblyOriginatorKeyFile) " parameter in the post-build command. See Signed project of the code example also.
|
|
|
|
|
|
General News Suggestion Question Bug Answer Joke Praise Rant Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.
|
|