I created a Windows service used to execute automated procedures (.Net code) for multiple periodic operations, like backups, sanity checks, reports generation, etc. After building the project, I installed the service with installutil. Everything worked great.
I decided to move various "static" parameters for these automated procedures in the App.config file. I uninstalled the previous version of the service with installutil /u and built the new version of the project. In my build output folder, there's a AppName.exe file and a AppName.exe.config file, as I would expect. I installed the new version of the service, again with installutil from the VS 2012 Developer Command Prompt as an administrator.
The problem is that the service doesn't seem to be able to read the configuration file from the ConfigurationManager. The call for ConfigurationManager.AppSettings("paramname") doesn't fail, but the resulting parameter value is an empty string. As far as I know, the problem occurs for all parameters and not only for specific ones. The parameters are located in the <appSettings> section, under <configuration>, like I've done multiple times before in various projects.
I don't know if it can help, but my service runs on the LocalSystem account and starts automatically after installation and with Windows.
What did I do wrong?
Edit: I already tried uninstalling/re-installing the service (multiple times), like some stackoverflow answers suggests. Also, I'm not looking to update/refresh the file at runtime.
I solved this by setting the location of the config file in runtime. This enables me to place and name the config file where ever I want. I fetch the executing assembly path and then checks if the config file is there. Check the answer on this thread how to dynamically set the config file
This is how I fetch the executing assembly:
var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var configFilePath = Path.Combine(path, "service.config");
Hope this helps!
Turns out I was getting the configuration value in a variable declared with the same name as the property I was using to store the value. For an unknown reason, I had no warning from the compiler. Since a Windows service cannot be debugged, the solution was to review the code/rewrite this part from scratch, and that's when I saw the error.
Related
I am trying to help port a .Net service to a more modern .Net version (possibly Core) and use the MSIX installer. The application has several configuration files generated by the compiler (in source they are app.config but compiled they become *.exe.xml), they are installed into Program File right next to the binaries and a GUI helper app as well and the application itself can modify them to change service behavior (port, ip, tls cert, etc).
Writes under C:\Program Files\WindowsApps\package_name are not allowed.Writes under C:\Program Files\WindowsApps\package_name are not allowed.
The problem I am facing is that the MSIX installer makes it so that files in it's sandboxed version of Program Files cannot be written to (see above). That means that this application cannot be configured, so I am trying to figure out not only how to make the app configurable again, but also how windows wants to handle app configuration.
Right now it seems like there is two general approaches to do this:
write the configuration data to the service account's AppData/local folder
try to mimic a /etc/Myservice behavior in another folder. (meaning a local system-wide directory that houses configuration data for the service)
If you suggest #1 please answer the following additional questions:
How would I move Application configuration files to a user configuration file directory
how can an admin with a normal account modify the config file in the Service Account's AppData folder with the mentioned GUI helper application? (do they need to enable desktop access to the service account, login and run the GUI)?
If you suggest #2:
Where would you suggest this directory exist (specifically where will MSIX allow it)?
How do I tell the .Net application that the files are not right next to it? Can I just use AppData.CurrentDomain.SetData?
Well, a service running on the system account is the same for all users, so I would say that CommonApplicationData is a better folder for storing its settings, instead of appdata. This folder is easily accessible to both your service and to any admin that needs to deploy a custom config file.
In AppData you should store only actual user files (like files or settings generated by the actions taken inside your app by a specific user - thus different files for different users).
Now, the second part is where you need to configure you code to load the config file from a custom path instead of looking for it next to the EXE. I am no .NET expert but after a quick search I found this:
Relocating app.config file to a custom path
The modern approach to deploying app customizations
What is not clear to me is how your customers use the GUI helper tool to customize the config file. Is this just a tool that is used by someone from the IT department to generate the config file, and then they would copy that file and deploy it to the end-user machines using an MSI/MST file (or through some other custom deployment method)?
If your application is only deployed by IT folks, then you can try another simpler (and much elegant) solution for providing it with a custom config file, which actually doesn't require any code changes.
You can still leave the config file next to the EXE, in ProgramFiles and instruct the IT teams that deploy the app to use an MSIX Modification Package to deploy the custom config file generated by your GUI helper. (check the link included above for an example - with a video version at the end of the article).
Note: IT teams can use multiple free or paid tools to generate MSIX Modifications Packages.
Of course, your GUI helper tool still needs to generate that customized config file in a folder where it is allowed, as it can no longer write under ProgramFiles. So actually, you do need to modify a little bit your code in this scenario too.
I want to know if there is a better way to use connection strings in external files in my solution, since my solution has 8 separate projects, all using the same connection strings. According to this:
https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/connection-strings-and-configuration-files#using-external-configuration-files
using an external file (for connection strings) is trivial, and can be easily done (using something like configSource="connections.config). However, in order to make this work, We have to follow these guidelines:
The external file has to exist within the project folder
The external file has to have the property "CopyToOutputFolder" set to true
In my case, this can be done, but it's problematic to manage this throughout all 8 projects, especially if I need to add a new connection string, change one of the settings (target database, username, password), or when I need to add a new project.
--FYI: each of these projects are web jobs that are deployed to an Azure server. Adding a new web job for a new feature, new utility, etc., or removing an old web job for any number of reasons is not uncommon. This solution is constantly changing, which adds to the difficulty of managing these settings within each project separately. Also, connection string management on the production server is not problematic, since each web job inherits these connection strings from the parent app service.
I would like to know if any of the following is possible;
Can I use a separate file that is NOT within the project folder for the connection strings?
Can I load connection strings dynamically (at run-time) into the configuration manager? (one note on this - I know this is possible, but I need to be able to do it without affecting the underlying app.config file)
Is there another type of ConfigurationManager that is commonly used to load these connection strings into the solution, that will meet my requirements?
Although this question is over a year old, I thought an up-to-date answer would be useful for any Windows desktop coders wanting to reference connection strings outside the project folder's app.config etc. This would be useful for sharing 1 config file for multiple projects, having to only change connection string in 1 centralised file.
The basics of telling App.Config to use a different file for connection strings, is straightforward; however, .NET appears NOT to be able to parse this external file, if it is either NOT in the project's root folder, or a subfolder(s) within that project. Let's see this by looking at the most basic example:
in the project's App.config, within the <configuration> tag, use the code below ('configSource' points to another file to read the connection strings):
<configuration>
<connectionStrings configSource="ExternalConnectionStrings.config"/>
</configuration>
Create ExternalConnectionStrings.config, with the following code:
<connectionStrings>
<clear/>
<add name = "ConnString1"
connectionString = "Server=myServer;Trusted_Connection=True;Database=myDB;Persist Security Info=false"/>
<add name = "ConnString2"
connectionString = "Server=tcp:azureserver.database.windows.net,1433;Database=AzureDB;User ID=**;Password=**;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;Persist Security Info=false"/>
</connectionStrings>
Save this config file in the same folder as your project's App.config and mark this file's property to 'Copy to Output Directory' or 'Copy if newer'.
This is the 'textbook' way of referring to connection strings from another file other than App.config. However, this may not be that useful: how to have this external file in a completely different folder from the project folder?
To do this, copy that same ExternalConnectionStrings.config to a folder OUTSIDE the project or solution folder, eg. C:\ConnectionStringsFolder\. REMOVE this config file from the project folder where we previously copied/created it (otherwise the following instructions will not work).
Keep App.config the same (making sure the ExternalConnectionStrings.config is not present in the project folder). Use the Windows mklink command to associate ExternalConnectionStrings.config to an external folder, with the following command prompt command:
mklink ExternalConnectionStrings.config C:\ConnectionStringsFolder\ExternalConnectionStrings.config
Windows should return with a 'symbolic link created for....'; make sure you did NOT have that particular config file present in the project folder where app.config sits.
You should see the ExternalConnectionStrings.config listed within eg. Visual Studio; make sure you mark this to COPY to the output folder (I use 'Copy if newer', which will pick up any changes to the external config file ONLY after a rebuild of the project).
This answers Matt Spinks' question 1; for question 2, the following method will return all connection strings found within the external file (as pointed to by App.config):
using System.Diagnostics;
using System.Configuration;
static void GetConnectionStrings()
{
ConnectionStringSettingsCollection cStringsSettings = ConfigurationManager.ConnectionStrings;
if (cStringsSettings != null)
{
foreach (ConnectionStringSettings cstrings in cStringsSettings)
{
Debug.WriteLine(cstrings.Name);
Debug.WriteLine(cstrings.ConnectionString);
}
}
}
This is currently working in Visual Studio 2019, with .NET Core 3.1, a WPF application, on Windows 10.
You don't mention which version of dotnet you are using, however if you are using dotnet core the application configuration can be sourced from environment variables.
I think this could solve your problem because you don't need to track any additional files between your projects and in my opinion makes a lot of sense because any secure data would not be visible to any developer or be stored in any repository. Only devops would know about it when they set it initially. Also this gets your closer to the 12 factor app https://12factor.net/.
To use this make sure that your Startup.cs file includes a section to load the environment variables such as
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", true)
.AddEnvironmentVariables()
.Build();
dotnet core Environment Variable cnfiguration provider
You can probably create a bat file to set everything initially
setx connection_string="Server=xxxxxxxxx" /M
This will create your environment variables and set the values you specity.
Hope you find this useful.
I developed a simple WinForms application, which gets its startup parameters from app.config file. That file is configured with values suitable for development and testing.
I created a setup project, and after much tweaking it installs the application. I can run from the installed exe, and it uses the myExe.exe.config file to get its parameters. I edited the file to changed the parameters, but the old parameters are still being used. I made sure the application directory, the executable and the config file all have full control for everyone. Not ideal I know, but to get it working...
Still, the program runs with the old parameters. UNLESS... I right-click on the executable and select Run As Administrator. Then, it uses the properly configured parameters.
Any ideas what is causing this behavior and how to fix? Thanks...
I have installed a C# Windows Service on Windows Server 2008. I installed it with InstallUtil. The service reads some data from the app.config file and it is doing it fine. Can you tell me where this file is located after installing the service?
I have been looking for hours but can't find it.
You can verify the exact location of the installed Windows Service by following the steps below:
Bring up the list of Windows Services by clicking the "Services" icon under the "Administrative Tools" icon. You can also get this list by typing "View local services" in the Search Menu under the Start Menu.
Select your Windows service in the list of installed services, right-click and then select Properties. You can also double click on row representing the service.
Locate the "Path to executable" value on the Properties dialog box. The value will include any command line parameters.
Open the folder in which the service executable resides.
If the Windows service has been built with .NET Framework, its configuration will be stored in the corresponding .config file, i.e., the name of the executable suffixed by ".config", e.g., if the name of the executable is "XyzService.exe", then the name of the .config file will be "XyzService.exe.config".
A couple of things to note:
If you installed the service after building it on the same machine using say, Visual Studio, then Visual Studio would have transformed the App.config file from the project and placed it in the build output folder automatically (and renamed it appropriately using the above naming convention).
If your machine is set to hide file extensions in Windows Explorer, you will see 2 files "XyzService" and "XyzService.exe". In this case, the "XyzService.exe" is your config file. If you then switch off the option to hide file extenions in Windows Explorer, you will then begin to see "XyzService.exe" and "XyzService.exe.config".
If you cannot find a corresponding .exe.config file, then it is possible that the code within the service is falling back to default values. In this case, you can place a properly named and formatted config file alongside the service executable and then restart the service and everything should be fine.
According to Microsoft
For client executables, the application configuration file resides in
the same directory as the application's executable and has the same
base name as the executable with a .config extension.
Note, if your exe is called appname.exe, and you have Windows explorer set to hide extensions, then your application will display as appname and your config file then it will be displayed as appname.exe (even though the true name is appname.exe.config)
As others have pointed out, InstallUtil doesn't do anything with the config file and it should have copied to the server in the same manner as the exe itself.
It is the same location from where you have registered service using installutil tool.
The App.config is likely called {ProjectName}.exe.config given the fact that it is a Windows Service. Check to see if that file exists and is what you are looking for.
The same place where your application (Windows Service) is.
Check it out, if it's not there place it in the same directory as of service.
If you have a live environment (and from your question it seems like you do), you can check what's actually happening using the superior Process Monitor utility. But usually the .config fileis located right next to the .exe, and named the same.
I have added App.Config in my project.
I have a installer class(ProjectInstaller.cs) which needs to read values from App.config.
I am providing the keys .
Below is the sample Code :
ConfigurationManager.AppSettings["CONFIG_FILE"]
I am getting null values as per above code ,when invoked in Installer class.
But in App.Config file the value for the above key exists.
Try:
public string GetServiceNameAppConfig(string serviceName)
{
var config = ConfigurationManager.OpenExeConfiguration(Assembly.GetAssembly(typeof(MyServiceInstaller)).Location);
return config.AppSettings.Settings[serviceName].Value;
}
Google helps: http://social.msdn.microsoft.com/Forums/ar/winformssetup/thread/896e110e-692d-4934-b120-ecc99b01c562
the point is that your installer is NOT running as exe alone and an app.config called whatever you imagine will not be loaded by default as the exe running your installer is InstallUtil.exe and it would eventually search appSettings from the file InstallUtil.exe.config which is not yours and is not what you want, read the following and check the links...
If you invoke it through InstallUtil then the configuration file is
defined as InstallUtil.exe.config which is not what you want. You
could manually load the config file using Configuration but it will
probably be a little messy
The trick is in the execution context of the installer classes. If you
install your app using InstallUtil all code will be executed in the
same process as InstallUtil.exe. If you need to pass some data to the
Installer class during deployment you should use install parameters.
They are passed to the installer during execution of Install, Commit,
Rollback and Uninstall methods by the execution enviroment
(installutil, windows instller...). You can access there parameters
using InstallContex property ot the installer class.
There is a excellent artiicle on CodeProject regarding Setup projects
and parameters:
http://www.codeproject.com/dotnet/SetupAndDeployment.asp
Check out
http://msdn2.microsoft.com/en-us/library/system.configuration.install.installcontext.aspx
Davide Piras explained very well, why you can't use your app.config and suggests to pass your values as parameters.
I found a nice and helpful article on how to pass parameters to the installutil.exe and use them in the serviceInstaller or projectInstaller:
Part 1: Using Parameters with InstallUtil
Part 2: Configuring Windows Services with Parameters from InstallUtil
It explains very shortly how to pass arguments and how to read them.
For me the easiest solution was to create InstallUtil.exe.config file, and fill it up with content from application config file. Service installer successfully read from this config file.
I created my service by following steps described in: Host a WCF Service in a Managed Windows Service