Wednesday, April 29, 2009

Download YouTube video as MP4

In Download YouTube Videos as MP4 Files post, I had written about how you can download YouTube video as an MP4 file to your computer.

It used a “bookmarklet” (a bookmark with some javascript code, to download the videos).

Here is an updated bookmarklet for downloading YouTube videos.

YouTube Download Bookmarklet

To use it:

Right click on the above link and select “Bookmark this link” in Firefox or “Add to favorites” in Internet Explorer. (Select “Favorites Bar” in Internet Explorer or “Bookmarks Toolbar” in Firefox, and to make it easily accessible via the toolbar in these browsers).

When you click on the link, it should open up a dialog to save the mp4 to your computer. A good thing about this bookmarklet is that it detects and automatically downloads the HQ video if it is available.

PS: Remember to use it according to Google’s user agreement policy.

Tuesday, April 28, 2009

Words – Gordian Knot

Gordian Knot:
1: an intricate problem ; especially : a problem insoluble in its own terms —often used in the phrase cut the Gordian knot
2: a knot tied by Gordius, king of Phrygia, held to be capable of being untied only by the future ruler of Asia, and cut by Alexander the Great with his sword

The Gordian Knot is a legend associated with Alexander the Great. It is often used as a metaphor for an intractable problem, solved by a bold stroke ("cutting the Gordian knot")

Alexander Cutting the Gordian Knot, oil on canvas by Jean-Simon Berthèlemy. (Britannica)

Song in Honda Insight at the beach ad

The song is by the Mostar Diving Club and is called Honey Tree.

The difference between an aggregate and a composition

Needed to jog my mind about the difference between an aggregate and a composition. Here is how I think about it….

Aggregation – Brain in a human. Human dies, so does brain.
Composition – Clothes on a human. Clothes make the human complete, but when a human dies, they are no longer needed and can be given away to another human.

Boils down to who creates the object that is being used by another object. If the object was created by the using class – it is a composition. If the object was created by some other class, then it is an aggregate.

Another way to break down a relationship into an aggregation and composition is to use the has and part-of phrases – like so:
Water is a part-of a pond (composition)
Ponds have ducks (aggregate)

Here is an example:

A university is composed of students and courses (if the university is shut down, students and courses no longer exist). Courses have (aggregate) students (If a course is cancelled, students still exist in the university and can take other courses).

Students and courses are a part-of the university. (composition)
Courses have students (aggregation)
Students have courses (aggregation)


Bottom line:

Aggregations are a weak relationship: a class cancelled doesn’t mean the student ceases to exist.
Composition are a strong relationship: courses and students cannot exist if the university is shut down.

Using the idea of who creates the objects:
1. University creates courses and students – its a composition relationship to courses and students.
2. University assigns students to courses – its an aggregation relationship.


Ideally, the relationship between students and courses would be a 2-way aggregation.


In the above example, if a student drops out, the courses don’t get cancelled, they just get notified about the fact that a student has dropped out and update their internal state to reflect that.

So what is a car?

So for the oft used example of a car – the car is truly an aggregate – as it is made up of a bunch of other parts, each of which can continue being used even if the car is sent to the junk yard and typically each of the parts are created at other factories, which are then put together (aggregated) at yet another factory to create a car.

Ports being used by Team Explorer to chat with TFS

I needed to find out which ports were being used by Team Explorer to communicate with the TFS server.

With Team Explorer 2008 and TFS 2008, I found that the following ports were being used

microsoft-ds (445)

The first two were being used by devenv.exe (Visual Studio). The last one (microsoft-ds) was being used by the system (not sure why this port is being used – but I suspect it might be TFS shell integration power-toys that are using it, as the communications is being initiated by [system]).

So, if you need to open a firewall between your client machines and the TFS server, I would start with the above 3 ports associated with your TFS server address.

Other useful information:

Read the topic “Ports required during installation” in the TFSInstall Guide.

TFS Server Ports:

Words - chirography

chirography • \kye-RAH-gruh-fee\  • noun
*1 : handwriting, penmanship 2 : calligraphy

TFS and client access from multiple domains

Is a client from a different domain unable to access your TFS system?

Check out this MSDN document “Trusts and Forests Considerations for Team Foundation Server”. It goes into configurations that are supported and unsupported and also goes into issues that might cause problems when a client from a different domain tries to access TFS.

Also, here is one thing that you can try to see whether or not TFS is able to get access to the user account of the person who is trying to access TFS. (Just to be clear, in this case, we are attempting to give access to a user from a different domain to TFS)

Log in to the TFS server using the TFSService account. Open a command prompt and run the following command

dsquery user -samid ACCOUNT_NAME -d DOMAIN | dsget user -dn -samid -sid -display -email –L

Replace account_name and domain, with the user id and domain of the user that you are trying to provide TFS access to. The command should run successfully and you should see account information such as the SID, email, displayname, etc. If you dont, then poke further into seeing if the 2 domains trust each other or if a firewall on one domain is blocking requests from the domain where TFS resides.

Monday, April 27, 2009

Microsoft Tags – V2.0

The next generation of Microsoft Tags has been released quietly and this version is even cooler as it allows you to customize the tags using images (instead of the boring set of triangles).

Read about it at


And they even have a contest for the best dressed custom tag -

Sunday, April 26, 2009

Design Patterns Cheat Sheet

Happened upon this blog post on the “A Coding Fool” blog, with a very useful design patterns cheat sheet. Print it and put it up on your wall.


Download PDF

Also check out the other cheat sheets that are on that site – very useful.

Update 04.27.2009 - Here is another good aggregation of design pattern UML diagrams from McDonaldLand -

Own your C – Sunshine - song

This Own your C ad has been airing for a while now.

The song is by CocoRosie and is called Sunshine

Song in LensCrafter ad

The new LensCrafter ad is called “See what you love”

And the song is Colors by Donovan

Song in new Lincoln MKZ ad

I like the song in the new Lincoln MKZ ad.

The song is by Shiny Toy Guns and is called Major Tom (coming home)

And here is the song in its entirety.

The song is a remix of a David Bowie song

Which was then remixed by Peter Schilling

Download the song from the following link...

Thursday, April 23, 2009

Words - Eidetic

eidetic • \eye-DET-ik\  • adjective
: photographic memory - marked by or involving extraordinarily accurate and vivid recall especially of visual images

Wednesday, April 22, 2009

Channel 9 video – Branching 101 in TFS

diA channel 9 video on branching source code in TFS.

Get Microsoft Silverlight

The discussion regarding different branching strategies is discussed at around 24 minutes into the show.

Tuesday, April 21, 2009

Can constructors throw exceptions?

In C# the answer is yes and is also part of the “Constructor Design Guidelines” on MSDN.

Do throw exceptions from instance constructors if appropriate.

The finalizer will be called on an object even if an exception occurred during its construction in C#.

In C++, it is important to remember that if an exception occurs in a constructor then, its destructor will not be called and you need to write the code to clean up memory in case an exception is raised in your constructor.

But the bottom line is this: because a constructor does not return a value – you need to typically use an exception to signal that the object was not created. And then depending on your language, handle the clean up appropriately. (The alternative is to use a zombie flag, that is set in case of an error in the constructor, which is then checked before any properties, methods are accessed).

Other guidelines from the document (C# constructor design guidelines):

  1. Consider providing simple, ideally default, constructors. A simple constructor has a very small number of parameters, and all parameters are primitive types or enumerations.
  2. Consider using a static factory method instead of a constructor if the semantics of the desired operation do not map directly to the construction of a new instance, or if following the constructor design guidelines feels unnatural.
  3. Do use constructor parameters as shortcuts for setting main properties.
  4. Do use the same name for constructor parameters and a property, if the constructor parameters are used to simply set the property. The only difference between such parameters and the properties should be casing.
  5. Do minimal work in the constructor. Constructors should not do much work other than to capture the constructor parameters. The cost of any other processing should be delayed until required.
  6. Do throw exceptions from instance constructors if appropriate.
  7. Do explicitly declare the public default constructor in classes, if such a constructor is required.
  8. Avoid having default constructors on structures.
  9. Do not call virtual members on an object inside its constructors.

Can you delete a NULL pointer?

What happens when you delete a pointer set to NULL in C++?

Thankfully, nothing! delete will check for a pointer being NULL and will skip deleting the object if it finds the pointer to be set to NULL.

So you dont have to do the following in your code:

if (ptr != NULL)
    delete ptr;
    ptr = NULL;

The check to see if ptr is null is redundant and can be removed.

But it is always a good idea to set your pointer to NULL after deleting it because delete does not set the pointer to NULL and calling delete twice on the same pointer can lead to BAD things happening. And what is worse is that multiple delete related bugs are extremely hard to debug and can cause your application to behave badly in totally unrelated sections of the code.

So always do this:

delete ptr;
ptr = NULL;

5.3.5 (6): If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will invoke the destructor (if any) for the object or the elements of the array being deleted. In the case of an array, the elements will be destroyed in order of decreasing address (that is, in reverse order of the completion of their constructor; see 12.6.2).

Working Draft, Standard for Programming Language C++

Now you know!

Monday, April 20, 2009

Millions miss the mark @ Four Corners Monument

fcvisit For all those people who have taken pictures with each of their limbs in the four states (Colorado, Utah, Arizona and New Mexico), here is a news flash – you need to go back and retake your photograph. And this time travel 2.5 miles north east of your previous location!

The four corners location was decreed by the U.S. Congress to be located at 37.0 N,109.0W. But errors in the survey done in 1868, located the four corners point wrongly at what is actually 36.998988N,109.045186W.,5143,705298412,00.html


What do you get when you combine a ham radio transmitter, a frequency doubler and a varactor diode?

10300284_T You get a “Bolinha”, a free world-wide satellite phone (courtesy of the US Department of Defense and the US tax payer!).

This article from “The Great Brazillian Sat-Hack Crackdown”, has an interesting story of how hobbyists have been able to modify their HAM radio equipment so that they could jump on to the communication bands of US military satellites (FLTSATCOM) and then use it for their personal communications! (Santa Claus is coming!) And apparently the practice is so widespread that even a college professor has been arrested and 12 year old kids know how to hack their HAM radios in order to communicate via the Bolinhas.Ch1a

The hackers end up using precious communications bandwidth to which they don't have any legal access. But at least, because all important communications within the US DOD is encrypted, others cannot eavesdrop on those conversations (Or at least that is what we hope).

It is an interesting read. And also brings up the all important point of: are the hackers doing anything wrong? After all they are only using an open communications channel on a satellite. But as the article points out, illegal access to a military com channel, can have serious implications on the welfare of a unit on the battlefield. And just because a door is open, it doesn't mean you have the right to walk on in, into the house (and in this case, eat something right out of the refrigerator in that house!).

TF55030 error while renaming TFS 2008 server name.

In “Unable to rename machine running TFS 2008”, I had written about a TF55030 error that I was getting everytime I ran the TfsAdminUtil RenameDT <newServerName>.

Raymond and Bill Wang from MS pointed me in the right direction for fixing the error. Turns out that some settings werent getting updated, which resulted in RenameDT looking for the old server name.

Here are the set of steps that I took right before running RenameDT, to get it to succeed.

On the server, after updating the Reporting Services database setup and windows identity....

1. Open folder "C:\Program Files\Microsoft Visual Studio 2008 Team Foundation Server\Web Services\Services"
2. Open the web.config file and change the connection string's server name to the new server name. Save and keep the file open.
3. Open a command prompt and go to folder "c:\Program Files\Microsoft Visual Studio 2008 Team Foundation Server\Tools" and run
    TfsAdminUtil ConfigureConnections /view
    Reports Uri and ReportServer Uri should show up as pointing to the old server name.... we need to update it.
4. update the reporting uris
     TfsAdminUtil ConfigureConnections /ReportsUri:http://<NewServerName>/Reports /ReportServerUri:http://<newServerName>/Reports/ReportService.asmx
    Should run successfully
5. Change the server name in the web.config file that you edited in (2) back to the old server name (required for TfsAdminUtil RenameDT to work!).
6. Start the Report Server and Microsoft Team Foundation Server Application Pool
7. Make sure that the default web site is running
8. Run TFSAdminUtil RenameDT <newServerName>
   should run successfully.

Thursday, April 16, 2009

Unable to rename machine running TFS 2008 (single server deployment).

I am trying to rename the host machine that is running TFS 2008 and it is not working!

Here are the steps I tried out on the Virtual PC 2007 TFS 2008 image (where i renamed the host from TfsRTM08 to TfsRTM-09) and the error message that I ended up with (which can be found right at the end):

Does any one know what is going on here?

Steps from How to: Rename a Data-Tier Server

According to the note right at the beginning:

If you restart the server in a single-server deployment, wait for the server to restart, and then stop the services that Team Foundation Server uses.

1. Shutdown services and sites

Windows SharePoint Services Timer
Default Web Site

Visual Studio Team Foundation Server Task Scheduler Service
Microsoft Team Foundation Server Application Pool

SQL Server Reporting Services (TFSINSTANCE)
ReportServer (application pool)

2. Rename the server to TFSRTM-09

Check if all services in step 1 are still shutdown (otherwise shut them down).

3. Redirect and rename reporting Services

Make sure that the following services are running

SQL Server Analysis Services
SQL Server Analysis Services (MSSQLSERVER)


On the server that is running Reporting Services, click Start, point to All Programs, point to Microsoft SQL Server, point to Configuration Tools, and then click Reporting Services Configuration
In the Report Server Installation Instance Selection dialog box, make sure that the name of the current data-tier server appears and that the instance name is MSSQLSERVER, and then click Connect.
In the Explorer pane, click Server Status.
In the Report Server Status pane, click Start.
In the Explorer pane, click Database Setup.
In Server Name, type the name of the new data-tier server, and then click Connect. The SQL Server Connection dialog box opens.
In Database Name, type ReportServer, and click OK.
In the Database Connection pane, click Apply.
In the SQL Server Connection Dialog dialog box, click OK.

Error is reported: “Assigning Reporting Services Rights to User”
System.Data.SqlClient.SqlException: Windows NT user or group 'TFSRTM-09\tfsSqlServer' not found. Check the name System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)at System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async)at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe)at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()at Microsoft.ReportingServices.Common.DBUtils.ApplyScript(String connectionString, String script)at ReportServicesConfigUI.SqlClientTools.SqlTools.ApplyScript(String connectionString, String script)

In the Explorer pane of Reporting Services Configuration Manager, click Windows Service Identity.
In the Built-in Service Account list, click Local Service, click Network Service, and then click Apply.
You are prompted to back up the symmetric key.
Redo the steps of setting up the Database above. This time no error will be reported.

Open Computer Manager, and start the ReportServer or ReportServer$InstanceName application pool.
Open Internet Explorer, type the following string in the Address bar, and press ENTER: http://localhost/Reports (Also had to start the Default Website for this to work)
Click TfsOlapReportsDS.
In the Connection string box, update the Data source argument with the name of the server that hosts SQL Server Analysis Services. For example, type the following string:
Data source=NameOfAnalysisServicesDataTierServer\TFSInstance
Do the same for TfsReportDS

4. Update the Application-Tier Server to Work with the Renamed Server

Start the following services and websites

Microsoft Team Foundation Server Application Pool
ReportServer (application pool)
SQL Server Reporting Services (TFSINSTANCE)

Open the Command Prompt window, change directories to %ProgramFiles%\Microsoft Visual Studio 2008 Team Foundation Server\Tools, and type the following command:

TfsAdminUtil RenameDT tfsRTM-09

Verifying new selected TFS data source tfsrtm-09.
Verified new selected TFS data source tfsrtm-09 as valid.
ERROR: TF55030: Rename Data Tier failed. Please verify that the supplied Data Tier name is a valid Team Foundation Data Tier name,that the Data Tier can be accessed on the network and that you have administrative rights.

type the following command Set TfsDetailedErrors=1

Run the RenameDT command once again to get more error detail

Verifying new selected TFS data source tfsrtm-09.
Verified new selected TFS data source tfsrtm-09 as valid.
ERROR: Microsoft.TeamFoundation.Server.TfsAdminException: TF55030: Rename Data Tier failed. Please verify that the supplied Data Tier name is a valid Team Foundation Data Tier name, that the Data Tier can be accessed on the network and that you have administrative rights. ---> System.Net.WebException: The remote name could not be resolved: 'tfsrtm08' at System.Net.HttpWebRequest.GetRequestStream() at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters) at Microsoft.TeamFoundation.Proxy.Reporting.ReportingService.GetDataSourceContents(String DataSource) at Microsoft.TeamFoundation.Server.TfsDT.ChangeDataSources(String oldDataTierName, String newDataTierName) at Microsoft.TeamFoundation.Server.TfsDT.Rename(String newDataSourceName)--- End of inner exception stack trace --- at Microsoft.TeamFoundation.Server.TfsDT.Rename(String newDataSourceName) at Microsoft.TeamFoundation.Server.TfsAdmin.RenameDataTier(TfsAdminContext context, String newDataTierName) at Microsoft.TeamFoundation.Server.TfsAdminUtil.RunRenameDTUtil(List`1 parameters, List`1 commandSwitches, String[] rawArgs) at Microsoft.TeamFoundation.Server.TfsAdminUtil.Run(String[] args)
> Inner Exception: Status Code: NameResolutionFailure
System.Net.WebException: The remote name could not be resolved: 'tfsrtm08'
at System.Net.HttpWebRequest.GetRequestStream() at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters) at Microsoft.TeamFoundation.Proxy.Reporting.ReportingService.GetDataSourceContents(String DataSource) at Microsoft.TeamFoundation.Server.TfsDT.ChangeDataSources(String oldDataTierName, String newDataTierName) at Microsoft.TeamFoundation.Server.TfsDT.Rename(String newDataSourceName)

The error is very weird because it says that “The remote name could not be resolved: 'tfsrtm08'”. And TfsRtm08 will obviously not be found because it has been renamed to tfsrtm-09!.'

Any suggestions?

update (Apr 16, 2009) : I have posted this question to MSDN forums.

Wednesday, April 15, 2009

TFS – Getting more information about errors when running TFS Admin utilities

When running TFS admin tools and if you encounter errors, the error messages are cryptic and mostly useless.

But here is a setting that will allow you to see more of the error information (sometimes even the stack trace) that will make it a lot more easier to track down errors.

At the command prompt run “Set TfsDetailedErrors=1”, before you run any of TFS admin tools. When you run a TFS admin tool after setting this environment variable, you will start getting a ton more information regarding errors.

Case in point. I was getting the TF55030 error whenever I ran RenameDT

ERROR: TF55030: Rename Data Tier failed.  Please verify that the supplied Data Tier name is a valid Team Foundation Data Tier name,
that the Data Tier can be accessed on the network and that you have administrative rights.

The error message does not give much information about why RenameDT was failing. In fact it was pointing me in the wrong direction by stating that it might be an administrative rights issue.

After setting TfsDetailedErrors to 1, I get the complete stack trace as well as the following message:

System.Net.WebException: The remote name could not be resolved: 'tfsrtm08'

That was weird because I was trying to rename the Data Tier from tfsrtm08 to tfsrtm0801 and the server tfsrtm08 did not exist any longer. So this was obviously not a permissions issue. But probably a bug in Microsoft’s code. More about the error itself will come in another post. But for now, just know that TfsDetailedErrors is your friend.

Tuesday, April 14, 2009

Blogger – Tag Cloud

I always wanted a tag-cloud for my blog that is hosted on Google’s Blogger.

I found the source for a tag-cloud widget on Raymond’s site – Compender and it works really well.


Here is what I needed to do to include it on my blog:

Because Raymond’s tag is based on a hack, you first need to add the Labels widget to template.

    • Go to Layout | Page Elements
    • Click on “Add a Gadget”
    • Click on “Basics” and select the “Labels” gadget
    • Make sure that the “Labels” gadget appears in the right section of your blog, otherwise drag it to the correct location.
    • Next click on “Edit Html”
    • Backup your current template by click on “Download Full Template”.
    • Put a check in front of “Expand Widgets”
    • You need to remove the current definition for the Label widget and replace it with Raymond’s hack.
      • Search for “<b:widget id='Label1' locked='false' title='Tags' type='Label'>”.
      • Select the text that appears between the above tag and the corresponding closing tag “</b:widget>”.
      • Delete the text.
      • Delete the tag “<b:widget id='Label1' locked='false' title='Tags' type='Label'></b:widget>”
    • Copy the code from below and place it in the same location that the old widget appeared.
    • Save the template. (If there was an error then Blogger will inform you. Otherwise you will be able to view the updated blog).

Here is the code for the cloud.

You can change the size of the tag cloud by changing the min and max values in the code below.


<b:widget id='Label1' locked='false' title='Tags' type='Label'>
    <b:includable id='main'>
        <b:if cond='data:title'>
        <div class='widget-content' style='text-align: justify;'>
             <script type='text/javascript'>
                var max = 200; //max css size (in percent)
                var min = 80; //min css size (...)
                var showCount = 1;  // show counts? 1 for yes
                var minCount = 1;  // what is the minimum count for a Tag to be shown? 1 for all.
                //Begin code:
                var range = max - min;
                //Build label Array
                var labels = new Array();
                 <b:loop values='data:labels' var='label'>
                var urls = new Array();
                 <b:loop values='data:labels' var='label'>
                var counts = new Array();
                 <b:loop values='data:labels' var='label'>
                //Number sort funtion (high to low)
                function sortNumber(a, b)
                 return b - a;
                //Make an independant copy of counts for sorting
                var sorted = counts.slice();
                //Find the largest tag count
                var most = sorted.sort(sortNumber)[0];
                //Begin HTML output
                for (x in labels)
                 if(x != &quot;peek&quot; &amp;&amp; x != &quot;forEach&quot; &amp;&amp; counts[x] &gt;= minCount)
                  //Calculate textSize
                  var textSize = min + Math.floor((counts[x]/most) * range);
                  //Show counts?
                  if(showCount == 1)
                   var count = &quot;(&quot; + counts[x] + &quot;)&quot;;
                   var count = &quot;&quot;;
                  document.write(&quot;<span style='font-size:&quot; + textSize + &quot;%'><a href='&quot; + urls[x] + &quot;'>&quot; + labels[x] + count + &quot;</a></span> &quot; );

ASP.Net – Never again fear publishing your website with debug=true

An alternative title for this post would be: “Why you should never deploy your ASP.Net application with debug set to true to a production server”.

An easy way to expose your ASP.Net website to hackers is to publish it to a production site with the debug attribute in the “compilation” element set to true. The reason for this is that exception messages are typically more verbose. Hackers try and make your website throw an exception, because they can then use the verbose exception messages to determine names of database tables and fields as well as property names in your application’s classes. In addition, deploying an application with debug set to true results in:

    • The compilation of ASP.NET pages takes longer (as batch compilation is turned off)
    • Code typically executes slower
    • Memory footprint is increased
    • Scripts and images downloaded from the WebResources.axd handler are not cached
    • Requests do not time out (this is bad, as in a production environment we dont want requests to be stuck indefinitely)

The obvious fix is to remember to set debug=false whenever you publish your website to a production environment. But what if you forget to set debug=false? This was something that always worried me…

And today I found out about the deployment element

What makes the deployment element special is that it is a machine level configuration element. When its retail attribute is set to true, it will disable the <compilation debug=”true”> for ALL ASP.Net applications running on that machine. In addition, it turns of detailed errors messages being sent to remote machines and disables the ability to trace output.

Here is how you set it (remember this is not an application level, web.config element, and is set only in the machine.config file, typically found in “%windir%\\Framework\[VersionNumber]\CONFIG”)

updated 04.24.2009: updated the quotes around the true below, which caused issues, because the formatter used non-standard quotes. I apologize to all those who had to deal with this issue, which apparently caused IIS to not start up.

      <deployment retail="true"/>

Deployment ElementMSDN Compilation ElementMSDN

TFS – Setting up team project members using user groups

Team project administration of users in TFS is not easy. A big contributing factor for this is that TFS is made up off multiple applications (SharePoint, Reporting Services and TFS Server) and there is no single tool that manages users across all 3 applications. (Actually, the TFS Admin Tool can do that – but it is extremely buggy and in my opinion does not lend itself well for an enterprise level organization).

So here is what I have come up with based on MSDN documentation and TFS labs (Mostly the labs)

The steps described in this post uses Windows groups to administer Team Project users (administrators and developers). The reason for this is that by adding Windows groups to each application (TFS, SharePoint Server, Reporting Services) rather than adding individual users is that by using groups, it makes it a lot more easier to add new users to specific projects.

There are 4 major tasks that the TFS admin needs to do.

1. Add the local user groups 2. Setup SharePoint 3. Setup Reporting Services 4. Setup TFS team membership

All of these steps must be done only after the Team Project has been created. These steps should be ideally done by the TFSSetup user or a user with similar privileges.

Once the 4 steps have been undertaken, new users (admins and developers) can be added to a project by simply adding them to the appropriate windows user group.

Step 1: Add the local user groups

  1. Click on Start->Administrative Tools->Computer Management
  2. Click on Local Users and Groups->Groups folder
  3. Right click on Groups and select New Group In general, the group name should be specific to your project. Add two groups using the New Group dialog (I like to use the following template)
    1. TFS_[ProjectName]_Administrators
    2. TFS_[ProjectName]_Developers
  4. If you know who is going to work on the Team Project, then add the appropriate users to the above local groups.

By default, the TFSSETUP user is the administrator over all the projects defined in TFS. The TFSSETUP user creates the project using Team Explorer. Once the project is created, you will find that TFS has built a Reporting Services site and a SharePoint portal for your project.

Step 2: Setting up SharePoint

  1. Visit the Project Portal by right clicking on the Team Project and selecting the “Show Project Portal” option.


  1. Click on Site Actions menu on the SharePoint web page and select the Site Settings command.
  2. The next screen that comes up has an option to manage users. Select the People and Groups hyperlink in the Users and Permissions section.
  3. On the People and Groups page, select New | Add Users from the toolbar.
  4. Enter the name of the team project admin group that you created in Task 1 in the Users/Groups field.
  5. Click on the check names button to validate the name you entered.
  6. In the Give Permission section, under the Give users permission directly section, place a check next to “Full Control - Has full control” and click OK
  7. Repeat steps 4 to 7 and this time add the team project developers group (that you created in task 1) and instead of giving the group “Full Control” give the user-group the Contribute - Can view, add, update, and delete” permission.

Step 3: Setting up Reporting Services

  1. In the Team Explorer right click on the Reports node under the Team Project and select the Show Report Site command. clip_image004
  1. The Reporting Services Home page for the selected project is loaded in a browser. Click the Properties tab.
  2. Click on the Security link
  3. Click on the Edit Item Security link. (Click ok to dismiss the warning message).
  4. Click on the New Role Assignment link and add the team project administrators user group to the Group or user name field, set their role as Content Manager, and click OK.
  5. Repeat the above step and this time add the team project developers user group for the project. Set the role to Publisher.

Step 4: Setting up users in TFS for the Team Project

  1. In the Team Explorer, right-click on the Team Project node and select Team Project Settings | Group Membership. clip_image005
  2. Select the Project Administrators group and then click the Properties button.
  3. Select the Windows User or Group option in the Add member panel, and then click the Add button.
  4. Enter Team Project Administrators group in the text field, click Check Names to validate. Click OK.
  5. Click OK
  6. Repeat steps 2 to 5, but this time choose “Contributors” role instead of “Project Administrators” and add the Team Project Developers group to this role.

Cool words - dyed-in-the-wool

dyed-in-the-wool : thoroughgoing, uncompromising

As Henry Paulson was a dyed-in-the-wool capitalist, he decided that for the sake of moral hazard, it was important for Lehman Brothers to fail.

Monday, April 13, 2009

Code-behind file for Global.asax

I like the code-behind file pattern that is used for the aspx files in an ASP.Net web-site as it allows me to separate the code from the presentation. But for some reason when you try and add the global.asax file (that contains the Global Application class) to a website, Visual Studio does not give you the option to create a code behind file (The most probable reason for this is that the asax file unlike the aspx, is not used to present a UI to the user).

Here are some steps that you can take so that you can move your code-logic to a code-behind file linked to the global.asax file:

1. Add the global.asax file to your website.

2. Add a new class and name it global.asax.cs. (If you are using VB, then name it global.asax.vb). Visual Studio might inform you that it is a class file type and that it should be added to App_Code folder. Answer Yes.


3. Extend the newly added global class by making it inherit from “System.Web.HttpApplication


4. Move all the code that appears between the <script runat=”server”>……</script> tags to the “global.asax.cs” file.


5. Delete the <script runat=”server”></script> tag from the asax file.

6. Modify the application directive in the asax file from

<%@ Application Language="C#" %>


<%@ Application Language="C#" CodeBehind="~/App_Code/global.asax.cs" Inherits="global" %>

7. Test the modification by dropping a break-point on the first line of the “Application_Start” method. Run the web-site. If everything you did was correct – the break-point should be hit as soon as your web-site starts.


If you are doing this for a VB ASP.Net application, then you need to name your class as [global], because “global” is a key-word in VB.


StalkDaily, XSS and you as an ASP.Net Developer

Twitter had been attacked on April 11th, 2009 by a cross site scripting (XSS) worm called “StalkDaily” that was created by a 17 year old.

At the very least this should have made you take a look at XSS vulnerabilities in your website. It should also drive home the point on how easy it is to take advantage of XSS vulnerabilities, as this worm was created by a 17 year old who was bored at home.

So as an ASP.Net developer what is the very least that you should be doing?

1. Know what cross-site scripting is. Basically any website that accepts user-input and uses that input to create custom web-page content can be vulnerable to XSS. XSS occurs when a malicious user injects code into a web-page that is run from your website. The code can be injected via any input control on your web-page that accepts information from a user. The code then runs as though it came from your web-page and can provide the malicious user login information, etc for for any other user of your website, that visits the page on which the malicious code was injected.

The simplest example of this sort of a XSS vulnerability is when a web-page accepts comments. It is possible for a malicious user to enter javascript code within the comments. When another user views the web-page containing the comments, the code will be run by the users browser and it would open up the user to theft of their information (via posting of the authentication cookie, or just snippets of information from the web-page, to the malicious user’s website).

2. Limit input. The biggest back-door in any website, that makes it vulnerable to XSS are all the fields that allow a user to provide information to your website. Firstname, lastname, comments, etc., if the user can type in information, and the information can make it back to the server – then it has the potential to being misused to inject XSS code into your site.

One simple check that you can do is to type in the following code into a text-input box and see if an alert is shown to you:

<script>alert('you are vulnerable to a XSS hack!');</script>

So, if you need the age of the user, then make sure the input is a number and does not contain alphabets and other special characters. Check for ranges, check for types, check for anything, check for something. Every check that you make, makes your page that much less vulnerable to XSS hacks.

Use the validators (RangeValidator, RegularExpressionValidatior, etc.) on your input fields. Use the Regex class to re-check inputs for containing valid characters and text sequences. Use the Convert class to make sure that input data is of the right data-type, handle any type conversion exceptions.

3. Encode output (especially if it is created from user input).

If the data was at some point entered by a user of your website, then treat is as potentially dirty data. If it is data, then use the HttpUtility.HtmlEncode method remove HTML special characters and re-encode them to the corresponding HTML character codes. If it is a link (url, mailto, etc.) then use the HttpUtility.UrlEncode method to sanitize the URL.

Look for Response.Write and <%= string sequences in your code. These are 2 of the major ways used to output html to the user. Evaluate if HtmlEncode or UrlEncode should be used to sanitize the output.

4. Filter user input

If you must accept potential dangerous user input (such as HTML tags), then see if it would be possible to remove some of the potentially dangerous tags from being part of the input. (Eg: allow tags for bold, underline, italic, and remove iframe, script tags).

It is always better to look for what you would like to allow and remove everything else, then to look for what is banned and to remove them.

5. Make sure that ValidateRequest is turned on for your entire website.

By default your Asp.Net website should be validating user input. This is done via the ValidateRequest value being set to true in either you machine.config file or your web.config file. (ASP.Net, allows you to turn this off on a page-by-page basis, allowing you to evaluate if a certain page does require ValidateRequest to be turned off).

6. Ensure that Asp.Net exceptions are not returned to the user.

Dont make the malicious user’s life easier by returning the entire exception information to them. The exception information frequently contains information (such as table names, field names, etc.) that can make it easier for the user to script code that can take advantage of the XSS vulnerabilities in your website.

Make sure that customErrors mode is set to “remoteOnly” in your web.config. (in addition you could even set it to redirect to a custom error page).


Trust nothing, Validate every Input and Sanitize every Output!

Update: Apr 13, 2009: has a good article titled "Securing ASP.Net Applications" that discusses many of the vulnerabilties that can creep into a poorly developed ASP.Net website.



How To: Protect From Injection Attacks in ASP.NET How To: Protect From SQL Injection in ASP.NET How To: Use Regular Expressions to Constrain Input in ASP.NET How To: Prevent Cross-Site Scripting in ASP.Net

Friday, April 10, 2009

System.InvalidOperationException: The ConnectionString property has not been initialized

Are you seeing the following exception message:

System.InvalidOperationException: The ConnectionString property has not been initialized. at System.Data.SqlClient.SqlConnection.PermissionDemand() at System.Data.SqlClient.SqlConnectionFactory.PermissionDemand(DbConnection outerConnection) at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) at System.Data.SqlClient.SqlConnection.Open() at xxxxxxx in yyyyyyyy

One cause for this error is, if you try and setup a SqlConnection object with a connection string that is empty (null, nadha, nill, nothing…). So test by hard coding the connection string that you are using to create the SqlConnection. If that works – then the way that you are retrieving your connection string (web.config, custom text/xml file, etc) might be having an issue (permissions, file missing, etc).

Thursday, April 09, 2009

.Net – Chatting with a Barcode Reader over the serial port

One of my projects requires that my application be able to read bar-codes. I had a slot badge scanner to test with, which connected to the computer via the serial port (its an old reader).


.Net 2.0 makes it extremely easy to talk to devices connected to the serial port using the “SerialPort” class.

You create a new instance of the class using one of the constructors, the simplest of which is: SerialPort port = new SerialPort(port, baudRate, parity, dataBits, stopBits), which sets up the class to communicate over the given port using the parameters specified. (port is a string like “COM1”).

You can now send data using one of the Write methods or receive data using one of the Read methods.

But, to process data that is being read from the bar-code scanner, I wanted the application to automatically react everytime it received barcode data. Using the Read, ReadLine methods, will only allow you to retrieve data that is already stored in the SerialPort’s buffer/stream. To be able to process data from the Barcode reader as it is received – the SerialPort class provides the “DataReceived” event.

port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived)

Where port_DataReceived has the following method signature: void port_DataReceived(object sender, SerialDataReceivedEventArgs e)

The one thing that you should know is that the DataReceived event can be fired more than once while the bar-code reader is sending its data. For this reason, you need to determine a method to find out when a complete bar-code has been received before you begin processing the data. In my case the bar-code would send a carriage return at the end of every bar-code that had been read. The port_DataReceived method would buffer in coming data in a StringBuilder. The StringBuilder is converted to a string and stored in a Queue everytime the bar-code reader send a NewLine character. The queue can then be read by another method to process the data. (My implementation provides for a method which signals when a complete bar-code has been received).

public class BarcodeDataReceivedEventArgs : EventArgs
   public BarcodeDataReceivedEventArgs(string barcodeData)
       _barcodeData = barcodeData;
   string _barcodeData = null;
   public string BarcodeData { get { return _barcodeData; } }
public delegate void Barcode_DataReceived(object sender, BarcodeDataReceivedEventArgs bdrEA);

public class SerialPortBarcodeReader
   bool _sendBarcodeDataToKeyboard = true;
   public event Barcode_DataReceived Barcode_DataReceived;
   public SerialPortBarcodeReader(bool sendBarcodeDataToKeyboard)
       _sendBarcodeDataToKeyboard = sendBarcodeDataToKeyboard;

   private SerialPort _port = null;

   /// <summary>
   /// use this method to start polling the barcode on the specified serial port with the specified settings
   /// </summary>
   public void StartPolling(string port, int baudRate, Parity parity, int dataBits, StopBits stopBits)
       if (!IsPortOpen)
           string[] portNames = SerialPort.GetPortNames();
           _port = new SerialPort(port, baudRate, parity, dataBits, stopBits);
           _port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
           //Sometimes you need to enable RTS for the SerialDataReceivedEventHandler event to be fired.
           _port.RtsEnable = true;
   public void StopPolling()
       if (IsPortOpen)

   /// <summary>
   /// returns true if the port is still open
   /// </summary>
   public bool IsPortOpen
       get { return (_port != null && _port.IsOpen); }

   /// <summary>
   /// returns data from the queue and removes it, so that the next call
   /// to this method will return the next item in the quque.
   /// </summary>
   /// <returns></returns>
   public string DequeBarCodeData()
       string data = null;
       if (_barcodeQue != null && _barcodeQue.Count > 0)
           data = _barcodeQue.Dequeue();
       return data;

   private StringBuilder _buffer = new StringBuilder();
   private Queue<string> _barcodeQue = new Queue<string>();
   /// <summary>
   /// this method might be called multiple times for a single piece of data (for example a bar-code)
   /// the number of times its called is based on the size of the serial ports buffer and the read threshold size
   /// which is why we need to buffer the received data, until we receive an end of transmission flag, after which we can process the data.
   /// </summary>
   /// <param name="sender"></param>
   /// <param name="e"></param>
   private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
       //read line and append to buffer
       string inLine = _port.ReadExisting();

       //check if complete information was received - which in this case is signalled by
       //a NewLine.
       if (inLine.EndsWith(Environment.NewLine))
           //process the buffer and clear it out.
           string barcodeInfo = _buffer.ToString();
           _buffer = new StringBuilder();

           //add to queue - so that it can be processed else where

           if (_sendBarcodeDataToKeyboard)

           //raise event
           if (Barcode_DataReceived != null)
               Barcode_DataReceived(this, new BarcodeDataReceivedEventArgs(barcodeInfo));

   /// <summary>
   /// closes the reader
   /// </summary>
   public void Close()
       if (_port != null && _port.IsOpen)


When I first began using SerialPort with the bar-code reader, the DataReceived event would not fire. After many hours of testing I found out that in the case of my Bar-code reader, I had to set the RtsEnable property of the SerialPort class to true.

In the class above, you can subscribe to the “Barcode_DataReceived” event to be notified when ever a complete bar-code has been received. The event args provide you with the bar-code data that was received. You can process items from the queue using the “DequeBarCodeData” method, which will remove the bar-code data from the processing queue.

Capture text - CopyText

CopyText_dlgOne of the most important tools that I have in my applications toolbox is a screen capture utility (I have SnagIt for my personal machines and Faststone Capture 5.3 for my work machine). There is almost no day that goes by when I have to flip one of these applications out and take a screen shot (especially because I blog so much).

But recently, I needed a tool that would copy text from an application. After much searching I found CopyText. CopyText copies text from any window and its free.



The usage of this tool is a little tricky though.

You need to create a shortcut for the app and drop it in your Start Menu.

Next you need to assign a short-cut key to shortcut.

Finally, start up the app that you want to capture text from, activate the window that you wish to copy the text out of and then hit the short-cut key to start up CopyText.

The controls from your window should show up in the Use drop-down of CopyText. Select the control/user-interface element that contains the text you are after and hit “Copy Found”.

.Net – Sending keys to the keyboard buffer

How do you send a bunch of keys to the keyboard buffer? Or in other words how do you emulate a user typing away at the keyboard through your application?

Well the simplest answer is that you should use the SendKeys class in the System.Windows.Forms namespace.

System.Windows.Forms.SendKeys.Send("Hello World");
SendKeys will work fine and dandy as long as you are running within a WinForms application. If you try and use SendKeys from a console application, or a dll (which you wish to keep generic so that it could be used in any type of application), then it will throw an exception with the following message:

SendKeys cannot run inside this application because the application is not handl ing Windows messages. Either change the application to handle messages, or use the SendKeys.SendWait method.

The alternative is to use the SendWait method, but that one expects you to have an active application waiting to accept the input. (Otherwise your app will hang, waiting for an active app to accept the keys).

What I needed was a method to send keys to the keyboard buffer and then forget about it. Just like Send works, only it had to be able to work without being run from within an active windows application.

The only way to get this functionality is to use PInvoke and call the WINApi method “SendInput”.

Here is my SendKeys class with a Send method that sets up the WIN API structures and calls the SendInput method.

public class SendKeys
/// <summary>
/// Sends the text to the keyboard buffer
/// </summary>
/// <param name="text"></param>
public static void Send(string text)
   if (string.IsNullOrEmpty(text))

   uint numCharsToSend = (uint)(text.Length * 2); //keydown keyup
   SendInputWin32.INPUT[] structInputArray = new SendInputWin32.INPUT[numCharsToSend];
   int index = 0;
   foreach (char key in text.ToCharArray())
       //key down
       SendInputWin32.INPUT structInputKeyDown = SendInputWin32.CreateNewINPUT(SendInputWin32.INPUT_KEYBOARD); = SendInputWin32.KEYEVENTF_UNICODE; = (ushort)key;
       structInputArray[index++] = structInputKeyDown;
       //key up
       SendInputWin32.INPUT structInputKeyUp = SendInputWin32.CreateNewINPUT(SendInputWin32.INPUT_KEYBOARD); = SendInputWin32.KEYEVENTF_UNICODE | SendInputWin32.KEYEVENTF_KEYUP; = (ushort)key;
       structInputArray[index++] = structInputKeyUp;
   int sizeOfINPUT = Marshal.SizeOf(structInputArray[0]);
   SendInputWin32.SendInput((numCharsToSend), ref structInputArray[0], sizeOfINPUT);

And here is the code that provides all the constants, structures, and the PInvoke required to call SendInput.

public class SendInputWin32
public const ushort INPUT_MOUSE = 0x000;
public const ushort INPUT_KEYBOARD = 0x0001;
public const ushort INPUT_HARDWARE = 0x0002;

public const ushort KEYEVENTF_KEYUP = 0x0002;
public const ushort KEYEVENTF_KEYDOWN = 0x0000;
public const ushort KEYEVENTF_SCANCODE = 0x0008;
public const ushort KEYEVENTF_UNICODE = 0x0004;

public const ushort XBUTTON1 = 0x0001;
public const ushort XBUTTON2 = 0x0002;
public const ushort MOUSEEVENTF_MOVE = 0x0001;
public const ushort MOUSEEVENTF_LEFTDOWN = 0x0002;
public const ushort MOUSEEVENTF_LEFTUP = 0x0004;
public const ushort MOUSEEVENTF_RIGHTDOWN = 0x0008;
public const ushort MOUSEEVENTF_RIGHTUP = 0x0010;
public const ushort MOUSEEVENTF_MIDDLEDOWN = 0x0020;
public const ushort MOUSEEVENTF_MIDDLEUP = 0x0040;
public const ushort MOUSEEVENTF_XDOWN = 0x0080;
public const ushort MOUSEEVENTF_XUP = 0x0100;
public const ushort MOUSEEVENTF_WHEEL = 0x0800;
public const ushort MOUSEEVENTF_VIRTUALDESK = 0x4000;
public const ushort MOUSEEVENTF_ABSOLUTE = 0x8000;

public enum VK : ushort
   SHIFT = 0x10,
   CONTROL = 0x11, //ctrl key
   MENU = 0x12,    //alt key
   ESCAPE = 0x1B,
   BACK = 0x08,
   TAB = 0x09,
   RETURN = 0x0D,
   PRIOR = 0x21,
   NEXT = 0x22,
   END = 0x23,
   HOME = 0x24,
   LEFT = 0x25,
   UP = 0x26,
   RIGHT = 0x27,
   DOWN = 0x28,
   SELECT = 0x29,
   PRINT = 0x2A,
   EXECUTE = 0x2B,
   SNAPSHOT = 0x2C,
   INSERT = 0x2D,
   DELETE = 0x2E,
   HELP = 0x2F,
   NUMPAD0 = 0x60,
   NUMPAD1 = 0x61,
   NUMPAD2 = 0x62,
   NUMPAD3 = 0x63,
   NUMPAD4 = 0x64,
   NUMPAD5 = 0x65,
   NUMPAD6 = 0x66,
   NUMPAD7 = 0x67,
   NUMPAD8 = 0x68,
   NUMPAD9 = 0x69,
   MULTIPLY = 0x6A,
   ADD = 0x6B,
   SEPARATOR = 0x6C,
   SUBTRACT = 0x6D,
   DECIMAL = 0x6E,
   DIVIDE = 0x6F,
   F1 = 0x70,
   F2 = 0x71,
   F3 = 0x72,
   F4 = 0x73,
   F5 = 0x74,
   F6 = 0x75,
   F7 = 0x76,
   F8 = 0x77,
   F9 = 0x78,
   F10 = 0x79,
   F11 = 0x7A,
   F12 = 0x7B,
   OEM_1 = 0xBA,   // ',:' for US
   OEM_PLUS = 0xBB,   // '+' any country
   OEM_COMMA = 0xBC,   // ',' any country
   OEM_MINUS = 0xBD,   // '-' any country
   OEM_PERIOD = 0xBE,   // '.' any country
   OEM_2 = 0xBF,   // '/?' for US
   OEM_3 = 0xC0,   // '`~' for US
   MEDIA_STOP = 0xB2,
   LWIN = 0x5B,        //windows key
   RWIN = 0x5C         //windows key

public struct MOUSEINPUT
   public int dx;
   public int dy;
   public uint mouseData;
   public uint dwFlags;
   public uint time;
   public IntPtr dwExtraInfo;

public struct KEYBDINPUT
   public ushort wVk;
   public ushort wScan;
   /// <summary>
   /// Set flags such as KEYEVENTF_KEYUP, UNICODE, etc
   /// Flags should be combined using bitwise |
   /// </summary>
   public uint dwFlags;
   public uint time;
   public IntPtr dwExtraInfo;

public struct HARDWAREINPUT
   public uint uMsg;
   public ushort wParamL;
   public ushort wParamH;

public struct INPUT
   public int type;
   [FieldOffset(4)] //*
   public MOUSEINPUT mi;
   [FieldOffset(4)] //*
   public KEYBDINPUT ki;
   [FieldOffset(4)] //*
   public HARDWAREINPUT hi;

public static extern uint SendInput(uint nInputs, ref INPUT pInputs, int cbSize);

public static INPUT CreateNewINPUT(ushort type)
   SendInputWin32.INPUT structInputKeyDown = new SendInputWin32.INPUT();
   structInputKeyDown.type = type; = 0; = 0; = 0; = IntPtr.Zero;
   return structInputKeyDown;

Once you have the code implemented, all you need to do to send a set of characters to the keyboard is to call “SendKeys.Send(“Hello World”);”

Notes: This code was created on a 32bit machine. On a 64 bit machine, your offsets for the structures might change – do your research on this. Specifically, look at the “INPUT” structure. The offsets are set to 4bytes. This might have to be changed to 8bytes on a 64bit machine. If you test this out – let everyone know – by leaving a comment.

On a Windows Vista machine – you might have issues with the User Account Control (UAC). Generally speaking, one app should be able to speak to another app that has the same or a lower security level.

The above method that I have described, has one more advantage – it will allow you to send keys to games that are being run using DirectX (DirectInput).

The reason I needed such a method was that I was creating an application that would run in the background which would speak to a bar-code reader. Everytime a bar-code would be read, the application would log the bar-code information in a database. In addition the application would send the barcode information to other applications using SendKeys. The effect of this, was that I could open any other application (NotePad, Internet Explorer forms, etc), and everytime I scanned a bar-code, the barcode text would appear in these other applications. In essence, the application made the bar-code appear as though it was connected to the keyboard. (Coming in another post – how to speak to a barcode reader over a serial port).

Note 2: Testing the code:

here is how (using a console app):

public static void Main()


//run as console app and then select different apps to see the code being pasted to the window.

//eg. notepad, textboxes, etc.



SendKeys.Send("Hello World ");


} while (true);

//press ctrl+break to exit




Monday, April 06, 2009

Viewing the list of TFS Portals in SharePoint

As an administrator you might need to view the list of Team Project portals running on your TFS SharePoint instance. Here is how:

1. Go to the TFS SharePoint Central Administration site. (Typically the address is : http://TFSServer:17012/).
note: The 17012 – is the port for the Central Administration site and might be different for your TFS installation.

2. Enter the address “http://TFSServer:17012/_admin/SiteCollections.aspx”on your toolbar and click GO. This will take you to the TFS SharePoint Central Administration site’s Site Collection list page.

Alternatively, you could go to the “Application Management” tab


And select “Site collection list” under SharePoint Site Management.


3. Make sure that the Web Application selected in the drop-down list on the right hand top corner is the TFS project portal site instance and not the central administration portal instance.


You should now be able to view all the Team Project portals running as part of your TFS installation.