April 09, 2009
ProjectAggregator
November 12, 2008
How to access the IIS Metabase programmatically, IIS metabase
using System.Collections.Generic;
using System.Text;
using System;
using System.Data;
using System.Configuration;
using System.DirectoryServices;
using System.Web.Configuration;
namespace IISMetabase
{
class IISMetabase
{
private string _machineName = "localhost";
private System.Collections.IDictionary _applications;//collection of websites
private Configuration _config;
public IISMetabase()
{
_applications = new System.Collections.Hashtable();
}
public System.Collections.IDictionary GetApplications()
{
DirectoryEntry webentry = new DirectoryEntry();
String path = "IIS://" + _machineName + "/W3SVC/1/ROOT";
webentry.Path = path;
Boolean exists = false;
try
{
exists = DirectoryEntry.Exists(path);
}
catch (System.Runtime.InteropServices.COMException e)
{
String error = e.Message;
Console.WriteLine(error + "\n");
}
if (exists)
{
DirectoryEntries webSiteChildren = webentry.Children;
foreach (DirectoryEntry website in webSiteChildren)
{
String scn = website.SchemaClassName;
String se = website.SchemaEntry.ToString();
if (website.Properties.Contains("KeyType"))
{
String webName = website.Name;
try
{
Object keyTypeValue = website.InvokeGet("KeyType");
String keyTypeValueStr = keyTypeValue.ToString();
if (!keyTypeValueStr.Equals(""))
{
try
{
_config = WebConfigurationManager.OpenWebConfiguration("/" + webName);
_applications.Add(webName, _config);
}
catch (ConfigurationErrorsException err)
{
String error = err.Message;
Console.WriteLine(error + "\n");
}
}
}
catch (Exception err)
{
Console.WriteLine(err.Message + "\n");
continue;
}
}
}
}
else
{
String err = "The Directory " + webentry.Path + "does not exist\n";
Console.WriteLine(err + "\n");
}
return _applications;
}
}
}
August 15, 2008
ActiveX Stopwatch Timer Control
<OBJECT id="activeXTimerControl" name="activeXTimerControl"
classid="clsid:4F3B57CE-D9CA-41c3-ACBD-EBB2380990E7" style="width: 504px; height: 182px" codebase="Bin/ActiveXTimerControl.dll" class="ActiveXTimerControl.TimerControl"></OBJECT>
To start the Stopwatch call Start() and to end call End() then call Time() to return the result as a string or use the AddTimeToGrid to add the time to a DataGrid Control (supplied by the StopWatch control) or call AddTimeToLabel which adds the time to a textbox.
activeXTimerControl.Start();
///do someting
activeXTimerControl.Stop();
var time = activeXTimerControl.Time;
activeXTimerControl.AddTimeToGrid("my timer", time);
activeXTimerControl.AddTimeToLabel("my timer", time);
Here's the full csharp code and at the end the full html
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace ActiveXTimerControl
{
[ClassInterface(ClassInterfaceType.AutoDual)]
[GuidAttribute("4F3B57CE-D9CA-41c3-ACBD-EBB2380990E7")]
public partial class TimerControl : UserControl, IActiveXTimerControl
{
public TimerControl()
{
InitializeComponent();
}
}
}
namespace ActiveXTimerControl
{
public interface IActiveXTimerControl
{
///
/// Add resulting timer name and time (as a string) to the GridView.
///
///
///
void AddTimeToGrid(string timerName, string time);
///
/// Add the resulting timer name and the time (as a string) to the Label.
///
///
///
void AddTimeToLabel(string timerName, string time);
///
/// Start the timer.
///
void Start();
///
/// Stop the timer, call Start() before this.
///
void Stop();
///
/// Get the Time between the Start() and Stop() in milliseconds.
///
string Time { get; }
}
partial class TimerControl : IActiveXTimerControl
{
///
/// Required designer variable.
///
private System.ComponentModel.IContainer components = null;
///
/// Clean up any resources being used.
///
/// true if managed resources should be disposed; otherwise, false.
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
this._timerDataGridView = new System.Windows.Forms.DataGridView();
this._timerNameColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
this._timerTimeColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
this._timerLabel = new System.Windows.Forms.Label();
this._timerTextBox = new System.Windows.Forms.TextBox();
((System.ComponentModel.ISupportInitialize)(this._timerDataGridView)).BeginInit();
this.SuspendLayout();
//
// _timerDataGridView
//
this._timerDataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this._timerDataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this._timerNameColumn,
this._timerTimeColumn});
this._timerDataGridView.Location = new System.Drawing.Point(6, 108);
this._timerDataGridView.Name = "_timerDataGridView";
this._timerDataGridView.Size = new System.Drawing.Size(261, 106);
this._timerDataGridView.TabIndex = 0;
//
// _timerNameColumn
//
this._timerNameColumn.HeaderText = "Timer Name";
this._timerNameColumn.Name = "_timerNameColumn";
//
// _timerTimeColumn
//
this._timerTimeColumn.HeaderText = "Time (msec)";
this._timerTimeColumn.Name = "_timerTimeColumn";
//
// _timerLabel
//
this._timerLabel.AutoSize = true;
this._timerLabel.Location = new System.Drawing.Point(223, 78);
this._timerLabel.Name = "_timerLabel";
this._timerLabel.Size = new System.Drawing.Size(129, 13);
this._timerLabel.TabIndex = 1;
this._timerLabel.Text = "Timer Name | Time (msec)";
//
// _timerTextBox
//
this._timerTextBox.Location = new System.Drawing.Point(6, 20);
this._timerTextBox.Multiline = true;
this._timerTextBox.Name = "_timerTextBox";
this._timerTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this._timerTextBox.Size = new System.Drawing.Size(261, 55);
this._timerTextBox.TabIndex = 2;
this._timerTextBox.Text = "Timer Name | Time (msec)";
//
// TimerControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this._timerTextBox);
this.Controls.Add(this._timerLabel);
this.Controls.Add(this._timerDataGridView);
this.Name = "TimerControl";
this.Size = new System.Drawing.Size(383, 313);
((System.ComponentModel.ISupportInitialize)(this._timerDataGridView)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
public void Start()
{
if (_stopWatch == null)
_stopWatch = new System.Diagnostics.Stopwatch();
_stopWatch.Start();
}
public void Stop()
{
_stopWatch.Stop();
}
public string Time
{
get
{
return string.Format("{0}", _stopWatch.ElapsedMilliseconds);
}
}
private System.Diagnostics.Stopwatch _stopWatch;
private System.Windows.Forms.DataGridView _timerDataGridView;
public void AddTimeToGrid(string timerName, string time)
{
if (_timerDataGridView == null)
_timerDataGridView = new System.Windows.Forms.DataGridView();
_timerDataGridView.Rows.Add(timerName, time);
this.Refresh();
}
private System.Windows.Forms.DataGridViewTextBoxColumn _timerNameColumn;
private System.Windows.Forms.DataGridViewTextBoxColumn _timerTimeColumn;
private System.Windows.Forms.Label _timerLabel;
public void AddTimeToLabel(string timerName, string time)
{
//if (_timerLabel == null)
// _timerLabel = new System.Windows.Forms.Label();
//_timerLabel.Text += ( "\n" + timerName + " " + time);
//this.Refresh();
if (_timerTextBox == null)
{
_timerTextBox = new System.Windows.Forms.TextBox();
_timerTextBox.Text = "Timer Name | Time (msec)";
}
_timerTextBox.Text += ("\r\n" + timerName + " " + time);
this.Refresh();
}
private System.Windows.Forms.TextBox _timerTextBox;
}
}
The aspx:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<OBJECT id="activeXTimerControl" name="activeXTimerControl" classid="clsid:4F3B57CE-D9CA-41c3-ACBD-EBB2380990E7" style="width: 504px; height: 182px" codebase="Bin/ActiveXTimerControl.dll" class="ActiveXTimerControl.TimerControl">
</OBJECT>
<form name="frm" id="frm">
<input type=button value="Click me" onClick="doScript();">
</form>
</body>
<script language="javascript">
function doScript()
{
activeXTimerControl.Start();
activeXTimerControl.Stop();
var time = activeXTimerControl.Time;
activeXTimerControl.AddTimeToGrid("my timer", time);
activeXTimerControl.AddTimeToLabel("my timer", time);
}
</script>
</html>
August 14, 2008
AcitveX Controls - how to create one and call with Javascript
You can then call this User Control from Javascript in your client.
There's nothing magical about this except, it only works on IE and there are a few small bits that you must implement in order for your User Control to be picked up, the errors you get aren't very helpful so I recommend following these steps (Some of these I'm not sure if they're required but they've worked for me).
- Create a Class Project in Visual Studio 2005.
- Add a User Control to the project.
- In the User Control Designer add a TextBox Control
- Add a Property to your class to set the Text of the TextBox you've just added.
{
if (_timerTextBox == null)
{
_timerTextBox = new System.Windows.Forms.TextBox();
_timerTextBox.Text = " Time (msec)";
}
_timerTextBox.Text += ("\r\n" + time);
this.Refresh();
}
- Create an Interface in the same namespace as your UserControl and add the signature of the Property you've just added.
{
void AddText( string time);
}
- Mark the Class with a the GuidAttribute and also inherit it from the interface you've just created.
{
[ClassInterface(ClassInterfaceType.AutoDual)]
[GuidAttribute("4F3B57CE-XXXX-XXXX-ACBD-EBB2380990E7")]
public partial class TimerControl : UserControl, IActiveXTimerControl
{
public TimerControl()
{
InitializeComponent();
}
}
}
- Mark the Assembly as Com Visible in the Project Properties "Assembly Information".
- Compile this and register it by adding a Post-Build Event to your project
- regasm $(TargetFileName) /tlb /codebase
You now have an Assembly which has a control usable in a browser.
Now to use this Control
Create a website and add the following aspx page (the content of this could also be in html page) inside the body
function doScript()
{
var time = "some time";
activeXTimerControl.AddText( time);
}
Note the clsid above this is crucial it must be the same GUID value as you gave the Class.
August 07, 2008
Localization and Globalization
ASP.NET provides all the Localization you need by just adding .RESX files to your Web App in the right location but sometimes you want to invoke the Localization yourself. To do this you'll use the System.Resources.ResourceManager class. This allows you to get resources by simply supplying the name of the resource and the Culture but to setup the resource itself is a bit trickier (in Visual Studio 2005) as some of the files required have been removed from Visual Studio's Templates. Because of this you'll need to use some tools to create the Resource (and optionally the Assembly to store it know as Resource Assemblies or Satellite Assemblies).
There are 3 steps to creating resources for use with ResourceManager:
1. Write a text file or .RESX file to hold your resources.
2. Compile the resources in step 1 into a .RESOURCES file.
3. Compile the .RESOURCES file in step 2 into a Satellite Assembly.
4. Locate the Satellite Assembly from step 3 into a location which is recognised by the Localisation mechanism.
1. If your resources are all strings then the easiest thing to do is add them to a .TXT file. If your resources are more than strings e.g. images then you should probably use a .RESX file. .RESX files are the usual location for resources. These are added to your Class Library Project in VS by selecting the project and clicking the Add File and then choosing Resource File.
If you wish to use a .TXT file then here's how you enter the resources:
myFirstResource = here is my first resources value
mySecondResource = here is my second resources value
(
Note that the .TXT file must be saved with UTF-8 support, you'll probably find this in your text editor save options somethere, notepad has it, this is required to support some culture specific characters like accents).2. The .TXT file or .RESX file is not enough for the ResourceManager, this .RESX file has to be converted to a .RESOURCES file before it can be used, this can be done with the tool, ResGen.exe,
resgen strings.en.txt strings.resources
resgen strings.es.txt strings.es.resources.
for more see http://msdn.microsoft.com/en-us/library/xbx3z216(VS.80).aspx
If you wish to use these bare .RESOURCES files then you can by following this http://msdn.microsoft.com/en-us/library/khyt7e7y.aspx but note that there are issues with Concurrent access on IIS so you may want to embed the resource in an Assembly (next step).
3. This .RESOURCES file can then be embedded in your DLL for later use by the ResourceManager but I found some little caveats here which cost me at least 1 day to figure out. This caveat is that the naming of the .RESOURCES files is also used as well as the DLL.
What I found it that for other cultures the .RESOURCES file must be named with that Culture as it does get used e.g.
The Default culture is English (en), the other culture used is Spanish (es).
You create 2 .TXT files to store the resources
strings.txt and strings.es.txt.
You then generate 2 .RESOURCES files from these with, it is these files that must have the correct naming i.e. the Spanish version must have the ".es" in the name before the extension.
resgen strings.en.txt strings.resources
resgen strings.es.txt strings.es.resources.
Now you use a Linker tool call AL.exe to embed each resource into it's own dll, note that you supply the culture here at the command line and also you must name the DLLS for the Cultures other than the default to have the ".resources" before the extentsion:
al /embed:strings.resources /culture:en /out:strings.dll
al /embed:strings.es.resources /culture:es /out:strings.resources.dll
You now copy only the .dll into the correct directories i.e. if it's aWebsite the root directory is the "bin" directory and it it's a standalone app then just copy into the same dir as the .exe.
Place the default assembly. strings.dll into the root and them create a subdirectory "es" and place the Spanish DLL in there
e.g.
for more see http://msdn.microsoft.com/en-us/library/21a15yht(VS.71).aspx
4. If you use the bare .RESOURCES file then you can use the method described here http://msdn.microsoft.com/en-us/library/khyt7e7y.aspx. All you need to do is name the files uniquely using the pattern described, e.g. strings.es.resources for Spanish. The use the ResourceManager _rm = ResourceManager.CreateFileBasedResourceManager("strings",Path.Combine( AppDomain.CurrentDomain.BaseDirectory, "Resources" ), null);
CultureInfo cInfo = new CultureInfo(culture);
string result = _rm.GetString(resourceStringName, cInfo);
If you use an Assembly to store your Resources then you cannot use the CreateFileBasedResourceManager instead you must use the ResourceManager's constructor.
Assembly stringsAssembly = Assembly.Load("strings");//the default assemblies name see step 3.
ResourceManager rm = new ResourceManager("strings", stringsAssembly );//"Strings" is the resource name of the default Culture i.e. strings.resource that you embedded earlier in the DLL.
string result = _rm.GetString("myFirstResource", new CultureInfo("es"));//get me the resource from the Spanish Culture DLL.
May 23, 2008
VSPackage - moving the assembly out of the GAC
First of all in order for Visual Studio to find the Package in it's new location you need to tell it where to find it, this is done in the Registry with the CodeBase key.
This is done in the Registry at
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Packages\{1237c0fa-4f6e-43c5-9ee4-a5938847c54a}]
"Codebase"="C:\\Program Files\\MyCompany\\MyProduct\\MyPackageAssembly.dll"
This is also the case for other classes which have GUIDs in your package.
Use regasm with the /codebase switch to tell you which classes may need an additional registry entry.
April 17, 2008
Automated update of AssemblyVersion and AssemblyFileVersion
This implementation fits my own problem, a build script which gets all our codebase sourcecode from SourceSafe, builds all the projects and then labels all the sourecode with a single label passed in by the builder at command-line.
This implementation takes either 1 or 2 arguments.
If only 1 is supplied it will do a check for a valid Version value i.e. the version must be of the format 1.0.0.0 and the third value must be less than 65535 (This is a .NET requirement, the assembly would not build if the number is above 65535, see here for more).
If 2 arguments are supplied it will do all that step 1 does and also will set the version to AssemblyVersion and AssemblyFileVersion in all AssemblyInfo.cs files under the directory specified (and it's subdirectories).
e.g. here's how I call it from my build.bat, NOTE AssemblyUpdateVersion.exe is the name of the Console app which I built the sourcecode shown later into.
@echo off
setlocal
c:
pushd ..\
echo "%cd%"
set CDIR=%cd%
if "%1" neq "" goto update_assembly_version
if "%1"=="" goto end
:update_assembly_version
REM Validate the label using the Custom tool AssemblyUpdateVersion.exe
call AssemblyUpdateVersion.exe %1 || goto validate_version_error
echo -- Update the Versions of the Assemblies before Building
REM First checkout all of the AssemblyInfo.cs files
set SSDIR=\\SourceSafe\sscodebase || goto error
call sscodebase C:\src checkout -R AssemblyInfo*.cs
REM now apply the new label to the AssemblyInfo.cs files using our custom tool AssemblyUpdateVersion.exe
call AssemblyUpdateVersion\bin\Debug\AssemblyUpdateVersion.exe %1 %CDIR% || goto update_version_error
REM now checking the file with the new version
call sscodebase . checkin -R AssemblyInfo*.cs || goto checkin_error
:end_update_assembly_version
goto great_success
:validate_version_error
echo --- Error with Version Validation
goto end
:end_validate_version_error
:checkout_error
echo --- Error with checkout
goto end
:end_checkout_error
:update_version_error
echo --- Error with Update Version
goto end
:end_update_version_error
:checkin_error
echo --- Error with checkin
goto end
:end_checkin_error
:great_success
echo --- Great Success!!!
goto end
:end_great_success
:end
Here's the code:
First File:
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace AssemblyUpdateVersion
{
///
/// Apply Version information to Assemblies before they are built by editing the AssemblyInfo.cs
/// Or just Validate the Version number against a valid format by supplying a single argument e.g. 1.0.0.1
/// Or Validate and Update the Version number by supplying 2 arguments e.g. 1.0.0.1 and C:\Galaxy\src
///
public class Program
{
static int Main(string[] args)
{
string versionValueEntered;
string directory;
if (args.Length == 1)
{
versionValueEntered = args[0];
if (!ValidateVersion(versionValueEntered))
return 1;
}
else if (args.Length == 2)
{
versionValueEntered = args[0];
directory = args[1];
if (!RunUpdateVersion(versionValueEntered, directory))
return 1;
}
else
{
Console.WriteLine("The new Version must be provided e.g. 1.2.3.4 ");
Console.WriteLine("The root directory must also be provided.");
return 1;
}
return 0;//Success
}
public static bool RunUpdateVersion(string version, string directory)
{
if (!ValidateVersion(version))
return false;
AssemblyUpdateVersion assemblyUpdateVersion = new AssemblyUpdateVersion(version, directory);
return true;
}
public static bool ValidateVersion(string version)
{
Console.WriteLine("ValidateVersion with {0}", version);
//Validate the format of the value entered. It must be of the format
//all ints and the
Regex assemblyVersionPattern = new Regex("[0-9]*[.][0-9]*[.][0-9]*[.][0-9]*");
if (!assemblyVersionPattern.IsMatch(version))
{
Console.WriteLine("The AssemblyVersion must be in the format 1.0.0.0.");
return false;
}
string[] values = version.Split('.');
if (values.Length != 4)//must be 4 values
{
Console.WriteLine("The AssemblyVersion must be in the format 1.0.0.0.");
return false;
}
else
{
int result;
Int32.TryParse(values[2], out result);
if (result > 65535)
{
Console.WriteLine("The AssemblyVersion must be in the format 1.0.0.0 and the 3rd value
return false;
}
}
return true;
}
}
}
2nd File:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
namespace AssemblyUpdateVersion
{
public class AssemblyUpdateVersion
{
private string _valueEntered;
private string _directory;
public AssemblyUpdateVersion(string valueEntered, string directory)
{
_valueEntered = valueEntered;
_directory = directory;
ChangeVersion();
}
///
/// Change the version number of all AssemblyInfo.cs files found in the current directory.
///
private void ChangeVersion()
{
string newAssemblyVersion;
string newAssemblyFileVersion;
string inputFile = "AssemblyInfo.cs";
string tempFile = "AssemblyInfoTemp.cs";
DirectoryInfo dirInfo = new DirectoryInfo(_directory);
foreach (FileInfo assemblyInfoFileInfo in GetFilesRecursive(dirInfo, inputFile))
{
string tempFileFullName = Path.Combine(assemblyInfoFileInfo.Directory.FullName, tempFile);
//[assembly: AssemblyVersion("1.2.3.9")]
newAssemblyVersion = @"[assembly: AssemblyVersion(""" + _valueEntered + @""")]";
//[assembly: AssemblyFileVersion("1.0.0.0")]
newAssemblyFileVersion = @"[assembly: AssemblyFileVersion(""" + _valueEntered + @""")]";
try
{
using (StreamReader sr = new StreamReader(assemblyInfoFileInfo.OpenRead()))
{
using (StreamWriter sw = new StreamWriter(tempFileFullName, false, sr.CurrentEncoding))
{
String line;
while ((line = sr.ReadLine()) != null)
{
if (line.Contains("AssemblyVersion"))
{
string result = line.Replace(line, newAssemblyVersion);
sw.WriteLine(result);
}
else if (line.Contains("AssemblyFileVersion"))
{
string result = line.Replace(line, newAssemblyFileVersion);
sw.WriteLine(result);
}
else
{
sw.WriteLine(line);
}
}
}
}
File.Copy(tempFileFullName, assemblyInfoFileInfo.FullName, true);
File.Delete(tempFileFullName);
Console.WriteLine("Successfully set Version to {0} on File {1} ", newAssemblyVersion, assemblyInfoFileInfo.FullName);
}
catch (UnauthorizedAccessException uae)
{
Console.WriteLine("Access Denied: ");
Console.WriteLine(uae.Message);
}
catch (Exception e)
{
// Let the user know what went wrong.
Console.WriteLine("The file could not be read:");
Console.WriteLine(e.Message);
}
}
}
public IEnumerable
{
foreach (DirectoryInfo di in dirInfo.GetDirectories())
foreach (FileInfo fi in GetFilesRecursive(di, searchPattern))
yield return fi;
foreach (FileInfo fi in dirInfo.GetFiles(searchPattern))
yield return fi;
}
}
}
February 06, 2008
Configuration namespace and reading settings from the Configuration file,app.config,web.config
There are a few different ways to read settings in from a .config file.
The confusion for me occurs when reading from certain sections and from config files other than ones for the currently running application.
The easiest example is reading settings from the currently running application, from within it's own code.
You can access the AppSettings Section
<configuration><appSettings>
<add key="hostname1" value="localhost"/><add key="hostname2" value="othername"/></appSettings></configuration>
using:
NameValueCollection appSettings = ConfigurationManager.AppSettings;
foreach(string key in appSettings.AllKeys)
Console.WriteLine(key + " " + appSettings[key]);
So now you've all the keys in the AppSettings section i.e. "hostname1" and "hostname" and all their values accessed with appSettings[key].
Note: First you must add a reference to 'System.Configuration.dll' to your project and also include the namespace 'using System.Configuration;' You'll also need 'using System.Collections.Specialized;' in order to use the 'NameValueCollection' type.
Similarly but slightly more tricky you can access the settings of another assembly by using the following:
Configuration config = ConfigurationManager.OpenExeConfiguration(@"
KeyValueConfigurationCollection kvpCollection = config.AppSettings.Settings;
foreach (string key in kvpCollection.AllKeys)
Console.WriteLine(key + " " + kvpCollection[key].Value);
You'll see the Settings collection is access in the same way and can be enumerated in the same way but the 'value' of the AppSetting must be accessed using the .Value property?
This is because the former call to AllKeys returns a NamedValueCollection and the latter returns a KeyValueCollection, this means that for one you have access to the values as strings themselves and the other you have access to the type KeyValueConfigurationElement, if you want to get the string value you additionally need to call the Value property!
Open a config for a dll, a dll being referenced by an exe
This reads the config file that's in the location that the exe is referencing the dll from, actually it makes a copy of the dll to the exes bin dir, so you have to add your dll's config file to the exe's bin directory and withing the dll's code add the following
//Only finds copy of the assembly as it's added as a reference to the exe.
System.Reflection.Assembly ass = System.Reflection.Assembly.GetAssembly(typeof(Class1));
System.Reflection.AssemblyName assName = ass.GetName();
string codeBase = assName.CodeBase;
int length = "file:///".Length;//remove the "file:///" from the string
string result = codeBase.Substring(length);
config = ConfigurationManager.OpenExeConfiguration(result);//open the dll's config
kvpCollection = config.AppSettings.Settings;
value = kvpCollection["hostname1"].Value;
February 04, 2008
HttpHandlers
The HttpHandler class is usually stored inside a .ashx file.
You can create your on .ashx and place it in you web app root dir, it will be entered whenever a request comes in from the client.
If you do not want to show your HttpHandler file code in your web app then you can hide it in a dll see details below.
How to hide the .ashx with a HttpHandler
I'll describe here how to create a handler for a file with the extension .ashx, it's coincidental I require a HttpHandler in my web.config in order to alias my .ashx file which is too a HttpHandler.
Note: Before you can compile a HttpHandler into a dll you must remove the directive at the start of the .ashx file e.g. delete this line
<%@ WebHandler Language="C#" Class="MyWebHandler" %>
If you fail to do this your .ashx code will not appear in your dll and you will not be able to register the type MyWebHandler.
One way to hide .ashx files is by a creating a handler for .ashx files.
The Handler is a HttpHandler.
This Handler will handle any calls to to files with the extension .ashx, this could be any extesion (I think), whatever extension you want will be supplied in the Web Applications web.config
e.g. if you created a HttpHandler and you want it to cater for all .ashx files in your Web Application add something like this to your Web Application's web.config
<system.web>
<httpHandlers>
<add verb="*" path="*.ashx"
type="MyWebHandler.Service1,
WebServiceHandler, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=9bae47114a98ed1a"/>
<add verb="*" path="Service5.asmx" type="Service4,
WebServiceHandler, Version=1.0.0.0,
Culture=neutral,
PublicKeyToken=9bae47114a98ed1a"/>
Or if you want to provide handler for a particular ashx file use something like this
<add verb="*" path="Service3.ashx"
type="MyWebServiceHandlerClass,
MyWebServiceHandlerDLL,
Version=1.0.0.0,
Culture=neutral,
PublicKeyToken=9bae47114a98ed1a"/>
Above the strong name is being used to locate the dll, this is because the dll is in the GAC, you do not have to put the dll in the GAC.
The second parameter in the 'type' list above is actually the name of the dll file, the Version, culture, PublicKeyToken, are all the fullname of the dll containing your HttpHandler code i.e. the strong name.
The Handler itself must include your code that was originally in your original file and also Implement the IHttpHandler Interface.
You can compile your HttpHandler into a seperate dll and add it to the GAC with the signature provided in the web.config of vice versa, just make sure that the Strong Name in the DLL is identical to the String name provided in the type in the Web Applications type.
If you choose to put your dll in the web apps bin directory then it can be registered as follows:
<add verb="*" path="Service3.ashx"
type="MyWebServiceHandlerClass,
MyWebServiceHandlerDLL"/>
Note: Only the dll name is required, no extension and no strong name it just needs to exist in the web apps bin directory.
Bug:
There's a small bug in ASP.NET 2.0, if you create a handler and you do not wish to use an assembly to store the HttpHandler code you can simple add the source file to the App_Code directory and just use the type in the web.config without and assembly, well this is how it's supposed to work but actually you have to do one more thing and that is you must create a codebehind file and place the code in there instead and add the details to the Http handlers directive
<%@ WebHandler Language="C#" Class="MyHandler" CodeBehind="MyHandler.ashx.cs" %>
web.config
<add verb="*" path="myfile.something"
type="MyHandler"/>
February 01, 2008
Remoting, Remote Computing
http://del.icio.us/rss/learnerplates/remoting
http://del.icio.us/search/?fr=del_icio_us&p=remoting&type=user
There are 2 main ways in .NET to invoke a method and/or create an instance of an object on machine other than the one your
1. Webservices
2. .NET Remoting.
1. Webservices allow the client to communicate with the server be emitting an receiving XML, these XML messages are first wrapped on both the client and server side into SOAP envelopes..NET Remoting on the other hand allows a remote client to invoke a method through Proxy classes.
2. .NET Remoting
You can access remote objects in 2 ways, by reference and by value. The first manipulates the actual original object on the server(the objects class must be MarshalByRefObject) , the second a copy of the original object is used which means any changes made will only effect the local copy, copy on the client, of the object (the objects class must be Serializable).
The communication between Server and Client is taken care of for you by a Proxy class. All you have to do is create your remote class, inherit from MarshalByRefObject (or implement the ISerializable interface depending on how your client will be calling your remote object), usually inherit from MarshalByRefObject .
Create a Server class which creates an a remote instance of the remote class and also create a Channel to allow the client to communicate with the remote object.
Here's a very simple Remote class:
public class RemotingClass : MarshalByRefObject
{
public string HelloWorld()
{
return "Hello World!";
}
}
Or better still, in order to hide your actual remote objects implentation, create an interface in a common dll instead (below) of a concrete class (above).
Include the dll as a reference on you Server side and implement the interface with some class. On the Client side also include the dll as a reference and use the interface type to get your remote object instance.
like so:
public interface IRemotingClass
{
string HelloWorld();
}
And change your remote class implementation accordingly
public class RemotingClass : MarshalByRefObject, IRemotingClass
{
public string HelloWorld()
{
return "Hello World!";
}
}
The client code calling this method could be a standalone app (Console App) or a web application (Http Client, running on IIS), the Console App would use TcpChannel the Http Client would use HttpChannel.
Here's a simple Server class (Note if you plan on running your server as a web app or in a web application, place this code inside your Global.asax files Application_Start method and remove the port numbers they're no longer required, if you do this your RemotingClass.cs will need to be in the App_Code dir) :
using System.Runtime.Remoting;
using System.Runtime.Remoting.Services;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
public partial class ServerApp
{
public void main()
{
// Create an instance of a channel
HttpChannel channel = new HttpChannel(1234);//the client must use this port too
ChannelServices.RegisterChannel(channel); // Register as an available service with the name
HelloWorld RemotingConfiguration.RegisterWellKnownServiceType(
typeof (Remoting.RemotingClass),
"RemotingClass.soap", //The name used by client when requesting this remote object, this tells IIS to deal with the request as a Remote request and let the .NET Remoting take care of it.
WellKnownObjectMode.Singleton); // Create an instance of the remote object
}
The Client code then creates an remote instance of the object using a url with the name just given, "RemotingClass.soap", and a channel to the same object with the same port 1234.
class ClientStartup
{
static void Main(string[] args)
{
//Send out messages on any port port
HttpChannel httpChannel = new HttpChannel();
//Requests will be sent through Http, which are SOAP messages.
ChannelServices.RegisterChannel(httpChannel);
//Get a reference to the remote object using the url
//Note that the same port number is used as on the Server, 1234
IRemotingClass remoteClass = (IRemotingClass)Activator.GetObject(
typeof(IRemotingClass),
"http://localhost:1234/RemotingClass.soap");
string result = remotingClass.HelloWorld();
Console.WriteLine("The Customer has been gotten on the client remotely with the age : " + result);
}
}
Hosting the Remote objects on IIS
When your objects live in a Web Application i.e. in IIS, you can use IIS's power to do some of the work for you, like security.
The above example can be run on IIS by simply placing a version of the code we had in the Server's main method into our web applications Global.asax files Application_Start method. This ensures that the remote service is initiallized only once i.e. when the web app starts.
The code will have to change slightly
HelloWorld RemotingConfiguration.RegisterWellKnownServiceType( typeof (Remoting.RemotingClass), "RemotingClass.soap", WellKnownObjectMode.Singleton);
As you can see the code has been simplified, there's no HttpChannel or Port number, because this remote object will not be referenced by the client using a Url instead of a machine and Port number i.e. using http://localhost/
like so
HttpChannel httpChannel = new HttpChannel();ChannelServices.RegisterChannel(httpChannel);
IRemotingClass remoteClass = (IRemotingClass)Activator.GetObject( typeof(IRemotingClass), http://localhost/
string result = remotingClass.HelloWorld();
Console.WriteLine("The Customer has been gotten on the client remotely with the age : " + result);
SoapSuds.exe
In the above examples we've used an interface on both Server and Client sides, this interface hid our true implementation from the Client, all the Client is aware of is the interface. This works fine but there is another way to implement this and that is with a representation of our remote class that is created from the WSDL of our remote object. The WSDL of our remote object can be accessed using the Url to our remote object followed by ?WSDL, (remember this in SOAP and webservices).
The SoapSuds.exe is an app found in the Visual Studio SDK e.g. C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\soapsuds.exe.
You pass soapsuds.exe your WSDL output and it generates a class representation of your remote object, this representation will be very similar to your real object on the Server side with additional SOAP attributes.
e.g.
C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0>soapsuds -url:http://localhost/
This class can now be used by the Client instead of the Interface IRemotingClass.
But what is the usefulness of this?
One use of this is that is provides the concrete class instead of the interface which is required when you are using Configurable Settings on your Server side.
Configurable settings allow you to dynamically change the remote object name and port at runtime using you config file, one issue with this however is that the Client side requires the concrete implentation in order to reference it.
e.g.
<configuration><
system.runtime.remoting><
application><
service><wellknown mode="Singleton" type="RemotingClass, RemotingClassAssemblyName"objectUri="RemotingClass.rem"/>
</service>
<channels>
<channel name="MyChannel" priority="100" ref="http"/>
</channels>
</application>
</system.runtime.remoting>
</configuration>
January 22, 2008
MVC in ASP.NET
I first came across this with MFC applications, it was well known design pattern when developing standalone applications now it appears to be the latest craze in ASP.NET 3.5 applications! In this post I'll try and find out why and what is the significance and benefits to us, developers.
MVC (scott gu)
Example Building a Simple Blog engine using MVC in ASP.NET (aspalliance.com)
January 11, 2008
WebServices
The .asmx file contains webmethods (ordinary methods with the [WebMethod] attribute). You place your .asmx file inside your webserver somewhere and these can be consumed directly by browsing to the uri or by consuming the webservice by adding it as a reference to your application and then accessing that reference. Webservices can also be created with Codebehind meaning the code itself is in another file, this results in the .asmx file just containing a Directive to the class name containing the webmethod, to use this webservice the codebehind dll must be placed within the websites bin directory.
The WebService works by the Client and Server sending SOAP XML (SOAP is just the root element, it's something that Internet Explorer knows how to parse) up and down over HTTP. Both sides need to know how to create this SOAP XML, on the Client side this is done by a Proxy class which Serializes/De-Serializes the SOAP XML into objects.
Proxy Class
Client side class, usually called Reference.cs, which Serializes/De-Serializes the SOAP XML into objects. This lives on the Client side (in .NET) e.g. if the client is an .NET Console application then in order to gain access to the methods available on a website the application will need a Proxy class (this is created for you by VS.NET when you add a Web Reference which points to the Webservice on the website i.e. http:..........).
If you want to use Webservices from a Client such as an ASPX form then you again need to add the Web Reference but a Proxy class is not generated, instead because your making calls on the same web application you can call into the WebService class directly like a normal class.
If your calling a WebService from on Web app to another then it's the same as a Console app, you'' have to use a Proxy class, again this is generated for you in the calling website by VS.NET when you add the Web Reference.
Hiding the asmx
I have not actually found a way to get rid of the .asmx file completely. You can hide the webservice endpoints functionality in an assembly and create a handler for it as described below but to implement the webservice you will always need a .asmx file somewhere. Even the Proxy Class uses the .asmx file. In order to implement a remote call to a method inside your own code I think the only solution is .NET Remoting.
An Extensive Examination of WebServices (4guysfromrolla thorough).
Creating a .NET Web Service (15seconds goood)
ASP.NET Web Services Techniques (Very thorough explanation of how WebServices work)
Writing a raw Web Service using HttpHandler
January 07, 2008
Active Directory
In .NET the Active Directory is accessed from System.DirectoryServices namespace.
.NET uses Windows Active Directory Service Interfaces (ADSI) to interact with the distributed devices, there are 5 of these ADSIs;
Path Windows NT version 5.0, Windows 2000, or Windows XP WinNT://path, Lightweight Directory Access Protocol (LDAP) ldap://path/, Novell NetWare Directory Service NDS://path, Novell Netware 3.x NWCOMPAT://path, Internet Information Services (IIS) IIS://.
e.g.
DirectoryEntry webentry = new DirectoryEntry();
String path = "IIS://localhost/W3SVC/1/ROOT";
DirectoryEntries webSiteChildren = webentry.Children;
foreach (DirectoryEntry website in webSiteChildren)
{
//can access metadata of each website on iis here
}
November 27, 2007
MMC Snapin creation
There are 2 main versions of MMC available on Windows, MMC 2.0 (Windows 2000 and XP) and MMC 3.0 (Windows Server 2003). I'll mainly explain how to develop a Snap-in with MMC 3.0.
You can upgrade MMC 2.0 to MMC 3.0 using this install supplied by Microsoft
MMC 2.0 snapin:
Download the Platform SDK, Windows® Server 2003 R2 Platform SDK Web Install, from http://www.microsoft.com/downloads/details.aspx?FamilyID=0baf2b35-c656-4969-ace8-e4c0c0716adb&DisplayLang=en
Build an example from the Platform SDK install:
C:\Program Files\Microsoft Platform SDK\Samples\SysMgmt\MMC\mmc2.0\simple>nmake
MMC 3.0 snapin:
How to Develop and MMC 3.0 Snap-in (msdn)
How to create a Snap-in using MMC 3.0 (msdn)
MMC Snap-ins for MMC 3.0 can be developed with the Windows SDK, for information on the snapin classes see msdn Management Console
There is also an opensource .NET library at http://sourceforge.net/projects/mmclibrary
The MMC 3.0 application installation is at http://www.microsoft.com/downloads/details.aspx?FamilyID=61fc1c66-06f2-463c-82a2-cf20902ffae0&DisplayLang=en
Here's an example of how to build a simple Snapin
To develop MMC 3.0 snapins you'll need the microsoft.managementconsole.dll library. This library can be gotten by installing the MMC 3.0, found at http://www.microsoft.com/downloads/details.aspx?FamilyID=61fc1c66-06f2-463c-82a2-cf20902ffae0&DisplayLang=enAdd this library as a reference to your Snapin project and you'll have access to it's functionality. .
You can also install the Windows Server 2003 R2 Service pack. Some example code is available on the web.
public override void Install(IDictionary stateSaver);
public override void Rollback(IDictionary savedState);
public override void Uninstall(IDictionary savedState);
MMC 3.0 - A managed code 'task manager' MMC 3.0 snap-in
Installation
The procedure to install the MMC Snapin in MMC 2.0 and MMC 3.0 is different, see below for further details:
MMC 2.0 snapin:
Register the snapin with MMC:
C:\Program Files\Microsoft Platform SDK\Samples\SysMgmt\MMC\mmc2.0\simple\WIN2000_DEBUG>regsvr32 Simple.dll
This snapin was build with MMC 3.0 as ManagementConsoleSnapin.dll
The resultant DLL, C:\MySnapinProject\bin\Debug\ManagementConsoleSnapin.dll, can be installed as a snapin to MMC 3.0 as follows: >C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\installutil.exe C:\MySnapinProject\bin\Debug\ManagementConsoleSnapin.dll
and uninstalled using:
>C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\installutil.exe /u C:\MySnapinProject\bin\Debug\ManagementConsoleSnapin.dll
After installation the snapin appears in the MMC Snapin list as "My Management Console". Add the snapin to MMC and select OK. The Snapin is now ready for use.
MMC Snapin Debugging
The easiest way to debug the MMC while installing the Snapin is to set the Project Properties of your snapin project, in the project properties Debug tab select 'Start External Program' and enter 'C:\WINDOWS\system32\mmc.exe' in the field. Now when you hit F5 the MMC app will launch, you now 'Add\Remove Snapin' from the MMC's File menu.
October 15, 2007
Handling SoapExceptions
e.g. if an IOException occurs it will bubble up to the caller as a SoapException, unlike other Exceptions the origninal Exception is not easily extracted, for instance by using the InnerException property.
Because of this you must handle the SoapException in the sender and the caller. Yuo must stuf your Originals details into a new instance of a SoapException and then throw it. The original Exception's details are contained in the Details property of the SoapException but you still have to do some work to extract it.
Also remember that WebServices return XML, the SoapException is also returned as XML, so our extraction code will need to know how to identify the inner exception and how to extract it from the correct XML Element.
One thing you may wish to do with the SoapException is extract the original Exceptions details. Here's how to achieve this:
First you must catch the SoapException
try
{
}
catch(System.Web.Services.Protocol.SoapException se)
{
//Somehow extract the details from the SoapException, I'll explain how to do this now
}
In here you must extract the SoapExceptions details. This is not as easy as a normal Exception, really to get the details you have to know the format in which they were entered. So you should throw the SoapException with the Details you want from within the WebService yourself.
You do this by stuffing entries into the Details property of the SoapException before throwing it.
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml.Serialization;
using System.Xml;
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class SoapExceptionSupplier : System.Web.Services.WebService
{
public SoapExceptionSupplier () {
//Uncomment the following line if using designed components
//InitializeComponent();
}
[WebMethod]
public string HelloWorld()
{
try
{
throw new System.IO.IOException("An IOException has been thrown");
}
catch(Exception e)
{
// Build the detail element of the SOAP fault.
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
System.Xml.XmlNode node = doc.CreateNode(XmlNodeType.Element, SoapException.DetailElementName.Name, SoapException.DetailElementName.Namespace);
// Build specific details for the SoapException.
// Add first child of detail XML element.
System.Xml.XmlNode message = doc.CreateNode(XmlNodeType.Element, "Message", "http://tempuri.org/");
message.InnerText = e.GetType().ToString();
// Append the two child elements to the detail node.
node.AppendChild(message);
// Build specific details for the SoapException.
// Add first child of detail XML element.
System.Xml.XmlNode details = doc.CreateNode(XmlNodeType.Element, "Details", "http://tempuri.org/");
details.InnerText = e.Message;
// Append the two child elements to the detail node.
node.AppendChild(details);
SoapException se = new SoapException("Fault occurred", SoapException.ClientFaultCode, Context.Request.Url.AbsoluteUri, node);
throw se;
}
return null;
}
}
The code above is a WebMethod, I've thrown a System.IO.IOException, this is caught and a SoapException is thrown instead, the reason for this is that I am actually embedding Details in the SoapException before it's thrown, if I simply threw the System.IO.IOException it would be wrapped in a SoapException by the WebService and I would find it very difficult to get any information from it on the Callers side.
In the Catch I construct a XML Document and create a XMLNode, this mode is populated with Details of the Exception thrown, I then pass the XMLNode into the SoapException and throw it.
Now on the Callers side I know what to expect and can check the SoapException for these details using the SoapExceptions Details property.
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Windows.Forms;
using System.Xml;
public partial class _Default : System.Web.UI.Page
{
protected void Button1_Click(object sender, EventArgs e)
{
localhost.SoapExceptionSupplier soapExSupplier = new localhost.SoapExceptionSupplier();
try
{
//Call the WebMethod I listed earlier
soapExSupplier.HelloWorld();
}
catch (System.Web.Services.Protocols.SoapException soapEx)
{
XmlDocument doc = new XmlDocument();
//First the Error message (Exception type)
MessageBox.Show("Error Message : " + soapEx.Message);
//Now the Detail, more detail on the exception.
doc.LoadXml(soapEx.Detail.OuterXml);
XmlElement root = doc.DocumentElement;
string result =string.Empty;
foreach (XmlElement child in root.ChildNodes)
{
result += child.InnerText;
}
MessageBox.Show("Error Detail : " + result);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
Here I catch the SoapException just thrown by the WebMethod. I construct a XMLDocument from the SoapExceptions Detail properties OuterXML. I extract each XmlElement from this XmlDocument, this will contain the contents I stuffed into the SoapException in the WebService.
Done.
September 24, 2007
SilverLight
http://www.microsoft.com/silverlight/
August 16, 2007
DynamicMethod, IL Code, CreateDelegate
This could be achieved by loading the assembly in an AppDomain and then invoking the methods within ????
Or you could use what are known as DynamicMethods to invoke the methods.
DynamicMethod is a class that also resides in the System.Reflection.Emit namespace. Why would you want to use DynamicMethod? it's useful when you want to load methods into memory or write methods in memory and invoke later.
We can write IL code in memory in the exact same was as we wrote IL code earlier using the MethodBuilder.
In my experience DynamicMethod is a bit tricky to get right especially when you try to invoke your Dynamic method. The problem I found was with the constructor parameters, what do they mean;
There are 6 constructor overloads in all;
half of these are associated with an class instance i.e. you supply a Type as one of the constructor parameters, the significance of this is that it gives you access to reference private data within the type.
The other half of the constructor overloads are associated with the Module i.e. the dll and not a type, the significance of this is that it does not give you access to private types data.
from msdn:
| DynamicMethod (String, Type, Type[], Module) | Creates a dynamic method that is global to a module, specifying the method name, return type, parameter types, and module. |
| DynamicMethod (String, Type, Type[], Type) | Creates a dynamic method, specifying the method name, return type, parameter types, and the type with which the dynamic method is logically associated. |
There's also another caveat which is a boolean option to skip the JIT checking. This means some verification of the parameter types which is usually done by the JIT is skipped.
| DynamicMethod (String, Type, Type[], Module, Boolean) | Creates a dynamic method that is global to a module, specifying the method name, return type, parameter types, module, and whether just-in-time (JIT) visibility checks should be skipped for members of all types in the module. |
| DynamicMethod (String, Type, Type[], Type, Boolean) | Creates a dynamic method, specifying the method name, return type, parameter types, the type with which the dynamic method is logically associated, and whether just-in-time (JIT) visibility checks should be skipped for members of other types in the module. |
And there's one more caveat, CallingConvention,......
DynamicMethod dynamicMethod = new DynamicMethod(
"MyMethod"),
typeof(object),
new Type[] { typeof(object[]), typeof(object[]) },
typeof(MyClass));
The instance of DynamicMethod results in a method with the following signature
MyClass
{
object MyMethod(object[], object[])
{
}
}
You now have to insert your own IL code is the methods like Emit, again see my other post, dotNET - Under the Hood, IL Assembly, you write the IL code in the same manner.
Now you want to Invoke the method you've just created. This can be done in 2 ways, one call Invoke on your instance i.e.
dynamicMethod.Invoke();
or create a Delegate and then call Invoke on that delegate. The advantage of this over the first option is that you supply the correct parameters here and then just call Invoke on it later, the first option requires you to call Invoke here and now.
I'll explain the second option here:
DynamicMethod.CreateDelegate is a method used to invoke a piece of IL code. It creates a Delegate as the name suggests. The Delegate must have the identical signature to the DynamicMethod instance which is calling CreateDelegate.
public delegate object CompiledMethod(object[] arguments);
//As the delegate above defines, our DynamicMethod must return an object, and take an array of objects as parameters, it's also tied to the type "Example".
dynamicMethod = new DynamicMethod(
"",
typeof(object),
new Type[] { typeof(Example), typeof(object) },
typeof(Example)
);
// Get a FieldInfo for the private field 'id'.
FieldInfo dynamicMethodFid = typeof(Example).GetField(
"id",
BindingFlags.NonPublic | BindingFlags.Instance
);
ILGenerator dynamicMethodIlg = d.dynamicMethod.GetILGenerator();
dynamicMethodIlg.Emit(OpCodes.Ldarg_0);
dynamicMethodIlg.Emit(OpCodes.Ldfld, dynamicMethodFid);
dynamicMethodIlg.Emit(OpCodes.Ldarg_0);
dynamicMethodIlg.Emit(OpCodes.Ldarg_1);
dynamicMethodIlg.Emit(OpCodes.Stfld, dynamicMethodFid);
dynamicMethodIlg.Emit(OpCodes.Ret);
CompiledMethod result = (CompiledMethod)d.dynamicMethod.CreateDelegate(typeof(CompiledMethod), d.ex);
The CreateDelegate has 2 constructor overloads, one which takes and instance of an object as the second parameter (Instance) and another which does not (Static).
The difference being at invokation time, if you created your delegate using the instance method you do not have to supply the instance at invokation time.
If you created your delegate using the Static method you must supply the instance at invokation time e.g.
Instance
public delegate int UseLikeInstance(int newID);//delegate declaration,i.e. when your invoking the delegate you must supply a value only, no instance.
UseLikeInstance uli = (UseLikeInstance) d.changeID.CreateDelegate(typeof(UseLikeInstance, d.ex));//instantiation, instance required
uli(1492); //Invokation, no instance
Static
public delegate int UseLikeStatic(Example ex, int newID);//delegate declaration,i.e. when your invoking the delegate you must supply an Instance and a value.
UseLikeStatic uls = (UseLikeStatic) d.changeID.CreateDelegate(typeof(UseLikeStatic));//instantiation, NO instance required
uls(d.ex, 1492); //Invokation, instance required
The following I'm not too sure about!
The resultant delegate looks the same, it has a Method and a Target. The Method is the DynamicMethod you've just created and the Target is the instance you provided. When the Delegate is invoked if the IL code within the DynamicMethod requires an instance of an object in order to run then it looks in the Target. If at invokation time this Target is null or is of the wrong type an Exception will be thrown.
The Target is created when you use the CreateDelegate, at this time the instance must exist, you can later set this same instance to null before invokation of the delegate, this must mean that a copy of the instance is added to the Target at runtime and the original can be done away with.
The Target object (instance) is a point of confusion for me! when is it required and when is it not? It's when the DynamicMethod's IL uses instances that are already on the stack, this is a achieved with
Idlem.ref
this gets a reference to an object which resides on the stack.
This is where the Target comes it, it is the instance that gets used.
Then this instance is used for something such as a call to another external method.
The above is not yet complete
August 14, 2007
Enumerator(s)
In order to allow the use of the foreach on an object the class must implement the IEnumerable.GetEnumerator method which also means you have to implement the IEnumerable interface. Within the GetEnumerator override you need to return an instance of IEnumerator, so you need to create a class that implements the IEnumerator interface. see the code.
This is useful if you class contains a member variable which is an or even a container. Let's say it's an array. When an instance of your class is create the array is also instantiated. But you don't want the array to be visible but you do want clients to be able to use a foreach on your class instance, this can be achieved by implementing the IEnumerable interface and overriding the GetEnumerator method.
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
namespace Enumerator
{
public class Person
{
public Person(string fName, string lName)
{
this.firstName = fName;
this.lastName = lName;
}
public string firstName;
public string lastName;
}
public class People : IEnumerable
{
private Person[] _people;
public People(Person[] pArray)
{
_people = new Person[pArray.Length];
for (int i = 0; i < position =" -1;" _people =" list;" position =" -1;" peoplearray =" new" peoplelist =" new">System.Collections.IEnumerable
Yield
This is a keyword in .NET, it's placed before the return value where your implementation of IEnumerable.
When you use yield your actually replacing the requirement for an Enumerator, Yield acts as the Enumerator. So if your iterating through your own type (a class made by yourself) you's usually have to create an implemetation of the IEnumerator interface to tell the foreach for example how to move to the next item in the list i.e. MoveNext(). Now you can use the Yield keyword instead which saves you work.
The Yield keyword can be used in your IEnumerable implementations GetEnumerator() method or in the foreach itself.
This can be done in the IEnumerable's GetEnumerator() method
e.g.
public IEnumerator GetEnumerator()
{
for(int i=0;i<_people.length;i++)>
yield return _people[i];
}
instead of
public IEnumerator GetEnumerator(){
return new PeopleEnum(_people);
}
which uses an implementation of IEnumerator, which in turn returns the array of _people, to return the enumerator
If you want to return an Enumerable list from a method i.e. you want to return a list of object which you can iterate through from a method you'll need to use the yield keyword.
The keyword yield is required when returning an IEnumerable object from a method like so
public static IEnumerable DoSomething()
{
int counter = 10;
int result = 1;
while (result <>
August 13, 2007
Security in Assemblies
Code Access Security.
msdn magazine.
August 08, 2007
dotNET - Under the Hood, IL Assembly, MSIL, ILGenerator, MethodBuilder
When you're developing with .NET languages with a Development tool such as Visual Studio 2005 the compiler you use will generate the correct/valid IL for you, but what if you want to create your own IL for some reason! One reason could be that you've developed your own Language and would like it to run on .NET, in this case you'll need to write out some sort of Assembly, be it EXE or DLL, with IL code. To do this you can use some Classes available in .NET API, these classes reside within the System.Reflection.Emit namespace. I'll explain how this is achieved later.
There are also some tools which are used to compile and decompile the IL code, ILASM (all caps means this is the compiler and not the language ILAsm) is the IL compiler and ILDASM is the IL Decompiler (allows the reverse of compilation, you can create .NET language source code from an IL assembly).
An IL assembly contains 2 things, Metadata and Managed Code. Metadata is information which describes the structures and methods within the assembly. The Managed Code is the actual IL code, it is stored in the assembly in Binary form, managed means that the Runtime controls it.
The assembly has 2 main components, the metadata and the code. At runtime the assembly is loaded, the metadata is read first to find the descriptions of the structures and methods, the JIT compiles the IL code in the assembly into machine code using the metadata. When a method is required the machine code for that method is executed (incidentally this is what differentiates this from an Interpreter).
PE and COFF are additional data embedded in the assembly to describe an EXE, we'll not worry about them here.
Contents of the Assembly:
The assembly contains one or more Modules. An Assembly may or may not have multiple modules but it must have at least one i.e. the Prime Module. The Prime Module contains a Metadata section which describes the contents of the Assembly, An Assembly Identity section and maybe some actual IL code. The Assembly may also contain additional Modules which each have their own Metadata and IL Code sections.
Boxed/Boxing and UnBoxed/UnBoxing:
You'll see these two terms appearing whenever you deal with IL, for you C++ developers you may have heard of the term before. It's related to Reference types and Value types, reference types are objects on the Heap to which a variable points, value types are not on the Heap and the variable contains the value in it's own memory location. This can be seen in the IL code. Reference types are more expensive, in terms of processor and memory, than Value types.
Boxing is the conversion of a value type to a reference type at compile time i.e. the value is contained in a variables memory location, for some reason (such as passing the value by reference, maybe as a method parameter) you want to convert your value type to a reference type. This means that a bit copy is made of the value type and instance of a Class is created with that copied value.
UnBoxing in the conversion of reference type to value type at compile time i.e. in your source code a variable points to a memory address on the Heap, you new-up an Object, when you compile the object is Boxed meaning the IL code is written for that Object, the memory required is allocated in IL code.
Int32 unBoxed = 20;//UnboxedHere's a great article on Boxing from msdn magazine.
Object boxed = unBoxed;//Boxed
Int32 unBoxedBoxed = (Int32)boxed;
Here's the IL code generated for the C# source code above
.entrypoint
// Code size 19 (0x13)
.maxstack 1
.locals init ([0] int32 unBoxed,
[1] object boxed,
[2] int32 unBoxedBoxed)
IL_0000: nop
IL_0001: ldc.i4.s 20
IL_0003: stloc.0
IL_0004: ldloc.0
IL_0005: box [mscorlib]System.Int32
IL_000a: stloc.1
IL_000b: ldloc.1
IL_000c: unbox.any [mscorlib]System.Int32
IL_0011: stloc.2
IL_0012: ret
When playing around with IL code and reflection you may want to invoke a method within an assembly. If you are writing the assembly you may want to write it using IL code, to do this you would use the MethodInfo class found in the System.Reflection.Emit namespace. Once you'd created your methodinfo you can write IL code directly to that method and then save all to a dll later using the ILGenerator class, this ILGenerator can be thought of as the writer of the IL code and a reference to this is gotten from the MethodInfo instance you've just created
// create a dynamic assembly and module
AssemblyName assemblyName = new AssemblyName();
assemblyName.Name = "HelloWorld";
AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(
assemblyName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder module;
module = assemblyBuilder.DefineDynamicModule("HelloWorld.dll");
// create a new type to hold our Main method
TypeBuilder typeBuilder = module.DefineType(
typeof(Example).Name,
TypeAttributes.Public TypeAttributes.Class);
// create the Main(string[] args) method
MethodBuilder methodbuilder = typeBuilder.DefineMethod(
DynamicMethod.GetCurrentMethod().Name,
DynamicMethod.GetCurrentMethod().Attributes,
DynamicMethod.GetCurrentMethod().CallingConvention,
changeID.ReturnType,
new Type[] { typeof(Example), typeof(int) });
Type t = changeID.DeclaringType;
// generate the IL for the Main method
ILGenerator ilGenerator = methodbuilder.GetILGenerator();
// Push the current value of the id field onto the
// evaluation stack. It's an instance field, so load the
// instance of Example before accessing the field.
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldfld, fid);
// Load the instance of Example again, load the new value
// of id, and store the new field value.
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldarg_1);
ilGenerator.Emit(OpCodes.Stfld, fid);
// The original value of the id field is now the only
// thing on the stack, so return from the call.
ilGenerator.Emit(OpCodes.Ret);
// bake it
Type helloWorldType = typeBuilder.CreateType();
assemblyBuilder.Save("HelloWorld.dll");
June 12, 2007
Design Patterns - Visitor Pattern
E.g. You have a collection of Objects, each of your objects has different member variables and you want some way to call print this info. One way to do it would be to cycle through each object and Call some common method on each such as Print(), this ties the Object tightly to the printing or whatever you are doing, we want to loosly couple the printing or whatever to the object itself, to unbind the Printing from the actual Object we introduce 2 interfaces. The Visitor and the Visitable.
The Visitor interface has a Visit(object) method (this is what would print the details).
The Visitable interface has an Accept method, this takes the Visiting object as a parameter.
The Object we want to print implements the Visitable interface, this has a callback to the implementor of the Visitor Interface, this callback is required because it is actually the implementor of the Visitor that does the printing.
The implementor of the Visitor has a visit(Object) method for each Object type, this removes the need for Use/Case statements for each Object type.
using System;
using System.Collections.Generic;
using System.Text;
namespace VisitorPattern
{
interface Visitor
{
void visit(Car car);
void visit(Bus bus);
}
interface Visitable
{
void accept(Visitor visitor);
}
class Car : Visitable
{
public void accept(Visitor visitor)
{
visitor.visit(this);
}
}
class Bus : Visitable
{
public void accept(Visitor visitor)
{
visitor.visit(this);
}
}
class PrintVisitor : Visitor
{
public void visit(Car car)
{
Console.WriteLine("Visiting car");
}
public void visit(Bus bus)
{
Console.WriteLine("Visiting bus");
}
}
public class VisitorDemo
{
static public void main(String[] args)
{
Visitable[] objects = { new Car(), new Bus() };
Visitor visitor = new PrintVisitor();
foreach (Visitable visited in objects)
{
visited.accept(visitor);
}
}
}
}
May 08, 2007
Installer (MSI) - Windows Installer general information
Common problems I've found while playing around with the creation of Windows installers is that if you break the uninstaller functionality it's very difficult to uninstall at all.
There are a couple of ways to attempt to repair, this
1. Force overwrite of the installer with a known good installer and then uninstall.
msiexec.exe /fvecmus
2. Repair the installer with a known good installer (Control Panel->Add/Remove Programs, locate you Applications installer and select "Click here for support Information", then select Repair from the resultant dialog, now point to a new location of a good installer)
Then Remove.
3. Use a tool name MsiZip.exe, it's available with the
4. A Windows Installer Cleanup Utility.
The windows installer keeps a list in the OS of files installed by the installer, if you cannot uninstall because windows thinks one of these files is being used then try option 1 above.
April 17, 2007
BootStrapper BootStrapping
It's useful if you have multiple installation files that you'll like the user to install in on step.
The Bootstrapper itself is a .exe installer file, it also may have .SED file which contains details of the contents of the .exe.
There are a few applications out there to generate a bootstrapper, the most simple I've seen is the an app called IEXpress.
IExpress
Is actually installed on windows (System32/iexpress.exe).
This creates an installer, you tell it what files you wish it to include in the installation.
I've only played with this so far and it appears to me that the isntaller can only install upto 2 setup.exes. You can include as many files as you like, but the files cannot have the same name, because of this you will probably need to rename your setup.exes to something else because you cannot have 2 files with the same name.
After you've included all the files you wish to run the next step asks you which files you wish to run, at this stage it looks like you can only specify upto 2 .exe installer files (there maybe a way around this, by editing the .sed file afterwards).
Also the final step I recommend telling the bootstrapper to use full files paths, this seems to ensure it works.
Visual Studio
In Visual Studio the BootStrapper is read in as an XML file. Visual Studio has a set BootStrapper XML file which it reads, below I will describe how to use this default bootstrapper and then I will describe how to create your own.
Using the BootStrapper in Visual Studio
Visual Studio also has a Bootstrapper, it's actually the Launch Conditions Editor in the Setup Project. The prerequisites are what your looking for, you'll see .NET is there by default.


By right clicking the Setup Project and bringing up the Properties dialog you'll a prerequisites button, this launches another dialog which lists the prerequisites, only the default ones are there.

There is a snag with this however you can to add your own you'll need to create your own BootStrapper XML file
Create your own BootStrapper file
There are 2 ways to create the XML file
1. Write the XML file yourself and copy to the correct location:
you'll need to follow the instructions in the .zip found in the BootStrapper SDK
2. You can use an Community UI (BMG) to write the bootstrapper for you (note the web installer doesn't work funnily enough, the msi does, the program can then be found at C:\Program Files\Microsoft\Bootstrapper Manifest Generator\BMG.exe).
It allows you to add .msi files, .exe files and apply conditions for them. It also installs the bootstrapper xml file for you after, this makes it available in Visual Studios Setup Project Prerequisite dialog that I mentioned earlier.
Here's a complete description of how to install you Custom Prerequisite.
Use the Visual Studio 2005 Bootstrapper to Kick-Start Your Installation - This article is the complete version and is not msdn, Also describe Custom bootstrapping.
Adding Custom Prerequisites.
MSBuild GenerateBootStrapper Task
After you've created your Visual Studio BootStrapper you can use it with MSBuild with the GenerateBootstrapper Task.
This MSBuild task allows you to include your custom prerequisite in any Visual Studio Project.
So if you wish to create a Bootstrapper for your installer using MSBuild it's the same thing as Adding Prerequisites to your Setup Project using the Project->Properties->Prerequisite dialog.
You'll still need to have your prerequisites created and installed beforehand.
Here's and example of a project to create a BootStrapper using 2 Prerequisites:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{7447AF9B-33E3-41AE-9D88-833704524543}</ProjectGuid>
</PropertyGroup>
<ItemGroup>
<Content Include="MyOriginalInstaller.msi">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Target Name="Bootstrapper">
<GenerateBootstrapper
ApplicationFile="MyOriginalInstaller.msi"
ApplicationName="MyInstallerName"
BootstrapperItems="@(BootstrapperFile)"
ComponentsLocation="Relative" Culture="en" FallbackCulture="en-IE" CopyComponents="True"
Validate="False"
OutputPath="$(OutputPath)"
Path="C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bootstrapper" />
</Target>
<ItemGroup>
<BootstrapperFile Include="Microsoft.Net.Framework.2.0">
<ProductName>.NET Framework 2.0</ProductName>
</BootstrapperFile>
<BootstrapperFile Include="ProjectAggregator2">
<ProductName>ProjectAggregator2.msi</ProductName>
</BootstrapperFile>
</ItemGroup>
</Project>
We're creating a BootStrapper called MyInstaller.msi.
We're setting our main Installer (the one whose Gui appears) to be "MyOriginalInstaller.msi".
We include this same main installer "MyOriginalInstaller.msi" in the current project, it appears that it must be found in the current project in order for this to work.
We include to pre-requisites which get installed beforehand, their ProductName must be identical to the folder name that appears in their installation location
C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\BootStrapper\Packages
e.g.
C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\BootStrapper\Packages\ProjectAggregator2.msi\
ClickOnce FAQs (installsite.org)
April 16, 2007
dotNET Custom Attributes
The Attribute is in fact a class that inherits from System.Attribute. The Attribute is read in at Runtime, Reflection takes care of this. Attributes are useful for flagging types, conditions when and when not to use certain methods like the WebMethod.
April 04, 2007
WiX - Windows Installer XML
WiX Tutorial (tramontana).
WiX documentation (sourceforge).
WiX and Visual Studio (msdn forum).
WiX integration in Visual Studio using Votive (Votive blog).
WiX integration (msdn).
WiX Forum.
Building setup packages for Visual Studio project templates and starter kits (Aaron Stebner's WebBlog)
WiX Tallow tool
WiX is a methodology to write installers.
WiX has a compiler/linker (candle/light) which may give the installer writer some validation before deployment.
The contents of the end installer (.msi) are edited using an XML file.
I haven't found WiX to be advantageous over the ordinary Visual Studio Setup Project.
Visual Studios SDK has a project template (from Votive) for creating WiX installers. This project template gives you the skeletol wix file to start and the build commands are built into the project, all you've to do is create new GUIDs ,these guids are only for the installer, they've nothing to do with your applications GUIDs, the WiX compiler, candle.exe, will complain if you fail to change one anyway.
The addition of items to install is done be editing the WiX xml file! if you choose to use it with Visual Studio , the Votive version (present Wix 2) then you'll get Intellisence, that with the Candle compiler are very useful but editing xml files is very tedious.
You'll also note that this version does not support integrated Visual Sourcesafe in Visual Studio, I found this a disadvantage. However WiX 3 (presently in developer status) does have this but lacks other vital components at present, such as it does not support the gui components you require for user input in the installer. In WiX 2 these are provided by the SDK in the wixui.dll.
All in all I don't think WiX is up to scratch just yet but WiX 3 does look promising.
There's a tool now called Tallow which included in the WiX SDK, apparently it's a command line tool that allows you to create a WiX installer be passing in you file locations and registry settings on the command line.
WiX pre-processor table (these items can be added to your WiX file, they are replaced at compile time).
| Preprocessor Variable | Example Variable Use | Example Variable Value |
|---|---|---|
| var.<Project>.ConfigurationName | $(var.MyApp.ConfigurationName) | Debug.NET |
| var.<Project>.ProjectDir | $(var.MyApp.ProjectDir) | C:\code\msdn\MyApp |
| var.<Project>.ProjectDosFileName | $(var.MyApp.ProjectDosFileName) | MYAPP.CSP |
| var.<Project>.ProjectExt | $(var.MyApp.ProjectExt) | .csproj |
| var.<Project>.ProjectFileName | $(var.MyApp.ProjectFileName) | MyApp.csproj |
| var.<Project>.ProjectName | $(var.MyApp.ProjectName) | MyApp |
| var.<Project>.ProjectPath | $(var.MyApp.ProjectPath) | C:\code\msdn\MyApp\MyApp.csproj |
| var.<Project>.TargetDir | $(var.MyApp.TargetDir) | C:\code\msdn\MyApp\obj\Debug |
| var.<Project>.TargetDosFileName | $(var.MyApp.TargetDosFileName) | MYAPP.EXE |
| var.<Project>.TargetExt | $(var.MyApp.TargetExt) | .exe |
| var.<Project>.TargetFileName | $(var.MyApp.TargetFileName) | MyApp.exe |
| var.<Project>.TargetName | $(var.MyApp.TargetName) | MyApp |
| var.<Project>.TargetPath | $(var.MyApp.TargetPath) | C:\code\msdn\MyApp\obj\Debug\MyApp.exe |
| var.SolutionDir | $(var.SolutionDir) | C:\code\msdn\MySetup |
| var.SolutionDosFileName | $(var.SolutionDosFileName) | MYSETUP.SLN |
| var.SolutionExt | $(var.SolutionExt) | .sln |
| var.SolutionFileName | $(var.SolutionFileName) | MySetup.sln |
| var.SolutionName | $(var.SolutionName) | MySetup |
| var.SolutionPath | $(var.SolutionPath) | C:\code\msdn\MySetup\MySetup.sln |
January 30, 2007
Visual Studio Extensibility
Extending the Visual Studio IDE means adding custom functionality to the IDE, this could be custom TextEditors, Add Ins to the Tools menu, Writing to the Output Console, basically anything that will require you to add or write to any component in the Visual Studio IDE.
Extending the Visual Studio IDE is not for the faint hearted. It is implemented with a Visual Studio Extensibility SDK downloadable from link 1 below. It's implemented using COM interfaces (not very nice).
There are 5 points of contact for Extensibility issues
1. MSDN Visual Studio SDK homepage.
2. MSDN documentation.
3. MSDN Extensibility Forum.
4. Dr. Ex's Weblog.
5. MZ-Tools.
There are also webcasts available from msdn.
I recommend watching some of the webcasts especially the Managed Package Framework as the explain briefly how some of the Class Attributes work with RegPkg.exe and how important the GUIDs are.
GUIDs
The GUIDs are static and each control and component in the VS environment should have one, if they have a static GUID it means that Visual Studio will persist that same object for all instance of Visual Studio, this means that if you create your own Control, say a Form, if you do not give it a static GUID a new Form object will be created each time it's launch from VS, if you give it a GUID then that same Form object will be used each time, the result of this is that if you change the size or something of that Form object then the next time the Form is shown it will pertain that size set.
Visual Studio Features
Some more detailed features of Visual Studio Extensibility are:
Language Services - Create your own language with Visual Studio TextEditor highlighting and intellisence etc, msdn.
Aaron Martin's Webblog - Managed Language Tools in Visual Studio SDK.
VSPackages - VSPackages allow your application to consume services provided by VSSDK. Your VSPackage implementation must implement the Package base class, this requires implementation of various Interface methods.
To create your own Project type i.e. your own project name type appears in the open dialog is quiet difficult, you'll need to create a VSPackage, see the steps below:
Steps To Create your own Custom Project Type using VSPackages
Steps To Create Your First VSPackage.
1. To create a VSPackage just use the File->New Project->Other Project Types->Visual Studio Integration Package,
2. After this has been created you can immediatly hit F5, this will install this with the Visual Studio Experimental Hive and launch it, you should see a new Project Type listed in the Visual Studio Help, but it probably will not appear in the File->New Project dialog just yet, this requires some more work....Tip: By hitting F5 you install your application into Visual Studio, the VSPackage template has included some commands in the build an Debug to do this for you. What it actually does in install your project in a developer sandbox called the Experimental Hive. This is is a location in the registry which you can play with, to Reset the Experimental Hive at any time goto the Start Menu->Visual Studio SDK->Tools->Reset Experimental Hive, this removes all memory of Project, add ons, item templates etc that you may have installed.
Tip: Each time you hit F5 it should rebuild you project and install it, sometimes this does not complete, you'll see in the Visual Studio installation there are an number of subfolders, there are 2 in particular we are interested in
C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\ProjectTemplatesExp
C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\ProjectTemplatesCacheExp
The Cache is populated with the unzipped version of what is in the non-cache version, and this is what Visual Studio actually uses to find the project templates. This does not always get updated however, to ensure that it does add this command to you Project's Post Build Event
"C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\devenv.exe" /rootsuffix Exp /setup
or
"C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\devenv.exe" /rootsuffix Exp /InstallVSTemplates
devenv.exe switches (msdn).
3. You now have you own VSPackage but you'll also want your own Project extension (something other than the default .csproj).
You'll need this because if you wish to create Templates to group under your Project Type they'll have a Project Type the same as what you've defined, when you create new project with one of your templates you will want to add Items to the project.
In order for Items to appear in the dialog they will need to also have a type the same as your project type, this depends on the extension of your project i.e. if you leave the default extension .csproj then when you add and item to your project you'll only see the CSharp items, you'll need to change the extension to something like .gtproj, then VS will know that you want to add an item that is the same as the project which manages the extension .gtproj.
Here's a snippet of the Package code
[PackageRegistration(UseManagedResourcesOnly = true)]
[DefaultRegistryRoot("Software\\Microsoft\\VisualStudio\\8.0")]
[InstalledProductRegistration(false, "#110", "#112", "1.0", IconResourceID = 400)]
[ProvideLoadKey("Standard", "1.0", "MyProject", "My Company", 1)]
// This attribute is needed to let the shell know that this package exposes some menus.
[ProvideMenuResource(1000, 1)]
[ProvideProjectFactory(typeof(MyProjectFactory), "MyProject", "MyProject Environment project Files (*.myproj);*.myproj", "myproj", "myproj", ".\\NullPath", LanguageVsTemplate = "MyProject")]
[Guid(GuidList.guidMyProjectPkgString)]
public sealed class MyProject : Package
{
public MyProject()
{
Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this.ToString()));
}
protected override void Initialize()
{
base.Initialize();
base.RegisterProjectFactory(new MyProjectFactory(this));
}
protected override void Dispose(bool disposing)
{
Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering Dispose() of: {0}", this.ToString()));
try
{}
finally
{
base.Dispose(disposing);
}
}
}
4. You'll need to need to implement certain interfaces in you VSPackage class, the stubs for these are provided and Visual Studios texteditor allows you to override the methods automatically by right-clickin the interface name and selecting implement from the context menu. You'll need to create a ProjectFactory and register it with your package.
Here's a snippet of the ProjectFactory code
[GuidAttribute(MyProjectFactory.MyProjectFactoryGuid)]
public class MyProjectFactory : Microsoft.VisualStudio.Package.ProjectFactory
{
public const string MyProjectFactoryGuid = "D1E0B0F6-5E61-42bf-ADD0-7CECC5665ED5";
public MyProjectFactory(MyProject package)
: base(package)
{
}
protected override ProjectNode CreateProject()
{
MyProjectNode project = new MyProjectNode();
project.SetSite((IOleServiceProvider)((System.IServiceProvider)Package).GetService(typeof(IOleServiceProvider)));
return project;
}
}
You'll notice a new instance of a class MyProjectNode, this is required, it is an Implementation of ProjectNode, this is required in order to use the Solution explorer in Visual Studio, without this you'll get an error at runtime, see step 7 for the ProjectNode code.
Hit F5 you should now see the new Project Type MyProject appear in the New Project dialog, it's Templates will be empty, so we'll move onto creating a template for your project type.
Tip: If you start to get an error which indicates a null memory address or something like that then make sure the ProjectFactory is being registered, if it is and the error persists then I'd recommend resetting the Experimental Hive with the Start menu utility I mentioned earlier and then F5 your project again.
5. You now have a VSPackage which is associated with your own project type and file type but we've mentioned nothing of your custom language yet. If you want all of the above to appear in Visual Studio from the installation (not from hitting F5 as internal developers would) you'll need to register this language and project type from the installer. A tool is used to fulfill this RegPkg.exe. To share the VSPackage with external users you'll also need a PLK, the PLK is a product key which can be obtained from Microsoft, it allows you to have your package installed on client machines without the requirement of the Visual Studio SDK. Without the PLK clients will not be allowed used your package and will get the following error when they try to create a project that uses your Language Service

RegPkg usage.
Obtain a PLK.
Using a PLK.
Also see Steps To Create Your First VSPackage, this explains how to install the PLK.
If this does not work have a look at step 8 of this post or go here "Debugging Package Load Failure (Dr. Ex)".
5. To create a Project Template file see the Topic at the end of this post "Creation of Project and Item Templates for..".
In order to associate the Project with your VSPackage type MyProject (with extenstion .myproj)
you'll need to edit the the contents of a file within the Project Template's zip file, the .vstemplate file. To do this you'll need to unzip the file. Open the .vstemplate file and edit an entry, the entry is
<ProjectType>CSharp</ProjectType>
The value CSharp could be another .NET language depending on what type of project you created your Template from.
Change the value to that of your Package i.e. MyProject.
Zip the contents back up, make sure the files are in the root of the zip file.
Now place the zip file under a new folder name MyProject, in parallel to the CSharp folder.
This zipping and installing can be done from your project using the MSBuild's ZipProject target.
To implement this you can set the type of the files to add to your template from Content to ZipProject. The files within the same folder under Template\Projects will be added to a Zip template and installed in the Visual Studio.
e.g.
creates a Project Template with the name of the last directory that the item resides in below the Templates folder.
<ZipProject Include="Templates\Projects\Windows\MyProject\projectitem.cs />
results in a project template named MyProject, MyProject.zip, created and installed in the Visual Studio installation.
same for ZipItem.
Oh yes, in order to get the template to appear in the VS installation in an addition subfolder you need to add another attribute to the ZipProject
<ZipProject Include="Templates\Projects\Windows\MyProject\projectitem.cs" >
<OutputSubPath>Windows</OutputSubPath>
</ZipProject>
results in the template appearing in the VS installation at
C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\ProjectTemplatesExp\MyProjectType\Windows
Select the project and ok, the contents listed by you in your .vstemplate file should appear in your new project.
You have a Project Type MyProject which has an extension .myproj and a project Template which appears within the Templates under your project type MyProject in the New project dialog.
7. If you want to customize the way Visual Studio handles the files found in your template then you'll need to create a ProjectNode implementation and override some methods in order to tell Visual Studio what to do when a node (file or project) is selected in the Solution Explorer of Visual Studio.
public class MyProjectNode : ProjectNode
{
public MyProjectNode()
{
// use the VS 2005 style property pages (project designer) instead of the old VS 2003 dialog
SupportsProjectDesigner = true;
// We allow destructive deletes on the project
CanProjectDeleteItems = true;
}
public override Guid ProjectGuid
{
get
{
return typeof(MyProjectFactory).GUID;
}
}
public override string ProjectType
{
get
{
return "myproj";
}
}
public override int InitializeForOuter(string filename,
string location,
string name,
uint flags,
ref Guid iid,
out IntPtr projectPointer,
out int canceled)
{
int result = base.InitializeForOuter(filename, location, name, flags, ref iid, out projectPointer, out canceled);
return result;
}
public override bool IsCodeFile(string strFileName)
{
if (String.IsNullOrEmpty(strFileName))
{
return false;
}
return
(String.Compare(Path.GetExtension(strFileName), string.Format("." + .my), StringComparison.OrdinalIgnoreCase) ==
0);
}
public override int GetFormatList(out string formatlist)
{
formatlist = string.Format("My Project File (*.myproj){1}*.myproj{2}",, "\0", "\0");
return VSConstants.S_OK;
}
#region IVsProjectSpecificEditorMap2 Members
public int GetSpecificEditorType(string pszMkDocument, out Guid pguidEditorType)
{
pguidEditorType = Guid.Empty; //typeof(YourEditorFactory).GUID;
return VSConstants.S_OK;
}
public int GetSpecificLanguageService(string pszMkDocument, out Guid pguidLanguageService)
{
pguidLanguageService = Guid.Empty; //typeof(YourLanguageService).GUID;
return VSConstants.S_OK;
}
#endregion
}
8. Debugging Package Load failures
If you were able to create a project of your custom type on your own development machine but failed when you tried it on another machine then this is the spot for you.
Tip : the easiest way to check that your package is registered with Visual Studio is to view the Help->About dialog in Visual Studio.
a. Debug the issue using a Tool called "Package Load Analyzer". It's part of the SDK version 4. It should appear in the Tools menu of Visual Studio if not the installer is present in the SDK installation, C:\Program Files\Visual Studio 2005 SDK\2007.02\VisualStudioIntegration\Tools\Bin\PackageLoader.msi, and when you've installed it it should appear in Visual Studio's Tool menu.
To use it, select Tools->Package Load Analyzer.
A dialog appears, select your Package from the list. Right Click and select "Analyze" from the list.
The information given is only an indicator to what is wrong, either the PLK is at fault of some of the dependencies failed to load.

b. A problem maybe that you've not installed the language service in the correct Hive. First try devenv /setup again from a command prompt. If this doesnt work have a look at the the registry
HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\8.0\Packages,
your Packages GUID should be here, if not it probably wasn't installed, look in the Experimantal hive's registry
HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\8.0Exp\Packages,
you may see your GUID here instead, this probably means that your installed registered only the Experimental Hive.
Remember back to RegPkg usage., you generated a registry setting using RegPkg.exe based on your packages assembly,
C:\Program Files\Visual Studio 2005 SDK\2007.02\VisualStudioIntegration\Tools\Bi
n\Regpkg.exe /assembly
do this again but with the first argument as the non-Experimental hive, and the second argument is /regfile to generate a registry settings file which you Import into your Setup project.
C:\Program Files\Visual Studio 2005 SDK\2007.02\VisualStudioIntegration\Tools\Bin\Regpkg.exe /root:Software\Microsoft\VisualStudio\8.0Exp /regfile:
Reinstall.
If this is not the problem it maybe that the Visual Studio SDK is not installed on the current machine.
c. If it isn't then you'll have to add a PLK to you VSPackage and reinstall. The PLK allows your custom package to be used on a machine that does not have the SDK, I've described some of this earlier.
According to the experts the most frequent cause of Package Load Failures is with a difference in detail supplied for the PLK and for your Package.
First look at you PLK details on the VSIP website and compare with the details in you VSPackage implementation. The Company name, Product Name and Package GUID must be identical.
If this does not work then it's time for some debugging.
d. To simulate Visual Studio without having the SDK installed you can launch VS with
devenv /noVSIP
or
devenv /rootsuffix Exp /noVSIP
Now try to create a project of your custom type, if it fails you'll see some warning messages appear in the output console.
You can read this message later using the Visual Studio logger using
devenv /noVSIP /log
or
devenv /rootsuffix Exp /noVSIP /log
an XML log file is created in the Application data folder
C:\Documents and Settings\
or
C:\Documents and Settings\
named ActivityLog.xml
e. Problems with your Package not appearing in Visual Studio can be that the Registry settings are incorrect. One problem I came across is that I had failed to generate the .reg file using RegPkg correctly, I had left out the /assembly, without this the registry does not know the Verioning details of the assembly, if you use /assembly you'll also need to let the Registry know where the assembly is this means you'll need to put the Assembly in the GAC! see my post "dotNet Miscellaneous" to see how to install the assembly in the GAC from code (you would do this in you Setup Project's Custom Action).
So now your Assembly is in the GAC (using code in your installer custom action).
You've used RegPkg to create a registry setting and added this to your Setup project.
At install time the Assembly gets added to the GAC, the Registry setting is set to register your Package with Visual Studio using the Assembly in the GAC.
Your setup runs updates Visual Studio with devenv /setup.
Your now good to go...
VSPackage Language Service
This is not the same as the Babel Language Service I mentioned earlier, this type used Flex and Bison directly and uses lots of additional resources in the VSPackage. It too allows you to create Text editors with color coding etc which you can use with a file type of your own and your own Project Types.
This too is available from the VS File->New Project dialog but at: File->New Project->Other Project Types->Visual Studio Language Package.
The wizard will step you through the setup but all is not straight forward, as usual.
The wizard requires you have Flex.exe and Bison.exe installed, the wizard will prompt you for these. Google will turn up installation for these resources.
VSPackage and Projects
To create a VSPackage Project with multiple projects being created when the solution of your type is create you need a to install a MultiFileTemplate in the VS installation along with your VSPackage implementation.
To install the MultiFileTemplate you have to add the contents to a zip file and copy the zip file with the MultiFileTemplate and sub .vstemplate files yourself to the MyDocuments or to the VS installtion directorty, C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\ProjectTemplates\MyProjects, so that VS will pick it up. This can be done from within the source project file which is generating the MultiFileTemplate using the MSBuild ZipItem or the third party Zip build task MSBuildTasks that I've mentioned in another thread MSBuild.
Examples of this functionality are included in the SDK.
Project and Item Templates
Create Reusable Project and Item Templates for the development team.
Editing VSPackage File and Project Icons and Bitmaps
There are usually 2 types of image in a project, the application icon and the filetype bitmap.
The application icon is the image that appears beside the project template in the new project dialog. This is a 32x32 pixel icon with the extension .ico. It is set with the icon element in the vstemplate file.
The second is the bitmap, this used by the VSPackage to display an image beside the file or the project in Visual Studios soulution explorer. The bitmap is created as part of a bitmap file in the Resources of your source project. The bitmap may have many images grouped together but each image should only be 16x16 pixels, this is because they are access by the code and indexed based on 16 pixels. Here's how the image is access
first you add all of the bitmaps to an imageList, remember they are indexed with 16 pixels
FileTypeImageList =
Utilities.GetImageList(typeof(MyProjectNode).Assembly.GetManifestResourceStream("MyNameSpace.Resources.FileTypeImageList.bmp"));
//Store the number of images in ProjectNode so we know the offset of the python icons.
_imageOffset = this.ImageHandler.ImageList.Images.Count;
foreach (System.Drawing.Image img in FileTypeImageList.Images)
{
this.ImageHandler.AddImage(img);
}
You now have a list of all the bitmaps. You now override the ImageIndex() method in both you FileNode implementation and your ProjectNode implementation like so
public override int ImageIndex
{
get
{
return (int)ImageOffset + (int)MyTypeImageIndex.GtProjFile;
}
}
the MyTypeImageIndex.GtProjFile is just an enum, in the above case the enum returns 1, this indicates the the bitmap for this type, in this case this override is in my ProjectNode implementation so the if a project is created of my type then the bitmap indexed at 1 (pixel 17 to 32) in the bitmap file FileTypeImageList.bmp is used with the project and will appear in solution explorer for that project.
January 24, 2007
ASP.NET, terms, filetypes, examples
http://feeds.delicious.com/rss/learnerplates/httphandler
ASP.NET is the .NET version of ASP, this new version adds more functionality.
ASP and ASP.NET are web development frameworks which allow you the developer to access and manipulate HTML before Posting it up to the client. Both frameworks allow you to access HTML controls (e.g. TextField) using the directive "script runat="server"" . This gives you a reference to the HTML control, that the user sees in their browser, from the server side.
You also have control over the Http Requests and Responses, allowing you to analyze responses sent back, errors and cookies etc can be transmitted over HTTP.
ASP was lacking in that you could not separate the scripting logic from the HTML front end of the file.
ASP.NET allows you to move your logic (written in any .NET language) to a separate file, this is referred to as CodeBehind. The codebehind can be accessed in the aspx file using
<%@ Page Language="C#" CodeFile="AppErr.aspx.cs" Inherits="AppErr" %>
As you can see you must indicate what language you've developed your logic in, C#, the source file which contains your code, AppErr.aspx.cs, and the namespace of the Class, AppErr.
The .cs file can now manipulate and HTML Controls which use the "script runat="server"".
The .cs file is compiled by IIS at runtime and stored, on the Server side, in .NET as MSIL (Microsoft Intermediate Language).
Always remember that the browser can only handle HTML and Script, so no matter what your design is written in, in this case ASP.NET controls, they all render to HTML and Script at runtime.
Here's a description of how a Http Request is processed by ASP.NET (aspalliance)
Lifecycle
The lifecycle of how a HttpRequest/Response is dealt with by ASP.NET (msdn) is pretty long, in short the AppDomain is built up, the Global.asax is compiled and it's Http Handler methods used, the Pages are compiled, the .ashx HttpHandler is compiled and used.
Debugging ASP.NET
Ensure the debug flag in your web.config is set to true. If you are unable to step into the code then edit the file and add the
System.Diagnostics.Debug.Write("Your Debug Message");
If in release mode and you wish the browser to prompt you to start a debug session then use
System.Diagnostics.Debug.Assert(false,"Your debug message");
Exception and Error Handling
There are 3 ways to handle Exceptions/Errors in ASP.NET.
1. Implement a IHttpHandler and name it with .ashx.
2. Implement a IHttpModule and name it with .asax.
3. Override the Page.OnError method in each of your .aspx files.
1. Is the easiest. Create a file name MyApp.ashx, IIS by default associates .ashx files with .aspx files as their error handler. So if an error occurs on a .aspx file it will be redirected to the .ashx file and it's up to you to do whatever you wish here.
The HttpHandler class can be generated in Visual Studio with one of the WebSite Templates. In your IHttpHandler implementation override the ProcessRequest(HttpContext context) method and access the Errors thrown using the context.Error collection.
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/xml";
if (context.Error != null)
{
// Code that runs when an unhandled error occurs
Exception ex = context.Error;
if (ex.GetType().Equals(typeof(HttpException)))
{
string errorString = string.Empty;
errorString = "Http Error: # " + ((HttpException)ex).GetHttpCode().ToString()+ " "+ ex.Message;
context.Response.Write("
}
context.ClearError();
}
}
context.Response.ContentType = "text/plain";
to
context.Response.ContentType = "text/html";
http://www.15seconds.com/issue/060406.htm
2. HttpModules are classes that Implementet IHttpModule.
HttpModules have methods which are all particular Event Handlers i.e. there is an Event Handler method for the Application_Start and more importantly in for this topic void Application_Error(object sender, EventArgs e) .
You can add your one EventHandler method for application Events in the HttpModules Init method
e.g.
using System;
using System.Web;
public class MyHttpModule : IHttpModule
{
/// In the Init method register for HttpApplication events by adding your handlers
public void Init(System.Web.HttpApplication application)
{
application.BeginRequest += new EventHandler(this.Application_BeginRequest);
application.AuthenticateRequest += new EventHandler(this.Application_AuthenticateRequest);
}
private void Application_BeginRequest(object sender, System.EventArgs e)
{
HttpApplication app = ((HttpApplication)(sender));
HttpContext context = app.Context;
}
private void Application_AuthenticateRequest(object sender, System.EventArgs e)
{
HttpApplication app = ((HttpApplication)(sender));
HttpContext context = app.Context;
}
}
You must then register your HttpModule in the web.config
e.g.
<system.web>
<httpModules>
<add name="MyHttpModuleName" type="MyHttpModule"/>
</httpModules>
What you've to do is create a .asax file, usually called Global.asax, the Global of course meaning this is Application wide, all errors that occur will be caught with this files EventHandler methods. Again the .asax file is mapping in IIS to .aspx pages as an Handler.
Again this file can be created from a Website Template.
Use the HttpApplication.Server object to get the Errors from using the GetLastError() method.
File Types:
.aspx - a file containing the HTML and maybe the scripting language.
.asmx - a proxy class which contains the definition for a webservice.
.asax - a HttpModule, Global.asax, this has handler methods for events which are invoked by the application e.g. Application_start, if you wish to add some handling code for certain events e.g. Exception handling then this is the file, one of the handler methods is Application_Error, just override this method and add append you Response with whatever messages you want.
.ashx - a Handler for the HttpRequests and HttpResponses Debugging HttpHandler (15seconds.com). If you want to examine the contents of the Requests and Responses then this is the place. If you have custom file types, files with your own extensions, then you can create your own Handler file, .ashx, and map it to you file type in your web.config. You can also use the .ashx file to handle your applications errors which have not been handled already, such ass Http 500. In the ProcessRequests method you can call the base,
IHttpHandler.ProcessRequest
for Exceptions if they've occurred then deal with them. Or you can extract all the Errors from the Request,
Context.Request.Error
and process them as you like.
ASP.NET Resources - ASP.NET Custom Error Pages.
January 22, 2007
dotNET - Lists comparisons and Predicates
When trying to find certain contents of a list you have a few options open to you.
The first is to enumerate through the list and do a comparison of each value in the list, this is the old way to do things:
foreach (string sf in sourceFiles)
{
if (sf.Contains("DBAccess.js"))
containsDBAccess = true;
}
.NET has provided a better implementation using Predicates.
Predicates are implemented with delegates.
You implement your Predicate with with a Function which takes a single parameter, a string, and returns bool.
e.g.
private static bool FindDBAccess(string s)
{
return s.Contains("DBAccess.js");
}
and use this in the List.Find like so
if (sourceFiles.Find(FindDBAccess) == null)
{........
}
The string 's' passed as a parameter is actually each of the values in the list.
The delegate FindDBAccess is called for each item in the list, and if it returns True for any one of these items we have success. In this case we compare 's' each time with a certain string, "DBAccess.js" in this case.
There is a shorthand way of writing this too, no need for the actual Predicate method:
if (sourceFiles.Find(new Predicate
{
}
January 17, 2007
dotNET - CLR Profiling and memory management
The CLR implements Garbage Collection. This GC can run into problems and may not run as well as it should if you implement your applications in certain ways. GC problems may manifest themselves as a slowdown in the application, this maybe due to the GC spending time trying to manage memory which is fragmented, in very large segments or objects with many references.
The CLR manages the memory by using a pointer to the memory addresses, these pointers must do alot of work particularly if an object has many references, each reference requires a pointer.
You can invoke the GC with the System.GC class. GC.collect().
There are some tools and code classes available to monitor memory. One of these is a class called Perfmon (performance monitor), to use this you add a Perfmon instance in your class, this instance gets incremented each time the GC gets used on that object. You may then query the Perfmon object to find which objects are using what memory.
.NET CLR Profiling (TechRepublic article)
January 16, 2007
dotNET - Protect IP, Obfuscation (Obfuscate)
It's achieved by scrambling and removing some of the contents of the assembly.
Scrambling maybe just renaming of methods (it does not rename methods which are public to other assemblies outside).
It may also remove unnecessary Metadata such as Property descriptors.
There are various levels of Obfuscation and these can all be set in the Obfuscation application of your choice.
Obfuscated assemblies maybe accompanied by a Map file, a kind of settings file for the Obfuscation application.This map file may contain the renaming pattern you have used and is input to the Obfuscator application, it is useful if you wish to patch a system with say one assembly, and you want your new assembly to be obfuscated in the same way as the application.
Under Construction
Thwart Reverse Engineering of Your Visual Basic .NET or C# Code
dotNET - Debugging
MSIL Assemblies
Assemblies compiled with .NET tools such as the CLR compiler are compiled into a file which contains MSIL (Microsoft Intermediate Language). At runtime the contents of the assembly are loaded into the CLR and ran as machine code. When you compile an assembly in debug a PDB file is generated alongside the DLL or EXE you've just created. The link between these 2 files is that the PDB contains the line numbers of the methods and classes as well as the file names of the original source code that created the assembly. When you launch the debugger in Visual Studio the assembly is loaded into the Debugger (similar to the CLR) along with the PDB file. The debugger now uses your PDB file contents to match the running code found in the assembly to locations in source files (hopefully in your present project).
CLR
CLR Inside Out (msdn magazine)
.NET Framework Tools: CLR Debugger
December 15, 2006
December 06, 2006
dotNET - Use app.config ApplicationSettings and UserSettings
But what if you're not working inside that Assembly or .exe? this presents a problem.
If your loading the Assembly externally and want to access that Assembly's .config file you'll probably wish to use something in the System.Configuration namespace... unfortunately it's not of much use if you've created the .config file from the Settings Designer in Visual Studio!!
This is because the Designer creates Sections and ApplicationSettings and UserSettings, the System.Configuration namespace does not provide a method to access these (it has a method to access AppSettings which are a different thing.
Below I've written a workaround which locates the app.config and accesses the ApplicationSettings and UserSettings using XML instead of System.Configuration.
_server = GetAssemblySettingOrDefault(Path.Combine("C:\temp\MyAssembly.dll"), "serverLocation", "http://localhost/mydefaultserver/mydefaultserver.asmx");
Return a key/value list of ApplicationSettings and UserSettings for a given .exe's of .dll's .config file.
public static KeyValueConfigurationCollection GetAssemblySettings(string assemblyPath)
{
Configuration config = ConfigurationManager.OpenExeConfiguration(assemblyPath);
XmlDocument dom = new XmlDocument();
dom.Load(config.FilePath);
//UserSettings and ApplicationSettings
KeyValueConfigurationCollection returnList = new KeyValueConfigurationCollection();
string[] settingsTypes = { "applicationSettings", "userSettings" };
foreach (string settingType in settingsTypes)
{
XmlNode node = dom.SelectSingleNode("//configuration//" + settingType);
if (node != null)
{
try
{
if (node.HasChildNodes)
{
foreach (XmlNode childNode in node.ChildNodes)
{
if (childNode.HasChildNodes)
{
foreach (XmlNode settingNode in childNode.ChildNodes)
{
if (settingNode != null)//the Settings node
{
if (settingNode.Attributes.Count > 0) //there should be at least one attribute
returnList.Add(settingNode.Attributes[0].Value, settingNode.InnerText);//the setting name and the setting/value
}
}
}
}
}
}
catch { throw; }
}
}
return returnList;
}
// Get the value of the Assembly ApplicationSetting or UserSetting from the key.
// If the key does not have a value then return the default supplied by the caller.
{
string result = assemblySettingDefaultValue;
KeyValueConfigurationCollection settings = GetAssemblySettings(assemblyPath);
if(settings != null)
{
KeyValueConfigurationElement key = settings[assemblySettingKey];
if(key != null)
result = key.Value;
}
return result;
}
November 29, 2006
Windows Miscellaneous
Creating a Virtual PC using another machines Virtual Hard Disk
Make a copy of the other machines .vhd file.
Copy to the local machine and point the new Virtual PC to the .vhd.
SID will have to be run on the new Virtual PC in order to change the name of the machine, as it will still have the name of the original machine the vhd was taken from.
http://www.microsoft.com/technet/sysinternals/Security/NewSid.mspx
HTML DOM Inspector
http://www.sharewareconnection.com/download-ie-dom-inspector-from-sharecon.html
November 14, 2006
dotNET - VS 2005 Web Deployment Projects + Installer (MSI) creation
There are 2 ways to do this:
1. Create a Web Deployment Project from your Web Project and then use the output of this as the input to another project, a Setup Project.
After you've achieved this you'll have an MSI installer file which has configurable elements, these configurable elements will be dictated by yourself when creating the Web Deployment Project and the Setup Project.
2. Create a WebSetup project from your Web Project.
After you've achieved this you'll have an MSI installer file.
Option 2 is the simpler option.
The difference between the 2 options is that the first provides extra control using the Deployment project, such things a MSBuild and assembly type deployment.
If you choose option 1 then:
- You can quickly create a Web Deployment Project by right-clicking the Web Project in the Visual Studio Solution Explorer. This will copy the contents of your Web Project into the new Deployment Project. See the links below on how to customise the settings.
- The Setup Project uses the Web Deployment Project, this is achieved by adding the Web Deployment projects output as a reference, Right Click on the Setup Project...
- Notes:
- I noted during the Web Deployment Project creation that;
- Changing the Project Properties does not always apply e.g. I changed the Property Pages->Deployment section to "appsettings=appsettings.config", this indicates the appsettings section of the Websites web.config should be overridden with that in the appsettings.config file in the source Web Project. But the appsettings is case-sensitive, I fixed it but it still did not apply resulting in a compilation error, I had to edit the project file itself using the VS editor. The contents of the appsettings.config file is the complete appSettings section, including the appSettings tags.
- The appSetting file is located in the original Web Project.
- Using appSettings=appsettings.config in the Web Deployement Project's Property Pages->Deployment section it overrides the appSettings section.
- This is a much simpler solution and really just copies the contents of your Web project into an exe or msi file for later installation.
- Create a Web Setup project from the File->New Project dialog, under Setup and Deployment.
- Add the Web site project's Output as a reference in the WebSetup project.
For both option 1 and 2 you'll need to create some sort of Setup project, either a Setup project (Option 1. which uses the output of the Web Deployment project) or a Web Setup project (Option 2. which uses the Web project directly).
For more on both types of Setup project and how to use the user input see the article later in this blog dotNET - Setup Programs Installer creation using VS2005
Using Web Deployment Projects with Visual Studio 2005 (msdn)
ScottGu's Blog - VS 2005 Web Deployment Projects
Modifying Web.Config during Website Installation (AspAlliance)
November 10, 2006
IIS admininstration Miscellaneous
Parser for IIS logs
A script to extract data from log files (can be IIS log files) using SQL Query format.
http://www.microsoft.com/technet/community/columns/profwin/pw0505.mspx
The script can be downloaded at
http://www.microsoft.com/downloads/details.aspx?FamilyID=890cd06b-abf8-4c25-91b2-f8d975cf8c07&displaylang=en
November 07, 2006
Cookies and FormsAuthentication
Cookies are simply a file stored in the client machine which are sent up and down to and from the server with every Request and Response.
The Cookie is used to store some client information such as details of their past session. It allows the Client to return to a webpage and have information already available to them without having to start from scratch.
The Cookie is first sent down from the Server and is stored somewhere on the Client's hard-drive.
It's up to the Web Application developer to do the Cookie processing on the Server side. The Cookie can be accessed from the Request as the Cookie is a property of the HttpRequest, Request.Cookie["cookiename"];
One problem I've encountered with Cookies is that all the cookies associated with your application get Posted from the Client on each Request, this adds to the amount of data sent as you can imagine. There is a solution however, in order to ensure a Cookie is only sent from Client to Server when a certain page is open you must create the Cookie with it's path set to that page;
HttpCookie rememberLogin = new HttpCookie("rememberLogin", rememberLogin.Expires = DateTime.Now.AddDays(5);
rememberLogin.Path = Request.Path;
Response.Cookies.Add(rememberLogin);
Codeproject.
Authentication
Who Are You?
There are 2 main types Windows and Forms.
Windows Authentication
This will use the clients credentials i.e. their actual windows login that they are presently using, allowing your server side app to examine who the client actually is.
Note that the client will usually have to pass their credentials onto the Server, this can be done in code adding their credentials, System.Net.CredentialCache.DefaultCredentials, to the HttpRequest with the following:
for a .NET Remoting call:
IDictionary channelProperties = ChannelServices.GetChannelSinkProperties(_remoteObject);
channelProperties["credentials"] = System.Net.CredentialCache.DefaultCredentials;
For all of this to work you muse have "Integrated Windows Authentication" (only) enabled on the Server or a subdirectory within the Server.
If you also have "Anonymous Access" enabled then Windows Authentication will not work, if you must have "Anonymous Access" enabled as well for some reason then follow these 3 steps:
1. Create a subdirectory and set it's Security settings to "Integrated Windows Authentication" e.g. "MyIntegratedDir". This subdirectory will then work as you'd expect and you can use Windows Authentication on this directory.
2. Add an entry to the Web.config to tell IIS Security to permit Anonymous users access to the particular directory.
<system.web>
<authorization>
<allow users="?"/>
</authorization>
</system.web>
</location>
3. Place you Custom Authorization code within this directory i.e. probably in an aspx page
FormsAuthentication
FormsAuthentication is not directly related to Cookies but they can work together.
You can access the FormsAuthentication information from code using the FormsAuthenticationTicket.
By querying this object you can redirect etc to other pages depending on roles etc.
The FormsAuthenticationTicket object can then be passed to the Cookie constructor in order to save the information, this means the Client does not have to provide the information on each page, the Cookie is sent up and down to the Server ensuring the Client has access.
codeproject.
Authorization
In the web.config you can specify what groups or who you wish to allow or deny from your website, you can break your website down into subfolders, this is known as Authorization i.e. What you are allowed to do.
dotNET - Http traffic debugging using Fiddler
You can view the traffic on requests including Cookies and Cached data.
For more see msdn and fiddler.com.
November 06, 2006
Setup Programs Installer creation using VS2005
Getting Started with Setup Projects (SimpleTalk).
Visual Studio Setup Projects and Custom Actions (Simple Talk).
Updates to Setup Projects (SimpleTalk).
To create an Installer using Visual Studio you must create a Setup Project. A setup project contents are files and outputs of other projects in the solution. The Setup Project template can be found in the Visual Studio New Project dialog under Other Projects->Setup and Deployment->Setup Project.
Tip! To debug installation or just see what's happening in the background and view system variable values use the msiexec logger, it logs everything that's happening on installation, it can also be used for uninstall
install:
msiexec /i yourinstaller
or verbose
msiexec /i
uninstall:
msiexec /uninstall yourinstaller
or verbose
msiexec /uninstall
Description of how to create a Setup Project
Deploying Windows Applications (Charp Onlline .NET).
You'll see that the Setup Project (note there's also a WebSetup project template) supplies the dialogs etc for user info entry, I don't think there's a way to override this. The dialogs allow limited user input. The user input from the dialogs textfields is available to you through Properties, I'll talk about how to access them now.
Setup Project Property Reference (msdn).
This is all done for you but there are a couple of things to look out for, as always if you wish to do something different it's tricky. For example if you wish to get some information on the Installation you must use a Custom Action.
Custom Action
The Custom Action is added to the Setup Project, select the Project node and hit the Custom Action button. This allows you add an Action to a particular phase in the Installation. But first you must create the Custom Action.
To Add a Custom Action you must first have a Custom Action created, this is usually in the form of a Installer Class, this should be created in a seperate project, the Installer Class is actually one of the File Templates in the C# Projects. So it's File->New Project and select Visual C# Projects. Then add a Class Library, this will prompt you for the Class Library Types , select "Installer Class".
Also here's a more comprehensive document on Setup/Installer implementations, it delves into the Registry etc
Getting Started with Setup Projects (SimpleTalk).
Visual Studio Setup Projects and Custom Actions (Simple Talk).
Create your Installer Class and then add it as a Custom Action to the Setup Project.
What happens now is that you can access the Installers data using what are called Installer Properties. How this is done is a bit messy, after you've added your Installer Class as a Custom Action to the Setup Project you must specify arguments to be passed to the Installer Class at runtime, apparantly this is the only way to references installer data after the installation has completed.
Here's an example of how to pass the name of the installation directory to the Custom Action assembly:
This is done in your Setup Project's Custom Action Tab.
Set the property of the Custom Action you have just added to
/InstallLoc="[TARGETDIR]\"
NOTE: the extra \" at the end is required to return a directory.
This tells the Custom Action dll that it can access the TARGETDIR (which is the installation location) with the InstallLoc arg, here's how it's done in the Custom Action class:
this.Context.Parameters["InstallLoc"];
see CustomEventInstaller.Installer
private void AfterInstallEventHandler(object sender, InstallEventArgs e)
{
//Copy the assemblies to the location _installationLoc
//From the command line (passed in by the Setup)
//_installationLoc is also used in the generated .targets file
//InstallLoc="[TARGETDIR]""
_installationLoc = this.Context.Parameters["InstallLoc"];
For multiple Custom Action parameters use a single space between each parameter.
/appsetting1=[EDITA1] /appsetting2=[EDITA2] /installLoc="[TARGETDIR]\"
accessible in your Custom Action code with
_installLoc = Context.Parameters["installLoc"];
_appsetting1 = Context.Parameters["appsetting1"];//first edit
_appsetting2 = Context.Parameters["appsetting2"];//second edit
For Common Properties used in installer applications and other like TARGETDIR see
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/property_reference.asp
http://msdn2.microsoft.com/en-us/library/9cdb5eda.aspx
Updating/Patching the installationUpdates to Setup Projects (SimpleTalk).
It doesn't appear that this is implemented all that well with Visual Studio setup projects, they don't have the power.
The SimpleTalk link gives instructions on the ways to implement update packages but it does mention that patching (just updating certain files) is not implemented, it's a full install/reinstall or nothing, even the repair does this.
Merge Modules
Installing Shared Components using Merge Modules (msdn).
The contents of the Setup Project can be broken up into Merge Modules. The Merge Modules contain Components, the components contain your installer items.
The Merge Modules are a useful tool if you wish to add contents to your installer from other resources such as projects outside of your current solution, if you wish to import outputs from a project in a location outside of your present solution then create a new Merge Module project in the other solution and then add the Merge Module to the current Setup Project.
MSBuild and Setup Projects
Word of warning on Setup Project (.vdproj). MSBuild will not build these type of projects. The result is a warning in MSBuild ... and the contents (the Outputs of other Projects which the Setup uses) are not updated, it could install older versions of files than you had hoped.
One solution would be to Build the Setup project manually after running MSBuild.Another solution is to tell devenv to explicitly build the Setup project
There are 2 ways to do this:
- On the command line, e.g in your build script with
- In another project file tell devenv to build the project using devenv
November 01, 2006
Miscellaneous
The "new" keyword can be used with methods, it provides a type of inheritance similar to polymorphism.
class Program
{
static void Main(string[] args)
{
BaseClass bc = new BaseClass();
ChildClass cc = new ChildClass();
bc.Foo();
bc.Bar();
Console.WriteLine("=======================");
((BaseClass)cc).Foo();
((BaseClass)cc).Bar();
Console.WriteLine("=======================");
cc.Foo();
cc.Bar();
Console.WriteLine("=======================");
Console.ReadLine();
}
}
class BaseClass
{
public void Foo() { Console.WriteLine("BaseClass.Foo"); }
public virtual void Bar() { Console.WriteLine("BaseClass.Bar"); }
}
class ChildClass : BaseClass
{
new public void Foo()
{
Console.WriteLine("ChildClass.Foo");
base.Foo();
}
public override void Bar()
{
Console.WriteLine("ChildClass.Bar");
base.Bar();
}
}
Output:
BaseClass.Foo
BaseClass.Bar
=======================
BaseClass.Foo
ChildClass.Bar
BaseClass.Bar
=======================
ChildClass.Foo
BaseClass.Foo
ChildClass.Bar
BaseClass.Bar
=======================
In the above the use of new in the method signature indicates that we the ClildClass wishes to override the BaseClass method Foo().
As we can see from the output the call to
((BaseClass)cc).Foo();
results in
BaseClass.Foo
as we would expect. You could use Virual/Override to do the same thing. But note in the above case we use DownCasting, and in this case the Virtual/Override still calls the ChildClass.Foo and the new assignment version does not, this is suprising to me at least as I would have thought the opposite were true.
Calling one Constructor from another with CSharp
This allows us to create an instance with either of these 2 overloads.
Note that if we use the first overload then the second parameter is null, usually a check would be done in the second overload for null and some default applied
public MyClass(string param1) : this(param1, null)
{
}
public MyClass(string param1, string param2)
{
if(param2==null)
param2 = "default";
}
These could also be called vice versa of course
public MyClass(string param1)
{
_param2 = "default";
}
public MyClass(string param1, string param2) : this(param1)
{
_param2 = param2;
}
Request a Web Page from outside a Web Application
using System.Net;
using System.Xml;
using System.IO;
namespace UrlCall
{
class Program
{
static void Main(string[] args)
{
string url = "http://localhost:3703/ServerProject/Cookie/GetCookie.aspx";
HttpWebRequest myWebRequest = (HttpWebRequest)HttpWebRequest.Create(url);
myWebRequest.Method = "GET";
// make request for web page
HttpWebResponse myWebResponse = (HttpWebResponse)myWebRequest.GetResponse();
StreamReader myWebSource = new StreamReader(myWebResponse.GetResponseStream());
string myPageSource = myWebSource.ReadToEnd();
myWebResponse.Close();
}
}
}
Reflection, how the metadata works
CodeProject has an explanation of the internals of a .NET assembly.
Training/Certification
dotNetSlackers has a good description of the .NET Certification available.
Tools
.NET Framework Tools (msdn).
CFF Explorer (Tool to investigate the internals of a .NET assembly).
Fix ASP.NET
If you've installed a new version of .NET you'll have to register it, it's required for ASP.NET to use the correct version
aspnet_regiis.exe -i
Depending on where you call this from will determine which .NET gets used with ASP.NET
e.g.
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_regiis -i
Errors that this may fix are
"Some of all identity References could not be translated"
"iis metadata access rights.
GAC (installing assemblies in the GAC)
The GAC is a location on Windows where Assemblies can be stored, the idea is that all applications can access this one assembly. It's also versioned. The GAC contents can be viewed at C:\Windows\Assembly.
To install your assembly in the GAC you can use to methods
The exe from the .NET SDK
C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\gacutil.exe /i
or the Visual Studio SDK
C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe /i
or programmically using the GacInstall class
try
{
System.EnterpriseServices.Internal.Publish gac = new System.EnterpriseServices.Internal.Publish();
gac.GacInstall(assemblyName);
}
catch (System.Security.SecurityException se)
{
}
Assembly Code Analysis Tool (FxCop)
FXCop is a MS Tool which analyzes you Managed Code by loading your assembly, reading the manifest and analysing the MSIL in the CLR.
FxCop bried description (TechRepublic)
FxCop (msdn)
Download from gotdotnet.com
Http debugging tool
So you want to view the Http traffic between client and server. Fiddler's your man.
You can view the traffic on requests including Cookies and Cached data.
For more see msdn and fiddler.com.
Some useful dotNET methods etc
- Ref and Out
- Use these keywords to pass a reference to another method so that it the reference itself can be changed. All objects are passed by reference in C#, meaning if you wish another object to change values in another object you simply pass the objects reference (the variable name) to the method as normal.
- Class Passer
{
List }messages = new List ();
Receiver r = new Receiver();
r.Method(messages); - Class Reveiver
{
void Method(Listmessages)
{
message.add("I'm adding a new item to the Passers member messages, this affects the original");
} - However if you want another method or object to change the instance itself, I mean new-up a new memory location with your original reference then you use Ref.
- The difference between Ref and Out is that Ref is initialized by the passing object and Out is initialized by the receiving object e.g. Class Passer
{
List
Receiver r = new Receiver();
r.RefMethod(ref refMessages);
}
- Class Reveiver
{
void RefMethod(ref List
{
refmessage.add("I'm adding a new item to the Passers member messages");
}
{
List
outmessage.add("I'm adding a new item to the Passers member messages");
}
}
- To determine if it's a web app or a windows app use
- System.Web.HttpContext.Current;
- If null it's a windows app.
- TryParse can now be used to determine if 2 values are equal.
- It can presently be used for int, double, DateTime ....
- There are 2 params, the value your comparing to and the out which is the result boolean value which is set.
- Using statement
- If you are using an Object which implements the Dispose/Inherits from IDisoposing then you can use Using to ensure the object is GC'd after use.
using(StopWatch sw = new StopWatch try)
{....
}//sw is deleted here.
- Get the name of the current method at runtime, useful for printing:
Get the name of the Current Method
string currentMethod = mn.Name;
Get the Current Class name
string currentClassName = mb.DeclaringType.Name;
Or Get a Method at a particular location in the stack: System.Diagnostics.StackFrame sf = new System.Diagnostics.StackFrame(0, true); //To get the calling method name, use 1 instead of 0.
string currentMethodName = System.Reflection.MethodBase mb = sf.GetMethod();
string methodName = mb != null ? mb.Name : "nothing found";
- Creating Disposable Objects
- Visual Studio Project Macros/Variable/Properties
- Here's a useful static method for dumping messages (debugging) to a file:
public class DebugWriter
{
public static void WriteDebugToFile(string message)
{
string path = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, "DebugFile.txt"));
StreamWriter sw = null;
if (!File.Exists(path))
{
using (sw = File.CreateText(Path.GetFullPath(Path.Combine (Environment.CurrentDirectory, "DebugFile.txt"))))
{
}
}
using (sw = File.AppendText(path))
{
sw.WriteLine(message);
sw.Close();
}
}
}
October 27, 2006
dotNET Project Template and Addin Creation
Project and Item Templates Create Reusable Project and Item Templates for the development team. Project Template Creation Creation of a project or item template is pretty straight forward. Use the File->Export Template to create the template, the wizard will ask you which Project you wish to base your Template on, this will result in the contents of that Project being added to the Template. Note that depending on the type of Project you select will determine where the Template appears in the File->New Project dialog. i.e. If you base your Template on a Website Project in VB then the Template will appear in the File-New Website and under the VB section in the File->New Project dialog. Create a project like the one found at http://msdn.microsoft.com/msdnmag/issues/06/01/CodeTemplates/default.aspx. The Template is just a zip file with an extra xml file within, the xml file describes what to add to the project when a user uses File->New Project. If you wish to generate Project Templates at build time from another project it can be done through MSBuild. I've written a description of this in the MSBuild Post, ZipProject is the MSBuild task. Editing the Contents of the Template File The File->Export Template will create the template for you. When the user uses this template they are not prompted for anthing besides the Project name, what if you wish for the user to supply some additional information when creating a new project using your template? It's not very easy... Basically you have to create a seperate class which is a type of Form, it's an implementation of IWizard. The assembly generate needs to be registered in the GAC and then a reference to this Assembly added to your Template file, see this msdn link: http://msdn2.microsoft.com/en-us/library/ms185301.aspx. Note that one of the steps is the Signing the Assembly, use the VS feature in the Project Properties for this. And then run C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0>gacutil /i C:\temp\WizIDE\WizIDE\bin\Debug\WizIDE.dll Now we have installed the assembly. After the assembly is installed you should use the File->Export Template. After that youwill need to edit the XML file, so unzip the file at and edit the xml file Add the assembly name details which were generated with the Project->Properties->Signing feature, to see what the value is used gacutil again C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0>gacutil /l WizIDE And add the result to the xml file after , that is outside of TemplateContent, like the following: PublicKeyToken=1d20aa67bdcd2085, processorArchitecture= Now Rezip and open a new project of this type. If you change the assemble functionality then maybe a reinstall is required C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0>gacutil /uf WizIDE Web Application Template Creation The template is generated in the same manner as the project template, select your website project and use File->Export Template. As with the project template, templates can be created at build time from your project using MSBuild, see my MSBuild post, ZipProject is the MSBuild task, you'll see that the Web application template is a bit more difficult to configure from MSBuild, there are alot of workarounds to include subfolders in the resultant zip file. String Substitution in Templates Project Template also provide the ability to substitute strings for strings in the files included in the Template. Your .template file which is created for you (and included in the .zip file) contains a list of all files in the Template. It also contains some properties of each file including a boolean which tells the Template installer to substitute certain strings when the Template is used. e.g. (open tag) ProjectItem ReplaceParameters="true" TargetFileName="app.aspx">app.aspx e.g. The original name of your project was "Server". You generate a Project Template from Server. The Template Generator sets all the file entries in the template file to have substitution on. The Template Generator sets replaces all entries of the word "Server" in all of those file with the placeholder $safeprojectname$. You create a new Project using the Template and name it "MyNewProject". When you create a Project with the Template the placeholder $safeprojectname$ is replaced with the name of your project. in the asp.aspx.cs file using MyNameSpace.Server; is replaced with MyNameSpace.MyNewProject; Original app.aspx.cs in Project named "Server". using System.Collections.Generic; using MyNameSpace.Runtime; using MyNameSpace.Server; The app.aspx.cs generated and add to the Template"Server". using System.Collections.Generic; using MyNameSpace.Runtime; using MyNameSpace.$safeprojectname$; The new app.aspx.cs in new Project named "MyNewProject". using System.Collections.Generic; using MyNameSpace.Runtime; using MyNameSpace.MyNewProject; http://msdn2.microsoft.com/en-us/library/eehb4faa(VS.80).aspx Other properties which can be substituted are: msdn Template Parameters. msdn (Modifying or Creating a Visual Studio Project Template). Addin Creation Addin Creation To create custom controls, menu items etc use the Addin. There is an Addin Project in VS, create a new Addin Project from scratch and compile. To register the new addin choose Tools->Addin Manager, select the new Addin from the list and restart VS, the Addin Tools items now appear in the menu. The Addin’s .addin file has been copied to C:\Documents and Settings\ http://msdn2.microsoft.com/en-us/library/80493a3w.aspx microsoft In order to add a custom item to the New Item command we have to create another template and Item Template. I have developed this in particular for the Entity creation. It is virtually identical in makeup to the Project Template but with a different form for data entry. The installation process is the same as the Project Template earlier except this time you create an Item Template instead of a Project Template when using File->Export Template. Summary When you create a Template the template .zip file gets copied into 3 locations where VS will detect them The original location: the Visual Studio Project Template location (This is an optional step in the Export Template Wizard): and also a cache location C:\Documents and Settings\me\Application Data\Microsoft\VisualStudio\8.0\ProjectTemplatesCache The Cache location may cause confusion, if you no longer want to use the Template and decide to delete it you must also delete from the Cache location. Problem here when referencing the DLLs, because we have made the Templates “Strongly Named” we cannot reference the “Weakly Named” assemblies. Solution?? Currently there are 3 projects. WizIDE which creates a Project Template, a dll, WizIDE.dll which is added to the Project Template as the Zip file WizIDETemplate.zip. IDEAddin, creates a DLL, IDEAddin.dll, this is is installed through the VS Addin Manager, it is added as an .addin file to the required, presently it adds a new item to the Project menu which does nothing right now. EventItemTemplate, creates a DLL, EventItemTemplate.dll, this is installed in the same manner as the Project Template at C:\Documents and Settings\ C:\Documents and Settings\ This provides the functionality to create new Entity through a wizard, New Item dialog now contains another item type, EntityItemTemplate which launches a wizard and prompts for Entity parameters.
| Click to read this topic 10/24/2006 11:25:42 - AVAEON-nfallon Tips for editing this Wiki. 02/06/2006 10:43:28 - AVAEON-bkelly | ||
Installing the Project and Item Templates If you create your Project and Item Templates using MSBuild from your source projects using ZipProject and ZipItem then the templates will be installed for you by MSBuild into the correct location (note a copy of these files can be found in your source projects obj/Debug or whatever directory). But of course you'll want to install these templates on a clients machine using an installer of something equivelant., the solution is to copy the files into the Visual Studio installation from your installer using a Custom Action (the custom action is a project you create, it inherits installer and can be called from the installer). You'll also need to reset the Visual Studio Cache, this can be done from the Custom Task code too System.Diagnostics.ProcessStartInfo ps = new System.Diagnostics.ProcessStartInfo(Path.Combine(vsDevenv, "devenv.exe")); ps.Arguments = @"/setup"; System.Diagnostics.Process.Start(ps).WaitForExit(10000);//give the process a max of 10sec to finish | |||
October 24, 2006
dotNET Custom Controls and Resource Scripts
The Custom Controls can then added to your website by adding the assembly containing the Custom Control or used through Visual Studio ASPX editor by created adding the Custom Control to the ToolBox (this is done by Right-Clicking the ToolBox and selecting Choose Items... then select the locate of the Assembly containing the Custom Control.
An example of a very simple Custom Control implementation can be found here
Simple Custom Control (msdn)
Additionally you can embed some of you JScript code in a .js file instead of in the aspx page itself. This js file should be added to you Custom Control project as an Embedded Resource (This means the JScript code is embedded in the resultant assembly. When this assembly is referenced by a website project it will be able to access the JScript functionality (??).
To add the js file add it to the Custom Control Project as a Resource.
Add a new Resource file to the Project.
Add you js file to the project. Set the Property of the js file, "Build Action" to "Embedded Resource".
Add the js file to the Resource file using the Resource Designer, Choose Add Existing Item.. from the menu at the top of the Resource Designer.
Add a Line to the AssemblyInfo.cs file,
first include:
using System.Web.UI;
to the top of the file
[assembly: WebResource("WebControlLibrary1.Resources.JScript1.js", "text/javascript", PerformSubstitution = true)]
Now to enable accessing the JScript contents from and ASP.NET file you must Register it with your Custom Control, do this in the OnPreRender Overload method using
protected override void OnPreRender(EventArgs e)
{
Page.ClientScript.RegisterClientScriptInclude(this.GetType(), "jscriptcontrol", Page.ClientScript.GetWebResourceUrl(this.GetType(), "WebControlLibrary1.Resources.JScript1.js"));
base.OnPreRender(e);
}
Now rebuild. Your JScript file is now embedded in the assembly and can be accessed from ASP.NET using when you add the Custom Control to the aspx page using
(open bracket)
cc1:WebCustomControl1 ID="WebCustomControl1_1" runat="server"
(close bracket)
Adding JScript files and other resources to an assembly ans accessing from ASP.NET
Adding and Accessing Embedded Resources
Adding Client Scripts to ASP.NET
http://msdn.microsoft.com/msdnmag/issues/06/05/CuttingEdge/default.aspx#S6
To test your embbedded resource is actually correct:
Drag and drop the Custom Control which references the JScript into a new aspx page.
Publish the website with the new aspx page.
Navigate to it's url in a browser, when it opens View-Source.
In the source locate the script src tag.
(start script tag)
src="/UseGalaxyWebControl/WebResource.axd?d=WS6VQ6ceILK4cpcSIBDTOA0gOitJm62vmYH2X7E8EUOB2p55z1WYfEnbnNTg7cQs-HZKaxcnh4sTcOoa0Og_HVTyCk7NIZnZpc-1MUQd7bw1&t=632974654934687088" type="text/javascript">(end script tag)
Copy the link you find in here. Copy the link back into the browser.
Hit return and you should see the contents of the JScript file you embedded in the assembly.
/UseGalaxyWebControl/WebResource.axd?d=WS6VQ6ceILK4cpcSIBDTOA0gOitJm62vmYH2X7E8EUOB2p55z1WYfEnbnNTg7cQs-HZKaxcnh4sTcOoa0Og_HVTyCk7NIZnZpc-1MUQd7bw1&t=632974654934687088This works because the ASP.NET 2.0 has an assembly reflection type service WebResource.xad. This uses the url passed into the service to extract it from the assembly.
http://localhost//UseGalaxyWebControl/WebResource.axd?d=WS6VQ6ceILK4cpcSIBDTOA0gOitJm62vmYH2X7E8EUOB2p55z1WYfEnbnNTg7cQs-HZKaxcnh4sTcOoa0Og_HVTyCk7NIZnZpc-1MUQd7bw1&t=632974654934687088
For more see this description
Using the WebResource.axd Handler with Embedded ASP.NET Resources (eggheadcafe.com)
So now you can drag and drop the Custom Control onto your ASPX page in Visual Studio designer. What good is that?
Your aspx page now has access to all your JScript functionality. You can call your JScript functions in the aspx page as normal using the tag.
Resource Caching:
So you've embedded the resource and added it to your aspx.
ASP.NET 2.0 also provides some caching functionality by default.
If the application is in Release (the web.config debug="false" set) all the resources are cached, meaning they will not be reloaded (GET) each time the page is POSTED as long as the resources have not changed.
In debug mode the resources are not cached, so they are always reloaded on page POST.
You'll see in the url for the resource, it contains a t=... this t is the time. This is what's used to compare the cached version of the resource and the latest version on the server.
October 23, 2006
October 19, 2006
dotNET Custom Configuration Handlers
The place for your data is in the configuration file, app.config or web.config.
You could put it in the AppSettings, a small problem with this is it uses Key/Value collections which means that the Keys must be unique, that's not the end of the world but you may want to access a complete section in the config file and sort them or something.
The alternative is to create your own section in the config file. To access this you'll need to create your own Handler Class.
I'll explain the settings in the config file followed by the Handler Class and then how to access it.
Custom Configuration Handlers on ASP.NET Forum
September 26, 2006
dotNET Web Application Stress Testing
There are some Microsoft Tools available, see below. These tools allow you to create scenarios on your website, increase number of users and view the results.
I'll report back with more information when I actually use these tools. For now this is just a placeholder.
Microsoft Stress Test Tool, ACT, Application Center Test. Available from your Visual Studio Enterprise Edition DVD.
Older Stress Test Tool, downloadable from msdn.
http://www.microsoft.com/downloads/details.aspx?
FamilyID=E2C0585A-062A-439E-A67D-75A89AA36495&displaylang=en">
Other useful resources could be Windows Performance Counters. These are Windows information that is available on Processes and Threads running.
You can provide your own applications Performance Counter information be incrementing the Performance counter containers.
The information is Viewable on the Perfmon.exe available on Windows machines.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconruntimeprofiling.asp
September 21, 2006
dotNET AppDomain and Remote Computing (Remoting)
AppDomain refers to the namespace, space allocated in .NET memory/CLR in which your application is allowed to run.
It's a security thing which prevents applications from cross communicating with each other.
Reasons why you'd want to access the AppDomain can include:
Need to manage the AppDomain i.e. delete or add resources from outside the applications namespace. If you wanted to ensure that certain assemblies for example were to be unloaded from another AppDomain at a certain point, you'd have to setup a new AppDomain and somehow grant it access to the other AppDomain.
AppDomain's have certain properties which are vital to setting them up. A major problem I encountered was forcing an Application to include Assemblies in different path from the one in which it was loaded. This had to be forced by the AppDomain.applicationBase, this was all done with a helper class AppSetup, which allows you to set various properties of an AppDomain before the AppDomain's creation. You must set these properties before creating the AppDomain.
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
AppDomainSetup appSetup = new AppDomainSetup();
appSetup.ApplicationBase = "file:///C:\\project\\export\\bin;
appSetup.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory+";C:\\Program Files\\Microsoft Visual Studio 8\\Common7\\IDE\\" ;
AppDomain ad = AppDomain.CreateDomain("Compiling", null, appSetup);
At this stage you have a new AppDomain which will first search the ApplicationBase for Assemblies. The ResolveEventHandler that I register is the actual method that is used to locate assemblies so in you EventHandler you should add code to locate Assemblies in certain paths
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
//Look for assemblies in the current VS project location
string projectDir = Path.GetDirectoryName(BuildTask._presentProjectLocation);
string shortAssemblyName = args.Name.Substring(0, args.Name.IndexOf(','));
string fileName = Path.Combine(projectDir, shortAssemblyName + ".dll");
if (File.Exists(fileName))
{
Assembly result = Assembly.LoadFrom(fileName);
return result;
}
fileName = Path.Combine(projectDir+"\\bin\\"+_mode+"\\", shortAssemblyName + ".dll");
if (File.Exists(fileName))
{
Assembly result = Assembly.LoadFrom(fileName);
return result;
}
//Try the Galaxy export directory
fileName = Path.Combine("C:\\project\\export\\bin\\" + _mode + "\\Tools\\", shortAssemblyName + ".dll");
if (File.Exists(fileName))
{
Assembly result = Assembly.LoadFrom(fileName);
return result;
}
fileName = Path.Combine("C:\\Program Files\\Microsoft Visual Studio 8\\Common7\\IDE\\", shortAssemblyName + ".dll");
if (File.Exists(fileName))
{
Assembly result = Assembly.LoadFrom(fileName);
return result;
}
else
return Assembly.GetExecutingAssembly().FullName == args.Name ? Assembly.GetExecutingAssembly() : null;
}
Calling a remote method from a Visual Studio MSBuild BuildTask
It can be tricky to get this right. We came across this when trying to add a BuildTask.
The problem we had was that we were compiling files with our own compiler and we needed to load and unload files at certain times, the way to implement this was through an AppDomain. This temporary AppDomain was required because the output fiiles are first compiled into a dll in the current AppDomain and it cannot be released until the processing of the results are finished. We wanted to access the info in the dll before it was released and so we created a temporary new AppDomain .
Because of this we had to introduce Remote Computing, this is required for one AppDomain to communicate with another. The implementation takes 2 parts, first the class that will be used must inherit from MarshalByRefObject, in our case the Configuration.Compiler class.
To create a remote instance use one of the AppDomain. CreateInstance.... methods. Depending on the location of the assembly containing the MarshalByRefObject class. In our case the Compiler class was in the Configuration.Compiler.dll, which was signed. This meant we could instantiate the object using
Configuration.CompileProxy.Compiler compiler = (Configuration.CompileProxy.Compiler) ad.CreateInstanceAndUnwrap ("Configuration.CompileProxy, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1f77dad97b7dc0c2", "Configuration.CompileProxy.Compiler");
But there were problems. This worked fine when the assembly was located in the GAC or in the Visual Studio installation (along with all of it's dependancies),
C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies.
I still haven't found a way to instantiate this with the assembly outside of these locations.
- Common problems were:
- Casting the resultant Object from CreateInstanceFromAndUnWrap.. to Type Configuration.CompileProxy.Compiler. Still haven't the resoltion for this, it only happens when I use CreateInstanceFromAndUnwrap or CreateInstanceFrom and then use UnWrap().
- When CreateInstanceAndUnWrap() is used it creates the Object without issue only if the Configuration.CompileProxy.dll is in the GAC or in the VS installation. I have no soluution for this either.
- There is an Event which can be handled to resolve the paths to assemblies,
- ad.AssemblyResolve += new ResolveEventHandler(BuildTask.CurrentDomain_AssemblyResolve);
- I tried to use this but found that it broke the initial AppDomain too.
I found the solution. The problem is that it is the original AppDomain that is used to resolve Assemblies. The problem was with the UnWrap(), what appears to happen is the UnWrap() tries to match the assembly name just loaded through CreateInstance or CreateInstanceFrom with it's full name, something like
"Configuration, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null".
If it can't match it bails out. The solution is to add an EventHandler for the AssemblyResolve Event match the full name with the actual name of an assembly and it's path (I had tried this earlier but it caused an infinite loop, another call I had made must have been causing this!!). The EventHandler is subscribed to the Event of the original AppDomain . Also you should set yourAppDomain's ApplicationBase to the location of your assemblies, and then use CreateInstanceAndUndWrap().
Once you have the object instance you can invoke your method using the ordinary call.
bool result = compiler.Compile(confFileStream, confFileName, out messages);
Also note that there is a tool available in the .NET Frameword for debugging assembly binding and resolving, it's called the Fusion Logger, Fuslogvw.exe, it allows you to view what the CLR is doing at runtime with the assemblies.
To run fuslogvw.exe you must install the .NET SDK and then run it from here
C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0>Fuslogvw.exe
Fuselogvs.exe CodeProject Tool to debug AppDomain Assemblies
AppDomain Class (msdn)
Suzanne Cooks .NET CLR Notes - Executing Code in Another AppDomain
Suzanne Cooks .NET CLR Notes - Choosing a Binding Context
AppDomain.CreateInstanceAndUnwrap(..) vs AppDomain.CreateInstanceFrom
Why does AppDomain.CreateInstanceAndUnwrap(..) work and AppDomain.CreateInstanceFrom(...).UnWrap doesn't?
Yariv Hammers - Calling Remote Objects - Basic Concepts.
Yariv Hammers - Create a Simple Client/Server App with Remoting..
I've mentioned in the AppDomain about marshalling and inheriting your class from MarshalByRefObject, now I'll describe what this actually means and does.
Marshaling
This is the related to the Serializing and De-Serializing of objects, this term is particular to Remote Computing. As I've described in another post Serializing of objects is creating a stream version of your objects data.
In Remote computing objects can be passed from one application to another by serializing the object on the sender side and de-serializing on the receiver side, Marshaling and Demarshaling.
In .NET this is achieved by making your class inherit from MarshalByRefObject. Now when you pass objects from your class to another AppDomain they will be marshalled before passing. The other AppDomain will then Demarshal the parameters when received.
September 20, 2006
dotNET Serialization (XmlSerializer)
This is a handy way to save out your Object structure to a file.
The XML version is XmlSerializer, this creates an XML file structure of your Object. It will parse out all the public properties and save to XML.
You can make your objects Serializable by simply adding the Serializable Attribute to each of your objects, when you call Serialize on that Object it and all of it's Child and Associated Objects which also have the Serializable Atribute will also be Serialized
[Serializable]To invoke Serialization on an Object you need a Formatter and a Stream. Or you can use XmlSerializer to perform the Serialization.
public class BaseballPlayer
{
[...]
}
see XMLSerializer and not expected type... (codeproject)
Stream str = File.OpenWrite(filename);The BinaryFormatter's output is binary, so the output is illegable and compacted, to create a readable format use the SoapFormatter. There is a problem with the SoapFormatter however, it cannot handle non generic types, such as your own types of containers, it can only handle .NET types including Lists. So if your Objects contain your own types of containers you must make them serializable, How?
System.Runtime.Serialization.Formatters.Soap.BinaryFormatter formatter =
new System.Runtime.Serialization.Formatters.Soap.BinaryFormatter();
formatter.Serialize(str, bp);
str.Close();
At runtime is XmlSerialization is requested by some process ( a webservice for example requires Objects to be Serialized, SOAP), then the Object (maybe the Type inside the assembly) will be XmlSerialized at runtime, resulting in a temporary file named
To create an XmlSerializer version of your file use sgen.exe, it's a Visual Studio Tool.
C:\bin\debug\Tools>"C:\Program Files\Microsoft Visual Studi
o 8\SDK\v2.0\Bin\sgen.exe" /a:Configuration.CompileProxy.dll /o:C:\server\bin
Microsoft (R) Xml Serialization support utility
[Microsoft (R) .NET Framework, Version 2.0.50727.42]
Copyright (C) Microsoft Corporation. All rights reserved.
Serialization Assembly Name: Configuration.CompileProxy.XmlSerializers, V
ersion=1.0.0.0, Culture=neutral, PublicKeyToken=null.
Generated serialization assembly for assembly C:\bin\debug\
Tools\Configuration.CompileProxy.dll --> 'C:\server\bin\Configuration.CompileProxy.XmlSerializers.dll'.
XMLSerializer (msdn)
Serialization in .NET (codeguru).
September 18, 2006
MSBuild
MSBuild Team Blog.
MSBuild documentation (msdn)
BuildTask creationMSBuild used by VS projects to build items. You can add build tasks to your Project file, either by directly adding to the .proj file or by creating .targets files with the task within and then adding this .targets file to your project file using the include option. The BuildTask can do anything you wish but the format which you call it is basically the same.
To use a build task you must include the file that contains the BuildTask functionality. The BuildTask itself is just a class that extends Microsoft.Build.Utilities.Task.
Compile it into a dll and include the dll in your project file with the UsingTask . The AssemblyFile points to the location and name of the task's assembly, AssemblyFileName can also be used but only requires the file name, this means the file must be in the GAC or in the installation of .NET.
<pre> <usingtask taskname="SetupUtils.RegistryTask" assemblyfile="..\Setup Util\bin\Debug\Setup Util.dll">
<target name="EnvSetup"></target></usingtask>
</></a><usingtask taskname="SetupUtils.RegistryTask" assemblyfile="..\Setup Util\bin\Debug\Setup Util.dll"><target name="EnvSetup"><registrytask variable="RegistrySetting" registrykey="Software\\Solutions Ltd\\Installation">
<output taskparameter="RegistryValue" propertyname="RegistryKeyValue">
</output>

Some other useful formats of the BuildTask are,
The above example sets the RegistryKeyValue variable to the Property RegistryValue found in the BuildTask itself, see below, the function with the Output property indicates to MSBuild that this is an output. So the Output method using the TaskParam shown above and provide and PropertyName to hold the result.it can take parameters and also return strings </a><usingtask taskname="SetupUtils.RegistryTask" assemblyfile="..\Setup Util\bin\Debug\Setup Util.dll"><target name="EnvSetup"><registrytask variable="RegistrySetting" registrykey="Software\\Solutions Ltd\\Installation">
<output taskparameter="RegistryValue" propertyname="RegistryKeyValue">
</output>
which can then be used to set other variables in the project file.<message text="The result is : $(RegistryKeyValue)">
</message>
</registrytask></target></usingtask></pre>
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.Win32;
namespace SetupUtils
{
///
/// Get or Set the Registry Entries
///
public class RegistryTask : Task
{
private string _variable;//not significant
private string _registryKey;//the Registry Key ,
e.g. "Software\Solutions Ltd\Installation"
private string _registryKeyValue;//the value of the _registryKey.
[Required]
public string Variable
{
get { return _variable; }
set { _variable = value; }
}
[Required]
public string RegistryKey
{
get { return _registryKey; }
set { _registryKey = value; }
}
[Output]
public string RegistryValue
{
get { return _registryKeyValue; }
set { _registryKeyValue = value; }
}
public override bool Execute()
{
if (_registryKeyValue != null)
{
RegistryKey key = Registry.CurrentUser.OpenSubKey(_registryKey);
if(key != null)
key.SetValue("Default",
_registryKeyValue, RegistryValueKind.String);
else
Log.LogMessage(MessageImportance.Normal,
"No key by that name : " + _registryKey);
}
else
{
RegistryKey key = Registry.CurrentUser.OpenSubKey(_registryKey);
if (key.GetValue("Default") != null)
{
_registryKeyValue = (string)key.GetValue("Default");
Log.LogMessage(MessageImportance.Normal,
"the variable value is : "
+ _registryKeyValue);
}
}
return true;
}
}
}
There is a also a third party tool which I have found very useful for Zipping files into a single zip file, the MSBuild
Zip utility is not usefult enough. It can be downloaded from an opensourse project MSBuildTasks. The download
provides the dll with the functionality, a targets file to import into you project file to point to the dll and a
help file. In my case I used
<target name="All">
<calltarget targets="Build">
<calltarget targets="Zip">
</calltarget>
</calltarget>
</target>
<itemgroup>
<zipfiles include="Templates\MyTemplate.vstemplate">
<itemgroup>
<target name="Zip">
<zip workingdirectory="Templates" files="@(ZipFiles)" zipfilename="Templates\MyDevenv.zip"><copy sourcefiles="@(ZipToCopy)" destinationfolder="">
<output taskparameter="CopiedFiles" itemname="SuccessfullyCopiedFiles">
</output>
<itemgroup>
<zipfiles include="Templates\MyTemplate.vstemplate">
<itemgroup>
<target name="Zip">
<zip workingdirectory="Templates" files="@(ZipFiles)" zipfilename="Templates\MyDevenv.zip">
In order to direct the output messages of the BuildTask to the correct tab in the output console of
Visual Studio you need to follow a particular string format, Errors get filtered to the Error List in the Console window.
MSBuild / Visual Studio aware error messages and message formats
Delegates
Delegates are similar to C++'s function pointers, as it says on the tin a pointer to a function.
The beauty of delegates and function pointers is that you can throw it around in different classes without that class having knowledge of the object to which the function belongs.
Here's a simple example:
declare your delegate in some class.
public delegate void DelegateFunc();// you must have a func with the same signature
now create a function in another class with same return type and same parameters.
class DelegateClass
{
public static void TheRealDelegateFunc()
{
}
}
now call the delegate from the first class
public delegate void DelegateFunc();// you must have a func with the same signature
static void Main(string[] args)
{
DelegateFunc df = new DelegateFunc(DelegateClass.TheRealDelegateFunc);
df.Invoke();
}
Notice the Invoke() function, this is required to run the function.
http://en.csharp-online.net/index.php?title=CSharp_Delegates_and_Events








