Tuesday, December 8, 2015

WPF DataGrid MouseDoubleClick

After migrating all of my desktop app development from WinForms to WPF many years ago, I was often frustrated by moving from the DataGridView control to the DataGrid control. The old grid followed a classical model where you could easily enumerate rows and cells, and the events had quite predictable timing and behaviour. The DataGrid grid follows a binding model which enforces a different style of coding to customise behaviour and you need to understand how to use binding value converters effectively.

Double-clicking a cell in the WPF DataGrid seems to require some peculiar processing to determine which parent row was clicked so you can process the data item bound to the row. There are many conflicting samples to be found in web searches and some assume certain settings for the grid's cell selection mode. I believe the following handler code will work safely in all modes.

private void gridTasks_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
  var cell = FindParentControlType<DataGridCell>((DependencyObject)e.OriginalSource);
  if (cell != null)
  {
    var item = cell.DataContext as BoundClassType;
    if (item != null)
    {
      // You now have the data bound item instance
      // for the row containing the clicked cell
    }
  }
}

The FindParentControlType method is a real nuisance, and many versions of it can be found in samples online. Mine looks like this:

public static T FindParentControlType<T>(object source) where T : DependencyObject
{
  var dep = (DependencyObject)source;
  while ((dep != null) && (!(dep is T)))
  {
    dep = VisualTreeHelper.GetParent(dep);
  }
  return dep == null ? default(T) : (T)dep;
}

Wednesday, November 18, 2015

InternalsVisibleTo PublicKey

I had to write some tests which accessed internal members of an assembly. You just have to put this in the app project being tested:

[assembly: InternalsVisibleTo("TestAssemblyName, PublicKey=XXXXXXXX...")]

But after a couple of years I couldn't remember how to get the PublicKey value. After bumbling around for 15 minutes I suddenly remembered you do this with the sn.exe utility from a Visual Studio Tools command prompt.

sn -Tp TestAssemblyFileName

Then you'll get output like the following. Paste the public key hex digits into the attribute and correct the formatting.

Public key (hash algorithm: sha1):
0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67
871e773a8fde8938c81dd402ba65b9301d60593e96c492651e889cc13f1415ebb53fac1131ae0b
d333c5ee6021672d9718ea31a844bd0db0072f25d87dba6fc90ffd598ed4da35e44c398c454307
13e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c3
08055da9

Public key token is 39af3856bd364e35

If you want to get the hex digits in code you can do this:

var asm = Assembly.Loadfile("Path to assembly file");
var sn = asm.Evidence.OfType<System.Security.Policy.StrongName>().FirstOrDefault();
if (sn != null)
{
  Trace(sn.PublicKey.ToString());
}

Sunday, August 30, 2015

Global IIS Logging

Up until several years ago Visual Studio included a C++ sample project which was a wrapper around the C API that could intercept IIS Logging events. The internal plumbing of IIS has changed many times since then and the sample vanished, which was a nuisance, as I was using it to track hits on my web sites in real-time and show them in a grid which sat at the bottom corner of my server's screen.

A few years ago I looked for way of implementing my old logging feature again, but due to confusing documentation and lack of clear guidance on how to do this in managed code I gave up.

This weekend I decided to have another bash with a clear mind on how to intercept global IIS logging using C# instead of C/C++. I eventually stumbled over these steps, which I want to document for myself and anyone else who might be interested:
  1. Create a class library targeting Framework 3.5 (not higher).
  2. Set the project to sign the assembly with a strong name (using a SNK file).
  3. Write a class of whatever name you like that implements IHttpModule.
  4. In the Init method add a listener to the PostLogRequest event.
  5. In the event handler you can cast the sender to a HttpApplication which then gives you properties for the Request and Response, and from there you can get lots of useful information to log.
  6. Add the assembly to the GAC by either dropping it into C:\windows\assembly or using the gacutil utility.
  7. In IIS Manager at the machine level node open IIS Modules, click Add managed Modules and you should see your assembly listed in the pick list. Give it a display name and uncheck the option for "only managed requests" (mine was unchecked by default).
There are many events that you can listen to, including a pair called LogRequest and PostLogRequest. Using guesswork and instinct I decided to use the latter event, and when I ran a web search I found someone who was also using it, but they had this statement inside:

var app = (HttpApplication)sender;
if (app.Context.IsPostNotification)
{
  // Log here
}

Why they were doing this wasn't clear, but I decided to do the same due to lack of time. So in the logging code I get useful properties like the RawUrl, StatusCode, UserAgent, UserHostAddress and more, put them into an XML element string and broadcast the string as utf-8 bytes using UdpClient. I like this idea because the logging code is quite short and fast, and all it does is spew broadcasts out to any other apps who might like to listen and display what's happening.

I did in fact write a small WPF desktop app to listen to the broadcasts and show them in a nice grid. Because events can arrive in rapid bursts, be sure to use UdpClient BeginReceive to asynchronously process the events. Look for some samples on how to do this thread safely and efficiently without blocking.

Windows Server 2012

In September 2017 I tried to follow the steps above on a 2012 Server in an Azure VM and ran into problems. You can no longer open C:\Windows\assembly in Windows Explorer and simply drag-and-drop a managed DLL file into the GAC that way. I installed part of the Windows SDK in the hope that I could get a copy of gacutil.exe that way, but it was a mistaken waste of time because the file was not included. I had to use PowerShell in an obscure way as described on this page. After many hours of pondering I eventually guessed correctly that you have to use the Add Roles and Features Wizard to add .NET Extensibility 3.5 to IIS, otherwise your managed module will not appear in the Add list.

Tuesday, July 21, 2015

C# XML DOC include

I wrote some C# XML code documentation and neatly put it all in a separate XML file because it was referenced via <include> statements in template generated code. Why did only the <summary> appear and nothing else in the Sandcastle generated documentation? After a bit of swearing, I eventually noticed the tiny mistake of omitting the trailing /* from the path. A correct <include> statement might be like this:
/// <include file='codedoc.xml' path='MyApp/MyProject/MyClass/member[@name="MyMethod"]/*'/>

Notice the /* suffix. If you accidentally leave that off you only see the first child documentation element, which is probably the <summary>. Very easy to forget and not notice.

Wednesday, July 15, 2015

WCF Custom Headers and Silverlight

This post is now a part of computer history. See: Silverlight Death and Funeral


Sometime at least 5 years ago I needed to insert custom headers into outgoing messages from a WCF basic http client generated by slsvcutil.exe. The headers were expected on the server-side as proof that the Silverlight client was authenticated and trusted.

I spent many frustrating hours attempting to find a way of using custom Behaviors to automatically insert the headers. At the time I failed and assumed that behaviors were not supported by Silverlight clients. Instead, I used the technique of manually wrapping a context around the proxy to insert the headers. It's a cumbersome technique as you have to make the proxy call inside the using, which means a callback is needed from outside callers to keep the objects alive during the call. A skeleton of the technique looks like this:

// OLD-FASHIONED TECHNIQUE
private static void CallWrapper(DemoAppClient client, Action callback)
{
  using (OperationContextScope ocs = new OperationContextScope(client.Proxy.InnerChannel))
  {
    OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("name", "namespace", 123456));
    callback();
  }
}

One morning I suddenly decided I was sick of this ugly technique and went looking for a better way. I soon stumbled upon an article by Adil Mughal where he has minimal sample code that uses IEndpointBehavior and IClientMessageInspector to insert a custom header. This is exactly what I wanted to do all those years ago, but now I have no idea why I failed to discover how to use a behaviour back then. I'm not sure if it's because I was using older frameworks, or the documentation was unclear, or I was misdirected.

In any case, using a custom behavior is more elegant and the code is simpler because it avoids the need for a clumsy callback.


NOTE 24-Sep-2015


The article link above seems to be unreliable, so I have placed a copy of my custom behavior and custom inspector source code here:

http://www.orthogonal.com.au/computers/CustomBehavior.cs.txt

The behavior is added to the Silverlight client proxy by:

client = new ServiceClient(...);
client.Endpoint.Behaviors.Add(new CustomBehavior(new HeaderData(id, ticket)));


The HeaderData class is something I used to pass a set of useful information down through the behaviour into the inspector. You can replace it with whatever data your custom classes might need.

NOTE 18-Oct-2018


I forgot to explain how to safely extract the header information in the server-side.

var inheads = OperationContext.Current.IncomingMessageHeaders;
int index = inheads.FindHeader("name", "namespace");
if (index >= 0)
{
  T data = inheads.GetHeader("name", "namespace");
}

Note that 'behavior' is US spelling used in code. 'Behaviour' is AU and GB spelling.

Sunday, May 3, 2015

Gmail send Authentication Required

An app that sends email notifications to my Gmail account stopped working sometime in the previous months (I wondered why no emails were arriving!). SmtpClient Send was throwing:

The SMTP server requires a secure connection or the client was not authenticated. The server response was: 5.5.1 Authentication Required. Learn more at

As usual, 15 solid minutes of web search produces confused, conflicting and preposterous suggestions. Finally someone hinted that they had to change their password to something "stronger", I didn't think that was credible, but when I visited the Gmail Account settings link they mentioned I noticed a suspicious entry titled Access for less secure apps which was disabled. I haven't been to this screen for years, so they had added an extra security hurdle which I didn't know about. Setting this to Enabled allowed my app to send its email without any code changes.

Wednesday, April 29, 2015

Bulk Nuget updates

This article is really old and the instructions are unlikely to work correctly any more. There might be some newer and safer way of performing bulk NuGet updates across many projects, but maybe that's just too dangerous in general these days.

Every few months I need to bulk update all of my VS2015 projects to use the latest version of a certain package. Because I spent another 10 minutes stuffing around to get the correct syntax, I'm putting it in here so I won't forget again. The following command recursively loops down looking for *.sln files and runs a nuget update against them.

for /R %f in (*.sln) do nuget update "%f" -id packagename

If the solution's projects do not contain the package id you will get this for each project:

Updating 'project name'...
WARNING: Unable to find 'packagename'. Make sure they are specified in packages.config.

If the solution's projects do not use Nuget then you get:

Scanning for projects...
No projects found with packages.config.

If a solution's project is updated you get this without a following warning message:

Updating 'project name'...

The resulting output from processing many solutions and projects can be quite cluttered and difficult to read to see what really happened.

UPDATE NOTE -- December 2015

Solutions containing web projects don't seem to upgrade correctly using the technique described above. I found I had to open these solutions in Visual Studio and manually update the packages. I haven't diagnosed the details of this issue yet, so this is just a friendly warning.

Saturday, April 25, 2015

Batch build vdproj

As I mentioned in a different blog post, support for vdproj (Visual Studio setup) projects was removed in VS2010, then it quietly returned as an optional extension for VS2013. After resurrecting some old setup projects I decided I needed to build them in batch scripts. I remember several years struggling for an hour to find the correct syntax of the command required to batch build a vdproj file. It turns out you need something like this sample:

"<path>\devenv" foo.sln /Project setup/foo.vdproj /Build Debug

The arguments are fragile and you must specify the path of the sln file first, then /Project specifies the path to the vdproj file (which is a part of the solution), then other switches which are documented HERE. If you get anything slightly wrong then all you get is the devenv help display. However, this time, all I could get out of this command was:

ERROR: An error occurred while validating.  HRESULT = '8000000A'

For ages I thought it was my fault due to a syntax error or a version stuff up. Then I ran a web search and found it was a known problem. The fix/workaround is documented HERE. You have to add this DWORD value to the registry:

HKCU
  Software
    Microsoft
      VisualStudio
        14.0_Config
          MSBuild
            EnableOutOfProcBuild = 0x0

This dreadfully obscure change allows the devenv command to work correctly. I'm quite angry about this as it took me over an hour to defeat the problem and document it.
Update July 2017 -- This post was updated for more recent versions. All of the information above was confirmed using Visual Studio 2015.

Friday, April 24, 2015

Solution Open with ... batch build

I often want to right-click a Visual Studio solution file (.sln) in Windows Explorer to ensure that it builds cleanly. I don't want to bother manually launching Visual Studio for the same purpose. Here's my way of doing this.

Create a batch file like the following (mine is called buildsln.bat). This command is rather simple, you can make one as fancy as you need.

@echo off
"C:\Program Files (x86)\MSBuild\14.0\Bin\msbuild" "%1" /t:build /v:minimal
if errorlevel 1 pause

Import the following into the Registry (as an Administrator) to add an Open With menu selection when you right-click a .sln file.

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Orthogonal.buildsln]
@="Build SLN"

[HKEY_CLASSES_ROOT\Orthogonal.buildsln\Shell\Open\Command]
@="\"C:\\ProgramData\\Utility\\buildsln.bat\" \"%1\""

[HKEY_CLASSES_ROOT\.sln\OpenWithProgids]
"Orthogonal.buildsln"=""

Change the key name Orthogonal.buildsln to be whatever suits you. Set the first default key value to be the text that is displayed in the context menu. The next default key value is the path to the batch file.

Note: Windows 7 to 10

After upgrading my development PC from Windows 7 to 10 and recreating the registry values above, I found that the Open With menu did not have my new item. Even worse, the OpenWithProgids key for .sln has 4 values, but only 2 of them appear in the Open With menu. Despite extensive experiments, I haven't figured out yet how to get my extra context commands to appear.

XmlReader/Writer and XElement

When reading or writing large amounts of XML you can use the XmlReader and XmlWriter classes to stream the data and avoid holding it all in memory. These classes are unfortunately quite low-level and the code that uses them can get quite cluttered. The XmlReader can be particularly tricky to use, as you must inspect the names and types of the nodes as they are sequentially consumed by the reader. You may need to remember your depth and position in the incoming XML, which can result in verbose and fragile code.

A great way to produce shorter and more readable code for XML stream reading is to combine the XmlReader class with the XElement class that was introduced in Framework 3.5. Use XmlReader to process the "outer" elements, then use XElement to read "inner" chunks of XML so they can be conveniently processed using LINQ to XML. Say you had incoming XML like this:

<export>
  <rows>
    <row id="1">
      <name>Fred Smith</name>
      <hired>1998</hired>
    </row>
    :
    : huge numbers of row elements
    :
  </rows>
</export>

Use an XmlReader to sequentially consume the XML, then when you hit the <rows> element, loop over the child <row> elements and pull them into an XElement for processing.

var settings = new XmlReaderSettings() { IgnoreWhitespace = true };
using (var reader = XmlReader.Create("export.xml", settings))
{
  reader.ReadToFollowing("rows");
  reader.Read(); // forward to the first row
  while (reader.Name == "row")
  {
    var elem = (XElement)XElement.ReadFrom(reader);
    int id = (int)elem.Attribute("id");
    string name = (string)elem.Element("name");
    int? hired = (int?)elem.Element("hired");
    Console.WriteLine("We would now import id {0} name {1} hired {2}", id, name, hired);
  }
  reader.ReadEndElement(); // eat the </rows>
}

A real example may contain many more nested elements, but the principle is the same where smaller inner chunks of XML are read into an XElement for convenient processing.

Wednesday, April 1, 2015

Drop data from Windows

This is a reminder to myself about what data is available when you drag-drop Windows Explorer files or folders onto a WPF control. I used this code to intercept and dump the Drop event:

private void ListBox_Drop(object sender, DragEventArgs e)
{
  foreach (string format in e.Data.GetFormats())
  {
    Trace.Write(format.PadLeft(22) + " ");
    object o = e.Data.GetData(format);
    if (o is string[])
    {
      Trace.WriteLine(string.Join(" + ", (string[])o));
    }
    else if (o is MemoryStream)
    {
      var ms = (MemoryStream)o;
      byte[] buff = new byte[ms.Length];
      ms.Read(buff, 0, (int)ms.Length);
      Trace.WriteLine(string.Format("[{0}]{1}",
          buff.Length,
          BitConverter.ToString(buff, 0, Math.Min(1024, buff.Length)).Replace("-", "")));
    }
    else
    {
      Trace.WriteLine(string.Format("Other {0}", o));
    }
  }
}

The number of formats is unpredictable and seems to depend upon whether you drop one or more objects, or if they have long names or not. I dropped three long name files and found this data (truncated):

   Shell IDList Array [549]0300000014000000A7000000230100008F01000014001F50E04FD020EA…
UsingDefaultDragImage [4]01000000
        DragImageBits [36896]60000000600000003000000059000000BD0C05D000000000FFFFFFFF…
          DragContext [16]00000000010000000000000000000000
DragSourceHelperFlags [4]01000000
      InShellDragLoop [4]00000000
             FileDrop K:\dev_experiments\sk-security-tiers.nuspec + K:\dev_experiment…
            FileNameW K:\dev_experiments\sk-security-tiers.nuspec
             FileName K:\DEV_EX~1\SK-SEC~1.NUS
     IsShowingLayered [4]00000000
           DragWindow [4]10042200
     IsComputingImage [4]00000000
      DropDescription [1044]FFFFFFFF0000000000000000000000000000000000000000000000000…
      DisableDragText [4]01000000
        IsShowingText [4]00000000
    ComputedDragImage [4]BD0C05D0

Friday, March 27, 2015

Powershell execution policy

I only run Poweshell scripts a few times a year when they're needed for some particular task, such as clearing all Event Logs at once (which is handy). I always run the scripts inside the UI (PowerShell_ISE.exe) because whenever I run a script from a command prompt I get

Name cannot be loaded because running scripts is disabled on this system

I finally got so sick of this irritation that I had to find a way around it. The reason and answer finally appeared on TechNet About Execution Policies. More explanation can be found on Scott Sutherland's Page and Carlos Perez's Blog. It turns out that the default script execution policy is "Undefined" which equates to "Restricted", meaning you simply can't run scripts.

Imagine that ... it's so secure you can't run it. It's like making your car theft-proof by welding the doors and windows shut. Surely it would be a better default to use "RemoteSigned" as the default policy and to let standard operating system security oversee what a script can do. Anyway, do this from an elevated command prompt:

powershell Set-ExecutionPolicy -ExecutionPolicy RemoteSigned

This sets the policy for the local machine. Thereafter you can run scripts, subject to a more sensible policy.

Monday, March 16, 2015

Custom Actions and Uninstall

This is just a reminder and warning about the rare but infuriating problem where the uninstall events in a Visual Studio Setup Project custom action (CA) will not run. While experimenting with a simple Setup Project to test some CA code I was changing various project properties and various haphazard ways and installing and uninstalling over and over many times. At some point, the CA's uninstall events and method overrides stopped running. Not matter what properties I changed, I could not get the uninstall events to ever run again. I searched the web and found dozens of questions from people who had suffered a similar problem. All of the advice I found was either irrelevant or made no difference.

I remember this problem happened to me many years ago, and I think after hours of suffering I gave up and either restored the original project or recreated it.

This time, as a stubborn challenge, I spent hours trying to get the project working correctly again, but msiexec verbose logs revealed nothing useful and comparisons of the vdproj files was not possible because their structure changes unpredictably each time it's altered and saved. I simply could not get the CA uninstall events to run again and I could not figure out what had corrupted them.

I created a brand new Setup Project, set the properties and custom action code back the way they were in the original project, and it all worked perfectly.

Overall, I guess the lesson is to be careful with Setup Projects, back them up and remember what you change.

I have a love-hate relationship with Setup Projects. Once you get a feel for them and find where they've hidden the various common settings you need, a familiar friendly MSI installer can be created very easily. However, as soon as you find you are pushing them too hard, then it's time to switch over to something like WiX and be prepared to wade into a morass of XML. To paraphrase the old adage: WiX gives you complete control, but you have to control everything.

16-Mar-2015

P.S. Setup Project (*.vdproj file) support was withdraw with the release of Visual Studio 2010, but after sustained protest they were made available again in 2013 as an add-in.

Saturday, March 14, 2015

Registry secrets and permissions

I tried to convince a product manager that we must not store user passwords in the application's SQL Server database in any reversible form. Plain text is a suicidal risk, and encrypted passwords lead you into the murky world of dealing with secret keys that encrypt the passwords and trusting who has access to those keys. I believe that it is highly objectionable to store passwords in any reversible form, as you are then at risk from sloppy security implementations or corrupt people. What if you use the same valuable password for a magazine subscription and your Internet banking? What if your password is the name of your secret lover? (don’t laugh, this has happened). You don’t want your passwords revealed to anyone by any means.

It is preferable to store passwords as a one-way hash. In .NET you can use the Rfc2898DeriveBytes class. I like to assign a permanent and immutable Guid to users which can conveniently provide 16 bytes of salt for the hash.

For historical reasons I was forced to reversibly encrypt user passwords, so I had to play the game called ‘hide the secret’. This tricky issue is discussed in Chapter 70 of Keith Brown’s book The .NET Developer’s Guide to Windows Security, and in Chapter 9 of Michael Howard’s Writing Secure Code.

I eventually decided that C# code in a CA (Custom Action) in the Visual Studio 2015 Setup Project would put the secret in a registry key under HKLM and set ACLs on the key to allow read access only by the specific account that needed it, which in this case was NETWORK SERVICE.

I had never written code to set registry permission before, so I wrote a small test program to get the C# code correct before migrating it into the installer. It took a bit of fiddling around to get the desired access rule combinations, so I have made the test program public as a reminder to myself and possible help for anyone else who wants to do this sort of thing. See:

https://dev.azure.com/orthogonal/RegKeySecurity

You must run this test program as an Administrator. Then run regedit as an Administrator and you will see the keys created under HKLM and the child key containing the secret will have special non-inherited permissions. Run regedit as a normal user and you will be denied access to the child key.

32/64-Bit Issues

The first time I migrated my test code into the CA, installed the application and ran it, I received a null argument crash. I discovered that the CA had created the registry keys under HKLM\Wow6432Node, but the application was reading the keys from HKLM\SOFTWARE.

So the installer was writing to the 32-bit view of the registry but the application was reading from the 64-bit view of the registry. Changing the TargetPlatform property of the Setup Project from x32 to x64 has no effect on the installer's registry view.

Since I know that all installations will be on 64-bit Windows servers I can force the CA to write to the 64-bit view of the registry by replacing this:

var machinekey = Registry.LocalMachine

with

var machinekey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64)

Then at runtime the 'Any CPU' application running on 64-bit Windows will be able to read the secret data from the registry.

Friday, January 23, 2015

Web Site Folders and Visual Studio Find and Replace

For many years I have used Visual Studio for Find (Ctrl+Shift+F) and Replace (Ctrl+Shift+H) in files. I know there are countless utilities of different styles available to do this, but if you have Visual Studio then it conveniently provides all of the typical power options you would need (Regex, match case, sub-folders, file name patterns, history, etc).

A few years ago I used Visual Studio to search through one of my Expression Web site folders, but it said "No files were found to look in". For half an hour I thought I'd gone mad, but I eventually deduced that folders with the S (System) attribute on are ignored. Later I found the easily missed caveat at the top of this MSDN Find in Files page. I did an attrib -S foldername and this makes the contents visible to Visual Studio again. However, read on...

If your Web Site uses the option "Maintain the site using hidden metadata files" then it uses hidden _vti_xxx folders to maintain a kind of database of the site contents and it uses a hidden desktop.ini file to give the folder a custom icon which is referenced as path _vti_pvt\fpdbw.ico. The custom icon only appears when the folder has the S attribute on and the hidden _vti files and desktop.ini file are being used and are correct.

It's a nuisance that Web Site folders have a peculiar and fragile management technique and they prevent Visual Studio searches. It's also a nuisance that the Windows Explorer folder properties page doesn't let you change the S attribute, you have to use the attrib command.