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);
}
}
Related
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?
I have embedded a class library into my executable, by adding the following to my .csproj file.
<Target Name="AfterResolveReferences">
<ItemGroup>
<EmbeddedResource Include="#(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
<LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
</EmbeddedResource>
</ItemGroup>
Then, I load the assemblies programmatically by subscribing to the AppDomain's AssemblyResolve event like so:
using System;
using System.Reflection;
using System.Windows.Forms;
using xofz.Research.UI.Forms;
internal static class EntryPoint
{
[STAThread]
private static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, e) => loadEmbeddedAssembly(e.Name);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var bootstrapper = new FormsBootstrapper();
new FormsAppBase(bootstrapper).Run(
args);
}
private static Assembly loadEmbeddedAssembly(string name)
{
var assemblyName = new AssemblyName(name);
if (name.EndsWith("Retargetable=Yes"))
{
return Assembly.Load(assemblyName);
}
var container = Assembly.GetExecutingAssembly();
var path = assemblyName.Name + ".dll";
using (var stream = container.GetManifestResourceStream(path))
{
if (stream == null)
{
return null;
}
var bytes = new byte[stream.Length];
stream.Read(bytes, 0, bytes.Length);
return Assembly.Load(bytes);
}
}
}
I then later in my app wish to read the embedded library's version number. There is a ReadCoreVersion() method in the embedded library which attempts to do just that:
public virtual string ReadCoreVersion()
{
var ea = Assembly.GetExecutingAssembly();
var vi = FileVersionInfo.GetVersionInfo(
ea.Location);
return vi.FileVersion;
}
However, when I attempted to call this method in my application's code, it threw an ArgumentException: The path is not of a legal form.
Is there any way to read an embedded assembly's version number?
Edit: after checking what the assembly's Location property consisted of, it was either null or empty. How can I retrieve the location of the embedded assembly to retrieve its version, or do I have to flush the assembly out to disk first?
You need to make sure the path you're passing to FileVersionInfo.GetVersionInfo is the correct format.
Assembly.Location returns UNC path
FileVersionInfo.GetVersionInfo expects a fully-qualified path.
You'll either need to parse the string yourself to convert the path to the correct format, or use a built-in method that does the conversion for you.
This page has a good breakdown on the different formats.
As it turns out, AssemblyName was of great help here. By modifying the ReadCoreVersion() method as follows, I was able to get it to display the embedded assembly's version information!
public virtual string ReadCoreVersion()
{
var ea = Assembly.GetExecutingAssembly();
var an = new AssemblyName(ea.FullName);
var v = an.Version;
var versionBuilder = new StringBuilder();
versionBuilder.Append(v.Major);
versionBuilder.Append('.');
versionBuilder.Append(v.Minor);
versionBuilder.Append('.');
versionBuilder.Append(v.Build);
versionBuilder.Append('.');
versionBuilder.Append(v.Revision);
return versionBuilder.ToString();
}
I have a command handler which basically works like this:
ControlList.Handlers[CommandType.MyCommandComesHere].Handle(data);
Handlers is a Dictionary<CommandType, ICommandHandler> and CommandType is a enum.
Handle by its turn would lead it to this:
using System;
using log4net;
namespace My_Application
{
public class MyCommand : ICommandHandler
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(MyCommand));
public void Handle(Events data)
{
Console.WriteLine("I can load cs files on the fly yay!!");
}
}
}
My question is how can I make so my application would compile and let me use that cs file while its running?
Any simple example of this would be greatly appreciated but not required as long as I can get some pointers as to what I need to look for as I am not even sure what do I need to make this happen.
To put it simple I am currently trying to understand how could I load a cs file into my application that is already compiled and is currently running.
Using CodeDOM, you need to first create a compiler provider. (You might want to set GenerateExecutable to false and GenerateInMemory to true for your purposes.)
var csc = new CSharpCodeProvider();
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "foo.exe", true);
parameters.GenerateExecutable = false;
parameters.GenerateInMemory = true;
Then, you can compile the assembly using CompileAssemblyFromSource and get the CompilerResults returned from it. From this returned object, get a reference to the generated assembly, using its CompiledAssembly property.
var results = csc.CompileAssemblyFromSource(parameters, "contents of the .cs file");
var assembly = results.CompiledAssembly;
Then you can use reflection to create instances from that assembly and call methods on them.
var instance = assembly.CreateInstance("MyCommand");
// etc...
Alternatively, if you're only interested in short code snippets, it might be worth it to use Roslyn instead. You need to create a ScriptEngine first.
var engine = new ScriptEngine();
Then you can just Execute strings on it - or Execute<T> if you're confident that the expression in the string returns a type assignable to T.
var myObject = engine.Execute("1+1");
var myInt = engine.Execute<int>("1+1");
It's definitely more immediate, so it's worth looking into if it serves your purpose.
I have looked for different ways to achieve this and found cs script library lightweight and usable. Here is code snippet how I use it. It runs cs code within app domain so it presumes, that the cs script being compiled comes form trusted source.
using CSScriptLibrary;
using csscript;
using System.CodeDom.Compiler;
using System.Reflection;
//Method example - variable script contains cs code
//This is used to compile cs to DLL and save DLL to a defined location
public Assembly GetAssembly(string script, string assemblyFileName)
{
Assembly assembly;
CSScript.CacheEnabled = true;
try
{
bool debugBuild = false;
#if DEBUG
debugBuild = true;
#endif
if (assemblyFileName == null)
assembly = CSScript.LoadCode(script, null);
else
assembly = CSScript.LoadCode(script, assemblyFileName, debugBuild, null);
return assembly;
}
catch (CompilerException e)
{
//Handle compiler exceptions
}
}
/// <summary>
/// Runs the code either form script text or precompiled DLL
/// </summary>
public void Run(string script)
{
try
{
string tmpPath = GetPathToDLLs(); //Path, where you store precompiled DLLs
string assemblyFileName;
Assembly assembly = null;
if (Directory.Exists(tmpPath))
{
assemblyFileName = Path.Combine(tmpPath, GetExamScriptFileName(exam));
if (File.Exists(assemblyFileName))
{
try
{
assembly = Assembly.LoadFrom(assemblyFileName); //Načtení bez kompilace
}
catch (Exception exAssemblyLoad)
{
Tools.LogError(exAssemblyLoad.Message);
assembly = null;
}
}
}
else
assemblyFileName = null;
//If assembly not found, compile it form script string
if (assembly ==null)
assembly = GetAssembly(script, assemblyFileName);
AsmHelper asmHelper = new AsmHelper(assembly);
//This is how I use the compiled assembly - it depends on your actual code
ICalculateScript calcScript = (ICalculateScript)asmHelper.CreateObject(GetExamScriptClassName(exam));
cex = calcScript.Calculate(this, exam);
Debug.Print("***** Calculated {0} ****", exam.ZV.ZkouskaVzorkuID);
}
catch (Exception e)
{
//handle exceptions
}
}
I'm working on a GUI that is able to manipulate xml files through a datagridview and have it saved to a destination of the user's choice. This program also has a .exe.config file in which I would also like to be able to freely edit inside a datagridview, since it's a lot more convenient than having the user manually going in to the file and changing the values accordingly.
I've tried declaring a dataset, and I intially thought that a .exe.config file was just an xml file, but this code does not work:
dataSet1.ReadXml(configpath);
bindingSource1.DataSource = dataSet1.Tables[0];
dataGridView1.DataSource = bindingSource1;
The datagridview is empty when I ran it and i confirmed that the filepath was correct and there was no exception when i debugged the code, whereas for the other xml files I open in the GUI work perfectly fine with the data displayed. Maybe readxml() only supports.. legit xml files rather than xml configuration files? I tried googling and looking for some answers, but all I got were threads related to changing the settings by manually accessing the xml file and changing the values (stuff I already know). I'm looking to be able to have the user do what they want to do with the data and then save it. The .exe.config settings may just as well be for another program, but it is essentially an xml configuration file. I figured there wasn't much on the web for this particular problem because settings are generally static and if they are changed, it's pretty easy to do manually.
To sum up,
I'm looking for a method to be able to open any .exe.config file, display it in a datagridview, allow the user to be able to manipulate the data values inside, and then save the file, overwriting the previous data settings.
Any help is appreciated.
Thank you in advance!
tf.rz (.NET 3.5 SP1, Visual Studio 2008 C#)
EDIT: I will upload a working example of an xml file I created: I kind of want the program to be able to navigate to a .exe.config file, then open it and have it displayed like this where the setting names are the columns and the values are in the cells of the datagridview. Unfortunately I am not at my home computer to be able to do this.
This is what I used to load up and manipulate a config file. You may want to change the loadAppSettings and loadConnStrings methods to suit your needs.
using System;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
namespace GenericManagementClasses
{
public class ConfigFile
{
private string m_ConfigFilePath;
private XmlDocument m_XmlDoc;
private FileStream fIn;
private StreamReader sr;
private StreamWriter sw;
private OrderedDictionary m_AppSettings;
private OrderedDictionary m_ConnectionStrings;
private XmlNode m_AppSettingsNode;
private XmlNode m_ConnectionStringsNode;
#region "Properties"
public String Path
{
get
{
return m_ConfigFilePath;
}
}
public OrderedDictionary AppSettings
{
get
{
return m_AppSettings;
}
}
public OrderedDictionary ConnectionStrings
{
get
{
return m_ConnectionStrings;
}
}
#endregion
#region "Constructors"
/// <summary>
/// Default constructor - declared private so that you can't instantiate an empty ConfigFile object
/// <code>ConfigFile cfg = new ConfigFile()</code> will result in a NotImplemented exception
/// </summary>
private ConfigFile()
{
throw new NotImplementedException("No default constructor for the ConfigFile class");
}
/// <summary>
/// Public constructor
/// <example>ConfigFile cfg = new ConfigFile(#"c:\MyApp\MyApp.exe.config");</example>
/// </summary>
/// <param name="ConfigFilePath">The path to the configuration file</param>
public ConfigFile(string ConfigFilePath)
{
//Check to see if the file exists
if (File.Exists(ConfigFilePath)){
//Initialise the XmlDocument to hold the config file
m_XmlDoc = new XmlDocument();
//Store the path to the config file
m_ConfigFilePath = ConfigFilePath;
//FileStream to get the contents out of the file
fIn = new FileStream(m_ConfigFilePath, FileMode.Open, FileAccess.ReadWrite);
//StreamReader to read the FileStream
sr = new StreamReader(fIn);
//StreamWriter to write to the FileStream
sw = new StreamWriter(fIn);
//Try and load the XML from the file stream
try
{
m_XmlDoc.LoadXml(sr.ReadToEnd());
m_AppSettingsNode = m_XmlDoc.GetElementsByTagName("appSettings")[0];
m_ConnectionStringsNode = m_XmlDoc.GetElementsByTagName("connectionStrings")[0];
loadAppSettings();
loadConnStrings();
}
catch (Exception ex)
{
//If it went pear shaped, throw the exception upwards
throw ex;
}
}
else
//If the file doesn't exist, throw a FileNotFound exception
{
throw new FileNotFoundException(ConfigFilePath + " does not exist");
}
}
#endregion
private void loadAppSettings()
{
m_AppSettings = new OrderedDictionary();
XmlNodeList nl = m_AppSettingsNode.SelectNodes("add");
foreach (XmlNode node in nl)
{
m_AppSettings.Add(node.Attributes["key"].Value, node.Attributes["value"].Value);
}
}
private void loadConnStrings()
{
m_ConnectionStrings = new OrderedDictionary();
XmlNodeList nl = m_ConnectionStringsNode.SelectNodes("add");
foreach (XmlNode node in nl)
{
m_ConnectionStrings.Add(node.Attributes["name"].Value, node.Attributes["connectionString"].Value);
}
}
public void setAppSetting(string name, string newValue)
{
if (!m_AppSettings.Contains(name))
{
throw new Exception(String.Format("Setting {0} does not exist in {1}", name, m_ConfigFilePath));
}
else
{
m_AppSettings[name] = newValue;
m_XmlDoc.SelectSingleNode(String.Format(#"//appSettings/add[#key='{0}']",name)).Attributes["value"].Value = newValue;
fIn.SetLength(0);
sw.Write(m_XmlDoc.InnerXml);
sw.Flush();
}
}
#region "Static Methods"
/// <summary>
/// Static method to return a ConfigFile object
/// <example>ConfigFile cfg = ConfigFile.LoadConfigFile(#c:\MyApp\MyApp.exe.config");"</example>
/// </summary>
/// <param name="ConfigFilePath">Path to the configuration file to load</param>
/// <returns></returns>
public static ConfigFile LoadConfigFile(string ConfigFilePath)
{
return new ConfigFile(ConfigFilePath);
}
#endregion
}
}
<configuration>
<configSections>
<section name="ADMIN" type="System.Configuration.DictionarySectionHandler"/>
</configSections>
<User>
<add key="ExtendTime" value="20"/>
<add key="Name" value="sss"/>
</User>
<configuration>
i have to remove first child element in user config section i.e . Reply me if you have any idea for this.
i am using
Configuration config = ConfigurationManager.OpenExeConfiguration(Context.Parameters["assemblypath"]);
ConfigurationSection section = config.GetSection("USER");
This article may have what you're looking for : http://raquila.com/software/configure-app-config-application-settings-during-msi-install/
Excerpt from article:
string exePath = string.Format("{0}MyWindowsFormsApplication.exe", targetDirectory);
Configuration config = ConfigurationManager.OpenExeConfiguration(exePath);
config.AppSettings.Settings["Param1"].Value = param1;
config.AppSettings.Settings["Param2"].Value = param2;
config.AppSettings.Settings["Param3"].Value = param3;
config.Save();
EDIT: Adding additional code sample and blog reference: http://ryanfarley.com/blog/archive/2004/07/13/879.aspx
using System;
using System.Xml;
using System.Configuration;
using System.Reflection;
//...
public class ConfigSettings
{
private ConfigSettings() {}
public static string ReadSetting(string key)
{
return ConfigurationSettings.AppSettings[key];
}
public static void WriteSetting(string key, string value)
{
// load config document for current assembly
XmlDocument doc = loadConfigDocument();
// retrieve appSettings node
XmlNode node = doc.SelectSingleNode("//appSettings");
if (node == null)
throw new InvalidOperationException("appSettings section not found in config file.");
try
{
// select the 'add' element that contains the key
XmlElement elem = (XmlElement)node.SelectSingleNode(string.Format("//add[#key='{0}']", key));
if (elem != null)
{
// add value for key
elem.SetAttribute("value", value);
}
else
{
// key was not found so create the 'add' element
// and set it's key/value attributes
elem = doc.CreateElement("add");
elem.SetAttribute("key", key);
elem.SetAttribute("value", value);
node.AppendChild(elem);
}
doc.Save(getConfigFilePath());
}
catch
{
throw;
}
}
public static void RemoveSetting(string key)
{
// load config document for current assembly
XmlDocument doc = loadConfigDocument();
// retrieve appSettings node
XmlNode node = doc.SelectSingleNode("//appSettings");
try
{
if (node == null)
throw new InvalidOperationException("appSettings section not found in config file.");
else
{
// remove 'add' element with coresponding key
node.RemoveChild(node.SelectSingleNode(string.Format("//add[#key='{0}']", key)));
doc.Save(getConfigFilePath());
}
}
catch (NullReferenceException e)
{
throw new Exception(string.Format("The key {0} does not exist.", key), e);
}
}
private static XmlDocument loadConfigDocument()
{
XmlDocument doc = null;
try
{
doc = new XmlDocument();
doc.Load(getConfigFilePath());
return doc;
}
catch (System.IO.FileNotFoundException e)
{
throw new Exception("No configuration file found.", e);
}
}
private static string getConfigFilePath()
{
return Assembly.GetExecutingAssembly().Location + ".config";
}
}
Then you would use it like this:
// read the Test1 value from the config file
string test1 = ConfigSettings.ReadSetting("Test1");
// write a new value for the Test1 setting
ConfigSettings.WriteSetting("Test1", "This is my new value");
// remove the Test1 setting from the config file
ConfigSettings.RemoveSetting("Test1");
I have come to the conclusion that it is not possible to access a custom configuration section during installation using:
MyCustomConfigurationSection section = (MyCustomConfigurationSection)config.GetSection("MyCustomConfigurationSection");
When the MSI package is installed, the program executed is the Windows Install(MsiExec), not the program which contains the installer class.
'%windir%\system32\msiexec.exe
In order to access the config we need to workaround this issue either by using the context:
Configuration config = ConfigurationManager.OpenExeConfiguration(this.Context.Parameters["assemblypath"]);
Or, by using reflection retrieve the location of the executing assembly:
Configuration config = ConfigurationManager.OpenExeConfiguration(System.Reflection.Assembly.GetExecutingAssembly().Location);
As Chuck suggested, you can access the AppSettings and modify them:
AppSettingsSection appSettings = (AppSettingsSection)config.GetSection("appSettings");
appSettings.Settings["Environment"].Value = _Environment;
config.Save();
This is fine, because the installer knows exactly how to deal with
System.Configuration.AppSettingsSection
because that library is part of .NET. However, when it comes to a custom section, the installer needs to know how to deal with that custom config. More than likely, you have it in a class library (in a DLL) that is referenced by your application and that DLL has now been installed in the install directory.
The problem is, as we know from above, MSIExec.exe isn't running in the context of that directory, so the install fails, when it can't find the appropriate DLL in system32, it throws the error:
An error occurred creating the
configuration section handler for
'XXX': Could not load file or assembly
'XXX.dll' or one of its dependencies.
The system cannot find the file
specified.
The only way therefore, to access the custom config, is to treat the config file as an XML document, and edit it using traditional XML management tools:
// load the doc
XmlDocument doc = new XmlDocument();
doc.Load(Assembly.GetExecutingAssembly().Location + ".config");
// Get the node
XmlNode node = doc.SelectSingleNode("//MyCustomConfigurationSection");
// edit node here
// ...
// Save
doc.Save(Assembly.GetExecutingAssembly().Location + ".config");
This technique is described on Ryan Farley's blog, as Chuck pointed out in the comments to his original answer.
Good news! I have found a way how to work-around this problem.
Solution is to intercept loading of assembly and return one we have. To do so
ResolveEventHandler handler = new ResolveEventHandler(CurrentDomain_AssemblyResolve);
AppDomain.CurrentDomain.AssemblyResolve += handler;
try
{
section = config.GetSection("mySection") as MySection;
}
catch(Exception)
{
}
AppDomain.CurrentDomain.AssemblyResolve -= handler;
and
Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
if (args.Name == "Dead.Beef.Rocks")
{
return typeof(MySection).Assembly;
}
return null;
}