It appears our application has an assembly leak. I noticed that on any calls where a web service call is invoked using the HttpWebRequest object a dynamic assembly is loaded on the call httpWebRequest.GetResponse()
I can see the assembly get loaded through the debugger ('w3wp.exe' (Managed): Loaded '7-6jav6v', No symbols loaded.) but I cannot figure out why this would occur.
Has anyone else experienced this before?
Edit:
To add clarifications to this question.
In c# when you create an XmlSerializer an assembly is created to complete the serialization. This always will occur unless you use a tool to do this for you in advance. If you use the constructor of (Type type) or (Type type, string "namespace") then only 1 assembly will be generated. If you use any other constructor then a new assembly will be generated for each serialization.
THis is not the case in the problem stated above.
There is a block of code in our codebase that manually makes a soap call and returns a string (the string is xml, ex: ). Each time this block of code executes a new assembly gets created. When examining one of these assemblies this is referenced "XmlSerializationWriter1.Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReader1.XmlSerializer1.ArrayOfObjectSerializer.ArrayOfObjectSerializer1.ArrayOfObjectSerializer2"
For a better understanding - the code block looks like below and when the last line executes the assembly gets generated...multiple assemblies, one for each time this block runs.
HttpWebRequest oHttpWebRequest =(HttpWebRequest)WebRequest.Create("URL TO WEBSERVICE");
oHttpWebRequest.Timeout =((1000*60)*30);
oHttpWebRequest.Method ="POST" ;
oHttpWebRequest.ContentType ="text/xml" ;
oHttpWebRequest.Headers.Add("SOAPAction: http://www.tempuri.com/"+WebMethodName);
StreamWriter oStreamWriter = new StreamWriter(oHttpWebRequest.GetRequestStream()) ;
string SoapRequest=#"<soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/""><soap:Body>";
SoapRequest=SoapRequest + HttpUtility.HtmlDecode(XmlHttpRequestData);
SoapRequest=SoapRequest + #"</soap:Body></soap:Envelope>";
oStreamWriter.Write(SoapRequest);
oStreamWriter.Close();
oHttpWebRequest.ProtocolVersion.Build;
WebResponse oWebResponse = oHttpWebRequest.GetResponse() ;
According to your comment below Sky Sanders' answer, the generated assemblies are for XML serialization. Serialization assemblies are dynamically generated, unless you pre-generate them using the XML Serializer Generator Tool (Sgen.exe). If you do that, the existing assemblies will be used and no assembly will be generated
Is the schema of the xml for the web services you call fixed, or dynamic? If you are calling arbitrary web services that each take arbitrary XML messages as input and return arbitrary XML messages as output...then the XmlSerializer is going to create a new assembly for each schema. If each message essentially uses the same schema, but varies enough in structure, even though they could use a common schema, the XmlSerializer is only so capable...its going to generate a assembly to handle each specific schema it identifies.
Like Thomas said, if your schema is fixed, use the XML Serializer Generator Tool to pre-generate your serialization assemblies.
Related
I am using dnLib to generate MSIL assemblies dynamically from a custom language I'm writing, named CSASM:
string absolute = Path.Combine(Directory.GetCurrentDirectory(), forceOutput ?? $"{asmName}.exe");
ModuleDefUser mod = new ModuleDefUser(asmName, Guid.NewGuid(), new AssemblyRefUser(new AssemblyNameInfo(typeof(int).Assembly.GetName().FullName))){
Kind = ModuleKind.Console,
RuntimeVersion = "v4.0.30319" //Same runtime version as "CSASM.Core.dll"
};
var asm = new AssemblyDefUser($"CSASM_program_{asmName}", new Version(version));
asm.Modules.Add(mod);
// Adding attribute code omitted for brevity
//Adds types to the module and constructs methods and method bodies for those types based on the CSASM source file in question
Construct(mod, source);
mod.Write(absolute);
The generation of the executable is working as intended.
However, when trying to run this executable, the TypeLoadException below is thrown:
System.TypeLoadException: Could not load type 'CSASM.Core.IntPrimitive' from assembly
'CSASM_program_Example, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' due to
value type mismatch.
at Example.Program.csasm_main()
CSASM_program_Example being the name of assembly for the generated executable, Example.exe.
The type IntPrimitive is actually found in the CSASM.Core.dll assembly, which is also in the same folder as the generated executable.
Due to the extreme lack of documentation surrounding dnLib, I'm essentially stumbling around in the dark here.
In short, is there a reason why the type is trying to be loaded from the wrong assembly?
If so, is there a way that I can remedy this?
Viewing the assembly in dnSpy shows the TypeRefs and MemberRefs referencing correct assemblies, which makes this predicament even more frustrating.
After a very thorough examination of a dnLib DLL, the problem was due to me using Importer.ImportDeclaringType(Type).ToTypeDefOrRef(), which caused value-type TypeSigs to be registered as class-type TypeSigs instead.
Even though the docs say to use Importer.ImportDeclaringType(Type) over Importer.ImportAsTypeSig(Type) for method and field declarations, you really shouldn't be using it.
Is there a way to find out the assembly name at design-time (i.e. not using reflection or runtime APIs such as System.Reflection.Assembly.GetEntryAssembly) from within Visual Studio?
The scenario requires a tool to get the assembly name that a Visual Studio project will eventually compile into.
This is like parsing the AssemblyName property of the .csproj - I am wondering if there are any APIs that can give this information reliably.
Please do not respond back with runtime APIs that use reflection - there is no assembly file present at the time I need the assembly name - just the metadata of the assembly in the csproj file.
if you are calling the tool via a post/pre-build event, this data is very easy to access.
Just go to the "project properties->Build Events" tab, then select either "edit pre-build" or "edit post-build", depending on when you want the tool to run. This should bring up an edit window with the ever helpful "Macros >>" button. Press this and you will be given a heap of macros to use and should be pretty much everything you need.
The "API" you could use is LINQ to XML after all the .csproj file is just xml. (and you can get the location of the .csproj file if you need from the solution file which for some reason is not XML but can be easily parsed)
You can use "TargetName" available in Macros for Post-build events. It will give you the assembly name for your project.
After a quick run through MSDN I found this article which might be a good start for some further research:
Accessing Project Type Specific Project, Project Item, and Configuration Properties
I think you will need to write some regular expression that will give you the value of "AssemblyTitle" attribute in AssemblyInfo.cs file.
Something like this:
public class Assembly
{
public static string GetTitle (string fileFullName) {
var contents = File.ReadAllText (fileFullName); //may raise exception if file doesn't exist
//regex string is: AssemblyTitle\x20*\(\x20*"(?<Title>.*)"\x20*\)
//loading from settings because it is annoying to type it in editor
var reg = new Regex (Settings.Default.Expression);
var match = reg.Match (contents);
var titleGroup = match.Groups["Title"];
return (match.Success && titleGroup.Success) ? titleGroup.Value : String.Empty;
}
}
I am using ILMerge and Quartz.NET in a C# .NET 4.0 Windows Service application. The app runs fine without using ILMerge, but now that we're nearing shipping release, I wanted to combine all DLLs into a single executable.
Problem is, that ILMerge seems to work fine, but when I run the combined executable, it throws this exception:
Unhandled Exception: Quartz.SchedulerException: ThreadPool type 'Quartz.Simpl.SimpleThreadPool' could not be instantiated. ---> System.InvalidCastException: Unable to cast object of type 'Quartz.Simpl.SimpleThreadPool' to type 'Quartz.Spi.IThreadPool'.
at Quartz.Util.ObjectUtils.InstantiateType[T](Type type) in :line 0
at Quartz.Impl.StdSchedulerFactory.Instantiate() in :line 0
--- End of inner exception stack trace ---
at Quartz.Impl.StdSchedulerFactory.Instantiate() in :line 0
at Quartz.Impl.StdSchedulerFactory.GetScheduler() in :line 0
Does anyone have any idea why this is? I have been wasting over 4 hours already and I can't figure it out. If I don't combine with ILMerge, then everything runs fine (with the Quartz.dll and Common.Logging.dll in the same directory).
I'm sure someone must have tried packaging Quartz.net up like this before, any ideas?
Disclaimer: I don't know Quartz.NET at all, although I spent some time struggling with ILMerge. When I finally understood its limitations... I stopped using it.
ILMerge'd application tends to have problems with everything which contains the word "reflection".
I can guess (I've never used Quartz.NET) that some classes are resolved using reflection and driven by configuration files.
Class is not only identified by its name (with namespace) but also by assembly it is coming from (unfortunatelly it doesn't get displayed in exception message).
So, let's assume you had (before ILMerging) two assemblies A (for you Application) and Q (for Quartz.NET).
Assembly 'A' was referencing assembly 'Q' and was using a class 'Q:QClass' which was implementing 'Q:QIntf'.
After merging, those classes became 'A:QClass' and 'A:QIntf' (they were moved from assembly Q to A) and all the references in code has been replaced to use those (completely) new classes/interfaces, so "A:QClass" is implementing "A:QIntf" now.
But, it did not change any config files/embedded strings which may still reference "Q:QClass".
So when application is reading those not-updated config files it still loads "Q:QClass" (why it CAN find it is a different question, maybe you left assembly 'Q' in current folder or maybe it is in GAC - see 1).
Anyway, "Q:QClass" DOES NOT implement "A:QIntf", it still implements "Q:QIntf" even if they are binary identical - so you can't cast 'Q:QClass' to 'A:QIntf'.
The not-ideal-but-working solution is to "embed" assemblies instead of "merging" them. I wrote a open-source tool which does it (embedding instead of merging) but it is not related to this question. So if you decide to embed just ask me.
You can test it by removing (hiding, whatever works for you) every single instance of Q.dll on your PC. If I'm right, the exception should say now 'FileNotFound'.
You could try creating your own ISchedulerFactory and avoid using reflection to load all of your types.
The StdSchedulerFactory uses this code to creat a threadpool. It's where your error is happening and would be the place to start looking at making changes:
Type tpType = loadHelper.LoadType(cfg.GetStringProperty(PropertyThreadPoolType)) ?? typeof(SimpleThreadPool);
try
{
tp = ObjectUtils.InstantiateType<IThreadPool>(tpType);
}
catch (Exception e)
{
initException = new SchedulerException("ThreadPool type '{0}' could not be instantiated.".FormatInvariant(tpType), e);
throw initException;
}
The ObjectUtils.InstantiateType method that is called is this one, and the last line is the one throwing your exception:
public static T InstantiateType<T>(Type type)
{
if (type == null)
{
throw new ArgumentNullException("type", "Cannot instantiate null");
}
ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes);
if (ci == null)
{
throw new ArgumentException("Cannot instantiate type which has no empty constructor", type.Name);
}
return (T) ci.Invoke(new object[0]);
}
Right after this section in the factory, datasources are loaded using the same pattern and then the jobs themselves are also loaded dynamically which means you'd also have to write your own JobFactory. Since Quartz.Net loads a bunch of bits and pieces dynamically at runtime going down this road means you might end up rewriting a fair amount of things.
I am currently saving my .NET FX 4.0.1 StateMachine activity like this:
var sb = new StringBuilder();
var xamlWriter = ActivityXamlServices.CreateBuilderWriter(
new XamlXmlWriter(new StringWriter(sb),
new XamlSchemaContext()));
XamlServices.Save(xamlWriter, activityBuilder);
return sb.ToString();
This works fine and the generated XAML looks good. Unfortunately, it is invalid. I can read it back in using ActivityXamlServices.Load but when I execute it, it says that it doesn't know the properties defined in the workflow. Opening it in the Visual Studio designer yields the same errors:
Compiler error(s) encountered processing expression "ActiveCall". "ActiveCall" is not declared. It may be inaccessible due to its protection level.
Through comparing the original XAML with the XAML produced by my code, I found out how to fix this problem. I have to have this tag before the StateMachine tag:
<mva:VisualBasic.Settings>
Assembly references and imported namespaces for internal implementation
</mva:VisualBasic.Settings>
By the way:
The text inside the tag must be exactly like this, otherwise there will be an error when opening the WF in VS:
Failed to create a 'Settings' from the text 'FooBar'
Question:
What do I have to change in my code to have this tag in the generated XAML?
I think I found the answer.
I have to use the following code before calling Save:
VisualBasic.SetSettings(activityBuilder, new VisualBasicSettings());
If the activityBuilder has been created from a DynamicActivity it is even better to use the following code:
VisualBasic.SetSettings(activityBuilder,
VisualBasic.GetSettings(dynamicActivity));
If this is not used, namespaces that are only needed for extension methods are not written to the XAML and will lead to an error when loading and executing the XAML.
I summarized my findings on code project.
I'm having a problem with XML deserialization that is baffling me.
I'm building an application that supports local customization of various services that it uses. I've implemented an abstract ServiceLocator class whose methods return various objects. Each custom installation is responsible for implementing a subclass of this and providing implementations of those methods. The meat of this class looks like this:
public abstract class ServiceLocator
{
public static void Initialize(string customFeaturesPath)
{
Assembly a = Assembly.LoadFrom(customFeaturesPath);
Type t = a.GetExportedTypes()
.AsEnumerable()
.Where(x => x.IsSubclassOf(typeof (ServiceLocator)))
.First();
Default = (ServiceLocator)a.CreateInstance(t.FullName);
}
public static ServiceLocator Default { get; private set; }
public abstract DefaultValuesContainer CreateDefaultValuesContainer();
}
This works just fine: I get the path to the custom features assembly from the application configuration file, the program calls Initialize, and then the application can call the various methods on ServiceLocator.Default and they return the appropriate custom implementations of the services.
One of these services is a DefaultValuesContainer. This is a simple object that exposes properties whose values need to be persisted in a user settings file. The idea is that I can serialize this object into a single user setting of type string. It makes for a user setting file that you wouldn't want to edit manually, but I'm cool with that.
Here's a concrete implementation of ServiceLocator.CreateDefaultValuesContainer:
protected override DefaultValuesContainer CreateDefaultValuesContainer(string serializedXml)
{
DefaultValuesContainer c = new ClientDefaultValuesContainer();
if (string.IsNullOrEmpty(serializedXml))
{
return c;
}
XmlSerializer x = new XmlSerializer(c.GetType());
return (DefaultValuesContainer) x.Deserialize(new StringReader(serializedXml));
}
Now here's the thing.
I've built unit tests for this using NUnit. When I run the tests in the test fixture class that exercises the client custom features, they work. When I run the entire test suite, the last line of the above method throws this exception:
System.InvalidOperationException : There is an error in XML document (0, 0).
----> System.IO.FileLoadException : Could not load file or assembly 'ClientCustomFeatures, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. Invalid pointer (Exception from HRESULT: 0x80004003 (E_POINTER))
----> System.ArgumentNullException : Value cannot be null.
Parameter name: path1
I'm kind of baffled as to why. The SetUp method still runs, and ServiceLocator.Default still returns an object of type ClientServiceLocator, which means that it has loaded the ClientCustomFeatures assembly. Indeed, the very method that's throwing the exception is in the assembly that I'm being told can't be loaded.
What is the XmlSerializer trying to do here? Why is it trying to load an assembly that's already loaded? What on earth does "Invalid pointer" mean? And above all, how should I be debugging something like this?
If your custom assembly does not know where to load the assembly containing the ClientCustomFeatures class, this will happen. This occurs when you've deployed your custom assembly to a location that is not in the path of your main assembly and your main assembly is not in the gac. So if your custom asseblies are loaded from sub directories of your main assembly this should go away. However, if they are located in arbitrary places, you'll have a problem because they need to load your main assembly as they need access to the ClientCustomFeatures type.
I've had problems with the assembly loader (Fusion?) when one assembly loads another assembly which itself has (non-GAC) references. YourDLL.XmlSerializers.dll might be one such assembly. Try turning off Visual Studio's option to automatically generate an XML serialization assembly (Project options) - this will remove the additional assembly (and hence the dependency on it).
Fusion Log Viewer
To help diagnose assembly loading problems like these, take a look at the Fusion Log Viewer (AKA fuslogvw.exe).
Fusion == the .NET component that locates and loads assemblies.
Try to replace the line:
XmlSerializer x = new XmlSerializer(c.GetType());
with:
XmlSerializer x = new XmlSerializer(c.GetType(), new Type[] { typeof(DefaultValuesContainer), typeof(ClientDefaultValuesContainer) });