WPF handle error/exception from app.config on app startup - wpf

I'm trying to capture error in the App.config file on application startup, but I'm not getting.
All global error events (as AppDomain.CurrentDomain.UnhandledException or Application.DispatcherUnhandledException) are not working to catch the incorret format App.Config file, even as the OnStartup method App.xaml, is not being called, the application crashes before.
Sample invalid app.config:
<configuration>
<configSections>
<section name="XXXX" type="TesteAssembly.MainSpace, TesteAssembly" />
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>
However, I found that if the App.config file is malformed, to create an instance of the Application class an exception is thrown. Thus, the only solution I found was to remove the App.xaml, create a class with the main method and manually start an instance of Application (App.xaml base class).
example:
[STAThread]
public static void Main(string[] args)
{
try
{
Application p = new Application();
p.StartupUri = new Uri("MainWindow.xaml", UriKind.Relative);
p.Run();
}
catch (Exception)
{
throw;
}
}
There is another solution to this situation?

Try manually loading your config like this to diagnose the problem. Put this code at the start of Main(). It is most likely that the section type name is wrong:
var configMap = new ExeConfigurationFileMap();
configMap.ExeConfigFilename = Assembly.GetExecutingAssembly().Location + ".config";
var config = ConfigurationManager.OpenMappedExeConfiguration(configMap, ConfigurationUserLevel.None);

Related

Where are the settings saved in a .NET 5 WinForms app?

In a .NET Framework WinForms project, there was an App.config file in the project, which was an XML file that contained a configSection that would reference a class in System.Configuration, and a section for the userSettings themselves, like so:
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561944e089">
<section name="MyAppName.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561944e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<userSettings>
<MyAppName.Properties.Settings>
<setting name="Test" serializeAs="String">
<value>Some Value</value>
</setting>
</MyAppName.Properties.Settings>
</userSettings>
And this created a file in the build folder with the app name plus .exe.config, as in MyAppName.exe.config.
But when I create a new WinForms project using .NET:
There is no App.config in the solution. I can edit the settings using the project properties:
And I can access these values, and update them using the same Properties object and methods:
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
textBox1.Text = Properties.Settings.Default.Test;
}
private void button1_Click(object sender, EventArgs e)
{
Properties.Settings.Default.Test = textBox1.Text;
Properties.Settings.Default.Save();
}
}
}
And everything seems to work, but when I examine the bin folder, there is no file that I can see for where the values are actually stored.
Where is .NET 5 storing the saved application settings if not in a file in the same folder as the application's exe?
User settings are stored in user.config file in the following path:
%userprofile%\appdata\local\<Application name>\<Application uri hash>\<Application version>
Application settings file are not created by default (unexpectedly), however if you create them manually beside the dll/exe file of your application, the configuration system respect to it. The file name should be <Application name>.dll.config. Pay attention to the file extension which is .dll.config.
You may want to take a look at the source code of the following classes:
LocalFileSettingsProvider (The default setting provider)
ClientSettingsStore
ConfigurationManagerInternal
ClientConfigurationPaths
At the time of writing this answer Application Settings for Windows Forms still doesn't have any entry for .NET 5 and redirects to 4.x documentations.
First of all, this is a known (to .NET team) issue: https://github.com/dotnet/project-system/issues/7772.
Secondly the issue and the solution are pretty much described in your question:
(before) ..there was an App.config file in the project,..
(now) There is no App.config in the solution...
Add the missing app.config and everything will work just like it did before.

Why cannot write log to files when WPF application publishing as a single file

I use Microsoft.Extensions.Hosting and NLog.Web.AspNetCore in WPF. The application run correctly with Debug and Release mode, But when I publish the app as a single file, I found File target does not work when fileName using relative path.
NLog version: 4.6.8
Platform: .NET Core 3
NLog config
<nlog>
<targets>
<default-wrapper xsi:type="BufferingWrapper" bufferSize="100"/>
<target xsi:type="File" name="file" fileName="logs/${level}-${shortdate}.log" encoding="utf-8"
layout="${longdate}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}" />
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="file" final="true"/>
</rules>
</nlog>
I use AddNLog to apply this configuration:
public App()
{
_host = new HostBuilder()
.ConfigureLogging(logBuilder =>
{
logBuilder.ClearProviders()
.SetMinimumLevel(LogLevel.Debug)
.AddNLog("NLog.config");
})
.ConfigureServices((hostContext, services) =>
{
//...
}).Build();
}
Show the MainWindow when application startup:
private void Application_Startup(object sender, StartupEventArgs e)
{
using var serviceScope = _host.Services.CreateScope();
var serviceProvider = serviceScope.ServiceProvider;
_logger = serviceProvider.GetRequiredService<ILogger<App>>();
SetupExceptionHandling();
MainWindow mainWindow = serviceProvider.GetRequiredService<MainWindow>();
mainWindow.Show();
_logger.LogInformation($"Application startup at {DateTime.Now} successfully");
}
Publishing as a single file and run it, the log of successful startup is not written to the file, But when i change fileName to an absolute path like /logs/${level}-${shortdate}.log or ${level}-${shortdate}.log, the log can be written.
I try to configure it in code:
var config = new LoggingConfiguration();
var file = new FileTarget("file")
{
FileName = "logs/${shortdate}-${level}.log",
Layout = "${longdate}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}"
};
config.AddRule(LogLevel.Info, LogLevel.Fatal, new BufferingTargetWrapper(file));
return config;
But the result is still the same.
Am I writing something wrong? thanks for your help.
NLog will automatically prefix relative fileName-path with the ${basedir}-layout. See also https://github.com/nlog/nlog/wiki/Basedir-Layout-Renderer
Sadly enough Microsoft decided not to fix AppDomain.BaseDirectory when doing Single File Publish in NetCore 3.1
https://github.com/dotnet/aspnetcore/issues/12621
https://github.com/dotnet/core-setup/issues/7491
The work-around is to explictly specify ${basedir:fixTempDir=true}:
<nlog>
<targets>
<default-wrapper xsi:type="BufferingWrapper" bufferSize="100"/>
<target xsi:type="File" name="file" fileName="${basedir:fixtempdir=true}/logs/${level}-${shortdate}.log" encoding="utf-8"
layout="${longdate}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}" />
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="file" final="true"/>
</rules>
</nlog>
Hopefully Microsoft will fix the illusion with NetCore5

Camel Blueprint Testing - how can I dynamically replace/proxy a remote service referenced in Blueprint in order to prevent use of the real service?

I have the following scenario:
I have an OSGI bundle that has a service reference defined in the blueprint XML that references an interface in a remote bundle, and a bean that uses one of the impl's methods to populate a Properties object.
Relevant snippet from Bundle #1's XML (the consumer):
...
<!-- other bean definitions, namespace stuff, etc -->
<!-- reference to the fetching service -->
<reference id="fetchingService" interface="company.path.to.fetching.bundle.FetchingService" />
<!-- bean to hold the actual Properties object: the getConfigProperties method is one of the overridden interface methods -->
<bean id="fetchedProperties" class="java.util.Properties" factory-ref="fetchingService" factory-method="getProperties" />
<camelContext id="contextThatNeedsProperties" xmlns="http://camel.apache.org/schema/blueprint">
<propertyPlaceholder id="properties" location="ref:fetchedProperties" />
...
<!-- the rest of the context stuff - routes and so on -->
</camelContext>
Remote Bundle's blueprint.xml:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:camel="http://camel.apache.org/schema/blueprint"
xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
xsi:schemaLocation="
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">
<cm:property-placeholder id="config-properties" persistent-id="company.path.configfetcher" />
<bean id="fetchingService" class="company.path.to.fetching.bundle.impl.FetchingServiceImpl" scope="singleton" init-method="createLoader" depends-on="config-properties">
<property name="environment" value="${environment}" />
<property name="pathToRestService" value="${restPath}" />
</bean>
<service ref="fetchingService" interface="company.path.to.fetching.bundle.FetchingService" />
<!-- END TESTING -->
</blueprint>
From Impl Class:
public synchronized Properties getProperties() {
if(!IS_RUNNING) {
// timer task that regularly calls the REST api to check for updates
timer.schedule(updateTimerTask, 0, pollInterval);
IS_RUNNING = true;
}
//Map<String, Properties> to return matching object if it's there
if(PROPERTIES_BY_KEY.containsKey(environment)) {
return PROPERTIES_BY_KEY.get(environment);
}
/* if nothing, return an empty Properties object - if this is the case, then whatever bundle is relying on these
* properties is going to fail and we'll see it in the logs
*/
return new Properties();
}
The issue:
I have a test class (extending CamelBlueprintTestSupport) and there are a lot of moving parts such that I can't really change the order of things. Unfortunately, the properties bean method gets called before the CamelContext is started. Not that big a deal because in the test environment there is no config file to read the necessary properties from so the retrieval fails and we get back an empty properties object [note: we're overriding the properties component with fakes since it's not that class being tested], but in a perfect world, I'd like to be able to do two things:
1) replace the service with a new Impl()
2) intercept calls to the getProperties method OR tie the bean to the new service so that the calls return the properties from the fake impl
Thoughts?
Edit #1:
Here's one of the things I'm doing as a workaround right now:
try {
ServiceReference sr = this.getBundleContext().getServiceReference(FetchingService.class);
if(sr != null) {
((FetchingServiceImpl)this.getBundleContext().getService(sr)).setEnvironment(env);
((FetchingServiceImpl)this.getBundleContext().getService(sr)).setPath(path);
}
} catch(Exception e) {
log.error("Error getting Fetching service: {}", e.getMessage());
}
The biggest problem here is that I have to wait until the createCamelContext is called for a BundleContext to exist; therefore, the getProperties call has already happened once. As I said, since in the testing environment no config for the FetchingService class exists to provide the environment and path strings, that first call will fail (resulting in an empty Properties object). The second time around, the code above has set the properties in the impl class and we're off to the races. This is not a question about something that isn't working. Rather, it is about a better, more elegant solution that can be applied in other scenarios.
Oh, and for clarification before anyone asks, the point of this service is so that we don't have to have a .cfg file for every OSGI bundle deployed to our Servicemix instance - this central service will go and fetch the configs that the other bundles need and the only .cfg file that need exist is for the Fetcher.
Other pertinent details:
Camel 2.13.2 - wish it was 2.14 because they've added more property-placeholder tools to that version that would probably make this easier
Servicemix - 5.3.1
Have you tried overriding CamelBlueprintTestSupport's addServicesOnStartup in your test (see "Adding services on startup" http://camel.apache.org/blueprint-testing.html)?
In your case something like:
#Override
protected void addServicesOnStartup(Map<String, KeyValueHolder<Object, Dictionary>> services) {
services.put(FetchingService.class.getName(), asService(new FetchServiceImpl(), null));
}

Winform application to write its messages in console

I am here with 2 proposals.. I have a windows application running.
First proposal is that I should have the messages written directly to a console(command prompt),even though it is not a console application.
Second option is that I should create a console application in which it should read the log file produced by the windows application and write the same to the console. Please note the windows application will be updating the log file in real time while it is running and I want the console app to read each and every updated messages in log at the very next moment itself..Is it possible??
Which will be feasible?? and how I can achieve that?
Fast responses are really appreciated..
Thanks...
And third approach - use inter process communication to listen winforms application events from console application. For example, you can use .NET Remoting, WCF, or MSMQ.
Thus you need to write log from your windows forms application, and receive same data in your console application, then you can take advantage of NLog logging framework, which can write logs both to files and MSMQ. Get NLog.dll and NLog.Extended.dll from Nuget.0 Configure two targets in NLog.config file:
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target xsi:type="MSMQ" name="msmq" queue=".\private$\CoolQueue"
useXmlEncoding="true" recoverable="true" createQueueIfNotExists="true"
layout="${longdate}|${level:uppercase=true}|${logger}|${message}"/>
<target xsi:type="File" name="file" fileName="logs/${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message}" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="msmq" />
<logger name="*" minlevel="Trace" writeTo="file" />
</rules>
</nlog>
Then obtain logger in your winforms application
private static Logger _logger = LogManager.GetCurrentClassLogger();
And use it to write log messages:
private void button1_Click(object sender, EventArgs e)
{
_logger.Debug("Click");
}
Now move to console application. You need to read messages from MSMQ queue, which are published by winforms application. Create queue and start listening:
string path = #".\private$\CoolQueue";
MessageQueue queue = MessageQueue.Exists(path) ?
new MessageQueue(path) :
MessageQueue.Create(path);
queue.Formatter = new XmlMessageFormatter(new[] { typeof(string) });
queue.ReceiveCompleted += ReceiveCompleted;
queue.BeginReceive();
Write messages to console when they are received:
static void ReceiveCompleted(object sender, ReceiveCompletedEventArgs e)
{
Console.WriteLine(e.Message.message.Body);
var queue = (MessageQueue)sender;
queue.BeginReceive();
}
If you want to use Remoting, take a look on Building a Basic .NET Remoting Application article.

how to use an external configuration file with WPF?

i would like to set up an external configuration file that I can store in a directory for my WPF app, not necessarily the directory of my exe when I create my program either.
I created an App.Config file and added System.Configuration to my assembly. My App.Config has:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings file="sd.config">
<add key="username" value="joesmith" />
</appSettings>
</configuration>
and my sd.config (external file) which is in the root of my project for now, has
<?xml version="1.0"?>
<appSettings>
<add key="username1" value="janedoe" />
</appSettings>
in my MainWindow cs class I used
string username = ConfigurationManager.AppSettings.Get("username1");
which returns a null string. when i just retrieve the username field from App.Config it works. What did i miss? Thanks so much!
See the documentation on ConfigurationManager:
The AppSettings property:
Gets the AppSettingsSection data for the current application's default
configuration.
You need to do a little extra work to get data that isn't in your application's default configuration file.
Instead of using the file= attribute, add a key to your <appSettings> that defines the location of the secondary config file, like so:
<add key="configFile" value="sd.config"/>
Then, in order to use ConfigurationManager to pull settings from the secondary config file, you need to use its OpenMappedExeConfiguration method, which should look a little something like this:
var map = new ExeConfigurationFileMap();
map.ExeConfigFilename = Path.Combine(
AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
ConfigurationManager.AppSettings["configFile"]
);
//Once you have a Configuration reference to the secondary config file,
//you can access its appSettings collection:
var config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
var userName1 = config.AppSettings["username1"];
That code might not be dead-on for your example, but hopefully it gets you on the right track!

Resources