Tuesday, November 04, 2008

This evening, I upgraded an installation of SQL Server Express 2005 to its 2008 counterpart because I have a few small websites that I've developed that rely on SQL User Instances.  The upgrade was smooth enough, but I was promptly and unexpectedly greeted with an error message that I had seen before:

Failed to generate a user instance of SQL Server due to a failure in starting the process for the user instance. The connection will be closed.

Well, in the past to address this issue I've had to 1) stop the SQL Express service, 2) delete the user's SQLEXPRESS directory, and 3) restart the service.  The user, in this case, is the account under which the User Instance is be created.

For instance, supposing that my user name were USERNAME, this directory would be C:\Documents and Settings\USERNAME\Local Settings\Application Data\Microsoft\Microsoft SQL Server Data\SQLEXPRESS.  Note, that on a Vista/Windows Server 2008 machine that path is addressable (due to some very ingenious reparse point (a.k.a. junction) mappings, the actual path is C:\Users\USERNAME\AppData\Local\Microsoft\Microsoft SQL Server Data\SQLEXPRESS.

In this particular instance, however, this wasn't working for me.  The reason was immediately obvious: my SQL Express service runs under the NETWORK SERVICE account, not USERNAME.  I verified that I could indeed host the User Instance by explicitly impersonating a priviliged user in the web.config file:

<identity impersonate="true" userName="MACHINE\Account" password="somethingSecure" />

However, when I set it back to not impersonate (as it was before the upgrade to SQL Server 2008) the User Instance would not start.

Ultimately, I did have to delete Network Service's SQLEXPRESS directory, but it's found in a completely different location:  %WINDIR%\ServiceProfiles\NetworkService\AppData\Local\Microsoft\Microsoft SQL Server Data\SQLEXPRESS.  Once I took care of that directory and restarted the SQL Services my site was off to the races.

Tuesday, November 04, 2008 4:00:03 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Sunday, October 26, 2008

I arrived to PDC 2008 (Professional Developers Conference) in Los Angeles, California yesterday and I can honestly say that the women have it lucky in one regard: no bathroom lines. :)

Today I had the opportunity to attend a great pre-conference session/workshop on Silverlight 2.0, presented by Jeff Prosise (pronounced Pro-sice) of Wintellect.

Having done s fair amount of Silverlight 1.0 development, I've been long awaiting the release of version 2.0 and with good reason.  Version 2.0 builds on a great platform and adds a slew of new capabilities.  These new features have long been touted, but I simply haven't had the chance to dive into them until RTM what with my day-to-day responsibilities.  These include some built-in support for Buttons, TextBoxes, ComboBoxes, ListBoxes, and Calendar controls to name just a few.  Additionally, you have one-time, one-way, and duplex databinding.  A nicely featured threading model and networking stack.

I'm looking forward to putting together a presentation of my own for the Utah .NET User Group on this very topic.  Stay tuned!

Sunday, October 26, 2008 8:26:17 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 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 [1]  |  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