Wednesday, September 30, 2015

Much needed improvements for Advanced Installer

If you have worked with Advanced Installer for any length of time, you may have come across a number of deficiencies in the product which makes it difficult to work with leaving you to come up with a number of workarounds to solve these limitations in the product.

Here are some of the improvements that are needed for Advanced Installer:

  1. String Editor similar to InstallShield - While Advanced Installer has a Translations section, it is not meant for editing and maintaining project strings in the exact same manner as InstallShield.
  2. Unified Property Grid similar to InstallShield - Property handling in Advanced Installer can be configured in a number of different places such as Install Parameters and the Property Dialog which can be accessed in a number of different areas throughout the IDE.  To have a centralized Property Grid would significantly improve the user experience while developing in Advanced Installer.
  3. Better handling of Path Variables - There are numerous areas in the IDE which still do not support Path Variables directly such as Prerequisites and Digital Signatures when running the Convert Paths wizard.  The Convert Paths wizard itself is quite unreliable, so it would be better to simply translate everything into Path Variables by default similar to the Path Variable support in InstallShield.
  4. Better handling of Synchronized Folders - Synchronized folders have been a pain point while using Advanced Installer for a number of users and is far more difficult to use than InstallShield's comparable dynamic file linking capability.  The requirement to "re-synchronize" the folder during development (rather than at build time) makes the whole process of using Synchronized Folders very clunky and a poor overall development experience.
  5. Better command line support/better API support - InstallShield clearly excels in this area by providing COMPLETE access to the underlying InstallShield API whereas the command line support provided by Advanced Installer is missing very large chunks of functionality making it very difficult if not impossible to automate the building of reasonably complex Advanced Installer packages. 
  6. XML File Testing - While Advanced Installer's handling of XML Files is considerably better than InstallShield's, InstallShield offers a way of testing the transformation of XML Files which is still not present even in the latest version of Advanced Installer.
  7. XML File Comparison - Once an XML File is imported into Advanced Installer, it is a complete manual process of comparing changes if the XML file has changed since it was last imported.  In order to simplify this process, the ability to compare the imported XML file with the original XML file should be available. 
  8. SQL Database validation - InstallShield allows targeting a specific database platform when installing to a database, however, Advanced Installer has still not implemented this functionality even in the latest release of Advanced Installer. 
  9. Wizard support - While Advanced Installer allows you to go through a Wizard when you initially create a project, it does not provide the Full Wizard experience offered by InstallShield.  InstallShield allows you to bounce back and forth between Wizard mode and the full version of the IDE to edit basic information quickly and easily.
  10. Cloning Dialogs - It is a very common requirement to be able to Clone/Duplicate Dialogs when developing an Installation User Interface and Advanced Installer does not have this capability making it a very tedious process to duplicate dialogs within the Advanced Installer IDE.
  11. Default dialog support - InstallShield offers default dialogs for Company Name information as well as a License Agreement dialog while Advanced Installer has the License Agreement dialog, but no Company Name dialog.  In addition, InstallShield ships a default License Agreement while Advanced Installer does not provide one by default.
  12. Reusable Dialogs - Advanced Installer provides the ability to export Dialogs to a Repository, but the Dialogs in the Repository can only be used AS IS!! There is no ability to customize the Dialogs that are added from the Repository, therefore, this limits the usefulness and reusability of creating a Repository of Dialogs.
  13. Property Picker for Dialogs - When attempting to enter a Property value for a Control, there is no Property Picker or Intellisense/autocompletion when entering values for Dialog values.  Instead, these have to be manually typed in hoping that there are no typos.
  14. Multiple SQLConnection Dialogs - InstallShield added support for multiple SQLConnection Dialogs a few releases ago, but Advanced Installer has yet to add this feature.  Advanced Installer still only allows a single SQLConnetion Dialog to be added by default (not based on the number of SQL Connections in the SQL Databases view).  Since there is also no ability to create reusable dialogs nor Clone Dialogs, this makes working with SQLConnection Dialogs that much more difficult in Advanced Installer.
  15. Dialog Events - When setting Properties for Dialog events, you cannot use Intellisense/autocompletion for the name of a Property and instead you are forced to use the Property Picker to select the name of a Property.
  16. Custom Actions - When using a Set Installer Property Custom Action, just as in the case of Dialog Events, you cannot use Intellisense/autocompletion and must manually choose/pick a Property name using the Property Picker.
  17. Clear out Recently Used Projects - Advanced Installer does not provide an option to clear out the Recently Used Projects from the Open menu.  Therefore, a history/record of all the Advanced Installer projects you have ever opened will continue to clutter your screen for as long as you have Advanced Installer installed on  your machine.
  18. Published Roadmap - While InstallShield does not also do this, many other companies (such as Microsoft) have a well published roadmap outlining the new and upcoming features for new releases of the product.
  19. User Community Feedback site - While Advanced Installer has a Forums section for providing community feedback, it does not compare to more sophisticated User Community Feedback sites such as UserVoice (which Microsoft heavily uses) for capturing user feedback and more importantly allowing users to vote on suggestions.  Based on the votes for a particular feedback suggestion, Microsoft incorporates these particular features into their planning and release cycles.  It would be great if Advanced Installer followed a similar path as what Microsoft and many other software companies/vendors are following to ensure that user feedback and suggestions are incorporated into their upcoming software releases based on overall user demand.  
  20. Online support ticket management - While this is not directly related to the Advanced Installer IDE, having an online support ticketing system is extremely useful to be able to track the status of any given request as well as see the history of all your support tickets and requests.  InstallShield support offers this as part of their support package and I sorely miss that capability while using Advanced Installer support.  

So as you can see, while Advanced Installer offers a lot of functionality to get you up and running quickly with an installation package, the large number of limitations in the product reflect many of its shortcomings and immaturity in the Enterprise Marketplace for MUST-HAVE features.  Hopefully, these shortcomings will be addressed in the very near future to make Advanced Installer a much more robust and Enterprise worthy installation and packaging product!!

Tuesday, September 29, 2015

SHA1 Self Signed Certificates in IIS

If you have ever created a Self Signed Certificate in IIS (or using SelfSSL7) you may discover that the Self Signed Certificate you created only supports SHA1!!


However, as you may well know, SHA1 is being deprecated in favor of SHA256 certificates (http://blogs.technet.com/b/pki/archive/2013/11/12/sha1-deprecation-policy.aspx) and if you are using an SHA1 SSL certificate, you may get a browser warning such as the following:





Therefore, in order to avoid using an SHA1 SSL Certificate, you will have to resort to setting up your own hosted Windows Certificate Services which supports SHA256 SSL Certificates.

If you want Windows to support Self Signed SHA256 SSL Certificates, then you should vote for this UserVoice item: https://windowsserver.uservoice.com/forums/310252-iis-and-web-server-role/suggestions/9979233-provide-support-for-sha256-self-signed-certificate





Cookie Expiration Times in Google Chrome Developer Tools

I was recently using Google Chrome Developer Tools to examine Cookie Expiration Times and noticed that the expiration times were completely unexpected based on how I had configured my cookies!


Well, as it turns out, Google Chrome Developer Tools is displaying the Cookie Expiration Times in GMT (Greenwich Mean Time)!

Unfortunately, there does not appear to be a setting to allow Google Chrome Developer Tools to display the Cookie Expiration Times in local time.

In order to view local expiration times, you can go into Google Chrome's Settings and look at the Cookie Expiration Times individually:



  1. Settings-->Privacy-->Content Settings
  2. All cookies and site data...
  3. Select the site that you want to view
  4. Click on the name of the Cookie to view the Cookie details which includes the Expiration Time





However, if you install an Add-In such as Cookie Inspector, you can get the Cookie Expiration Times to show in local time!  (https://chrome.google.com/webstore/detail/cookie-inspector/jgbbilmfbammlbbhmmgaagdkbkepnijn?hl=en)



If you are using Firebug for Mozilla Firefox, you can also directly get the local Cookie Expiration Times:


Monday, September 28, 2015

Specifying a Layout file from an MVC Controller

 

Even though Intellisense for an MVC Controller may not display the correct Intellisense for changing or specifying the Layout for a specific MVC View, you can use the following syntax to explicitly use a different Layout view before rending an MVC View:

public ActionResult Index()
{
   //specify the CustomLayout Layout file for the Index view
   return View("Index", "_CustomLayout");
}

Doing this through the MVC Controller allows you to override Layout values configured directly in the MVC View as well as allows you to dynamically load different Layouts for specific MVC Views.

Linking to an MVC Controller in an Area

 

If you have been wondering how to link to a Controller that is in an MVC Area, the answer is surprisingly easy!

You can link to a Controller in an Area of your MVC Web Application project in the following manner:

@Url.Action("Index", "AreaHome", new {Area = "MyArea"}, null)


Alternatively, you can also use the following code:



@Html.ActionLink("Index", "AreaHome", new {Area = "MyArea"}, null)

That is all there is to it!!

Wednesday, September 23, 2015

Installing Multiple TeamCity Build Agents

If you want to install TeamCity Build Agents either on separate servers or the same server, you will want to look at this TeamCity documentation: https://confluence.jetbrains.com/display/TCD9/Setting+up+and+Running+Additional+Build+Agents

If you want to install multiple Windows Build Agent services, you definitely want to take a look at this article: https://handcraftsman.wordpress.com/2010/07/20/multiple-teamcity-build-agents-on-one-server/

Office 2016 now available for download!

Office 2016 was just released today and is now available for download.

You can sign up for a trial version from here: https://portal.office.com/Signup/Signup.aspx?OfferId=2A3F5C07-BBB2-4786-857C-054F5DDD3486&DL=OFFICESUBSCRIPTION&ali=1#0

If you currently have an MSDN Subscription, it is also available for download from MSDN Subscriptions: https://msdn.microsoft.com/subscriptions

Tuesday, September 22, 2015

Adding NuGet Package Dependencies

If you are publishing your own NuGet Packages, you will first need to download a copy of NuGet Package Explorer: https://npe.codeplex.com/

Once you have that downloaded and installed, you can then begin adding Dependencies to your NuGet Package!

  1. Create a new Package in Package Explorer
  2. Begin editing the Package Metadata
  3. Click on the button for "Edit dependencies"
  4. Click on the + sign to create a new group
  5. Click on the icon in the bottom left hand corner for "Select dependency from NuGet feed"
  6. Make sure you type in the Url of your NuGet server or the Official NuGet server Url: https://nuget.org/api/v2/
  7. Select a package that you want to use as your dependency and click the "Open" hyperlink
  8. Click on the + icon to "Add new dependency"
  9. Once all of your dependencies have been added, you can click on the OK button
  10. You will now be able to view your package dependencies as part of your Package Metadata!






NuGet Package Sources for Visual Studio 2013 and Visual Studio 2015

If you haven't already noticed, there is a major change in NuGet between Visual Studio 2013 and Visual Studio 2015 which is undocumented on the NuGet site!

If you read this article: https://docs.nuget.org/consume/nuget-config-file#package-sources

You will see that the NuGet official package source is listed as: https://nuget.org/api/v2/

This is the same Url that is listed in Visual Studio 2013 as the NuGet official package source.

However, Visual Studio 2015 is using a newer version of NuGet and therefore has the following Url: https://api.nuget.org/v3/index.json


Using Ninject Dependency Injection in an ASP.NET Web API Web Application

I have been using Ninject Dependency Injection in my ASP.NET Web API Projects by simply doing the following things in the past:

  1. Install the Ninject Web Host for WebApi 2 NuGet package: https://www.nuget.org/packages/Ninject.Web.WebApi.WebHost/
  2. Add the necessary DI mappings in the RegisterServices method of the NinjectWebCommon.cs file found in the App_Start folder

However, even after doing all of this, I experienced the following error message:

An error occurred when trying to create a controller of type 'ValuesController'. Make sure that the controller has a parameterless public constructor.

I had earlier been using an older release of Ninject and did not experience this problem, so I decided to check what version I was using and I was using v. 3.2.4.

As a test to see if a new problem was introduced, I decided to use the older version of Ninject from NuGet (https://www.nuget.org/packages/Ninject.Web.WebApi/3.2.3) by running the following command in the Package Manager Console:

Install-Package Ninject.Web.WebApi -Version 3.2.3

Sure enough, my Web API Controller resolved correctly once I reverted to the older version of Ninject Web API!!

<ArrayOfstring><string>value1</string><string>value2</string></ArrayOfstring>

Therefore, there were some problems in an earlier release of Ninject.Web.Common (v. 3.2.0) that caused the use of the NinjectWebCommon.cs file by itself to no longer be sufficient.

You can use a NinjectDependencyResolver class to workaround this problem as described in this article (http://www.peterprovost.org/blog/2012/06/19/adding-ninject-to-web-api), but if you are having the same problem, it is best to make sure you are using at least v. 3.2.3 of Ninject.Web.Common or a later release.

 

Monday, September 21, 2015

Mcafee VirusScan Enterprise 8.8i Patch 6 now available for download!

If you have been eagerly anticipating the release of Patch 6 for Mcafee VirusScan Enterprise v. 8.8i in order to support Windows 10, you can now download it from the Mcafee Downloads site here: https://secure.mcafee.com/apps/downloads/my-products/login.aspx

Just enter your Grant Number and you will then be able to download Patch 6 from the Packages tab:






You have the option of downloading just Patch 6 or the fully patched version including Patch 6 in order to perform your clean installations of Windows 10!

Sunday, September 20, 2015

Team Foundation Server Administration Tool

One of the hidden gems that can be found in the TFS Install and Administration Guides (http://www.microsoft.com/en-us/download/details.aspx?id=29035) is a reference to the Team Foundation Server Administration Tool: http://tfsadmin.codeplex.com/

If you do not have a version of Visual Studio installed on your machine that is compliant with this tool, you will need to install the Team Foundation Server Object Model in order to be able to run this tool: https://visualstudiogallery.msdn.microsoft.com/19311823-5262-4e63-a586-2283384ae3bf

The numerous limitations that are currently present in the Team Foundation Server Web Access UI can be overcome by using this tool.  There is no telling when Microsoft will improve the Team Foundation Server Web Access User Interface for managing security permissions, but for now, you can rely on this handy tool for managing your Team Foundation Server security permissions!

Friday, September 18, 2015

Resolving NuGet Package Reference problems

If you have numerous NuGet Package References in your project or solution, at one point or another, you will inevitably run into NuGet Package Reference problems.

One of the most common problems with NuGet Packages is ensuring that your NuGet Package References are restored at build time.

This can be corrected by making sure you have this setting in Visual Studio Tools-->Options-->NuGet Package Manager:



The second problem you might run into is that NuGet Package References may differ across your entire solution!  For example, if you have numerous projects in your solution, there is a good chance that they may be using different NuGet Package Reference Assembly Versions!  So how do you manage these discrepancies?

You right click on the solution and select "Manage NuGet Packages for Solution":



This will show you all of the projects in your solution that have NuGet Package References along with their associated installed assembly versions.

 If you are still having problems with differing assembly versions, then you will need to check the packages.config file for each of the different projects:

In the packages.config file you will find all of the various NuGet Package References along with their respective versions.  You will have to compare all of these versions across all of your Visual Studio projects to ensure that they have matching versions.

Lastly, if you are still encountering issues with building packages with NuGet references, then you should close your Visual Studio solution and delete the packages  directory from the root of the solution in Windows Explorer.





Now, you can go ahead and try re-building your Visual Studio solution to restore all missing NuGet Packages to see if your NuGet Package Reference issues are now resolved!

If you are having problems with NuGet Package Versions and you need to re-install a NuGet Package Reference, in Visual Studio 2013 (especially if that NuGet Package has other dependencies), you will have to MANUALLY REMOVE the references from Visual Studio and the referring lines in packages.config.

Then, in NuGet Package Manager in Visual Studio 2013, you can re-install the NuGet packages.

If you are using Visual Studio 2015, however, you have a new option to remove or uninstall the NuGet Packages called "Force uninstall, even if there are dependencies on it":


This removes many of the obstacles present while working with NuGet Package References in Visual Studio 2013 and allows you to effortlessly uninstall and reinstall any NuGet Package References that you need!





Thursday, September 17, 2015

Publishing to GitHub from Visual Studio

If you are interested in publishing one of your Visual Studio projects to GitHub, you can easily do that using the GitHub Extension for Visual Studio which can be downloaded from here: https://visualstudiogallery.msdn.microsoft.com/75be44fb-0794-4391-8865-c3279527e97d

Once you install the GitHub Extension for Visual Studio, you will see something like the following in Visual Studio Team Explorer:







Once you have logged into GitHub with your account, you will get a screen similar to the following:





You can then begin cloning your repositories and committing/syncing your changes back to your GitHub repository!

For detailed steps on how to work with Visual Studio and Git you can check out these articles:

https://visualstudio.github.com/

https://msdn.microsoft.com/Library/vs/alm/Code/git/get-started


Web Site Administration Tool for Visual Studio 2013 and Visual Studio 2015

If you are looking for the Web Site Administration Tool that originally shipped with older versions of Visual Studio, you may be disconcerted to discover that this tool no longer ships with newer versions of Visual Studio such as Visual Studio 2013!

Microsoft seems to offer a workaround for this issue based on this article: http://blogs.msdn.com/b/webdev/archive/2013/08/19/asp-net-web-configuration-tool-missing-in-visual-studio-2013.aspx

However, when you attempt to browse the site and perform various operations, you may encounter error messages such as:

An error was encountered. Please return to the previous page and try again. 



Fortunately, instead of trying to figure out how to get this tool to work properly for your needs, there is a better alternative released as a project on CodePlex: https://udriwsat.codeplex.com/

The tool is in a very rough state and has some invalid references to MySQL as well as lacking the ability to Add New Roles and several other settings available in the Web Site Administration Tool.  It also requires several settings to be configured in the Web.config file before you have a working system.

However, once it is up and running, you can easily manage and create new Users as well as assign Users to their respective Roles!


If you want an updated version of the WSAT code from CodePlex, you can grab the code from GitHub here: https://github.com/ssvaidya/WSAT

Using a SqlMembership Provider in your ASP.NET Web Application

If you are looking to implement a SqlMembershipProvider in your ASP.NET Web Application, you will definitely want to take a look at these articles:


Creating the Membership Schema in SQL Server
http://www.asp.net/web-forms/overview/older-versions-security/membership/creating-the-membership-schema-in-sql-server-cs

Configuring an ASP.NET Application to Use Membership
https://msdn.microsoft.com/en-us/library/6e9y4s5t.aspx

For general information about the SqlMembershipProvider class, you can check out this MSDN article: https://msdn.microsoft.com/en-us/library/system.web.security.sqlmembershipprovider%28v=vs.110%29.aspx


Initiating a Code Review in Team Foundation Server

Ever since the release of Visual Studio 2012 and Team Foundation Server 2012, Microsoft has provided the ability to initiate Code Reviews from within the Visual Studio IDE.

Unfortunately, many developers are unaware of this wonderful ability to conduct code reviews as part of the TFS ALM Lifecycle.

Well, fortunately, Microsoft has created a detailed article (including screenshots) which walks you through the process of initiating and conducting a Code Review from within Visual Studio:  https://www.visualstudio.com/en-us/get-started/code/get-code-reviewed-vs

The best part of the code review process is that all of the code review comments become a part of TFS and can be reviewed historically if there are several code reviews and code reviewers for a particular design change/feature!


Error CS0656: Missing compiler required member 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create'

I was recently attempting to compile my Telerik Sitefinity Web Application in Visual Studio 2015 which included an MVC Controller which made a reference to the ViewBag property when I suddenly saw this error message in the Build Output which was preventing me from compiling my project succesfully:

Error CS0656: Missing compiler required member 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create'

After doing some research on the error message, I discovered that Visual Studio 2015 for some reason was having trouble with the ViewBag property in my Controller!

As soon as I removed the reference to the ViewBag property from my Controller, I was able to successfully compile my Sitefinity Web Application once again!


Wednesday, September 16, 2015

Working with ADSI in C#

If you have a requirement to work with ADSI in C#, unfortunately, the default MSDN documentation on ADSI (Active Directory Service Interfaces) does not provide much guidance on accomplishing this: https://msdn.microsoft.com/en-us/library/aa746486.aspx

All of the sample articles refer to using either VBScript, Visual Basic 6.0 or Visual C++ 6.0!

Well, fortunately, you may also come across this article which points you to using the System.DirectoryServices assembly in .NET: https://support.microsoft.com/en-us/kb/315716

When you then search for examples using System.DirectoryServices, you may find examples such as this:

Invoking ADSI Methods
https://msdn.microsoft.com/en-us/library/ms180896%28v=vs.80%29.aspx

Invoking ADSI Properties
https://msdn.microsoft.com/en-US/library/ms180895%28v=vs.80%29.aspx

For the complete documentation on the System.DirectoryServices assembly, you can check out this MSDN article: https://msdn.microsoft.com/en-us/library/system.directoryservices%28v=vs.110%29.aspx

Setting up a Virtual Path Provider for ASP.NET MVC

You may already be familiar with setting up a Virtual Path Provider for ASP.NET Web Forms, but what if you want to set up a Virtual Path Provider for ASP.NET MVC?

Well, fortunately, ASP.NET MVC has VPP support as well!

For guidance on how to accomplish this, you can check out this blog post:   http://weblogs.asp.net/ricardoperes/loading-asp-net-mvc-controllers-and-views-from-an-assembly

Tuesday, September 15, 2015

Updating Path Variables for Advanced Installer

Unfortunately, Advanced Installer does not have great command-line support or API support for Path Variables, therefore, you often have to resort to workarounds to accomplish things that would otherwise be easily possible if Advanced Installer had better command-line or API support.

One such case is when dealing with Path Variables.  If you look at Advanced Installer's command-line for Path Variables, you will notice that there is only support for adding New Path Variables or Deleting Path Variables:

http://www.advancedinstaller.com/user-guide/new-path-variable.html

http://www.advancedinstaller.com/user-guide/delete-path-variable.html

But what if you want to simply update your existing Path Variables stored in your PathVariables.xml file?  Well, unfortunately, there is no solution for that built into Advanced Installer's command-line support!

Therefore, you can use a workaround courtesy of MSBuild and MSBuild Extension Pack as in the following code snippet:
<Target Name="BuildPackageWithPathVars" DependsOnTargets="PrepareNRClaims">
   <Message Text="This is the Advanced Installer Path $(AdvancedInstallerPath)" />
   <MSBuild.ExtensionPack.Xml.XmlFile TaskAction="UpdateAttribute" File="$(PathVariablesFile)" XPath="/PathVariables/Var[@Name='Published_Dir']" Key="Path" Value="$(PublishFolder)" />
   <MSBuild.ExtensionPack.Xml.XmlFile TaskAction="UpdateAttribute" File="$(PathVariablesFile)" XPath="/PathVariables/Var[@Name='PackagingFiles_Dir']" Key="Path" Value="$(PackagingFilesDir)" />
   <MSBuild.ExtensionPack.Xml.XmlFile TaskAction="UpdateAttribute" File="$(PathVariablesFile)" XPath="/PathVariables/Var[@Name='Prerequisites_Dir']" Key="Path" Value="$(PrerequisitesFilesDir)" />
   <MSBuild.ExtensionPack.Xml.XmlFile TaskAction="UpdateAttribute" File="$(PathVariablesFile)" XPath="/PathVariables/Var[@Name='PackagingSigning_Dir']" Key="Path" Value="$(CodeSigningDir)" />
   <ItemGroup>
     <BuildArg Include="/rebuild" />
     <BuildArg Include="&quot;$(AdvancedInstallerProjectFilePath)&quot;" />
   </ItemGroup>
   <Exec Command="&quot;$(AdvancedInstallerPath)&quot; /loadpathvars $(PathVariablesFile)" />
   <Message Text="This is the command to be run: &quot;$(AdvancedInstallerPath)&quot; @(BuildArg, ' ')" />
   <Exec Command="&quot;$(AdvancedInstallerPath)&quot; @(BuildArg, ' ')" ContinueOnError="False"  />
 </Target>

 

Once you have loaded the Path Variables into Advanced Installer, you should then be able to successfully build the installation package using the update PathVariables.xml file values!

Jetbrains Toolbox

If you use several Jetbrains tools such as Resharper, dotTrace and WebStorm, you will be happy to know that Jetbrains has announced the Jetbrains Toolbox!  https://www.jetbrains.com/toolbox/

The Jetbrains Toolbox allows you to purchase a monthly or yearly subscription to the set of tools that you need most.  You can now get the entire suite of Jetbrains Desktop Tools for a yearly cost of $199!!

That is absolutely awesome!!

Monday, September 14, 2015

The "Copy" task does not support copying directories.

I was recently trying to perform a standard copy using the Copy task in MSBuild when I was suddenly faced with this error message:

The "Copy" task does not support copying directories.

Well, fortunately, I came across this article which describes how to workaround this limitation of the base MSBuild Copy Task:  http://blogs.msdn.com/b/msbuild/archive/2005/11/07/490068.aspx 

Of course, another alternative is to simply use the RoboCopy task from the MSBuild Extension Pack: https://github.com/mikefourie/MSBuildExtensionPack/releases

Limitations of Advanced Installer

I have been a long time user of InstallShield, so I know most of the ins and outs of InstallShield and how it works as well as numerous tips and tricks as to how to work around its various limitations.

Well, I started using Advanced Installer a few years ago and have been more recently using it very heavily so I am starting to see many of its numerous deficiencies now that I thought I would share with other Advanced Installer users (as of the most recent version which is version 12.2 as of this writing):

  1. In InstallShield, you can bounce back and forth between the "Advanced" IDE view and the "Wizard" IDE view for all Basic MSI project types.  Advanced Installer is pretty much an all or nothing situation depending on the project type you choose.  If you choose an Architect template, you do not get a Wizard UI at all.  If you choose another project type that has a Wizard, then after you initially complete the Wizard, there is no going back--you are stuck in Advanced IDE view from then on. 
  2. In InstallShield, you can set up and create your own custom components which allows you a great deal of flexibility with regards to customizing the structure of your installation package as well as managing how it is built.  Unfortunately, Advanced Installer does not provide the ability to associate an entire folder with a single component as is possible with InstallShield.  In fact, you cannot even CREATE custom components in Advanced Installer.  You are limited to just being able to create features.  
  3. Icons do not support Path Variables or Properties.  When you attempt to set the path for icons in the Product Details section or the Media section, these values are hard coded into the installation package.
  4. In Install Parameters section, when you attempt to use properties such as [APPDIR], there is no autocompletion for this property.
  5. The Install Parameters section is crammed into a very small and tight space.  It would be better if it were allocated to a larger space similar to the InstallShield Property Manager to allow easier viewing of all available Properties. 
  6. There is no easy to search, sort and filter Install Parameters.  InstallShield provides a nice Grid View for searching, sorting and filtering all of the available Install Parameters.
  7. InstallShield 2015 introduced a new "Summary View" which provides a summary of how many files/folders, registry keys, components etc. are in your installation package.  Advanced Installer has yet to release a similar feature.
  8. The "Run in VM" feature does not work if you are already running Advanced Installer within a virtual machine (as most companies are)!!
  9. No easy way to view the Property Manager directly.  Instead, you have to go to a section of the IDE that can leverage Properties and then browse the Property Dialog from there.
  10. Even when setting the option for "converting paths", Path Variables are not automatically created when adding new files/folders as is the default in InstallShield.
  11. When setting up Synchronized Folders, there is no way to specify a Path Variable or Property for the Synchronized Folder path.
  12. Working with Synchronized Folders is not as easy and intuitive as using Dynamic Folders in InstallShield.   
  13. When building an installation package using Synchronized Folders at the command line using the /ResetSync and /NewSync commands, there is no way to specify Inclusion and Exclusion Filters.  This can only be done in the IDE and is not exposed through the command-line build process.
  14. Converting Paths in Advanced Installer is an extremely long and error-prone process.  You might wait an hour or more for a project's paths to be converted (if at all).  If the Convert Paths wizard gets stuck, there is no way to cancel the conversion process other than to completely KILL the Advanced Installer process in Task Manager.
  15. Adding Files and Folders does not always correctly convert File Source Paths.  On hundreds of occasions while using the Files and Folders section in Advanced Installer, Advanced Installer has not been able to successfully convert files and folders into their appropriate Path Variables when selecting "Convert Paths", thus leaving hard coded path values in the installation package.
  16. The "Convert Paths" dialog has an option that states "Show paths that cannot be converted", but there is no way to tell from the dialog whether or not the path will be converted or not.
  17. In the Registry section, you can import Reg files but cannot export any Reg files from your existing registry settings.
  18. In the Digital Signature section, you cannot specify a Path Variable or a Property for the Code Signing Certificate nor a way to dynamically pass or store off the password for the Code Signing Certificate.
  19. In the Prerequisites section, there is no way to specify a Path Variable or a Property for custom Prerequisites (such as if you are installing Crystal Reports from an MSI)
  20. Even though Path Variable support has been improved in Advanced Installer 12.4, the Path Variable support still remains very unreliable.  
  21. There is no method to automate the inclusion and exclusion paths when using Synchronized Folders through the command line.
  22. In the Dialogs section, there is no way to "Clone" or "Duplicate" an existing Dialog.  Instead, you have to create a brand new Dialog and copy over the individual controls from an existing dialog and edit and rename them appropriately.
  23. In the Dialogs section, there is no way to right click and "Move Up" or "Move Down" dialogs in the Dialog sequence.  You can only drag and drop dialogs in the sequence (if that works correctly).
  24. In the Dialogs section, when you add a Set installer property value, there is no way to simply type in the name of an Argument using autocomplete/Intellisense.  Instead, you are forced to pick the name of the Argument from an "Edit Control Event" dialog.  This is inconsistent with the rest of the IDE which allows you to type in the names of Properties in most other sections of the IDE. 
  25. In the SQL Databases section, there is no way to specify platform conditions for each SQL Server database connection such as specifying SQL Server 2012 or SQL Server 2014 or above etc.
  26. In the Custom Actions, there are no distinguishing icons for various types of Custom Actions such as .NET custom actions vs VBScripts vs PowerShell. All of the icons are exactly the same "gear" icon.
  27. Advanced Installer has no "Automation API".  Instead, you can only perform operations on the installation package via a command-line interface.  While the command-line interface is still very useful in assisting with automation, there are numerous areas of the installation package that are not exposed through the command-line interface, thereby, leaving large pieces of functionality to be manually edited in the Advanced Installer IDE.
  28. No built-in controls for an end-user to select or browse to a Url field in the Dialogs section. 
  29. In the IIS section, you cannot capture multiple properties of an IIS website or Application/Virtual Directory.  For example, if you want the name of the website as well as any port information or binding information, you cannot capture all of these various values into separate properties. 
Of course, I am sure there are many more deficiencies that I have missed, but I will continue to add to this list as I discover them.  If you have found any additional defects or deficiencies in Advanced Installer, I would love to hear about them so  I can include them in this list as well.

That's all for now!

Using Path Variables in Advanced Installer

With the release of Advanced Installer 12.4, you now have greater support for Path Variables to now include Synchronized Folders, Prerequisites and Digital Signing Certificates: http://www.advancedinstaller.com/release-12.4.html#enhancements

However, using this feature is not as easy as it may seem!

In the past you would end up running the "Convert Paths" wizard only to discover that the Paths were not actually converted to Path Variables after all!













Even with the release of Advanced Installer 12.4, this holds true!

So how do you solve this conundrum??

You have to go into File-->Options-->Path Variables and define your own Path Variables!








First you need to change the PathVariables.xml file to a path that can be used as part of your autobuild/Continuous Integration build process and then you can begin defining your own custom Path Variables.

Once you have defined your own custom Path Variables to point to the folder paths where your Synchronized Folders, Prerequisites and Digital Signature files will reside, Advanced Installer will automatically translate these file paths into their respective Path Variables!



Saturday, September 12, 2015

Alternatives to BrowserStack

If you want to perform Cross-Browser testing of your application and you have looked at BrowserStack, here are some things to know before you make your decision:

  1. BrowserStack offers one of the shortest trials of any other Cross-Browser software vendor.  The trial is limited to a mere 30 minutes of Live testing!!  https://www.browserstack.com/question/590
  2. If you choose the Freelancer option for $12.50/month (or $19/month if you choose the monthly plan), you only get a 100 minutes of Live testing per month!  https://www.browserstack.com/pricing 
Therefore, if you are looking for cheaper alternatives to BrowserStack, you will definitely want to take a look at these options:

  1. Sauce Labs: https://saucelabs.com/pricing  - Sauce Labs offers a 14-day free trial and for only $12/month, you get Unlimited Manual Testing!
  2. TestingBot: https://testingbot.com/pricing - While TestingBot only offers a 100-minute trial, for $12/month, you get Unlimited Manual Testing and 400 minutes of automated testing and 4 concurrent VMs (which is nearly double the automated testing you get with Sauce Labs!)
  3. BrowseEmAll: http://www.browseemall.com/Buy - For the same cost of the Live tier of BrowserStack, you can get the Professional subscription ($29/month) which offers Unlimited Manual Testing as well as Unlimited Automated Testing!!
  4. Mobile1st: http://mobile1st.com/pricing-purchase/ - If you are primarily concerned with Mobile Browser testing, Mobile1st offers a very cheap option of only $10/month with support for 14 different mobile devices.  

Why we use JIRA Issue Tracking instead of Team Foundation Server Work Item Tracking

There is 1 MAJOR reason:  Web-based Work Item Customization

JIRA Issue Tracking offers this very convenient method for customizing issues while even the latest release of Team Foundation Server still requires using the Process Editor that is installed as an extension to Visual Studio.

The ease of customizing issues/work items in JIRA makes choosing JIRA compelling over TFS Work Item Tracking extremely compelling.  Using the Process Editor is a very complex, difficult and overconvoluted process which easily dissuades novice users from adopting this methodology of customizing TFS Work Items.  In addition, customizing TFS Work Items using the Process Editor introduces problems while upgrading from one release of TFS to the next.

Even though there are many great reasons to use TFS Work Item Tracking because of its tight integration with other products released by Microsoft such as SharePoint, the difficulty of managing customizations to Work Items usually trumps those integration points.

For integration with source control check-ins, you can use add-ons such as TFS4JIRA to provide similar functionality as associating check-ins with TFS Work Items: https://marketplace.atlassian.com/plugins/com.spartez.jira.plugins.bork.tfs4jira

Thursday, September 10, 2015

The Controls collection cannot be modified because the control contains code blocks (i.e. <% ... %>)

I was recently adding Script Bundling to my ASP.NET Web Forms Web Application, when I suddenly encountered this error message:

The Controls collection cannot be modified because the control contains code blocks (i.e. <% ... %>)

Well, this was how my code was placed on the page originally:
<%: Scripts.Render("~/bundles/jquery") %>
<%: Scripts.Render("~/bundles/jqueryUI") %>



However, after taking a look at Microsoft's solution to this problem, I discovered that I had to surround these code blocks with a PlaceHolder control!!

Therefore, I had to modify my code to look like the following:

<asp:PlaceHolder runat="server">
<%: Scripts.Render("~/bundles/jquery") %>
<%: Scripts.Render("~/bundles/jqueryUI") %>
<%: Scripts.Render("~/scripts/custom") %>
</asp:PlaceHolder>


Once I did that, my code worked just as expected!!

My favorite FREE Visual Studio 2015 Extensions

Differences in OneASP.NET Project Templates

If you are creating a OneASP.NET Project using Visual Studio 2013 or Visual Studio 2015, you may be very surprised as to how the project templates are generated!

The project templates are basically using an order-of-operations methodology!!

Therefore, if you select an ASP.NET MVC Project Template FIRST and then you select ASP.NET Web Forms, you will get a Project Template which contains absolutely NO ASPX Web Form Pages but a Home Controller and several Razor Views!!





However, if you instead select an ASP.NET Web Forms Project Template FIRST and then you select ASP.NET MVC, you will get a Project Template which contains ASPX Web Form Pages but no Controllers or Razor Views!!




Therefore, when you are using Visual Studio, you have to carefully decide which piece of functionality is of primary importance to you because even in OneASP.NET, you only get 1 or the other!!


Wednesday, September 9, 2015

Testing 64-bit Database Driver Applications with Visual Studio

I was recently building an application that was dependent on 64-bit Microsoft Access drivers running in 64-bit IIS and I began running my Unit Tests in Visual Studio.

On my development system I had ONLY installed the 64-bit Microsoft Access drivers and had not installed the corresponding 32-bit drivers.

Therefore, whenever I attempted to run my Unit Tests, my Unit Tests ALWAYS failed because it stated that it could not find the appropriate database drivers on my system!!!

Well, once I remembered that Visual Studio remained a 32-bit process, I attempted to compile and run my Unit Tests as a 64-bit application.

Unfortunately, Unit Tests do not work as a 64-bit application!!

If I attempted to switch to the .NET OLE DB Provider, I got the following error message when running in 64-bit IIS instead: http://weblogs.asp.net/kencox/asp-net-iis-fix-the-microsoft-jet-oledb-4-0-provider-is-not-registered-on-the-local-machine

So the only thing I could do was do all of my testing with 32-bit drivers and then when I deployed to my target system running as a 64-bit process with 64-bit database drivers.

It took me hours upon hours to figure this out, so hopefully if you encounter the same issue with Visual Studio and 64-bit database drivers, now you will know what you have to do!!!

Dealing with SQL Server Timestamp columns in ADO.NET

I recently had a requirement to copy/export SQL Server data to MS Access.

Since I could not use SSIS, I used ADO.NET to perform the table copy based on this article: http://stackoverflow.com/questions/17253453/how-to-export-data-from-sql-server-compact-to-access-mdb

Unfortunately, one of the limitations with doing this is that you have to manually create the SQL to generate the MS Access database tables which therefore requires a knowledge of the underlying DataTable datatypes and what they map to in MS Access.

I found this article which describes the MS Access datatypes: https://msdn.microsoft.com/en-us/library/ms714540%28v=vs.85%29.aspx

I was able to map most of the SQL Server datatypes accurately based on the listing, however, I encountered a hurdle when I encountered the SQL Server timestamp column!!

After doing some debugging, I discovered that it was translating the timestamp column into a Byte[] array, therefore, I needed to do some special handling for Byte[] and figure out its appropriate datatype mapping.

Based on this SQL Server MSDN article: https://msdn.microsoft.com/en-us/library/ms182776%28v=sql.90%29.aspx?f=255&MSPPError=-2147217396

I discovered that I could map the timestamp column to a VarBinary data type in MS Access when creating the target database table in MS Access.

Once I did that, the copy table operation worked beautifully!!
 
This was the code that I used to generate the MS Access database table SQL:
 
public string GetCreateTableSql(DataTable table)
{
    StringBuilder sql = new StringBuilder();
    int i = 0;
 
    sql.AppendFormat("CREATE TABLE [{0}] (", table.TableName);
 
    while (i < table.Columns.Count)
    {
        bool isNumeric = false;
        bool usesColumnDefault = true;
        string columnSeparator = ",";
        
 
        sql.AppendFormat("\n\t[{0}]", table.Columns[i].ColumnName);
 
        switch (table.Columns[i].DataType.ToString().ToUpper())
        {
            case "SYSTEM.BYTE":
                sql.Append(" Byte");
                break;
            //The SQL timestamp column is translated into a System.Byte[] array data type
            case "SYSTEM.BYTE[]":
                sql.Append(" VarBinary");
                break;
            case "SYSTEM.INT16":
                sql.Append(" Integer");
                isNumeric = true;
                break;
            case "SYSTEM.INT32":
                sql.Append(" Integer");
                isNumeric = true;
                break;
            case "SYSTEM.INT64":
                sql.Append(" Long");
                isNumeric = true;
                break;
            case "SYSTEM.DATETIME":
                sql.Append(" DateTime");
                usesColumnDefault = false;
                break;
            case "SYSTEM.BOOLEAN":
                sql.Append(" YesNo");
                break;
            case "SYSTEM.CHAR":
                sql.Append(" Text");
                break;
            case "SYSTEM.STRING":
                sql.AppendFormat(" Text");
                break;
            case "SYSTEM.SINGLE":
                sql.Append(" Single");
                isNumeric = true;
                break;
            case "SYSTEM.DOUBLE":
                sql.Append(" Double");
                isNumeric = true;
                break;
            case "SYSTEM.DECIMAL":
                sql.AppendFormat(" Double");
                isNumeric = true;
                break;
            default:
                sql.AppendFormat(" Text");
                break;
        }//switch
 
        if (table.Columns[i].AutoIncrement)
        {
            sql.AppendFormat(" AutoNumber");
        }//if
 
        //Increment the counter
        i++;
 
        //Add a column separator for the Create Table SQL statement
        if (i < table.Columns.Count)
        {
            sql.AppendFormat("{0}", columnSeparator);
        }//if
 
        
    }//while
 
    sql.AppendFormat(" {0}", ")");
    return sql.ToString();
}//method: GetCreateTableSQL() 


 

 

Upgrading ASP.NET MVC via NuGet does not update Web.config files

I was recently upgrading a sample project from ASP.NET MVC 4 to ASP.NET MVC 5 via NuGet and when I examined my Web.config files, I noticed that none of the Web.config entries had been updated to match the newer version of ASP.NET MVC!
 
This was my original Web.config file at the root of the project:
 
<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <appSettings>
    <add key="webpages:Version" value="2.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="PreserveLoginUrl" value="true" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  </appSettings>
  <system.web>
    <httpRuntime targetFramework="4.5" />
    <compilation debug="true" targetFramework="4.5">
      <assemblies>
        <add assembly="TXTextControl.Web, Version=22.0.200.500, Culture=neutral, PublicKeyToken=6B83FE9A75CFB638" />
        <add assembly="System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
      </assemblies>
    </compilation>
    <pages>
      <namespaces>
        <add namespace="System.Web.Helpers" />
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
        <add namespace="System.Web.WebPages" />
      </namespaces>
    </pages>
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <handlers>
      
      
      
      
      
      
      <add name="TXWebSocketHandler" verb="*" path="TXWebSocketHandler.ashx" type="TXTextControl.Web.WebSocketHandler, TXTextControl.Web, Version=22.0.200.500, Culture=neutral, PublicKeyToken=6B83FE9A75CFB638" />
      <add name="TXPrintHandler" verb="*" path="TXPrintHandler.ashx" type="TXTextControl.Web.TXPrintHandler, TXTextControl.Web, Version=22.0.200.500, Culture=neutral, PublicKeyToken=6B83FE9A75CFB638" />
    <remove name="ExtensionlessUrlHandler-Integrated-4.0" /><remove name="OPTIONSVerbHandler" /><remove name="TRACEVerbHandler" /><add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" /></handlers>
  </system.webServer>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

This was my original Web.config file at the root of the Views folder:

 


<?xml version="1.0"?>
 
<configuration>
  <configSections>
    <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
      <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
      <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
    </sectionGroup>
  </configSections>
 
  <system.web.webPages.razor>
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
      <namespaces>
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
      </namespaces>
    </pages>
  </system.web.webPages.razor>
 
  <appSettings>
    <add key="webpages:Enabled" value="false" />
  </appSettings>
 
  <system.web>
    <httpHandlers>
      <add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
    </httpHandlers>
 
    <!--
        Enabling request validation in view pages would cause validation to occur
        after the input has already been processed by the controller. By default
        MVC performs request validation before a controller processes the input.
        To change this behavior apply the ValidateInputAttribute to a
        controller or action.
    -->
    <pages
        validateRequest="false"
        pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
        pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
        userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
      <controls>
        <add assembly="System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
      </controls>
    </pages>
  </system.web>
 
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
 
    <handlers>
      <remove name="BlockViewHandler"/>
      <add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
    </handlers>
  </system.webServer>
</configuration>

 

After I manually upgraded the Web.config files, this was the resultant root Web.config file:

 


<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <appSettings>
    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="PreserveLoginUrl" value="true" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  </appSettings>
  <system.web>
    <httpRuntime targetFramework="4.5" />
    <compilation debug="true" targetFramework="4.5">
      <assemblies>
        <add assembly="TXTextControl.Web, Version=22.0.200.500, Culture=neutral, PublicKeyToken=6B83FE9A75CFB638" />
        <add assembly="System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
      </assemblies>
    </compilation>
    <pages>
      <namespaces>
        <add namespace="System.Web.Helpers" />
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
        <add namespace="System.Web.WebPages" />
      </namespaces>
    </pages>
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <handlers>
      <add name="TXWebSocketHandler" verb="*" path="TXWebSocketHandler.ashx" type="TXTextControl.Web.WebSocketHandler, TXTextControl.Web, Version=22.0.200.500, Culture=neutral, PublicKeyToken=6B83FE9A75CFB638" />
      <add name="TXPrintHandler" verb="*" path="TXPrintHandler.ashx" type="TXTextControl.Web.TXPrintHandler, TXTextControl.Web, Version=22.0.200.500, Culture=neutral, PublicKeyToken=6B83FE9A75CFB638" />
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" /><remove name="OPTIONSVerbHandler" />
      <remove name="TRACEVerbHandler" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
  </system.webServer>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

This was the Web.config file in the root of the Views folder:



<?xml version="1.0"?>
 
<configuration>
  <configSections>
    <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
      <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
      <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
    </sectionGroup>
  </configSections>
 
  <system.web.webPages.razor>
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
      <namespaces>
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
      </namespaces>
    </pages>
  </system.web.webPages.razor>
 
  <appSettings>
    <add key="webpages:Enabled" value="false" />
  </appSettings>
 
  <system.web>
    <httpHandlers>
      <add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
    </httpHandlers>
 
    <!--
        Enabling request validation in view pages would cause validation to occur
        after the input has already been processed by the controller. By default
        MVC performs request validation before a controller processes the input.
        To change this behavior apply the ValidateInputAttribute to a
        controller or action.
    -->
    <pages
        validateRequest="false"
        pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
        pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
        userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
      <controls>
        <add assembly="System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" />
      </controls>
    </pages>
  </system.web>
 
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
 
    <handlers>
      <remove name="BlockViewHandler"/>
      <add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
    </handlers>
  </system.webServer>
</configuration>