Friday, September 11, 2015

WPF/Prism using C# strings in Xaml to name Regions

The MVVM design pattern in WPF is often implemented using Prism. In such an implication Views (UserControls) are mapped to regions within WPF pages. Each region is named and all too often said region name is hard coded -- bad programming style.  An example of such is the following excerpt from Shell.xaml where the Prism region is specified as follows:
prsm:RegionManager.RegionName="MainRegion"

The entire Shell.xaml is as follows with the region control noted in boldface:

<Window x:Class=Desktop.Shell"        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc=
   "http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:prsm="http://www.codeplex.com/prism"
        xmlns:local="clr-namespace:Desktop"              
        mc:Ignorable="d"
        Title="Any Title Here" Height="350" Width="525"
        TextBlock.TextAlignment="Center">
    <Grid>
        <ContentControl Name="MainRegion" 
             prsm:RegionManager.RegionName="MainRegion" />
    </Grid>
</Window>

A static resource can be used WPF page's title or the content of a TextBlock so why not in the RegionName of a Prism region? Below is the same Shell.xaml modified to reference a C# namespace/assembly and then using the namespace to access a string constant that specifies the region name:

<Window x:Class=Desktop.Shell"        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc=
   "http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:prsm="http://www.codeplex.com/prism"
        xmlns:local="clr-namespace:Desktop" 
        xmlns:infrastructure=
"clr-namespace:ViewModels.Infrastructure;assembly=ViewModels"             mc:Ignorable="d"
        Title="Any Title Here" Height="350" Width="525"
        TextBlock.TextAlignment="Center">
    <Grid>
        <ContentControl Name="MainRegion" 
            prsm:RegionManager.RegionName=
              "{x:Static infrastructure:Regions.MainRegion}" />
    </Grid>
</Window>

The C# variable bound to the RegionName is as follows (in boldface):

namespace ViewModels.Infrastructure
{
    public static class Regions
    {
        public const string MainRegion = "MainRegion";

Just because Xaml is markup language and not C# does not mean we should abandon good programming practices. 



Wednesday, September 9, 2015

Using Windows Scheduler and NET START/STOP to Restart a Windows Service

I recently encountered a client with a Windows Service that was slowly leaking memory. The Windows Service was developed in .NET (hence managed code) but it used third-party, unmanaged C++ DLLs from another vendor and hence the leak. There was no way to upgrade to the latest version of the vendor's unmanaged DLLs so the simply way to address the memory leak was to restart the service every three days.

.NET provides a method so an application can stop a Windows service and a method so an application can start a Windows service. .NET does not provide a Windows service restart method. A Windows service can stop itself but once stopped it does not have a means by which to start itself. Specifically the ServiceController class (assembly: System.ServiceProcess.dll, namespace: System.ServiceProcess) can manage a specific Windows Service and this class contains a Start and Stop method.

Since the service cannot restart itself, Windows Scheduler will be used to run a task that stops and starts the service, hence a Windows service restart. The command used to stop and start the server will be NET STOP and NET START respectively. Windows Scheduler will also be configured to run the task every three days.

For demonstration purposes, the Windows service acted against will be "SQL Server VSS Writer". This service was selected because it is only used when a SQL Server database is backup and and a backup device takes a backup snapshot (hence not used on my development machine). The SQL Server VSS Writer is show below where the service is started and set to automatically start:


NET START/STOP

To start the service using NET START:
1) Run a console window as an administrator:

1.1) In the Windows 10 search field enter CMD (this step will differ depending on the version of Windows you are running) and right click on "Command Prompt" and select "Run as administrator":



1.2) In the Windows 7 search field enter CMD and right click on "cmd.exe" and select "Run as administrator":



2) In order to stop the Windows service, NET STOP will be invoked.In the Console Window enter NET STOP <windows service name> which for the SQL Server VSS Writer service is as follows:



3) When NET STOP is run successfully it behaves as follows:



Starting a service is simply a matter of invoking NET START <windows service name>:



To make creating a restart task simpler, create a batch file names LeakRestart.bat and include the following two commands:


Windows Scheduler

To run Windows Task Schedule type "Task" in the Windows' search field select "Task Scheduler" or to be exact type in Taskschd.msc and select "Task Scheduler":



Additionally Task Scheduler can be reached from  Control Panel | System and Maintenance | Administrative Tools | Task Scheduler.

Task Scheduler appears as follows:



In order to create a task, click on "Create Task" from the Actions panel:


The Windows service needs to restart even if a user is not logged in. Additionally, administrative privileges are required to stop and start a service.

The user "When running the task" should be changed to a local local administrators as opposed to an active user. In order to change the user account for the task click on "Change User or Group..."



Select Object Types which displays:


In the Object Types dialog insure that only Groups is checked the click on OK which closes the "Object Types" dialog and displays the Select User or Group dialog but with "Object Types" set to Groups (see below):



From the "Select User or Group" dialog select the "Advanced" button:


The "Advanced" button displays the "Select User or Group" with advanced options showing. Click on the "Find Now" button:


Notice the local Administrators group was selected for the local machine. This is the local Administrators group because the "In Folder" column is the local machine as opposed to the domain. The selected group is the exact group we want to select. Click on "OK" which displays the less-advanced version of "the Select User or Group" dialog:



Click on OK to close the dialog thus returning to the "Create Task" dialog:



Notice in the previous screenshot that the "use the following account' is set to BUILTIN\Administrators.

Under the General tab enter:

  • Name: set value to Restart to Handle Leak
  • Checked "Run with highest privileges.

  • Under Configure for set to the current operating system (which for this example is Windows 7)

The final Create Task | General tab screen shout appear as follows:


Note: the task will fail if "Run with highest priveleges" is not set.

Select the Triggers tab from the Create Task dialog:



Select the New button to create a trigger that will run the task once every three days:


In order to create a once every three days trigger select "Daily" under "Settings":


Set "Recur every" to 3 day. Note the "Start" date/time can specified to a convenient start date and a time when it is appropriate to restart the Windows service.

Once the recurrence has be specified to Daily, recur every 3 days, click on "OK" from the New Trigger dialog which closes the New Trigger dialog:


Closing the New Trigger dialog displays the "Create Task" dialog again which now contains the newly created trigger (see below):



Select the Actions tab which is where we will specify the action which invokes the LeakRestart.bat batch file. The Create Task | Actions tab is as follows:



Click on New to create a new action via the New Action dialog:



Select "Browse..." which displays the Open dialog and from this dialog (see below) navigate to the location of the LeakRestart.bat batch file and select said file:


Click on Open closes the Open dialog return to the Create Task | Actions screen which now includes the newly created Action:




Click OK to create the task return to Create Task | Actions:

Testing

It is possible to run a task manually from Window Scheduler.in order to test of said task works. The "gotch ya" with this task is that is stops and restarts a Windows service so simply running the task does not show a change. In order to test, the batch file should be tweaked to only stop the Windows service (for testing purposes only) because this will verify if the batch file can run and has the correct permissions, etc.

The steps to test are:

1) Edit the LeakRestart.bat such that it only stops the Windows service and then save this batch file. This edit is simply a matter of placing a REM in front of the NET START command as follows:

NET STOP "SQL Server VSS Writer"
REM NET START "SQL Server VSS Writer"

2) Open Windows Scheduler and under "Task Scheduler (Local)"select "Task Scheduler Library":


Notice that the "Restart to Handle Leak" task is listed.

3) Right click on the "Restart to Handle Leak" task and select Run:



4) To verify that the task execute successfully check the Control Panel | System and Security | Administrative Tools | Services. The Windows Service should be stopped:



Notice in the Service windows that the selected Windows Service is not "Started" hence it is stopped.

In order to restore normal behavior post testing:

1) Started the Windows service from the Services dialog.

2) Remote the REM before NET START from LeakRestart.bat. The correct version of the batch file is as follows:

NET STOP "SQL Server VSS Writer"
NET START "SQL Server VSS Writer"

Monday, September 7, 2015

WPF/XAML: Specifying a Namespace in a Referenced Assembly

In the previous post it was shown how to create a class library that contained a publicly accessible resource file: Visual Studio: Making a Resource File (resx) Public Versus Internal (default), The premise of the previous post was to share a class library (assembly) containing resources with multiple projects. In order for a WPF page to make use of the resources from another assembly, the page's XAML file must contain a reference to the namespace of the assembly containing resource file.

The solution used in the previous blog posting is as follows where the below screenshot is from Visual Studio 2015's Solution Explorer:



The WPF application's MainWindow.xaml page is as follows:

<Window x:Class="EpicWPFApplication.MainWindow"
  xmlns=
      "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc=
    "http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:local="clr-namespace:EpicWPFApplication"
  xmlns:epicclasslib=
    "clr-namespace:EpicClassLibrary;assembly=EpicClassLibrary"
  mc:Ignorable="d"
  Title="MainWindow" Height="350" Width="525">
  <Grid Height="319" VerticalAlignment="Bottom">

    <TextBlock Text="{x:Static epicclasslib:ResourcesThatWouldBeSwellToShare.MotherlyAdvice}" 
                   HorizontalAlignment="Center" 
                   VerticalAlignment="Center"/>

  </Grid>
</Window>

In the previous code snippet the top line in bold face (shown below) specifies EpicClassLibrary assembly and the EpicClassLibrary assembly:

xmlns:epicclasslib=
  "clr-namespace:EpicClassLibrary;assembly=EpicClassLibrary"

A TextBox control in the application XAML makes use of a text resource exposed by EpicClassLibrary:

    <TextBlock Text="{x:Static epicclasslib:ResourcesThatWouldBeSwellToShare.MotherlyAdvice}" 
                   HorizontalAlignment="Center" 
                   VerticalAlignment="Center"/>

When run the WPF applications, it displays the following:


Sunday, September 6, 2015

Visual Studio: Making a Resource File (resx) Public Versus Internal (default)

I was developing a project that was a WPF desktop application which included various resources (text, icons, images, etc.). I knew that there was potential to develop another application that could also make use of the resources. The issue is that by default the class generated by a resource file is created with the access modifier set to internal.  Setting the class to be internal means that only files within the same assembly can see the data type defined by the class.

The project was organized as follows:



The EpicApplicationMaster solution contains two projects:
  • EpicClassLibrary: a class library (a.k.a. an assembly) to potentially be shared by multiple applications.
    • This class library contains a resource file named, ResourcesThatWouldBeSwellToShare.resx
  • EpicWPFApplication: a stock WPF application that references the EpicClassLibrary project.

The previous screenshot demonstrates two resources (ScaryPurpleDinosaur and MotherlyAdvice) that could be shared with other applications.

Within Solution Explorer it is possible to double click on the C# file, ResourcesThatWouldBeSwellToShare.designer.cs, associated with resource file, ResourcesThatWouldBeSwellToShare.resx. This reveals the C# code generated based on the resources defined:



The class defined in the previous image is specified as internal (internal class ResourcesThatWouldBeSwellToShare) which means the WPF project does not have access to any of the defined resources.

The ResourcesThatWouldBeSwellToShare.designer.cs is generated by a custom build tool that takes as input the resource file, ResourcesThatWouldBeSwellToShare.resx, and generates the corresponding C# code. To see this tool right click on ResourcesThatWouldBeSwellToShare.resx from within Solution Explorer and select the Properties item from the context menu:



Selecting Properties from the ResourcesThatWouldBeSwellToShare.resx context menu displays the following:



The value for the "Custom Tool" property is ResXFileCodeGenerator which is the tool responsible for generating the C# class with the internal access modifier. There is no way to pass a command-line value to ResXFileCodeGenerator in order to tell it to generate public as the class' access modifier.

To create a resource files with the class generated contains public as the access modifier replace the ResXFileCodeGenerator custom tool with PublicResXFileCodeGenerator:



The name of PublicResXFileCodeGenerator indicates that it creates public rather than internal classes from resx files. Building the code following the aforementioned change generates a public class:




Saturday, August 22, 2015

Visual Studio 2015: Error Adding "Shared Project" to Solution

This entry presents an error I encountered and how I solved it.

My standard development Virtual Machine (as of August 2015) is Windows 10 Enterprise and Visual Studio 2015 Professional. As part of a desktop project (a solution containing a WPF project), I attempted to add a project of type "Shared Project" and received the following error dialog:



The text of the error is as follows:
The imported project "C:\Program Files (x86)\MSBuild\Microsoft\WindowsXaml\v14.0\8.1\Microsoft.Windows.UI.Xaml.CSharp.targets" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.C:\Program Files (x86)\MSBuild\Microsoft\WindowsXaml\v14.0\Microsoft.Windows.UI.Xaml.CSharp.targets


Notie the 8.1 in the previous path meaning Visual Studio 2015 is categorizing the Shared Project as Windows 8.1.

I found a stackoverflow.com article that pointed to the following as a way to solve the problem and it looked promising: Why can't I create Shared Project in Visual Studio 2015? and the answer that caught my attention is as follows:


To clarify what Mr. Kondrasovas is recommending, look at the following example found under the New Project dialog (from Visual Studio, File | New | Project) under Templates | Visual C# | Windows | Windows 8:


In a perfect world where developers "grow apple trees and honey bees and snow-white turtle doves" this would have solved the problem by on my machine the following error was displayed:



Then I thought back to the Visual Studio 2015 install process. I had selected the default instalation. There must be a set of Windows 8.1 categorized objects that did not install.

I was coding a desktop application that could run on Windows 8.1 or Windows 10. There was nothing that tied my project to only run on Windows 8.1. So Microsoft's categorization is odd.

So under Control Panel | Programs | Uninstall a program, I selected Visual Studio 2015. Most savvy Windows users realize to add features to a previously installed program to select "Uninstall a program" in order to display the "Uninstall or change a program" dialog as follows:


Clicking on "Microsoft Visual Studio Professional" display the following dialog:


To add the missing project templates, select "Modify" which displays:


Rather than guess at what items to check to remedy the situation, I clicked on "Select All":


Since the whole nine yards was selected, I clicked on Next:


After this I was able to create and add a Shared Project to a solution.

Revisiting the "New Project" dialog (from Visual Studio, File | New | Project) under Templates | Visual C# | Windows | Windows 8 the following is now displayed (not the missing templates have been installed):



The irony is that Shared Project template is not event listed as a Windows 8 template. The Shared Project template is founder under Templates | Visual C# | Windows: