Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

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.

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.