Playing a video using gstreamer on OSX

by timking 4. January 2012 14:09

 

#import "AppDelegate.h"
#import <gst/gst.h>

static GMainLoop *loop;

static gboolean bus_call(GstBus *bus, GstMessage *msg, void *user_data)
{
    switch (GST_MESSAGE_TYPE(msg)) {
        case GST_MESSAGE_EOS: {
            g_message("End-of-stream");
            g_main_loop_quit(loop);
            break;
        }
        case GST_MESSAGE_ERROR: {
            GError *err;
            gst_message_parse_error(msg, &err, NULL);
            g_error("%s", err->message);
            g_error_free(err);
            
            g_main_loop_quit(loop);
            break;
        }
        default:
            if (gst_structure_has_name(msg->structure, "have-ns-view")) { 
                NSView* videoWindow = (__bridge NSView*)g_value_get_pointer(gst_structure_get_value(msg->structure, "nsview"));
                AppDelegate* del = (__bridge AppDelegate*)user_data;
                [del.window setContentView: videoWindow];
            }
            
            break;
	}
    
	return true;
}

static void gst_finish(GstElement* object, gpointer user_data)
{
    //set uri to the next clip
}

static void play_uri(const char *uri, void *user_data)
{
	GstElement *pipeline;
	GstBus *bus;
    
	loop = g_main_loop_new(NULL, FALSE);
	pipeline = gst_element_factory_make("playbin2", "player");
    
	if (uri)
		g_object_set(G_OBJECT(pipeline), "uri", uri, NULL);
    
	bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
	gst_bus_add_watch(bus, bus_call, user_data);
	gst_object_unref(bus);
    
    g_signal_connect(pipeline, "about-to-finish", G_CALLBACK(gst_finish), user_data);
    
	gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING);
    
	g_main_loop_run(loop);
    
	gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL);
	gst_object_unref(GST_OBJECT(pipeline));
}

@implementation AppDelegate

@synthesize window = _window;

-(void)g_init:(id)param
{
    gst_init(NULL, NULL);
	play_uri("file:///Users/TimK/Movies/test.avi", (__bridge void*)self);
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    NSThread *g_thread = [[NSThread alloc] initWithTarget:self selector:@selector(g_init:) object:nil];
    [g_thread start]; 
}

@end

 

Tags:

OSX

express.js sub domains

by timking 12. May 2011 20:01

It's easy to create sub domains that serve multiple applications from a single node.js server when using express.js. You need to use the connect middleware vhost which is made available in express as express.vhost.

First you need to create the applications that you want to serve using your sub-domains in separate folders. For this example I create two folders subdomain1 and subdomain2 in my main node application directory. Each folder contains a full node.js application - the server file in each case is named app.js.

In order for the applications to be started as subdomains you need to export each express server like this:

var app = express.createServer();
exports.app = app;

This allows each sub domain to be started as a virtual host using the following code in your main node application:

var sys = require('sys'),
	express = require('express');
	
var app = express.createServer();

app.configure('development', function() {
	app.use(express.vhost('subdomain1.local', require('./subdomain1/app').app));
	app.use(express.vhost('subdomain2.local', require('./subdomain2/app').app));
	app.listen(3000);
});

app.configure('production', function() {
	app.use(express.vhost('subdomain1.domain.com', require('./subdomain1/app').app));
	app.use(express.vhost('subdomain2.domain.com', require('./subdomain2/app').app));
	app.listen(80);
});

I set these up using my hosts file for local development so that I can easily access both sub domains.

Tags:

node.js | express.js

Aspect ratio calculator

by timking 2. March 2010 12:09

I recently needed to calculate the aspect ratio for some pretty unusually sized video clips for a project I was working on. I needed to know this so I could re-encode the clips using ffmpeg. I wrote this little JavaScript tool to assist me.




Here is the code it uses:

function calculateRatio (a, b) {
	return (b == 0) ? a : calculateRatio (b, a % b);
}
             
function checkPos(number)
{
	if(parseInt(number) > 0)
    {
		return true;
	}
    else
    {
		return false;
	}
}
            
function displayRatio()
{
	var width = document.getElementById("width").value;
    var height = document.getElementById("height").value;
                
    if(!checkPos(width) || !checkPos(height))
    {
		alert("Width and height must be positive numbers");
        return false;
	}
                
	var ratio = calculateRatio(width, height);
            
    var html = "Width = " + width + "
"; html += "Height = " + height + "
"; html += "Ratio = " + ratio + "
"; html += "Aspect = " + width/ratio + ":" + height/ratio; document.getElementById("result").innerHTML = html; return false; }

Tags:

Video

Exporting stills from video with an alpha channel

by timking 1. March 2010 11:56

I recently had the need to export a series of still images with transparency from several videos with alpha channels. I immediately thought of the extremely useful and efficient program ffmpeg. This command line tool makes it easy to convert video between a whole load of different formats, including creating stills in a variety of image formats. I chose to use png as I wanted high quality with transparency for use in animations on an iPad. The command to do this using ffmpeg is:

ffmpeg -i video.flv -f image2 -pix_fmt rgb32 -r 12 %03d.png

This command takes the input video and encodes it into an image sequence at 12 frames per second. The important bit for getting the alpha channel to work correctly is the pixel format rgb32. %03d numbers the images zero padded to be 3 digits long.

In order to do this to a whole directory of videos encoded in flv format I used the following in a batch script:

mkdir icons
FOR %%i IN (*.flv) DO mkdir icons\%%~ni
FOR %%i IN (*.flv) DO ffmpeg -i %%i -f image2 -pix_fmt rgb32 -r 3.6 icons\%%~ni\frame_%%03d.png

This script creates an output directory called icons then loops through the videos creating sub directory for the stills and then runs ffmpeg to create the stills.

Tags: , ,

Video

Export solution information from Visual Studio (continued)

by timking 11. November 2009 12:08

In my last postI talked about exporting solution information from Visual Studio using a macro to create an xml file. In this post I’m going to use this exported information to answer questions about the health of my solution and highlight things that I may wish to investigate further to improve consistency and hopefully get rid of any nasty surprises. The following methods query the xml output that has been loaded into an XDocument and dump the results to another XDocument.

Find all projects with names that don’t match the output file name – there may well be a legitimate reason for naming an output file differently from a project, but if there isn’t, for example when a project has been renamed and the output file name hasn’t, it can lead to confusion.

///
/// Find all of the projects that have a output file name that doesn't match the project name
/// 
private void ProjectsWithNamesNotMatchingOutputFileName(XDocument loaded)
{
    var q = from c in loaded.Descendants("project")
            where c.Descendants("property")
                    .Where(p => p.Attribute("type")
                            .Value == "OutputFileName").Count() > 0
                && c.Attribute("name").Value !=
                    Path.GetFileNameWithoutExtension(
                                c.Descendants("property")
                                .Where(p => p.Attribute("type").Value == "OutputFileName")
                                .ElementAt(0).Value)
            select c;
 
    XElement resultSet = new XElement("resultSet",
                                        new XAttribute("type",
                                        "ProjectsWithNamesNotMatchingOutputFileName"));
 
    foreach (XElement x in q)
    {
        resultSet.Add(new XElement("result",
                                    new XAttribute("name", x.Attribute("name").Value),
                                    new XAttribute("value",
                                        x.Descendants("property")
                                            .Where(p => p.Attribute("type")
                                                .Value == "OutputFileName")
                                                .ElementAt(0).Value)));
    }
 
    root.Add(resultSet);
}

Find all projects with names that don’t match the containing folder name – again there may be a legitimate reasons for doing this but 9 time out of 10 this will occur when someone has renamed a project and not updated the file system to reflect the change. This can lead to confusion when viewing the solution in explorer.

/// 
/// Find all of the projects with names that don't match the containing folder name
/// 
private void ProjectsWithNamesNotMatchingContainingFolder(XDocument loaded)
{
    var q = from c in loaded.Descendants("project")
            where !c.Attribute("fullname").Value.EndsWith("\\") &&
                    Path.GetDirectoryName(c.Attribute("fullname").Value)
                                            .TrimEnd('\\')
                                            .Substring(Path.GetDirectoryName(
                                                        c.Attribute("fullname").Value)
                                            .LastIndexOf("\\") + 1) !=
                    c.Attribute("name").Value
            select c;
 
    XElement resultSet = new XElement("resultSet",
                                        new XAttribute("type",
                                            "ProjectsWithNamesNotMatchingContainingFolder"));
 
    foreach (XElement x in q)
    {
        resultSet.Add(new XElement("result",
                                    new XAttribute("name", x.Attribute("name").Value),
                                    new XAttribute("value",
                                        Path.GetDirectoryName(x.Attribute("fullname").Value)
                                            .TrimEnd('\\')
                                            .Substring(Path.GetDirectoryName(
                                                            x.Attribute("fullname").Value)
                                             .LastIndexOf("\\") + 1))));
    }
 
    root.Add(resultSet);
}

Find all projects with less than 5 classes – most of the time projects with few classes in them cause more harm than good especially if there are lots of them in a solution. This adds cost at development and compile time, cost at deployment time and cost at runtime. See here http://www.theserverside.net/tt/articles/showarticle.tss?id=ControllingDependencies for a detailed explanation of the problems.

/// 
/// Find all projects that have less than 5 classes 
/// 
private void ProjectsWithLessThan5Classes(XDocument loaded)
{
    var q = from c in loaded.Descendants("project")
            where c.Descendants("codeItem")
                        .Where(i => i.Attribute("type").Value == "class")
                        .Count() < 5
            select c;
 
    XElement resultSet = new XElement("resultSet",
                                        new XAttribute("type",
                                            "ProjectsWithLessThan5Classes"));
 
    foreach (XElement x in q)
    {
        resultSet.Add(new XElement("result",
                                    new XAttribute("name", x.Attribute("name").Value),
                                    new XAttribute("value", x.Descendants("codeItem")
                                        .Where(i => i.Attribute("type").Value == "class")
                                        .Count())));
    }
 
    root.Add(resultSet);
}

Find all projects with less than 5 code artifacts – this is closely related to the previous example but it is expanded to include all .Net types such as structs, interfaces and enums.

/// 
/// Find all projects that have less than 5 classes 
/// 
private void ProjectsWithLessThan5CodeArtifacts(XDocument loaded)
{
    var q = from c in loaded.Descendants("project")
            where c.Descendants("codeItem").Count() < 5
            select c;
 
    XElement resultSet = new XElement("resultSet",
                                        new XAttribute("type",
                                            "ProjectsWithLessThan5CodeArtifacts"));
 
    foreach (XElement x in q)
    {
        resultSet.Add(new XElement("result",
                                    new XAttribute("name", x.Attribute("name").Value),
                                    new XAttribute("value", x.Descendants("codeItem")
                                            .Count())));
    }
 
    root.Add(resultSet);
}

Find duplicate output file names – this may occur when 2 or more projects may or may not have the same name but are set to produce output files with the same name. This is a bad idea that can lead to confusion.

/// 
/// Find all of the projects that have duplicated output file names
/// 
private void ProjectsDuplicateOutputFileName(XDocument loaded)
{
    var q = from c in loaded.Descendants("project")
                .GroupBy(p => p.Descendants("property")
                    .Where(a => a.Attribute("type").Value == "OutputFileName"))
                .Where(g => g.Count() > 1)
            select c;
 
    XElement resultSet = new XElement("resultSet",
                                        new XAttribute("type",
                                            "ProjectsDuplicateOutputFileName"));
 
    foreach (XElement x in q)
    {
        resultSet.Add(new XElement("result",
                                    new XAttribute("name", x.Attribute("name").Value),
                                    new XAttribute("value", x.Descendants("property")
                                        .Where(p => p.Attribute("type")
                                            .Value == "OutputFileName")
                                        .ElementAt(0).Value)));
    }
 
    root.Add(resultSet);
}

Find all the projects that don’t a file called VersionInfo.cs in them – this is used to make sure all of the dlls in the solution contain version information.

/// 
/// All of the projects which don't have a version.cs file in the root
/// 
/// 
private void ProjectsMissingVersionCS(XDocument loaded)
{
    var q = from c in loaded.Descendants("project")
            where c.Descendants("codeFile")
                    .Where(a => a.Attribute("name").Value.ToLower() == "versioninfo.cs")
                    .Count() == 0
            select c;
 
    XElement resultSet = new XElement("resultSet",
                                        new XAttribute("type",
                                            "ProjectsMissingVersionCS"));
 
    foreach (XElement x in q)
    {
        resultSet.Add(new XElement("result",
                                    new XAttribute("name", x.Attribute("name").Value)));
    }
 
    root.Add(resultSet);
}

List all of the code artifacts that are not in the root namespace for the project – there may well be legitimate reasons why code in a project is not in the default namespace for the project. However most often this is caused by people being lazy and not making sure when they move code between projects they update the namespaces. Keeping consistency with things like this makes the code much easier to understand for new people looking at it.

/// 
/// List all code assets that are not in the default namsepace defined for containing project
/// 
private void CodeAssetsNotInTheDefaultNamespace(XDocument loaded)
{
    var q = from c in loaded.Descendants("codeItem")
            where !c.Attribute("fullname").Value
                    .StartsWith(
                        c.Ancestors("project")
                        .Descendants("property")
                        .Where(o => o.Attribute("type").Value == "RootNamespace")
                        .ElementAt(0).Value)
            select c;
 
    XElement resultSet = new XElement("resultSet",
                                        new XAttribute("type",
                                            "CodeAssetsNotInTheDefaultNamespace"));
 
    foreach (XElement x in q)
    {
        resultSet.Add(new XElement("result",
                                        new XAttribute("name", x.Attribute("type").Value),
                                        new XAttribute("expectedValue", x.Ancestors("project")
                                            .Descendants("property")
                                            .Where(o => 
                                            o.Attribute("type").Value == "RootNamespace")
                                            .ElementAt(0).Value),
                                        new XAttribute("value", x.Attribute("fullname").Value)));
    }
 
    root.Add(resultSet);
}

Find projects that are not reference by other projects in the solution – they are certainly genuine reasons why this can happen but it can also happen when old code is left lying around and it not cleaned up properly. Again this makes life much harder for new people looking at the code. How are they supposed to know what isn’t being used anymore?

/// 
/// Find all of the projects that are not referenced by other projects in the solution
/// 
private void ProjectsNotReferencedInTheSolution(XDocument loaded)
{
    var q = from c in loaded.Descendants("project")
            where (from d in loaded.Descendants("reference")
                   where d.Descendants("path")
                        .Where(p =>
                            c.Descendants("property")
                                .Where(o => o.Attribute("type").Value == "OutputFileName")
                                .Count() > 0 &&
                            p.Value == Path.Combine(
                                Path.Combine(
                                    Path.GetDirectoryName(
                                        c.Attribute("fullname").Value)
                                            , "bin\\Debug"
                                    ),
                                    c.Descendants("property")
                                        .Where(o => 
                                            o.Attribute("type").Value == "OutputFileName")
                                            .ElementAt(0).Value
                                    )
                                )
                            .Count() == 0
                   select d).Count() == 0
            select c;
 
    XElement resultSet = new XElement("resultSet",
                                        new XAttribute("type",
                                            "ProjectsNotReferencedInTheSolution"));
 
    foreach (XElement x in q)
    {
        resultSet.Add(new XElement("result",
                                    new XAttribute("name", x.Attribute("name").Value)));
    }
 
    root.Add(resultSet);
}

Find all projects that are only referenced by one other project in the solution – there could be genuine reasons for this but it is a good indicator that the projects should be merged to avoid the dependency problems that I touched on earlier, especially if the project has very few types in it.

/// 
/// Find all of the projects with names that don't match the containing folder name
/// 
private void ProjectsReferencedOnceInTheSolution(XDocument loaded)
{
    var q = from c in loaded.Descendants("project")
            where (from d in loaded.Descendants("reference")
                   where d.Descendants("path")
                        .Where(p =>
                            c.Descendants("property")
                                .Where(o => o.Attribute("type").Value == "OutputFileName")
                                    .Count() > 0 &&
                            p.Value == Path.Combine(
                                Path.Combine(
                                    Path.GetDirectoryName(
                                        c.Attribute("fullname").Value), 
                                        "bin\\Debug"
                                    ),
                                    c.Descendants("property")
                                        .Where(o => o.Attribute("type").Value == "OutputFileName")
                                        .ElementAt(0).Value
                                    )
                                )
                            .Count() == 1
                   select d).Count() == 0
            select c;
 
    XElement resultSet = new XElement("resultSet",
                                        new XAttribute("type",
                                            "ProjectsReferencedOnceInTheSolution"));
 
    foreach (XElement x in q)
    {
        resultSet.Add(new XElement("result",
                                    new XAttribute("name", x.Attribute("name").Value)));
    }
 
    root.Add(resultSet);
}

You can see from these examples that we can answer quite a few questions about the health of a solution by using an xml file exported using a macro. Obviously it isn’t the slickest way to do this like you could achieve by writing an AddIn with a UI but it can be very useful when trying to make sense of a large complicated and potentially very messy inherited solution.

Tags:

Visual Studio

Export solution information from Visual Studio

by timking 6. November 2009 16:55

As I started talking about in a recent post I have recently taken over as the lead developer on reasonably complex project. The main solution for the project has 79 projects and I need ways to quickly get up to speed with the code and to find areas which are likely to cause me problems going forward. Although I’m loving using NDepend (www.ndepend.com) as a tool to help me analyse the output from the solution I can’t help thinking that there’s always the possibility of nasty surprises lurking in the solution and project configurations. This may be as innocuous as inconsistent naming conventions through to multiple versions of the same library being used in different places. I think it would be useful to be able to ask questions like:

  • Show me the projects that have an assembly name that doesn't match the project name
  • Show me any duplicated output assembly names
  • Show me the classes/interfaces/enums/structs in a project that aren’t in a namespace starting with the default
  • Show me all of the projects that aren’t referenced by any other projects in the solution
  • Draw a graph of the references between the projects
  • Find types that are not in a folder structure which matches the namespace, the same as is required in Java

I’m sure there are many more questions like these that would help to quickly assess the health and consistency of a complex solution and aid in repairing it. I’ve decided to start with a Visual Studio macro that will dump information about the solution into an xml file which can be used to answer these types of questions.

The purpose of the main sub, ExportSolutionInfo, is to loop through all of the projects in the solution adding and xml element for each then calling Navigate which is responsible for getting information about specific a projects. Once we have walked the whole solution structure building up xml elements along the way the document is saved and added to the ‘Solutions Items’ folder.

Sub ExportSolutionInfo()
    Dim project As EnvDTE.Project
 
    Try
        Dim xmlPath As String = GetSolutionPath() + "\Solution.xml"
        Dim solutionName As String = GetSolutionName()
 
        xml = New XmlTextWriter(xmlPath, Encoding.UTF8)
 
        xml.Formatting = Formatting.Indented
        xml.WriteStartDocument()
 
        If Not DTE.Solution.IsOpen Then
            xml.WriteStartElement("error")
            xml.WriteString("There is no solution open")
            xml.WriteEndElement()
        Else
            xml.WriteStartElement("solution")
            xml.WriteStartElement("projects")
            For Each project In DTE.Solution.Projects
                Navigate(project)
            Next
            xml.WriteEndElement()
            xml.WriteEndElement()
        End If
        xml.Close()
        DTE.Windows.Item(EnvDTE.Constants.vsWindowKindSolutionExplorer).Activate()
        DTE.ActiveWindow.Object.GetItem(solutionName + "\Solution Items") _
                                    .Select(vsUISelectionType.vsUISelectionTypeSelect)
        DTE.ItemOperations.AddExistingItem(xmlPath)
 
    Catch ex As System.Exception
        Debug.WriteLine("ExportSolutionInfo: " + ex.ToString)
    End Try
End Sub

The next thing to do is to is to create an xml element for each project this is done in the Navigate method. This method is also responsible for calling other methods which add xml elements for the detailed project information such as the project properties and the project references.

Private Sub Navigate(ByVal project As Project)
    If Not (project.ConfigurationManager Is Nothing) Then
        xml.WriteStartElement("project")
        xml.WriteAttributeString("name", project.Name)
        xml.WriteAttributeString("fullname", project.FullName)
        xml.WriteAttributeString("targetFramework", GetTargetFramework(project))
 
        If (project.Kind = VSLangProj.PrjKind.prjKindCSharpProject _
                Or project.Kind = VSLangProj.PrjKind.prjKindVBProject) Then
            Dim vsproj As VSLangProj.VSProject
            vsproj = CType(project.Object, VSLangProj.VSProject)
            GetPropeties(project)
            GetReferences(vsproj)
            GetCodeItems(project)
        End If
 
        xml.WriteEndElement()
    Else
        NavigateItems(project.ProjectItems)
    End If
End Sub

The other helper methods GetTargetFramework, GetProperties, GetReferences and GetCodeItems handle getting detailed information about the project and out putting in the same way as xml elements.

Private Function GetTargetFramework(ByVal project As EnvDTE.Project) As String
    Try
        Dim value As Integer = CType(project.Properties.Item("TargetFramework").Value, Integer)
        Return value.ToString("X")
    Catch ex As Exception
        Debug.WriteLine("GetTargetFramework: " + ex.ToString)
    End Try
End Function

GetProperties – gets all of the projects properties such as RootNamespace, OutputFileName, AssemblyGuid and many more then creates an xml element for each.

Private Sub GetPropeties(ByVal project As EnvDTE.Project)
    Try
		xml.WriteStartElement("properties")
        Dim prop As EnvDTE.Property
 
        For Each prop In project.Properties
			If prop.Name = "OutputFileName" Or prop.Name = "RootNamespace" Then
				xml.WriteStartElement("property")
                Try
					xml.WriteAttributeString("type", prop.Name)
                    If Not prop.Value Is Nothing Then
						xml.WriteString(prop.Value.ToString)
                    End If
				Catch ex As Exception
					Debug.WriteLine(ex.ToString)
				End Try
				xml.WriteEndElement()
            End If
        Next
        xml.WriteEndElement()
    Catch ex As Exception
        Debug.WriteLine("GetPropeties: " + ex.ToString)
    End Try
End Sub

Get references loops through the projects references and creates and xml element for each with detailed information.

Private Sub GetReferences(ByVal project As VSLangProj.VSProject)
    Try
        xml.WriteStartElement("references")
 
        For Each prop In project.References
            xml.WriteStartElement("reference")
 
            With prop
                xml.WriteStartElement("name")
                xml.WriteString(.Name)
                xml.WriteEndElement()
 
                xml.WriteStartElement("description")
                xml.WriteString(.Description)
                xml.WriteEndElement()
 
                xml.WriteStartElement("version")
                xml.WriteString(String.Format("{0}.{1}.{2}.{3}", _
                   .MajorVersion, .MinorVersion, .BuildNumber, .RevisionNumber))
                xml.WriteEndElement()
 
                xml.WriteStartElement("path")
                xml.WriteString(.Path)
                xml.WriteEndElement()
 
                Dim Type As String
                Select Case .Type
                    Case prjReferenceType.prjReferenceTypeActiveX
                        Type = "COM"
                    Case prjReferenceType.prjReferenceTypeAssembly
                        Type = "Assembly"
                End Select
 
                xml.WriteStartElement("type")
                xml.WriteString(Type)
                xml.WriteEndElement()
 
                xml.WriteStartElement("culture")
                xml.WriteString(.Culture)
                xml.WriteEndElement()
            End With
            xml.WriteEndElement()
        Next
 
        xml.WriteEndElement()
    Catch ex As Exception
        Debug.WriteLine("GetReferences: " + ex.ToString)
    End Try
End Sub

GetCodeItems gets details about the code assets within the project, I’ve kept it to a minimum at the moment but this could easily be extended to get more detailed information about the types in each project. I purposely kept it simple because tools such as NDepend are much better suited to making detailed source code analysis and there’s no point reinventing the wheel.

Private Sub GetCodeItems(ByVal items As EnvDTE.ProjectItems)
    Try
        Dim item As EnvDTE.ProjectItem
        For Each item In items
            If item.Name.EndsWith(".cs") Or item.Name.EndsWith(".vb") Then
                Try
                    Dim codeModel As FileCodeModel = item.FileCodeModel
                    If Not codeModel Is Nothing Then
                        xml.WriteStartElement("codeFile")
                        Try
                            xml.WriteAttributeString("name", item.Name)
                        Catch ex As Exception
                            Debug.WriteLine("GetCodeItems error accessing document: " + ex.ToString)
                        End Try
 
                        xml.WriteAttributeString("language", codeModel.Language)
 
                        For Each element In codeModel.CodeElements
                            If (TypeOf element Is CodeNamespace) Then
                                GetNamespace(CType(element, CodeNamespace))
                            ElseIf TypeOf element Is CodeClass Then
                                GetCodeClass(CType(element, CodeClass))
                            ElseIf TypeOf element Is CodeEnum Then
                                GetCodeEnum(CType(element, CodeEnum))
                            ElseIf TypeOf element Is CodeInterface Then
                                GetCodeInterface(CType(element, CodeInterface))
                            ElseIf TypeOf element Is CodeStruct Then
                                GetCodeStruct(CType(element, CodeStruct))
                            End If
                        Next
 
                        xml.WriteEndElement()
                    End If
                Catch ex As Exception
                    Debug.WriteLine("GetCodeItems error accessing file code model: " + ex.ToString)
                End Try
            ElseIf item.ProjectItems.Count > 0 Then
                GetCodeItems(item.ProjectItems)
            End If
        Next
    Catch ex As Exception
        Debug.WriteLine("GetCodeItems: " + ex.ToString)
    End Try
End Sub

I won’t go into the details of all the supporting methods here but I have attached the full source code of the macro. In a later post I will use the xml output from the macro to answer the questions that I raised at the beginning of this post. I have attached the source code for the macro (ExportSolutionInfo.vb) and an example xml file that was generated from one of my projects (Solution.xml) you can get it here.

Tags: , ,

Visual Studio

Taking over a project

by timking 5. November 2009 16:58

Today was my first day as the lead developer of one my companies products. The project is relatively well documented and I’ve had a decent handover period from the previous lead who handled it extremely professionally. However there is a fair amount of work to be done before I can say that I’m completely happy in my new position.

The product’s main solution consists of 79 projects which are a mixture of web services, web applications, windows services and winform applications. As you can imagine this means there is fair amount of code. I’ve also began to realise that a certain amount of this is dead code and that certain areas would benefit from greater test coverage and potentially refactoring. Dead code is something that I don’t believe has any place in this type of application, I think it adds unnecessary confusion and complexity and it should be removed. This is especially apparent when applications get handed between developers and the knowledge of what is and isn’t being used is lost.

So the time has come to audit the application to help me fully understand the code, remove dead code, identify areas lacking test coverage and to highlight areas that would benefit from refactoring to reduce complexity and any unnecessarily tight coupling between components. After spending a some time reading the source code I realised this was getting me nowhere and I needed tools to help with the task. Enter NDepend (http://www.ndepend.com), the website describes it as “a tool that simplifies managing a complex .Net code base”. It sounded just the ticket.

After firing up NDepend and opening the solution the first thing that I noticed was that 73 project DLLs loaded with 6 in error.

image

Closer inspection revealed that this was partially being caused by problems in the build which contains multiple versions of the same assembly. This is obviously a bad idea which is likely to lead to hard to find errors. There were several causes of this issue:

  1. The solution had multiple projects that were setup to output DLLs with the same name
  2. Some projects referenced a earlier compiled version of a project while others referenced the project directly

Digging down into these types of problems and fixing them as soon as you find them makes life much simpler later on when you’re trying to diagnose problems. Even at this early stage, NDepend is proving to be very useful for detecting issues which could potentially cause me to waste a lot of time later on.

Next I will use NDepend to produce an in depth analysis of the solution and hopefully remove the dead code, find under tested areas and identify areas that would benefit from refactoring.

Tags: ,

NDepend