Thursday, December 11, 2008

ArgumentNullException raised by Profile when deploying a web project

Your ASP.NET web site works perfectly in your development environment and you build a Visual Studio Web Deployment Project to deploy it in a production environment. You launch the default page and you get:

Value cannot be null.
Parameter name: type

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: type

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[ArgumentNullException: Value cannot be null. Parameter name: type]
System.Activator.CreateInstance(Type type, Boolean nonPublic) +2843963
System.Web.Profile.ProfileBase.CreateMyInstance(String username, Boolean isAuthenticated) +76
System.Web.Profile.ProfileBase.Create(String username, Boolean isAuthenticated) +312
System.Web.HttpContext.get_Profile() +108
Global.Application_AcquireRequestState(Object sender, EventArgs e) +72
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +92
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +64

The solution to this problem is hidden in the Web Deployment Project property pages. Select the "Output Assemblies" node under "Configuration Properties" and below the "Merge all outputs to a single assembly" option, uncheck "Treat as library component (remove App_code.compiled file)".


Friday, November 28, 2008

How to configure a membership provider programmatically

The following C# sample code demonstrates how to configure a .NET membership provider programmatically. This code requires that you also configure a connection string named MyDatabase.

NameValueCollection objConfig = new NameValueCollection();
objConfig.Add("connectionStringName", "MyDatabase");
objConfig.Add("enablePasswordRetrieval", "false");
objConfig.Add("enablePasswordReset", "true");
objConfig.Add("requiresQuestionAndAnswer", "true");
objConfig.Add("applicationName", "MyApp");
objConfig.Add("requiresUniqueEmail", "true");
objConfig.Add("maxInvalidPasswordAttempts", "5");
objConfig.Add("passwordAttemptWindow", "10");
objConfig.Add("commandTimeout", "30");
objConfig.Add("passwordFormat", "Hashed");
objConfig.Add("name", "AspNetSqlMembershipProvider");
objConfig.Add("minRequiredPasswordLength", "8");
objConfig.Add("minRequiredNonalphanumericCharacters", "2");
objConfig.Add("passwordStrengthRegularExpression", "(?=^.{8,25}$)(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()_+}{\\":;'?/>.<,])(?!.*\\s).*$"));

SqlMembershipProvider objSqlMembershipProvider = new SqlMembershipProvider();
objSqlMembershipProvider.Initialize(objConfig["name"], objConfig);
MembershipProviderCollection colMembershipProviders = new MembershipProviderCollection();
colMembershipProviders.Add(objSqlMembershipProvider);
colMembershipProviders.SetReadOnly();

BindingFlags enuBindingFlags = BindingFlags.NonPublic | BindingFlags.Static;
Type objMembershipType = typeof(Membership);
objMembershipType.GetField("s_Initialized", enuBindingFlags).SetValue(null, true);
objMembershipType.GetField("s_InitializeException", enuBindingFlags).SetValue(null, null);
objMembershipType.GetField("s_HashAlgorithmType", enuBindingFlags).SetValue(null, "SHA1");
objMembershipType.GetField("s_HashAlgorithmFromConfig", enuBindingFlags).SetValue(null, false);
objMembershipType.GetField("s_UserIsOnlineTimeWindow", enuBindingFlags).SetValue(null, 15);
objMembershipType.GetField("s_Provider", enuBindingFlags).SetValue(null, objSqlMembershipProvider);
objMembershipType.GetField("s_Providers", enuBindingFlags).SetValue(null, colMembershipProviders);

This code assumes you have the following statements in your cs file:

using System.Web.Security;
using System.Collections.Specialized;
using System.Reflection;

Then you can use Membership functions like Membership.ValidateUser, Membership.GetUser and Membership.CreateUser.

Monday, November 17, 2008

How to create a connection string programmatically

The code snippet for creating a connection string programmatically in .NET C# follows:

/*
// Opening the configuration file for a DLL, i.e. %ProgramFiles%\Memba\Velodoc\Management Console\Memba.Console.dll.config
ExeConfigurationFileMap objExeConfigurationFileMap = new ExeConfigurationFileMap();
objExeConfigurationFileMap.ExeConfigFilename = System.Reflection.Assembly.GetExecutingAssembly().Location + ".config";
Configuration objExeConfiguration = ConfigurationManager.OpenMappedExeConfiguration(objExeConfigurationFileMap, ConfigurationUserLevel.None);
*/

// Opening the configuration file for an executable, i.e. C:\Windows\System32\mmc.config
Configuration objExeConfiguration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

// Creating a connection string element from the connection string in web.config
ConnectionStringSettings objConnectionStringSettings = new
ConnectionStringSettings(

"MyDatabase",

"Data Source=.;Initial Catalog=MyDatabase;Integrated Security=True;Connect Timeout=30",
"System.Data.SqlClient"
);

// Get the connection strings section
ConnectionStringsSection objConnectionStringsSection = objExeConfiguration.ConnectionStrings;

// Add the new element
objConnectionStringsSection.ConnectionStrings.Clear();
objConnectionStringsSection.ConnectionStrings.Add(objConnectionStringSettings);

// Save the configuration file
objExeConfiguration.Save(ConfigurationSaveMode.Modified);

// Refresh and check
ConfigurationManager.RefreshSection(objConnectionStringsSection.SectionInformation.Name); // IMPORTANT!
Debug.Assert(ConfigurationManager.ConnectionStrings.Count == 1);
Debug.Assert(ConfigurationManager.ConnectionStrings[0].Name == objConnectionStringSettings.Name);
Debug.Assert(ConfigurationManager.ConnectionStrings[0].ConnectionString == objConnectionStringSettings.ConnectionString);
Debug.Assert(ConfigurationManager.ConnectionStrings[0].ProviderName == objConnectionStringSettings.ProviderName);

Note that this code assumes you have the following statements in your .cs file:

using System.Configuration;
using System.Diagnostics;

Friday, November 14, 2008

Sharing data between processes

Memba Velodoc Outlook Add-In lets you send large files from Outlook. The first component is a VSTO add-in running in the Outlook process, which provides a user interface like WinZip and WinRar to add large files to a transfer package and hyperlinks in the email body to download these files. The second component is an executable called transfer controller which actually uploads this package to a file transfer server in the background. The transfer controller provides a user interface to monitor the progress of transfers and pause, resume and cancel transfers. A plug-in architecture makes the transfer controller compatible with several file transfer protocols and server platforms including FTP. The VSTO add-in and the transfer controller communicate together via .NET remoting which is the standard inter-process communication (IPC) mechanism in .NET.

In all builds including and prior to build #081107 of release 1.0, the TCP port opened for .NET remoting on the transfer controller is hard coded. This has proved to be an issue when using windows fast user switching and terminal services sessions because each user needs his/her own instance of the transfer controller to access the user interface and they are all competing to listen on the same port.

First, ensuring one and only one instance of the transfer controller process per user session is achieved using a local mutex. Note that using a global mutex is a well-known design pattern to ensure one instance of a process per machine.

Second, we needed a way to configure .NET remoting in the transfer controller to listen on the first available port and notify the VSTO add-in to communicate on the same port. Actually the problem is even more complicated as we cannot tell which between the VSTO add-in and the transfer controller is started first because normally the VSTO add-in starts the transfer controller, but then Outlook might be closed and re-opened while the transfer controller is already loaded. Accordingly, we needed a way to scan for an available TCP port, cache it and share it between processes. We could have used the current user registry, but since the user registry is persistent there is a risk to lock the processes into a port which is no more available. So we needed to ensure that even in the case of a crash the port used would not persist once the add-in processes had exited. We have opted for shared memory.

In Windows, shared memory is a special case of memory mapped files, where the file mapping object accesses memory backed by the system paging file. However, the lifetime of this memory ends when the last process connected to the shared memory object closes connection or the application exits, so there is no data persistence. If an application creates shared memory, fills it with data and exits, the data is lost and this is exactly what we need.

Memory mapped files are implemented using the following Win32 kernel APIs: CreateFileMapping, OpenFileMapping, MapViewOfFile, MapViewOfFileEx, UnmapViewOfFile, FlushViewOfFile and CloseHandle. Note that the naming convention used in CreateFileMapping and OpenFileMapping follows the convention of mutexes where a name prefixed with Global\ creates the object in the global address space (machine) and a name prefixed with Local\ or not prefixed at all creates the object in the local address space (user session).

This will feature in the next build of the Memba Velodoc Outlook Add-In due by the end of the year. In the meantime we provide here a prototype in the form of a Visual Studio 2008 console application project. The first instance of the console application which you may launch should scan for an available TCP port, store it in shared memory and display it. All other instances should find that port in shared memory and display it. As a consequence, all instances of the console application should display the same shared TCP port.

Monday, November 10, 2008

Starting batch files in the current directory

Our development projects include batch files to build, install and clean the project. Projects are shared with source control and developers and testers have each developed their own directory structures where they store projects on their computer. Accordingly it is important that these batches work whatever their location.

The magic DOS command which achieves just that is cd /d %~dp0

This command actually changes the current directory to the directory where the batch file is located.

Thursday, October 02, 2008

Debugging installer classes

There are many blog posts which describe clumsy workarounds to debug installer classes but certainly not the best way to achieve it.

There are two scenarios:

  1. You really need to debug your installer class in the msiexec.exe process, and you need to buy some time to be able to attach the Visual Studio debugger to this process (menu Debug -> Attach to Process… in Visual Studio). One way to buy time is to popup a message box to halt the execution of the install method while you are attaching your debugger to the process. For more information, see http://msdn.microsoft.com/en-us/library/c6wf8e4z.aspx.
  2. You can also conveniently run your installer class using the installer tool (InstallUtil.exe). If this scenario works for your debugging requirements:
    1. Open the properties of the project containing your installer class (right-click the project in the solution explorer of Visual Studio and select contextual menu option Properties);
    2. Check the assembly name on the Application tab. In our example, the assembly name is "AssemblyName", and since our project is a class library the assembly is "AssemblyName.dll";
    3. Go to the Debug tab and in the Start Action, select Start external program and enter "C:\Windows\Microsoft.NET\Framework\v2.0.50727\InstallUtil.exe";
    4. On the same tab, in the start Options, enter the following command line arguments: "AssemblyName.dll /LogToConsole=true" according to point b.
      Open your installer class in the code editor and at the top of the install method (assuming you are debugging the installation) add:
      #if DEBUG
      if (System.Diagnostics.Debugger.IsAttached)
      System.Diagnostics.Debugger.Break();
      #endif

    5. Set your project as startup project, press F5 or start debugging in Visual Studio and the debugger will automatically stop on the Debugger.Break() statement.

Monday, July 28, 2008

ASP.NET Debugging Timeout

When you debug ASP.NET applications in IIS using Debug -> Attach To Process… and you keep the w3wp.exe process hanged on a code line for too long, it is automatically recycled and you lose your debugging session. To avoid that, go to the IIS Management Console, edit the application pool properties and set Ping Enabled = false.

Friday, July 04, 2008

ASP.NET projects deployed with Web Deployment Project 2005 do not load localized resources

Scott Guthrie explains how to use VS 2005 Web Deployment Project in his blog. But if you use global resources to localize your ASP.NET application and sign and version your web deployment assembly, these localized resources will not load. If you look at the fusion log, you will find an entry like follows:

Calling assembly : Memba.WebSite.XP, Version=1.1.0.0, Culture=neutral, PublicKeyToken=a4ae091aa8097a5a.
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\inetpub\wwwroot\VelodocXP\web.config.
LOG: Using host configuration file: C:\Windows\Microsoft.NET\Framework\v2.0.50727\Aspnet.config.
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v2.0.50727\config\machine.config.
LOG: Post-policy reference: Memba.WebSite.XP.resources, Version=0.0.0.0, Culture=fr, PublicKeyToken=a4ae091aa8097a5a
LOG: The same bind was seen before, and was failed with hr = 0x80131040.
ERR: Unrecoverable error occurred during pre-download check (hr = 0x80131040).

The problem comes from the fact that aspnet_merge.exe versions your satellite resource assemblies as "0.0.0.0" whatever the version number you set for your web deployment strong named assembly.

I have not found any way to version satellite assemblies according to the main assembly, but the following workaround works:

  1. Create an AssemblyInfo.cs class file in your App_Code directory;
  2. Include the typical information including versioning information;
  3. Add [assembly: System.Resources.SatelliteContractVersion ("0.0.0.0")]

Tuesday, May 27, 2008

Write about Velodoc, get a free license and a chance to win $100 cash

To celebrate the new releases of Velodoc Enterprise Edition, Velodoc XP Edition and Velodoc Outlook Add-In due in September, we will reward the best articles and blog posts written about Velodoc with a $100 cash payment to a Paypal account. The contest will run for the months of September, October and November 2008 and one article or blog post will be rewarded with the $100 cash prize each month. Better, all participants who have submitted a compliant article or blog post will get a full license serial number of Memba Velodoc Outlook Add-In worth $37 for free.

How does it work?

  1. Write and publish a new 'How-to' article or blog post comprising (definition of a compliant blog post):
    • At least 250 words giving advice, tips or a tutorial on any Velodoc product in any language that we can translate with Google translation tools,
    • a screenshot of Memba Velodoc,
  2. Submit a comment here with a link to your blog post and how to contact you if you are the winner.
  3. All participants commit to keep the blog post published for a year. Participation is limited to one original blog post per individual.
  4. We require at least 5 participating blog posts in a month to allocate the prize. Otherwise these blog posts will compete with the blog posts submitted on the following month. If this occurs in November, the contest will continue until we have 5 participating blog posts, so there should be a winner anyway.
  5. Participating blog posts will be judged on (1) quality of content and (2) number of referrals sent to our web sites as reported by our traffic analysis tools.
  6. The winning blog posts will be published here below.
  7. At the end of the contest, full licenses of Memba Velodoc Outlook Add-In will be made available to all participants who have submitted a compliant blog post.
  8. For any question, use our contact form.

Note that the process is completely transparent: all participating and winning blog posts will be available here as hyperlinks for anyone to read them.

September winner: <to be announced>

October winner: <to be announced>

November winner: <to be announced>

Tuesday, May 13, 2008

The Velodoc Outlook Add-In is being released

We are currently releasing the Outlook Add-In for Velodoc. We have posted a quick video tour on YouTube. This video demonstrates how to send large files to email contacts using Memba Velodoc Outlook Add-In and various file transfer server platforms including Velodoc servers, FTP servers, UNC File Shares, Microsoft BITS Servers and Amazon S3.




You can also find the high-definition version of the video tour on Silverlight Streaming Services.

Memba Velodoc Outlook Add-In is open-source and you can download the code from:

Monday, April 21, 2008

Error 1316 when installing Office 2007 PIA’s

If you build a setup for an Office 2007 VSTO add-in following the steps described in Deploying Visual Studio 2005 Tools for Office Second Edition Solutions Using Windows Installer, your setup may report that the installation of the Office 2007 PIA's have failed. Looking at the log or installing o2007PIA.msi separately, you will get "Error 1316. Setup cannot read from file <tempdir>\PIARedist.msi. Check your connection to the network, or CD-ROM drive. For other potential solutions to this problem, see SETUP.CHM".

This occurs on fresh installs of Office 2007 with Microsoft Business Contact Manager and more generally, it seems to occur on any computer which has the Office 2007 PIA's installed using another installer than o2007pia.msi, which is the one Microsoft recommends using.

There seems to be a workaround which is to rename o2007pia.msi into PIARedist.msi both in the file system where bootstrapper packages are stored and in the product.xml file as shown below:

<PackageFiles>
<PackageFile Name="PIARedist.msi"/>
<PackageFile Name="ComponentCheck.exe"/>
</PackageFiles>

...

<Command PackageFile="PIARedist.msi"
Arguments=""
EstimatedInstalledBytes="30000000"
EstimatedInstallSeconds="60">

You will need to rebuild your setup project.

Please let me know if this workaround does not work for you.

Monday, March 31, 2008

Validating email notification HTML for Outlook 2007

Velodoc issues numerous email notifications. The presentation uses the latest CSS tags and sophisticated layered HTML. Everything worked fine in Outlook 2003 which uses Internet Explorer as the HTML rendering engine. Outlook 2007 and its Word 2007 rendering engine broke many little things.

Although it is definitely a step back in terms of HTML rendering capability for emails, everything is explained in Word 2007 HTML and CSS Rendering Capabilities in Outlook 2007. The article comes with a very handy tool to validate HTML and CSS in your development environment.

I have not been able to install the tool for Visual Studio 2005 SP1 on Windows Vista Ultimate x64 because I could not instantly find the registry keys described in the setup instructions, but it works perfectly with Dreamweaver 8 provided you replace "Dreamweaver MX 2004" by "Dreamweaver 8" in the default installation path.

FileNotFoundException in Visual Studio Setup Project

The Velodoc XP setup project would consistently fail on a specific Windows Server 2003 server of ours but not on other computers of our network. The complete issue is described at http://www.codeplex.com/VelodocXP/WorkItem/View.aspx?WorkItemId=1005. The error message was:

Exception occurred while initializing the installation: System.IO.FileNotFoundException: Could not load file or assembly 'file:///C:\WINDOWS\system32\and' or one of its dependencies. The system cannot find the file specified.

Obviously, there is no such "C:\WINDOWS\system32\and" file in our application or on the system. MSI logs and fusion logs would not give any more information.

We have been puzzled by this issue for quite some time. We have finally removed features from our setup one by one until we have been able to identify that a specific custom action was at the source of the problem.

In the literature and samples that you will find regarding developing Visual Studio setup projects, custom action data is passed in the form /NAME = VALUE which works fine unless value contains a space.

The reference documentation at http://msdn2.microsoft.com/en-us/library/2w2fhwzz.aspx states that in setup custom actions, data should be passed as:

/NAME = VALUE if not a directory and value has no space
/NAME = "VALUE" if spaces are expected in a value which is not a directory
/NAME = "VALUE\" if the expected value is a directory

I wish we had not simply read how-to articles and sample code, but also the reference documentation.

Monday, March 17, 2008

Installing Visual Studio Extensions for WCF after an upgrade to .NET 3.0 Service Pack 1

Yesterday, I got stuck installing Visual Studio 2005 extensions for .NET Framework 3.0 (WCF & WPF), November 2006 CTP after my computer had been upgraded to .NET Framework 3.0 Service Pack 1. The installation fails with a warning that a prerequisite, i.e. Microsoft .NET framework 3.0, is missing.

A quick search on the Internet gave me the following trick which works:

  1. Add the following key and value to the registry aimed at luring the installer:
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{15095BF3-A3D7-4DDF-B193-3A496881E003}]
    "DisplayName"="Microsoft .NET Framework 3.0"
  2. Install vsextwfx.msi.
  3. Remove the key above.

Source: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2550726&SiteID=1.
Thanks Erich.