Wednesday, October 08, 2008

When I awoke this morning I was greeted with a high priority email indicating that our CRM server down.  I promptly discovered that we were receiving a very descriptive "Invalid Action: The selected action was not valid" error message.

I quickly Googled the error message and found a single entry where the issue was disk space.  Well, it turns out that disk space wasn't the issue, but it prompted me to clean up the transaction logs.  That was a nice detour, but it didn't get me closer to solving the problem.

The next thing I checked was the system's Application Event Log.  Lo and behold, I found the following error message:

Source:   MSCRMKeyGenerator
Event ID: 18949

Current active key (KeyType : CrmWRPCTokenKey) is expired. This can indicate that a key is not being regenerated properly. Current Active Key : CrmKey(Id:c2e0c738-dc7a-dd11-b61e-00188b3466e9, ScaleGroupId:00000000-0000-0000-0000-000000000000, KeyType:CrmWRPCTokenKey, Expired:True, ValidOn:09/04/2008 23:50:21, ExpiresOn:10/07/2008 23:50:21, CreatedOn:09/04/2008 23:50:21, CreatedBy:NT AUTHORITY\NETWORK SERVICE. Key Setting :

Fortunately, this problem was easily solved by running services.msc and starting the Microsoft CRM Asynchronous Processing Service.

Wednesday, October 08, 2008 10:06:29 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, September 16, 2008

Tomorrow night I have the opportunity to speak at the monthly Utah County .NET User Group meeting.  Here's a synopsis of the talk:

Delving Into the WinForms Control Designer Experience

 

The .NET framework makes writing WinForms controls accessible to any and all programmers.  In this session we’re going to focus on the control’s design-time experience; that is, what users of your controls will experience when writing software using your controls.  There is a rich set of classes and tools that can help you create slick, professional components that will make your users’ lives easier, but without knowing what they are or where to find them it can be a challenge.  Even sometimes when armed with the knowledge, it’s not always intuitive how to use the objects.  We’re going to discuss Editors, Extensions, Verbs, Action Lists, Designers, and more.  We’ll see what it takes to have your control actually ‘run’ at design-time to offer a rich, interactive experience for the end user.

 

If you write WinForms controls for the .NET platform, you won’t want to miss it!

The meeting will be held at the NuSkin building at 1175 S 350 E in Provo.  I look forward to seeing you there!

Tuesday, September 16, 2008 11:36:12 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Sunday, September 07, 2008

When granting database permissions to built-in security accounts you need to be aware of potentially localized account names.

Just this past week I ran into an issue when attempting to execute a SQL Server database script on a German server OS.  The database script in question had hard-coded the usage of 'NT AUTHORITY\NETWORK SERVICE' as the account name for granting database permissions.  As it turns out, however, this account doesn't exist in the German version of Windows.  Instead, the account is 'NT-AUTORITÄT\NETZWERKDIENST'.  I found it interesting that the account name is not localized on other non-English versions of Windows.

Unless I misunderstanding something, the documentation indicates that "the name of the account in all locales is NT AUTHORITY\NETWORK SERVICE" so I was initially confused by this.

In this particular case my script file is, conveniently, not executed 100% verbatim.  Via a little script engine that I wrote, I have the ability to update variables with runtime values.  In this case, rather than use the exact string 'NT AUTHORITY\NETWORK SERVICE', I replaced it with a variable and determined the value to use at runtime.

Resolving to the properly localized account name is pretty easy:

private static string getNetworkServiceAcctName() {
   SecurityIdentifier id = new SecurityIdentifier(WellKnownSidType.NetworkServiceSid, null);
   NTAccount acct = ( NTAccount )id.Translate(typeof( NTAccount ));
   return acct.Value;
}

What's quite interesting is after researching the issue and updating the application to properly resolve the account name, we stumbled upon a blog post made a couple of years ago that resolved the exact same issue.

Sunday, September 07, 2008 6:08:42 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Monday, September 01, 2008

I ran across some interesting (read "unexpected") behavior the other day when writing a .NET WinForms control.  This particular control has some logic that executes if and only if the host OS has visual themes enabled.

In order to check for the existence of enabled themes, I call a method called 'IsVisualStylesEnabled()' which is defined as below:

public static bool IsVisualStylesEnabled() {
   bool isEnabled = false;
   try {
      OperatingSystem os = Environment.OSVersion;
      // decide whether the OS even supports visual styles
      bool isSupported = ( os.Platform == PlatformID.Win32NT )
                         && ( ( ( os.Version.Major == 5 ) && ( os.Version.Minor >= 1 ) )
                              || ( os.Version.Major > 5 ) );
      if ( isSupported ) {
         int majorDllVersion = getComCtlMajorVersion();
         isEnabled = ( majorDllVersion > 5 ) && IsThemeActive() && IsAppThemed();
      }
   }
   catch ( Exception ) {
      // do nothing explicitly
   }
   return isEnabled;
}


private static int getComCtlMajorVersion() {
   DLLVERSIONINFO dllVersion = new DLLVERSIONINFO { cbSize = Marshal.SizeOf(typeof( DLLVERSIONINFO )) };
   DllGetVersion(ref dllVersion);
   return dllVersion.dwMajorVersion;
}

 

[StructLayout(LayoutKind.Sequential)]
private struct DLLVERSIONINFO {
   public int cbSize;
   public int dwMajorVersion;
   public int dwMinorVersion;
   public int dwBuildNumber;
   public int dwPlatformID;
}

 

[DllImport("comctl32.dll", CharSet = CharSet.Auto)]
private static extern int DllGetVersion(ref DLLVERSIONINFO version);


[DllImport("uxtheme.dll", CharSet = CharSet.Auto)]
private static extern bool IsThemeActive();


[DllImport("uxtheme.dll", CharSet = CharSet.Auto)]
private static extern bool IsAppThemed();

This code is pretty standard.

At design-time (i.e., within Visual Studio) my control would render properly.  The problem, however, was evident when I executed the code - it would think that visual styles were disabled and degrade gracefully to the non-themed output.

After a little bit of debugging I discovered that the comctl32.dll version being loaded at design-time was version 6 (the desired version) whereas at runtime I was getting version 5.  The first thing I tried was embedding a manifest into my executable to explicitly load version 6.  No dice - I was still getting version 5.

Ultimately, however, I did discover the source of the issue: I was calling the IsVisualStylesEnabled() too early in the load process of my application.  This was made apparent by placing my control on a form other than the start-up form - everything worked!

When initialized, my control began setting up UI components such as brushes, pens, etc and some of this logic was based on whether the themes were enabled.  Most, if not all, of the setup logic occurs in the InitializeComponent() method which is called from the .ctor() of the control.

In order to guarantee that the proper version of comctl32.dll is loaded, you cannot call the GetDllVersion() method until at least the OnHandleCreated() method of your control.  If you call it earlier, you effectively lock your application (and consequently the host application if your application is a visual component) into using version 5 whether or not the manifest dictates otherwise.

For my purposes, I had to wait until the OnPaintBackground() method to initialize the brushes and other auxiliary objects, but that at least happens after the OnHandleCreated().

Monday, September 01, 2008 10:50:27 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, August 28, 2008

I've been putting together a nice little WinForms Wizard control recently that had a few twist and turns to its development.  In particular, I wanted to make the design-time experience a good one for the end user.  I wanted to support design-time navigation of the wizard pages, drag/drop of control to each child page, reordering of the pages, and much more.  In fact, as soon as I tidy up the code a bit and flesh it out a bit, I'll be posting the component here on my blog for general use.  But enough of that...

The wizard control has a rich design-time experience.  I owe some of the thanks for the functionality to control's ControlDesigner and DesignerActionList.  Among many other things, these classes provides a mechanism for a developer to associate custom verbs and actions with the control at design-time.  These actions are made available to the developer through the smart-tag menu when the control is selected on its design surface.

It is through these custom actions that I've given the programmer the ability to manage pages on the wizard.  Internally, these pages are represented not only as controls on the wizard itself, but also in a collection of pages that the wizard uses to properly navigate the sequence of pages.  It is this collection that gets persisted (serialized) in the form's InitializeComponent() method when saving changes made to the wizard at design-time.

I quickly discovered, however, that all wasn't green grass and blue skies.  I wanted to support reordering the pages through the design-time smart-tags.  While I was able to easily reorder the elements in the page collection and even update the control's display at design-time to reflect the changes, the Visual Studio environment didn't register the change.  Unless I then went ahead and further edited a property through Visual Studio itself (effectively to cause a change in the environment), the reordering of the pages never persisted.  So I had to figure out how to notify Visual Studio that a change had been made.

Fortunately, this isn't difficult, but it's not very intuitive at the same time.

The ControlDesigner's GetService() method provides access to the services available to the control at design time.  Among these is the IComponentChangeService.  It is through this service that you can notify the designer of a change.  To do so, you need a property to update.  I, for one, don't really like the idea of utilizing an existing property for this purpose (though it could be done).

What I did in my solution was create a design-time-only property in the ControlDesigner.  Then, when a change is made that requires the change notification I call a method which 'updates' the property.  The property doesn't really get updated, but at least the code is clearer.

In the control designer:

protected override void PreFilterProperties(IDictionary properties) {
   base.PreFilterProperties(properties);

   Attribute[] attribs = {
                            new BrowsableAttribute(false),
                            new DesignOnlyAttribute(true)
                         };
   PropertyDescriptor prop = TypeDescriptor.CreateProperty(GetType(), "DesignTimeChange", typeof(string), attribs);
   properties.Add("DesignTimeChange", prop);
}

/// <summary>
/// Property created exclusively for the purpose of notifying the
/// designer of changes to properties made through the designer
/// (such as reordering the pages).
/// </summary>
public string DesignTimeChange { get; set; }

Then, also in the Designer, a method that causes a change to be acknowledged:

private void notifyOfChange() {
   PropertyDescriptor prop = TypeDescriptor.GetProperties(this)["DesignTimeChange"];
   getComponentChangeService().OnComponentChanged(this, prop, null, null);
}

That's all there is to it.  When an action occurs in the designer that would not otherwise register a change in the environment, I simply have it call the notifyOfChange() method.

A little more work would be involved if the change were coming from outside the designer.  For instance, if the change was made within the collection or within the control, you'd first have to evaluate whether it's running in design mode, and then retrieve a reference to the ControlDesigner, invoking the method (it'd probably have to be made internal (and it's name appropriately Pascal-cased) rather than private).

Maybe something like this (NOTE: untested, but seemingly approximately accurate):

if ( DesignMode ) {
   IDesignerHost host = ( IDesignerHost )Site.Container;
   ControlDesigner designer = ( ControlDesigner )host.GetDesigner(this);
   designer.NotifyOfChange();
}

Thursday, August 28, 2008 6:47:20 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, August 26, 2008

Earlier this year I posted an article demonstrating how to use the SGEN.exe utility to generate a serialization assembly.  The article identified how to create a custom MSBuild task by editing the project file (e.g., csproj).  I've recently come to discover that the MSBuild task will fail when automating a build of the project through TFS Build.  The error presents itself as follows:

SGEN(0,0): error : File or assembly name 'C:....\bin\CustomLibrary.dll', or one of its dependencies, was not found.

Ultimately, the root of the problem goes back to the SGEN MSBuild task's usage of the $(OutputPath) variable.  Even in TFS Build this is pointing to same location that Visual Studio would point to.  TFS, however, will override the output locations of the compiled applications, but this variable doesn't get updated.  As stated in the MSDN documentation, "OutputPath has been deprecated and OutDir should be used instead whenever possible.".  I was happy to discover that by simply replacing the $(OutputPath) with $(OutDir) in the MSBuild task was all I needed to do; the task will execute properly both from my development machine as well as the build server.

My updated task resembles the following:

<Target Name="GenSerializationAssembly"
        DependsOnTargets="AssignTargetPaths;Compile;ResolveKeySource"
        Inputs="$(MSBuildAllProjects);@(IntermediateAssembly)"
        Outputs="$(OutDir)$(_SGenDllName)">
  <SGen BuildAssemblyName="$(TargetFileName)"
        BuildAssemblyPath="$(OutDir)"
        References="@(ReferencePath)"
        ShouldGenerateSerializer="true"
        UseProxyTypes="false"
        KeyContainer="$(KeyContainerName)"
        KeyFile="$(KeyOriginatorFile)"
        DelaySign="$(DelaySign)"
        ToolPath="$(SGenToolPath)">
    <Output TaskParameter="SerializationAssembly" ItemName="SerializationAssembly" />
  </SGen>
</Target>
<Target Name="AfterBuild" DependsOnTargets="GenSerializationAssembly" />

Tuesday, August 26, 2008 12:06:57 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, August 17, 2008

I've had an issue on two computers (my primary development machine and a virtual pc dev box) which I was finally able to solve today after many hours of frustrated searching and experimenting.  Interestingly, this issue only affected my two machines, but not those of a co-worker.  What was more peculiar was that I have higher privileges in TFS.  The issue revolved around getting the Team Foundation Server Team Explorer 2008 to recognize our automated builds.

Within Visual Studio 2008's Team Explorer pane I am able to browse all work items, documents, reports, etc, but not builds.  In fact, the node shows up with a red 'X' icon and is mislabeled 'Build' rather than the correct 'Builds'.

I attempted to fix it by uninstalling / reinstalling the TFS 2008 Power Tools (July Release), repairing my VS2008 installation, disabling my firewall, disabling my antivirus.  I tried digging into various configuration files and renaming / deleting my cache folder to no avail.

Ultimately, to fix the issue I had to resort to resetting my Visual Studio settings to their factory settings.  To do this, I did the following:

  1. Renamed/Deleted by TFS Cache folder.  On my Vista machine it's found in (C:\Users\[USERNAME]\AppData\Local\Microsoft\Team Foundation\2.0).
  2. Reset Visual Studio 2008 settings by typing devenv /resetuserdata from the command line.
Sunday, August 17, 2008 10:34:53 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Thursday, August 14, 2008

I upgraded my Visual Studio 2008 installation the other day to SP1 and everything went beautifully.  One of the issues that Microsoft fixed centered around the naming of embedded binary resource files.

Traditionally, binary resource files have an extension of .resources.  However, when VS2008 was introduced, it came with a bug that forced you to tack on a second extension: fileName.resources.resources.

Apparently, this is now fixed, but I did have to go back and rename my files accordingly.

Thursday, August 14, 2008 3:27:22 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback