Monday, February 7, 2011

Reading a Structure from a Stream

Here is a nice way to read a data structure from a stream.



I created a generic method to help reading structured data from a Stream

It is easy to implement as an Extension method on the base type Stream.



public T ReadStruct(this Stream stream) where T : struct
{
var size = Marshal.SizeOf(typeof(T));

byte[] buffer = new byte[size];
stream.Read(buffer, 0, size);

var ptr = GCHandle.Alloc(buffer, GCHandleType.Pinned);
var obj = (T)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T));
ptr.Free();

return obj;
}


This way it may for example help to read an ID3Tag of an MP3 file

As an example I will show you how to read this ID3Tag



But first I have to explain a few things about Structures in C#,

because there are a few ways to do this.

1. LayoutKind.Explicit

this way you can explicitly set the Offset of each field.

It wouldn't make it easy for you to read array's of a ValueType because the array is instantiated, so you will be able to read only the first char for example.


[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct HeaderData
{
[FieldOffset(0)]
public char[] TYPECODE;
[FieldOffset(100)]
public Int32 Code;
}



2. LayoutKind.Sequential and fixed fields

This way you'll be able to define fixed array's in .Net

There is only one catch that still makes it a bit difficult to access these arrays

This is marking an field as fixed will actually only give you a pointer to this array.


[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct HeaderDataFixed
{
public fixed char TYPECODE[4];
public Int32 Code;
}

Declaring fixed char TYPECODE[4]; will give you char*



3. LayoutKind.Sequential and MashalAs

This is the way I think will fit our needs the best and for no other reason I used in my sample code below.




[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct tagID3TAG
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public char[] tag;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)]
public char[] songname;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)]
public char[] artist;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)]
public char[] album;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public char[] year;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)]
public char[] comment;

public char genre;
}

public tagID3TAG ReadID3Tag(string filename)
{
using (var stream = File.OpenRead(filename))
{
var size = Marshal.SizeOf(typeof(tagID3TAG));

stream.Seek(-size, SeekOrigin.End);

byte[] buffer = new byte[size];
stream.Read(buffer, 0, size);

var ptr = GCHandle.Alloc(buffer, GCHandleType.Pinned);
var obj = (tagID3TAG)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(tagID3TAG));
ptr.Free();

return obj;
}
}



Well, that will be it for this great little article.

Hope you find good use for it, if anyone needs help or would like me to explain how to do certain thing in .Net, please don't hesitate to contact me :-)

Monday, September 13, 2010

How To: Serialize Objects to Xml using LINQ

Serializing Objects to Xml can be done in numerous ways,
for example by using the System.Xml.Serialization.XmlSerializer.
Most of these performs only shallow serialization and in return you get your basic Xml.

In this post I will try and explain how easy it is to write your own Xml serializer using LINQ and Reflection.
The code sample below can create en read xml files from any Object or List of Objects and it also adds an Xml comment line.

Let me present the code, I'll go over it afterwards:


#region Copyright © 2010 Ruben Knuijver [r.knuijver@primecoder.com]
/*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the author(s) be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Reflection;
using System.Linq.Expressions;
using System.ComponentModel;

public class ObjectXml
{

public static XElement ObjectToElement(XName name, object obj)
{
if (obj == null)
throw new ArgumentNullException("obj");

Type type = obj.GetType();

var elems = from property in type.GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance)
let value = property.GetValue(obj, null)
where property.CanRead == true
select new XAttribute(property.Name, value);

return new XElement(name, elems);
}

public static void SaveListAsXml(List list, string filename)
{
var elements = from item in list
select ObjectToElement("Item", item);

XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XComment("List - XmlExport by R.F. Knuijver"),
new XElement(typeof(T).Name,
new XAttribute("version", "1.0"), elements.ToArray()));

doc.Save(filename);
}

public static T ElementToObject(XElement element)
{
T value = Activator.CreateInstance();
var properties = from a in typeof(T).GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance)
where a.CanWrite
select a;

foreach (var item in properties)
item.SetValue(value, ChangeType(element.Attribute(item.Name).Value, item.PropertyType), null);

return value;
}
public static List LoadListFromXml(string filename)
{
var doc = XDocument.Load(filename);
var elements = from e in doc.Element(typeof(T).Name).Elements()
select ElementToObject(e);

return elements.ToList();
}

public static object ChangeType(object value, Type conversionType)
{
return ChangeType(value, conversionType, System.Globalization.CultureInfo.InvariantCulture);
}
public static object ChangeType(object value, Type conversionType, IFormatProvider provider)
{
if (conversionType == null)
throw new ArgumentNullException("conversionType");


if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
if (value == null)
return null;

// It's a nullable type, and not null, so that means it can be converted to its underlying type
// so overwrite the passed-in conversion type with this underlying type
NullableConverter nullableConverter = new NullableConverter(conversionType);
conversionType = nullableConverter.UnderlyingType;
}

// deal with conversion to enum types when input is a string
if (conversionType.IsEnum && value is string)
return Enum.Parse(conversionType, value as string);

// deal with conversion to enum types when input is a integral primitive
if (value != null && conversionType.IsEnum && value.GetType().IsPrimitive && !(value is bool) && !(value is char) && !(value is float) && !(value is double))
return Enum.ToObject(conversionType, value);

// pass the call on to Convert.ChangeType
return Convert.ChangeType(value, conversionType, provider);
}

public static T ChangeType(this object value)
{
if (value == null || value is DBNull)
return default(T);

return (T)ChangeType(value, typeof(T));
}
}

Wednesday, January 28, 2009

Perfect Random numbers


#region Copyright © 2009 Ruben Knuijver [r.knuijver@primecoder.com]
/*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the author(s) be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
#endregion

using System;
using System.Threading;

class TaskRnd : IDisposable
{
internal class RandomParam
{
public int Seed { get; set; }
public int minValue { get; set; }
public int maxValue { get; set; }
}

private Thread worker = null;
public delegate void WorkCompleteHandle(int rnd);
public event WorkCompleteHandle OnWorkComplete;

private RandomParam _randomParam = new RandomParam();

public int Seed { set { _randomParam.Seed = value; } }
public int minValue { set { _randomParam.minValue = value; } }
public int maxValue { set { _randomParam.maxValue = value; } }

public TaskRnd()
{
Seed = 1978;
minValue = int.MinValue;
maxValue = int.MaxValue;
}

private void DoWork(Object param)
{
RandomParam p = param as RandomParam;
Random rnd = new Random(p.Seed);
int min = p.minValue,
max = p.maxValue;

while (OnWorkComplete != null)
{
int value = rnd.Next(min, max);

OnWorkComplete(value);
}
}

public void Start()
{
worker = new Thread(new ParameterizedThreadStart(this.DoWork));
worker.Start(_randomParam);
Thread.Sleep(1);
}

#region IDisposable Members

public void Dispose()
{
if (worker.IsAlive)
{
worker.Abort();
worker.Join();
worker = null;
}
}

#endregion
}


Here is how to use the code:

1: using (TaskRnd t = new TaskRnd())
2: {
3: int i = 0;
4: t.maxValue = 255;
5: t.minValue = 0;
6: t.OnWorkComplete += (rnd) => { i = rnd; };
7: t.Start();
8:
9: for (int x = 0; x < 10; x++)
10: {
11: listValues2.Items.Add(string.Format("rnd {0}: {1}", x, i));
12: }
13: }

Monday, January 5, 2009

How to write a download task list in WPF


In this short tutorial I’ll explain how to write a ListBox that is databound
to a list of download tasks. The sourcecode for this tutorial is in C# 3.5 (WPF)
XAML. Let’s start by writing our DownloadTaskItem class.




internal class DownloadTaskItem : INotifyPropertyChanged, IDisposable



The INotifyPropertyChanged event can be used to signal
the parent the one of our property values has changed.



public event PropertyChangedEventHandler PropertyChanged;

void OnPropertyCanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}



Now let’s add a fue priviate members.



private WebClient webClient;


This WebClient object will use for our async download.


private string fileName;

private Uri uriData;

private long fileSize;

private Guid _guid;

private string _progressText;

private string _buttonText;

private string _associatedGroupText;



To implement this in our properties and make this all
work we’ll have to call OnPropertyChanged in our properties.



public string AssociatedGroupText
{
get { return _associatedGroupText; }
set
{
_associatedGroupText = value;
OnPropertyCanged("AssociatedGroupText");
}
}

public string ProgressText
{
get { return _progressText; }
set
{
_progressText = value;
OnPropertyCanged("ProgressText");
}
} // DETAIL_BYTES

public string ButtonText
{
get { return _buttonText; }
set
{
_buttonText = value;
OnPropertyCanged("ButtonText");
}
}


The rest of the properties can be write as usual.



public string OriginalUrl { get { return uriData.OriginalString; } } // DETAIL_URI

public string TaskGuid
{
get
{
if (null != _guid)
_guid = new Guid();
return _guid.ToString();
}
}

public long ReceivedFileSize { get { return fileSize; } }



Now how does the parent object know if this downloadtask
has completed. Well the best way to do this is by adding an other event.

First thing we need is an EventArgs class:



public class DownloadTaskEventArgs : EventArgs
{
private readonly Uri uri;

private readonly System.ComponentModel.AsyncCompletedEventArgs args;

//Constructor.
public DownloadTaskEventArgs(Uri uri, System.ComponentModel.AsyncCompletedEventArgs e)
{
this.uri = uri;
this.args = e;
}

public Uri DataUri
{
get { return uri; }
}

public System.ComponentModel.AsyncCompletedEventArgs Args
{
get { return args; }
}

// Notifying Text property that contains a status message.
//
public string Text
{
get
{
if (args.Error == null || args.Cancelled)
{
return ("completed");
}
else
{
return ("failed");
}
}
}
}


Now adding the event to our DownloadTaskItem class:



public delegate void CompletedEventHandler(object sender, DownloadTaskEventArgs e);

public event CompletedEventHandler Completed;

// The protected OnCompleted method raises the event by invoking
// the delegates. The sender is always this, the current instance
// of the class.
//
protected virtual void OnCompleted(DownloadTaskEventArgs e)
{
CompletedEventHandler handler = Completed;
if (handler != null)
{
// Invokes the delegates.
handler(this, e);
}
}


DownloadTaskItem Class:



internal class DownloadTaskItem : INotifyPropertyChanged, IDisposable
{
#region Task Completed events
public delegate void CompletedEventHandler(object sender, DownloadTaskEventArgs e);

public event CompletedEventHandler Completed;

// The protected OnCompleted method raises the event by invoking
// the delegates. The sender is always this, the current instance
// of the class.
//
protected virtual void OnCompleted(DownloadTaskEventArgs e)
{
CompletedEventHandler handler = Completed;

if (handler != null)
{
// Invokes the delegates.
handler(this, e);
}
}
#endregion

#region Private fields
private WebClient webClient;

private string fileName;

private Uri uriData;

private long fileSize;

private Guid _guid;

private string _progressText;

private string _buttonText;

private string _associatedGroupText;
#endregion

#region Great Properties
public string AssociatedGroupText
{
get { return _associatedGroupText; }
set
{
_associatedGroupText = value;
OnPropertyCanged("AssociatedGroupText");
}
}

public string ProgressText
{
get { return _progressText; }
set
{
_progressText = value;
OnPropertyCanged("ProgressText");
}
} // DETAIL_BYTES

public string OriginalUrl { get { return uriData.OriginalString; } } // DETAIL_URI

public string TaskGuid
{
get
{
if (null != _guid)
_guid = new Guid();
return _guid.ToString();
}
}

public string ButtonText
{
get { return _buttonText; }
set { _buttonText = value; OnPropertyCanged("ButtonText"); }
}

public long ReceivedFileSize { get { return fileSize; } }
#endregion

#region DownloadTaskItem Constructors
///
/// Initializes a new instance of the class.
///

public DownloadTaskItem(Uri uriData, string path)
{
#region Prepare for download
this.uriData = uriData;

fileName = this.uriData.Segments[this.uriData.Segments.Length - 1];
fileName = path.Equals(string.Empty) ? fileName : string.Format("{0}\\{1}", path, fileName);

webClient = new WebClient();
#endregion

#region Assign call backs
webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler((sender, e) =>
{
#region Check if additional info. is required
#endregion

#region Show progress info
fileSize = e.TotalBytesToReceive;
AssociatedGroupText = String.Format("{0} - {1}%", fileName, e.ProgressPercentage);

ProgressText = String.Format("Downloaded {0}/{1}", GetHumanReadableFileSize(e.BytesReceived), GetHumanReadableFileSize(fileSize));
#endregion
});

webClient.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler((sender, e) =>
{
#region Check if additional info. is required
#endregion

#region Process error state
if (e.Error == null)
{
AssociatedGroupText = String.Format("{0} - completed", fileName);
ProgressText = String.Format("File size - {0}", GetHumanReadableFileSize(fileSize));
}
else if (e.Cancelled == false && e.Error != null)
{
AssociatedGroupText = String.Format("{0} - Failed", fileName);
ProgressText = e.Error.Message;
}
#endregion

#region Process canceled download
if (e.Cancelled == true)
{
AssociatedGroupText = String.Format("{0} - Canceled", fileName);
}
#endregion

ButtonText = "Remove";

OnCompleted(new DownloadTaskEventArgs(uriData, e));
});

AssociatedGroupText = String.Format("{0} - starting ... ", fileName);
ProgressText = "Downloaded 0/? bytes";
ButtonText = "Cancel";
#endregion

#region Start file download
webClient.DownloadFileAsync(uriData, fileName, this);
#endregion
}
#endregion

public bool StopDownload(string message)
{
ProgressText = message;
if (webClient.IsBusy == true)
{
webClient.CancelAsync();
return true;
}
else
{
webClient.CancelAsync();
return false;
}
}

#region Helper classes
///
/// Convert bytes to a human readable format
///

/// the number bytes
/// apropriate amount of bytes (Gb, Mb, Kb, bytes)
private string GetHumanReadableFileSize(long fileSize)
{
#region Gb
if ((fileSize / (1024 * 1024 * 1024)) > 0)
{
return String.Format("{0} Gb", (double)Math.Round((double)(fileSize / (1024 * 1024 * 1024)), 2));
}
#endregion

#region Mb
if ((fileSize / (1024 * 1024)) > 0)
{
return String.Format("{0} Mb", (double)Math.Round((double)(fileSize / (1024 * 1024)), 2));
}
#endregion

#region Kb
if ((fileSize / 1024) > 0)
{
return String.Format("{0} Kb", (double)Math.Round((double)(fileSize / 1024), 2));
}
#endregion

#region Bytes
return String.Format("{0} b", fileSize);
#endregion
}
#endregion

#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;

void OnPropertyCanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion

#region IDisposable Members
public void Dispose()
{
if (this.webClient != null)
{
this.webClient.CancelAsync();

this.webClient.Dispose();
}
}
#endregion
}



How to use this class.

First we’ll create a window and set it’s properties
like the sample below.

I chose a grid layout for this tutorial but you could
use canvas if you like.



xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006”
Title="My Downloader"
Height="480" Width="640"
mc:Ignorable="d"
xmlns:src="clr-namespace:MyDownloader" WindowStartupLocation="CenterScreen">






Now we can add our ListBox. You have to give it a name
using the x:Name property. Now let’s add a ItemTemplate with a DataTemplate
and add our databound fields in a layout. The binding source of these fields are
the properties of our DownloadTask object. I chose to use StackPanel orientation
vertical to have a first row containing the title and the the second row tobe an
other StackPanel with it’s orientation set to horizontal. The second StackPanel
content is in this case a Textbox showing the text for an Cancel Button and a Textbox showing our download progress.

















Sunday, December 14, 2008

Almost free domain and hosting

I found something really nice!

Just paid €1,50 for a domainname at http://sms.easyhost.nl/nl (this goes till 31 januari 2009)
After that I went to http://www.woelmuis.nl and registered for free hosting. This makes it even more fun!
Only thing.. You also receive a free icon of woelmuis at the bottom-left corner of all your pages.

Dont complain, it's free after all.

Only one thing left to do, registering the nameservers. This can be done in the easyhost controle panel. Accept there seams to be a bug, aahhh well, there is an alternative way. I just send an email to easyhost support.

Guess what,
It's Saturday evening, around 22.00 / 23.00 o'clock... hmmm, do I really have to wait till Monday?
Nope! In less than 20 minutes I received a very pleasing response :)

All there was left.. waiting for server records to be updated.
http://knuijver.nl

Saturday, September 13, 2008

First things first!

Hey, welcome to my first blog post!

It's been a few years, thou I finally got myself to write a new post on my new blog.
My last blog.. I really can't remember, it's gone. Sade but true. :(


About my blog.
I think it will be mainly about ASP.NET, development, security, and other thing that I work on.