Posts Tagged ‘cSharp’

Return To Sender


Straight up, the sender object that gets passed in as an argument in all .Net events kicks ass. If you already aren’t using it for copious amounts of cool stuff, you should be. Sender has so many uses it is truly silly. This article will demonstrate a couple of the common ones for your use.


Before the Code a Note


One of the few functionality items that vb.net has over c# is the handles language feature. On the .aspx page you create your linkbutton, etc. and on the code behind you simply select that object from your little drop down at the top of the page, select the event you want and away you go, you get a nicely formatted event handler for the click, or other event, of your control.


C#, on the other hand, requires us to write the handler events ourselves and reference them in our aspx code as well as code behind. Of course, one could throw up design view and, in the properties dialog, click the events button and double click an event to create a nicely formatted event, but who uses design view? I find design view insulting most days and only mildly annoying on other days; the last place I want to go to create my events is design view. Due to this little bugaboo, I write all my handlers myself. The problem with this is that sometimes I don’t know what the correct eventargs object is. My quick tip on this is that, if you don’t know what your eventargs object is, you can just throw in a junk reference, run your code locally and get a nice YSOD stating that your current eventargs object cannot be casted to the correct type of object. So, if anyone was ever wondering when a good time to purposely put an error in code…

//So, if you didn't remember that the eventargs object type for the
//rowdatabound event of a gridview was rowdataboundeventargs, throw down this:
protected void GridView1_RowDatabound(object sender, string e) {

}

//the error received will let you know the correct eventargs to use.


Less Read, More Code


A common issue one reads about is getting access to a gridviewrow from a linkbutton click inside that row. With sender, SIMPLE!

//Condensed
protected void linkbuttonInRow_Click(object sender, Eventargs e) {
GridViewRow gvr = (GridViewRow)(((linkbutton)sender).parent.parent);
}

//Simpler
protected void linkbuttonInRow_Click(object sender, Eventargs e) {
linkbutton lb = sender as linkbutton;

//in GridViewRows, going up two levels from controls in a cell will 
//give you a gridviewrow. In a repeater it's one level.
GridViewRow = (GridViewRow)lb.parent.parent;
}


How about if you have multiple linkbuttons on a page and they all do the same thing but you want to recognize which button the click came from:

protected void linkbuttonInRow_Click(object sender, Eventargs e) {
//I love using "as" in c#, makes me feel like I'm writing VB
Linkbutton lb = sender as linkbutton;

switch(lb.ID) {
case "button1":
response.write("Look I picked button 1 and didn't put a semicolon on this line because I am still digging on the vb")
break;
case "button2:
response.write("I picked button 2 and am now feeling the c# again.");
break;
}
}


If you can think of other super creative ways of using sender, let me know.

Free C# Snippets For All!

I think I had been developing for over 4 years, or so, in .net before truly figuring out snippets. If you haven’t used them before, now is the time. Snippets allow you to type in a quick couple letters and expand into a complete set of code that you can use. The great thing about them is they are customizable and super simple to build. While the snippets that ship with VS are great — see the “prop” snippet, there is a great deal that one can add.

Creating Your Own Snippets

First let’s try using an existing snippet. The “prop” snippet ships with VS 2008 and can be used by simply typing in “prop” on an empty line in code. Once you have typed in the snippet, hit the “tab” key twice, the snippet will expand into:

public int MyProperty { get; set; }

Notice, you get a nicely formatted little simple get and set statement. The “prop” snippet is great for building classes, as you can just tab tab away and your class will be built before you know it. What happens, however, if you are building a web page and your simple get and set methods lose their values every time you post the page back to the server?

In the case of aspx page variables, the general way of persisting data through postback is to use viewstate. For all you folks that are storing variables in hidden fields out there, this is for you. If you have an editor page for, let’s say, a user — and who hasn’t built one of these — you will most likely store a user id somewhere. The right way to go about this is not by storing it in a hiddenfield. For more reasons than I care to mention, let’s just assume that storing your data in viewstate is superior to hidden fields and get to the solution.

public long UserID {
	get {
		//get the viewstate variable and put it into an object variable
		object o = ViewState["_UserID"];
		
		//if the object is not null return the casted object
		if(o != null) {
			return (long)o;
		}
		
		//ok, we have no value, let's just return -1
		return -1;
	}
	set {
		//Set our value here.
		ViewState["_UserID"] = value;
	}	
}

So, as you can see we create a page level viewstate variable that will work across postbacks. The nice thing is the simplicity of using the variable, once it has been created. It is far more simple to type in “LoadAUserRecord(UserID);” then the multiple lines of code to check a hidden field’s value property. The problem with the viewstate property, the way I have created it, is how many lines it takes to write the property code. The snippet, then, will make our lives a bit easier.

Dissecting a Snippet

Your customized snippets are stored in “C:\Users\YourName\Documents\Visual Studio 2008\Code Snippets\Visual C#\My Code Snippets” by default. If you open up that directory, you will see a bunch of .snippet files. You can open them up directly in VS. You will notice that they are XML formatted and should appear to be pretty simple to edit.

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
	<CodeSnippet Format="1.0.0">
		<Header>
			<Title>propv</Title>
			<Shortcut>propv</Shortcut>
			<Description>Code snippet for setting up a page level ViewState Property</Description>
			<Author>Jason Janofsky</Author>
			<SnippetTypes>
				<SnippetType>Expansion</SnippetType>
			</SnippetTypes>
		</Header>
		<Snippet>
			<Declarations>
				<Literal>
					<ID>type</ID>
					<ToolTip>Type</ToolTip>
					<Default>TypeName</Default>
				</Literal>
				<Literal>
					<ID>property</ID>
					<ToolTip>Property name</ToolTip>
					<Default>MyProperty</Default>
				</Literal>
				<Literal>
					<ID>DefaultReturn</ID>
					<ToolTip>Default Return</ToolTip>
					<Default>null</Default>
				</Literal>
			</Declarations>
			<Code Language="csharp">
				<![CDATA[
			public $type$ $property$ {
				get {
					object o = ViewState["_$property$"];
					if(o != null) {
						return ($type$)o;
					}
					return $DefaultReturn$;
			}
			set {
				ViewState["_$property$"] = value;
			}	
			}
			$end$]]>
			</Code>
		</Snippet>
	</CodeSnippet>
</CodeSnippets>

As you can see, you just create properties that will take values upon snippet expansion and decorate the variable names with a “$” character in front, and in back of, your property name. Simple eh? Once you have saved your snippet file into your snippet directory, it should be immediately available for use.

Some Snippets For You

So, while I have completely run amok building snippets for myself doing all sorts of DAL and CRUD code for myself, the code for that stuff is very much specialized for what I do on a daily basis.. not for what you do. I do, however, have some snippets that I use all the time that I have removed some customizing and left more open for your use.

propr – A Quick Request.Querystring Page Level Property

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
	<CodeSnippet Format="1.0.0">
		<Header>
			<Title>propr</Title>
			<Shortcut>propr</Shortcut>
			<Description>Code snippet for setting up a page level Querystring Property</Description>
			<Author>Jason Janofsky</Author>
			<SnippetTypes>
				<SnippetType>Expansion</SnippetType>
			</SnippetTypes>
		</Header>
		<Snippet>
			<Declarations>
				<Literal>
					<ID>type</ID>
					<ToolTip>Type</ToolTip>
					<Default>TypeName</Default>
				</Literal>
				<Literal>
					<ID>property</ID>
					<ToolTip>Property name</ToolTip>
					<Default>MyProperty</Default>
				</Literal>
				<Literal>
					<ID>DefaultReturn</ID>
					<ToolTip>Default Return</ToolTip>
					<Default>null</Default>
				</Literal>
			</Declarations>
			<Code Language="csharp">
				<![CDATA[
			public $type$ rq$property$ {
				get {
					object o = Request.Querystring["$property$"];
					$type$ _$property$;
					if(o != null) {
						$type$.TryParse(o.ToString(), out _$property$);
					}
					return _$property$;
				}	
			}
			$end$]]>
			</Code>
		</Snippet>
	</CodeSnippet>
</CodeSnippets>

propm – A Custom Class Variable with Lazy Loading Abilities

Tip: Lazy loading is the process of creating an object that does not fill itself up until you need it. If you have a large object with properties that contain other objects, it might be smarter not to fill up that property until you explicitly need it. Linq uses lazy loading to prevent too many database calls every time one pulls up an object.

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
	<CodeSnippet Format="1.0.0">
		<Header>
			<Title>propm</Title>
			<Shortcut>propm</Shortcut>
			<Description>Code snippet for setting up a private and public property with a lazy loader connection</Description>
			<Author>Jason Janofsky</Author>
			<SnippetTypes>
				<SnippetType>Expansion</SnippetType>
			</SnippetTypes>
		</Header>
		<Snippet>
			<Declarations>
				<Literal>
					<ID>type</ID>
					<ToolTip>Type</ToolTip>
					<Default>TypeName</Default>
				</Literal>
				<Literal>
					<ID>property</ID>
					<ToolTip>Property name</ToolTip>
					<Default>MyProperty</Default>
				</Literal>
				<Literal>
					<ID>method</ID>
					<ToolTip>DB Method Name</ToolTip>
					<Default>GetByID</Default>
				</Literal>
			</Declarations>
			<Code Language="csharp">
				<![CDATA[private $type$ _$property$;
			public $type$ $property$ {
				get {
					if(_$property$ == null) {
						Fill$property$();
					}
					return _$property$;
			}
			set {
				_$property$ = value;
			}	
			}
			public void Fill$property$() {
				_$property$ = 
			}
			$end$]]>
			</Code>
		</Snippet>
	</CodeSnippet>
</CodeSnippets>

par – A Snippet For Quickly Adding Parameters and Values to DB Calls.

I usually use approximately the same name for my connections and, therefore, normally do not have the “connection” variable in there, my own snippet is hardcoded for “con” instead of “$Connection$”.

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
	<CodeSnippet Format="1.0.0">
		<Header>
			<Title>par</Title>
			<Shortcut>par</Shortcut>
			<Description>Code snippet for an automatically setup command parameter addwithvalue</Description>
			<Author>Jason Janofsky</Author>
			<SnippetTypes>
				<SnippetType>Expansion</SnippetType>
			</SnippetTypes>
		</Header>
		<Snippet>
			<Declarations>
				<Literal>
					<ID>Connection</ID>
					<ToolTip>The name of your connection variable</ToolTip>
					<Default>Connection</Default>
				</Literal>
				<Literal>
					<ID>type</ID>
					<ToolTip>ParamName</ToolTip>
					<Default>string</Default>
				</Literal>
				<Literal>
					<ID>property</ID>
					<ToolTip>Property name</ToolTip>
					<Default>MyProperty</Default>
				</Literal>
			</Declarations>
			<Code Language="csharp"><![CDATA[$Connection$.Command.Parameters.AddWithValue("@$type$", $property$);$end$]]>
			</Code>
		</Snippet>
	</CodeSnippet>
</CodeSnippets>

That’s It, But….

I would love to be let in on your favorite snippets, drop me a line and I will be happy to include your snippets on this article.

Multi Presentation (tenancy) Websites for c#

So you want to build a single web application that can be used multiple times and deployed once? Have you ever built a site, only to have another customer request to have it replicated with some minor changes? One possibility is to copy the files into another directory, make changes as needed and deploy. Of course a month later your customer requests a bug fix or functionality changes to approximately the same file that now exists in two sites. Wouldn’t it be nice to have one set of files that you could change and have the change reflected on all your applications? Creating a multi presentation project might be for you.

In a Nutshell (the short version)

To make a fairly long story short, we will be creating an httpmodule that, upon being called checks to see if an application level variable for the application version has been set. If it finds that the application setting variable is null, the system will attempt to look up a record for itself inside the web.config file using the Request.URL.Host string. If the string exists as an app setting, we can grab the value (in this example I will store a numeric ID to pull up a database record for settings. Once you have your settings the only question is how to get all those sites pointed at the same set of files. To get this accomplished I use IIS to create multiple sites pointed at the same set of files with host header records to store what the incoming host name is for the site.

Let’s Break This Down…

Since this type of site takes some doing, lets go over the steps first:

  1. Create your project
  2. Create an CustomAppSettings class to store some basic information about the different versions of your site.
  3. Create a method to fill that CustomAppSettings class per site.
  4. Modify your site based on the version of the site your users have requested.
  5. Setup IIS to handle the different sites.

Create your project

There isn’t really anything tricky going on in the early stages here. Since we will be using the settings in our web.config file, go ahead and add a System.Configuration reference to your project. The System.Configuration reference will allow us to make calls to ConfigurationManager.AppSettings.
Adding the System.Configuration reference

Create an AppSettings class to store some basic information about the different versions of your site.

So now we want to create a class that will store some basic information like an application id, the site’s address, and any other settings that one might need for the application version to run independently of the other applications you will be hosting.

	public class CustomAppSettings
	{
		public int ID { get; set; }
		
		//This property will be to store the short name of our application version
		//for a template directory, if we choose to use one.
		public string ApplicationReference { get; set; }
		
		//This is the hostname our application is coming in on.
		public string HostName { get; set; }
	}	

This app settings class can be expanded significantly to include all sorts of specific settings like you would normally put in a single presentation project like SMTP servers, etc.

Create a method to fill that CustomAppSettings class per site.

We now need a method to store our CustomAppSettings into our Application object for easy access later. In a recent post (Storing User Information in Session for c#) I wrote up a class called GlobalVars that makes accessing untyped storage objects like Application and Session easier. For this project, let’s use the GlobalVars class to create a placeholder for our CustomAppSettings Object.

	public static class GlobalVars
	{
		public static CustomAppSettings CurrentAppSettings
		{
			get
			{
				object o = HttpContext.Current.Application["_CurrentAppSettings"];
				if (o != null)
				{
					//one could potentially do thier setting of the currentapp variable here
					//using the HTTPContext.Current object.
					return (CustomAppSettings)o;
				} 
				return null;
			}
		}
	}

With this code we can just type in GlobalVars.CurrentAppSettings.HostName to easily access our CurrentAppSettings whenever we need it.

The next part of this step is to get the CurrentAppSettings application level variable stored so it doesn’t return null all the time. To do this, we are going to create an httpmodule.

public class ConfigureApp : IHttpModule
{
	public void Dispose()
	{

	}

	public void Init(System.Web.HttpApplication context)
	{
		context.BeginRequest += context_BeginRequest;
	}

	public void context_BeginRequest(object sender, EventArgs e)
	{
		HttpApplication application = (HttpApplication)sender;
		Uri url = application.Context.Request.Url;

		//plug on the application setting the first time someone pulls up the app.
		if (CurrentAppSettings == null)
		{
			//check to see if our value exists in the web.config file.
			string config = ConfigurationManager.AppSettings[url.Host.ToString().ToLower()];
			
			//Setup a long variable for our try parse.
			long holderid = 1;
			if (!string.IsNullOrEmpty(config))
			{
				Int64.TryParse(config, out holderid);
				if (holderid <= 0)
				{
					holderid = 1;
				}
			}
			
			//this is where we would load our id from the db and set
			//the different settings.
			CurrentAppSettings = LoadMyApp(holderid);
		}
	}
}

While building the httpmodule you might have noticed that there is a call in there to the web.config. We actually need to make a couple different changes to the web.config file, first the reference to run the new httpmodule in our web app and, second, the application url references. Let’s take a look at those now.

Registering the http module

	<system.web>
		<httpModules>
		  <add name="ConfigureApp" type="ConfigureApp" />
		</httpModules>
	  </system.web>
	<system.webServer>
		<modules>
		  <add name="ConfigureApp" type="ConfigureApp" />
		</modules>
	</system.webServer>

Note that there are two different registrations here. The first is for iis6, the second for iis7. You will also note that the name and type both reference the name of our class from the http module.

Creating Our Application References

<configuration>
	<appSettings>
		<add key="MySite1.MyDomain.com" value="1"/>
		<add key="www.MySite1.MyDomain.com" value="1"/>
		<add key="MySite2.MyDomain.com" value="2"/>
		<add key="localhost" value="1"/>
	</appSettings>
</configuration>

We have added a few different settings into our appSettings section here to let the httpmodule know which app should point at which application. An important note here is that any individual host header record one makes into IIS needs to be entered here as, without it, the record won’t pull up from the web.config. You will also note that I have included a reference for “localhost”. If you are doing work on the different versions of the site in Visual Studio, the local web server will show up “localhost”. You can alter which version of the site you want to work on by changing the value of the localhost app setting.

Create the Site in IIS

We are on the home stretch now. Open up IIS and add a web site.
Adding a new web site
Note we are using a host header record here. Point your new web site to your file set and hit Ok. Your new web site will be up on that new host header record. To put up more sites on the same set of files, rinse and repeat!

Image Resizing and Cropping in C#

So the other day I put up a c# wrapper for ffmpeg. The wrapper I built is a part of my own asset storage system which I guess I will probably be putting up in chunks over the next few weeks. Today I have decided to put up my image resizing tools for your use.

Resize Now or Resize Later?

There are a couple theories to image resizing, should the image be resized at the point it gets uploaded, or at the point it gets sent out to the client?

Each method has advantages and disadvantages. Resizing at upload is good because it speeds up time to delivery as the processor has already done the resizing, additionally, the image is only resized once, vs. if the image is resized every time it is downloaded, it could be resized countless times. The problem with resizing at upload, however, is that it is now resized — you can’t un-resize it. Resizing at download then, is more processor intensive as it is resized every time someone requests a different size (one could probably get around some of this with caching, but the no matter how you slice it, it is still more processor intensive). The nice thing is that based on querystring or, however you pass the resizer data, you get a customized image size.

I have my own theory on this. If one has a ton of storage space (which most of us do have available to us now adays) it makes sense to resize in some basic sizes at upload while keeping the original as well for future resizing, if needed. Most good graphic designers break all their pages up into grids of magic 3rds anyways and would, generally, have ideas about their perfect image sizes for thumbnails, swatches, large previews, etc..

In another post I will detail how I get all of these different sizes and what not stored for use, but — for now — I will just give you the tools with which to do resizing and cropping in one spot.


//Overload for crop that default starts top left of the image.
public static System.Drawing.Image CropImage(System.Drawing.Image Image, int Height, int Width)
{
	return CropImage(Image, Height, Width, 0,0);
}

//The crop image sub
public static System.Drawing.Image CropImage(System.Drawing.Image Image, int Height, int Width, int StartAtX, int StartAtY)
{
	Image outimage;
	MemoryStream mm = null;
	try
	{
		//check the image height against our desired image height
		if (Image.Height < Height) {
			Height = Image.Height;
		}
		
		if (Image.Width < Width) {
			Width = Image.Width;
		}
		
		//create a bitmap window for cropping
		Bitmap bmPhoto = new Bitmap(Width, Height, PixelFormat.Format24bppRgb);
		bmPhoto.SetResolution(72, 72);
		
		//create a new graphics object from our image and set properties
		Graphics grPhoto = Graphics.FromImage(bmPhoto);
		grPhoto.SmoothingMode = SmoothingMode.AntiAlias;
		grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
		grPhoto.PixelOffsetMode = PixelOffsetMode.HighQuality;
		
		//now do the crop
		grPhoto.DrawImage(Image, new Rectangle(0, 0, Width, Height), StartAtX, StartAtY, Width, Height, GraphicsUnit.Pixel);
		
		// Save out to memory and get an image from it to send back out the method.
		mm = new MemoryStream();
		bmPhoto.Save(mm, System.Drawing.Imaging.ImageFormat.Jpeg);
		Image.Dispose();
		bmPhoto.Dispose();
		grPhoto.Dispose();
		outimage = Image.FromStream(mm);

		return outimage;
	}
	catch (Exception ex)
	{
		throw new Exception("Error cropping image, the error was: " + ex.Message);
	}
}

//Hard resize attempts to resize as close as it can to the desired size and then crops the excess
public static System.Drawing.Image HardResizeImage(int Width, int Height, System.Drawing.Image Image)
{
	int width = Image.Width;
	int height = Image.Height;
	Image resized = null;
	if (Width > Height)
	{
		resized = ResizeImage(Width, Width, Image);
	}
	else
	{
		resized = ResizeImage(Height, Height, Image);
	}
	Image output = CropImage(resized, Height, Width);
	//return the original resized image
	return output;
}

//Image resizing
public static System.Drawing.Image ResizeImage(int maxWidth, int maxHeight, System.Drawing.Image Image)
{
	int width = Image.Width;
	int height = Image.Height;
	if (width > maxWidth || height > maxHeight)
	{
		//The flips are in here to prevent any embedded image thumbnails -- usually from cameras
		//from displaying as the thumbnail image later, in other words, we want a clean
		//resize, not a grainy one.
		Image.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX);
		Image.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipX);

		float ratio = 0;
		if (width > height)
		{
			ratio = (float)width / (float)height;
			width = maxWidth;
			height = Convert.ToInt32(Math.Round((float)width / ratio));
		}
		else
		{
			ratio = (float)height / (float)width;
			height = maxHeight;
			width = Convert.ToInt32(Math.Round((float)height / ratio));
		}

		//return the resized image
		return Image.GetThumbnailImage(width, height, null, IntPtr.Zero);
	}
	//return the original resized image
	return Image;
}

Extenders

Yesterday I posted on extenders after seeing this great post http://blog.wekeroad.com/2010/01/20/my-favorite-helpers-for-aspnet-mvc. It strikes me that so much of what we do can be thrown into extenders. The code above, for example could really simply be used as extenders for you existing System.Drawing.Imaging objects.

public static string Crop(this System.Drawing.Imaging ThisImage, int maxHeight, int maxWidth)
{
    return CropImage(ThisImage, maxHeight, maxWidth);
}

public static string Resize(this System.Drawing.Imaging ThisImage, int Height, int Width)
{
    return ResizeImage(Width, Height, ThisImage);
}

public static string HardResize(this System.Drawing.Imaging ThisImage, int Height, int Width)
{
    return HardResizeImage(Width, Height, ThisImage);
}

More C# Extenders

Cool article today over at http://blog.wekeroad.com/2010/01/20/my-favorite-helpers-for-aspnet-mvc about extenders. If you didn’t already know, extenders are static methods that work off the end of objects like special little hangars on. They are like little upgrades you get in a video game — like double jump in Shadow Complex.

Since I loved the article so much I figured I would add some of my collection to the list. I cannot take credit for all of these, as I have collected them over a long time — I might have written half of them.

DateTime

//The w3c string format is used for RSS feeds.
public static string ToW3CString(this DateTime ThisDate)
{
    //Return the datetime in the W3C standard format
    return String.Format("{0:yyyy-MM-ddTHH:mm:ss.fffzzz}", ThisDate);
}

public static DateTime FirstDayOfWeek(this DateTime dt) {
	int diff = dt.DayOfWeek - DayOfWeek.Sunday; 
	if (diff < 0) 
	{ 
			diff += 7; 
	} 
	return dt.AddDays(-1 * diff).Date;
}

public static DateTime LastDayOfWeek(this DateTime dt) {
	return dt.FirstDayOfWeek().AddDays(6);
}

Simple first and last day of week for a specific date. Like, for February 11, 2010 the first day of the week was the 7th.

DataTable

		
public static bool HasColumn(this IDataRecord dr, string columnName)
{	
	for (int i=0; i < dr.FieldCount; i++)
	{
		if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
			return true;        
	}
	return false;
}

Boy, this one has been sitting around for a while. A simple check to see if an untype dataset or table has a specific column.

Strings

//One could probably do more to strip MS Word stuff, etc..
public static string StripHTML(this string str)
{
	if (!string.IsNullOrEmpty(str)) {
		string pattern = "<.*?>";
        string str = Regex.Replace(htmlString, pattern, string.Empty);
        pattern = "&nbsp;";
        str = Regex.Replace(str, pattern, string.Empty);
        return str;
	}
	return string.Empty;
}

public static string CapitalizeWords(this string str)
{
    //Capitalize the words
    if (str == null)
        return str;
    if (str.Length == 0)
        return str;

    StringBuilder Result = new StringBuilder(str);
    Result[0] = char.ToUpper(Result[0]);
    for (int i = 1; i < Result.Length; ++i)
    {
        if (char.IsWhiteSpace(Result[i - 1]))
            Result[i] = char.ToUpper(Result[i]);
        else
            Result[i] = char.ToLower(Result[i]);
    }
    return Result.ToString();
}

//Cleans up text to make a nice little url string
public static string ToURLItem(this string str)
{
    //Make the item a URL item
    if (!string.IsNullOrEmpty(str))
    {
        return str.Replace("™", "")
                     .Replace("&trade;", "")
                     .Replace("©", "")
                     .Replace("&copy;", "")
                     .Replace(".", "")
                     .Replace(":", "")
                     .Replace("?", "")
                     .Replace("!", "")
                     .Replace("'", "")
                     .Replace("\"", "")
                     .Replace("/", "")
                     .Replace("&", "")
                     .Replace("  ", " ")
                     .Replace(" ", "-");
    }
    else
    {
        return string.Empty;
    }
}

Address Geocoding with C#

Sure an address class is simple, create a few properties for city, state, zip code, and away you go. Wouldn’t it be nice to also have your address automatically be able to geocode itself for automatic googlemapping at a later date? This article describes just how this is accomplished using c#.

Getting Started

Get yourself an API Key by going to: http://code.google.com/apis/maps/signup.html.

This article will use a bit of xml to parse the results that google will send out, so it might be good, if you have not used xml before to check out the MSDN Documentation

Let’s check out the namespaces we will be using:

//For getting our settings from our app.config or web.config file
using System.Configuration;

//For the streamreader object we will be using to get our results from the goog.
using System.IO;

//For reading the results that the Goog returns.
using System.Xml;

//For our webclient to contact Google.
System.Net;

You will need to add a reference to System.Configuration into your project so that the System.ConfigurationManager object will have the AppSettings object attached to it.

Once we have our imports all squared away we will need to get our class and basic properties all setup.

public class Address
{
	//These are abbreviated property declarations as we do not need the private
	//variables for these simple properties.
	public string Latitude { get; set; }
	public string Longitude { get; set; }
	public string Line1 { get; set; }
	public string Line2 { get; set; }
	public string City { get; set; }
	public string State { get; set; }
	public string Zip { get; set; }
	public string Country { get; set; }

	//whereas this property is a read only and, therefore, we need some way to
	//set our property from inside the class without someone outside the object
	//being able to overwrite.
	private LatLngAccurateToTypes _LatLngAccuracy = 0;
	public LatLngAccurateToTypes LatLngAccuracy
	{
		get { return _LatLngAccuracy; }
	}

Quick Tip: The prop snippet.

I love snippets; I use them all the time. If you type in “prop” and hit the tab key twice, a nicely formatted property will appear before you for your quick entry.

Add Some Flavor

To make our address class a bit more functional, we are going override our ToString method to give us a nicely formatted address string.

	public override string ToString()
	{
		//Using the stringbuilder gives us better performance than stinky concatenation.
		System.Text.StringBuilder sb = new System.Text.StringBuilder();

		//Assume we have a line 1
		sb.AppendLine(Line1);

		//now check if the string is null or empty before appending
		if (!string.IsNullOrEmpty(Line2)) sb.AppendLine(Line2);

		//one could expand upon this to check for values in the other properties.
		sb.Append(City);
		sb.Append(", ");
		sb.Append(State);
		sb.Append(" ");
		sb.Append(Zip);
		sb.Append(" ");
		sb.Append(Country);

		//send out our new string
		return sb.ToString();
	}

Ok, Let’s GeoCode

Even though we can assume that Google already knows what we are about to send them, we can still assume that they would like to be asked, so we will now send our data to Google for GeoCoding.

	public void GeoCode()
	{
		//Setup a streamreader for retrieving data from Google.
		//This is line that requires System.IO.
		StreamReader sr = null;

		//Get the maps key in the web config file.
		//This is what we needed the reference to System.Configuration for.
		string mapskey = ConfigurationManager.AppSettings["GoogleMapsAPIKey"];

		//Check to see if our maps key exists
		if (string.IsNullOrEmpty(mapskey))
		{
			//if the key does not exist, we have an error so throw an exception.
			throw new Exception("No valid google maps api key to use for geocoding.  Please add an app key named \"GoogleMapsAPIKey\" to the web.config file.");
		}

		//Create the url string to send our request to googsie.
		string url = string.Format("http://maps.google.com/maps/geo?q={0} +{1} +{2} +{3} +{4}&output=xml&oe=utf8&sensor=false&key={5}", this.Line1, this.City + ", ", this.State, this.Zip, this.Country, mapskey);

		//Create a web request client.
		//This is where the System.Net import is used.
		WebClient wc = new WebClient();

		//Even though logic would tell us that having a site outage is a bit evil and Google is never evil,
		//therefore Google will never have a site outage, we will still throw a try catch around our call
		//just in case Google does become evil for a few minutes on some random day.
		try
		{
			//retrieve our result and put it in a streamreader
			sr = new StreamReader(wc.OpenRead(url));
		}
		catch (Exception ex)
		{
			//This is where we can assume they were evil.
			throw new Exception("An error occured while retrieving GeoCoded results from Google, the error was: " + ex.Message);
		}

		try
		{
			//Now that we have the results, lets parse the returned data with an xmltextreader.
			//Why an XMLTextReader vs. An XMLDocument?  Simple, XMLTextReaders are faster and this is
			//Simple enough not to warrant a full XMLDocument.
			XmlTextReader xtr = new XmlTextReader(sr);

			bool coordread = false;
			bool accread = false;

			//Start reading our xml document.
			while (xtr.Read())
			{
				xtr.MoveToElement();
				switch (xtr.Name)
				{
					case "AddressDetails": //Check for the address details node
						while (xtr.MoveToNextAttribute())
						{
							switch (xtr.Name)
							{
								case "Accuracy": //move into the accuracy attrib and....
									if (!accread)
									{
										//get the accuracy, convert it to our enum and save it to the record
										this._LatLngAccuracy = (LatLngAccurateToTypes)Convert.ToInt32(xtr.Value);
										accread = true;
									}
									break;
							}
						}
						break;
					case "coordinates": //the coordinates element
						if (!coordread)
						{
							//move into the element value
							xtr.Read();

							//split the coords and then..
							string[] coords = xtr.Value.Split(new char[] { ',' });

							//save the values
							Longitude = coords[0];
							Latitude = coords[1];

							//finally, once this has been done, we don't want the process to run again on the same file
							coordread = true;
						}
						break;
				}
			}
		}
		catch (Exception ex)
		{
			throw new Exception("An error occured while parsing GeoCoded results from Google, the error was: " + ex.Message);
		}
	}

Ok, so there we go, we have now GeoCoded our address for saving into our database or throwing onto a dynamic Google map. There is only one thing we are missing. Google returns an enum of accuracy types for their results. If our address only contains a city, the GeoCode will be far less accurate than if we were to put in a full address. The enum below helps us to track that.

	public enum LatLngAccurateToTypes : int
	{
		Unknown = 0,
		Country = 1,
		Region = 2,
		SubRegion = 3,
		Town = 4,
		PostCode = 5,
		Street = 6,
		Intersection = 7,
		Address = 8,
		Premises = 9
	}

Full Source

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Xml.Linq;
using System.Net;
using System.Web;
using System.Configuration;
using System.IO;
using System.Xml;

public class Address
{
	public string Latitude { get; set; }
	public string Longitude { get; set; }
	public string Line1 { get; set; }
	public string Line2 { get; set; }
	public string City { get; set; }
	public string State { get; set; }
	public string Zip { get; set; }
	public string Country { get; set; }

	private LatLngAccurateToTypes _LatLngAccuracy = 0;
	public LatLngAccurateToTypes LatLngAccuracy
	{
		get { return _LatLngAccuracy; }
	}

	public override string ToString()
	{
		System.Text.StringBuilder sb = new System.Text.StringBuilder();
		sb.AppendLine(Line1);
		if (!string.IsNullOrEmpty(Line2)) sb.AppendLine(Line2);
		sb.Append(City);
		sb.Append(", ");
		sb.Append(State);
		sb.Append(" ");
		sb.Append(Zip);
		sb.Append(" ");
		sb.Append(Country);
		return sb.ToString();
	}

	public Address()
	{
	}

	public void GeoCode()
	{
		//get the maps key in the web config file
		string mapskey = ConfigurationManager.AppSettings["GoogleMapsAPIKey"];
		//setup a streamreader for retrieving data from Google.
		StreamReader sr = null;

		//Check to see if our maps key exists
		if (string.IsNullOrEmpty(mapskey))
		{
			throw new Exception("No valid google maps api key to use for geocoding.  Please add an app key named \"GoogleMapsAPIKey\" to the web.config file.");
		}

		//Create the url string to send our request to googs.
		string url = string.Format("http://maps.google.com/maps/geo?q={0} +{1} +{2} +{3} +{4}&output=xml&oe=utf8&sensor=false&key={5}", this.Line1, this.City + ", ", this.State, this.Zip, this.Country, mapskey);

		//Create a web request client.
		WebClient wc = new WebClient();

		try
		{
			//retrieve our result and put it in a streamreader
			sr = new StreamReader(wc.OpenRead(url));
		}
		catch (Exception ex)
		{
			throw new Exception("An error occured while retrieving GeoCoded results from Google, the error was: " + ex.Message);
		}

		try
		{
			//now lets parse the returned data as an xml
			XmlTextReader xtr = new XmlTextReader(sr);
			bool coordread = false;
			bool accread = false;
			while (xtr.Read())
			{
				xtr.MoveToElement();
				switch (xtr.Name)
				{
					case "AddressDetails": //Check for the address details node
						while (xtr.MoveToNextAttribute())
						{
							switch (xtr.Name)
							{
								case "Accuracy": //move into the accuracy attrib and....
									if (!accread)
									{
										//get the accuracy, convert it to our enum and save it to the record
										this._LatLngAccuracy = (LatLngAccurateToTypes)Convert.ToInt32(xtr.Value);
										accread = true;
									}
									break;
							}
						}
						break;
					case "coordinates": //the coordinates element
						if (!coordread)
						{
							//move into the element value
							xtr.Read();

							//split the coords and then..
							string[] coords = xtr.Value.Split(new char[] { ',' });

							//save the values
							Longitude = coords[0];
							Latitude = coords[1];

							//finally, once this has been done, we don't want the process to run again on the same file
							coordread = true;
						}
						break;
				}
			}
		}
		catch (Exception ex)
		{
			throw new Exception("An error occured while parsing GeoCoded results from Google, the error was: " + ex.Message);
		}
	}

	public enum LatLngAccurateToTypes : int
	{
		Unknown = 0,
		Country = 1,
		Region = 2,
		SubRegion = 3,
		Town = 4,
		PostCode = 5,
		Street = 6,
		Intersection = 7,
		Address = 8,
		Premises = 9
	}
}

A Simple c# Wrapper for ffMpeg

Converting video in .Net is a pain in the butt. The .Net framework, as of 2/8/2010 does not have a nice, simple, conversion process that will take a file in the many different formats out there and drop it into a nicely formatted .flv file for easy slideshowpro (or other flash based package) streaming.

The alternative, that I have most often seen, is using ffMpeg to convert source video into .flv format. Unfortunately, this process can a bit arcane. While there are other libraries out there that wrap up ffMpeg with a nice shiny, albeit complex, bow. I figured there might be some use for a simple c# library that does a couple simple, but very useful processes.

The following class will convert your input file to a .flv file and create a preview image from 1/3 of the way into the movie. By getting the preview image from 1/3 into the movie, we can avoid a completely black preview image that can come from a video that fades in.

Getting Started:

Create a directory in your project that contains the ffMpeg.exe file, this file will be referenced by the class below. This class is also going to use the web.config or app.config to store the location of our ffMpeg.exe file and a couple key settings. To do this, you will need to import system.configuration into your project and include the following line:

	using System.Configuration;

To start with, we have a couple properties that expose the working path that ffMpeg will need while it is processing our video file along with a property that exposes the location of our exe file.

	private string _ffExe;
	public string ffExe
	{
		get
		{
			return _ffExe;
		}
		set
		{
			_ffExe = value;
		}
	}

	private string _WorkingPath;
	public string WorkingPath
	{
		get
		{
			return _WorkingPath;
		}
		set
		{
			_WorkingPath = value;
		}
	}

In our constructors, we will call an initialize method that will attempt to grab our exepath from web.config, assuming that one has not been entered into our second constructor.

Note: In this example, the class is name “Converter”

	public Converter()
	{
		Initialize();
	}
	public Converter(string ffmpegExePath)
	{
		_ffExe = ffmpegExePath;
		Initialize();
	}

Initialization

You will note that the we are testing to see if the ffmpeg.exe file location exists before attempting to pull it from web.config.

	//Make sure we have valid ffMpeg.exe file and working directory to do our dirty work.
	private void Initialize()
	{
		//first make sure we have a value for the ffexe file setting
		if (string.IsNullOrEmpty(_ffExe))
		{
			object o = ConfigurationManager.AppSettings["ffmpeg:ExeLocation"];
			if (o == null)
			{
				throw new Exception("Could not find the location of the ffmpeg exe file.  The path for ffmpeg.exe " +
				"can be passed in via a constructor of the ffmpeg class (this class) or by setting in the app.config or web.config file.  " +
				"in the appsettings section, the correct property name is: ffmpeg:ExeLocation");
			}
			else
			{
				if (string.IsNullOrEmpty(o.ToString()))
				{
					throw new Exception("No value was found in the app setting for ffmpeg:ExeLocation");
				}
				_ffExe = o.ToString();
			}
		}

		//Now see if ffmpeg.exe exists
		string workingpath = GetWorkingFile();
		if (string.IsNullOrEmpty(workingpath))
		{
			//ffmpeg doesn't exist at the location stated.
			throw new Exception("Could not find a copy of ffmpeg.exe");
		}
		_ffExe = workingpath;

		//now see if we have a temporary place to work
		if (string.IsNullOrEmpty(_WorkingPath))
		{
			object o = ConfigurationManager.AppSettings["ffmpeg:WorkingPath"];
			if (o != null)
			{
				_WorkingPath = o.ToString();
			}
			else
			{
				_WorkingPath = string.Empty;
			}
		}
	}

	private string GetWorkingFile()
	{
		//try the stated directory
		if (File.Exists(_ffExe))
		{
			return _ffExe;
		}

		//oops, that didn't work, try the base directory
		if (File.Exists(Path.GetFileName(_ffExe)))
		{
			return Path.GetFileName(_ffExe);
		}

		//well, now we are really unlucky, let's just return null
		return null;
	}

Loading Files Without Creating Locks

These subs might be a bit out of place but are useful as they create our memory resident objects without throwing a lock on the source file. This means that we can run more than one process at once, or delete our file without screwing things up for another process.

	public static System.Drawing.Image LoadImageFromFile(string fileName)
	{
		System.Drawing.Image theImage = null;
		using (FileStream fileStream = new FileStream(fileName, FileMode.Open,
		FileAccess.Read))
		{
			byte[] img;
			img = new byte[fileStream.Length];
			fileStream.Read(img, 0, img.Length);
			fileStream.Close();
			theImage = System.Drawing.Image.FromStream(new MemoryStream(img));
			img = null;
		}
		GC.Collect();
		return theImage;
	}

	public static MemoryStream LoadMemoryStreamFromFile(string fileName)
	{
		MemoryStream ms = null;
		using (FileStream fileStream = new FileStream(fileName, FileMode.Open,
		FileAccess.Read))
		{
			byte[] fil;
			fil = new byte[fileStream.Length];
			fileStream.Read(fil, 0, fil.Length);
			fileStream.Close();
			ms = new MemoryStream(fil);
		}
		GC.Collect();
		return ms;
	}

Running the ffMpeg Process

Now that we have setup our class and have some basic helper subs to get us going, we need to actually have a method that will run the ffMpeg process.


	//Note the private call here and the argument for Parameters.  The private call is
	//being made here because, in this class, we don't really want to have this method
	//called from outside of the class -- this, however flies in the face of allowing the
	//parameters argument (why not just allow out the public call so that a developer can
	//put in the parameters from their own code?  I guess one could do it and it would probably
	//work fine but, for this implementation, I chose to leave it private.
	private string RunProcess(string Parameters)
	{
		//create a process info object so we can run our app
		ProcessStartInfo oInfo = new ProcessStartInfo(this._ffExe, Parameters);
		oInfo.UseShellExecute = false;
		oInfo.CreateNoWindow = true;

		//so we are going to redirect the output and error so that we can parse the return
		oInfo.RedirectStandardOutput = true;
		oInfo.RedirectStandardError = true;

		//Create the output and streamreader to get the output
		string output = null; StreamReader srOutput = null;

		//try the process
		try
		{
			//run the process
			Process proc = System.Diagnostics.Process.Start(oInfo);

			proc.WaitForExit();

			//get the output
			srOutput = proc.StandardError;

			//now put it in a string
			output = srOutput.ReadToEnd();

			proc.Close();
		}
		catch (Exception)
		{
			output = string.Empty;
		}
		finally
		{
			//now, if we succeded, close out the streamreader
			if (srOutput != null)
			{
				srOutput.Close();
				srOutput.Dispose();
			}
		}
		return output;
	}

Now let’s make some functional stuff

Now that we have called the ffMpeg process, we need to get some video info, get a video file, and a preview image. So, with that being said, onto more source code.

Getting the Details

So, we need to now parse the details of our ffMpeg.exe output and read some details about the video file.

	//We are going to take in memory stream for this file to allow for different input options.
	//Unfortunately, this means that we also need the file extension to pass it out to ffMpeg.
	public VideoFile GetVideoInfo(MemoryStream inputFile, string Filename)
	{
		//Create a temporary file for our use in ffMpeg
		string tempfile = Path.Combine(this.WorkingPath, System.Guid.NewGuid().ToString() + Path.GetExtension(Filename));
		FileStream fs = File.Create(tempfile);

		//write the memory stream to a file and close our the stream so it can be used again.
		inputFile.WriteTo(fs);
		fs.Flush();
		fs.Close();
		GC.Collect();

		//Video File is a class you will see further down this post.  It has some basic information about the video
		VideoFile vf = null;
		try
		{
			vf = new VideoFile(tempfile);
		}
		catch (Exception ex)
		{
			throw ex;
		}

		//And, without adieu, a call to our main method for this functionality.
		GetVideoInfo(vf);

		try
		{
			File.Delete(tempfile);
		}
		catch (Exception)
		{

		}

		return vf;
	}

	//This sub is just another overload to allow input of a direct path, we are just
	//using the videofile class to send in.  More on the video file class further down
	//the article.
	public VideoFile GetVideoInfo(string inputPath)
	{
		VideoFile vf = null;
		try
		{
			vf = new VideoFile(inputPath);
		}
		catch (Exception ex)
		{
			throw ex;
		}
		GetVideoInfo(vf);
		return vf;
	}

	//And now the important code for the GetVideoInfo
	public void GetVideoInfo(VideoFile input)
	{
		//set up the parameters for video info -- these will be passed into ffMpeg.exe
		string Params = string.Format("-i {0}", input.Path);
		string output = RunProcess(Params);
		input.RawInfo = output;

		//Use a regular expression to get the different properties from the video parsed out.
		Regex re = new Regex("[D|d]uration:.((\\d|:|\\.)*)");
		Match m = re.Match(input.RawInfo);

		if (m.Success)
		{
			string duration = m.Groups[1].Value;
			string[] timepieces = duration.Split(new char[] { ':', '.' });
			if (timepieces.Length == 4)
			{
				input.Duration = new TimeSpan(0, Convert.ToInt16(timepieces[0]), Convert.ToInt16(timepieces[1]), Convert.ToInt16(timepieces[2]), Convert.ToInt16(timepieces[3]));
			}
		}

		//get audio bit rate
		re = new Regex("[B|b]itrate:.((\\d|:)*)");
		m = re.Match(input.RawInfo);
		double kb = 0.0;
		if (m.Success)
		{
			Double.TryParse(m.Groups[1].Value, out kb);
		}
		input.BitRate = kb;

		//get the audio format
		re = new Regex("[A|a]udio:.*");
		m = re.Match(input.RawInfo);
		if (m.Success)
		{
			input.AudioFormat = m.Value;
		}

		//get the video format
		re = new Regex("[V|v]ideo:.*");
		m = re.Match(input.RawInfo);
		if (m.Success)
		{
			input.VideoFormat = m.Value;
		}

		//get the video format
		re = new Regex("(\\d{2,3})x(\\d{2,3})");
		m = re.Match(input.RawInfo);
		if (m.Success)
		{
			int width = 0; int height = 0;
			int.TryParse(m.Groups[1].Value, out width);
			int.TryParse(m.Groups[2].Value, out height);
			input.Width = width;
			input.Height = height;
		}
		input.infoGathered = true;
	}

Finally, do the conversion

So, here is the conversion meat and preview imaging.

	//Note the ouputpackage object output.  The output package class is detailed below.
	//this overload does much the same as the one above does, in that, it offers the ability
	//to input a memory stream vs. a filename or our videofile input class.
	public OutputPackage ConvertToFLV(MemoryStream inputFile, string Filename)
	{
		string tempfile = Path.Combine(this.WorkingPath, System.Guid.NewGuid().ToString() + Path.GetExtension(Filename));
		FileStream fs = File.Create(tempfile);
		inputFile.WriteTo(fs);
		fs.Flush();
		fs.Close();
		GC.Collect();

		VideoFile vf = null;
		try
		{
			vf = new VideoFile(tempfile);
		}
		catch (Exception ex)
		{
			throw ex;
		}

		OutputPackage oo = ConvertToFLV(vf);

		try{
			File.Delete(tempfile);
		} catch(Exception) {

		}

		return oo;
	}
	public OutputPackage ConvertToFLV(string inputPath)
	{
		VideoFile vf = null;
		try
		{
			vf = new VideoFile(inputPath);
		}
		catch (Exception ex)
		{
			throw ex;
		}

		OutputPackage oo = ConvertToFLV(vf);
		return oo;
	}

	//The actually important code, rather than an overload.
	public OutputPackage ConvertToFLV(VideoFile input)
	{
		//check to see if we have already gathered our info so we know
		//where to get our preview image from.
		if (!input.infoGathered)
		{
			GetVideoInfo(input);
		}

		//Create our output object
		OutputPackage ou = new OutputPackage();

		//set up the parameters for getting a previewimage
		string filename = System.Guid.NewGuid().ToString() + ".jpg";
		int secs;

		//divide the duration in 3 to get a preview image in the middle of the clip
		//instead of a black image from the beginning.
		secs = (int)Math.Round(TimeSpan.FromTicks(input.Duration.Ticks / 3).TotalSeconds, 0);

		string finalpath = Path.Combine(this.WorkingPath, filename);

		//These are the parameters for setting up a preview image that must be passed to ffmpeg.
		//Note that we are asking for a jpeg image at our specified seconds.
		string Params = string.Format("-i {0} {1} -vcodec mjpeg -ss {2} -vframes 1 -an -f rawvideo", input.Path, finalpath, secs);
		string output = RunProcess(Params);

		ou.RawOutput = output;

		//Ok, so hopefully we now have a preview file.  If the file
		//did not get created properly, try again at the first frame.
		if (File.Exists(finalpath))
		{
			//load that file into our output package and attempt to delete the file
			//since we no longer need it.
			ou.PreviewImage = LoadImageFromFile(finalpath);
			try
			{
				File.Delete(finalpath);
			}
			catch (Exception) { }
		} else { //try running again at frame 1 to get something
			Params = string.Format("-i {0} {1} -vcodec mjpeg -ss {2} -vframes 1 -an -f rawvideo", input.Path, finalpath, 1);
			output = RunProcess(Params);

			ou.RawOutput = output;

			if (File.Exists(finalpath)) {
				ou.PreviewImage = LoadImageFromFile(finalpath);
				try
				{
					File.Delete(finalpath);
				}
				catch (Exception) { }
			}
		}

		finalpath = Path.Combine(this.WorkingPath, filename);
		filename = System.Guid.NewGuid().ToString() + ".flv";

		//Now we are going to actually create the converted file.  Note that we are asking for
		//a video at 22khz 64bit.  This can be changed by a couple quick alterations to this line,
		//or by extending out this class to offer multiple different conversions.
		Params = string.Format("-i {0} -y -ar 22050 -ab 64 -f flv {1}", input.Path, finalpath);
		output = RunProcess(Params);

		//Check to see if our conversion file exists and then load the converted
		//file into our output package.  If the file does exist and we are able to
		//load it into our output package, we can delete the work file.
		if (File.Exists(finalpath))
		{
			ou.VideoStream = LoadMemoryStreamFromFile(finalpath);
			try
			{
				File.Delete(finalpath);
			}
			catch (Exception) { }
		}
		return ou;
	}

Supplementary Classes

Throughout this article, you will have noticed the use of “VideoFile” and “OutputPackage”. These classes are just informational classes that provide simple ways of accessing everything.
Here they are:

	public class VideoFile
	{
		#region Properties
		private string _Path;
		public string Path
		{
			get
			{
				return _Path;
			}
			set
			{
				_Path = value;
			}
		}

		public TimeSpan Duration { get; set; }
		public double BitRate { get; set; }
		public string AudioFormat { get; set; }
		public string VideoFormat { get; set; }
		public int Height { get; set; }
		public int Width { get; set; }
		public string RawInfo { get; set; }
		public bool infoGathered {get; set;}
		#endregion

		#region Constructors
		public VideoFile(string path)
		{
			_Path = path;
			Initialize();
		}
		#endregion

		#region Initialization
		private void Initialize()
		{
			this.infoGathered = false;
			//first make sure we have a value for the video file setting
			if (string.IsNullOrEmpty(_Path))
			{
				throw new Exception("Could not find the location of the video file");
			}

			//Now see if the video file exists
			if (!File.Exists(_Path))
			{
				throw new Exception("The video file " + _Path + " does not exist.");
			}
		}
		#endregion
	}

	public class OutputPackage
	{
		public MemoryStream VideoStream { get; set; }
		public System.Drawing.Image PreviewImage { get; set; }
		public string RawOutput { get; set; }
		public bool Success { get; set; }
	}

Full Code for the class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using System.Diagnostics;
using System.Configuration;
using System.Text.RegularExpressions;

namespace ffMpeg
{
	public class Converter
	{
		#region Properties
		private string _ffExe;
		public string ffExe
		{
			get
			{
				return _ffExe;
			}
			set
			{
				_ffExe = value;
			}
		}

		private string _WorkingPath;
		public string WorkingPath
		{
			get
			{
				return _WorkingPath;
			}
			set
			{
				_WorkingPath = value;
			}
		}

		#endregion

		#region Constructors
		public Converter()
		{
			Initialize();
		}
		public Converter(string ffmpegExePath)
		{
			_ffExe = ffmpegExePath;
			Initialize();
		}
		#endregion

		#region Initialization
		private void Initialize()
		{
			//first make sure we have a value for the ffexe file setting
			if (string.IsNullOrEmpty(_ffExe))
			{
				object o = ConfigurationManager.AppSettings["ffmpeg:ExeLocation"];
				if (o == null)
				{
					throw new Exception("Could not find the location of the ffmpeg exe file.  The path for ffmpeg.exe " +
					"can be passed in via a constructor of the ffmpeg class (this class) or by setting in the app.config or web.config file.  " +
					"in the appsettings section, the correct property name is: ffmpeg:ExeLocation");
				}
				else
				{
					if (string.IsNullOrEmpty(o.ToString()))
					{
						throw new Exception("No value was found in the app setting for ffmpeg:ExeLocation");
					}
					_ffExe = o.ToString();
				}
			}

			//Now see if ffmpeg.exe exists
			string workingpath = GetWorkingFile();
			if (string.IsNullOrEmpty(workingpath))
			{
				//ffmpeg doesn't exist at the location stated.
				throw new Exception("Could not find a copy of ffmpeg.exe");
			}
			_ffExe = workingpath;

			//now see if we have a temporary place to work
			if (string.IsNullOrEmpty(_WorkingPath))
			{
				object o = ConfigurationManager.AppSettings["ffmpeg:WorkingPath"];
				if (o != null)
				{
					_WorkingPath = o.ToString();
				}
				else
				{
					_WorkingPath = string.Empty;
				}
			}
		}

		private string GetWorkingFile()
		{
			//try the stated directory
			if (File.Exists(_ffExe))
			{
				return _ffExe;
			}

			//oops, that didn't work, try the base directory
			if (File.Exists(Path.GetFileName(_ffExe)))
			{
				return Path.GetFileName(_ffExe);
			}

			//well, now we are really unlucky, let's just return null
			return null;
		}
		#endregion

		#region Get the File without creating a file lock
		public static System.Drawing.Image LoadImageFromFile(string fileName)
		{
			System.Drawing.Image theImage = null;
			using (FileStream fileStream = new FileStream(fileName, FileMode.Open,
			FileAccess.Read))
			{
				byte[] img;
				img = new byte[fileStream.Length];
				fileStream.Read(img, 0, img.Length);
				fileStream.Close();
				theImage = System.Drawing.Image.FromStream(new MemoryStream(img));
				img = null;
			}
			GC.Collect();
			return theImage;
		}

		public static MemoryStream LoadMemoryStreamFromFile(string fileName)
		{
			MemoryStream ms = null;
			using (FileStream fileStream = new FileStream(fileName, FileMode.Open,
			FileAccess.Read))
			{
				byte[] fil;
				fil = new byte[fileStream.Length];
				fileStream.Read(fil, 0, fil.Length);
				fileStream.Close();
				ms = new MemoryStream(fil);
			}
			GC.Collect();
			return ms;
		}
		#endregion

		#region Run the process
		private string RunProcess(string Parameters)
		{
			//create a process info
			ProcessStartInfo oInfo = new ProcessStartInfo(this._ffExe, Parameters);
			oInfo.UseShellExecute = false;
			oInfo.CreateNoWindow = true;
			oInfo.RedirectStandardOutput = true;
			oInfo.RedirectStandardError = true;

			//Create the output and streamreader to get the output
			string output = null; StreamReader srOutput = null;

			//try the process
			try
			{
				//run the process
				Process proc = System.Diagnostics.Process.Start(oInfo);

				proc.WaitForExit();

				//get the output
				srOutput = proc.StandardError;

				//now put it in a string
				output = srOutput.ReadToEnd();

				proc.Close();
			}
			catch (Exception)
			{
				output = string.Empty;
			}
			finally
			{
				//now, if we succeded, close out the streamreader
				if (srOutput != null)
				{
					srOutput.Close();
					srOutput.Dispose();
				}
			}
			return output;
		}
		#endregion

		#region GetVideoInfo
		public VideoFile GetVideoInfo(MemoryStream inputFile, string Filename)
		{
			string tempfile = Path.Combine(this.WorkingPath, System.Guid.NewGuid().ToString() + Path.GetExtension(Filename));
			FileStream fs = File.Create(tempfile);
			inputFile.WriteTo(fs);
			fs.Flush();
			fs.Close();
			GC.Collect();

			VideoFile vf = null;
			try
			{
				vf = new VideoFile(tempfile);
			}
			catch (Exception ex)
			{
				throw ex;
			}

			GetVideoInfo(vf);

			try
			{
				File.Delete(tempfile);
			}
			catch (Exception)
			{

			}

			return vf;
		}
		public VideoFile GetVideoInfo(string inputPath)
		{
			VideoFile vf = null;
			try
			{
				vf = new VideoFile(inputPath);
			}
			catch (Exception ex)
			{
				throw ex;
			}
			GetVideoInfo(vf);
			return vf;
		}
		public void GetVideoInfo(VideoFile input)
		{
			//set up the parameters for video info
			string Params = string.Format("-i {0}", input.Path);
			string output = RunProcess(Params);
			input.RawInfo = output;

			//get duration
			Regex re = new Regex("[D|d]uration:.((\\d|:|\\.)*)");
			Match m = re.Match(input.RawInfo);

			if (m.Success)
			{
				string duration = m.Groups[1].Value;
				string[] timepieces = duration.Split(new char[] { ':', '.' });
				if (timepieces.Length == 4)
				{
					input.Duration = new TimeSpan(0, Convert.ToInt16(timepieces[0]), Convert.ToInt16(timepieces[1]), Convert.ToInt16(timepieces[2]), Convert.ToInt16(timepieces[3]));
				}
			}

			//get audio bit rate
			re = new Regex("[B|b]itrate:.((\\d|:)*)");
			m = re.Match(input.RawInfo);
			double kb = 0.0;
			if (m.Success)
			{
				Double.TryParse(m.Groups[1].Value, out kb);
			}
			input.BitRate = kb;

			//get the audio format
			re = new Regex("[A|a]udio:.*");
			m = re.Match(input.RawInfo);
			if (m.Success)
			{
				input.AudioFormat = m.Value;
			}

			//get the video format
			re = new Regex("[V|v]ideo:.*");
			m = re.Match(input.RawInfo);
			if (m.Success)
			{
				input.VideoFormat = m.Value;
			}

			//get the video format
			re = new Regex("(\\d{2,3})x(\\d{2,3})");
			m = re.Match(input.RawInfo);
			if (m.Success)
			{
				int width = 0; int height = 0;
				int.TryParse(m.Groups[1].Value, out width);
				int.TryParse(m.Groups[2].Value, out height);
				input.Width = width;
				input.Height = height;
			}
			input.infoGathered = true;
		}
		#endregion

		#region Convert to FLV
		public OutputPackage ConvertToFLV(MemoryStream inputFile, string Filename)
		{
			string tempfile = Path.Combine(this.WorkingPath, System.Guid.NewGuid().ToString() + Path.GetExtension(Filename));
			FileStream fs = File.Create(tempfile);
			inputFile.WriteTo(fs);
			fs.Flush();
			fs.Close();
			GC.Collect();

			VideoFile vf = null;
			try
			{
				vf = new VideoFile(tempfile);
			}
			catch (Exception ex)
			{
				throw ex;
			}

			OutputPackage oo = ConvertToFLV(vf);

			try{
				File.Delete(tempfile);
			} catch(Exception) {

			}

			return oo;
		}
		public OutputPackage ConvertToFLV(string inputPath)
		{
			VideoFile vf = null;
			try
			{
				vf = new VideoFile(inputPath);
			}
			catch (Exception ex)
			{
				throw ex;
			}

			OutputPackage oo = ConvertToFLV(vf);
			return oo;
		}
		public OutputPackage ConvertToFLV(VideoFile input)
		{
			if (!input.infoGathered)
			{
				GetVideoInfo(input);
			}
			OutputPackage ou = new OutputPackage();

			//set up the parameters for getting a previewimage
			string filename = System.Guid.NewGuid().ToString() + ".jpg";
			int secs;

			//divide the duration in 3 to get a preview image in the middle of the clip
			//instead of a black image from the beginning.
			secs = (int)Math.Round(TimeSpan.FromTicks(input.Duration.Ticks / 3).TotalSeconds, 0);

			string finalpath = Path.Combine(this.WorkingPath, filename);
			string Params = string.Format("-i {0} {1} -vcodec mjpeg -ss {2} -vframes 1 -an -f rawvideo", input.Path, finalpath, secs);
			string output = RunProcess(Params);

			ou.RawOutput = output;

			if (File.Exists(finalpath))
			{
				ou.PreviewImage = LoadImageFromFile(finalpath);
				try
				{
					File.Delete(finalpath);
				}
				catch (Exception) { }
			} else { //try running again at frame 1 to get something
				Params = string.Format("-i {0} {1} -vcodec mjpeg -ss {2} -vframes 1 -an -f rawvideo", input.Path, finalpath, 1);
				output = RunProcess(Params);

				ou.RawOutput = output;

				if (File.Exists(finalpath)) {
					ou.PreviewImage = LoadImageFromFile(finalpath);
					try
					{
						File.Delete(finalpath);
					}
					catch (Exception) { }
				}
			}

			finalpath = Path.Combine(this.WorkingPath, filename);
			filename = System.Guid.NewGuid().ToString() + ".flv";
			Params = string.Format("-i {0} -y -ar 22050 -ab 64 -f flv {1}", input.Path, finalpath);
			output = RunProcess(Params);

			if (File.Exists(finalpath))
			{
				ou.VideoStream = LoadMemoryStreamFromFile(finalpath);
				try
				{
					File.Delete(finalpath);
				}
				catch (Exception) { }
			}
			return ou;
		}
		#endregion
	}

	public class VideoFile
	{
		#region Properties
		private string _Path;
		public string Path
		{
			get
			{
				return _Path;
			}
			set
			{
				_Path = value;
			}
		}

		public TimeSpan Duration { get; set; }
		public double BitRate { get; set; }
		public string AudioFormat { get; set; }
		public string VideoFormat { get; set; }
		public int Height { get; set; }
		public int Width { get; set; }
		public string RawInfo { get; set; }
		public bool infoGathered {get; set;}
		#endregion

		#region Constructors
		public VideoFile(string path)
		{
			_Path = path;
			Initialize();
		}
		#endregion

		#region Initialization
		private void Initialize()
		{
			this.infoGathered = false;
			//first make sure we have a value for the video file setting
			if (string.IsNullOrEmpty(_Path))
			{
				throw new Exception("Could not find the location of the video file");
			}

			//Now see if the video file exists
			if (!File.Exists(_Path))
			{
				throw new Exception("The video file " + _Path + " does not exist.");
			}
		}
		#endregion
	}

	public class OutputPackage
	{
		public MemoryStream VideoStream { get; set; }
		public System.Drawing.Image PreviewImage { get; set; }
		public string RawOutput { get; set; }
		public bool Success { get; set; }
	}
}