Fixing 'mscorlib.dll' targets a different processor When Building x86

Currently I’m working on a product that has been around for a long time, and will be for a lot longer.  Even thought 64bit has been out for a long time, this application has it roots in 32bit.  This is not necessarily a bad thing, as 64bit will not provide any real processing performance enhancements.  Prior to Visual Studio 2010, building with AnyCPU platform configuration was straight forward.  Now that we support purely 64bit OS on our server side, things because a little more difficult.  Long story short, we needed to configure everything to specify x86.  In doing so, we continued to get build errors on our TFS team build server.  The error “error CS1607: Assembly generation -- Referenced assembly 'mscorlib.dll' targets a different processor” would show up all over the place.  After ensuring that all our projects were set to x86 and the Configuration Manager for debug/release specified x86, we eventually found that the build process MSBuild Platform needed to be set to X86.

As an interesting note, in our researching over x86 and x64, we came across this article AnyCPU Exes are usually more trouble than they're worth.  If you’ve been adding new projects in Visual Studio 2010, you’ll notice that they all default to x86. 

Office Communicator Is Cool Check This Out

So being a somewhat Microsoft shop, we use Office Communicator which is a pretty dandy little piece of software for inter company screen sharing and instant messaging.  The other day a colleague had a theory that we could create an infinite loop with Office Communicator by doing  a three way share.  Well it worked and was quite humorous, here is our result.

image

Alter Visual Studio Template Files

Out of the box, I’ve found Visual Studio template files to be just fine.  Recently I was doing some researching into using StyleCop so I installed in on my machine.  Little did I know, it went behind the scenes and altered some if now all template files.  In particular, I don’t care for their Interface and Class file templates, I tend to use ReSharper and GhostDoc to do cleanup and documentation.  If you want to change or revert your templates, your in luck, Visual Studio has backups.  Follow the steps below to restore whatever templates you need to.  In the example I will be restoring the C# class file for Visual Studio 2010 (10.0).

1.  Go to the template folder you desire.  You will find the templates in the Program Files folder (Program Files (x86) on 64 bit system) and the particular version  here C:\Program Files\Microsoft Visual Studio {Version of VS, 10.0, 9.0…}\Common7\IDE\ItemTemplates.  In the case of the C# class you will have to drill down into \CSharp\Code\1033\ folder, and in there you will find a file called Class.zip.bak.  Delete the existing Class.zip file, copy the Class.zip.bak and rename it to Class.zip.  Do this process for any other templates you wish to revert.

2.  Reset the IDE environment.  Open a Visual Studio command prompt (I do it in administrator mode on Windows 7).  It’s found in All Programs^Microsoft Visual Studio {Version 2010, 2008,…}^Visual Studio Tools.  Once the command windows is open run “devenv /setup” and you’re all set!

Things To Improve Visual Studio Development

Visual Studio has come a long way since I started working with the first version of it.  Currently I’m on Visual Studio 2010.  Below lists some additions you can do to make you development experience better.

ReSharper : productivity tool that all developers should use.  Promotes good coding practices and makes refactoring a breeze!

Extensions

Extensions can be added by going to Tools^Extension Manager… and searching the online gallery.

Team Foundation Server Custom Controls

So we’ve installed TFS 2010, and now the company is starting to pick up the pace with all types of users performing different roles.  So far, most of our modifications to the process template have been cosmetic, like moving tabs around, or adding fields.  A group of users came to us the other day, asking why the history tab has comments interwoven with the historical changes.  At that point all I could say is “Whatever you say in the past is history man.”  No, I didn’t say that, but it did get me thinking why there isn't a clear way to view the comments in chronological order. 

In comes the Custom Control.  A custom control is a way to extend the process template in a way you see fit.  I definitely wouldn’t jump to using this as my first choice but in this case it fits.  Our end result would be another tab next to the history tab called “Comment History”, and in the tab would be a readonly window displaying only the comments from the work items history.  Before I start, I would like to list all the sites I used in researching this, as I didn’t just pull this out of a black hat.

One of the key items which I will discuss which isn’t found in these threads is a way keep you work item layout xml to a minimum.

NOTE: You should do all this work on the TFS server as it will make referencing certain DLLs much easier.

1.  Open Visual Studio 2010, create 2 new class library projects, one called WitCustomControls, and one called WitCustomControls.WebAccess.  Set both projects to .NET 3.5.  The reason why we have 2 libraries will become clear in a bit.

2.  In the WitCustomControls library, add a windows form User Control called CommentHistoryControl.  In the the WitCustomControls.WebAccess create a class called HistoryCommentControl.  For each project, create a file called HistoryCommentControl.wicc (this is a work item custom control file), and a class called WorkItemHistoryComment.  Right click the .wicc file and go to Properties; set the Build Action to Content (do this in both projects).  (NOTE: I know I’m doing a little duplicate coding here, but for the concept of the example I’m keeping it simple).  Remove any default class files like Class.cs.

image

3.  Let’s fill in the .wicc files real quick.  When the work item UI is loaded, whether it be in Visual Studio (winform) or team web access, the control that is referenced in the work item layout xml is searched for by that name, in our case HistoryCommentControl. The file defines the library and class to load to render the control.  Do the following:

  • In the WitCustomControls project .wicc file add:
<?xml version="1.0" encoding="utf-8" ?>
<CustomControl xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Assembly>WitCustomControls.dll</Assembly>
<FullClassName>WitCustomControls.HistoryCommentControl</FullClassName>
</CustomControl>

  • In the WitCustomControls.WebAccess project .wicc file add:
<?xml version="1.0" encoding="utf-8" ?>
<CustomControl xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Assembly>WitCustomControls.WebAccess.dll</Assembly>
<FullClassName>WitCustomControls.WebAccess.HistoryCommentControl</FullClassName>
</CustomControl>

4.  Now the references we will need to inherit and implement the IWorkItemControl interface.  This is a little tricky, part of the DLLs are from Visual Studio install, and the other part come from TFS install. 

For both projects you will need:

  1. C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\ReferenceAssemblies\v2.0\Microsoft.TeamFoundation.WorkItemTracking.Client.dll
  2. C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Microsoft.TeamFoundation.WorkItemTracking.Controls.dll

For the WitCustomControls.WebAccess project you will need these additional ones:

  1. C:\Windows\assembly\GAC_MSIL\Microsoft.TeamFoundation.WebAccess.Controls\10.0.0.0__b03f5f7f11d50a3a\Microsoft.TeamFoundation.WebAccess.Controls.dll
  2. C:\Windows\assembly\GAC_MSIL\Microsoft.TeamFoundation.WebAccess.WorkItemTracking\10.0.0.0__b03f5f7f11d50a3a\Microsoft.TeamFoundation.WebAccess.WorkItemTracking.dll

5.  I created a helper class to group the comment history data.  In the WorkItemHistoryComment file for both projects, add the following:

public class WorkItemHistoryComment
{
public DateTime ChangedDate;
public string ChangedBy;
public string Comment;
}

6.  The real work happens because these controls implement an interface called IWorkItemControl.  Below shows the code needed to create the content for the controls display.  In a nutshell, because we implement the interface, we get a reference to the WorkItem.  From that we can iterate over the revisions of the work item and look at all the fields that were modified.  The comment that is associated with a work item is in the WorkItem.History property.  For the winform control I used a WebBrowser control docked in the control.  The core of what we were trying to accomplish is in the method ExtractCommentsFromWorkItem(…) and AppendHtmlCommentSection(…).

WitCustomControls user control code:

public partial class HistoryCommentControl : UserControl, IWorkItemControl
{
private IServiceProvider _serviceProvider;

public HistoryCommentControl()
{
InitializeComponent();
}

#region IWorkItemControl Members

public event EventHandler BeforeUpdateDatasource;

public event EventHandler AfterUpdateDatasource;

public void Clear() { }

public void FlushToDatasource()
{
// do nothing because this is a readonly control
}

public void InvalidateDatasource()
{
if (IsInitialized())
{
// fill in the text box
IEnumerable<WorkItemHistoryComment> comments = ExtractCommentsFromWorkItem(WorkItemDatasource as WorkItem);

if (comments.Count() > 0)
{
StringBuilder sb = new StringBuilder("<html><head>");

sb.Append("<style rel=\"stylesheet\" type=\"text/css\">body {font-family: Tahoma,Geneva,Arial,Helvetica,Sans-serif; font-size:0.8em; } .comment-title { font-weight:bold; background-color:#EEEEEE; } .comment { font-size:0.8em; }</style>");
sb.Append("</head><body>");

AppendHtmlCommentSection(comments, sb);

sb.Append("</body></html>");

// set web browser box
webBrowserCommentHistory.DocumentText = sb.ToString();
}
}
}

public System.Collections.Specialized.StringDictionary Properties { get; set; }

public bool ReadOnly
{
get { return true; }
set { }
}

public void SetSite(IServiceProvider serviceProvider)

{
this._serviceProvider = serviceProvider;
}

public object WorkItemDatasource { get; set; }

public string WorkItemFieldName { get; set; }

#endregion

private bool IsInitialized()
{
// this control doesn't need a field name becasue it's readonly
return (!IsDisposed
&& WorkItemDatasource != null);
}

private IEnumerable<WorkItemHistoryComment> ExtractCommentsFromWorkItem(WorkItem wi)
{
List<WorkItemHistoryComment> comments = new List<WorkItemHistoryComment>();

foreach (Revision rev in wi.Revisions)
{
foreach (Field f in rev.Fields)
{
// determine if this revision has a the history field changed
// if so and not empty show it
if (0 == string.Compare(f.Name, "History", true)
&& !string.IsNullOrEmpty(f.Value as string))
{
comments.Add(new WorkItemHistoryComment
{
ChangedBy = (string)rev.Fields["Changed By"].Value,
ChangedDate = (DateTime)rev.Fields["Changed Date"].Value,
Comment = (string)f.Value
});
}
}
}

return comments.OrderByDescending(c => c.ChangedDate);
}

private void AppendHtmlCommentSection(IEnumerable<WorkItemHistoryComment> comments, StringBuilder sb)
{
// help verbage
sb.Append("<div>To add a comment, use the History tab where the comment input is located.</div><br/<br/>");

// start container for comments
sb.Append("<div><div><table style=\"width:100%;table-layout:fixed;\" cellspacing=\"1\" cellpadding=\"0\"><tbody>");

foreach (WorkItemHistoryComment comment in comments)
{
// row that indicates comment header
sb.Append("<tr><td> <table style=\"width:100%;font-size:0.8em;\" cellspacing=\"0\" cellpadding=\"1\"> <tbody> <tr>");

// from TFS history header row onclick=\"_ctl00_c_we_ctl48_wc.onExpandCollapseChangeTitle(this);\"
// wish we could pigback on this collapsing behavior
sb.Append("<td width=\"16\" align=\"center\"><img border=\"0\" align=\"absMiddle\" style=\"width:18px;height:17px;cursor:pointer\" src=\"https://tfs.pcbancorp.com:8443/tfs/web/Resources/Images/minus.gif\"></td>");
sb.AppendFormat("<td class=\"comment-title\">{0} [{1}]</td>", comment.ChangedBy, comment.ChangedDate);
sb.Append("</tr> </tbdody> </table> </td></tr>");
// row that represents the actual comment
sb.AppendFormat("<tr class=\"comment\"><td><div>{0}</div></td></tr>",
comment.Comment);
}

// finish container for comments
sb.Append("</tbody></table></div></div>");
}
}

WitCustomControls.WebAccess user control code:

public class HistoryCommentControl : BaseWorkItemWebControl

public HistoryCommentControl()

: base(HtmlTextWriterTag.Div) { }

public System.Web.UI.HtmlControls.HtmlGenericControl HtmlControl { get; protected set; }

public override void InitializeControl()
{
base.InitializeControl();

// Create control
EnsureInnerControls();
}

public void EnsureInnerControls()
{
if (HtmlControl != null)
return;

HtmlControl = new System.Web.UI.HtmlControls.HtmlGenericControl("div");

base.Controls.Clear();
base.Controls.Add(HtmlControl);
}

public override void InvalidateDatasource()
{
base.InvalidateDatasource();

EnsureInnerControls();

// set control value
IEnumerable<WorkItemHistoryComment> comments =
ExtractCommentsFromWorkItem(this.WorkItemDatasource as WorkItem);

if (comments.Count() > 0)
{
StringBuilder sb = new StringBuilder();

// get html with comments embedded
AppendHtmlCommentSection(comments, sb);

// set contents of html control
HtmlControl.InnerHtml = sb.ToString();
}
}

public override void FlushToDatasource()
{
base.FlushToDatasource();

HtmlControl.InnerHtml = string.Empty;
}

private IEnumerable<WorkItemHistoryComment> ExtractCommentsFromWorkItem(WorkItem wi)
{
List<WorkItemHistoryComment> comments = new List<WorkItemHistoryComment>();

foreach (Revision rev in wi.Revisions)
{
foreach (Field f in rev.Fields)
{
// determine if this revision has a the history field changed
// if so and not empty show it
if (0 == string.Compare(f.Name, "History", true)
&& !string.IsNullOrEmpty(f.Value as string))
{
comments.Add(new WorkItemHistoryComment
{
ChangedBy = (string)rev.Fields["Changed By"].Value,
ChangedDate = (DateTime)rev.Fields["Changed Date"].Value,
Comment = (string)f.Value
});
}
}
}

return comments.OrderByDescending(c => c.ChangedDate);
}

private void AppendHtmlCommentSection(IEnumerable<WorkItemHistoryComment> comments, StringBuilder sb)
{
// help verbage
sb.Append("<div>To add a comment, use the History tab where the comment input is located.</div><br/<br/>");



// start container for comments

sb.Append("<div class=\"tswa-compositectrl\"><div class=\"tswa-historyctrl-changeshost\"><table style=\"width:100%;table-layout:fixed;\" cellspacing=\"1\" cellpadding=\"0\"><tbody>");



foreach (WorkItemHistoryComment comment in comments)

{

// row that indicates comment header

sb.Append("<tr><td> <table width=\"100%\" cellspacing=\"0\" cellpadding=\"1\"> <tbody> <tr class=\"tswa-historyctrl-revhead\">");

// from TFS history header row onclick=\"_ctl00_c_we_ctl48_wc.onExpandCollapseChangeTitle(this);\"

// wish we could pigback on this collapsing behavior

sb.Append("<td width=\"16\" align=\"center\"><img border=\"0\" align=\"absMiddle\" style=\"width:18px;height:17px;cursor:pointer\" src=\"https://tfs.pcbancorp.com:8443/tfs/web/Resources/Images/minus.gif\"></td>");

sb.AppendFormat("<td>{0} [{1}]</td>", comment.ChangedBy, comment.ChangedDate);

sb.Append("</tr> </tbdody> </table> </td></tr>");

// row that represents the actual comment
sb.AppendFormat("<tr><td><div class=\"tswa-historyctrl-description\">{0}</div></td></tr>",
comment.Comment);
}

// finish container for comments
sb.Append("</tbody></table></div></div>");
}
}

7.  Now that we have our libraries created, we have to package them up so they can be installed in the correct location.  We at the point of understanding why we created 2 libraries.  When Visual Studio opens it loads the control from C:\Documents and Settings\All Users\Application Data\Microsoft\Team Foundation\Work Item Tracking\Custom Controls\10.0 and when TFS web access loads the control it does it from %ProgramFiles%\Microsoft Team Foundation Server 2010\Application Tier\Web Access\Web\App_Data\CustomControls.  Because of this, we will create a setup package that will install our libraries in different locations, the winform control will go in the all user’s app data, and the web control will go in the TFS web application folder. 

Create 2 setup projects (found in Other Project Types^Setup and Deployment^Visual Studio Installer^Setup Project).  One call WitCustomControlsSetup (for the 32-bit version) and one called WitCustomControlsSetup.x64 (for the 64-bit version).  The key difference is that on the 64-bit version, the files need to go in the 64-bit program files directory.  Both will follow the steps below and I will note when the 64-bit version differs.

a.  Right click the setup project and select View^file System.  Create the folder TWAAppDataFolder, the Application Folder will already exist.

b.  Select the Application Folder.  Right click in the output area and add the WitCustomControls libraries project output, repeat this and add it’s Content output as well.

image

c.  Select the TWAAppDataFolder.  Like before, right click in the output area and add the WitCustomControls.WebAccess libraries project output, repeat this and add it’s Content output as well.  The final should be similar to the following.

image

d.  Now right click on the Application Folder and select Properties Window. Set the DefaultLocation value to [CommonAppDataFolder]\All Users\Application Data\Microsoft\Team Foundation\Work Item Tracking\Custom Controls\10.0.  Right click on the TWAAppDataFolder and go to the properties.  Set it’s DefaultLocation to [ProgramFilesFolder]\Microsoft Team Foundation Server 2010\Application Tier\Web Access\Web\App_Data\CustomControls (IMPORTANT: on the 64-bit setup project replace [ProgramFilesFolder] with [ProgramFiles64Folder]).

image

e.  Set the project property InstallAllUsers and RemovePreviousVersion to true.

f.  For the 64-bit version, you must change the setup projects property TargetPlatform to x64.

g.  Remove the TFS DLL dependencies.

image

8.  Install the 64-bit version on your TFS server.  Install the 32/64 –bit on any client machines you want this control to be used in by Visual Studio.

9.  The very last thing we need to do is modify our work item xml file and add our new control to the layout section.  The reason we have 2 libraries and 2 wicc files is so we don’t have to duplicate our layout setup for winform and web access.  If we didn’t we’d have to use the <Layout Target=”Web”> and <Layout Target=”WinForm”>, which is a pain.  Below is the tab I added to our work item layout.  Notice the Type attribute, this is the same name as the wicc file, and there lies the magic of it all!

<Tab Label="Comment History">
<Control Type="HistoryCommentControl" Label="" LabelPosition="Top" Dock="Fill" />
</Tab>

Cropping Images In .NET The Easy Way

I’ve been working on a pet project of mine Site Travelers, and I needed a easy way to crop images that were uploaded to the server (ASP.NET).  Obviously I knew how to do this the manual way, but this is one of those all too common cases of functionality that many people require.  So, using my trusty search engine, I went shopping.  There are quite a few good examples out there on how to do it, including writing helper functions, but I wanted something already packaged.  I hit the jackpot when I came across a library from Code Carvings. They have a really slick image manipulation library called piczard.  Their documentation is fantastic and easy to read with plenty of examples to get you down the road.  Using this library reduced everything I needed to do, crop and get bytes of data, down to the following 2 lines!

FixedResizeConstraint constraint = new FixedResizeConstraint(180, 120);
byte[] bytes = constraint.SaveProcessedImageToByteArray(
    file.InputStream, 
    new JpegFormatEncoderParams(60));

Automate Work Item Modification To Team Projects

I’ve successfully rolled out a full implementation of TFS at the company I work for and it’s been very smooth and rewarding.  The best part was that we spent time with the company and determined their work flows which we then used to created a process template to fit company not the company trying to fit an existing template.  As always, you need to tune it slightly as you start to use it.  As we’ve added more team projects and a few team collections, one thing we quickly realized is that updating team project with modified work item definitions needed to be automated.  Luckily there is most of the leg work was done for us already provided by this blog Deploying Process Template Changes Using TFS 2010 Build which contains the build process template.  Below I provide the steps we went through to achieve the desired results.

NOTE: We stored this automated build in a team project called TFS.  The team project was a source control repository for all of our team project process templates.

1.  Ensure that your TFS build service account (we kept it simple and had an AD account tfsbuild) is part of the Team Foundation Administrators group; this is done at the Application Tier level in the TFS administration console.  See article Managing Global Lists for Work Item Types [witadmin] for the reason why.

2.  Create a new build definition and give it a meaningful name, we named it to be that of the team project collection since all our team projects in most collections used the same process template.

3.  Set the workspace location, this will be the version control folder that contains your process template.  This will be a path like: $/TFS/ProcessTemplates/Test/My Template/Process Template.

image

4.  Define a drop location for you build items.  It will be a UNC path like.

image

5.  On the process tab set the process template to the ProcessTemplateDeploymentTemplate.xaml.  If this process template is not in the list,  you need to use the New button to select this existing template like $/TFS/BuildProcessTemplates/ProcessTemplateDeploymentTemplate.xaml.

image

6.  Fill in the required build process parameters.

  • Team Projects: this is a string list of team project names contained within the collection being updated.
  • TFS Server Collection URL: enter in the TFS url for the team collection being updated.  This will be like: https://tfs.pcbancorp.com:{port}/tfs/{Team Project Collection Name}. 
  • Work Item Type Definition Files:  a list of the work item type definition files which will be updated.  NOTE:  This list must be in the same order as the type name list defined in the step next bullet.
  • Work Item Type Name:  a string list of work item type names.  This must be in the same order as the file list defined in step previous bullet

image

7.  Save the build definition.

8.  Ensure that the [{TeamCollectionName}]\Project Collection Build Service Accounts role has the following permissions:

  • For the team collection the following highlighted permissions.

image

  • For each team project that is to be updated.

image

9.  You can now queue this build and watch your team projects within the given project collection updated with you process template changes!  Man, that beets manual work.

Debugging in Visual Studio and Eclipse, Hotkey Consistency

Most developers become accustom to hotkeys and shortcut key strokes.  I, for one, cannot live without them, but there is one thing that drives me crazy and that’s debugging with different key bindings between Visual Studio and Eclipse.  I’ll have to admit, that I should probably keeps with the Eclipse key bindings when it comes to debugging, but I’ve grown custom to Visual Studios F5, F10, F11.  To make my life a pleasant one, I like to modify Eclipse’s key binding when it comes to debugging.  Follow the step below to achieve just this.

1.  Open Eclipse and go to Window^Preferences.  Select the section General->Keys.  I like to sort the items by Binding so that the ones to modify are all clumped together.

image

2.  Modify the following items show in the diagram below.  Unbind the Refresh for “When=In Windows”, otherwise two operations will be bound to F5.

image

 

If you want to go the other way around and modify Visual Studio, then open Visual Studio and navigate to Tools^Options.  Select section Environment->Keyboard.

image