Configuration File Settings
The .NET Framework allows applications to store settings in XML configuration files. In many cases, these settings are known only to the applications
An application's settings can be specified in one of a number of XML configuration files arranged in a hierarchy. At a minimum, settings can be supplied in either a configuration file that affects all applications on a machine or in a file specific to a particular application. Some scenarios support more
All application scenarios supported by the CLR include the notion of machine-wide configuration settings. These settings are stored in a file with a fixed
AppDomainSetup adSetup = new AppDomainSetup();
adSetup.ApplicationBase = @"c:\MyApp";
adSetup.ConfigurationFile = @"c:\MyApp\MyApp.exe.config";
AppDomain ad = AppDomain.CreateDomain("MyApp", null, adSetup);
Other application scenarios work the same way. ASP.NET uses the same technique to associate a web.config file with a domain, for example.
The
ConfigurationBytes
property enables you to specify your configuration file as an array of bytes instead of by supplying the name of a configuration file on disk. The byte array you pass to
ConfigurationBytes
is simply the contents of your XML configuration file laid out in memory. Specifying a configuration file with
ConfigurationBytes
is
|
Shadow Copy Settings
One reason ASP.NET applications are easier to deploy and manage than their Active Server Pages (ASP) counterparts is because of a feature called shadow copy. Using shadow copy enables ASP.NET Web applications to be updated dynamically without shutting down and restarting the Web server. Because the application can be updated without
Shadow copy works by making copies of the files it is
Four properties on AppDomainSetup enable you to configure the shadow copy feature when creating a new domain: ShadowCopyFiles, CachePath, ApplicationName , and ShadowCopyDirectories . At a minimum, you must use ShadowCopyFiles . The other three properties are used to customize the way shadow copy works. To enable shadow copy for an application domain, complete the following steps:
These steps are described in detail in the sections that follow. Turning on Shadow Copy
Shadow copy is off by default. To turn it on, you must set the value of the
ShadowCopyFiles
property on
AppDomainSetup
to any non-null string.
ShadowCopyFiles
is a string value and not a
boolean
as you'd probably expect. As a result, setting the value to any string enables shadow copy, although most
using System;
AppDomainSetup setup = new AppDomainSetup();
setup.ShadowCopyFiles = "true";
AppDomain newAD = AppDomain.CreateDomain("Shadow Copy Domain",), null, setup);
Specifying the Location for the Copied FilesAs described, shadow copy works by making a copy of each file it is asked to load. As a result, after you've enabled shadow copy, you need to decide where you'd like the CLR to store the copied files. By default, the CLR will copy the files to a location in the downloaded files cache . This is the same cache that is used to store files that have been downloaded from another machine while running an application. If you'd rather not use the downloaded files cache, you can specify a directory in which the CLR will place the copied files by using the CachePath and ApplicationName properties on AppDomainSetup .
Note
Whether you choose to use the downloaded files cache or to specify a custom location depends on your scenario. The primary advantage of storing the copied files in the downloaded files cache is that you don't have to worry about cleanup. The cache has a size limit that, when reached, causes the files used least recently to be deleted. As a result, you're free to cause files to be copied into the cache and they will eventually get deleted automatically. If you specify a custom directory in which to store the files, you're responsible for deleting them when they're no longer used. Using the downloaded files cache does have its disadvantages, however. First, because the CLR cleans up the cache based on
If you decide that a custom location works better for your scenario, you must set both the
CachePath
and
ApplicationName
properties on
AppDomainSetup
when you create a new domain. The CLR
Always remember that if you use CachePath and ApplicationName to specify a custom shadow copy location, you're responsible for deleting those directories when they are no longer needed. In part because shadow copy target directories don't map one-to-one with application domains, the CLR has no idea when it's OK to clean up the copied files. So it's up to you to decide when no running domains rely on a particular shadow copy location so you can delete the directory. Specifying Which Files Are CopiedBy default, all assemblies the CLR loads out of a domain's ApplicationBase directory structure are shadow copied. This includes assemblies found by the CLR's default loading rules (that is, those in the ApplicationBase and those in subdirectories named by version and culture) as well as any assemblies located in subdirectories specified using the PrivateBinPath property of AppDomainSetup . If you don't want all privately located assemblies shadow copied, you can use the ShadowCopyDirectories property of AppDomainSetup to customize this behavior. ShadowCopyDirectories takes a semicolon-delimited set of subdirectories containing files that you do want to leave unlocked through shadow copy. If you specify a list of subdirectories using ShadowCopyDirectories , all files loaded out of directories not on the list, including the ApplicationBase itself, will not be copied before being loaded. For example, the following code creates an application domain such that only those files in the bin subdirectory of ApplicationBase are shadow copied:
using System;
AppDomainSetup setup = new AppDomainSetup();
setup.ShadowCopyFiles = "true";
setup.ShadowCopyDirectories = "bin";
AppDomain newAD = AppDomain.CreateDomain("Shadow Copy Domain", null, setup);
The System.AppDomain class also has a method called SetShadowCopyPath that you can use to set the list of directories copied by shadow copy after the application domain has been created. The Shadow Copy SampleThe shadowcopy.exe sample included with this book is a Windows Forms application that shows how to use the ShadowCopyFiles, CachePath , and ApplicationName properties of AppDomainSetup to configure the shadow copy feature. Shadowcopy.exe creates secondary application domains configured for shadow copy based on the options you pick in the user interface. For example, shadowcopy.exe allows you to select either the default or a custom location in which the CLR will place the copied files. The shadowcopy.exe application is shown in Figure 6-2. Figure 6-2. The shadowcopy.exe sample
Shadowcopy.exe determines when to create a new application domain by using the
System.IO.FileSystemWatcher
class to monitor changes made to a particular assembly. Each time the file is changed, a new domain is created in which to load the new file. As domains are created, their friendly
C:\<project directory>\bin\Debug\
ShadowCopy.exe
CopySampleAppBase\
CopiedFile.dll
Note that under the location into which shadowcopy.exe is built is a subdirectory called
CopySampleAppBase
. This directory serves as the
ApplicationBase
for all new domains created by shadowcopy.exe. In the
CopySampleAppBase
directory is a file called copiedfile.dll. This is the file containing the assembly that the
FileSystemWatcher
class is configured to monitor. Copiedfile.dll can be replaced in a few different ways. First, you can
When a new domain is created based on a change to copiedfile.dll, shadowcopy.exe sets the target location for the copied files based on the values of the controls in the Shadow Copy Destination
'Domain for Copy 0': Loaded 'c:\documents and settings\stevenpr\ local settings\application data\assembly\dl2kokvbt.qmq\m96q78ev.m4o\ 7c177d03791c6f_4a37c301\copiedfile.dll', Symbols loaded.
A few interesting characteristics about this path are worth pointing out. First, the
In contrast, if you specify a custom copy location, the CLR will load copiedfile.dll from that directory. Assuming that you've told the CLR to save the copied files to c:\program files\extapp\shadow\401k, the debugger's Output window will include the following line: 'Domain for Copy 0': Loaded 'c:\program files\extapp\shadow1k\assembly\dl2\ 7c177d03791c6f_4a37c301\copiedfile.dll', Symbols loaded.
As described, each new application domain created by shadowcopy.exe is displayed in the user interface.
To summarize, the shadowcopy.exe sample works as
The code for shadowcopy.exe is shown in the following listing. I've deleted much of the Windows Forms
Listing 6-1. shadowcopy.exe Source Code
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
using System.Diagnostics;
using System.Threading;
using System.Reflection;
using CopiedFile;
namespace ShadowCopy
{
public class frmShadowCopy : System.Windows.Forms.Form
{
// Windows Forms locals omitted...
private FileSystemWatcher m_watcher;
private String m_copyAppBase;
private int m_numCopies;
public static frmShadowCopy ShadowCopyAppForm ;
public static DateTime eventTime;
// This event handler receives the "File Changed" event generated by
// FileSystemWatcher when copiedfile.dll gets overwritten. Three events
// get raised for every file copy so I use time to make sure I don't
// process the "extra" events.
private static void OnFileChanged(object source, FileSystemEventArgs e)
{
TimeSpan t = DateTime.Now - frmShadowCopy.eventTime;
if (t > (new TimeSpan(TimeSpan.TicksPerSecond)))
{
frmShadowCopy.ShadowCopyAppForm.CreateDomainForNewFile();
}
frmShadowCopy.eventTime = DateTime.Now;
}
public frmShadowCopy()
{
// Required for Windows Forms Designer support
InitializeComponent();
// Initialize a private string to hold the name of the ApplicationBase
// for all new domains I create.
m_copyAppBase = AppDomain.CurrentDomain.BaseDirectory +
"CopySampleAppBase";
// Initialize the event time so we handle only one FileChange event from
// the FileSystemWatcher.
frmShadowCopy.eventTime = DateTime.Now;
// I keep track of the number of copies made just as a convenience for
// assigning friendly names to application domains and to initialize the
// the "Application Name" field in the UI with a reasonable
// value for the next copy.
m_numCopies = 0;
}
[STAThread]
static void Main()
{
frmShadowCopy frm = new frmShadowCopy();
frmShadowCopy.ShadowCopyAppForm = frm;
Application.Run(frm);
}
private void frmShadowCopy_Load(object sender, System.EventArgs e)
{
// Initialize the FileSystemWatcher so OnFileChanged is called each time
// copiedfile.dll is overwritten.
m_watcher = new FileSystemWatcher();
m_watcher.Path = m_copyAppBase;
m_watcher.Filter = "CopiedFile.dll";
m_watcher.NotifyFilter = NotifyFilters.LastWrite;
m_watcher.Changed += new FileSystemEventHandler(OnFileChanged);
m_watcher.EnableRaisingEvents = true;
// Initialize the CachePath and ApplicationName text boxes to point to a
// directory under the AppBase.
txtCachePath.Text = m_copyAppBase;
txtAppName.Text = "Copy" + m_numCopies.ToString();
}
private void btnCopy_Click(object sender, System.EventArgs e)
{
// Copy over copiedfile.dll. This causes the FileSystemWatcher
// to raise an event that we'll catch to begin the process
// of creating a new application domain.
File.Copy(AppDomain.CurrentDomain.BaseDirectory + "CopiedFile.dll",
m_copyAppBase + "\CopiedFile.dll", true);
}
// This method gets called from the OnFileChanged event handler. It
// contains all the logic to create a new application domain
// configured for shadow copy.
public void CreateDomainForNewFile()
{
String copyLocation;
// Create a new instance of AppDomainSetup and turn shadow copy on by
// setting ShadowCopyFiles.
AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationBase = m_copyAppBase;
setup.ShadowCopyFiles = "true";
// Determine where the files should be copied to. Initialize CachePath
// and ApplicationName appropriately.
if (rbCustom.Checked)
{
if ((txtCachePath.TextLength == 0) (txtAppName.TextLength == 0))
{
MessageBox.Show("You must enter values for Cache Path
and Application Name when specifying
a custom copy location");
return;
}
setup.CachePath = txtCachePath.Text;
setup.ApplicationName = txtAppName.Text;
copyLocation = setup.CachePath + "\" + setup.ApplicationName;
}
else
{
copyLocation = "Default";
}
// Create a new domain.
AppDomain newAD = AppDomain.CreateDomain("Domain for Copy " +
m_numCopies.ToString(), null, setup);
// Load the assembly into the domain. As a side effect of this
// call to Load, copiedfile.dll will be copied to the "copied files
// location" and will be loaded from there, thereby leaving the
// original unlocked so it can be overwritten.
Assembly asm = newAD.Load("CopiedFile", null);
// Add the new domain to the list displayed in the UI.
ListViewItem newLVI = new ListViewItem(new string[] {
newAD.FriendlyName , copyLocation}, -1);
newLVI.Tag = newAD;
listView1.Items.Add(newLVI);
m_numCopies++;
// Update the AppName text box so we copy to a new location next time.
txtAppName.Text ="Copy" + m_numCopies.ToString();
}
private void btnUnload_Click(object sender, System.EventArgs e)
{
if (listView1.CheckedItems.Count == 0)
{
MessageBox.Show("Please select one or more Domains to unload
by selecting the check boxes");
return;
}
foreach (ListViewItem i in listView1.CheckedItems)
{
AppDomain ad = (AppDomain)i.Tag;
// If a custom shadow copy location was specified, we need to
// delete the contents of the directory after the domain is
// unloaded. Remember the name of the custom directory here
// (if one was specified).
String delDirectory = ad.SetupInformation.CachePath + "\" +
ad.SetupInformation.ApplicationName;
// Unload the domain and remove the entry from the UI.
AppDomain.Unload(ad);
listView1.Items.Remove(i);
// Delete the shadow copy directory if any. We check to see
// if it's greater than one because above we added "\".
if (delDirectory.Length > 1)
{
Directory.Delete(delDirectory, true);
}
}
}
}
}
|