C# Referencing dll in subfolder - c#

i can't figure out what i do wrong.
My project is like that :
MainProject
SubProject (referencing my desires DLL)
I have my app.config (of MainProject) :
<configuration>
<configSections>
<section name="DirectoryServerConfiguration" type="System.Configuration.NameValueSectionHandler"/>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,Log4net"/>
<section name="GeneralConfiguration" type="System.Configuration.NameValueSectionHandler"/>
<section name="ServerConnectionConfiguration" type="System.Configuration.NameValueSectionHandler"/>
<section name="cachingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Caching.Configuration.CacheManagerSettings,Microsoft.Practices.EnterpriseLibrary.Caching"/>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/>
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="ReferencesDLL" />
</assemblyBinding>
</runtime>
<GeneralConfiguration configSource="ConfigFiles\GeneralConfiguration.config"/>
<cachingConfiguration configSource="ConfigFiles\cachingConfiguration.config"/>
<DirectoryServerConfiguration configSource="ConfigFiles\YPAdress.config"/>
<log4net configSource="ConfigFiles\log4net.config"/>
</configuration>
And my compile repository is like that :
Debug
ConfigFiles\ (All the .config files defined in the app.config)
ReferencesDLL\ (All the Dll needed by SubProject)
(All the others compiled files : .exe, .dll, etc..)
As you can see, i have my privatePath define, but when running the app, it can't find the needed dll for SubProject. (no problem for the .config files)
I don't know what i did wrong :( .

You could manually look for the assembly at the known location using an AppDomain.AssemblyResolve event handler:
static void Run()
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
// Do work...
AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;
}
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
var dllName = new AssemblyName(args.Name).Name + ".dll";
var assemblyPath = Assembly.GetExecutingAssembly().Location;
var referenceDirectory = new DirectoryInfo(Path.Combine(Path.GetDirectoryName(assemblyPath), "ReferencesDLL"));
if (!referenceDirectory.Exists)
return null; // Can't find the reference directory
var assemblyFile = referenceDirectory.EnumerateFiles(dllName, SearchOption.TopDirectoryOnly).FirstOrDefault();
if (assemblyFile == null)
return null; // Can't find a matching dll
return Assembly.LoadFrom(assemblyFile.FullName);
}

Related

Persistent user settings

I am trying to save user settings in C# and reuse them in the next session.
All over the net, one finds tipps like this:
How to save user settings programatically?
I have a .setting file in my project with the default settings.
When a user changes a setting, I call save, and in %APPDATA% the file appears, with the user settings.
It looks like this:
private void SetSetting(string prop, Object val)
{
try
{
string strval = String.Format("G{0}_{1}", Group_Id, prop);
Properties.Last.Default[strval] = val;
}
catch
{
System.Configuration.SettingsProperty property = new System.Configuration.SettingsProperty(String.Format("G{0}_{1}", Group_Id, prop));
property.PropertyType = Properties.Def.Default[String.Format("G0_{1}", Group_Id, prop)].GetType();
property.DefaultValue = Properties.Def.Default[String.Format("G0_{1}", Group_Id, prop)];
property.PropertyType = val.GetType();
property.Provider = Properties.Last.Default.Providers["LocalFileSettingsProvider"];
property.Attributes.Add(typeof(System.Configuration.UserScopedSettingAttribute), new System.Configuration.UserScopedSettingAttribute());
Properties.Last.Default.Properties.Add(property);
Properties.Last.Default.Reload();
string strval = String.Format("G{0}_{1}", Group_Id, prop);
Properties.Last.Default[strval] = val;
}
Properties.Last.Default.Save();
}
As you can see, I have the settings instance Last as in "settings from last session" and Def as in default.
Basically, a user can add and delete groups at will in the app. Def holds the default configuration for a new group. Last should hold the number of groups in the last user session, as well as the properties of each group. Thus the formatting with the Group_Id and so on.
So, as I said. During runtime, its fine. User settings are properly written to the file in %APPDATA% and everything.
The file is like this:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c5629387e089" >
<section name="Project1.Properties.Last" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c5629387e089" " allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<userSettings>
<Project1.Properties.Last>
<setting name="G1_do_cut" serializeAs="String">
<value>True</value>
</setting>
</Project1.Properties.Last>
</userSettings>
</configuration>
So, the setting for do_cut for Group 1 is saved as G1_do_cut
But as soon as I restart the application, the user setting is ignored.
The code used is:
private Object GetSetting(string prop)
{
try { return Properties.Last.Default[String.Format("G{0}_{1}", Group_Id, prop)]; }
catch { return Properties.Def.Default[String.Format("G0_{1}", Group_Id, prop)]; }
}
and in the try, an exception G1_do_cut is not found in settings is thrown and the catch loads the default.
During a user session, the GetSettings works correctly - if I try to read G1_do_cut after it has been set, no exception is thrown and I get the correct value.
How do I get C# to reuse the settings after restarting the application?

How to write c# appSettings with several (sub)sections

i have been checking around, trying to find a way to write to the exe.config-file.
I can read/write to area just fine, but since we use 2 different DLL's from siemens to automate some stuff, i need to add a bunch of paths in a different section of the exe.config-file.
This is how the config should look like in the structure.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="Siemens.Engineering.dll" value="C:\Program Files (x86)\Siemens\Automation\Portal V13\PublicAPI\V13 SP1\Siemens.Engineering.dll" />
<add key="Siemens.Engineering.Hmi.dll" value="C:\Program Files (x86)\Siemens\Automation\Portal V13\PublicAPI\V13 SP1\Siemens.Engineering.Hmi.dll" />
<add key="TempDirectory" value="D:\Scania Oskarshamn\Alarm_text" />
<add key="AlarmTextPath" value="AlarmTexts" />
<add key="Auto.Compile" value="True" />
<add key="Auto.ExtractAlarms" value="True" />
<add key="Auto.ImportAlarms" value="True" />
<add key="Compile.HMI" value="changes" />
<add key="Lang1" value="sv-SE:Swedish (Sweden)" />
<add key="Lang2" value="en-GB:English (United Kingdom)" />
<add key="Lang3" value="pt-BR:Portugese (Brazil)" />
<add key="LangChecked" value="sv-SE:en-GB" />
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Siemens.Engineering" culture="neutral" publicKeyToken="d29ec89bac048f84"/>
<codeBase version="1.2.0.0" href="FILE://C:\Program Files (x86)\Siemens\Automation\Portal V13\PublicAPI\V13 SP1\Siemens.Engineering.dll"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Siemens.Engineering.Hmi" culture="neutral" publicKeyToken="37b6e3a80df0900f"/>
<codeBase version="1.2.0.0" href="FILE://C:\Program Files (x86)\Siemens\Automation\Portal V13\PublicAPI\V13 SP1\Siemens.Engineering.Hmi.dll"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
I need to change the href part in the tags, and if this section does not exist, i also need to add the whole structure below .
I just need to write these parts.
EDIT: Now i dont even need the paths in the config, using registry to lookup path and use AssemblyResolve to make it work.
Solved this by using:
public frmAutomate(AutoData autoData)
{
InitializeComponent();
AppDomain CurrentDomain = AppDomain.CurrentDomain;
CurrentDomain.AssemblyResolve += new ResolveEventHandler(MyResolver);
}
private static Assembly MyResolver(object sender, ResolveEventArgs args)
{
int index = args.Name.IndexOf(',');
if (index == -1)
{
return null;
}
string name = args.Name.Substring(0, index) + ".dll";
// Check for 64bit installation
RegistryKey filePathReg = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Wow6432Node\\Siemens\\Automation\\_InstalledSW\\TIAP13\\TIA_Opns");
// Check for 32bit installation
if (filePathReg == null)
filePathReg = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Siemens\\Automation\\_InstalledSW\\TIAP13\\TIA_Opns");
string filePath = filePathReg.GetValue("Path").ToString() + "PublicAPI\\V13 SP1";
string path = Path.Combine(filePath, name);
// User must provide the correct path
string fullPath = Path.GetFullPath(path);
if (File.Exists(fullPath))
{
return Assembly.LoadFrom(fullPath);
}
return null;
}

C# replace XML node with a new attribute and subnode

I've got this xml file:
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="MyApp.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</sectionGroup>
</configSections>
<startup><supportedRuntime version="v2.0.50727"/></startup>
<applicationSettings>
<MyApp.Settings>
...
...
</XNet.XManager.Properties.Settings>
</applicationSettings>
I need to replace the <startup> node with:
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
Which is the best way?
If you use LINQ to XML (it's an XML API rather than LINQ):
XDocument doc = XDocument.Load("dat.xml");
XElement startup1 = doc.Root.Element("startup");
startup1.Remove();
doc.Root.Add(new XElement("startup", new XAttribute("useLegacyV2RuntimeActivationPolicy", "true"),
new XElement("supportedRuntime", new XAttribute("version", "v4.0"),
new XAttribute("sku", ".NETFramework"),
new XAttribute("Version", "v4.5.2"))));
doc.Save("dat.xml");
Edit - as Jon Skeet suggested the proper way should be to use XElement.ReplaceWith :
XDocument doc = XDocument.Load("dat.xml");
XElement startup1 = doc.Root.Element("startup");
startup1.ReplaceWith(new XElement("startup", new XAttribute("useLegacyV2RuntimeActivationPolicy", "true"),
new XElement("supportedRuntime", new XAttribute("version", "v4.0"),
new XAttribute("sku", ".NETFramework"),
new XAttribute("Version", "v4.5.2"))));
doc.Save("dat.xml");
You can use the below code to do the same, where the element is being find and it is replaced with other.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("path to your file");
string strXml =
#"<startup useLegacyV2RuntimeActivationPolicy='true'>
<supportedRuntime version='v4.0' sku='.NETFramework,Version=v4.5.2' />
</startup>";
XmlDocumentFragment xmlDocFragment = xmlDoc.CreateDocumentFragment();
xmlDocFragment.InnerXml = strXml;
xmlDoc.SelectSingleNode("startup").AppendChild(xmlDocFragment);
Update: Using LINQ. Working Tested Code
var doc = XDocument.Load(#"path to file");
string input = #"<startup useLegacyV2RuntimeActivationPolicy='true'>
<supportedRuntime version='v4.0' sku='.NETFramework,Version=v4.5.2' />
</startup>";
var replacement = XElement.Parse(input);
var nodeToReplace = doc.Descendants().Elements("startup").FirstOrDefault();
nodeToReplace.ReplaceWith(replacement);
doc.Save(#"path to file");
Console.WriteLine(doc);
Console.Read();

Saving and Loading Strongly-Typed DataSet from Settings

I added a strongly-typed DataSet object to my project. It's type name is DocRetrieverDataSet.
I also have in my project settings a row for a user-scope DataSet property named DocRetrieverDataSource to which I want to save an instance of DocRetrieverDataSet.
Here is boiled-down code:
using Settings = MyProjectNameSpace.Properties.Settings;
....
private DocRetrieverDataSet myDocRetrieverDataSet;
public myForm()
{
Initialize();
if (Settings.Default.DocRetrieverDataSource == null)
{
Settings.Default.DocRetrieverDataSource = new DocRetrieverDataSet();
Settings.Default.Save();
}
this.myDocRetrieverDataSet = (DocRetrieverDataSet)Settings.Default.DocRetrieverDataSource;
}
The first time I run it, when Settings.Default.DocRetrieverDataSource is null, it works fine! However, when I run it the second time, I get an InvalidCastException at
this.myDocRetrieverDataSet = (DocRetrieverDataSet)Settings.Default.DocRetrieverDataSource;
It says
Unable to cast object of type 'System.Data.DataSet' to type 'DocRetriever.DocRetrieverDataSet'.
The funny thing is that it doesn't have this problem the first time around. What is going on and how can I fix it?
MORE INFO:
Here's the relevant code from Settings.Designer.cs
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public global::System.Data.DataSet DocRetrieverDataSource {
get {
return ((global::System.Data.DataSet)(this["DocRetrieverDataSource"]));
}
set {
this["DocRetrieverDataSource"] = value;
}
}
And from app.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=abcdefghijkl" >
<section name="DocRetriever.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=abcdefghijkl" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<userSettings>
<DocRetriever.Properties.Settings>
<setting name="SpoolDirectoryPath" serializeAs="String">
<value />
</setting>
<setting name="OutputDirectoryPath" serializeAs="String">
<value />
</setting>
</DocRetriever.Properties.Settings>
</userSettings>
</configuration>
You need to change the casting in your Settings file from DataSet to your DocRetrieverDataSet
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public global::System.Data.DataSet DocRetrieverDataSource {
get {
return ((global::FullNamespace.DocRetrieverDataSet)(this["DocRetrieverDataSource"]));
}
set {
this["DocRetrieverDataSource"] = value;
}
}
You can also do it via the Settings Designer, just go to your property and browse for you class

perfmon using EntLib - no instance in perfmon

We are in need to add performance monitoring to our application.
For the prototype I've created a sample project, which I'm trying to get into work.
I'm trying to use policyInjection for the performance counters, so we'll be able to turn on and off the performance monitoring in the production environment.
So far I can see the actual category in perfmon, but I cannot see any instances (see image), even I'm pretty sure the application is running and instance exists, as you can see in attached sources.
I've tried a lot of things, also googled around, but didn't find any usable solution or clue what to look for.
Application is created as consoleApplication
You can also download a zipped project for VS here:
http://dl.dropbox.com/u/19457132/stackOverflow/Mpd.Instrumentation.PerformanceCounter.zip
Here are my sources.
Program.cs
using System;
using System.Collections;
using System.Configuration.Install;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.PolicyInjection;
using Microsoft.Practices.EnterpriseLibrary.PolicyInjection.Installers;
namespace Mpd.Instrumentation.PerformanceCounter
{
class Program
{
static void Main(string[] args)
{
//RemoveCounters();
InstallCounters();
MyCounters myCounter = PolicyInjection.Create<MyCounters>();
for (int i = 0; i < 100000000; i++)
{
myCounter.SampleMethod(i);
}
Console.ReadLine();
}
public static void InstallCounters()
{
PerformanceCountersInstaller installer = new PerformanceCountersInstaller(new SystemConfigurationSource());
IDictionary state = new Hashtable();
installer.Context = new InstallContext();
installer.Install(state);
installer.Commit(state);
Console.WriteLine("Performance counters have been successfully installed. Press enter to continue");
Console.ReadLine();
}
private static void RemoveCounters()
{
PerformanceCountersInstaller installer = new PerformanceCountersInstaller(new SystemConfigurationSource());
installer.Context = new InstallContext();
installer.Uninstall(null);
Console.WriteLine("Performance counters have been successfully removed. Press enter to continue.");
Console.ReadLine();
}
}
}
MyCounters.cs
using System;
using System.Threading;
namespace Mpd.Instrumentation.PerformanceCounter
{
public class MyCounters : IPerformanceCounter
{
public void SampleMethod(int i)
{
Console.WriteLine(i);
Thread.Sleep(50);
}
}
}
IPerformanceCounter.cs
using System;
namespace Mpd.Instrumentation.PerformanceCounter
{
public class IPerformanceCounter : MarshalByRefObject
{
}
}
And finally app.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="policyInjection" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.Configuration.PolicyInjectionSettings, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" />
<section name="instrumentationConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Common.Instrumentation.Configuration.InstrumentationConfigurationSection, Microsoft.Practices.EnterpriseLibrary.Common, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" />
</configSections>
<policyInjection>
<policies>
<add name="SampleCountersPolicy">
<matchingRules>
<add type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.MethodSignatureMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
match="SampleMethod" ignoreCase="true" name="Method Signature Matching Rule" />
</matchingRules>
<handlers>
<add type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers.PerformanceCounterCallHandler, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
categoryName=".aaaTest" instanceName="Default" useTotalCounter="true"
incrementNumberOfCalls="true" incrementCallsPerSecond="true"
incrementAverageCallDuration="true" incrementTotalExceptions="true"
incrementExceptionsPerSecond="true" order="1" name="Performance Counter Call Handler" />
</handlers>
</add>
</policies>
</policyInjection>
<instrumentationConfiguration performanceCountersEnabled="true"
applicationInstanceName="Default" />
</configuration>
Since the SampleMethod accepts a parameter you need to add that to the matching rules configuration:
<matchingRules>
<add type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.MethodSignatureMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
match="SampleMethod" ignoreCase="true" name="Method Signature Matching Rule">
<parameters>
<parameter name="i" typeName="System.Int32" />
</parameters>
</add>
</matchingRules>
Without the parameter the matching rule does not match so the call handler is not invoked. If you modify the configuration you should see the instances in perfmon.

Categories