Amazon.com Widgets All posts by williarob

WilliaBlog.Net

I dream in code

About the author

Robert Williams is an internet application developer for the Salem Web Network.
E-mail me Send mail
Code Project Associate Logo
Ads by Lake Quincy Media

Recent comments

Authors

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.


Use lambda expressions to aggregate values into a delimited string

Let's say you need to aggregate one value from each object in a list into a single string. For Example, you want to send an e-mail to a set of customers. This requires a string with the email addresses seperated by a semicolon (;). The following code will create a generic List of Books, and provide a method ListAllEmails() that will print the delimited list of emails to the console window:

 

namespace ConsoleApplication1

{

    using System;

    using System.Collections.Generic;

    using System.Linq;

 

    public class Lambdas

    {

        /// <summary>

        /// Define the Book Class

        /// </summary>

        public class Book

        {

            public string Title { get; set; }

            public string Author { get; set; }

            public double Price { get; set; }

            public string EmailAddress { get; set; }

        }

 

        public List<Book> Books { get; private set; }

 

        public Lambdas()

        {

            // Create a new list of Books

            Books = new List<Book> {

                new Book { Title = "Pro ASP.Net MVC Framework", Author = "Steven Sanderson", Price = 49.99, EmailAddress = "steve@nospam.com" },

                new Book{ Title = "Pro Silverlight 2 in C# 2008", Author = "Matthew MacDonald", Price = 49.99, EmailAddress = "Matthew@nospam.com" },

                new Book{ Title = "Pro VB 2008 and the .Net 3.5 Platform", Author = "Andrew Troelsen", Price = 59.99, EmailAddress = "Andrew@nospam.com" }

            };

        }

 

        /// <summary>

        /// Creates a semicolon (;) delimited list of email addresses

        /// </summary>

        public void ListAllEmails()

        {

            Console.WriteLine(this.Books.Select(b => b.EmailAddress).Aggregate((items, item) => items + "; " + item));

        }

    }

}

 

The Select Method selects the EmailAddress for each Book. The Aggregate method builds a list of the items based on the lambda expression. The result:

 

steve@nospam.com; Matthew@nospam.com; Andrew@nospam.com

 

Notice that this did not require any additional code to ensure there is no extra semi-colon at the beginning or end of the list, which is often required when using a loop to concatenate text.

 

Note: Be careful when using the Aggregate method because it is very inefficient on large numbers of strings. Consider using the String Join method instead.

 

In VB, the lambda would look like this:

 

   Console.WriteLine(Books.Select(Function(b) b.EmailAddress).Aggregate(Function(items, item) items & "; " & item))

 

You can also filter the list of email addresses. For Example, suppose you want to send an email to all the authors who sell their books for under $50, telling them that you think you can sell their next book for $59.99:

 

        /// <summary>

        /// Creates a semicolon (;) delimited list of email addresses where the price of the book is under $50

        /// </summary>

        public void ListSomeEmails()

        {

            Console.WriteLine(this.Books.Where(b => b.Price < 50).Select(b => b.EmailAddress).Aggregate((items, item) => items + ", " + item));

        }

 

 


Tags:
Categories: ASP.Net | C# | VB
Posted by Williarob on Friday, February 26, 2010 9:44 AM
Permalink | Comments (0) | Post RSSRSS comment feed

Taking P90X to the next level.

First let me start by saying that I think the P90X Workout system is great. The workouts are intense, effective and fun (at least once you are finally fit enough to do them - the first time I tried the Plyometrics Jump training I barely made it through the warm up and I'll admit I still don't look forward to it). However, I believe the system works best for people who are trying to lose a little weight and add some definition, rather than those of us who are already slim and trying to add 10 lbs or more of muscle. Having said that, if you follow the program closely and watch what you eat, you certainly will gain some muscle - my arms were noticeably bigger after 90 days and my body fat had dropped from around 14% to 7%, while my body weight remained the same.

About 5 months after I started the P90X program it became apparent to me, that all the exercises relying heavily on the use dumbbells added significantly more muscle than the workouts that rely more on body weight and gravity alone (such as Chest and Back which is mostly just push ups and pull ups). Sure my chest was toned and more than a little sore after these workouts, even after 90 days, but growth was minimal. Push ups can only take you so far, especially when you only weigh 150 lbs. Since I have a gym membership, I took the standard Chest and Back, and the Chest, Shoulders, Triceps workouts and replaced most of the push ups with exercises that can be done at the gym using the machines and free weights, and immediately felt that I had taken P90X to the next level. Here is my new Chest and Back Workout:

Date:          
Warm Up, Stretch, Std Push-Ups (25) 25 25 25 25 25
Wide Front Pull-Ups          
Incline Press (Barbell, 2 sets 8-12) R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
Reverse Grip Chin-Ups          
Incline Press (Dumbbell, 2 sets 8-12) R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
Closed Grip Overhand Pull-ups          
Bench Press (Barbell, 2 sets 8-12) R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
Heavy Pants R______W_______ R______W_______ R______W_______ R______W_______ R______W_______
Bench Press (Dumbbell, 2 sets 8-12) R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
Lawnmowers R______W_______ R______W_______ R______W_______ R______W_______ R______W_______
Decline Bench Press (Dumbbell or barbell, 2 sets 8-12) R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
Back Flies R______W_______ R______W_______ R______W_______ R______W_______ R______W_______
Decline Push-Ups (Max Reps/25) R_______________ R_______________ R_______________ R_______________ R_______________
Elbows-out Lawnmowers R______W_______ R______W_______ R______W_______ R______W_______ R______W_______
Incline Press (Machine, 2 sets 8-12) R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
Supermans 5 5 5 5 5
Bench Press (Machine, 2 sets 8-12) R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
CHEST & BACK          
           

Download this as an Excel Spreadsheet:

Chest & Back.xls (24.00 kb)

And here is my new Chest, Shoulders, Triceps workout:

Date:          
Warm Up, Stretch, Std Push-Ups (25) 25 25 25 25 25
In & Out Shoulder Flys R______W_______ R______W_______ R______W_______ R______W_______ R______W_______
Chair Dips R_______________ R_______________ R_______________ R_______________ R_______________
Incline Press (Barbell, 2 sets 8-12) R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
Deep Swimmer's Press R______W_______ R______W_______ R______W_______ R______W_______ R______W_______
Overhead Tricep Extensions R______W_______ R______W_______ R______W_______ R______W_______ R______W_______
Incline Press (Dumbbell, 2 sets 8-12) R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
Scarecrows R______W_______ R______W_______ R______W_______ R______W_______ R______W_______
Lying Tricep Extensions R______W_______ R______W_______ R______W_______ R______W_______ R______W_______
Bench Press (Barbell, 2 sets 8-12) R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
Y-Presses R______W_______ R______W_______ R______W_______ R______W_______ R______W_______
Side-Leaning Tricep Extensions R______W_______ R______W_______ R______W_______ R______W_______ R______W_______
Bench Press (Dumbbell, 2 sets 8-12) R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
Weighted Circles R______W_______ R______W_______ R______W_______ R______W_______ R______W_______
Throw the Bomb R______W_______ R______W_______ R______W_______ R______W_______ R______W_______
Decline Bench Press (Dumbbell or barbell, 2 sets) R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
Pour Flys (straight arms, out to sides, pour) R______W_______ R______W_______ R______W_______ R______W_______ R______W_______
Front-to-Back Tricep Extensions R______W_______ R______W_______ R______W_______ R______W_______ R______W_______
Incline Press (Machine, 2 sets 8-12) R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
Two-Angle Shoulder Flys R______W_______ R______W_______ R______W_______ R______W_______ R______W_______
Two-Arm Tricep Kickbacks R______W_______ R______W_______ R______W_______ R______W_______ R______W_______
Bench Press (Machine, 2 sets 8-12) R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
R______W_______
CHEST, SHOULDERS & TRICEPS      
           

Chest Shoulders & Triceps.xls (24.50 kb)

I think the Back and Biceps and the Shoulder, Biceps, Triceps workouts are great as they are, just keep upping the weight whenever you can, and every once in a while (to maintain that "Muscle confusion" - and this applies to the chest workouts too), start at the bottom of your worksheet and work you way up. This will help you avoid or break through a plateau. For example, the last exercise in the Back & Biceps workout is the strip set curl, which for 90 days was always last on the list and my muscles were always pretty cooked by the time I got there. By doing the workout in reverse, this is the first exercise and your muscles are fresh, you'll be able to start with at least 5 lbs more than normal and all of the exercises on the bottom half of your workout sheet will show improvement. (After 6 months of following the worksheets in order I was struggling to improve since my body was used to the movements by now. After doing it in reverse, my body ached the next day in a way it hadn't for quite some time and 2 weeks later when I came to do it again in the right order, I was finally able to up the weights on almost all the exercises. Just be sure to mark on the sheet that you did it backwards that week.

I have been using these new worksheets for 6 weeks now, and I have gained another 3 lbs of muscle, most of it on my chest.


Categories: P90X | Fitness
Posted by Williarob on Thursday, February 25, 2010 6:44 AM
Permalink | Comments (0) | Post RSSRSS comment feed

How to update multiple tables using T-SQL

Lets say you have a database made up of many tables. All of those tables have a field called "DateCreated" which cannot be null, but at the time the tables were created, you didn't think to set a default value for the field. Now you could open each table in design mode and set the default value manually, but here is an easier way:

The syntax to set a default value looks like this:

ALTER TABLE [table-name]
ADD CONSTRAINT constraint-name DEFAULT default-value FOR column-NAME;

While quicker that opening each table in design view this still only allows you to set the default value for a single table at a time. However, we can write some simple SQL that will generate a complete SQL Script for us:

SELECT 'ALTER TABLE [' + sysobjects.NAME +
'] ADD CONSTRAINT DF_' + sysobjects.NAME + '_DateCreated DEFAULT getdate()
FOR DateCreated;' from sysobjects inner join syscolumns on
sysobjects.id = syscolumns.id
inner join systypes on
syscolumns.xtype = systypes.xtype
WHERE syscolumns.NAME = 'DateCreated'

Run that query and each row returned will contain an Alter Table statement for each table. Simply highlight all the rows returned, then copy and paste them to a new query and run it. The SysObjects table contains one row for each object (table, column, constraint, default, log, rule, stored procedure, and so on) created within a database. This means you could write a query to update the sysobjects table directly and make this even easier, but if you mess up the sysobjects table your database could be trashed so use caution if you go that route.


Categories: SQL Server
Posted by Williarob on Friday, February 19, 2010 1:35 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Simplify Your Code with Lambda Expressions

Most applications retain lists of things, and a common task is to find an item in that list. The following class illustrates three ways to find an item in a generic list:

 

namespace ConsoleApplication1

{

    using System;

    using System.Collections.Generic;

    using System.Linq;

 

    public class Lambdas

    {

        public class Book

        {

            public string Title { get; set; }

            public string Author { get; set; }

            public double Price { get; set; }

        }

 

        public List<Book> Books { get; private set; }

 

        public Lambdas()

        {

            Books = new List<Book> {

                new Book { Title = "Pro ASP.Net MVC Framework", Author = "Steven Sanderson", Price = 49.99 },

                new Book{ Title = "Pro Silverlight 2 in C# 2008", Author = "Matthew MacDonald", Price = 49.99},

                new Book{ Title = "Pro VB 2008 and the .Net 3.5 Platform", Author = "Andrew Troelsen", Price = 59.99 }

            };

        }

 

        /// <summary>

        /// Returns a book using a traditional loop

        /// </summary>

        private Book FindUsingTraditionalLoop(string title)

        {

            Book foundBook = null;

 

            foreach (var b in this.Books)

            {

                if (b.Title == title)

                {

                    foundBook = b;

                    break;

                }

            }

 

            return foundBook;

        }

 

        /// <summary>

        /// Returns the book using a Linq expression

        /// </summary>

        private Book FindUsingLinq(string title)

        {

            var query = from b in this.Books

                        where b.Title == title

                        select b;

 

            return query.Count() > 0 ? query.ToList()[0] : null;

        }

 

        /// <summary>

        /// Returns the book using a Lambda expression

        /// </summary>

        private Book FindUsingLambda(string title)

        {

            return this.Books.FirstOrDefault(b => b.Title == title);

        }

 

        public void Test()

        {

            Console.WriteLine("Found: {0}", this.FindUsingTraditionalLoop("Pro Silverlight 2 in C# 2008").Author);

            Console.WriteLine("Found: {0}", this.FindUsingLinq("Pro Silverlight 2 in C# 2008").Author);

            Console.WriteLine("Found: {0}", this.FindUsingLambda("Pro Silverlight 2 in C# 2008").Author);

        }

    }

}

As these examples show, you can save time reading and writing your code by using Lambda expressions to find items in a list.

For VB programmers, the syntax of the Lambda expression looks like this:

return Me.Books.FirstOrDefault(Function(b) b.Title = title)


Categories: ASP.Net | C# | VB
Posted by Williarob on Wednesday, January 06, 2010 8:10 AM
Permalink | Comments (0) | Post RSSRSS comment feed

Mock a database repository using Moq

The concept of unit testing my code is still fairly new to me and was introduced when I started writing applications with the Microsoft MVC Framework in Visual Studio 2008. Intimidated somewhat by the Moq library's heavy reliance on lambdas, my early tests used full Mock classes that I would write myself, and which implemented the same interface as my real database repositories. I'd only write the code for the methods I needed, all other methods would simply throw a "NotImplementedException". However, I quickly discovered that the problem with this approach is that whenever a new method was added to the interface, my test project would no longer build (since the new method was not implemented in my mock repository) and I would have to manually add a new method that threw another "NotImplementedException". After doing this for the 5th or 6th time I decided to face my fears and get to grips with using the Moq library instead. Here is a simple example, of how you can mock a database repository class using the Moq library.

Let's assume that your database contains a table called Product, and that either you or Linq, or LLBLGen, or something similar has created the following class to represent that table as an object in your class library:

The Product Class

namespace MoqRepositorySample

{

    using System;

 

    public class Product

    {

        public int ProductId { get; set; }

 

        public string Name { get; set; }

 

        public string Description { get; set; }

 

        public double Price { get; set; }

 

        public DateTime DateCreated { get; set; }

 

        public DateTime DateModified { get; set; }

    }

}

 

Your Product Repository class might implement an interface similar to the following, which offers basic database functionality such as retrieving a product by id, by name, fetching all products, and a save method that would handle inserting and updating products.

 

The IProductRepository Interface

 

namespace MoqRepositorySample

{

    using System.Collections.Generic;

 

    public interface IProductRepository

    {

        IList<Product> FindAll();

 

        Product FindByName(string productName);

 

        Product FindById(int productId);

 

        bool Save(Product target);

    }

}

 

The test class that follows demonstrates how to use Moq to set up a mock Products repository based on the interface above. The unit tests shown here focus primarily on testing the mock repository itself, rather than on testing how your application uses the repository, as they would in the real world.

 

Microsoft Unit Test Class

 

namespace TestProject1

{

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using Microsoft.VisualStudio.TestTools.UnitTesting;

 

    using Moq;

 

    using MoqRepositorySample;

 

    /// <summary>

    /// Summary description for UnitTest1

    /// </summary>

    [TestClass]

    public class UnitTest1

    {

        /// <summary>

        /// Constructor

        /// </summary>

        public UnitTest1()

        {

            // create some mock products to play with

            IList<Product> products = new List<Product>

                {

                    new Product { ProductId = 1, Name = "C# Unleashed", Description = "Short description here", Price = 49.99 },

                    new Product { ProductId = 2, Name = "ASP.Net Unleashed", Description = "Short description here", Price = 59.99 },

                    new Product { ProductId = 3, Name = "Silverlight Unleashed", Description = "Short description here", Price = 29.99 }

                };

 

            // Mock the Products Repository using Moq

            Mock<IProductRepository> mockProductRepository = new Mock<IProductRepository>();

 

            // Return all the products

            mockProductRepository.Setup(mr => mr.FindAll()).Returns(products);

 

            // return a product by Id

            mockProductRepository.Setup(mr => mr.FindById(It.IsAny<int>())).Returns((int i) => products.Where(x => x.ProductId == i).Single());

 

            // return a product by Name

            mockProductRepository.Setup(mr => mr.FindByName(It.IsAny<string>())).Returns((string s) => products.Where(x => x.Name == s).Single());

 

            // Allows us to test saving a product

            mockProductRepository.Setup(mr => mr.Save(It.IsAny<Product>())).Returns(

                (Product target) =>

                {

                    DateTime now = DateTime.Now;

 

                    if (target.ProductId.Equals(default(int)))

                    {

                        target.DateCreated = now;

                        target.DateModified = now;

                        target.ProductId = products.Count() + 1;

                        products.Add(target);

                    }

                    else

                    {

                        var original = products.Where(q => q.ProductId == target.ProductId).Single();

 

                        if (original == null)

                        {

                            return false;

                        }

 

                        original.Name = target.Name;

                        original.Price = target.Price;

                        original.Description = target.Description;

                        original.DateModified = now;

                    }

 

                    return true;

                });

 

            // Complete the setup of our Mock Product Repository

            this.MockProductsRepository = mockProductRepository.Object;

        }

 

        /// <summary>

        /// Gets or sets the test context which provides

        /// information about and functionality for the current test run.

        ///</summary>

        public TestContext TestContext { get; set; }

 

        /// <summary>

        /// Our Mock Products Repository for use in testing

        /// </summary>

        public readonly IProductRepository MockProductsRepository;

 

        /// <summary>

        /// Can we return a product By Id?

        /// </summary>

        [TestMethod]

        public void CanReturnProductById()

        {

            // Try finding a product by id

            Product testProduct = this.MockProductsRepository.FindById(2);

 

            Assert.IsNotNull(testProduct); // Test if null

            Assert.IsInstanceOfType(testProduct, typeof(Product)); // Test type

            Assert.AreEqual("ASP.Net Unleashed", testProduct.Name); // Verify it is the right product

        }

 

        /// <summary>

        /// Can we return a product By Name?

        /// </summary>

        [TestMethod]

        public void CanReturnProductByName()

        {

            // Try finding a product by Name

            Product testProduct = this.MockProductsRepository.FindByName("Silverlight Unleashed");

 

            Assert.IsNotNull(testProduct); // Test if null

            Assert.IsInstanceOfType(testProduct, typeof(Product)); // Test type

            Assert.AreEqual(3, testProduct.ProductId); // Verify it is the right product

        }

 

        /// <summary>

        /// Can we return all products?

        /// </summary>

        [TestMethod]

        public void CanReturnAllProducts()

        {

            // Try finding all products

            IList<Product> testProducts = this.MockProductsRepository.FindAll();

 

            Assert.IsNotNull(testProducts); // Test if null

            Assert.AreEqual(3, testProducts.Count); // Verify the correct Number

        }

 

        /// <summary>

        /// Can we insert a new product?

        /// </summary>

        [TestMethod]

        public void CanInsertProduct()

        {

            // Create a new product, not I do not supply an id

            Product newProduct = new Product

                { Name = "Pro C#", Description = "Short description here", Price = 39.99 };

 

            int productCount = this.MockProductsRepository.FindAll().Count;

            Assert.AreEqual(3, productCount); // Verify the expected Number pre-insert

 

            // try saving our new product

            this.MockProductsRepository.Save(newProduct);

 

            // demand a recount

            productCount = this.MockProductsRepository.FindAll().Count;

            Assert.AreEqual(4, productCount); // Verify the expected Number post-insert

 

            // verify that our new product has been saved

            Product testProduct = this.MockProductsRepository.FindByName("Pro C#");

            Assert.IsNotNull(testProduct); // Test if null

            Assert.IsInstanceOfType(testProduct, typeof(Product)); // Test type

            Assert.AreEqual(4, testProduct.ProductId); // Verify it has the expected productid

        }

 

        /// <summary>

        /// Can we update a prodict?

        /// </summary>

        [TestMethod]

        public void CanUpdateProduct()

        {

            // Find a product by id

            Product testProduct = this.MockProductsRepository.FindById(1);

 

            // Change one of its properties

            testProduct.Name = "C# 3.5 Unleashed";

 

            // Save our changes.

            this.MockProductsRepository.Save(testProduct);

 

            // Verify the change

            Assert.AreEqual("C# 3.5 Unleashed", this.MockProductsRepository.FindById(1).Name);

        }

    }

}

 

Download the Sample project and run the tests yourself:

MoqRepositorySample.zip (691.96 kb)


Categories: ASP.Net | C# | CodeProject | Moq | MVC | Unit Testing
Posted by Williarob on Tuesday, December 15, 2009 8:17 AM
Permalink | Comments (0) | Post RSSRSS comment feed

Start Building with the Microsoft SDK for Facebook Platform

The Microsoft SDK for Facebook Platform contains rich social features and offers something for almost any kind of Facebook developer who is building with Microsoft technology, whether you're implementing Facebook Connect or are building a Web-based or desktop application. If you're one of the six million Microsoft software developers just starting to build for Facebook, you can use this SDK to make your applications more social, letting your users share and connect with their friends.

Learn more.


Posted by Williarob on Tuesday, November 24, 2009 6:45 AM
Permalink | Comments (0) | Post RSSRSS comment feed

Visio files not opening in Internet Explorer

Recently, a colleague sent me a Visio file and informed me that I should be able to view it in IE. So I double clicked on it and sure enough, IE opened and warned me that "To Help protect your security, Internet Explorer has restricted this webpage from running scripts or ActiveX controls that could access your computer. Click here for options." so I did and I clicked on "Allow blocked content". Instead of seeing the document, I was presented with a small red x, like that of a broken image. Undaunted, I thought that perhaps there is a Visio Viewer, rather like the Word and Powerpoint Viewers you can download, that would let me view Visio files, but not create them. Sure enough I found the Visio 2007 Viewer on Microsoft's site, downloaded it and installed it.

.vsd files were still associated with IE, and the results were the same. So I tracked down the Visio Viewer application in C:\Program Files (x86)\Microsoft Office\Office12; and double clicked it. "This Program can only run from within another program." Great. So, I checked the security settings in IE, and went to Manage Add-ons to see if the Visio Viewer was disabled or blocked, but it didn't even appear in the list.

With a little help from Google, I discovered that the solution was to uninstall a security update "Cumulative Security Update for ActiveX Killbits for Windows 'Your Version of Windows Here' (KB973525)"

The link to the update is here:  http://go.microsoft.com/fwlink/?LinkId=158202.  It appears that Microsoft in it's infinite wisdom accidentally put the Visio Viewer's CLSID in the kill list. Uninstalling this update, resolved the issue and I can now open Visio documents in IE.


Categories: Office
Posted by Williarob on Thursday, November 19, 2009 1:22 PM
Permalink | Comments (0) | Post RSSRSS comment feed

How to get the length (duration) of a media File in C# on Windows 7

If you have ever looked at a media file (audio or video) in the explorer window on a Windows 7 PC, you may have noticed that it displays additional information about that media file that previous versions of Windows didn't seem to have access to, for example the length/duration of a Quicktime Movie Clip:

 

Even right clicking the file and choosing Properties > Details does not give me this information on my Vista Ultimate PC. Of course, now that Windows has the ability to fetch this information, so do we as developers, through the Windows API (The DLL to Import by the way is "propsys.dll"):

        internal enum PROPDESC_RELATIVEDESCRIPTION_TYPE

        {

            PDRDT_GENERAL,

            PDRDT_DATE,

            PDRDT_SIZE,

            PDRDT_COUNT,

            PDRDT_REVISION,

            PDRDT_LENGTH,

            PDRDT_DURATION,

            PDRDT_SPEED,

            PDRDT_RATE,

            PDRDT_RATING,

            PDRDT_PRIORITY

        }

 

 

        [DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true)]

        internal static extern int PSGetNameFromPropertyKey(

            ref PropertyKey propkey,

            [Out, MarshalAs(UnmanagedType.LPWStr)] out string ppszCanonicalName

        );

 

        [DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true)]

        internal static extern HRESULT PSGetPropertyDescription(

            ref PropertyKey propkey,

            ref Guid riid,

            [Out, MarshalAs(UnmanagedType.Interface)] out IPropertyDescription ppv

        );

 

        [DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true)]

        internal static extern int PSGetPropertyKeyFromName(

            [In, MarshalAs(UnmanagedType.LPWStr)] string pszCanonicalName,

            out PropertyKey propkey

        );

However, before you rush off to play with these, you may be interested to know that Microsoft has created a great Library that showcases this and many of the other new API features of Windows 7. It's called the WindowsAPICodePack and you can get it here.

If you open the WindowsAPICodePack Solution and compile the Shell Project, it creates a nice wrapper around all the neat new system properties available through propsys.dll. Adding a reference to WindowsAPICodePack.dll and WindowsAPICodePack.Shell.dll in a console application will allow you to get the duration of just about any media file that Windows recognizes. (Of course the more codec packs you install, the more types it will recognize, I recommend The Combined Community Codec Pack to maximize your range of playable files.)

Here is a simple example showing how to get the duration of a media file in C# using this library:

namespace ConsoleApplication1

{

    using System;

 

    using Microsoft.WindowsAPICodePack.Shell;

 

    class Program

    {

        static void Main(string[] args)

        {

            if(args.Length < 1)

            {

                Console.WriteLine("Usage: ConsoleApplication1.exe [Filename to test]");

                return;

            }

 

            string file = args[0];

            ShellFile so = ShellFile.FromFilePath(file);

            double nanoseconds;

            double.TryParse(so.Properties.System.Media.Duration.Value.ToString(), out nanoseconds);

            Console.WriteLine("NanaoSeconds: {0}", nanoseconds);

            if (nanoseconds > 0)

            {

                double seconds = Convert100NanosecondsToMilliseconds(nanoseconds) / 1000;

                Console.WriteLine(seconds.ToString());

            }

        }

 

        public static double Convert100NanosecondsToMilliseconds(double nanoseconds)

        {

            // One million nanoseconds in 1 millisecond, but we are passing in 100ns units...

            return nanoseconds * 0.0001;

        }

    }

}

As you can see, the System.Media.Duration Property returns a value in 100ns units so some simple math will turn it into seconds. Download the Test Project which includes the prebuilt WindowsAPICodePack.dll and WindowsAPICodePack.Shell.dll files in the bin folder:

ConsoleApplication1.zip (218.76 kb)

For the curious, I tested this on Windows XP and as you'd expect, it didn't work:

Unhandled Exception: System.DllNotFoundException: Unable to load DLL 'propsys.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)

On Vista Ultimate SP2, it still didn't work - nanoseconds was always 0, though it didn't throw any exceptions.

For the older systems I guess we are limited to using the old MCI (Media Control Interface) API:

        using System.Runtime.InteropServices;

 

        [DllImport("winmm.dll")]

        public static extern int mciSendString(string lpstrCommand, StringBuilder lpstrReturnString, int uReturnLength, int hwndCallback);

 

        [DllImport("winmm.dll")]

        private static extern int mciGetErrorString(int l1, StringBuilder s1, int l2);

 

        private void FindLength(string file)

        {

            string cmd = "open " + file + " alias voice1";

            StringBuilder mssg = new StringBuilder(255);

            int h = mciSendString(cmd, null, 0, 0);

            int i = mciSendString("set voice1 time format ms", null, 0, 0);

            int j = mciSendString("status voice1 length", mssg, mssg.Capacity, 0);

            Console.WriteLine(mssg.ToString());

        }

Which works fine for .mp3 and .avi and other formats that play natively in Windows Media Player, but even with a codec pack installed, it doesn't work on Quicktime or .mp4 files, where the new Windows 7 API did.


Categories: C# | CodeProject | Windows | Windows 7
Posted by Williarob on Wednesday, October 21, 2009 12:14 PM
Permalink | Comments (0) | Post RSSRSS comment feed

New and Improved Microsoft AntiXss 3.1.

Until now, the preferred way to selectively allow only certain HTML tags like <b> and <i> was to regex the input to ensure it contained only valid Unicode letter and number characters and those specified tags, something like this:

if (!Regex.IsMatch(input, @"^([\p{L}\p{N}'\s]|<b>|</b>|<i>|</i>){1,40}$")) throw new Exception();

This approach will prevent all unwanted tags, but it will also prevent all attributes on the allowed tags. Sometimes this is good – attackers can add malicious script to onmouseover attributes of <b> and <i> tags – but again, sometimes this is overkill and blocks the use of benign attributes like lang or title. It would be theoretically possible to extend the regular expression to allow these attributes, as well as other safe HTML tags and their attributes, but realistically that would be an incredibly difficult regex both to develop and maintain.

AntiXss 3.1 takes care of all of this logic for you, using the same whitelist approach: it filters the input using a list of known good tags and attributes and strips out all other text. Simply pass the untrusted input through the AntiXss.GetSafeHtml or GetSafeHtmlFragment method to sanitize it:

string output = AntiXss.GetSafeHtml(input);

I strongly encourage everyone to download the new AntiXss 3.1 and incorporate it into your applications starting today. It’s a very effective defense, especially when used in conjunction with the output encoding functionality that’s been a part of AntiXss from the beginning.

Read the Full Article Here.

Download AntiXss 3.1 from Microsoft.


Posted by Williarob on Tuesday, September 29, 2009 1:50 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Shepherds Pie Recipe

Ingredients

2 tsp1 Worcestershire Sauce

1/4 tsp thyme

1/4 tsp pepper

1/4 tsp salt

1/8 tsp Paprika

2 tbsp2 Ketchup

1 tsp Parsley

1 Medium Onion

1 lb3 Ground Lamb (If you use Beef you're making Cottage Pie, not Shepherds Pie!)

2 Large Carrots

1 24oz4 Package Refrigerated Garlic Mashed Potatoes5

1 packet Gravy mix6

1 Cup7 Water

Directions

Peel & Chop the onion, then combine it and the ground Lamb in a large skillet over a medium heat. Meanwhile, peel and chop the carrots, then add them to the skillet and cover it, but don't forget to stir it occasionally while you work on the sauce.

In a small saucepan, add the gravy mix, Worcestershire Sauce, thyme, salt, pepper, paprika, ketchup, parsley and water. Mix it all up then put it on a high heat and keep stirring it until it boils (if you stop stirring it may get lumpy). When it boils, remove it from the heat. Assuming the meat is brown at this point, drain excess juices from the skillet and pour in the sauce. Cover the skillet and let it simmer over a low heat while you heat the potatoes.

Pour the contents of the skillet into a casserole or baking dish and add the potatoes evenly across the top, fluffing with a fork. I usually place the baking dish on top of a foil covered baking sheet to catch any drips, but that's because my dish is always packed to the very top with juicy goodness and it tends to make a small mess in the oven. Broil (Grill for those in Britain) until the potatoes are golden brown on top and a little crispy (about 5 mins). Serves 4.

Notes

  1. tsp = Teaspoon
  2. tbsp = Tablespoon
  3. 1 Pound (lb) = 0.45 kilograms (453.59 grams)
  4. 24 ounces is equal to 0.68 kilograms (680.39 grams)
  5. I use Bob Evans Garlic Mashed potatoes.
  6. An oxo/bovril cube gravy would be better, but very hard to come by in the US, so I use McCormick Brown Gravy mix which is a pretty good substitute.
  7. 1 cup is equal to 236.59 milliliters (ml)

Categories: recipes
Posted by Williarob on Monday, September 21, 2009 11:00 AM
Permalink | Comments (0) | Post RSSRSS comment feed