Reading internationalized text files 
Does your text file have more characters than just plain ASCII?

Typically, the lazy man (myself) opens a text file using this command:

StreamReader sr = File.OpenText("C:\\batchtoimport.csv");


However, if you have any high ASCII characters, ISO-8859-1 characters, UTF-8 characters, or whatever you want to call them, you will need to actually tell .NET what encoding your file will use so they can be interpreted correctly.

Remember, all your strings are being converted into Unicode internally. So if your file is encoded using something odd like 7-bit ASCII, .NET needs to know to ignore the 8th bit. The way .NET knows this is that it is told your conversion encoding when the streamreader is created. Here's what you do:

StreamReader input = new StreamReader(new FileStream("C:\\batchtoimport.csv", FileMode.Open), Encoding.UTF8));


Yes, I know this is a really simple issue. But when you're so used to just typing "File.OpenText", sometimes you need just need help to remember the correct answer.

FYI: If you want ISO-8859-1, which is usually always the correct answer, the correct encoding value is Encoding.GetEncoding(28591).
  |  permalink  |  related link  |   ( 3 / 218 )
Using the ASP.Net Membership / Authentication system to logon a user manually 
It's always this way: something really simple, like logging on a user, is difficult to figure out, but really easy when you find the answer.

I was hunting through the code looking for a "Membership.LogonUser(username, password);" function, but then I remembered - this stuff is actually handled through FormsAuthentication. So we determine this by using their cookie system.

Let's say you have a user, "bob_roberts", and he's just completed a complex, five-page registration form on your website. You don't want to make him log on by himself after he's gone through all this work. So here's what you do:

HttpCookie cookie = FormsAuthentication.GetAuthCookie(Customer.username, false);
Response.SetCookie(cookie);


Fun!
  |  permalink  |  related link  |   ( 3 / 206 )
Making Thumbnails for Videos using DirectShow.NET 
Sure, you can share videos on the web without a preview thumbnail, but why? Everybody likes to see thumbnails of videos: it’s a quick, appealing way to separate the vacation videos from the latest high-def movie trailers. Unfortunately, you can’t make a thumbnail of a video as easily as you can make a thumbnail of a photo. What I’d really like is a tool that I can use as an embedded component on my webserver that can “snap” this thumbnail and save it to disk automatically when I upload a video.

Fortunately, a quick and useful approach exists, with a few caveats. With .NET 3.0, you can snap a thumbnail using the Windows Presentation Framework. If you’re limited to .NET 2.0, you’ll find that there is no .NET native object to do the job, so we’ll need to use the Component Object Model (COM) interoperability layer. Through COM, we can use Microsoft’s DirectShow API, which is the foundation for Windows Media Player.

Both of these approaches use the core DirectShow API, which supports WMV, AVI, ASF, MPEG, and MPEG2 formats. Since DirectShow supports pluggable codecs and drivers, in theory you can provide support for other formats as well.

Using Windows Presentation Framework



In the March 2007 issue of MSDN Magazine, Lee Brimelow demonstrates how to snap a thumbnail of a video using a MediaPlayer object:

MediaPlayer mp = new MediaPlayer();
mp.Open(myValidMediaUri);
RenderTargetBitmap rtb = new RenderTargetBitmap(320, 240, 1 / 200, 1 / 200, PixelFormats.Pbgra32);
DrawingVisual dv = new DrawingVisual();
DrawingContext dc = dv.RenderOpen();
dc.DrawVideo(mp, new Rect(0, 0, 320, 240));
dc.Close();
rtb.Render(dv);
Image im = new Image();
im.Source = BitmapFrame.Create(rtb);


That’s pretty quick and clean. However, what happens if you’re stuck in .NET 2.0, or if you want more exact control over the Windows Media Player library? In that case, we’ll have to use the core DirectShow API.

Using DirectShow



My two favorite features of the .NET environment are Microsoft’s powerful tools and the robust open source community that has emerged around it. Where one side of the equation has a gap, the other side fills it. So when Microsoft left DirectShow out of .NET 2.0, a bunch of clever developers created and shared their own source code freely to help you develop your projects: and that’s how we come to have DirectShowNet.

If you don’t know anything about the Component Object Model (COM), consider yourself lucky. With the COM interoperability libraries in .NET 2.0, you can think of a COM object as a .NET object with manual garbage collection and weak type checking. All the other minute differences are carefully hidden – provided someone has gone to the work to put together an interoperability class. Fortunately, that’s exactly what the DirectShowNet guys did, and it makes our lives that much easier. You can download DirectShowNet from their website.

Now, time to get started. When using COM, you need to know a component’s GUID (also called a class ID). Once you know that GUID, you can tell COM to create an instance of that object for you. For example, the first object we need to work with DirectShow is a thing called a FilterGraph, and here’s how we create it:

using DShowNET;

...

Type comtype = Type.GetTypeFromCLSID( Clsid.FilterGraph );
IGraphBuilder graphbuilder =
(IGraphBuilder)Activator.CreateInstance(comtype);


Why is it called a filter graph? Well, in DirectShow, every component doing work is considered a filter, and the whole thing links together in a graph like a flowchart. Imagine the original video file as a filter that feeds the raw bytes out. Then, the WMV decoder consumes these bytes and produces separate video and audio streams of bytes. Then, the codecs receive these bytes and produce actual waveforms and images that go to the speakers and the screen. The FilterGraph object keeps track of all the connections in this chart, thus the name.

By default, a FilterGraph will create all the necessary DirectShow components to play video and audio, but nothinig else. We need some way to grab images from the video codec before they go to the screen. Fortunately, there's a ready-made object does exactly this, and it's called the SampleGrabber. Let’s make one and add it to the graph:

comtype = Type.GetTypeFromCLSID(Clsid.SampleGrabber);
ISampleGrabber samplegrabber =
(ISampleGrabber)Activator.CreateInstance(comtype);
graphbuilder.AddFilter((IBaseFilter)samplegrabber,
"samplegrabber");


Just like the FilterGraph, we get the COM type information and instantiate it. Once we have the object, we tell the GraphBuilder to add the SampleGrabber to the overall media player "graph," and the FilterGraph is smart enough to know where it should be connected.
Next, let's tell SampleGrabber that we want a 24bpp RGB image format for our thumbnail:

AMMediaType mt = new AMMediaType();
mt.majorType = MediaType.Video;
mt.subType = MediaSubType.RGB24;
mt.formatType = FormatType.VideoInfo;
samplegrabber.SetMediaType(mt);


That done, we tell the GraphBuilder to actually load the Windows Media File.

int hr = graphbuilder.RenderFile(filename, null);


Keep in mind that DirectShow is basically Windows Media Player. What you’re doing here is equivalent to opening Windows Media Player and selecting File | Open. Unless you’ve got a special plugin, RenderFile will fail to open Quicktime, Flash, or MP4 files.
Now that we have a file loaded, we need other objects to handle the playback. DirectShow has a bunch of specialized objects to do all the detail work. We need IMediaEventEx and IMediaSeeking to communicate with the video playback engine; IMediaControl to start playback; and IVideoWindow and IBasicAudio to deal with sound and video. Boy, I'd hate to have to type out all the COM creation stuff over and over again - do I have to? Nope! We just typecast the GraphBuilder into what we want, and it'll do all the work for us. Snappy!

IMediaEventEx mediaEvt = (IMediaEventEx)graphbuilder;
IMediaSeeking mediaSeek = (IMediaSeeking)graphbuilder;
IMediaControl mediaCtrl = (IMediaControl)graphbuilder;
IBasicAudio basicAudio = (IBasicAudio)graphbuilder;
IVideoWindow videoWin = (IVideoWindow)graphbuilder;


Screenshot Preparation



Now that we've got a video file, you may be excited to just jump right in and snap a picture. However, if we start playback now, you’ll suddenly get a video window on the screen and hear sounds out your speaker. Since we’re trying to run this app on a web server, that's not what we want. How about we silence the audio and hide the video?

basicAudio.put_Volume(-10000);
videoWin.put_AutoShow(DsHlp.OAFALSE);


WindowsMedia thinks of audio in terms of decibels of amplification. It recognizes numbers in the range -10000 (full mute) and 0 (full volume); in practice, not all speakers or audio chips will respond identically. For this sample program, we'll just drop it to nothing.
On the video side, the AutoShow flag tells the VideoWindow object whether it should launch a new window when the video plays or not; we can just tell it “no”.

Now that we've hidden our work from the user at the keyboard, let's tell the SampleGrabber to take one snapshot for us. We want it to take a single screenshot and keep it in a buffer until we can retrieve it.

samplegrabber.SetOneShot(true);
samplegrabber.SetBufferSamples(true);


This gives the SampleGrabber everything it needs to get the job done.

Seeking A Particular Frame



Next, let's find a specific frame of the video. We do this with the IMediaSeeking interface; it provides the ability to set the stop and start positions within a media file precisely. Very precisely, in fact. IMediaSeeking uses time intervals of 1/10,000,000 of a second. Here's how you calculate the duration of a media file:

long d = 0;
mediaSeek.GetDuration(out d);
long numSecs = d / 10000000;


I'm presuming you know what frame you want grabbed from your media file. Since many media files begin with a black screenshot or a fade in, I suggest either jumping forward 1.5 seconds or 10% of the video’s duration. You might also want to snap multiple screenshots and let the user pick the most appropriate thumbnail.

Here’s how you might calculate the ten percent duration snap:

long secondstocapture = (long)(numSecs * 0.10f);
DsOptInt64 rtStart, rtStop;
rtStart = new DsOptInt64(secondstocapture * 10000000);
rtStop = rtStart;
mediaSeek.SetPositions(rtStart, SeekingFlags.AbsolutePositioning, rtStop, SeekingFlags.AbsolutePositioning);


Now that we’ve told the IMediaSeek object which frame we want, we'll tell DirectShow to go ahead and play the media file. Since DirectShow runs asynchronously, we’ll use the IMediaEventEx object to wait until our screengrab is complete. If you’re lazy, you can tell MediaEvent to wait forever by passing it -1 for number of milliseconds to wait; otherwise, provide a useful number and check the evcode parameter when it returns.

mediaCtrl.Run();
int evcode;
mediaEvt.WaitForCompletion(-1, out evcode);


Congratulations! You've got a screenshot sitting in your SampleGrabber.

Retrieving the Screenshot



But where is the image? All that work won't help us unless we actually can get this screenshot. So next thing we do is start working with the SampleGrabber and ask it to provide us with the bitmap it took. We can only do that by using the COM interoperability services – specifically, an object called the “Marshal” which can copy memory for us. We’ll use it here to take a copy of memory and turn it into a .NET object we can manipulate.

using System.Runtime.InteropServices;

...

VideoInfoHeader videoheader = new VideoInfoHeader();
AMMediaType grab = new AMMediaType();
samplegrabber.GetConnectedMediaType(grab);
videoheader =
(VideoInfoHeader)Marshal.PtrToStructure(grab.formatPtr,
typeof(VideoInfoHeader));


First, we get the MediaType structure from the SampleGrabber. MediaType has an “IntPtr” object within it called the formatPtr. An IntPtr by itself is just a 32-bit value that means nothing to .NET. However, using the Marshal, we can get at the memory pointed to by formatPtr and turn it into a VideoInfoHeader object.

This VideoHeader object also has some useful information: it tells us how large the video is! We can pull the width and height of the video image from the videoheader.SrcRect structure, and we can create a bitmap matching its size.

using System.Drawing;

...

int width = videoheader.SrcRect.Right;
int height = videoheader.SrcRect.Bottom;
Bitmap b = new Bitmap(width, height, PixelFormat.Format24bppRgb);


So we've got a blank bitmap in memory, and we also have this SampleGrabber object that supposedly has the image in its buffer. We need some way to copy one into the other. So, of course, the first thing we need to do is to know exactly how large the buffer is so that we can avoid any overflow errors. Here's how to calculate the size of a bitmap in memory:

uint bytesPerPixel = (uint)(24 >> 3);
uint extraBytes = ((uint)width * bytesPerPixel) % 4;
uint adjustedLineSize = bytesPerPixel * ((uint)width + extraBytes);
uint sizeOfImageData = (uint)(height) * adjustedLineSize;


In my case, I can simply multiply the number of pixels by three for 24bpp color; if you use some other format, or if you're recording audio, you should adjust your calculations as appropriate.

To actually copy the buffer into our Bitmap object, we need to lock the bitmap in memory so that we can get a pointer to memory. The SampleGrabber object will actually do the work of copying for us, but it needs to know exactly how much memory we've reserved.

using System.Drawing.Imaging;

...

BitmapData bd1 = b.LockBits(new Rectangle(0, 0, width, height), mageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int bufsize = (int)sizeOfImageData;
int n = samplegrabber.GetCurrentBuffer(ref bufsize, bd1.Scan0);
b.UnlockBits(bd1);


That's it - you now have a .NET Bitmap object with our screenshot in it. Congratulations! You can Bitmap.Save() the file to disk and use it however you normally use a thumbnail. However, before you run off and do something else, remember that COM does not have a garbage collector for its objects. You need to dispose of them manually using the ReleaseComObject function:

Marshal.ReleaseComObject(graphbuilder);
Marshal.ReleaseComObject(samplegrabber);


Epilogue


If you’re still reading this article, perhaps you’ve noticed that the bitmap you got from the SampleGrabber is upside down. You didn’t do anything wrong; you just exposed a difference between memory representations and file representations. Windows stores device independent bitmaps in memory in bottom-scan-line-first format. This habit dates back to the Windows 3.0 days and the reason had something to do with OS/2 compatibility; but nowadays it's just a silly nuisance. Here’s the easy solution:

b.RotateFlip(RotateFlipType.RotateNoneFlipY);

  |  permalink  |  related link  |   ( 3 / 377 )
Epoch Time in C# / ASP.NET 
I had a flash developer request the "Epoch" time from my web service. What is it? Epoch time is a Unix convention - the number of seconds elapsed since Jan 1, 1970. As good a time as any. Here's a function to produce it:

public static int MakeEpochTime(DateTime t)
{
TimeSpan ts = (t.ToUniversalTime() - new DateTime(1970, 1, 1));
return ts.TotalSeconds;
}


Thanks goes to Brad Abrams, who wrote nearly exactly the code I wanted. The code above is a slight modification of his.
  |  permalink  |  related link  |   ( 3 / 383 )
Installing Windows Server 2003 R2 - "Post-Setup Security Updates" 
The article on Microsoft Technet

Something I always forget about during the Windows Server setup process is the post-setup security updates. It tells you that all incoming network connections are disabled until you're finished patching the server with the latest and greatest fixes.

The best way to fix this is to run the Security and Configuration Wizard, which takes a bit of time but guarantees that your server is setup according to your preferences.
  |  permalink  |  related link  |   ( 3 / 232 )

Back Next