I am trying to update a custom configuration section of a web.config file during the installation of my product in a custom action. I wanted to use the actual configration classes to do this however when the installer runs it loads my installer class but then the
Configuration.GetSection throw a File Not Found exception as it is trying to load my custom section class from the windows system directory. I managed to get this to work by copying the required assemblies into the windows system directory but this is not an ideal solution as I cannot guarantee I will always have access to that directory.
How else can I solve this problem?
My update code looks like this
[RunInstaller(true)]
public partial class ProjectInstaller : Installer
{
public override void Install(System.Collections.IDictionary stateSaver)
{
//some code here
webConfig = WebConfigurationManager.OpenWebConfiguration("MyService");
MyCustomSection mySection = webconfig.GetSection("MyCustomSection") //<--File Not Found: CustomConfigSections.dll
//Update config section and save config
}
}
My config file looks like this
<configuration>
<configSections>
<section name="myCustomSection" type="CustomConfigSections.MyCustomSection, CustomConfigSections" />
</configSections>
<myCustomSection>
<!-- some config here -->
</myCustomSection>
</configuration>
Hope you would understand the answer the way it is intended.
Assuming that you have setup the installer to have your project output. If Not
Right Click on installer Project click add->Project Output->select your project
and then you can continue using your code.
Moreover if you are using dll except the .net Ones make sure to change there
properties to copylocal = true
If You want to read the element Before Installation use BeforeInstall Event
Handler and try reading your file. ihope your problem will be solved
If in case You want to read the element after installation Right Click On
installer project Click view->customActions->On Install Click Add Custom Action
->Select Application Folder -> Select Primary output from your project and click
ok .
Now Click on primary output and press F4 and in Custom Action Data write
/DIR="[TARGETDIR]\"
and after that write your code as follows.
[RunInstaller(true)]
public class ProjectInstaller : Installer
{
public ProjectInstaller()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
this.AfterInstall += new InstallEventHandler(ProjectInstaller_AfterInstall);
}
void ProjectInstaller_AfterInstall(object sender, InstallEventArgs e)
{
string path = this.Context.Parameters["DIR"] + "YourFileName.config";
// make sure you replace your filename with the filename you actually
// want to read
// Then You can read your config using XML to Linq Or you can use
// WebConfigurationManager whilst omitting the .config from the path
}
Related
I am attempting to create a custom ASP.NET MVC4 template. I start from the Basic MVC4 template, make my modifications, and then use the "Export Template" wizard to create the template zip file. Right now (almost) everything is working smoothly. When I use the template to create a new MVC application, it recreates all of my settings the way I want them except one. For some reason, it changes the project properties for my web application to have a Start Action of "Current Page" instead of "Specific Page" (like it was in the original template and like it is in my template). This setting is in the project properties under the Web tab. Here is what it is set to in my template application (before I generate the actual template zip file):
And here is what it is like when I create a new project using that template:
How do I modify my template to set this setting properly (or more accurately, how do I force it to remember what I set initially)?
Edit: answer revised due to misunderstanding of both original request and behavior of previously proposed solution.
To set the Start Action of a custom MVC project template, you'll need to create a dll with a class that implements the Microsoft.VisualStudio.TemplateWizard.IWizard interface. To use the wizard dll, you'll either need to copy it to Visual Studio's probing path, which is (VS2010 Install Dir)\Common7\IDE, (VS2010 Install Dir)\Common7\IDE\PrivateAssemblies, or (VS2010 Install Dir)\Common7\IDE\PublicAssemblies. If you don't put the compiled dll in one of those directories, you'll need to strong name and sign the dll and add it to the GAC and get the publickeytoken of the dll and add it to the Assembly element in the vstemplate file.
In testing the following code I copied the dll to (VS2010 Install Dir)\Common7\IDE\PrivateAssemblies, so the dll is not signed.
Wizard code
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using EnvDTE;
using Microsoft.VisualStudio.TemplateWizard;
namespace WarrenG.StartAction {
public class Wizard : IWizard {
private readonly Dictionary<string, object> data = new Dictionary<string, object>();
public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary,
WizardRunKind runKind, object[] customParams) {
if (replacementsDictionary.ContainsKey("$wizarddata$")) {
string xml = replacementsDictionary["$wizarddata$"];
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
foreach (XmlNode node in doc.ChildNodes) {
data.Add(node.Name, node.InnerText);
}
}
}
public bool ShouldAddProjectItem(string filePath) {
return true;
}
public void RunFinished() {
}
public void BeforeOpeningFile(ProjectItem projectItem) {
}
public void ProjectItemFinishedGenerating(ProjectItem projectItem) {
}
public void ProjectFinishedGenerating(Project project) {
if (data.ContainsKey("WebApplication.DebugStartAction")) {
project.Properties.Item("WebApplication.DebugStartAction").Value =
data["WebApplication.DebugStartAction"];
} else {
project.Properties.Item("WebApplication.DebugStartAction").Value = 1;
}
}
}
}
Add wizard specific elements to vstemplate file of custom MVC project template
<VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Project">
<TemplateContent>
<!-- various template content -->
</TemplateContent>
<!-- add the following -->
<WizardExtension>
<Assembly>WarrenG.StartAction, Version=1.0.0.0, Culture=Neutral, PublicKeyToken=null</Assembly>
<FullClassName>WarrenG.StartAction.Wizard</FullClassName>
</WizardExtension>
<WizardData>
<WebApplication.DebugStartAction>1</WebApplication.DebugStartAction>
</WizardData>
</VSTemplate>
The start actions on the project page appear to be numbers 0 through 4, following their display order. A value of 1 corresponds with Specific Page.
Unfortunately, or fortunately, depending on the side of the coin you're on...
Like the "Startup Project" setting, that setting is NOT part of the project file or the template file that's generated. It is stored in the "SUO" or Solution User Options, file. The SUO is not included by the template generator.
Some background on the SUO file: http://msdn.microsoft.com/en-us/library/bb165909(v=vs.80).aspx
I've had serious problems on how to solve this: I don't know where the OnAfterInstall event goes.
Let me explain myself. I created a C# project which compiles perfectly and built in Release mode. After that, I've created a Setup Project using the wizard. I have added an extra dialog, which lets the user choose between two languages. Now, my problem is that I want to store that language into the registry (or app.config file, the easier the better), and I've read that you need to detect it within the OnAfterInstall method in an inherited class of Installer.
Now, where should I put that class? Logic tells me it goes in the C# project, but it complains that neither Context nor Installer class exist. When I add this class to the Setup Project, it doesn't complain, but it doesn't work after that. Here's the class.
using System;
using System.Configuration.Install;
public class Install : Installer
{
public Install()
{
}
protected override void OnAfterInstall(IDictionary savedState)
{
string lang = Context.Parameters["lang"];
RegistryKey key = Registry.LocalMachine;
using (key = key.CreateSubKey(#"SOFTWARE\MyCompany\MyApp"))
{
key.SetValue("lang", lang);
key.Close();
}
base.OnAfterInstall(savedState);
}
}
PS: I'm already passing lang as CustomActionData using /lang=[LANG] (where LANG is the radio value)
First, you should add the RunInstallerAttribute to you class.
[RunInstaller(true)]
public class Install : Installer
...
Next, put the installer in a separate project (class library), e.g. MyCustomInstaller.
Finally, add the primary output of this project to a custom action in the custom actions editor of the setup project.
It's up to you in which custom action you want to use.
When I am trying to create an installation project, I get this error message when I try to build the installation class.
Unable to build custom action because the project output group does not have a key file.
What is wrong my custom action
I have a custom action that does some manipulation after the
I have a custom action as follows.
public class InstallerHelper:Installer
{
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
Console.WriteLine("Hello World");
Console.ReadLine();
}
}
Within your installation project, you need to identify a key file in the Project Output group that you have referenced.
To resolve the issue, right-click on the appropriate project output that you have added to the installation program, expand the KeyOutput node, and click in the (Files) box and click the ... button.
If there is nothing in there, then you should probably remove that output from your installer.
I'm working on a setup project. In a separate library project I created a custom installer by inheriting from System.Configuration.Install.Installer and added the generated .dll as a custom action.(Install step). I added an app.config file to the library project where I store a connection string which I need to make a connection to Sql Server.
Once I run the setup project, the custom installer doesn’t retrieve the connectionString stored in the app.config file.
Where can I store the connection string? Can a setup project have an app.config? Could somebody recommend a book on deployment/setup projects?
Thanks
UPDATE
Hi,
Thanks. Based on the replies I've updated my code this is what I´m doing now:
-> In setup project, I added a custom action to the install step, selecting application folder and primary output(library project).
-> Added an app.config to the library project and set its build action to "content".
-> Added the library project content files to the setup project.
This way the app.config file appears in the install folder.
Within the install handler from my custom install class I do the following: (in this case I access the application settings)
string appPath = "";
appPath = Path.Combine(new DirectoryInfo(Context.Parameters["assemblypath"].ToString()).Parent.FullName, "App.config");
ExeConfigurationFileMap map = new ExeConfigurationFileMap();
map.ExeConfigFilename = appPath;
System.Configuration.Configuration c = null;
c = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
string result = c.AppSettings.Settings["test"].Value;
You can still reuse app.config if you insist on doing so, but you need to come up with your own way to parse it.
Here is the necesary steps for this workaround:
Create your own mechanism to
read the appSettings using
System.Xml.XmlReader or whatever from
the app.config file that will be
copied into the Application Folder.
Use your custom xml parser to
extract the value and use it in your
custom action.
Mark
App.config as Content in the Build Action property
window (in the custom action project).
In the Setup Project, add Project Output in the Application Folder and choose Content Files
instead of Primary output. You can
verify that app.config is the output
by invoking context menu and choosing
Outputs for the Content Files from
... item.
After you've done this, you should have 2 outputs from the Custom Installer class library project (1 for Primary output and 1 for the Content Files).
Here is an example custom installer action that will launch whatever I have in my appSettings key called launch along with my replacement ConfigurationManager implementation:
using System;
using System.Configuration.Install;
using System.Xml;
using System.IO;
using System.Reflection;
using System.ComponentModel;
using System.Collections;
namespace ClassLibrary1
{
[RunInstaller(true)]
public partial class Installer1 : System.Configuration.Install.Installer
{
public Installer1()
{
InitializeComponent();
}
[System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
public override void Commit(IDictionary savedState)
{
base.Commit(savedState);
System.Diagnostics.Process.Start(ConfigurationManager.AppSettings["launch"]);
}
}
public class ConfigurationManager
{
private static AppSetting _appSettings = new AppSetting();
public static AppSetting AppSettings
{
get { return _appSettings; }
}
public class AppSetting
{
public string this[string key]
{
get
{
var path = Path.Combine(Path.GetDirectoryName(Assembly.GetCallingAssembly().Location), "app.config");
var xpath = string.Format("/configuration/appSettings/add[#key='{0}']", key);
var doc = new XmlDocument();
doc.Load(path);
var node = doc.SelectSingleNode(xpath);
if (node == null)
return string.Empty;
return node.Attributes["value"].Value;
}
}
}
}
}
Instead of trying to parse the app.config, I would suggest adding the configSource attribute to your app.config to externalize the connection strings, something like:
<connectionStrings configSource="connections.config" />
Then get the setup script to generate the entire connections.config file, similar to:
<connectionStrings>
<clear />
<add name="...." connectionString="...." providerName="...." />
</connectionStrings>
Make sure you use .config as the extension of your file so it cannot be served to the browser if someone guesses the file name.
This is very frustrating... I can set the Configuration File for a Windows Forms Application just fine. Consider this:
public static void Main(){
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", #"SharedAppConfig.config");
//do other things
}
However, in a WPF application, this doesn't seem to work! If I set this value, the value of the AppDomain.CurrentDomain.SetupInformation.ConfigurationFile property is correct, but any calls to that configuration file while debugging yield no results. There are WCF configuration settings in an App.config that I need to share between application, so this is my proposed solution. Is it possible to dynamically set the location of my config file in WPF?
Help! Thanks!
You should be able to do something along the lines of:
using System.Configuration;
public class TryThis
{
Configuration config = ConfigurationManager.OpenExeConfiguration("C:\PathTo\app.exe");
public static void Main()
{
// Get something from the config to test.
string test = config.AppSettings.Settings["TestSetting"].Value;
// Set a value in the config file.
config.AppSettings.Settings["TestSetting"].Value = test;
// Save the changes to disk.
config.Save(ConfigurationSaveMode.Modified);
}
}
NOTE: This will attempt to open a file named app.exe.config at C:\PathTo. This also REQUIRES that a file exists at the same path with the name "app.exe". The "app.exe" file can just be an empty file though. For your case I'd almost make a shared "Config.dll" library that would handle the config file.
~md5sum~
Is this on the service side or the client side? If on the service side, it is often the case that the service is running in its own AppDomain, so that if you set AppDomain.CurrentDomain.SetData(...) it won't apply to the service configuration.
I'm not entirely sure how to get around this, but you should be able to control the service's configuration by implementing your own ServiceHost.