windows forms app.config on common files folder - c#

I have an application that stores single username and password on the app.config file.
I currently have the app.config writable on runtime so that the user will be able to change it.
problem begins upon installation using a setup project , the app.config is installed on the program files which is not writable for any user.
So , i have changed the app.config location upon installation to the common files folder so it will be accessible for reading and writing for all users.
Now, upon installation it seems that the data stored there is inaccessible at all , using ConfigurationManager.AppSettings["networkPath"] for example returns empty strings.
what am i doing wrong ?

You can do it in two ways:
Give special permission to app config file in program files by your installer. We do it using innosetup. If you are incapable of it, then do it from your program startup:
static void Main()
{
GrantAccess()
}
private static bool GrantAccess()
{
FileInfo fInfo = new FileInfo(Assembly.GetEntryAssembly().Location + ".config");
FileSecurity dSecurity = fInfo .GetAccessControl();
fSecurity.AddAccessRule(new FileSystemAccessRule("everyone",
FileSystemRights.FullControl,
AccessControlType.Allow));
fInfo .SetAccessControl(fSecurity);
return true;
}
Else you can choose another location to place your appconfig file, and then read it like this:
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), Assembly.GetEntryAssembly().GetName().Name) + ".exe.config";
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(fileMap,
ConfigurationUserLevel.None);
return config.AppSettings.Settings["Key"].Value);
This is a poor choice in my opinion.

Related

How to create a folder with c# which is NOT read only?

In my C# forms application, I try to download the data in a directory of my SFTP Server. The data should be stored in a folder which I want to create in "MyDocuments". When the folder is created, I receive an Renci error "failure" because the folder is "read-only".
I tried many ways to create a folder, but I in most ways I used I either got an error, that I don't have the permission to create a folder, or I got an empty file instead of a folder. Right now I got a folder, but unluckily it is read only.
String localPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\MyNewFolder\\";
if (Directory.Exists(localPath))
{
Console.WriteLine("Folder already exists");
}
if (!Directory.Exists(localPath))
{
Directory.CreateDirectory(localPath);
DirectoryInfo directory = new DirectoryInfo(localPath);
DirectorySecurity security = directory.GetAccessControl();
}
I expect the folder not to be read only, so that I can safe data in it using my programm. Anyone knows why my code still creates a read only one?
I believe you have to set the following using the DirectorySecurity object:
DirectorySecurity securityRules = new DirectorySecurity();
securityRules.AddAccessRule(new FileSystemAccessRule(#"Domain\Account", FileSystemRights.FullControl, AccessControlType.Allow));
Then you can create the directory using the following:
DirectoryInfo di = Directory.CreateDirectory(#"directoryToCreatePath", securityRules);
EDITED:
Once you've created the directory using Directory.CreateDirectory(), you can then apply the following to the folder. This will allow the user you've specified to have FullControl of the folder. You can check the permissions for that user via Properties > Security
DirectoryInfo directory = new DirectoryInfo("C:\\CreatedFolder");
DirectorySecurity security = directory.GetAccessControl();
security.AddAccessRule(new FileSystemAccessRule(#"USERNAME",
FileSystemRights.FullControl,
AccessControlType.Allow));
directory.SetAccessControl(security);

How to replace a file to all user profiles on a network in c# using SCCM

I want to copy an xml file from a server and then connect to all user profiles and overwrite the user profile xml file they have with the one copied from the server. I would like this to be an executable so it can be run with SCCM for all users. I know there will have to be administration privileges but I'm not certain how to code it. I'm also open to ideas as how to do this differently, but I do want to do this with C# and make it into an executable for SCCM.
namespace copy_delete_move_files
{
public class SimpleFileCopy
{
public static object Logger { get; private set; }
static void Main()
{
string fileName = "Customize.xml";
string sourcePath = #"\\pathToServer\c$\TestFolder";
string targetPath = #"\\pathToUserProfiles\c$\%USERPROFILE%\APPDATA\Roaming\Folder\Customize";
// Use Path class to manipulate file and directory paths.
string sourceFile = Path.Combine(sourcePath, fileName);
string destFile = Path.Combine(targetPath, fileName);
// To copy a folder's contents to a new location:
// Create a new target folder, if necessary.
if (!Directory.Exists(targetPath))
{
Directory.CreateDirectory(targetPath);
}
// To copy a file to another location and
// overwrite the destination file if it already exists.
File.Copy(sourceFile, destFile, true);
// To copy all the files in one directory to another directory.
// Get the files in the source folder.
// Note: Check for target path was performed previously
// in this code example.
if (Directory.Exists(sourcePath))
{
string[] files = Directory.GetFiles(sourcePath);
// Copy the files and overwrite destination files if they already exist.
foreach (string s in files)
{
// Use static Path methods to extract only the file name from the path.
fileName = Path.GetFileName(s);
destFile = Path.Combine(targetPath, fileName);
File.Copy(s, destFile, true);
}
}
else
{
Console.WriteLine("Source path does not exist!");
}
// Keep console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
}
With SCCM you can achieve this using the following steps:
Create your program in the way that it works for one user (the whole multi user part will be left to sccm you just write it in a way it works for any user you double click it with). As appdata path you just use something like:
string fileNameWithExt = "your folderpath and filename";
string destPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), fileNameWithExt);
In SCCM when you create your program in the wizard you select "Program can run: Only when a user is logged on" and "Run mode: Run with user's rights". Once created you enter the properties of the program go to "Advanced" and chose "When this program is assigned to a computer: Run once for every user that logs on"
This makes sure that not only all users now but all in the future will get the program. It also help you if you really want to copy from a server because by default SCCM uses the local system account which has no access rights to your server shares (although you could grant them). The should also be possible with an application instead of a program.
Downsides are:
Your users ALL have to be allowed to access that share (could be
bypassed by using sccms distribution point method but would be more
complicated if the file on the server is updated often)
If you need admin rights for any operations it is not possible this
way. If the admin rights are only for parts that are not per user
specific you could split the program into two parts one per machine,
one per computer.

Why does XmlDocument read .config file, but ConfigurationManager does not? [duplicate]

I have added multiple app.config (each with a differet name) files to a project, and set them to copy to the output directory on each build.
I try and access the contents of each file using this:
System.Configuration.Configuration o = ConfigurationManager.OpenExeConfiguration(#"app1.config");
The code runs, but o.HasFile ends up False, and o.FilePath ends up "app1.config.config". If I change to code:
System.Configuration.Configuration o = ConfigurationManager.OpenExeConfiguration(#"app1");
Then the code bombs with "An error occurred loading a configuration file: The parameter 'exePath' is invalid. Parameter name: exePath".
If I copy/paste the config file (so I end up with app1.config and app1.config.config) then the code runs fine, however, I posit this is not a good solution. My question is thus: how can I use ConfigurationManager.OpenExeConfiguration to load a config file corretly?
I can't remember where I found this solution but here is how I managed to load a specific exe configuration file:
ExeConfigurationFileMap map = new ExeConfigurationFileMap { ExeConfigFilename = "EXECONFIG_PATH" };
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
According to http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/3943ec30-8be5-4f12-9667-3b812f711fc9 the parameter is the location of an exe, and the method then looks for the config corresponding to that exe (I guess the parameter name of exePath makes sense now!).
To avoid this problem altogether, you can read in the config file as an XML file, for example:
using System.Xml;
using System.Xml.XPath;
XmlDocument doc = new XmlDocument();
doc.Load(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\..\\..\\..\\MyWebProject\\web.config");
string value = doc.DocumentElement.SelectSingleNode("/configuration/appSettings/add[#key='MyKeyName']").Attributes["value"].Value;
using System.Reflection;
try
{
Uri UriAssemblyFolder = new Uri(System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase));
string appPath = UriAssemblyFolder.LocalPath;
//Open the configuration file and retrieve
//the connectionStrings section.
Configuration config = ConfigurationManager.
OpenExeConfiguration(appPath + #"\" + exeConfigName);
ConnectionStringsSection section =
config.GetSection("connectionStrings")
as ConnectionStringsSection;
}
At least, this is the method I use when encrypting and decrypting the connectionStrings section for my console/GUI apps. exeConfigName is the name of the executable including the .exe.

How to access "user.config" of different application

The user.config file of an application can be accessed in C# by
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).FilePath
Now, I need to get the user.config of a DIFFERNT application, identified by the fullpath to its exe in order replace current applications user.config by that one.
Any suggestions how to do so?
Edit: Please notice that I am interested in the user.config (ConfigurationUserLevel.PerUserRoamingAndLocal), not the application level settings like app.config or Application.exe.config. The latter is accessable by the OpenExeConfiguration(string), but is not what I want.
What about the overload that accepts a string?
var config = ConfigurationManager.OpenExeConfiguration(pathToAssebmly);
Anyway it´s a bad idea to bind your assembly that way to another one. You should consider to copy the config-file into all your assemblies and change the parts that are specific to a given one.
EDIT: As you´re interested in the user-specific setting you may use the following which is taken from here:
string configFile = string.Concat(appName, ".config");
// Map the new configuration file.
ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap
{
ExeConfigFilename = configFile
};
var config = ConfigurationManager.OpenMappedExeConfiguration(
configFileMap,
ConfigurationUserLevel.PerUserRoamingAndLocal);

Modifying a config section saves that section to a different config file

I have myapp.somenamespace.exe.config file with a connectionStrings section, which I need to encrypt. Also there are some other config settings that I want intact. So I wrote this small tool that would do just that:
class Program
{
static void Main(string[] args)
{
EncryptSection("myapp.somenamespace.exe.config", "connectionStrings");
}
static void EncryptSection(string fileName, string sectionName)
{
var config = ConfigurationManager.OpenExeConfiguration(fileName);
var section = config.GetSection(sectionName);
if (section.SectionInformation.IsProtected) return;
secction.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
config.Save();
}
}
What happens, it creates a new config file named myapp.somenamespace.exe.config.config - adding a duplicate .config extension, which only contains the encrypted section. It does not modify the original config file.
Any ideas why such an odd behavior and how I could fix that?
Change this line:
EncryptSection("myapp.somenamespace.exe.config", "connectionStrings");
to this:
EncryptSection("myapp.somenamespace.exe", "connectionStrings");
the documentation on MSDN states that the first parameter is in fact he path of the executable (exe) file and thus it's tacking on an additional .config during Save.

Categories