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.