Get access to AppSettings from custom Installer during uninstall (BeforeUninstall) - c#

I have a VS solution with following structure:
Library project (.dll)
Application using the #1 library project
I have app.config defined in the application (#2) which defines a SaveLogsToDirectory path in appSettings. This value is eventually used by the library project to save the logs generated.
Simple use of api System.Configuration.ConfigurationManager.AppSettings["SaveLogsToDirectory"]
in the library fetches the value from app.config.
Library project has a custom System.Configuration.Install.Installer class defined. When the application is uninstalled from windows via Control Panel, I would like the logs generated at path SaveLogsToDirectory to be deleted. Issue is the below code returns null only and only during the uninstall execution
System.Configuration.ConfigurationManager.AppSettings["SaveLogsToDirectory"]
One of the other approaches I tried was using System.Configuration.ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly())
but during uninstall the api Assembly.GetExecutingAssembly() returns reference to library project.
I need help on how I can access the application assembly from library during uninstall? One thing to mention is that I cannot provide a class path defined in application to OpenExeConfiguration api since the dll can be used by any other application and that other application may not have that class defined.

As an option you can store the dll settings in config file of the dll rather than config file of the application.
Then you can easily use OpenExeConfiguration and pass the dll address as parameter and read the settings.
To make it easier and harmonic to reading from app settings, you can create a like following and use it this way: LibrarySettings.AppSettings["something"]. Here is the a simple implementation:
using System.Collections.Specialized;
using System.Configuration;
using System.Reflection;
public class LibrarySettings
{
private static NameValueCollection appSettings;
public static NameValueCollection AppSettings
{
get
{
if (appSettings == null)
{
appSettings = new NameValueCollection();
var assemblyLocation = Assembly.GetExecutingAssembly().Location;
var config = ConfigurationManager.OpenExeConfiguration(assemblyLocation);
foreach (var key in config.AppSettings.Settings.AllKeys)
appSettings.Add(key, config.AppSettings.Settings[key].Value);
}
return appSettings;
}
}
}
Notes: Just in case you don't want to rely on Assembly.ExecutingAssembly when uninstall is running, you can easily use TARGETDIR property which specifies the installation directory. It's enough to set CustomActionData property of the custom action to /path="[TARGETDIR]\", then inside the installer class, you can easily get it using Context.Parameters["path"]. Then in the other hand you know the name of the dll file and using OpenMappedExeConfiguration by passing config file address as parameter, read the settings.
To setup a custom installer action and getting target directory, you may find this step-by-step answer useful:Visual Studio Setup Project - Remove files created at runtime when uninstall.

Related

Opening a solution with msbuildworkspace gives diagnostics errors without details

I am trying to analyse a solution with Roslyn, with MSBuildWorkspace.
The solution is a new solution, with 2 class library projects in them, one referencing the other.
They are created in Visual Studio 2017, .Net 4.6.2.
When I open the solution, I receive two generic errors in workspace.Diagnostics, both are :
Msbuild failed when processing the file 'PathToProject'
There is nothing more in the diagnostics or output window, to indicate WHY it failed to process the project file.
The code for opening the solution:
namespace RoslynAnalyse
{
class Program
{
static void Main(string[] args)
{
LocalAnalysis();
}
private static void LocalAnalysis()
{
var workspace = MSBuildWorkspace.Create();
var solution = workspace.OpenSolutionAsync(#"D:\Code\Roslyn\RoslynAnalyse\SolutionToAnalyse\SolutionToAnalyse.sln").Result;
var workspaceDiagnostics = workspace.Diagnostics;
}
}
}
The version of Microsoft.CodeAnalysis is 2.0.0.0.
Does anybody have any idea why MSBuild failed, how I can get more information ?
When MSBuildWorkspace fails to open a project or solution this way, it is almost always because the application using MSBuildWorkspace does not include the same binding redirects that msbuild.exe.config has in it.
MSBuild uses binding redirects to allow tasks (typically already compiled C# code using possibly different versions of msbuild API libraries) to all use the current msbuild API's. Otherwise, msbuild gets runtime load failures.
The solution is to add an app.config file to your project and copy the binding redirects (the assemblyBinding section of the msbuild.exe.config file) into your file.

Tell web references to use web.config or how to make settings.settings editable

I am migrating an ASP.NET 2.0 (3.5 SP1) app to .NET 4.0. It leverages SQL Server Reporting Services 2008R2 web service. This web service is an old-school asmx soap web service.
Back then I added a web-reference which I set to dynamic and this opted in some configuration code in my <applicationSettings> of the web.config.
After migrating to Visual Studio 2010 and .Net 4.0 I had to re-add the web reference. The .Net 2.0 web references dialog adds the proxy class as it was. But now it includes a settings.settings file to my project in the Properties folder. I could live with that but the settings.settings file is not available after compilation. It looks like it has been compiled into the dll.
Even pasting the settings.settings config in my web.config and deleting the settings.settings file does not help. I get a namespace compilation error because the vs-generated proxy class expected the settings.settings...
So how can I include my web reference and make it adjustable within a configuration file?
Either tell the web reference editor to use web.config, maybe?
Or make the settings.settings file available as xml so it could be changed on deployment?
Or any other help would be fantastic too...
This may or may not help you but this is our approach to web services to get around this problem. We create a wrapper class for a given web service and, every time we instantiate the proxy class, we set the URL in code referencing the <appSettings /> node.
e.g.
public class MyWebService
{
private static readonly string _serviceUrl = ConfigurationManager.AppSettings["serviceUrl"] ?? "http://someserviceurl.com/service.asmx";
public MyWebService()
{
}
private WebServiceClient GetClient()
{
WebServiceClient client = new WebServiceClient();
client.Url = _serviceUrl;
return client;
}
private void Method1()
{
var client = GetClient();
// do stuff
}
}

Application root path

I've recently been having some issues with correctly discovering application root path in c#. I want my applications to use the correct folder in following instances:
web application in debug (visual studio)
web application in release
deployed web application
console application in debug
console application in release
deployed console application
windows service (really same as console application)
Namely I need this for a logging assembly which is shared across all those types of applications. It's using log4net and it needs to correctly resolve physical path internally - inside logging assembly.
Because log4net requires either a call to BasicConfiguration.Configure to load it from web.config / app.config. Issue with this is it doesn't set up a file watcher so changes are not monitored. Solution is to have a log4net.config file separately.
Now, I don't like placing things inside bin, bin/debug or bin/release folder since it is never included in source control. Instead for things like that I have a Config folder in application root. So that you end up with ~\Application\Config\log4net.config.
In the static logger there are 2 methods:
public static string GetRootPath()
{
var debugPath = string.Empty;
#if (DEBUG)
debugPath = "..\\..\\";
#endif
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, debugPath);
}
public static void Init(string loggerName)
{
LoggerName = loggerName;
XmlConfigurator.Configure(new FileInfo(Path.Combine(GetRootPath(), "Config\\log4net.config")));
}
So you can just call Logger.Init() in Application_Start or inside Main() for console apps. This works great for console applications but not for web applications since AppDomain.CurrentDomain.BaseDirectory points to web application root and not to it's bin folder (which also has no debug or release).
Has anyone got a reliable way to resolve root path for all above requirements? So - what should GetRootPath be doing?
PS: I know I could be checking if (HttpContext.Current != null) then don't merge debug path in but there must be a more elegant way?
You could use the CodeBase property of the Assembly class to determine the path to the executing assembly:
public static string GetRootPath()
{
var debugPath = string.Empty;
#if (DEBUG)
debugPath = "..\\..\\";
#endif
return Path.Combine(Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().GetName().CodeBase).LocalPath), debugPath);
}
Note, for web applications and windows services the file path is in file URI scheme format. So, I use the Uri class to convert the path to standard windows path format.
Hope, this helps.

DTM automation using DSSO and XML-RPC.NET

Trying to automate WHQL testing using the ONE AND ONLY document available on the subject: http://www.microsoft.com/whdc/devtools/wdk/dtm/dtm_dsso.mspx
I've played with the example code and am able to connect, list devices, etc. From there I've created a new project, a .NET 2.0 C# class:
using System;
using System.Reflection;
using System.IO;
using CookComputing.XmlRpc;
using Microsoft.DistributedAutomation.DeviceSelection;
using log4net;
class WhqlXmlRpcService : XmlRpcService
{
private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public static DeviceScript deviceScript;
[XmlRpcMethod("connect")]
public Boolean Connect(String dtm)
{
Boolean retVal = false;
deviceScript = new DeviceScript();
try
{
deviceScript.ConnectToNamedDataStore(dtm);
retVal = true;
}
catch (Exception e)
{
Log.Debug("Error: " + e.Message);
}
return retVal;
}
}
I'm using XML-RPC.NET to create a server that is hosted by IIS (using ASP.NET 2.0). The DTM Studio is installed in C:\Inetpub\wwwroot\xmlrpc\bin, the same place where the target of my class goes, to assure there are no resolution issues with the dozen or so .dll's I reference (as instructed by the DSSO doc). I tried adding the necessary DSSO libraries to the GAC to avoid this, but not all of them have strong names. So, despite being able to see all the libraries it needs to link against (and the Studio app functions just fine installed in a non-standard location), attempting .ConnectToNamedDatastore("nameofDTM") still results in the following:
xmlrpclib.Fault: <Fault 0: 'Could not connect to the controller to retrieve information. Several issues can cause this error including missing or corrupt files from the installation, running the studio application from a folder other than the install folder, and running an application that accesses the scripting APIs from a folder other than the installation folder.'>
I'm accessing the scripting APIs from the installation folder, as it's the same dir as my web service .dll, and the files aren't corrupt, because if I stick an .exe with the DSSO sample code in that same directory I can see it connect just fine in the debugger.
I'm at the end of my rope with this, and have been unable to find a helpful source for DTM/DSSO info anywhere.
Anyone done anything similar in the past, or had any success automating their WHQL testing?
I was unable to get this to work using an ASP.NET web service .dll, however, I was able to access the DSSO API by making my XML RPC server available using the HttpListener class in .NET. If you deploy the target application into the same directory as DTM Studio, all works as expected.
For an example of how to use XML-RPC.NET with HttpListener, see:
http://www.cookcomputing.com/blog/archives/000572.html
Note: "ListenerService" has been incorporated into the latest versions of XML-RPC.NET since the time of the linked post above. It can be found under CookComputing.XmlRpc.XmlRpcListenerService

Can I use reflection to find the bin/[Configuration] folder in ASP.NET instead of the asp temporary folder

I have an ASP.NET website and I want to find the /bin/[Configuration] folder to use an external tool (an exe file). When I use reflection to get calling assemblies location it returns something similar to:
C:\Windows\Microsoft.NET\Framework\\...\Temporary ASP.NET Files\a1388a5e\\...\my.dll
Since each dll has its own directory under the temp ASP.NET Files this fails for me.
How can I get the location of the compiled binary folder where the dll's and the .exe is (i.e. bin/) instead of asp.net's temporary cache?
Notes
This code is in a supporting library that can be called from ASP.NET websites or other console/windows apps.
You could try (taken from How to: Get the Application Directory):
Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase);
I set up a quick test with:
A Web Application project running under the VS dev server (compiled site, /bin directory, etc).
A class library project, referenced by the WA.
In the web application I created a page that called the following method from the library project:
using System.IO;
using System.Reflection;
namespace TestHelpers {
public class ClassHelpers {
public static string PathToBin() {
return Path.GetDirectoryName(
Assembly.GetExecutingAssembly().GetName().CodeBase);
}
}
}
This resulted in the following output on the page:
file:\C:\Users\ UserName \Documents\Visual Studio 2008\Websites\ Solution \ Project \bin
Which is what I'd expect.
Server.MapPath("~\bin")
Any reason not to just do
Server.MapPath("~/bin");
?

Categories