Third Shelf

Automating a ClickOnce Deployment – Part3

Posted in team foundation server by Sydney du Plooy on March 20, 2010

Continuing the series on Automating a ClickOnce Deployment it’s time to implement the deployment scheme in a build file.

After the build file compiled the solution we are ready to start the copying of the installation files to the deployment server. Before we start the copy process we have to create a new directory for the new application version. In the following snippet I simply generate a new version number and then create a directory where we will copy the installation files to:

<Target Name="DeployApplication">
  <Message Text="Deploying the client application to $(PublishServer)" />
  <MakeDir Directories="$(DeploymentServer)\Published\App\$(VersionNumber)" />

We are ready to copy the application files to the server:

<Message Text="Copying files to deployment directory [$(DeploymentServer)\Published\App\$(VersionNumber)]" />
<Copy DestinationFiles="@(ApplicationFiles->'$(DeploymentServer)\Published\App\$(VersionNumber)\%(Filename)%(Extension)')" SourceFiles="@(ApplicationFiles->'$(BinariesFolder)\%(Filename)%(Extension)')" />

The ApplicationFiles is an item group with the installation files that we are copying. Note: we do not transform the filename to include the .deploy extension because we first need to generate the deployment manifest with the files in the right location before changing the name of the files. (See Gotcha #3). Next, we create the application manifest with the Mage utility which will be stored in the application directory (not in the version directory). Mage is a manifest generator and is used to generate both application and deployment manifests. In the snippet below we are using Mage explicitly to generate the application manifest:

<CreateProperty Value="$(DeploymentServer)\Published\App\$(VersionNumber)\App.exe.manifest">
  <Output TaskParameter="Value" PropertyName="ApplicationManifestFile"/>
</CreateProperty>
<Message Text="Generating application manifest  [$(ApplicationManifestFile)] using  [$(DeploymentServer)\Published\App\$(VersionNumber)]" />
<Exec Command="mage.exe -New  Application -p msil -TrustLevel FullTrust -ToFile  $(ApplicationManifestFile) -Name  &quot;App&quot; -Version $(VersionNumber) -FromDirectory  $(DeploymentServer)\Published\App\$(VersionNumber)" />

So far so good, we have now managed to generate the deployment manifest from the files located in the …\Published\App directory and placed the file in the …\Published\App\$(VersionNumber) directory. We will now point the application manifest to this deployment manifest so that the correct files are installed on the client machine.

We now have everything that we need in order to generate the application manifest.

<CreateItem Include="$(ApplicationManifestFile)" AdditionalMetadata="TargetPath=$(VersionNumber)\App.exe.manifest">
  <Output TaskParameter="Include" ItemName="RelativeApplicationManifestFile"/>
</CreateItem>

<GenerateDeploymentManifest AssemblyName="App.exe.application"
  AssemblyVersion="$(VersionNumber)"
  DeploymentUrl="http://deployment-server/Published/App/App.exe.application"
  Product="App"
  Description="App"
  Publisher="thirdshelf.com"
  Install="true"
  UpdateEnabled="true"
  UpdateMode="Foreground"
  OutputManifest="$(DeploymentServer)\Published\App\App.exe.application"
  MapFileExtensions="true"
  EntryPoint="@(RelativeApplicationManifestFile)" />

If you read part 2 of this series you should be able to map these properties straight to their positions in the application manifest file and their functions. The only thing that remains is to rename the files to include their .deploy extensions. This can easily be done in the following way:

<Message Text="Replacing files in [$(DeploymentServer)\Published\App\$(VersionNumber)\] with [.deploy]" />
<Delete Files="@(ApplicationFiles->'$(DeploymentServer)\Published\App\$(VersionNumber)\%(Filename)%(Extension)')" />
<Copy SourceFiles="@(ApplicationFiles->'$(BinariesFolder)\%(Filename)%(Extension)')
  DestinationFiles="@(ApplicationFiles->'$(DeploymentServer)\Published\App\$(VersionNumber)\%(Filename)%(Extension).deploy')" />

Visiting the URL http://deployment-server/Published/App/App.exe.manifest and clicking on the Install button you should see the following dialog:

Gotchas

There are a few gotchas that you might find on your way to success and I’ve listed the most common ones here and their solutions for your convenience:

Gotcha #1: Reference in the manifest does not match the identity of the downloaded assembly …

This is easily resolved by adding the NoWin32Manifest property to solution in the SolutionsToBuild group:

<SolutionToBuild Include="$(SolutionRoot)\Application.sln">
  <Properties>NoWin32Manifest=true</Properties>
</SolutionToBuild>

Gotcha #2: XML files are marked as data files, by default.

This means that XML files are published to the data directory whose location can found by querying the System.Deployment.Application.ApplicationDeployment.CurrentDeployment.DataDirectory  property.

Gotcha #3: The customHostSpecified attribute is not supported for Windows Forms applications.

Not a very helpful error, but chances are that you generated the deployment manifest with the installation files having the .deploy extension and not on the original filenames.

Tagged with: , ,

Automating a ClickOnce Deployment – Part2

Posted in team foundation server by Sydney du Plooy on March 20, 2010

Continuing the series on Automating a ClickOnce Deployment it’s time to have a look at the manifest files. There are two required manifest files that give clickonce the necessary information to deploy the application:

  • an application manifest (.application);
  • a deployment manifest (.manifest).

Application Manifest

The application manifest describes the application that we are going to install. So, let’s have a look at the contents of this file:

xml version="1.0" encoding="utf-8"?>
<asmv1:assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:co.v1="urn:schemas-microsoft-com:clickonce.v1" xmlns="urn:schemas-microsoft-com:asm.v2" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xrml="urn:mpeg:mpeg21:2003:01-REL-R-NS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <assemblyIdentity name="App.application" version="1.0.903.18476" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" xmlns="urn:schemas-microsoft-com:asm.v1" />
  asmv2:publisher="App Publishers Company" asmv2:product="App" xmlns="urn:schemas-microsoft-com:asm.v1">App
  mapFileExtensions="true">
    <subscription>
      <update>
        <beforeApplicationStartup />
      </update>
    </subscription>
    <deploymentProvider codebase="http://deployment-server/Published/App/app.exe.application" />
  </deployment>
  <dependency>
    <dependentAssembly dependencyType="install" codebase="1.0.903.18476\App.exe.manifest" size="33600">
      <assemblyIdentity name="App.exe" version="1.0.903.18476" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" type="win32" />
      <hash>
        <dsig:Transforms>
          <dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
        </dsig:Transforms>
        <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
        <dsig:DigestValue>DrLiyVjtlKhOmtuzuXhFSTqi/9Q=
      </hash>
    </dependentAssembly>
  </dependency>
</asmv1:assembly>

One of the important things to note in this manifest is the mapFileExtensions attribute in the deployment tag. This attribute indicates that our installation files have a .deploy extension – MyApplication.exe is copied to the deployment server as MyApplication.exe.deploy and is renamed when installed on the client machine. In the deploymentProvider tag we specify where the application can find future updates and from where this application was installed.

The next interesting tag is dependentAssembly, which points to the deployment manifest to use when installing the application on the client machine. We specify the deployment manifest in the codeBase attribute which describes how the application installs. Note that the codebase attribute points to a version folder in which the deployment manifest resides – for every new release of the application this application manifest is either overwritten or updated to point to the new version of the installation files and deployment manifest.

Deployment Manifest

The deployment manifest describes the how the application installs and what files to install on the client. Once again, let’s have a look at the contents:

<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns="urn:schemas-microsoft-com:asm.v2" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:co.v1="urn:schemas-microsoft-com:clickonce.v1">
  <asmv1:assemblyIdentity name="App.exe" version="1.0.903.18476" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" type="win32" />
  <application />
  <entryPoint>
    <assemblyIdentity name="App" version="1.0.903.18476" language="neutral" processorArchitecture="msil" />
    <commandLine file="App.exe" parameters="" />
  </entryPoint>
  <trustInfo>
    <security>
      <applicationRequestMinimum>
        <PermissionSet Unrestricted="true" ID="Custom" SameSite="site" />
        <defaultAssemblyRequest permissionSetReference="Custom" />
      </applicationRequestMinimum>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
        <requestedExecutionLevel level="asInvoker" uiAccess="false" />
      </requestedPrivileges>
    </security>
  </trustInfo>
  <dependency>
    <dependentOS>
      <osVersionInfo>
        <os majorVersion="5" minorVersion="1" buildNumber="0" servicePackMajor="0" />
      </osVersionInfo>
    </dependentOS>
  </dependency>
  <dependency>
    <dependentAssembly dependencyType="preRequisite" allowDelayedBinding="true">
      <assemblyIdentity name="Microsoft.Windows.CommonLanguageRuntime" version="2.0.50727.0" />
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="DevExpress.Data.v9.1.dll" size="855552">
      <assemblyIdentity name="App" version="9.1.5.0" publicKeyToken="B88D1754D700E49A" language="neutral" processorArchitecture="msil" />
      <hash>
        <dsig:Transforms>
          <dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
        </dsig:Transforms>
        <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
        <dsig:DigestValue>CXehkPHON56TeTj7ZWsy++QsymE=
      </hash>
    </dependentAssembly>
  </dependency>
  <file name="GlobalConfiguration.xml" size="21074">
    <hash>
      <dsig:Transforms>
        <dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
      </dsig:Transforms>
      <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
      <dsig:DigestValue>6hHtbL9F9sfgWmKLYrWRpYmILCE=
    </hash>
  </file>
</asmv1:assembly>

The first tag of interest here is the entryPoint tag that identifies the application to run once the installation completes. In order to start the application clickonce will execute the command with the parameters specified in the commandLine tag.

Every application has dependencies that must either be installed with the program or should have been installed. In the first dependency tag we see that the minimum operating system version required is Windows XP. In the second dependency tag we see that the dependencyType attribute instructs clickonce to verify that the .NET Framework 2.0 is already installed on the client.

In the third dependency tag we see that the dependencyType is install, which instructs clickonce to install this dependency on the client. Remember that the .deploy extension will automatically be removed during the installation process.

The next tag is a file tag that specifies a non-assembly file that is copied into the application installation directory. Note: by default when clickonce encounters a xml file it assumes it as a configuration file and will copy it to a data directory; along the application directory; instead of the application directory. You can find the data directory location by querying the System.Deployment.ApplicationApplicationDeployment.DataDirectory property.

In the next installment of this series we will show a build file example and generate a deployment manifest with the Mage tool.

Tagged with: , ,

Executing a task remotely with MsBuild

Posted in team foundation server by Sydney du Plooy on October 19, 2009

Recently, we were in need of executing a command on a remote server as part of our automated build process. For this we decided to make use of Windows Management Instrumentation (WMI). Before jumping to the code, copying and pasting it, there are always some security issues that need to be addressed. For this task there are also some service configurations that are required on the remote machine.

Note: All of the following settings are configured on the remote machine on which the command will be run remotely.

Security

Follow the steps below to set the WMI permissions for the build service account:

  1. Click on Start > Control Panel > Administrative Tools > Computer Management.
  2. Expand Service and Applications.
  3. Right-click on WMI Control and click on Properties.
  4. On the property dialog select the Security tab.
  5. Expand Root > CIMV2.
  6. Click on the Security button.
  7. Click on Add and add <TFSSERVICE> account.
  8. In the list of permissions, allow the following permissions:
    • Execute methods
    • Remote enable
  9. Click on OK.

Follow the step below to set the Local Security Policy for the build service account:

  1. Click on Start > Control Panel > Administrative Tools > Local Security Policy.
  2. Expand Local Policies > User Rights Assignment.
  3. Find and double-click on Log on as a service.
  4. Click on Add User or Group and add <TFSSERVICE> account.
  5. Click on OK.
  6. Find and double-click Log on as a batch job.
  7. Click on Add User or Group and add <TFSSERVICE> account.
  8. Click on OK and close the Local Security Policy window.

Services

Follow the steps below set the dependent services to automatically start:

  1. Click on Start > Control Panel > Administrative Tools.
  2. Double click on Services.
  3. Ensure that the following services are Started, and are set to start Automatic.
    • COM+ Event System
    • Remote Access Auto Connection Manager
    • Remote Access Connection Manager
    • Remote Procedure Call (RPC)
    • Remote Procedure Call (RPC) Locator
    • Remote Registry
    • Server
    • Windows Management Instrumentation
    • Windows Management Instrumentation Driver Extensions
    • WMI Performance Adapter
    • Workstation
  4. Close the services dialog.

Here is the code for the build task that will execute a command on a remote machine:

/// <summary>
/// Executes a command line on a remote machine.
/// </summary>
public class RemoteExec : Task
{
 /// <summary>
 /// Initialises a new instance of the <see cref="RemoteExec"/> class.
 /// </summary>
 public RemoteExec()
 {
   RemoteMachine = Environment.MachineName;
 }

 /// <summary>
 /// Gets or sets the name of the machine on which to execute the command.
 /// </summary>
 public string RemoteMachine
 {
   get;
   set;
 }

 /// <summary>
 /// Gets or sets the command to execute on the remote machine.
 /// </summary>
 [Required]
 public string Command
 {
   get;
   set;
 }

 /// <summary>
 /// Executes the task on the remote machine.
 /// </summary>
 /// <returns>true if the task succeeded, otherwise, false</returns>
 public override bool Execute()
 {
   if (string.IsNullOrEmpty(Command))
   {
     Log.LogError("Command property was not set.");
     throw new ArgumentNullException("Command");
   }

   var connOptions = new ConnectionOptions();
   connOptions.Impersonation = ImpersonationLevel.Impersonate;
   connOptions.EnablePrivileges = true;

   var managementScope = new ManagementScope(
     string.Format(@"\\{0}\ROOT\CIMV2", RemoteMachine), connOptions);
   managementScope.Connect();

   Log.LogMessage(string.Format(CultureInfo.CurrentCulture, "Connected to {0}: {1}",
   RemoteMachine, managementScope.IsConnected));

   if (!managementScope.IsConnected)
   return false;

   var objectGetOptions = new ObjectGetOptions();
   var managementPath = new ManagementPath("Win32_Process");
   var processClass = new ManagementClass(managementScope, managementPath, objectGetOptions);

   var processParameters = processClass.GetMethodParameters("Create");

   processParameters["CommandLine"] = Command;
   try
   {
     var outParams = processClass.InvokeMethod("Create", processParameters, null);
     var processID = Convert.ToUInt32(outParams["processId"]);

     Log.LogMessage(string.Format(CultureInfo.CurrentCulture,
       "Creation of the process {0} returned: {1}", processID, outParams["returnValue"]));

     var stopQuery = new WqlEventQuery(string.Format(CultureInfo.CurrentCulture,
       "select * from Win32_ProcessStopTrace where ProcessID={0}", processID));

     var processStopEvent = new ManagementEventWatcher(managementScope, stopQuery);
     processStopEvent.Options.Timeout = new TimeSpan(0, 1, 0);

     Log.LogMessage("Waiting for process to complete...");
     processStopEvent.WaitForNextEvent();
     processStopEvent.Stop();

     Log.LogMessage("Completed.");
   }
   catch (Exception ex)
   {
     Log.LogError(ex.Message);
   }

   return true;
   }
}

The task can then be used as follows:

<UsingTask TaskName="RemoteExec" AssemblyFile="BuildTasks.dll"/>
<RemoteExec RemoteMachine="$(Server)" Command="application.exe" />

There we have it, executing a command remotely from MsBuild!

Tagged with: ,

Creating JunctionPoints with MsBuild

Posted in team foundation server by Sydney du Plooy on October 10, 2009

With our build process we decided on a deployment folder structure that required us to create NTFS Junction Points. Junction Points are links to other folders, also known as soft links. Below is an example of how these links appear in Windows 7. It also shows our deployment folders for various build types:

JunctionPointFolderStructureAll the folders with shortcut arrows are junction points or links to folders in the Published folder. For each build, the binaries are copied to a build numbered folder in the Published folder. Only when a build is successful; meaning that all code compiled and unit tests passed; do we update the junction point to the relevant build numbered folder in Published.

By using the code written by Jeff Brown in this article, I was able to create a custom build task that enabled us to the create and delete junction points with relative ease. Here is a link to the source code for an MsBuild task that will enable you to manage junction points with MsBuild.

Tagged with: ,

Automating a ClickOnce Deployment – Part1

Posted in team foundation server by Sydney du Plooy on September 6, 2009

In our build process, using Team Foundation Build, we decided to use ClickOnce for application deployment. In this post I’ll try to set the lay of the land. In the following couple of posts I will take you through the process of setting up a clickonce deployment.

Choosing the deployment strategy

There are essentially two different ways to deploy clickonce applications. They are:

  1. Physical media such as CD or DVD
  2. Network share or web

Deciding on the strategy that will work for you is largely determined by the available bandwidth at the client site. If there is none to very little bandwidth, or no internet connectivity, choose CD / DVD. Otherwise choose Network share or web.

We opted for the web strategy and so I will base the series around this strategy.

Lay of the land

Assume that we have one build server and one deployment server. The build server will push the build artifacts to the deployment server from where clients will use clickonce to install the application. The application is pushed to the deployment server after every build with a new version number.

To make the explanations easier we will setup a hypothetical configuration and base the deployment configuration on it. Our hypothetical solution consists of a single application, which we will call App. Below is a diagram that demonstrates this configuration:

Note: To hide the published share simply name it with a trailing dollar sign i.e. “Published$”.

Initial configuration

First of all we need a folder on the deployment server to push the build artifacts to. For this, create a published folder on the deployment server and share it with modify permissions granted to the TFSSERVICE account. Remember the TFSSERVICE account that TFS uses for the build service?

Furthermore, we need to configure Internet Information Services (IIS) to interpret the clickonce files correctly according to their extension. For this, add the following MIME types in IIS:

.application    application/x-ms-application
.manifest       application/x-ms-manifest
.deploy         application/octet-stream

If you are using IIS 6.0, have a look at this article “IIS 6.0 Does Not Serve Unknown MIME Types”.

In the next installment I’ll discuss the different files that describe the application and deployment.

Tagged with: , ,

Team Foundation Server Build Notification

Posted in team foundation server by Sydney du Plooy on September 5, 2009

greentickIncluded in the Team Foundation Server Tools download is an absolute must-have, Team Foundation Build Notification. If you have are making use of continuous integration chances are that you are building often. Instead of keeping an eye out for the latest build report in your email or constantly checking it in Visual Studio, use this notification utility. It has little popups that appear when someone requested a build either via checkin or ad hoc, which is configurable. It has a notification icon in the system tray (one the few that I will tolerate) and changes whenever one of the builds succeeds, fails or starts. It gives immediate access to the build log which you would normally have to browse for. It is also able to monitor multiple builds. A definite win!

Tagged with: ,

XML Intellisense for NHibernate

Posted in .net, visual studio by Sydney du Plooy on May 16, 2009

nhibernate-intellisenseUsing NHibernate requires the writing of some tedious XML-based configuration mapping files. Remembering all the tags and attributes can sometimes be overwhelming. Thankfully the contributors decided to include intellisense hints, which are in nhibernate-configuration.xsd and nhibernate-mapping.xsd.

In order to have the hints available in Visual Studio 2008 copy the files into C:\Program Files\Microsoft Visual Studio 9.0\XML\Schemas and restart Visual Studio.

Personal Knowledge Management with a Wiki

Posted in books, gtd, management, productivity by Sydney du Plooy on April 7, 2009

In the excellent book Pragmatic Thinking and Learning: Refactor Your Wetware (Pragmatic Programmers) by Andy Hunt, he suggests that every programmer should have a personal wiki in order to manage knowledge effectively. A sort of exocortex. A place where you can keep ideas, thoughts and nearly anything you want outside your brain.

Ever received one of those emails that you just have to keep somewhere? That snippet of source code that might come in handy? That chocolate muffin recipe? Why not put all of it into your wiki?

My personal choice of this kind of wiki is TiddlyWiki. Simply because all of the content is in a single HTML file. Fan of Getting Things Done? TiddlyWiki can easily be configured to support the Getting Things Done methodology. Have a look at d-cubed for example.

Bear in mind that it has a learning curve to it, but if you are willing to stick to it you will surely reap the benefits.

Some of the features include:

  • Tagging
  • Searching
  • Text formatting, including support for monospace
  • Highlighting
  • Block quotes
  • Tables
  • Headers
  • Save with backups
  • RSS feeds

I would suggest the following plugins to really spice-up TiddlyWiki:

Check out TiddlyTools and TiddlyVault for other plugins. It supports themes, which can be downloaded from TiddlyThemes. There is also a great cheat sheat available.

Tagged with: , , ,

Rollback Changesets

Posted in team foundation server by Sydney du Plooy on March 12, 2009

In Team Foundation Server 2008, there doesn’t seem an easy way to simply perform a rollback on a comitted changeset. Well, that is until you discover the power of the updated Team Foundation Server Power Toys.

rollback-changeset

Rollback command

Before rolling back a changeset, the local workspace must have no pending changes. After issuing the rollback command, tfpt rollback a dialog will appear asking whether you want the local workspace brought up to date with the repository. After affirming the dialog, a get command will be issued for your workspace and updated with the latest changes.

After the get operation completed successfully, another dialog will appear asking you to identify the changeset that you want to rollback. Note: Make sure that you have the correct changeset before continuing.

Select the files that you want to rollback in the changeset and click on “Roll Back”. After the rollback completed, you might have to resolve some conflicting changes. Now that the rollback is complete, you will still need to commit the new changeset, which is the latest code, without the changeset that was rolled back.

If you want to skip all of the GUI stuff, then you can simply execute the rollback command this way: tfpt rollback /changeset:4018.

Tagged with: , ,

AWOL CD-ROM

Posted in windows by Sydney du Plooy on February 22, 2009

For the second time now I have had the CD-ROM drive on my HP nw8440 disappear from Windows. The Device Manager reports the following for the CD-ROM drive: “Windows cannot load the device driver for this hardware. The driver may be corrupted or missing. (Code 39)”. Why? It was perfect yesterday and today it is missing or corrupted? Weird. According to the Microsoft Knowledge Base article it can happen after removing a CD or DVD burning program.

Although the error is rather annoying, there is a simple fix for it. I have replicated the steps here for your convenience:

Step 1: Start Registry Editor

Start Registry Editor.

Step 2: Delete the UpperFilters registry entry

  1. In Registry Editor, expand My Computer, and then expand HKEY_LOCAL_MACHINE.
  2. Expand SYSTEM, and then expand CurrentControlSet.
  3. Expand Control, and then expand Class.
  4. Under Class, click {4D36E965-E325-11CE-BFC1-08002BE10318}.
  5. In the details pane of Registry Editor, on the right side, click UpperFilters.Note You may also see an UpperFilters.bak registry entry. You do not have to remove that entry. Click UpperFilters only.
    • If you see the UpperFilters registry entry in the details pane of Registry Editor, go to step 6.
    • If you do not see the UpperFilters registry entry, you still might have to remove the LowerFilters registry entry. To do this, go to “Step 3: Delete the LowerFilters registry entry.”
  6. On the Edit menu, click Delete.
  7. Click Yes when you receive the following message:

    Are you sure you want to delete this value?

The UpperFilters registry entry is removed.

Step 3: Delete the LowerFilters registry entry

  1. In the details pane of Registry Editor, on the right side, click LowerFilters.Note You might see a LowerFilters.bak registry entry. You do not have to remove that entry. Click LowerFilters only.

    If you do not see the LowerFilters registry entry, unfortunately this content is unable to help you any more. Go to the “Next Steps” section for information about how you can find more solutions or more help on the Microsoft Web site.

  2. On the Edit menu, click Delete.
  3. Click Yes when you receive the following message:

    Are you sure you want to delete this value?

    The LowerFilters registry entry is removed.

  4. Exit Registry Editor.

The article recomends restarting the computer after deleting the two registry entries, but I have found that it works fine without restarting.

Tagged with:
Follow

Get every new post delivered to your Inbox.

Join 144 other followers