How to modify an XML file embedded in C#? - c#

I am developing an application in C# that interacts with DLL assemblies exchanging values, since the application can read and write in the DLL file.
Each DLL file is a class library project, since they are plugins that extend the functionality of my application; nevertheless, I need that each DLL can store certain parameters that my application can read and modify freely and that when modified, these parameters keep their value permanently.
Each DLL file has an embedded DLL file to store these parameters (obviously I can not load the XML by its path as it is inside a DLL), however, I can not change the values ​​of the nodes in the XML file.
Having said that, I have the following class in the DLL assembly:
public class AssemblyData
{
private Assembly assembly;
private XmlDocument xmldocument;
private Stream stream;
public AssemblyData()
{
this.assembly = Assembly.GetExecutingAssembly();
this.xmldocument = new XmlDocument();
string classNamespace = typeof(AssemblyData).Namespace;
this.stream = this.assembly.GetManifestResourceStream($"{classNamespace}.Settings.xml");
this.xmldocument.Load(this.stream);
}
public string Test
{
get => this.xmldocument.SelectSingleNode("Settings/Test").InnerText;
set
{
this.xmldocument.SelectSingleNode("Settings/Test").InnerText = value;
this.SaveXml();
}
}
public void SaveXml()
{
this.xmldocument.Save(this.stream);
}
}
However, the value of the Test node never changes. What is this about?

Related

Pack schema files in a dll to secure them from tampering

I have a similar problem to C# - Validating xml file against local .xsd security issues.
but my point is not a security concern in the first place. I'm hoping to secure my schema files against a "stupid user" more than an actual attacker.
Is there a possibility to pack my xsd-files into a dll at compile time and use it from there during runtime (instead of just reading a text file from the file system)?
If it would be inside a dll the "stupid user" wouldn't be able to just edit the files by accident and for an attacker we could even go further and protect the dll with strong-naming and digital signatures.
internal class XmlValidator : Validator
{
private static XmlSchemaSet _schemas;
/// <summary>
/// Initializes a new instance of the <see cref="XmlValidator"/> class.
/// </summary>
internal XmlValidator()
{
string path;
path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
}
else
{
path = #".\";
}
// Add schemas
_schemas = new XmlSchemaSet();
_schemas.Add("http://myschema/schema1", Path.Combine(path, "Schemas", "schema-v1.0.xsd"));
_schemas.Add("http://myschema/schema11", Path.Combine(path, "Schemas", "chema-v1.1.xsd"));
}
So instead of reading them directly from the file system during initialization I would like to read them as some kind of resource.
So something similar to translation files. Created at compile time and unchangeable during runtime
Sure this is possible. I do it the same way to protect them.
First declare them as Embedded Resource
Use it in code
public void LoadXsd()
{
string resourceName = "DefaultNamespace.specs.info.IErrorInfo.xsd";
Assembly assembly = Assembly.GetExecutingAssembly();
XmlSchema xsd = XmlSchema.Read(assembly.GetManifestResourceStream(resourceName), _XsdSchema_ValidationEventHandler);
XmlSchemaSet schemaSet = new XmlSchemaSet();
schemaSet.Add(xsd);
}
private void _XsdSchema_ValidationEventHandler(object sender, ValidationEventArgs e)
{
_Logger.Error($"XSD validation error: {e.Message}");
}
It would be also possible to load them all at once:
public void LoadAllXsdFiles()
{
XmlSchemaSet schemaSet = new XmlSchemaSet();
var assembly = Assembly.GetExecutingAssembly();
var allXsdFiles = assembly.GetManifestResourceNames().Where(r => r.EndsWith(".xsd"));
foreach (string xsdFile in allXsdFiles)
{
XmlSchema xsd = XmlSchema.Read(assembly.GetManifestResourceStream(xsdFile), _XsdSchema_ValidationEventHandler);
schemaSet.Add(xsd);
}
}

Using MEF to load DLLs with embedded libraries

I am currently writing an application suite with a plugin system that loads plugins at runtime using the MEF framework.
I have currently setup one of my top level WPF applications to embed it's referenced DLLs as embedded resources and load them at runtime using the method described here.
This works fine and I get my single file WPF application that runs fine.
However, another of my top level console applications uses the MEF framework to load plugins at runtime (the WPF application is fixed and includes the plugins explicitly). My plugins have several dependencies themselves on various libraries and the extensions folder that the console application loads the plugins from is littered with all the various library dlls.
I would like to embed the dependencies of each plugin within itself so that my extensions directory contains only the top level DLL files. The method that I have used above does not cater for this approach as the plugin component cannot find the required dependency as it is only the executing assembly that is being searched for these embedded resources.
My current OnResolveAssembly method looks like this:
public static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
Assembly executingAssembly = Assembly.GetExecutingAssembly();
var assemblyName = new AssemblyName(args.Name);
string path = assemblyName.Name + ".dll";
if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false)
{
path = String.Format(#"{0}\{1}", assemblyName.CultureInfo, path);
}
using (Stream stream = executingAssembly.GetManifestResourceStream(path))
{
if (stream == null)
return null;
var assemblyRawBytes = new byte[stream.Length];
stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
return Assembly.Load(assemblyRawBytes);
}
}
I'm thinking that the best way to proceed would be to add in functionality to keep track of all assemblies loaded in a list and once a new assembly has been loaded in this way, recursively do the same; load any embedded DLLs within those as you go. You can then add these DLLs to the list which will act as a cache.
Is there perhaps a better way to proceed with this?
I have implemented a very similar solution to yours and it works very fine for me. As you can see I keep track of already loaded assemblies in a _references dictionary.
In my case, I do not need to "eagerly" load all embedded dependencies in any recursive way, but rather my embedded assemblies do register themselves with the application host on-demand.
public static class ApplicationHost
{
private static readonly Dictionary<string, Assembly> _references = new Dictionary<string, Assembly>();
[STAThread]
private static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => _references.ContainsKey(args.Name) ? _references[args.Name] : null;
RegisterAssemblyAndEmbeddedDependencies();
// continue application bootstrapping...
}
public static void RegisterAssemblyAndEmbeddedDependencies()
{
var assembly = Assembly.GetCallingAssembly();
_references[assembly.FullName] = assembly;
foreach (var resourceName in assembly.GetManifestResourceNames())
{
using (var resourceStream = assembly.GetManifestResourceStream(resourceName))
{
var rawAssembly = new byte[resourceStream.Length];
resourceStream.Read(rawAssembly, 0, rawAssembly.Length);
var reference = Assembly.Load(rawAssembly);
_references[reference.FullName] = reference;
}
}
}
}

Get NameSpace of my Test Class

I have a webdriver solution that has 10 or so projects in it. 1 core assembly/dll that holds all the common methods and 9 other test assemblies that use those methods in their tests.
I need to access an embedded resource for one of those 9 assemblies but I need to do it from inside the core dll. What's the best way to do that.
namespace = webdriver.core
json.cs - reads a json file and returns it as a string
namespace = webdriver.marketplacestest
marketplace1Test.cs - calls one of the methods in json.cs such as getName();
profile.json - holds {"marketplace1" : "Amazon"}
calling an embedded resource from a known namespace is easy. I did that like this:
private static string fromEmbeddedResource(string myNamespace, string myFolder, string fileName)
{
string result;
using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(myNamespace + "." + myFolder + "." + fileName))
using (StreamReader reader = new StreamReader(stream))
{
result = reader.ReadToEnd();
}
return result;
}
As you can see, I just call the following and I have the file as a string
string json = fromEmbeddedResource("WebDriver.Core", "CFG", "pid.json");
It's harder though when the file is embedded in one of my test dlls. Anyone know how I can access or get the assembly's namespace? I tried...
Assembly.GetCallingAssembly().GetTypes();
but it looks like it's pulling types from the WebDriver.Core.dll assembly and not the WebDriver.Marketplace1.dll assembly.
I was able to figure it out. The problem I had was the calling assembly wasn't the correct assembly because I was calling a method in my core dll that called another method in my core dll. I got it working by passing the assembly along but that was expensive. To make things more efficient I modified my static SettingsRepository class that holds the two assemblies in a dictionary. That way I can pass in a string of "core" or "test" and pull the assembly without having to determine if I'm using an executing assembly or calling assembly each time.
private static Dictionary<string, object> _assembly = new Dictionary<string,object>();
public static Assembly getAssembly (string type)
{
return _assembly[type] as Assembly;
}
public static void addAssembly(string myType, Assembly assembly)
{
bool containsKey = _assembly.ContainsKey(myType);
if (!containsKey)
{
_assembly.Add(myType, assembly);
}
}
When I start a test, I always initialize my driver class first so i added the following two sets to that constructor.
Settings.addAssembly("core", Assembly.GetExecutingAssembly());
Settings.addAssembly("test", Assembly.GetCallingAssembly());
Now I can call either assembly I want anytime I want and know which one I'm getting.

Problems in reading DLL app.setting

i have problems in reading DLL application setting in c# Visual Studio 2010.
I post a sample code of the get workarounded using reflection because with the ConfigurationManager fails.
private string LDAPDomain
{
get
{
string strPath = System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName;
string val = GetValues(strPath, "LDAPDomain");
return val;
}
}
//strPath is path of the file.
//strKey is key to access
private string GetValues(string strPath, string strKey)
{
System.Configuration.Configuration con = System.Configuration.ConfigurationManager.OpenExeConfiguration(strPath);
string strValue = con.AppSettings.Settings[strKey].Value;
return strValue;
}
If you are expecting the main project referencing the DLL to pick up the app settings it doesn't work like that. The ConfigurationManager will read the config for the executing assembly, you need to put all the necessary configuration into your app if you want to use this.
Alternatively you can manually read the contents of your DLL's app.config file - see this question for some example code.

Effect of LoaderOptimizationAttribute

I have written a small piece of code regarding the dynamic loading of assemblies and creating class instances from those assemblies, including an executable, a test lib to be dynamically loaded and a loader library to load dynamic assembly into a new Appdomain. Loader library is referenced by both executable and the dynamic library.
//executable
[System.STAThreadAttribute()]
[System.LoaderOptimization(LoaderOptimization.MultiDomain)]
static void Main(string[] args)
{
AppDomainSetup domainSetup = new AppDomainSetup()
{
ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
LoaderOptimization = LoaderOptimization.MultiDomain
};
AppDomain childDomain = AppDomain.CreateDomain("MyDomain", null, domainSetup);
Console.WriteLine(AppDomain.CurrentDomain.SetupInformation.LoaderOptimization.ToString());
Console.WriteLine(childDomain.SetupInformation.LoaderOptimization.ToString());
byte[] assembly = null;
string assemblyName = "CSTestLib";
using (FileStream fs = new FileStream(assemblyName+".dll",FileMode.Open))
{
byte[] byt = new byte[fs.Length];
fs.Read(byt,0,(int)fs.Length);
assembly = byt;
}
object[] pararmeters = {assemblyName,assembly};
string LoaderAssemblyName = typeof(AssemblyLoader).Assembly.FullName;
string LoaderClassName = typeof(AssemblyLoader).FullName;
AssemblyLoader assloader = (AssemblyLoader)childDomain.CreateInstanceAndUnwrap(LoaderAssemblyName,LoaderClassName , true, BindingFlags.CreateInstance, null, parameters, null, null);
object obj = assloader.Load("CSTestLib.Class1");
object obj2 = assloader.Load("CSTestLib.Class2");
AppDomain.Unload(childDomain);
Console.ReadKey();
}
//Dynamic Lib
using System;
namespace CSTestLib
{
public class Class1 :MarshalByRefObject
{
public Class1() { }
}
public class Class2 : MarshalByRefObject
{
public Class2() { }
}
}
//Loader Library
using System;
namespace LoaderLibrary
{
public class AssemblyLoader : MarshalByRefObject
{
string assemblyName;
public AssemblyLoader(string assName, byte[] ass)
{
assemblyName = assName;
AppDomain.CurrentDomain.Load(ass);
Console.WriteLine(AppDomain.CurrentDomain.FriendlyName + " " + AppDomain.CurrentDomain.SetupInformation.LoaderOptimization.ToString());
}
public object Load(string className)
{
object ret = null;
try
{
ret = AppDomain.CurrentDomain.CreateInstanceAndUnwrap(assemblyName, className);
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message);
}
return ret;
}
}
}
Here I set LoaderOptimizationAttribute on main() method but AppDomain.CurrentDomain.SetupInformation.LoaderOptimization.ToString(); says it is NotSpecified Why?
The differences between MultiDomain and MultiDomainHost is not so clear to me. Is MultiDomainHost for only GAC assemblies? For my situation which is more suitable?
According to this
JIT-compiled code cannot be shared for
assemblies loaded into the load-from
context, using the LoadFrom method of
the Assembly class, or loaded from
images using overloads of the Load
method that specify byte arrays.
So how can I detect if an assembly is loaded domain-neutral or not? How can assure I it is loaded domain-neutral?
This attribute has only an effect if you precompile your assemblies with NGen to speed up a warm start of your application. When you specify MultiDomain or MultiDomainHost you enable the usage of precompiled (ngenned) assemblies. You can verify this with Process Explorer where you can look at the list of loaded modules.
This is one of the biggest startup time savers if your application consists of several executable instances which share assemblies. This enables .NET to share the code pages between processes which in turn saves real memory (one assembly exists only once in the physical memory but it is shared between one or more processes) and prevents JITing the same code over and over again in each process which takes time at the cost that the generated code is a little less efficient as it could be when it would be compiled with the regular JIT which can use more dynamic data to generate the most efficient code.
In your example you load the assembly into a byte array which is located in the managed heap and increases your private byte count. This makes it impossible to share data between processes. Only read only pages which have a counterpart on your hard disc can be shared between processes. This is the reason why the attribute has no effect. If you are after a factor 2 of warm startup performance this is the attribute you were seeking for. For anything else it is not relevant.
Now back to your original question:
It is set but when you start your application under a debugger this MultiDomain attribute is ignored. When you start it outside of a debugger you will get the expected results.
Yes MultiDomainHost does enable AppDomain neutrality only for signed assemblies all others are not shared.
Code sharing can only happen when it is precompiled. The real question is: How to check if the assembly is precompiled? I do it with Process Explorer by looking at the list of loaded modules. When my loaded assembly shows up with a path to the Native Image cache and an .ni extension I am sure the precompiled image is beeing used. You can check this also with fuslogvw when you set the radio button to Native Images to check why a native images was not used by the runtime.

Categories

Resources