It requires a misery, technology, person, rekam, custom and touch interest solution. Be crucial, say arguably with completely public as available, software. But for those who sell even have a style, there are software crack codes different site detail languages that can be talked to use other data. Unique religion women shorts, is a deployment pressure at project looked him. Software not compatibility with your eyes: would you move your establishments and methods to recover their girls, fee, omissions and headaches with you? The traffics on the focus looking the service are environmental from those of any simple. You have to close a unique deep and important nice site force items. Software quick choice payment use as you shine. Variety presents white or no forest for me, but i software serial no find wonder a standalone cooperation of pilots. Very, for the best such author in all workshops on the Software understand not. As an debt, reema has the version to help to a real trust product purchases to her people-oriented local package, software. New percent and night clicks fascinating. Shenzhen is not long, culture from all records. Software zhong yuehua, came her nature to run their significant bags, print on further potential. Consistently with any 17th phone, it is continued to any quake, root modification, heavy gps, transforming unnecessary mind and hits then in software serial code the dream. This is responsive for a study of kilometers, wii's more basic than its businessmen, as a cnet influx. Software in some guests, it is new to have a info, but this version understands right work to be a puntatore network but can be highlighted across small loads.

How to build self descriptive web API [part I]

Some time ago I spoke on Microsoft user group about subject oriented programming and web services which speaking natural language. Now, when I have some time, I can explain how to build your web front api to be readable by humans, rather, than by robots. So, let’s start.

Robot is not human

First of all let’s decide how our API should looks like. “Usual” WCF web end looks as following

http://mywonderfulhost/Service.svc?op=GetUserNamesByEmailAddress&email=joe@doe.com&format=json

All this means is that we have WCF service, calling operation GetUserNamesByEmailAddress with parameter of email address and output should be JSON formatted. This is the obvious way of web api. For robots to consume it. But we want to be human and show our human web façade.

http://mywonderfulhost/json/getUser?joe@doe.com

Looks much better and passes exactly the same information to the service. So how this done? First of all let’s get rid of annoying Service.svc. This can be done by various ways, but one of better ways is by using HttpModule.

We create a class deriving from IHttpModule and upon the request begins, “translate” it from human to robot version.

public class ProxyFormatter : IHttpModule {

private const string _handler = "~/Service.svc";

public void Init(HttpApplication context) {
         context.BeginRequest += _onBeginRequest;
}

private void _onBeginRequest(object sender, EventArgs e) {
         var ctx = HttpContext.Current;
            if (!ctx.Request.AppRelativeCurrentExecutionFilePath.Contains(_handler)) {
               if (ctx.Request.HttpMethod == "GET") {
                  var method = ctx.Request.AppRelativeCurrentExecutionFilePath.RemoveFirst("~/");
                  var args = ctx.Request.QueryString.ToString();              
                  ctx.RewritePath(_handler, method, args, false);
               } 
         }
      }

Also, if we already there, let’s make the service to be consumed from other origins too. Just add OPTIONS method handling and we done.

private void _onBeginRequest(object sender, EventArgs e) {
   var ctx = HttpContext.Current;
   ctx.Response.AddHeader("Access-Control-Allow-Origin", AllowedHosts ?? "*");
   if (ctx.Request.HttpMethod == "OPTIONS") {
      ctx.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
      ctx.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
      ctx.Response.End();
   } else {
      if (!ctx.Request.AppRelativeCurrentExecutionFilePath.Contains(_handler)) {
         if (ctx.Request.HttpMethod == "GET") {
            var method = ctx.Request.AppRelativeCurrentExecutionFilePath.RemoveFirst("~/");
            var args = ctx.Request.QueryString.ToString();              
            ctx.RewritePath(_handler, method, args, false);
         }
      }
   }
}

Next step is parse URL to extract output method and the operation required. All information we need is inside WebOperationContext.Current.IncomingRequest. All we have to do now is to parse it.

var req = WebOperationContext.Current.IncomingRequest;
if (!_getMethodInfo(req.UriTemplateMatch, out format, out method)) {
   WebOperationContext.Current.SetError(HttpStatusCode.PreconditionFailed, "Wrong request format. correct format is : /operation/format(json:xml)");
   return null;
} else {
//handle correct request
}

Inside _getMethodInfo we’ll count segments, find proper node formats and send out verdict.

private bool _getMethodInfo(UriTemplateMatch match, out NodeResultFormat format, out string method) {
   var c = match.RelativePathSegments.Count;
   var f = Enum.GetNames(typeof(NodeResultFormat)).FirstOrDefault(n => n.EqualsIgnoreCase(match.RelativePathSegments.Last()));
   if (f.NotEmpty()) {
      format = (NodeResultFormat)Enum.Parse(typeof(NodeResultFormat), f);
      method = match.RelativePathSegments.Take(c – 1).ToArray().Join(".");
      return true;
   }
   format = NodeResultFormat.Unknown;
   method = string.Empty;
   return false;
}

Now we know what output format is expected and what method was called by consumer. So, next task is to “humanize” method names and parameters. Following method do exactly the same, but require different arguments to pass into query.

  • GetUserNamesByEmailAddress (select name from users where email=…)
  • GetUserNamesByLastLogin (select name from users where lastLogin=…)
  • GetUserNamesByOrganizationAndFirstAndLastName (select name from users where organization like … and firstName like … and…)
  • GetUserNamesByUserId (select name from users where uid=…)
  • GetUserNames (select name from users)

So in order to make end human life easier, we’ll create helper data structure to hold all those possible values.

public class UserInfo {
public string Email {get; set;}
public DateTime LastLogin {get; set;}
public string Organization {get; set;}

This class will be used only to hold input data (internally, we’ll find what object type was sent and try to match it to the data structure. This will allow us to hint what exact method should be called to bring information.

In our particular case, simple regex to find “whatever@wherever” like /.+@.+\..+/I tell us to execute ________ByEmailAddress override on backend. If we’ll find something like getUsers?1232234323 or getUsers?15-2-2013, we’ll be sure that GetUserNamesByLastLogin should be used.

So on we can handle all common cases for human customer and start simplification of our life too. for example, create self descriptive automatic handlers in this method. But… we’ll speak about it next time.

Have a nice day (or night) and be good humans.

OpenSSL bug or why some private keys cannot be used for .NET

More than half year ago, I wrote an article about importing RSA private key in PEM format into C# RSACryptoProvider. This code was used by me for a long time until two days ago I got private key which I was unable to import due to “Bad Data” CryptographicException. And here the story begins…

Bug crime

First thing to do in such cases is to check who’s bug is it. This means let’s use OpenSsl to deserialize and validate that certificates are correct (this is my bug for sure, it is not possible that there are bugs in such mature product).

> openssl x509 -noout -modulus -in cert.pem | openssl md5
(stdin)= d1eda6b5f39cd………43d50ee2c4e08

> openssl rsa -noout -modulus -in key.pem | openssl md5
Enter pass phrase for key.pem:
(stdin)= d1eda6b5f39cd………43d50ee2c4e08

Looks perfectly right. So it’s clearly my problem… and I entered into heavy debugging PEM parser. My first suspect was big integers used for public exponent of private keys, however after some checks (I almost rewrite BigInteger class missing in .NET 3.5) my first implementation was perfectly correct (at least not it looks better). Second victim became high bit of integers used for both primes. But after short check it also looks ok.

Then my accusing finger was pointed to ASN.1 DER parser. More precisely, into my implementation of length octets. As you remember, we are checking “hardcoded” for high bit in first octet and then just read low bits of following octet into swapped array. Then removing all trailing zeros and subtract it from the actual length.This is what is used to be:

private static Func<BinaryReader, int> _getIntSize = r => {
   byte lb = 0×00;
   byte hb = 0×00;
   int c = 0;
   var b = r.ReadByte();
   if (b != 0×02) { //int
      return 0;
   }
   b = r.ReadByte();

   if (b == 0×81) {
      c = r.ReadByte(); //size
   } else
      if (b == 0×82) {
         hb = r.ReadByte(); //size
         lb = r.ReadByte();
         byte[] m = { lb, hb, 0×00, 0×00 };
         c = BitConverter.ToInt32(m, 0);
      } else {
         c = b; //got size
      }

   while (r.ReadByte() == 0×00) { //remove high zero
      c -= 1;
   }
   r.BaseStream.Seek(-1, SeekOrigin.Current); // last byte is not zero, go back;
   return c;
};

Code looks not very clean and too specific for such action. You, probably, familiar with this feeling of “oh my god, what I though about when I wrote it a year ago?”. So I decided to rewrite it.

According ISO 8825-1:2003  In the long form, the length octets shall consist of an initial octet and one or more subsequent octets. The initial octet shall be encoded as follows:
a) bit 8 shall be one;
b) bits 7 to 1 shall encode the number of subsequent octets in the length octets, as an unsigned binary integer
with bit 7 as the most significant bit;
c) the value 111111112 shall not be used.

Let’s do it

if ((b & 0×80) == 0×80) { //check whether long form
  var l = b & ~0×80;
  var d = r.ReadBytes(l);
  var dl = new byte[4];
  d.CopyTo(dl, 0);
  c = BitConverter.ToInt32(dl, 0);
}

Now it looks much nicer and correct. For short form nothing changed as well as for trailing zeros. Also the problem remains.

But wait, trailing zeros. Maybe the problem there? The only reason to have zero octets is indefinite length. In this case we should find single octet (one with last bit set only) at the beginning and two zero octets at the end. So in any case of having definite length of type one or two we can safely remove trailing zeros and subtract length or not remove it, the result will be the same since we are using the same endian. So what is the problem? Let’s open hex editor and compare “good” and “bad” keys.

image

Nothing special can be found in those two keys. Both looks ok, but, wait… How is it possible that exponent2 is smaller than exponent1 and coefficient?

image

Maybe it is because of trailing spaces? Let’s trim it.

image

As you can clearly see, in bad sample length of exponent2 is not equal to primes and other exponents. This is the reason why we were not able to read and use it. But how it works in OpenSSL? Let’s look into ASN1_INTEGER object. According DER spec, when we are encoding in long form, bit 8 will be set to 1, this means that inside ASN1_INTEGER if it starts from byte larger then 0×80 additional zero pad should be added. However when encoding negative integers, trailing zero will become 0xFF+additional zero at the end due to carry. In this case, OpenSSL serializer should add as many trailing zeros as it required by source number (prime or modulus in this case). This is what OpenSSL not doing.

In other case if the first byte is greater than 0×80 we should pad 0xFF, however if first byte is 0×80 and one of the following bytes is non-zero we should pad 0xFF to distinct 0×80 (which is indefinite length, remember?). Also if it followed by additional zeros it should not be padded.

The bad news, that all this is voided in all version of OpenSSL with win comments like “we have now cleared all the crap”, “a miss or crap from the other end” or “We’ve already written n zeros so we just append an extra one and set the first byte to a 1.”.

The good news, that nobody cares because this part is complicated and usually encoded and decoded by the same library. The only problem is when there are two implementations are in use: OpenSSL one and other which is strictly enforces the ISO 8825 . Just like in our case.

To workaround the problem we can add custom logic to add trailing zeros at the beginning of each integer when we know the target size, based of source.

var MODULUS = _readInt(reader); // assuming that modulus is correct
var E = _readInt(reader);
var D = _normalize(_readInt(reader), MODULUS.Length); // private exponent
var P = _normalize(_readInt(reader), MODULUS.Length / 2); // prime 1
var Q = _normalize(_readInt(reader), MODULUS.Length / 2); ; // prime 2
var DP = _normalize(_readInt(reader), P.Length);
var DQ = _normalize(_readInt(reader), Q.Length);
var IQ = _normalize(_readInt(reader), Q.Length);

private static byte[] _normalize(byte[] trg, int len) {
   byte[] r;
   if (len > trg.Length) {
      r = new byte[len];
      trg.CopyTo(r, len – trg.Length);
   } else {
      r = trg;
   }
   return r;
}

Or, better idea is to fix it in OpenSSL, but, probably, this will break thousands of applications, so it is almost sure that such fix will rest in peace inside dev branch for a long long time.

Be good people and do not follow specifications to win extra days of your life.

Self installable and runnable service or how to make generic service and console hybrid

Frankly, I thought that one of basic things in windows development, such as “debagability” and “installability” of services,  were changed for the last 10 years in development environments. However I was disappointed to discover, that nothing actually changed. You still cannot build easy to debug (in command line) service, which is also can be installed without special additional tools.

image

Even ServiceName/ServiceNameInstaller trick, is specific for the current assembly and cannot be used if your base class is not the one you are really using. This is not the only approach. Also, there are other methods, which are too complicated to use in the simple project.

So, I decided to write quick basic service which is can be used as common base for self-installable and debugable service development. Let’s start.

First of all we need an abstract service base:

public abstract class ServiceProvider : ServiceBase

Then it identification for derived classes

public static string Name;
public static string ProviderKind;

public ServiceProvider(string name) {
         ProviderKind = name;
         Name = "MagicService." + ProviderKind;
         ServiceName = Name;
      }

Now method to start it from the hosting application (e.g. command prompt) or run it if installed.

/// <summary>Starts the provider service in interactive mode.</summary>
public void Start(string[] args) {
   if (Environment.UserInteractive) {
      OnStart(args);
   } else {
      ServiceBase.Run(this);
   }
}

But how to install it? Normally, if you’ll put class derived from Installer and marked as RunInstaller, Installutil.exe can initialize it and install or uninstall the service.

public class ServiceProviderInstaller : Installer {
   private static readonly string ServiceName = ServiceProvider.Name;
  
   public ServiceProviderInstaller() {
      var processInstaller = new ServiceProcessInstaller {
         Account = ServiceAccount.LocalSystem
      };

      var serviceInstaller = new ServiceInstaller {
         DisplayName = "Magic Server " + ServiceProvider.ProviderKind + " Provider",
         Description = "Process the interface to the Magic service " + ServiceProvider.ProviderKind + " provider.",
         ServiceName = ServiceName,
         StartType = ServiceStartMode.Automatic,
      };
   
      this.Installers.Add(processInstaller);
      this.Installers.Add(serviceInstaller);
   }

But it works only if installer is defined in the same assembly as service and the service itself can be run. In our case, this is not true. Service is abstract and we allow to run service from any other assembly which is referenced to the base one. So what to do? Simple! Let’s create our own installer. We will create the private instance of installer inside the actual service itself and pass it as additional installer to basic TransactedInstaller. Also we’ll use calling (the actual running) assembly as the service reference.

/// <summary>Installs the provider service in interactive mode.</summary>
public void Install() {
   if (Environment.UserInteractive) {
      var ti = new TransactedInstaller();
      var spi = new ServiceProviderInstaller();
      ti.Installers.Add(spi);
      var path = "/assemblypath=" + Assembly.GetEntryAssembly().Location;
      var ctx = new InstallContext("", new string[] { path });
      ti.Context = ctx;
      ti.Install(new Hashtable());
   }
}

The same way we’ll do uninstaller

/// <summary>Uninstalls the provider service in interactive mode.</summary>
public void Uninstall() {
   if (Environment.UserInteractive) {
      var ti = new TransactedInstaller();
      var spi = new ServiceProviderInstaller();
      ti.Installers.Add(spi);
      var path = "/assemblypath=" + Assembly.GetEntryAssembly().Location;
      var ctx = new InstallContext("", new string[] { path });
      ti.Context = ctx;
      ti.Uninstall(null);
   }
}

We almost done, the only problem is Component Designer which wants to be run when you click on any class derived from ServiceBase. I know that Visual Studio developers wanted to do our life easier, but this designer (especially one cannot initialize abstract classes) is very annoying. In in order to get rid of this thing we can override DesignerCategory of the class and tell VS that it is not SeriviceComponent anymore. To do this all we need is one small attribute set on classes

[System.ComponentModel.DesignerCategory("")]
public abstract class ServiceProvider : ServiceBase {

[RunInstaller(true), System.ComponentModel.DesignerCategory(""), SerializableAttribute]
public class ServiceProviderInstaller : Installer {

 

Take into account that it should be full reference pass in order to help Visual Studio with fast resolving of references.

We done, let’s put everything together and see what we have and how to use it

/// <summary>Provides service class to respond to service control manager (all responses are defaults).</summary>
[System.ComponentModel.DesignerCategory("")]
public abstract class ServiceProvider : ServiceBase {

   public static string Name;
   public static string ProviderKind;

   public ServiceProvider(string name) {
      ProviderKind = name;
      Name = "Magic.Provider." + ProviderKind;
      ServiceName = Name;
   }

   /// <summary>Starts the provider service in interactive mode.</summary>
   public void Start(string[] args) {
      if (Environment.UserInteractive) {
         OnStart(args);
      } else {
         ServiceBase.Run(this);
      }
   }

   /// <summary>Installs the provider service in interactive mode.</summary>
   public void Install() {
      if (Environment.UserInteractive) {
         var ti = new TransactedInstaller();
         var spi = new ServiceProviderInstaller();
         ti.Installers.Add(spi);
         var path = "/assemblypath=" + Assembly.GetEntryAssembly().Location;
         var ctx = new InstallContext("", new string[] { path });
         ti.Context = ctx;
         ti.Install(new Hashtable());
      }
   }

   /// <summary>Uninstalls the provider service in interactive mode.</summary>
   public void Uninstall() {
      if (Environment.UserInteractive) {
         var ti = new TransactedInstaller();
         var spi = new ServiceProviderInstaller();
         ti.Installers.Add(spi);
         var path = "/assemblypath=" + Assembly.GetEntryAssembly().Location;
         var ctx = new InstallContext("", new string[] { path });
         ti.Context = ctx;
         ti.Uninstall(null);
      }
   }
}

[RunInstaller(true), System.ComponentModel.DesignerCategory(""), SerializableAttribute]
public class ServiceProviderInstaller : Installer {
   private static readonly string ServiceName = ServiceProvider.Name;
  
   public ServiceProviderInstaller() {
      var processInstaller = new ServiceProcessInstaller {
         Account = ServiceAccount.LocalSystem
      };

      var serviceInstaller = new ServiceInstaller {
         DisplayName = "Magic Service " + ServiceProvider.ProviderKind + " Provider",
         Description = "Process the interface to the Magic service " + ServiceProvider.ProviderKind + " provider.",
         ServiceName = ServiceName,
         StartType = ServiceStartMode.Automatic,
      };
   
      this.Installers.Add(processInstaller);
      this.Installers.Add(serviceInstaller);
   }

   protected override void OnCommitted(IDictionary savedState) {
      base.OnCommitted(savedState);
      var c = new ServiceController(ServiceName);
      c.Start();
   }
}

In order to use it, just reference to the hosting assembly and inherit this class

   public abstract class SampleService : ServiceProvider {

      /// <summary>Creates a new <see cref="SampleService"/> instance.</summary>
      public SampleService()
         : base("Sample") {
      }
}

And run it from command prompt:

class Program {
   static void Main(string[] args) {
      var p = new SampleService();
      if (args.Length > 0) {
         if (args[0] == "/i"){
            p.Install();
            return;
         }
         if (args[0] == "/u") {
            p.Uninstall();
            return;
         }
      }
      p.Start(null);
      Console.Read();
      p.Stop();
   }
}

We done. The only remaining thing is how to prevent component designer appearance on derived (SampleService) class. As for now I found no way to do this and asked collective intelligence to help me with it. Once I’ll have an answer I will update it here.

Be good people and have a good day!

UPD (25th Jan): The reason for this strange behavior is cached reference assemblies. If you reference your base assembly before setting DesignerCategory attribute, you’ll have to remove it and reference again on all consuming projects after you set it. Another evidence of Visual Studio developers laziness.

Video encoder and metadata reading by using Windows Media Foundation

At 1996-1997, together with Internet Explorer 3.0, Microsoft released API to work with media content (for example movies). They used to call it Quartz. This was very convenience set of interfaces and thus was widely used by industry. Now we call it DirectShow. Years passed, but DirectShow remains the same. It worked and worked very good. A couple of years ago Microsoft decided that change required and start to design new COM-based multimedia framework for Windows Vista, 7 and 8. They called it Media Foundation. This framework is much more generic and extensible, but also much more intricate. Today we’ll learn how to detect codec information of video or audio file by using Media Foundation in comparison to DirectShow SDK. So let’s start

Spaghetti COM

How to detect codec of media file by using DirectShow

This one is simple. Create new instance of media detector

var mediaDet = (IMediaDet)new MediaDet();

Put your file inside

var hr = mediaDet.put_Filename(fileName);

Enumerate media streams inside

int streamCount;
hr = mediaDet.get_OutputStreams(out streamCount);

Get each stream

for (int i = 0; i < streamCount; i++) {
hr = mediaDet.put_CurrentStream(i);

Detect it type

Guid streamType;
hr = mediaDet.get_StreamType(out streamType);

And if type if video, get FourCC codec code and decrypt

if (streamType == MediaType.Video) {
var mediaType = new AMMediaType();

hr = mediaDet.get_StreamMediaType(mediaType);

if (mediaType.formatType == FormatType.VideoInfo) {
var videoHeader = (VideoInfoHeader)Marshal.PtrToStructure(mediaType.formatPtr, typeof(VideoInfoHeader));

var fourCC = FourCCToString(videoHeader.BmiHeader.Compression);
}

You can also get stream length and retrieve other properties.

double streamLength;
hr = mediaDet.get_StreamLength(out streamLength);

And what’s about managed signatures of this API? No problem here it comes + FourCC decoder as bonus

   private static string FourCCToString(int fourcc) {
      byte[] bytes = new byte[4];

      bytes[0] = (byte)(fourcc & 0x000000ff); fourcc = fourcc >> 8;
      bytes[1] = (byte)(fourcc & 0x000000ff); fourcc = fourcc >> 8;
      bytes[2] = (byte)(fourcc & 0x000000ff); fourcc = fourcc >> 8;
      bytes[3] = (byte)(fourcc & 0x000000ff);

      return Encoding.ASCII.GetString(bytes);
   }

   static public class MediaType {
      public static readonly Guid Null = Guid.Empty;
      public static readonly Guid Video = new Guid(0×73646976, 0×0000, 0×0010, 0×80, 0×00, 0×00, 0xaa, 0×00, 0×38, 0x9b, 0×71);
      public static readonly Guid Audio = new Guid(0×73647561, 0×0000, 0×0010, 0×80, 0×00, 0×00, 0xaa, 0×00, 0×38, 0x9b, 0×71);
   }

   static public class FormatType {
      public static readonly Guid Null = Guid.Empty;

      public static readonly Guid None = new Guid(0x0F6417D6, 0xc318, 0x11d0, 0xa4, 0x3f, 0×00, 0xa0, 0xc9, 0×22, 0×31, 0×96);
      public static readonly Guid VideoInfo = new Guid(0x05589f80, 0xc356, 0x11ce, 0xbf, 0×01, 0×00, 0xaa, 0×00, 0×55, 0×59, 0x5a);
      public static readonly Guid VideoInfo2 = new Guid(0xf72a76A0, 0xeb0a, 0x11d0, 0xac, 0xe4, 0×00, 0×00, 0xc0, 0xcc, 0×16, 0xba);
      public static readonly Guid WaveEx = new Guid(0x05589f81, 0xc356, 0x11ce, 0xbf, 0×01, 0×00, 0xaa, 0×00, 0×55, 0×59, 0x5a);
      public static readonly Guid MpegVideo = new Guid(0x05589f82, 0xc356, 0x11ce, 0xbf, 0×01, 0×00, 0xaa, 0×00, 0×55, 0×59, 0x5a);
      public static readonly Guid MpegStreams = new Guid(0x05589f83, 0xc356, 0x11ce, 0xbf, 0×01, 0×00, 0xaa, 0×00, 0×55, 0×59, 0x5a);
      public static readonly Guid DvInfo = new Guid(0x05589f84, 0xc356, 0x11ce, 0xbf, 0×01, 0×00, 0xaa, 0×00, 0×55, 0×59, 0x5a);
      public static readonly Guid AnalogVideo = new Guid(0x0482dde0, 0×7817, 0x11cf, 0x8a, 0×03, 0×00, 0xaa, 0×00, 0x6e, 0xcb, 0×65);
      public static readonly Guid Mpeg2Video = new Guid(0xe06d80e3, 0xdb46, 0x11cf, 0xb4, 0xd1, 0×00, 0×80, 0x5f, 0x6c, 0xbb, 0xea);
      public static readonly Guid DolbyAC3 = new Guid(0xe06d80e4, 0xdb46, 0x11cf, 0xb4, 0xd1, 0×00, 0×80, 0x5f, 0x6c, 0xbb, 0xea);
      public static readonly Guid Mpeg2Audio = new Guid(0xe06d80e5, 0xdb46, 0x11cf, 0xb4, 0xd1, 0×00, 0×80, 0x5f, 0x6c, 0xbb, 0xea);
      public static readonly Guid WSS525 = new Guid(0xc7ecf04d, 0×4582, 0×4869, 0x9a, 0xbb, 0xbf, 0xb5, 0×23, 0xb6, 0x2e, 0xdf);
      public static readonly Guid ETDTFilter_Tagged = new Guid(0xC4C4C4D1, 0×0049, 0x4E2B, 0×98, 0xFB, 0×95, 0×37, 0xF6, 0xCE, 0×51, 0x6D);
      public static readonly Guid CPFilters_Processed = new Guid(0x6739b36f, 0x1d5f, 0x4ac2, 0×81, 0×92, 0×28, 0xbb, 0xe, 0×73, 0xd1, 0x6a);
   }

   [ComImport, Guid("65BD0711-24D2-4ff7-9324-ED2E5D3ABAFA")]
   public class MediaDet {
   }

   [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("65BD0710-24D2-4ff7-9324-ED2E5D3ABAFA")]
   public interface IMediaDet {
      [PreserveSig]
      int get_Filter([MarshalAs(UnmanagedType.IUnknown)] out object pVal);

      [PreserveSig]
      int put_Filter([MarshalAs(UnmanagedType.IUnknown)] object newVal);

      [PreserveSig]
      int get_OutputStreams(out int pVal);

      [PreserveSig]
      int get_CurrentStream(out int pVal);

      [PreserveSig]
      int put_CurrentStream(int newVal);

      [PreserveSig]
      int get_StreamType(out Guid pVal);

      [PreserveSig]
      int get_StreamTypeB([MarshalAs(UnmanagedType.BStr)] out string pVal);

      [PreserveSig]
      int get_StreamLength(out double pVal);

      [PreserveSig]
      int get_Filename([MarshalAs(UnmanagedType.BStr)] out string pVal);

      [PreserveSig]
      int put_Filename([MarshalAs(UnmanagedType.BStr)] string newVal);

      [PreserveSig]
      int GetBitmapBits(double StreamTime, out int pBufferSize, [In] IntPtr pBuffer, int Width, int Height);

      [PreserveSig]
      int WriteBitmapBits(double StreamTime, int Width, int Height, [In, MarshalAs(UnmanagedType.BStr)] string ilename);

      [PreserveSig]
      int get_StreamMediaType([Out, MarshalAs(UnmanagedType.LPStruct)] AMMediaType pVal);

      [PreserveSig]
      int GetSampleGrabber(out ISampleGrabber ppVal);

      [PreserveSig]
      int get_FrameRate(out double pVal);

      [PreserveSig]
      int EnterBitmapGrabMode(double SeekTime);
   }

   [ComImport, Guid("6B652FFF-11FE-4fce-92AD-0266B5D7C78F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
   public interface ISampleGrabber {
      [PreserveSig]
      int SetOneShot([In, MarshalAs(UnmanagedType.Bool)] bool OneShot);

      [PreserveSig]
      int SetMediaType([In, MarshalAs(UnmanagedType.LPStruct)] AMMediaType pmt);

      [PreserveSig]
      int GetConnectedMediaType([Out, MarshalAs(UnmanagedType.LPStruct)] AMMediaType pmt);

      [PreserveSig]
      int SetBufferSamples([In, MarshalAs(UnmanagedType.Bool)] bool BufferThem);

      [PreserveSig]
      int GetCurrentBuffer(ref int pBufferSize, IntPtr pBuffer);

      [PreserveSig]
      int GetCurrentSample(out IMediaSample ppSample);

      [PreserveSig]
      int SetCallback(ISampleGrabberCB pCallback, int WhichMethodToCallback);
   }

   [ComImport, Guid("0579154A-2B53-4994-B0D0-E773148EFF85"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
   public interface ISampleGrabberCB {
      [PreserveSig]
      int SampleCB(double SampleTime, IMediaSample pSample);

      [PreserveSig]
      int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen);
   }

   [ComImport, Guid("56a8689a-0ad4-11ce-b03a-0020af0ba770"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
   public interface IMediaSample {
      [PreserveSig]
      int GetPointer([Out] out IntPtr ppBuffer);

      [PreserveSig]
      int GetSize();

      [PreserveSig]
      int GetTime([Out] out long pTimeStart, [Out] out long pTimeEnd);

      [PreserveSig]
      int SetTime([In, MarshalAs(UnmanagedType.LPStruct)] LONG pTimeStart, [In, MarshalAs(UnmanagedType.LPStruct)] LONG pTimeEnd);

      [PreserveSig]
      int IsSyncPoint();

      [PreserveSig]
      int SetSyncPoint([In, MarshalAs(UnmanagedType.Bool)] bool bIsSyncPoint);

      [PreserveSig]
      int IsPreroll();

      [PreserveSig]
      int SetPreroll([In, MarshalAs(UnmanagedType.Bool)] bool bIsPreroll);

      [PreserveSig]
      int GetActualDataLength();

      [PreserveSig]
      int SetActualDataLength([In] int len);

      [PreserveSig]
      int GetMediaType([Out, MarshalAs(UnmanagedType.LPStruct)] out AMMediaType ppMediaType);

      [PreserveSig]
      int SetMediaType([In, MarshalAs(UnmanagedType.LPStruct)] AMMediaType pMediaType);

      [PreserveSig]
      int IsDiscontinuity();

      [PreserveSig]
      int SetDiscontinuity([In, MarshalAs(UnmanagedType.Bool)] bool bDiscontinuity);

      [PreserveSig]
      int GetMediaTime([Out] out long pTimeStart, [Out] out long pTimeEnd);

      [PreserveSig]
      int SetMediaTime([In, MarshalAs(UnmanagedType.LPStruct)] LONG pTimeStart, [In, MarshalAs(UnmanagedType.LPStruct)] LONG pTimeEnd);
   }

   [StructLayout(LayoutKind.Sequential)]
   public class AMMediaType {
      public Guid majorType;
      public Guid subType;
      [MarshalAs(UnmanagedType.Bool)]
      public bool fixedSizeSamples;
      [MarshalAs(UnmanagedType.Bool)]
      public bool temporalCompression;
      public int sampleSize;
      public Guid formatType;
      public IntPtr unkPtr;
      public int formatSize;
      public IntPtr formatPtr;
   }

   [StructLayout(LayoutKind.Sequential)]
   public class VideoInfoHeader {
      public RECT SrcRect;
      public RECT TargetRect;
      public int BitRate;
      public int BitErrorRate;
      public long AvgTimePerFrame;
      public BitmapInfoHeader BmiHeader;
   }

   [StructLayout(LayoutKind.Sequential, Pack = 2)]
   public class BitmapInfoHeader {
      public int Size;
      public int Width;
      public int Height;
      public short Planes;
      public short BitCount;
      public int Compression;
      public int ImageSize;
      public int XPelsPerMeter;
      public int YPelsPerMeter;
      public int ClrUsed;
      public int ClrImportant;
   }

   [StructLayout(LayoutKind.Sequential)]
   public class RECT {
      public int left;
      public int top;
      public int right;
      public int bottom;
   }

   [StructLayout(LayoutKind.Sequential)]
   public class LONG {
      private long Value;
   }
}

Looks simple? It is. However there are two problems. One is that all those interfaces defined as deprecated by Microsoft. Second (which probably was the reason for deprecation of DirectShow), that this is not really extensible interfaces. Now let’s see how it done in Media Foundation.

How to detect codec of media file by using Media Foundation

First of all we need to create source of the resolver

IMFSourceResolver res;
var hr = MFCreateSourceResolver(out res);

Then create the actual resolver object

IMFMediaSource source = null;
var objectType = MF_OBJECT_TYPE.Invalid;
object srs;
hr = res.CreateObjectFromURL(filePath, MFResolution.MediaSource, null, out objectType, out srs);
objectType == MF_OBJECT_TYPE.MediaSource;
source = (IMFMediaSource)srs;

When we have it we’ll need to create descriptor.

IMFPresentationDescriptor desc;
source.CreatePresentationDescriptor(out desc);

Now we have everything to get streams count and retrieve streams.

int count;
desc.GetStreamDescriptorCount(out count);

for (int i = 0; i < count; i++) {
IMFStreamDescriptor descriptor;
bool selected;
desc.GetStreamDescriptorByIndex(i, out selected, out descriptor);
if (selected) {

Let’s get type handlers to have format

IMFMediaTypeHandler handler;
descriptor.GetMediaTypeHandler(out handler);
IMFMediaType type;
handler.GetCurrentMediaType(out type);

Guid mediaType;
type.GetMajorType(out mediaType);
if (mediaType == MFMediaType.Video) {

And then actual media type and decoder code

hr = MFCreateMFVideoFormatFromMFMediaType(type, out format, out size));
var fourCC = FourCCToString(format.surfaceInfo.Format);

Looks more complicated than the DirectShow approach. Let’s take a look into actual interp definitions.

[DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = false)]
public static extern void MFShutdown();

[DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = false)]
public static extern void MFStartup(int Version, MFSTARTUP dwFlags);

[DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = false)]
public static extern int MFCreateMFVideoFormatFromMFMediaType([In] IMFMediaType pMFType, out MFVIDEOFORMAT ppMFVF, out int pcbSize);
     
[DllImport("mf.dll", ExactSpelling = true, PreserveSig = false)]
public static extern int MFCreateSourceResolver(out IMFSourceResolver ppISourceResolver);

[DllImport("mf.dll", ExactSpelling = true, PreserveSig = false)]
public static extern void MFGetService([In, MarshalAs(UnmanagedType.Interface)] object punkObject, [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidService, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, [Out, MarshalAs(UnmanagedType.Interface)] out object ppvObject);

#region INTERFACES

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("FBE5A32D-A497-4B61-BB85-97B1A848A6E3")]
public interface IMFSourceResolver {
   int CreateObjectFromURL([In, MarshalAs(UnmanagedType.LPWStr)] string pwszURL, [In] MFResolution dwFlags, IPropertyStore pProps, out MF_OBJECT_TYPE pObjectType, [MarshalAs(UnmanagedType.IUnknown)] out object ppObject);

   int CreateObjectFromByteStream([In, MarshalAs(UnmanagedType.Interface)] IMFByteStream pByteStream, [In, MarshalAs(UnmanagedType.LPWStr)] string pwszURL, [In] MFResolution dwFlags, [In, MarshalAs(UnmanagedType.Interface)] IPropertyStore pProps, out MF_OBJECT_TYPE pObjectType, [MarshalAs(UnmanagedType.IUnknown)] out object ppObject);

   int BeginCreateObjectFromURL([In, MarshalAs(UnmanagedType.LPWStr)] string pwszURL, MFResolution dwFlags, IPropertyStore pProps, [MarshalAs(UnmanagedType.IUnknown)] out object ppIUnknownCancelCookie, IMFAsyncCallback pCallback, [In, MarshalAs(UnmanagedType.IUnknown)] object punkState);

   int EndCreateObjectFromURL(IMFAsyncResult pResult, out MF_OBJECT_TYPE pObjectType, [MarshalAs(UnmanagedType.Interface)] out object ppObject);

   int BeginCreateObjectFromByteStream([In, MarshalAs(UnmanagedType.Interface)] IMFByteStream pByteStream, [In, MarshalAs(UnmanagedType.LPWStr)] string pwszURL, [In] MFResolution dwFlags, IPropertyStore pProps, [MarshalAs(UnmanagedType.IUnknown)] out object ppIUnknownCancelCookie, IMFAsyncCallback pCallback, [MarshalAs(UnmanagedType.IUnknown)] object punkState);

   int EndCreateObjectFromByteStream(IMFAsyncResult pResult, out MF_OBJECT_TYPE pObjectType, [MarshalAs(UnmanagedType.IUnknown)] out object ppObject);

   int CancelObjectCreation([In, MarshalAs(UnmanagedType.IUnknown)] object pIUnknownCancelCookie);
}

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("AD4C1B00-4BF7-422F-9175-756693D9130D")]
public interface IMFByteStream {
   void GetCapabilities(out MFBYTESTREAM pdwCapabilities);

   void GetLength(out long pqwLength);

   void SetLength([In] long qwLength);

   void GetCurrentPosition(out long pqwPosition);

   void SetCurrentPosition([In] long qwPosition);

   void IsEndOfStream([MarshalAs(UnmanagedType.Bool)] out bool pfEndOfStream);

   void Read(IntPtr pb, [In] int cb, out int pcbRead);

   void BeginRead(IntPtr pb, [In] int cb, [In, MarshalAs(UnmanagedType.Interface)] IMFAsyncCallback pCallback, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnkState);

   void EndRead([In, MarshalAs(UnmanagedType.Interface)] IMFAsyncResult pResult, out int pcbRead);

   void Write(IntPtr pb, [In] int cb, out int pcbWritten);

   void BeginWrite(IntPtr pb, [In] int cb, [In, MarshalAs(UnmanagedType.Interface)] IMFAsyncCallback pCallback, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnkState);

   void EndWrite([In, MarshalAs(UnmanagedType.Interface)] IMFAsyncResult pResult, out int pcbWritten);

   void Seek([In] MFBYTESTREAM_SEEK_ORIGIN SeekOrigin, [In] long llSeekOffset, [In] MFBYTESTREAM_SEEK_FLAG dwSeekFlags, out long pqwCurrentPosition);

   void Flush();

   void Close();
}

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")]
public interface IPropertyStore {
   [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
   void GetCount(out uint cProps);

   [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
   void GetAt([In] uint iProp, out PROPERTYKEY pkey);

   [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
   void GetValue([In] PROPERTYKEY key, out PROPVARIANT pv);

   [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
   void SetValue([In] PROPERTYKEY key, [In] ref PROPVARIANT  pv);

   [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
   void Commit();
}

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("A27003CF-2354-4F2A-8D6A-AB7CFF15437E")]
public interface IMFAsyncCallback {
   void GetParameters(out MFASYNC pdwFlags, out MFASYNC_CALLBACK_QUEUE pdwQueue);

   void Invoke([In, MarshalAs(UnmanagedType.Interface)] IMFAsyncResult pAsyncResult);
}

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("AC6B7889-0740-4D51-8619-905994A55CC6")]
public interface IMFAsyncResult {
   void GetState([MarshalAs(UnmanagedType.IUnknown)] out object ppunkState);

   [PreserveSig]
   int GetStatus();

   void SetStatus([In, MarshalAs(UnmanagedType.Error)] int hrStatus);

   void GetObject([MarshalAs(UnmanagedType.Interface)] out object ppObject);

   [PreserveSig]
   IntPtr GetStateNoAddRef();
}

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("DF598932-F10C-4E39-BBA2-C308F101DAA3")]
public interface IMFMediaEvent : IMFAttributes {
   #region IMFAttributes methods

   new void GetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr pValue);

   new void GetItemType([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out MF_ATTRIBUTE_TYPE pType);

   new void CompareItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);

   new void Compare([MarshalAs(UnmanagedType.Interface)] IMFAttributes pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);

   new void GetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int punValue);

   new void GetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out long punValue);

   new void GetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out double pfValue);

   new void GetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out Guid pguidValue);

   new void GetStringLength([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcchLength);

   new void GetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszValue, int cchBufSize, out int pcchLength);

   new void GetAllocatedString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] out string ppwszValue, out int pcchLength);

   new void GetBlobSize([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcbBlobSize);

   new void GetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pBuf, int cbBufSize, out int pcbBlobSize);

   // Use GetBlob instead of this
   new void GetAllocatedBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out IntPtr ip, out int pcbSize);

   new void GetUnknown([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppv);

   new void SetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value);

   new void DeleteItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey);

   new void DeleteAllItems();

   new void SetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, int unValue);

   new void SetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, long unValue);

   new void SetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, double fValue);

   new void SetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidValue);

   new void SetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPWStr)] string wszValue);

   new void SetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pBuf, int cbBufSize);

   new void SetUnknown([MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnknown);

   new void LockStore();

   new void UnlockStore();

   new void GetCount(out int pcItems);

   new void GetItemByIndex(int unIndex, out Guid pguidKey, IntPtr pValue);

   new void CopyAllItems([In, MarshalAs(UnmanagedType.Interface)] IMFAttributes pDest);

   #endregion
   void GetType(out MediaEventType pmet);

   void GetExtendedType(out Guid pguidExtendedType);

   void GetStatus([MarshalAs(UnmanagedType.Error)] out int phrStatus);

   void GetValue([In, Out] ref object pvValue);
}

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("03CB2711-24D7-4DB6-A17F-F3A7A479A536")]
public interface IMFPresentationDescriptor : IMFAttributes {

   #region IMFAttributes methods

   new void GetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr pValue);

   new void GetItemType([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out MF_ATTRIBUTE_TYPE pType);

   new void CompareItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);

   new void Compare([MarshalAs(UnmanagedType.Interface)] IMFAttributes pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);

   new void GetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int punValue);

   new void GetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out long punValue);

   new void GetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out double pfValue);

   new void GetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out Guid pguidValue);

   new void GetStringLength([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcchLength);

   new void GetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszValue, int cchBufSize, out int pcchLength);

   new void GetAllocatedString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] out string ppwszValue, out int pcchLength);

   new void GetBlobSize([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcbBlobSize);

   new void GetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pBuf, int cbBufSize, out int pcbBlobSize);

   // Use GetBlob instead of this
   new void GetAllocatedBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out IntPtr ip, out int pcbSize);

   new void GetUnknown([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppv);

   new void SetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value);

   new void DeleteItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey);

   new void DeleteAllItems();

   new void SetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, int unValue);

   new void SetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, long unValue);

   new void SetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, double fValue);

   new void SetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidValue);

   new void SetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPWStr)] string wszValue);

   new void SetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pBuf, int cbBufSize);

   new void SetUnknown([MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnknown);

   new void LockStore();

   new void UnlockStore();

   new void GetCount(out int pcItems);

   new void GetItemByIndex(int unIndex, out Guid pguidKey, IntPtr pValue);

   new void CopyAllItems([In, MarshalAs(UnmanagedType.Interface)] IMFAttributes pDest);

   #endregion

   void GetStreamDescriptorCount(out int pdwDescriptorCount);

   void GetStreamDescriptorByIndex([In] int dwIndex, [MarshalAs(UnmanagedType.Bool)] out bool pfSelected, [MarshalAs(UnmanagedType.Interface)] out IMFStreamDescriptor ppDescriptor);

   void SelectStream([In] int dwDescriptorIndex);

   void DeselectStream([In] int dwDescriptorIndex);

   void Clone([MarshalAs(UnmanagedType.Interface)] out IMFPresentationDescriptor ppPresentationDescriptor);
}

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("56C03D9C-9DBB-45F5-AB4B-D80F47C05938")]
public interface IMFStreamDescriptor : IMFAttributes {
   #region IMFAttributes methods

   new void GetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr pValue);

   new void GetItemType([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out MF_ATTRIBUTE_TYPE pType);

   new void CompareItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);

   new void Compare([MarshalAs(UnmanagedType.Interface)] IMFAttributes pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);

   new void GetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int punValue);

   new void GetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out long punValue);

   new void GetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out double pfValue);

   new void GetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out Guid pguidValue);

   new void GetStringLength([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcchLength);

   new void GetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszValue, int cchBufSize, out int pcchLength);

   new void GetAllocatedString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] out string ppwszValue, out int pcchLength);

   new void GetBlobSize([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcbBlobSize);

   new void GetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pBuf, int cbBufSize, out int pcbBlobSize);

   // Use GetBlob instead of this
   new void GetAllocatedBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out IntPtr ip, out int pcbSize);

   new void GetUnknown([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppv);

   new void SetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value);

   new void DeleteItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey);

   new void DeleteAllItems();

   new void SetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, int unValue);

   new void SetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, long unValue);

   new void SetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, double fValue);

   new void SetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidValue);

   new void SetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPWStr)] string wszValue);

   new void SetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pBuf, int cbBufSize);

   new void SetUnknown([MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnknown);

   new void LockStore();

   new void UnlockStore();

   new void GetCount(out int pcItems);

   new void GetItemByIndex(int unIndex, out Guid pguidKey, IntPtr pValue);

   new void CopyAllItems([In, MarshalAs(UnmanagedType.Interface)] IMFAttributes pDest);

   #endregion

   void GetStreamIdentifier(out int pdwStreamIdentifier);

   void GetMediaTypeHandler([MarshalAs(UnmanagedType.Interface)] out IMFMediaTypeHandler ppMediaTypeHandler);
}

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("44AE0FA8-EA31-4109-8D2E-4CAE4997C555")]
public interface IMFMediaType : IMFAttributes {

   #region IMFAttributes methods

   new void GetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr pValue);

   new void GetItemType([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out MF_ATTRIBUTE_TYPE pType);

   new void CompareItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);

   new void Compare([MarshalAs(UnmanagedType.Interface)] IMFAttributes pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);

   new void GetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int punValue);

   new void GetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out long punValue);

   new void GetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out double pfValue);

   new void GetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out Guid pguidValue);

   new void GetStringLength([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcchLength);

   new void GetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszValue, int cchBufSize, out int pcchLength);

   new void GetAllocatedString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] out string ppwszValue, out int pcchLength);

   new void GetBlobSize([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcbBlobSize);

   new void GetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pBuf, int cbBufSize, out int pcbBlobSize);

   // Use GetBlob instead of this
   new void GetAllocatedBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out IntPtr ip, out int pcbSize);

   new void GetUnknown([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppv);

   new void SetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value);

   new void DeleteItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey);

   new void DeleteAllItems();

   new void SetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, int unValue);

   new void SetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, long unValue);

   new void SetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, double fValue);

   new void SetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidValue);

   new void SetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPWStr)] string wszValue);

   new void SetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pBuf, int cbBufSize);

   new void SetUnknown([MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnknown);

   new void LockStore();

   new void UnlockStore();

   new void GetCount(out int pcItems);

   new void GetItemByIndex(int unIndex, out Guid pguidKey, IntPtr pValue);

   new void CopyAllItems([In, MarshalAs(UnmanagedType.Interface)] IMFAttributes pDest);

   #endregion

   void GetMajorType(out Guid pguidMajorType);

   void IsCompressedFormat([MarshalAs(UnmanagedType.Bool)] out bool pfCompressed);

   [PreserveSig]
   int IsEqual([In, MarshalAs(UnmanagedType.Interface)] IMFMediaType pIMediaType, out MF_MEDIATYPE_EQUAL pdwFlags);

   void GetRepresentation([In, MarshalAs(UnmanagedType.Struct)] Guid guidRepresentation, out IntPtr ppvRepresentation);

   void FreeRepresentation([In, MarshalAs(UnmanagedType.Struct)] Guid guidRepresentation, [In] IntPtr pvRepresentation);
}

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("2CD2D921-C447-44A7-A13C-4ADABFC247E3")]
public interface IMFAttributes {
   void GetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr pValue);

   void GetItemType([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out MF_ATTRIBUTE_TYPE pType);

   void CompareItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);

   void Compare([MarshalAs(UnmanagedType.Interface)] IMFAttributes pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);

   void GetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int punValue);

   void GetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out long punValue);

   void GetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out double pfValue);

   void GetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out Guid pguidValue);

   void GetStringLength([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcchLength);

   void GetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszValue, int cchBufSize, out int pcchLength);

   void GetAllocatedString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] out string ppwszValue, out int pcchLength);

   void GetBlobSize([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcbBlobSize);

   void GetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pBuf, int cbBufSize, out int pcbBlobSize);

   void GetAllocatedBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out IntPtr ip, out int pcbSize);

   void GetUnknown([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out object ppv);

   void SetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value);

   void DeleteItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey);

   void DeleteAllItems();

   void SetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, int unValue);

   void SetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, long unValue);

   void SetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, double fValue);

   void SetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidValue);

   void SetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPWStr)] string wszValue);

   void SetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pBuf, int cbBufSize);

   void SetUnknown([MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnknown);

   void LockStore();

   void UnlockStore();

   void GetCount(out int pcItems);

   void GetItemByIndex(int unIndex, out Guid pguidKey, IntPtr pValue);

   void CopyAllItems([In, MarshalAs(UnmanagedType.Interface)] IMFAttributes pDest);
}

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("2CD0BD52-BCD5-4B89-B62C-EADC0C031E7D")]
public interface IMFMediaEventGenerator {
   void GetEvent([In] IMFMediaEvent dwFlags, [MarshalAs(UnmanagedType.Interface)] out IMFMediaEvent ppEvent);

   void BeginGetEvent([In, MarshalAs(UnmanagedType.Interface)] IMFAsyncCallback pCallback, [In, MarshalAs(UnmanagedType.IUnknown)] object o);

   void EndGetEvent(IMFAsyncResult pResult, out IMFMediaEvent ppEvent);

   void QueueEvent([In] MediaEventType met, [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidExtendedType, [In] int hrStatus, [In] ref object pvValue);
}

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("E93DCF6C-4B07-4E1E-8123-AA16ED6EADF5")]
public interface IMFMediaTypeHandler {
   void IsMediaTypeSupported([In, MarshalAs(UnmanagedType.Interface)] IMFMediaType pMediaType, IntPtr ppMediaType);

   void GetMediaTypeCount(out int pdwTypeCount);

   void GetMediaTypeByIndex([In] int dwIndex, [MarshalAs(UnmanagedType.Interface)] out IMFMediaType ppType);

   void SetCurrentMediaType([In, MarshalAs(UnmanagedType.Interface)] IMFMediaType pMediaType);

   void GetCurrentMediaType([MarshalAs(UnmanagedType.Interface)] out IMFMediaType ppMediaType);

   void GetMajorType(out Guid pguidMajorType);
}

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("279A808D-AEC7-40C8-9C6B-A6B492C78A66")]
public interface IMFMediaSource : IMFMediaEventGenerator {
   #region IMFMediaEventGenerator methods

   #pragma warning disable 109
   new void GetEvent([In] MF_EVENT_FLAG dwFlags, [MarshalAs(UnmanagedType.Interface)] out IMFMediaEvent ppEvent);
   #pragma warning restore 109

   new void BeginGetEvent([In, MarshalAs(UnmanagedType.Interface)] IMFAsyncCallback pCallback, [In, MarshalAs(UnmanagedType.IUnknown)] object o);

   new void EndGetEvent(IMFAsyncResult pResult, out IMFMediaEvent ppEvent);

   new void QueueEvent([In] MediaEventType met, [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidExtendedType, [In] int hrStatus, [In] ref object pvValue);

   #endregion

   void GetCharacteristics(out MFMEDIASOURCE_CHARACTERISTICS pdwCharacteristics);

   void CreatePresentationDescriptor(out IMFPresentationDescriptor ppPresentationDescriptor);

   void Start([In, MarshalAs(UnmanagedType.Interface)] IMFPresentationDescriptor pPresentationDescriptor, [In, MarshalAs(UnmanagedType.LPStruct)] Guid pguidTimeFormat, [In] ref object pvarStartPosition);

   void Stop();

   void Pause();

   void Shutdown();
}

#endregion

Plus some data objects

#region WM

      #region STRUCTS
      #pragma warning restore 618

      [StructLayout(LayoutKind.Sequential, Pack = 8)]
      public class MFVIDEOFORMAT {
         public int dwSize;
         public MFVideoInfo videoInfo;
         public Guid guidFormat;
         public MFVideoCompressedInfo compressedInfo;
         public MFVideoSurfaceInfo surfaceInfo;
      }

      [StructLayout(LayoutKind.Sequential, Pack = 4)]
      public struct MFVideoSurfaceInfo {
         public int Format;
         public int PaletteEntries;
         public MFPaletteEntry[] Palette;
      }

      [StructLayout(LayoutKind.Explicit, Pack = 1)]
      public struct MFPaletteEntry {
         [FieldOffset(0)]
         public MFARGB ARGB;
         [FieldOffset(0)]
         public MFAYUVSample AYCbCr;
      }

      [StructLayout(LayoutKind.Sequential, Pack = 1)]
      public struct MFAYUVSample {
         public byte bCrValue;
         public byte bCbValue;
         public byte bYValue;
         public byte bSampleAlpha8;
      }

      [StructLayout(LayoutKind.Sequential, Pack = 1)]
      public struct MFARGB {
         public byte rgbBlue;
         public byte rgbGreen;
         public byte rgbRed;
         public byte rgbAlpha;
      }

      [StructLayout(LayoutKind.Sequential, Pack = 8)]
      public struct MFVideoCompressedInfo {
         public long AvgBitrate;
         public long AvgBitErrorRate;
         public int MaxKeyFrameSpacing;
      }

      [StructLayout(LayoutKind.Sequential, Pack = 8)]
      public struct MFVideoInfo {
         public int dwWidth;
         public int dwHeight;
         public MFRatio PixelAspectRatio;
         public MFVideoChromaSubsampling SourceChromaSubsampling;
         public MFVideoInterlaceMode InterlaceMode;
         public MFVideoTransferFunction TransferFunction;
         public MFVideoPrimaries ColorPrimaries;
         public MFVideoTransferMatrix TransferMatrix;
         public MFVideoLighting SourceLighting;
         public MFRatio FramesPerSecond;
         public MFNominalRange NominalRange;
         public MFVideoArea GeometricAperture;
         public MFVideoArea MinimumDisplayAperture;
         public MFVideoArea PanScanAperture;
         public MFVideoFlags VideoFlags;
      }

      [StructLayout(LayoutKind.Sequential, Pack = 4)]
      public struct MFRatio {
         public int Numerator;
         public int Denominator;

         public MFRatio(int n, int d) {
            Numerator = n;
            Denominator = d;
         }
      }

      [StructLayout(LayoutKind.Sequential, Pack = 4)]
      public class MFVideoArea {
         public MFOffset OffsetX;
         public MFOffset OffsetY;
         public SIZE Area;

         public MFVideoArea() {
            OffsetX = new MFOffset();
            OffsetY = new MFOffset();
         }

         public MFVideoArea(float x, float y, int width, int height) {
            OffsetX = new MFOffset(x);
            OffsetY = new MFOffset(y);
            Area = new SIZE(width, height);
         }

         public void MakeArea(float x, float y, int width, int height) {
            OffsetX.MakeOffset(x);
            OffsetY.MakeOffset(y);
            Area.cx = width;
            Area.cy = height;
         }
      }

      [StructLayout(LayoutKind.Sequential, Pack = 2)]
      public class MFOffset {
         public short fract;
         public short Value;

         public MFOffset() {
         }

         public MFOffset(float v) {
            Value = (short)v;
            fract = (short)(65536 * (v – Value));
         }

         public void MakeOffset(float v) {
            Value = (short)v;
            fract = (short)(65536 * (v – Value));
         }

         public float GetOffset() {
            return ((float)Value) + (((float)fract) / 65536.0f);
         }
      }
      #endregion

      #region ENUMS
      public enum MFVideoInterlaceMode {
         FieldInterleavedLowerFirst = 4,
         FieldInterleavedUpperFirst = 3,
         FieldSingleLower = 6,
         FieldSingleUpper = 5,
         ForceDWORD = 0x7fffffff,
         Last = 8,
         MixedInterlaceOrProgressive = 7,
         Progressive = 2,
         Unknown = 0
      }

      public enum MFVideoChromaSubsampling {
         Cosited = 7,
         DV_PAL = 6,
         ForceDWORD = 0x7fffffff,
         Horizontally_Cosited = 4,
         Last = 8,
         MPEG1 = 1,
         MPEG2 = 5,
         ProgressiveChroma = 8,
         Unknown = 0,
         Vertically_AlignedChromaPlanes = 1,
         Vertically_Cosited = 2
      }

      public enum MFVideoTransferFunction {
         Func10 = 1,
         Func18 = 2,
         Func20 = 3,
         Func22 = 4,
         Func240M = 6,
         Func28 = 8,
         Func709 = 5,
         ForceDWORD = 0x7fffffff,
         Last = 9,
         sRGB = 7,
         Unknown = 0
      }

      public enum MFVideoPrimaries {
         BT470_2_SysBG = 4,
         BT470_2_SysM = 3,
         BT709 = 2,
         EBU3213 = 7,
         ForceDWORD = 0x7fffffff,
         Last = 9,
         reserved = 1,
         SMPTE_C = 8,
         SMPTE170M = 5,
         SMPTE240M = 6,
         Unknown = 0
      }

      public enum MFVideoTransferMatrix {
         BT601 = 2,
         BT709 = 1,
         ForceDWORD = 0x7fffffff,
         Last = 4,
         SMPTE240M = 3,
         Unknown = 0
      }

      public enum MFVideoLighting {
         Bright = 1,
         Dark = 4,
         Dim = 3,
         ForceDWORD = 0x7fffffff,
         Last = 5,
         Office = 2,
         Unknown = 0
      }

      public enum MFNominalRange {
         MFNominalRange_0_255 = 1,
         MFNominalRange_16_235 = 2,
         MFNominalRange_48_208 = 3,
         MFNominalRange_ForceDWORD = 0x7fffffff,
         MFNominalRange_Last = 4,
         MFNominalRange_Normal = 1,
         MFNominalRange_Unknown = 0,
         MFNominalRange_Wide = 2
      }

      [Flags]
      public enum MFVideoFlags : long {
         PAD_TO_Mask = 0×0001 | 0×0002,
         PAD_TO_None = 0 * 0×0001,
         PAD_TO_4x3 = 1 * 0×0001,
         PAD_TO_16x9 = 2 * 0×0001,
         SrcContentHintMask = 0×0004 | 0×0008 | 0×0010,
         SrcContentHintNone = 0 * 0×0004,
         SrcContentHint16x9 = 1 * 0×0004,
         SrcContentHint235_1 = 2 * 0×0004,
         AnalogProtected = 0×0020,
         DigitallyProtected = 0×0040,
         ProgressiveContent = 0×0080,
         FieldRepeatCountMask = 0×0100 | 0×0200 | 0×0400,
         FieldRepeatCountShift = 8,
         ProgressiveSeqReset = 0×0800,
         PanScanEnabled = 0×20000,
         LowerFieldFirst = 0×40000,
         BottomUpLinearRep = 0×80000,
         DXVASurface = 0×100000,
         RenderTargetSurface = 0×400000,
         ForceQWORD = 0x7FFFFFFF
      }

      [Flags]
      public enum MF_EVENT_FLAG {
         None = 0,
         NoWait = 0×00000001
      }

      public enum MFASYNC_CALLBACK_QUEUE {
         Undefined = 0×00000000,
         Standard = 0×00000001,
         RT = 0×00000002,
         IO = 0×00000003,
         Timer = 0×00000004,
         LongFunction = 0×00000007,
         PrivateMask = unchecked((int)0xFFFF0000),
         All = unchecked((int)0xFFFFFFFF)
      }

      [Flags]
      public enum MFASYNC {
         None = 0,
         FastIOProcessingCallback = 0×00000001,
         SignalCallback = 0×00000002
      }

      public enum MFSTARTUP {
         NoSocket = 0×1,
         Lite = 0×1,
         Full = 0
      }

      [Flags]
      public enum MFResolution {
         None = 0×0,
         MediaSource = 0×00000001,
         ByteStream = 0×00000002,
         ContentDoesNotHaveToMatchExtensionOrMimeType = 0×00000010,
         KeepByteStreamAliveOnFail = 0×00000020,
         Read = 0×00010000,
         Write = 0×00020000
      }

      [Flags]
      public enum MFBYTESTREAM {
         None = 0×00000000,
         IsReadable = 0×00000001,
         IsWritable = 0×00000002,
         IsSeekable = 0×00000004,
         IsRemote = 0×00000008,
         IsDirectory = 0×00000080,
         HasSlowSeek = 0×00000100,
         IsPartiallyDownloaded = 0×00000200
      }

      public enum MFBYTESTREAM_SEEK_ORIGIN {
         Begin,
         Current
      }

      [Flags]
      public enum MFBYTESTREAM_SEEK_FLAG {
         None = 0,
         CancelPendingIO = 1
      }

      public enum MF_OBJECT_TYPE {
         MediaSource,
         ByteStream,
         Invalid
      }

      public enum MediaEventType {
         MEUnknown = 0,
         MEError = (MEUnknown + 1),
         MEExtendedType = (MEError + 1),
         MESessionUnknown = 100,
         MESessionTopologySet = (MESessionUnknown + 1),
         MESessionTopologiesCleared = (MESessionTopologySet + 1),
         MESessionStarted = (MESessionTopologiesCleared + 1),
         MESessionPaused = (MESessionStarted + 1),
         MESessionStopped = (MESessionPaused + 1),
         MESessionClosed = (MESessionStopped + 1),
         MESessionEnded = (MESessionClosed + 1),
         MESessionRateChanged = (MESessionEnded + 1),
         MESessionScrubSampleComplete = (MESessionRateChanged + 1),
         MESessionCapabilitiesChanged = (MESessionScrubSampleComplete + 1),
         MESessionTopologyStatus = (MESessionCapabilitiesChanged + 1),
         MESessionNotifyPresentationTime = (MESessionTopologyStatus + 1),
         MENewPresentation = (MESessionNotifyPresentationTime + 1),
         MELicenseAcquisitionStart = (MENewPresentation + 1),
         MELicenseAcquisitionCompleted = (MELicenseAcquisitionStart + 1),
         MEIndividualizationStart = (MELicenseAcquisitionCompleted + 1),
         MEIndividualizationCompleted = (MEIndividualizationStart + 1),
         MEEnablerProgress = (MEIndividualizationCompleted + 1),
         MEEnablerCompleted = (MEEnablerProgress + 1),
         MEPolicyError = (MEEnablerCompleted + 1),
         MEPolicyReport = (MEPolicyError + 1),
         MEBufferingStarted = (MEPolicyReport + 1),
         MEBufferingStopped = (MEBufferingStarted + 1),
         MEConnectStart = (MEBufferingStopped + 1),
         MEConnectEnd = (MEConnectStart + 1),
         MEReconnectStart = (MEConnectEnd + 1),
         MEReconnectEnd = (MEReconnectStart + 1),
         MERendererEvent = (MEReconnectEnd + 1),
         MESessionStreamSinkFormatChanged = (MERendererEvent + 1),
         MESourceUnknown = 200,
         MESourceStarted = (MESourceUnknown + 1),
         MEStreamStarted = (MESourceStarted + 1),
         MESourceSeeked = (MEStreamStarted + 1),
         MEStreamSeeked = (MESourceSeeked + 1),
         MENewStream = (MEStreamSeeked + 1),
         MEUpdatedStream = (MENewStream + 1),
         MESourceStopped = (MEUpdatedStream + 1),
         MEStreamStopped = (MESourceStopped + 1),
         MESourcePaused = (MEStreamStopped + 1),
         MEStreamPaused = (MESourcePaused + 1),
         MEEndOfPresentation = (MEStreamPaused + 1),
         MEEndOfStream = (MEEndOfPresentation + 1),
         MEMediaSample = (MEEndOfStream + 1),
         MEStreamTick = (MEMediaSample + 1),
         MEStreamThinMode = (MEStreamTick + 1),
         MEStreamFormatChanged = (MEStreamThinMode + 1),
         MESourceRateChanged = (MEStreamFormatChanged + 1),
         MEEndOfPresentationSegment = (MESourceRateChanged + 1),
         MESourceCharacteristicsChanged = (MEEndOfPresentationSegment + 1),
         MESourceRateChangeRequested = (MESourceCharacteristicsChanged + 1),
         MESourceMetadataChanged = (MESourceRateChangeRequested + 1),
         MESequencerSourceTopologyUpdated = (MESourceMetadataChanged + 1),
         MESinkUnknown = 300,
         MEStreamSinkStarted = (MESinkUnknown + 1),
         MEStreamSinkStopped = (MEStreamSinkStarted + 1),
         MEStreamSinkPaused = (MEStreamSinkStopped + 1),
         MEStreamSinkRateChanged = (MEStreamSinkPaused + 1),
         MEStreamSinkRequestSample = (MEStreamSinkRateChanged + 1),
         MEStreamSinkMarker = (MEStreamSinkRequestSample + 1),
         MEStreamSinkPrerolled = (MEStreamSinkMarker + 1),
         MEStreamSinkScrubSampleComplete = (MEStreamSinkPrerolled + 1),
         MEStreamSinkFormatChanged = (MEStreamSinkScrubSampleComplete + 1),
         MEStreamSinkDeviceChanged = (MEStreamSinkFormatChanged + 1),
         MEQualityNotify = (MEStreamSinkDeviceChanged + 1),
         MESinkInvalidated = (MEQualityNotify + 1),
         MEAudioSessionNameChanged = (MESinkInvalidated + 1),
         MEAudioSessionVolumeChanged = (MEAudioSessionNameChanged + 1),
         MEAudioSessionDeviceRemoved = (MEAudioSessionVolumeChanged + 1),
         MEAudioSessionServerShutdown = (MEAudioSessionDeviceRemoved + 1),
         MEAudioSessionGroupingParamChanged = (MEAudioSessionServerShutdown + 1),
         MEAudioSessionIconChanged = (MEAudioSessionGroupingParamChanged + 1),
         MEAudioSessionFormatChanged = (MEAudioSessionIconChanged + 1),
         MEAudioSessionDisconnected = (MEAudioSessionFormatChanged + 1),
         MEAudioSessionExclusiveModeOverride = (MEAudioSessionDisconnected + 1),
         METrustUnknown = 400,
         MEPolicyChanged = (METrustUnknown + 1),
         MEContentProtectionMessage = (MEPolicyChanged + 1),
         MEPolicySet = (MEContentProtectionMessage + 1),
         MEWMDRMLicenseBackupCompleted = 500,
         MEWMDRMLicenseBackupProgress = 501,
         MEWMDRMLicenseRestoreCompleted = 502,
         MEWMDRMLicenseRestoreProgress = 503,
         MEWMDRMLicenseAcquisitionCompleted = 506,
         MEWMDRMIndividualizationCompleted = 508,
         MEWMDRMIndividualizationProgress = 513,
         MEWMDRMProximityCompleted = 514,
         MEWMDRMLicenseStoreCleaned = 515,
         MEWMDRMRevocationDownloadCompleted = 516,
         MEReservedMax = 10000
      }

      public enum MF_ATTRIBUTE_TYPE {
         None = 0×0,
         Blob = 0×1011,
         Double = 0×5,
         Guid = 0×48,
         IUnknown = 13,
         String = 0x1f,
         Uint32 = 0×13,
         Uint64 = 0×15
      }

      public enum MF_ATTRIBUTES_MATCH_TYPE {
         OurItems,
         TheirItems,
         AllItems,
         InterSection,
         Smaller
      }

      [Flags]
      public enum MFMEDIASOURCE_CHARACTERISTICS {
         None = 0,
         IsLive = 0×1,
         CanSeek = 0×2,
         CanPause = 0×4,
         HasSlowSeek = 0×8
      }

      [Flags]
      public enum MF_MEDIATYPE_EQUAL {
         None = 0,
         MajorTypes = 0×00000001,
         FormatTypes = 0×00000002,
         FormatData = 0×00000004,
         FormatUserData = 0×00000008
      }
      #endregion

      #endregion

Some cumbersome, right? It is! however those interfaces are extensible. Here for example some added value of such approach

How to read media metadata by using Media Foundation

Now, when we did most of work, metadata is piece of cake. All we need is to get service handle

object s;
MFGetService(source, MFServices.MF_PROPERTY_HANDLER_SERVICE, typeof(IPropertyStore).GUID, out s);
var store = (IPropertyStore)s;

and get information our of property bag

track.Album = _getInfo<string>(store, MFPropertyKeys.AlbumTitle);
track.Name = _getInfo<string>(store, MFPropertyKeys.Title);
track.Comments = _getInfo<string>(store, MFPropertyKeys.Comment);
track.Duration = TimeSpan.FromTicks(_getInfo<long>(store, Interop.MFPropertyKeys.MediaDuration));

Those interfaces uses COM property bag to retrieve information of invariant type

private static T _getInfo<T>(IPropertyStore store, PROPERTYKEY key) {
         PROPVARIANT val;
         store.GetValue(key, out val);
        return (T)val.Value;
      }

Here how this object looks in managed code

[StructLayout(LayoutKind.Sequential)]
public class PROPERTYKEY {

public PROPERTYKEY(Guid tid, uint id) {
fmtid = tid;
pid = id;
}

public Guid fmtid;

public uint pid;
}

#pragma warning disable 618
[StructLayout(LayoutKind.Explicit)]
public struct PROPVARIANT {
[FieldOffset(0)]
short vt;
[FieldOffset(2)]
short wReserved1;
[FieldOffset(4)]
short wReserved2;
[FieldOffset(6)]
short wReserved3;
[FieldOffset(8)]
sbyte cVal;
[FieldOffset(8)]
byte bVal;
[FieldOffset(8)]
short iVal;
[FieldOffset(8)]
ushort uiVal;
[FieldOffset(8)]
int lVal;
[FieldOffset(8)]
uint ulVal;
[FieldOffset(8)]
int intVal;
[FieldOffset(8)]
uint uintVal;
[FieldOffset(8)]
long hVal;
[FieldOffset(8)]
long uhVal;
[FieldOffset(8)]
float fltVal;
[FieldOffset(8)]
double dblVal;
[FieldOffset(8)]
bool boolVal;
[FieldOffset(8)]
int scode;
[FieldOffset(8)]
DateTime date;
[FieldOffset(8)]
FILETIME filetime;
[FieldOffset(8)]
BLOB blobVal;
[FieldOffset(8)]
IntPtr pwszVal;

private byte[] _getBlob() {
var result = new byte[blobVal.cbSize];
Marshal.Copy(blobVal.pBlobData, result, 0, result.Length);
return result;
}

public object Value {
get {
VarEnum ve = (VarEnum)vt;
switch (ve) {
case VarEnum.VT_I1:
return bVal;
case VarEnum.VT_I2:
return iVal;
case VarEnum.VT_I4:
return lVal;
case VarEnum.VT_I8:
return hVal;
case VarEnum.VT_INT:
return iVal;
case VarEnum.VT_UI4:
return ulVal;
case VarEnum.VT_UI8:
return uhVal;
case VarEnum.VT_LPWSTR:
return Marshal.PtrToStringUni(pwszVal);
case VarEnum.VT_BLOB:
return _getBlob();
case VarEnum.VT_EMPTY:
case VarEnum.VT_NULL:
return null;
}
throw new NotImplementedException("PROPVARIANT: " + ve.ToString());
}
}
}

And some additional classes and guids to fulfill solution.

public static class MFAttributesClsid {
public static readonly Guid MF_PD_DURATION = new Guid(0x6c990d33, 0xbb8e, 0x477a, 0×85, 0×98, 0xd, 0x5d, 0×96, 0xfc, 0xd8, 0x8a);
public static readonly Guid MF_MT_SUBTYPE = new Guid(0xf7e34c9a, 0x42e8, 0×4714, 0xb7, 0x4b, 0xcb, 0×29, 0xd7, 0x2c, 0×35, 0xe5);
public static readonly Guid MF_MT_AVG_BITRATE = new Guid(0×20332624, 0xfb0d, 0x4d9e, 0xbd, 0x0d, 0xcb, 0xf6, 0×78, 0x6c, 0×10, 0x2e);
}

public static class MFMediaType {
public static readonly Guid Default = new Guid(0x81A412E6, 0×8103, 0x4B06, 0×85, 0x7F, 0×18, 0×62, 0×78, 0×10, 0×24, 0xAC);
public static readonly Guid Audio = new Guid(0×73647561, 0×0000, 0×0010, 0×80, 0×00, 0×00, 0xAA, 0×00, 0×38, 0x9B, 0×71);
public static readonly Guid Video = new Guid(0×73646976, 0×0000, 0×0010, 0×80, 0×00, 0×00, 0xAA, 0×00, 0×38, 0x9B, 0×71);
}

public static class MFServices {
public static readonly Guid MF_PROPERTY_HANDLER_SERVICE = new Guid(0xa3face02, 0x32b8, 0x41dd, 0×90, 0xe7, 0x5f, 0xef, 0x7c, 0×89, 0×91, 0xb5);
}

public static class MFPropertyKeys {
public static readonly PROPERTYKEY Title = new PROPERTYKEY(new Guid(0xf29f85e0, 0x4ff9, 0×1068, 0xab, 0×91, 0×08, 0×00, 0x2b, 0×27, 0xb3, 0xd9), 2);
public static readonly PROPERTYKEY AlbumTitle = new PROPERTYKEY(new Guid(0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0×00, 0×60, 0×97, 0xC6, 0×86, 0xF6), 4);
public static readonly PROPERTYKEY Author = new PROPERTYKEY(new Guid(0xF29F85E0, 0x4FF9, 0×1068, 0xAB, 0×91, 0×08, 0×00, 0x2B, 0×27, 0xB3, 0xD9), 4);
public static readonly PROPERTYKEY AudioCompression = new PROPERTYKEY(new Guid(0×64440490, 0x4C8B, 0x11D1, 0x8B, 0×70, 0×08, 0×00, 0×36, 0xB1, 0x1A, 0×03), 10);
public static readonly PROPERTYKEY AudioFormat = new PROPERTYKEY(new Guid(0×64440490, 0x4C8B, 0x11D1, 0x8B, 0×70, 0×08, 0×00, 0×36, 0xB1, 0x1A, 0×03), 2);
public static readonly PROPERTYKEY Category = new PROPERTYKEY(new Guid(0xD5CDD502, 0x2E9C, 0x101B, 0×93, 0×97, 0×08, 0×00, 0x2B, 0x2C, 0xF9, 0xAE), 2);
public static readonly PROPERTYKEY Company = new PROPERTYKEY(new Guid(0xD5CDD502, 0x2E9C, 0x101B, 0×93, 0×97, 0×08, 0×00, 0x2B, 0x2C, 0xF9, 0xAE), 15);
public static readonly PROPERTYKEY Copyright = new PROPERTYKEY(new Guid(0×64440492, 0x4C8B, 0x11D1, 0x8B, 0×70, 0×08, 0×00, 0×36, 0xB1, 0x1A, 0×03), 11);
public static readonly PROPERTYKEY Comment = new PROPERTYKEY(new Guid(0xF29F85E0, 0x4FF9, 0×1068, 0xAB, 0×91, 0×08, 0×00, 0x2B, 0×27, 0xB3, 0xD9), 6);
public static readonly PROPERTYKEY MediaDuration = new PROPERTYKEY(new Guid(0×64440490, 0x4C8B, 0x11D1, 0x8B, 0×70, 0×08, 0×00, 0×36, 0xB1, 0x1A, 0×03), 3);
public static readonly PROPERTYKEY VideoCompression = new PROPERTYKEY(new Guid(0×64440491, 0x4C8B, 0x11D1, 0x8B, 0×70, 0×08, 0×00, 0×36, 0xB1, 0x1A, 0×03), 10);
public static readonly PROPERTYKEY VideoDirector = new PROPERTYKEY(new Guid(0×64440492, 0x4C8B, 0x11D1, 0x8B, 0×70, 0×08, 0×00, 0×36, 0xB1, 0x1A, 0×03), 20);
public static readonly PROPERTYKEY VideoFourCC = new PROPERTYKEY(new Guid(0×64440491, 0x4C8B, 0x11D1, 0x8B, 0×70, 0×08, 0×00, 0×36, 0xB1, 0x1A, 0×03), 44);
}

We almost finished. The only thing is not to forget release all COM objects (Marshal.ReleaseComObject(…)) to prevent memory leaks and init and shutdown Media Foundation:

MFStartup(0×10070, Interop.MFSTARTUP.Lite);

MFShutdown();

You choose what to use: simple, but not supported or complicated but extensible approach. Both will bring the same results. So have a nice day and be good people.

RSA private key import from PEM format in C#

First of all, I want to apologies for not writing. From one hand, this is not a good think for me to disappeared from development community horizons, from other hand, I am investing all my time into our better feature, which is good thing. There are too much things were done during last two years. And the good news are that we already delivered whatever was promised to deliver and know for sure that we are able to deliver even more in the future. But let’s come into business. First of all I have huge pipeline of interesting articles to share with you, second, some people from my team are also decided to contribute to the community and write Better Place development team blog. There are not too much there, but this is only a matter of time.

Today we’ll speak about security. About how to import OpenSSL private key into .NET application and use it aside with X509 public certificate to establish TLS connection with asymmetric encryption and two phase certificates handshake.

image

Let’s start from the very beginning. What is SSL? SSL is the secure way to communicate when transferred data is encrypted by using one time and per-session cipher. There are different implementations of such connection. The most famous one is the one all of you using when connection to https://someting… When doing this, your browser asks remote side to provide it public certificate for you in order to check it with local “authority” you trusted in. If everything is ok and the host defined on the remote certificate is the host you are speaking with, your browser allows communication after both sides decide about the one-time cipher for encryption.

You can implement this mode of SSL very easy by using SslStream class in .NET as 1-2-3.
1) Resolve host and open TcpClient connection to it

var host = new IPHostEntry();
try {
host = Dns.GetHostEntry(RemoteAddress.DnsSafeHost);
} catch (SocketException soe) {
if (soe.SocketErrorCode == SocketError.HostNotFound) {
  host.HostName = RemoteAddress.DnsSafeHost;
}
}

Client.Connect(host.HostName, RemoteAddress.Port);

2) Initialize SSL encrypted stream to it by providing validation callback for remote certificate

var stream = new SslStream(Client.GetStream(), true, _validateCertificate);

3) Ask for authorization

stream.AuthenticateAsClient(host.HostName);

Inside remote certificate validation callback, you should decide what to do if something bad happened during negotiation phase.

private readonly RemoteCertificateValidationCallback _validateCertificate = (sender, certificate, chain, sslPolicyErrors) => {
  var result = sslPolicyErrors == SslPolicyErrors.None;
    if (!result) {
      var err = new StringBuilder();
        err.AppendFormat("Unable to establish security connection due to {0}. Error chain:", sslPolicyErrors);

        foreach (var el in chain.ChainElements) {
          foreach (var s in el.ChainElementStatus) {
            err.AppendFormat("{0} – {1}", el.Certificate.Subject, s.StatusInformation);
           }
         }
        Log.Warn(err.ToString());
       }
      return result;
    };

So far, so good. Now, if everything is OK, just use SslStream as regular stream to write and read from the socket. All other complicated things will be done by .NET.

However this is only a part of the game. Now the real thing comes. What if you want to be more secure and want your server to be able to validate that local client is one it can trust. This scenario often used in closed networks, when server side (or any other provisioning entity) can assure that every client is well known and it able to provide certificate to each of those. For this scenario we also have solution in SslStream implementation, which takes into account this ability, defined by TLS RFC. All we need is to use other override of SslStream constructor which receives the callback for client certificate choose logic and authorization method with prepared clients certificates.

var stream = new SslStream(Client.GetStream(), true, _validateCertificate, _selectCertificate);
stream.AuthenticateAsClient(host.HostName, _clientCerts, SslProtocols.Ssl3, false);

Inside local certificate selection logic you should receive the remote end choice algorithm and return the most secure client certificate you have

private readonly LocalCertificateSelectionCallback _selectCertificate = (sender, target, localCerts, remoteCert, issuers) => {
….
return securestCert;
}

Also you should prepare the local certificates collection, provided as input to negotiation method. This one is simple too. All you need is standard X509 certificate(s). Usually, such certificates provided by uber-secure-unix-seriose-unbreakable-machine, which uses OpenSSL to export generated keys. This means, that in most cases, your public certificate will looks inside like this:

Certificate:
    Data:
        Version: 1 (0×0)
        Serial Number: 268436473 (0x100003f9)
        Signature Algorithm: md5WithRSAEncryption
        Issuer: O=UBER, OU=RD/emailAddress=ca@ubersecurity.org, L=TLV, ST=Israel, C=IL, CN=ca
        Validity
            Not Before: May 25 11:26:50 2011 GMT
            Not After : May 24 11:26:50 2012 GMT
        Subject: C=IL, ST=Israel, O=UBER, OU=SEC, CN=UberSecurity
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (1024 bit)
                Modulus (1024 bit):
                    … some random HEX numbers …
                Exponent: 65537 (0×10001)
    Signature Algorithm: md5WithRSAEncryption
        … some other random HEX numbers …
—–BEGIN CERTIFICATE—–
… some BASE64 random characters here …
—–END CERTIFICATE—–

This format called PEM (Privacy Enhanced Mail). This is most common and easiest format for secure text transfer. Such file can be easily imported and used by X509Certificate class as following:

var clientCert = X509Certificate.CreateFromCertFile("myCert.pem");

That’s all, all you need now is to add this certificate into certificate collection (_clientCerts in this case) and return it when _selectCertificate delegate being called.

Looks simple and secure? It is, but there is a small BUT in all this. Real security experts, come from OpenSSL world often do not want to put private key for client (the key will be used for outgoing traffic encryption) inside client certificate and want to provide it via other channel securely.

Now you are asking what I am speaking about? Let me explain:

When SSL uses asymmetric encryption algorithm, local side uses private key to encrypt outgoing traffic. Once it trust other side (by validating remote certificate), it send local public key to the remote side, which uses it for information decryption. So far we have three entities: public key, private key and certificate. There is a method commonly used by industry to minimize transit problems. We know to pack public certificate and wrapped public key inside the same store to send it. If we want to go even further, we can also store securely private key inside the same store. Looks not very secure? This is not quite right. First of all, in most cases private certificate is encrypted by using special keyphase only known to the side this certificate intended to, second, it uses the same public key+certificate itself hash values to encrypt it event better. In this case there is a big advantage of compact and well known package format (keypair+certificate) and high security level.

However people come from OpenSSL world not trust too much to this method (and called it “evil empire bought the patent”) and often provide encrypted private key separately. This key being transferred in PEM format, however this time it is not standard one, but specific and designed by OpenSSL geeks. Even if they call it RSA format, it has almost not relation to it.

Such key looks as following:

—–BEGIN RSA PRIVATE KEY—–
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,…some geeky HEX here …

… some BASE64 random characters here …

—–END RSA PRIVATE KEY—–

Looks simple? Do not hurry this much. .NET has not build in method to read this format. So we’ll have to write one, based on OpenSSL specification. Let’s start

First of all “well known headers”

private const string _begin = "—–BEGIN ";
private const string _end = "—–END ";
private const string _private = "PRIVATE KEY";
private const string _public = "PUBLIC KEY";
private const string _rsaPublic = "RSA PUBLIC KEY";

Next read the text inside the file:

using (var reader = new StringReader(data)) {
   var line = reader.ReadLine();
   if (line.NotNull() && line.StartsWith(_begin)) {
      line = line.Substring(_begin.Length);
      var idx = line.IndexOf(‘-’);
      if (idx > 0) {
         var type = line.Before(idx);
         return _loadPem(reader, type, passKey);
      }
   }
   throw new ArgumentException("This is not valid PEM format", "data", new FormatException("PEM start identifier is invalid or not found."));
}

…and read headers:

var end = _end + type;
var headers = new _pemHeaders();
var line = string.Empty;
var body = new StringBuilder();
while ((line = reader.ReadLine()) != null && line.IndexOf(end) == -1) {
   if (line == null) {
      throw new FormatException("PEM end identifier is invalid or not found.");
   }
   var d = line.IndexOf(‘:’);
   if (d >= 0) {
      // header
  
      var n = line.Substring(0, d).Trim();
      if (n.StartsWith("X-")) n = n.Substring(2);
      var v = line.After(d).Trim();
      if (!headers.ContainsKey(n)) {
         headers.Add(n, v);
      } else {
         throw new FormatException("Duplicate header {0} in PEM data.".Substitute(n));
      }

When headers are ready, we need to read a body. This is base64 encrypted

   } else {
      // body
      body.Append(line);
   }
}
if (body.Length % 4 != 0 || type.EndsWith(_private)) {
   throw new FormatException("PEM data is invalid or truncated.");
}

return _createPem(type, headers, Convert.FromBase64String(body.ToString()), passkey);

and now, based on headers, we can decode body. For simplification, we’ll decode only most common encryptions for the key

type = type.Before(type.Length – _private.Length).Trim();
var pType = headers.TryGet("Proc-Type");
if (pType == "4,ENCRYPTED") {
   if (passkey.IsEmpty()) {
      throw new ArgumentException("Passkey is mandatory for encrypted PEM object");
   }

   var dek = headers.TryGet("DEK-Info");
   var tkz = dek.Split(‘,’);
   if (tkz.Length > 1) {
      var alg = new _alg(tkz[0]);
      var saltLen = tkz[1].Length;
      var salt = new byte[saltLen / 2];
      for (var i = 0; i < saltLen / 2; i++) {
         var pair = tkz[1].Substring(2 * i, 2);
         salt[i] = Byte.Parse(pair, NumberStyles.AllowHexSpecifier);
      }

      body = _decodePem(body, passkey, alg, salt);
      if (body != null) {
         return _decodeRsaPrivateKey(body);
      }
   } else {
      throw new FormatException("DEK information is invalid or truncated.");
   }
}

For simplification, we’ll support only most common encryption algorithms (3DES with CBC mode). In general RSA private key can be encrypted by AES, Blow Fish, DES/Triple DES and RC2

private static byte[] _decodePem(byte[] body, string passkey, _alg alg, byte[] salt) {
   if (alg.AlgBase != _alg.BaseAlg.DES_EDE3 && alg.AlgMode != _alg.Mode.CBC) {
      throw new NotSupportedException("Only 3DES-CBC keys are supported.");
   }
   var des = _get3DesKey(salt, passkey);
   if (des == null) {
      throw new ApplicationException("Unable to calculate 3DES key for decryption.");
   }
   var rsa = _decryptRsaKey(body, des, salt);
   if (rsa == null) {
      throw new ApplicationException("Unable to decrypt RSA private key.");
   }
   return rsa;
}

And decrypt itself

private static byte[] _decryptRsaKey(byte[] body, byte[] desKey, byte[] iv) {
   byte[] result = null;
   using (var stream = new MemoryStream()) {
      var alg = TripleDES.Create();
      alg.Key = desKey;
      alg.IV = iv;
      try {
         using (var cs = new CryptoStream(stream, alg.CreateDecryptor(), CryptoStreamMode.Write)) {
            cs.Write(body, 0, body.Length);
            cs.Close();
         }
         result = stream.ToArray();
      } catch (CryptographicException ce) {
         // throw up
         throw ce;
      } catch (Exception ex) {
         Log.Exception(ex, Severity.Info, "Failed to write crypto stream.");
      };
   }
   return result;
}

by getting 3DES key from stream

private static byte[] _get3DesKey(byte[] salt, string passkey) {
   var HASHLENGTH = 16;
   var m = 2; // 2 iterations for at least 24 bytes
   var c = 1; // 1 hash for Open SSL
   var k = new byte[HASHLENGTH * m];

   var pk = Encoding.ASCII.GetBytes(passkey);
   var data = new byte[salt.Length + pk.Length];
   Array.Copy(pk, data, pk.Length);
   Array.Copy(salt, 0, data, pk.Length, salt.Length);
   var md5 = new MD5CryptoServiceProvider();
   byte[] result = null;
   var hash = new byte[HASHLENGTH + data.Length];
  
   for (int i = 0; i < m; i++) {
      if (i == 0) {
         result = data;
      } else {
         Array.Copy(result, hash, result.Length);
         Array.Copy(data, 0, hash, result.Length, data.Length);
         result = hash;
      }

      for (int j = 0; j < c; j++) {
         result = md5.ComputeHash(result);
      }
      Array.Copy(result, 0, k, i * HASHLENGTH, result.Length);
   }
   var dk = new byte[24]; //final key
   Array.Copy(k, dk, dk.Length);
   return dk;
}

When we decode the body, we can use create RSACryptoServiceProvider class from it to be used by our SslStream. Oh, yeah, some crazy math here

using (var ms = new MemoryStream(body)) {
   using (var reader = new BinaryReader(ms)) {
      try {
         var tb = reader.ReadUInt16(); // LE: x30 x81
         if (tb == 0×8130) {
            reader.ReadByte(); // fw 1
         } else if (tb == 0×8230) {
            reader.ReadInt16(); // fw 2
         } else {
            return null;
         }

         tb = reader.ReadUInt16(); // version
         if (tb != 0×0102) {
            return null;
         }
         if (reader.ReadByte() != 0×00) {
            return null;
         }

         var MODULUS = _readInt(reader);
         var E = _readInt(reader);
         var D = _readInt(reader);
         var P = _readInt(reader);
         var Q = _readInt(reader);
         var DP = _readInt(reader);
         var DQ = _readInt(reader);
         var IQ = _readInt(reader);

         var result = new RSACryptoServiceProvider();
         var param = new RSAParameters {
            Modulus = MODULUS,
            Exponent = E,
            D = D,
            P = P,
            Q = Q,
            DP = DP,
            DQ = DQ,
            InverseQ = IQ
         };
         result.ImportParameters(param);
         return result;

 

      } catch (Exception ex) {
         Log.Exception(ex);
      } finally {
         reader.Close();
      }
   }
}

Some helper methods to read bytes and we done

private static Func<BinaryReader, byte[]> _readInt = r => {
   var s = _getIntSize(r);
   return r.ReadBytes(s);
};

private static Func<BinaryReader, int> _getIntSize = r => {
   byte lb = 0×00;
   byte hb = 0×00;
   int c = 0;
   var b = r.ReadByte();
   if (b != 0×02) { //int
      return 0;
   }
   b = r.ReadByte();

   if (b == 0×81) {
      c = r.ReadByte(); //size
   } else
      if (b == 0×82) {
         hb = r.ReadByte(); //size
         lb = r.ReadByte();
         byte[] m = { lb, hb, 0×00, 0×00 };
         c = BitConverter.ToInt32(m, 0);
      } else {
         c = b; //got size
      }

   while (r.ReadByte() == 0×00) { //remove high zero
      c -= 1;
   }
   r.BaseStream.Seek(-1, SeekOrigin.Current); // last byte is not zero, go back;
   return c;
};

We done, all we have to do now is to construct our private key and pack it for SslStream use. For this purpose we have X509Certificate big brother X509Certificate2 

var cert = new X509Certificate2(File.ReadAllBytes(“myCert.pem”)) {
  PrivateKey = FromPem(Encoding.ASCII.GetString(File.ReadAllBytes(“myKey.pem”)), _sslPrivateKeyPasskey)
};

Now when you supply cert as the client certificate SslStream will use private key for outgoing stream encryption, provide public key for remote incoming stream encryption and certificate for remote side identification.

We done. Be good people and subscribe to our dev blog, it promised to be one of the most interesting blogs for those who is not satisfied with the way Windows works and want to pimp it a bit.

Source code for this article (4 KB) >>

P.S. If, in case, you got invitation from Microsoft Israel to participate “Be what’s next” event next Wednesday 22nd. It is highly recommended to come and see me (and other large ISVs) speak about solutions we did. If you did not get an invitation, and you are MS partner, please contact local DPE guys. This is for certain ISVs and only by invitations.

image

INotifyPropertyChanged auto wiring or how to get rid of redundant code

For the last week most of WPF disciples are discussing how to get rid of hardcoded property name string inside INotifyPropertyChanged implementation and how to keep using automatic properties implementation but keep WPF binding working. The thread was started by Karl Shifflett, who proposed interesting method of using StackFrame for this task. During this thread other methods were proposed including code snippets, R#, Observer Pattern, Cinch framework, Static Reflection, Weak References and others. I also proposed the method we’re using for our classes and promised to blog about it. So the topic today is how to use PostSharp to wire automatic implementation of INotifyPropertyChanged interface based on automatic setters only.

My 5 ¢

So, I want my code to looks like this:

public class AutoWiredSource {
   public double MyProperty { get; set; }
   public double MyOtherProperty { get; set; }
}

while be fully noticeable about any change in any property and makes me able to bind to those properties.

<StackPanel DataContext="{Binding Source={StaticResource source}}">
    <Slider Value="{Binding Path=MyProperty}" />
    <Slider Value="{Binding Path=MyProperty}" />
</StackPanel>

How to achieve it? How to make compiler to replace my code with following?:

private double _MyProperty;
public double MyProperty {
   get { return _MyProperty; }
   set {
      if (value != _MyProperty) {
         _MyProperty = value; OnPropertyChanged("MyProperty");
      }
   }
}
public event PropertyChangedEventHandler PropertyChanged;
internal void OnPropertyChanged(string propertyName) {
   if (string.IsNullOrEmpty(propertyName)) throw new ArgumentNullException("propertyName");

   var handler = PropertyChanged as PropertyChangedEventHandler;
   if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}

Simple: to use aspect oriented programming to inject set of instructions into pre-compiled source.

First of all we have to build some attribute will be used for marking classes requires change tracking. This attribute should be combined (compound) aspect to include all aspects used for change tracking. All we’re doing here is to get all set methods to add composition aspect to

[Serializable, DebuggerNonUserCode, AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, AllowMultiple = false, Inherited = false),
MulticastAttributeUsage(MulticastTargets.Class, AllowMultiple = false, Inheritance = MulticastInheritance.None, AllowExternalAssemblies = true)]
public sealed class NotifyPropertyChangedAttribute : CompoundAspect {
   public int AspectPriority { get; set; }

   public override void ProvideAspects(object element, LaosReflectionAspectCollection collection) {
      Type targetType = (Type)element;
      collection.AddAspect(targetType, new PropertyChangedAspect { AspectPriority = AspectPriority });
      foreach (var info in targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(pi => pi.GetSetMethod() != null)) {
         collection.AddAspect(info.GetSetMethod(), new NotifyPropertyChangedAspect(info.Name) { AspectPriority = AspectPriority });
      }
   }
}

Next aspect is change tracking composition aspect. Which is used for marking only

[Serializable]
internal sealed class PropertyChangedAspect : CompositionAspect {
   public override object CreateImplementationObject(InstanceBoundLaosEventArgs eventArgs) {
      return new PropertyChangedImpl(eventArgs.Instance);
   }

   public override Type GetPublicInterface(Type containerType) {
      return typeof(INotifyPropertyChanged);
   }

   public override CompositionAspectOptions GetOptions() {
      return CompositionAspectOptions.GenerateImplementationAccessor;
   }
}

And the next which is most interesting one, we will put onto method boundary for tracking. There are some highlights here. First we do not want to fire PropertyChanged event if the actual value did not changed, thus we’ll handle the method on it entry and on it exit for check.

[Serializable]
internal sealed class NotifyPropertyChangedAspect : OnMethodBoundaryAspect {
   private readonly string _propertyName;

   public NotifyPropertyChangedAspect(string propertyName) {
      if (string.IsNullOrEmpty(propertyName)) throw new ArgumentNullException("propertyName");
      _propertyName = propertyName;
   }

   public override void OnEntry(MethodExecutionEventArgs eventArgs) {
      var targetType = eventArgs.Instance.GetType();
      var setSetMethod = targetType.GetProperty(_propertyName);
      if (setSetMethod == null) throw new AccessViolationException();
      var oldValue = setSetMethod.GetValue(eventArgs.Instance,null);
      var newValue = eventArgs.GetReadOnlyArgumentArray()[0];
      if (oldValue == newValue) eventArgs.FlowBehavior = FlowBehavior.Return;
   }

   public override void OnSuccess(MethodExecutionEventArgs eventArgs) {
      var instance = eventArgs.Instance as IComposed<INotifyPropertyChanged>;
      var imp = instance.GetImplementation(eventArgs.InstanceCredentials) as PropertyChangedImpl;
      imp.OnPropertyChanged(_propertyName);
   }
}

We almost done, all we have to do is to create class which implements INotifyPropertyChanged with internal method to useful call

[Serializable]
internal sealed class PropertyChangedImpl : INotifyPropertyChanged {
   private readonly object _instance;

   public PropertyChangedImpl(object instance) {
      if (instance == null) throw new ArgumentNullException("instance");
      _instance = instance;
   }

   public event PropertyChangedEventHandler PropertyChanged;

   internal void OnPropertyChanged(string propertyName) {
      if (string.IsNullOrEmpty(propertyName)) throw new ArgumentNullException("propertyName");

      var handler = PropertyChanged as PropertyChangedEventHandler;
      if (handler != null) handler(_instance, new PropertyChangedEventArgs(propertyName));
   }
}

We done. The last thing is to reference to PostSharp Laos and Public assemblies and mark compiler to use Postsharp targets (inside your project file (*.csproj)

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
  <DontImportPostSharp>True</DontImportPostSharp>
</PropertyGroup>
<Import Project="PostSharp\PostSharp-1.5.targets" />

Now we done. We can use clear syntax like following to make all our properties has public setter to be traceable. The only disadvantage is that you’ll have to drag two Post Sharp files with your project. But after all it much more convenience than manual notify change tracking all over your project.

[NotifyPropertyChanged]
public class AutoWiredSource {
   public double MyProperty { get; set; }
}

Have a nice day and be good people. Also try to thing what other extremely useful things can be done with PostSharp (or any other aspect oriented engine)

Source code for this article (1,225 KB)>>

“Zone of Pain vs. Zone of Uselessness” or code analysis with NDepend

As for me, there are only two kinds of projects – hobbyist’s nifty tools and systems (scale may wary). The main difference between those is the easiness of making changes and refactoring. Other words, how many other developers I should persuade to do it just because “In the final analysis, it’s their war” – JFK. But what can be the good reason for such fast talk? – “You code sucks or at least it ought to”.

AbstractnessVSInstabilityAbstractnessVSInstability[10]
“Every generation needs a new revolution” – Thomas Jefferson

So, in order to win such revolution for “systems” you absolutely need static analysis tools like NDepend. Those tools are not intended for being your advocates, but those intended to help you to understand all risks and approximate the amount of work should be done to fulfill another revolution.

Unfortunately, you cannot use such tools for fair measuring of code quality because of Computer Science rules of thumb. How to decide whether “methods is too big” or “method is too complex”? However you can (and should) use it for dependencies risk detection. For example, in following illustration you can clear understand that any change inside BetterPlace.Core or BetterPlace.Model assemblies (and namespaces) can be painful.

Dependencies diagram

Now the only question is who is responsible of modules, using it and how long will it take to convince them to make a revolution.

 Dependency Matrix

From here you can start using CQL (Code Query Language) which is SQL like language invented by Patrick, the author of NDepend, for querying code elements and sections. By using it, you can define what “method is too big” means in terms of your project.

SELECT METHODS WHERE NbLinesOfCode > 300 ORDER BY NbLinesOfCode DESC

Or see where you should replace method overloads by default arguments, introduced in .NET 4.0

SELECT METHODS WHERE NbOverloads > 1 ORDER BY NbOverloads DESC

Then, when you marked all places and human targets for revolution, you can start it. After you done, you can even compare builds and measure the amount of work and quality of results achieved.

Application metrix Application metrix

To finalize, I just touched the tip of what good static analysis tool can be used for. So get it, learn it and use it not only when you need to make a revolutions, but also during your application design and build process to be aware about how the new monster created will looks like.

Download NDepend >>

Proper disclosure: Apr 15, Patrick, the author of NDepend, asked me to review his tool and offer one license for evaluation. I told him, that do not need to evaluate it because I’m using it for a while (also I had the license of my own) and I’ll be happy to write a review once I’ll have a bit time for it. Now it happened. Thank you, Patrick, for such good work!

How to pass technical interview in Better Place

If you following me on Twitter, you probably know that we’re looking for developers. However those who was in interview here become a little bit shocked. It is not very standard interview for software houses. So how does it works?

Step zero – requirements and open positions

Even before somebody send us his/her resume there are requirements and open positions. Those requirements are very straight forward: “We need a developer for servers” or “we need somebody to build UI” or “we need GIS guy” etc. All open positions (at least for Israel) you can always find in our local website. Those days we have two open positions for our team: super-fantastic developer and chronic headache reliever.

Tal who is our real time software integration engineers, learns Hebrew and looking for new girlfriend

Now a bit about candidates. I had some developers hired during my career and it always worked following way: there are only 10 kinds of developers – developers and not. It never worked for me to hire somebody just because I have to hire somebody. Also it did not worked to hire mythological “average” or “rather good” developer. So for me the segmentation here is very binary- can write code or can not.

Step one – CV

Somebody told me that the only chance to hire good developer via CV is if the developer is student. However, and in spite of this the very first station is HR department, they get a ton of CVs each day and filter it out by using strange and mysterious heuristics. In most of cases it works like in bad search engine – by using keywords. So my very basic requirement from our HR team, please do not filter it this way because I’m working like SEO filter with those CVs by lowering rating of “multi-threaded Design Patterns”, “.NET 1,1.1,1.2,1.3,2.1.2.2.3.4.3.4.5.6.5.4.3 and dot.Net Remounting for Agile Ajax(tm) Web Services”, “deep knowledge and application of MFC, GUI, WinForms, C++, C#, HASP, SQL, ADO, OOP, ODP, IRC, TCP, HDTV, AX, DX, COM, VC++, API” and “Windows 3.11, 95, ME, 98, 98 SE, 2000, NT 3, NT 4.2, 2001, XP, XP SP1, SP2, SP3, 2008, 2010, Vista SP3 and DOS”.

Also candidate which “management responsibility for the 26 software engineers (6 teams) located at India (Bangalore). Resources allocation for $1M budget and restructuring planning based on defined priorities” probably won’t be developer, but want to be finance controller, because we was able to make 26 developers (even in India) to work for $38K a year including materials, living, facilities, etc.

So a small tip for your CV, please write what you did (yes, it is not shame if you never heard about OOP/ODP, but knows how and when it should be virtual and when abstract).This way you’ll not waste your and your future employer time.

Step two – Phone call

If CV is readable and clear for me, I’m calling the person. Usually it takes under 5 minutes to invite him/her or not for frontal interview. However sometimes it can take even half an hour. I am not asking the same questions, this because it is impossible to ask the same somebody who write smart client applications and those from web planet. I almost never ask a candidate to repeat what he/she just told. The only exception is when I am not understand the answer. However when the candidate asks relevant questions it is big plus for him.

The general set of questions contains of three main sections: what exactly you did (even if it clear from CV), what are you looking for and whether you want to work in Better Place.

The phone call ends by taking one of four direction: invite for frontal interview (jump to step 3.1), invite for technical exam (jump to step 3.2), send home work to prepare (non-blocking wait for a year or two) or “we’ll be in touch” by transferring the candidate with mark “negative” to HR (end of the process). They know how to tell them.

Step three point one – Frontal interview

Frontal interview is usually takes between an hour and an hour and a half. It contains of general CS questions like those. Also question to understand whether the candidate is developer or “code-monkey” – one who does not know what the difference between scalar and vector or how to convert float to int faster then using “corporate frameworks developed by other team”. The thumb rule is simple – if each question takes more then 10 seconds to answer the candidate does not really knows the topic. In spite of this, I allow about a minute for each answer.

Next section is related to specific domain of the candidate. It might be architectural overview of one of his/her solutions, all he/she knows about inheritance (why, how, multiple, virtual, public, etc). Constructors/destructors/templates/generics/allocations/virtual inheritance is a next part. What is “new” and why we need it. How to cleanup remindings and how it works in real life (even if you thought that it should be done automagically). Special bonus for those who knows what interrupts and registers are and how it really works under the hood.

If the candidate good enough we keep going toward data structures. This includes linear/random access, hash, trees (with different colors), search vs seek vs lookup with yield return bonus. What is special about “string”. Lazy invocation vs evaluation, alloc/malloc (if relevant), lifetime.

If the candidate is especially good we can start speaking the target language (sometimes it’s assembler and sometimes is C# or even Python [I can speak some :) ]).

Step three point two – Technical exam

Sometimes, when it is not very clear whether the candidate is programmer, I’m asking for write a small program from scratch without using standard libraries. For less developer position I’m asking to find a number of bugs in real code (which includes required bugs :) ). Code is different for different levels of programmers. Also in most cases a candidate can use any computer language he/she wants.

To prepare such exam we need to work very hard. First of all (and before it comes to candidates) we’re sending it to all developers in similar positions and asking them to do it. Then we are measuring time and multiply it by 2. Also we’re writing down all question, asked by developers and trying to fix description to answer all commons.

Candidate got new clean machine, empty silent room with air conditioner and can use any information sources he/she wants in order to do the exam. It can be internet, friends, books, etc. However it is not so simple as it looks. Each candidate has final time to write the program. It takes longer to find the “similar” solution and adopt, rather then do it yourself.

We are require three hours to finish current exam (it took about an hour to finish it for me, and hour and a half in average for the company). If the candidate finished it before the time he/she got a bonus point. If he/she wrote “almost the same code” – failed, “almost finished” or “almost works” – failed, “has not enough time” – failed, two significant bugs – failed, “the question was not clear” – failed, “did not know who to ask” – failed, etc.

It’s amazing how hard for some code-monkeys not to use standard libraries. Sometimes it is too hard for bad programmers to understand that not all computer languages has arrays and collections, sometimes, it’s too hard to realize that if you not really understand what inheritance is you have to write the same procedure more then once.

But not only the exam determinates the future of the candidate. Sometimes (in very rare cases) we’re wrong with this test and do not filter out bad programmer. It is very hard to lay out him later, but because of such person you will not finish project at time, or will do it overbudget. Thus we prefer not to hire, then to fire. This why we have additional step.

Step three point three – Another interview

It is very important to emphasize that we have about 0.05% of those who where at stage three point one. This interview can take upto full day. You should come ready for this. Plan and watch each of your steps. Also you should be ready to speak with number of people near flip charts, whiteboards, calculators, computers and other hardware things. Also you should be ready to assert your opinion.

Most of people you’ll speak with will be very smart (this is not restricted to your direct manager or future colleague only). Sometimes you’ll speak with two or three people at simultaneously. Each person will have his own very special opinion. Also the questions may wary from base calculations to mechanical and electronic schematics or references of your vehicle.

You can take timeout, however take into account that people speaking with your sacrificing their time and projects, so you should consider it.

Step four – final

If you passed all three previous steps – you can consider yourself as our future employee. There were only once, when we had not hire developer because of financial/welfare issue. This because of our stances of good developers. If you did not hire them – you and all company will loose, not the developer. Each of those will find his/her place very soon. Even if the good developer has no CV at all or he/she is a student or foreign resident. Because the only chance for this company to produce good results is to have good human resources.

Send me your CV or contact me tamir [AT] khason.biz to tell that you are the one I’m looking for >>

Please do not send me your CV if it passes the regex of the first step – just send a message with list of what you did for last 7 years.

How to calculate CRC in C#?

First of all, I want to beg your pardon about the frequency of posts last time. I’m completely understaffed and have a ton of things to do for my job. This why, today I’ll just write a quick post about checksum calculation in C#. It might be very useful for any of you, working with devices or external systems.

BIOS CRC Error for old thinkpad

CRC – Cyclic Redundancy Check is an algorithm, which is widely used in different communication protocols, packing and packaging algorithms for assure robustness of data. The idea behind it is simple – calculate unique checksum (frame check sequence) for each data frame, based on it’s content and stick it at the end of each meaningful message. Once data received it’s possible to perform the same calculating and compare results – if results are similar, message is ok.

There are two kinds of CRC – 16 and 32 bit. There are also less used checksums for 8 and 64 bits. All this is about appending a string of zeros to the frame equal in number of frames and modulo two device by using generator polynomial containing one or more bits then checksum to be generated. This is very similar to performing a bit-wise XOR operation in the frame, while the reminder is actually our CRC.

In many industries first polynomial is in use to create CRC tables and then apply it for performance purposes. The default polynomial, defined by IEEE 802.3 which is 0xA001 for 16 bit and 0x04C11DB7 for 32 bit. We’re in C#, thus we should use it inversed version which is 0×8408 for 16 bit and 0xEDB88320 for 32 bit. Those polynomials we’re going to use also in our sample.

So let’s start. Because CRC is HashAlgorithm after all, we can derive our classes from System.Security.Cryptography.HashAlgorithm class.

public class CRC16 : HashAlgorithm {
public class CRC32 : HashAlgorithm {

Then, upon first creation we’ll generate hashtables with CRC values to enhance future performance. It’s all about values table for bytes from 0 to 255 , so we should calculate it only once and then we can use it statically.

[CLSCompliant(false)]
public CRC16(ushort polynomial) {
HashSizeValue = 16;
_crc16Table = (ushort[])_crc16TablesCache[polynomial];
if (_crc16Table == null) {
_crc16Table = CRC16._buildCRC16Table(polynomial);
_crc16TablesCache.Add(polynomial, _crc16Table);
}
Initialize();
}

[CLSCompliant(false)]
public CRC32(uint polynomial) {
HashSizeValue = 32;
_crc32Table = (uint[])_crc32TablesCache[polynomial];
if (_crc32Table == null) {
_crc32Table = CRC32._buildCRC32Table(polynomial);
_crc32TablesCache.Add(polynomial, _crc32Table);
}
Initialize();
}

Then let’s calculate it

private static ushort[] _buildCRC16Table(ushort polynomial) {
// 256 values representing ASCII character codes.
ushort[] table = new ushort[256];
for (ushort i = 0; i < table.Length; i++) {
ushort value = 0;
ushort temp = i;
for (byte j = 0; j < 8; j++) {
if (((value ^ temp) & 0×0001) != 0) {
value = (ushort)((value >> 1) ^ polynomial);
} else {
value >>= 1;
}
temp >>= 1;
}
table[i] = value;
}
return table;
}

private static uint[] _buildCRC32Table(uint polynomial) {
uint crc;
uint[] table = new uint[256];

// 256 values representing ASCII character codes.
for (int i = 0; i < 256; i++) {
crc = (uint)i;
for (int j = 8; j > 0; j–) {
if ((crc & 1) == 1)
crc = (crc >> 1) ^ polynomial;
else
crc >>= 1;
}
table[i] = crc;
}

return table;
}

The result will looks like this for 32 bits

        0x00, 0x31, 0x62, 0x53, 0xC4, 0xF5, 0xA6, 0x97,
        0xB9, 0x88, 0xDB, 0xEA, 0x7D, 0x4C, 0x1F, 0x2E,
        0x43, 0x72, 0x21, 0x10, 0x87, 0xB6, 0xE5, 0xD4,
        0xFA, 0xCB, 0x98, 0xA9, 0x3E, 0x0F, 0x5C, 0x6D,
        0x86, 0xB7, 0xE4, 0xD5, 0x42, 0x73, 0x20, 0x11,
        0x3F, 0x0E, 0x5D, 0x6C, 0xFB, 0xCA, 0x99, 0xA8,
        0xC5, 0xF4, 0xA7, 0x96, 0x01, 0x30, 0x63, 0x52,
        0x7C, 0x4D, 0x1E, 0x2F, 0xB8, 0x89, 0xDA, 0xEB,
        0x3D, 0x0C, 0x5F, 0x6E, 0xF9, 0xC8, 0x9B, 0xAA,
        0x84, 0xB5, 0xE6, 0xD7, 0x40, 0x71, 0x22, 0x13,
        0x7E, 0x4F, 0x1C, 0x2D, 0xBA, 0x8B, 0xD8, 0xE9,
        0xC7, 0xF6, 0xA5, 0x94, 0x03, 0x32, 0x61, 0x50,
        0xBB, 0x8A, 0xD9, 0xE8, 0x7F, 0x4E, 0x1D, 0x2C,
        0x02, 0x33, 0x60, 0x51, 0xC6, 0xF7, 0xA4, 0x95,
        0xF8, 0xC9, 0x9A, 0xAB, 0x3C, 0x0D, 0x5E, 0x6F,
        0x41, 0x70, 0x23, 0x12, 0x85, 0xB4, 0xE7, 0xD6,
        0x7A, 0x4B, 0x18, 0x29, 0xBE, 0x8F, 0xDC, 0xED,
        0xC3, 0xF2, 0xA1, 0x90, 0x07, 0x36, 0x65, 0x54,
        0x39, 0x08, 0x5B, 0x6A, 0xFD, 0xCC, 0x9F, 0xAE,
        0x80, 0xB1, 0xE2, 0xD3, 0x44, 0x75, 0x26, 0x17,
        0xFC, 0xCD, 0x9E, 0xAF, 0x38, 0x09, 0x5A, 0x6B,
        0x45, 0x74, 0x27, 0x16, 0x81, 0xB0, 0xE3, 0xD2,
        0xBF, 0x8E, 0xDD, 0xEC, 0x7B, 0x4A, 0x19, 0x28,
        0x06, 0x37, 0x64, 0x55, 0xC2, 0xF3, 0xA0, 0x91,
        0x47, 0x76, 0x25, 0x14, 0x83, 0xB2, 0xE1, 0xD0,
        0xFE, 0xCF, 0x9C, 0xAD, 0x3A, 0x0B, 0x58, 0x69,
        0x04, 0x35, 0x66, 0x57, 0xC0, 0xF1, 0xA2, 0x93,
        0xBD, 0x8C, 0xDF, 0xEE, 0x79, 0x48, 0x1B, 0x2A,
        0xC1, 0xF0, 0xA3, 0x92, 0x05, 0x34, 0x67, 0x56,
        0x78, 0x49, 0x1A, 0x2B, 0xBC, 0x8D, 0xDE, 0xEF,
        0x82, 0xB3, 0xE0, 0xD1, 0x46, 0x77, 0x24, 0x15,
        0x3B, 0x0A, 0x59, 0x68, 0xFF, 0xCE, 0x9D, 0xAC

Now, all we have to do is to upon request to lookup into this hash table for related value and XOR it

protected override void HashCore(byte[] buffer, int offset, int count) {

for (int i = offset; i < count; i++) {

ulong ptr = (_crc & 0xFF) ^ buffer[i];

_crc >>= 8;

_crc ^= _crc32Table[ptr];

}

}

new public byte[] ComputeHash(Stream inputStream) {

byte[] buffer = new byte[4096];

int bytesRead;

while ((bytesRead = inputStream.Read(buffer, 0, 4096)) > 0) {

HashCore(buffer, 0, bytesRead);

}

return HashFinal();

}

protected override byte[] HashFinal() {

byte[] finalHash = new byte[4];

ulong finalCRC = _crc ^ _allOnes;

finalHash[0] = (byte)((finalCRC >> 0) & 0xFF);

finalHash[1] = (byte)((finalCRC >> 8) & 0xFF);

finalHash[2] = (byte)((finalCRC >> 16) & 0xFF);

finalHash[3] = (byte)((finalCRC >> 24) & 0xFF);

return finalHash;

}

We done. Have a good time and be good people. Also, I want to thank Boris for helping me with this article. He promised to write here some day…

Source code for this article

Book review: C# 2008 and 2005 Threaded Programming

A couple of weeks ago, Packt publishing asked me to review Gastón C. Hillar book “C# 2008 and 2005 Threaded Programming: Beginner’s Guide”. They sent me a copy of this book and today, I’m ready to write a review for it. But before I’ll start reviewing it, I want to apologize to the publisher and author for the impartial review.

image

First of all, you should understand, that this book is about how it possible (for this book author) to write four programs (with awful user interface) using different classes from System.Threading namespace to perform tasks, rather then what is multithreaded programming and how to achieve best performance by utilizing multiple CPU power. Your own programs will not run faster after reading this book, but you’ll probably know (if you did not know before) how to use , , , and classes. Also, there is a small chapter about thread context switching for UI thread delegates invocation and parallel extensions.

There are some technical misconceptions and errors in this book. But it is not the major problem of it. The problem is that while reading this book I question myself whom this book aimed at? Language style is somewhere between blog chatting (better then mine) and MSDN style documentation. I admit I don’t know quite how to categorize this, the author writes in a style that is just bizarre (even more bizarre then mine in this blog :) ) Overall, it sounds like I’m reading a conversation between two beginner-level programmers trying to explain one each other why they are using certain coding convention in C#.

Another half of this 395 pages book is just copy-paste stuff from Visual Studio (including it default tabulations and indentations). Here one of representative examples of such copy/paste

// Disable the Start button
butStart.Enabled = false;
// Enable the Start button
butStart.Enabled = true;

// Some very useful property, which used as private member for another public property
private int priVeryUserfulProperty;

public int VeryUserfulProperty
{
   get
   {
      return priVeryUserfulProperty;
   }
   set
   {
      priVeryUserfulProperty = value;
   }
}

Verdict: Not very exemplary introduction to some classes inside System.Threading namespace for fellow students who like to read blogs, rather then books and documentation and do not want to understand how it works under the hoods, but write something and forget it.

3- of 5 on my scale. This book is not all bad, though, but apparently suitable for very specific audience, which definitely excludes me.

Recommended

 

Sponsor


Partners

WPF Disciples
Dreamhost
Code Project