Thursday, December 30, 2010

Search Youtube Using C#

In this post I want to show you, how to search Youtube with C#, so searching for videos on that platform.
For that we use the Google Data API, especially the Youtube SDK. With these programming libraries  the access to Youtube & Co pretty easy.
The Google Data API can be downloaded on this page, for access on Youtube we only need this file, the Youtube SDK. We need to install it in order to be able to use it.
Searching through Youtube is done with so called feeds. Feeds are something like the content of a webpage in short form, mostly in plain text. In that way, readers can quickly inform themselves of the newest topics, furthermore many programs read these text files (e.g. search engines use them for indexing). This blog has a feed as well.
Using the Youtube SDK we can read feeds with a C# program, for searching on Youtube we use the feed http://gdata.youtube.com/feeds/videos?q=Suchbegriff.
This feed is provided by Youtube and lists the to "Suchbegriff" matching search results in text form, the feed can also be viewed in a browser.

Now I want to explain the code, over which this feed can be analyzed in C#. First the downloaded libraries  Google.GData.Client.dll and Google.GData.Extensions.dll have to be included. They are contained in the Redist folder of the install folder (in my case, C:\Program Files (x86)\Google\Google YouTube SDK for .NET\Redist). Including can be done with Project - Add Reference - Browse. Then the following code includes the needed parts:

using Google.GData.Client;
using Google.GData.Extensions;

Furthermore it is important, to set the target frameproject in project properties correctly - to .Net Framework 4 and not .Net  Framework 4 Client Profile! (Since they dlls were compiled for the first one.)
I think the actual source code is self explaining.
The following console application searches for "VLC einbinden" on Youtube and then prints the first 10 results on the console:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Google.GData.Client;
using Google.GData.Extensions;

namespace YoutubeToolkit
{
    class YoutubeQuery
    {
        static void Main(string[] args)
        {
            FeedQuery Abfrage = new FeedQuery(); // object for handling feeds
            Abfrage.Uri = new Uri("http://gdata.youtube.com/feeds/videos?q=VLC+einbinden"); // feed URL
            Abfrage.StartIndex = 0; // index, from which on search results should be displayed
            Abfrage.NumberToRetrieve = 10; // maximal number of search results

            Service Service = new Service(); // object to execute the feed query with the FeedQuery Objekt
            AtomFeed Feed = Service.Query(Abfrage); // result of the search query as feed

            // print the title of the video for every search result
            foreach (AtomEntry Ergebnis in Feed.Entries)
            {
                Console.WriteLine(Ergebnis.Title.Text);
            }
        }
    }
}

Monday, December 27, 2010

Disable Sound / Mute Computer

In this post I want to show how to mute the computer. Again, the solution goes over the WinAPI, which is why using System.Runtime.InteropServices; is needed.
The needed P/Invoke function is SendMessageW and has the signature ntPtr SendMessageW(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam).
The function sends a message to a specific program or window. To disable the sound in the computer, we send a command to a system process.
The code looks as follows, first the declaration part:

        [DllImport("user32.dll")]
        public static extern IntPtr SendMessageW(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

        private const int APPCOMMAND_VOLUME_MUTE = 0x80000;
        private const int WM_APPCOMMAND = 0x319;

And the function:

private void Mute()
{
    SendMessageW(this.Handle, WM_APPCOMMAND, this.Handle, (IntPtr)APPCOMMAND_VOLUME_MUTE);
}

Tuesday, December 7, 2010

Add Menus and Submenus Dynamically

I devote this post to menu controls in C# and show how to add menus and submenus during runtime, since I got the impression that this is not so clearly understandable.
I show here methods using some extra stuff, so who knows more direct code, please tell me.
Aim of the sample program will be to create the following menu structure:


First we add a control of the type menuStrip to the form using the designer (of course this also can be dynamically, see add controls during runtime).
Now in the form a yet invisible menu bar is created, which is still empty. In the designer you could now write in this bar to add menus, but we focus on the dynamic part.
Super menus, so menus on the highest level in the menu bar (e.g. "Obermenü 1" and "Obermenü 2") can be edited with the property Items of the class menuStrip.




The following code commands create 2 supermenus:

menuStrip1.Items.Add("Obermenü 1");
menuStrip1.Items.Add("Obermenü 2");

Using indicces (e.g. menuStrip1.Items[0]) we can access these menuentries, but we cannot create submenus in them.
For that we have to convert the object explicitely to an object of the type ToolStripMenuItem. This now has the property DropDownItems, with which we can create submenus.
To create the submenus "Untermenü 1" and "Untermenü 2" for example, we run the following code

((ToolStripMenuItem)menuStrip1.Items[0]).DropDownItems.Add("Untermenü 1");       
((ToolStripMenuItem)menuStrip1.Items[0]).DropDownItems.Add("Untermenü 2");

This submenus right now are not accessible any more. But we need them, if we want to create more submenus, which open to the right (in the example "rechtes Untermenü 1" and "rechtes Untermenü 2").
Therefore, we just save the submenu when creating in a variable of type ToolStripMenuItem, e.g. like this:

ToolStripMenuItem Untermenue1 = (ToolStripMenuItem)((ToolStripMenuItem)menuStrip1.Items[0]).DropDownItems.Add("Untermenü 1");

(In front of the assignment again an explicite type conversion has to happen.)
Now we have continous access to the desired submenu, and can add own submenus to it with DropDownItems.Add().
Finally the complete soure code:

            menuStrip1.Items.Add("Obermenü 1");
            menuStrip1.Items.Add("Obermenü 2");

            ToolStripMenuItem Untermenue1 = (ToolStripMenuItem)((ToolStripMenuItem)menuStrip1.Items[0]).DropDownItems.Add("Untermenü 1");
            ((ToolStripMenuItem)menuStrip1.Items[0]).DropDownItems.Add("Untermenü 2");

            Untermenue1.DropDownItems.Add("rechtes Untermenü 1");
            Untermenue1.DropDownItems.Add("rechtes Untermenü 2");

Sunday, December 5, 2010

Check Pressing of Multiple Keys Simultaneously

In the functions KeyDown() and KeyPress(), which are implemented by nearly every control, key events can be analyzed.
The argument e of the type KeyEventArgs provides the methods and properties needed for that, for example with e.KeyCode can be checked, which key is pressed.
If multiple keys are pressed (at the same time), for each key an event is triggered, which does not help when wanting to check for key combinations.
In this post I will describe, how to check these cases with they property KeyData and Flags. But, these methods only work when a combination of Alt and / or Shift and / or Ctrl and at most one other key is pressed.
First with KeyData:
In this property 4 bytes are saved, the lower 2 ones represent the pressed key, the upper 2 ones the pressed function keys (Alt, Shift, Ctrl). If multiple keys are pressed, the byte values are combined via Or.
Now when you press a function keys and one other key, a unique bit combination is created. But of course, if multiple "normal" keys are pressed, this uniqueness is not guaranteed anymore, since the bits then can overlap.
To check a pressed combination, the value from e.KeyData is compared to an Or conjunction of the desired value.
The following sample terminates the program, if the key combination Ctrl + Shift + Alt + "E" is pressed:

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyData == (Keys.Shift | Keys.Alt | Keys.Control | Keys.E))
        this.Close();
}

The same result though can also be used if checking the pressed key with e.KeyCode and then checking the properties e.Shift, e.Alt and e.Control (these properties are so called flags, representing the function keys), which describe whether these keys are pressed:

if (e.KeyCode == Keys.E && e.Shift && e.Alt && e.Control)
    this.Close();

Friday, December 3, 2010

Check Status of a Network Adapter

In a previous post was shown how to check if the computer is connected with a network.
In this post now we determine the status of a network interface more exactly. We do not want to distinguish any more only between "connected" and "not connected", but get more information.
Again we use the class System.Net.NetworkInformation, but this time use the property OperationalStatus.
This can have the following values:

  • Dormant - The interface waits for an external event, it cannot transmit data.
  • Down - No connection.
  • LowerLayerDown - The interface cannot transmit data, since it user lower layer interfaces and at least one of them is not working.
  • NotPresent - Not working, mostly due to a hardware failure.
  • Testing - The interface is doing tests.
  • Unknown - Unknown status.
  • Up - The interface is working.

The following small sample lists all network adapters which are working:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            foreach (System.Net.NetworkInformation.NetworkInterface n in System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces())
            {
                if (n.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up)
                    Console.WriteLine(n.Name + " is up.");
            }     
        }
    }
}

List All Network Adapters and Show Further Information

As in the previous post we again use the class System.Net.NetworkInformation here. The function  NetworkInterface.GetAllNetworkInterfaces() returns an array of the type System.Net.NetworkInformation.NetworkInterface, in which each entry represents a network interface or network adapter.
We now can iterate over this array and list all network adapters in this way. The following example prints name and status (is explained further in the next post) of each network adapter:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            foreach (System.Net.NetworkInformation.NetworkInterface n in System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces())
            {
                Console.WriteLine("Name: " + n.Name + " Status: " + n.OperationalStatus.ToString());
            }
        }
    }
}

Sunday, November 28, 2010

Check If Network Connection Exists

We use the class System.Net.NetworkInformation.NetworkInterface to check, if the computer has a connection to a network.
The function needed for that is GetIsNetworkAvailable(). It checks, whether the computer is connected to a network by any adapter. However, it does not check, whether this network is working or having a connection to the internet.
The following code sample checks connectivity:

bool NetworkAvailable = System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable();

Thursday, November 25, 2010

Assertions

Assertions are methods to verify the correctness of a program. They are conditions, which throw an exception when not met.
Assertions mostly preceed sections of the code, which do not work without meeting the condition. If an assertion is thrown, the programmer knows, that he has to catch the exception in this area.
The expression System.Diagnostics.Debug.Assert() can be used in the debug configuration, System.Diagnostics.Trace.Assert() in the release version. As example a function for division, which prohibits a division through 0:

public double divide(double divident, double divisor)
{
    System.Diagnostics.Debug.Assert(divisor != 0);
    return (divident / divisor);
}

Tuesday, November 23, 2010

String Representation of a Class Via Overloading ToString()

All objects and structures in C# inherit implicitely of the class Object. This class provides basic functions like Equals() (checks for equality) and ToString() (displays the class as string).
That means, by default all classes and structures implement these functinos too.
Most will probably know the notation Console.WriteLine(x.ToString());, it prints the value of the integer as a string. Many classes created by programmers also inherit from Object, therefore they also implement ToString(). But, this will probably not lead to the desired result. When called, the compiler simply prints the type name of the class as string, where should it know how to display the value of an instance as a string.
For that reason it might be clever to override the function ToString() in own classes. For overriding, in the child class a function with the same signature as in the father class has to be created. If now the function is called with an instance of the child class, the call is not forwarded to the base class anymore, but directly done in the child class.
The following console application implements a little class Employee, in which the function ToString() is overridden.
In the modified version, name and salary of the employee are printed out:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

class Program
{
    static void Main(string[] args)
    {
        Employee TestEmployee = new Employee("Hans Meier", 1000);
        Console.WriteLine(TestEmployee.ToString());
    }
}

class Employee
{
    public string Name;
    public int Salary;

    public Employee(string name, int salary)
    {
        Name = name;
        Salary = salary;
    }

    public override string ToString()
    {
        return "Name: " + Name + ", Salary: " + Salary + ".";
    }
}

Sunday, November 21, 2010

A Quine With C#

A quine is a program, which prints it complete source code as output.
So for example, if the source code is

class Quine
{
    static void Main()
    {
        System.Console.WriteLine("This is no quine (yet).");
    }
}

The program had to output exactly this text. Depending on your experience in this topic, you will maybe find the task to write a quine as easy, hard or even impossible.
(For people interested in theory: It is guaranteed that quines exist in all "higher" programming languages, so for example, C, C#, Java etc. - since all these languages are Turing complete, and in Turing complete languages quines can be realised.)
If you start the problem naively, you could maybe suppose it is quickly solved. First you print the code until the line WriteLine(), then this line and finally the rest.
But hang on - we have to print the whole source code, including all WriteLine().
So if you write for example
string test = "abc";
and print this line via WriteLine()
WriteLine("string test = \"abc\";");
, in the next step this line has to be printed as well.
To avoid infinite recursion, the trick is to save the code in a string and then just print this string. Furthermore we also use, that in C# there are placeholders in the WriteLine() command which can be replaced later.
I now show the code of a quine written by me, the abstract requirements from above then hopefully get clear:

class Quine
{
    static void Main(string[] args)
    {
        string s = "class Quine {3} {0} {3} static void Main(string[] args) {3} {0} {3} string s = {2}{1}{2}; {3} System.Console.WriteLine(s, System.Convert.ToChar(123), s, System.Convert.ToChar(34), System.Environment.NewLine, System.Convert.ToChar(125)); {3} {4} {3} {4}";
        System.Console.WriteLine(s, System.Convert.ToChar(123), s, System.Convert.ToChar(34), System.Environment.NewLine, System.Convert.ToChar(125));
    }
}

Saturday, November 13, 2010

Write to the Windows Event Viewer

The Windows Event Log saves many information relevant to the system (e.g. the starting / stopping of clients, to name just one of many fields of application).
With C# we can easily add entries to this with the class System.Diagnostics.EventLog, for example to monitor the program flow. For that we have to first create a new source in the event log (if not already existing).
If there is not yet an entry of the application, so if we create a new source, we have to specify, to which category the program belongs to. For example there are the categories "Application", "Security", "Installation" etc.
The writing of the event is done via the function WriteEntry(). This expects the source as the first parameter, the category as second and optionally as the third the type of the event (e.g. warning, information, error ...)
For writing in the Event Log the programm will probably need administrative rights, how to do that you can read there.
The following code sample writes a short information text to the Windows Event Log (using System.Diagnostics; is required):

        private void Form1_Load(object sender, EventArgs e)
        {
            string Source;
            string LogType;
            string LogEvent;

            Source = "Event Log C# Demo";
            LogType = "Application";
            LogEvent = "I like this blog.";

            if (!EventLog.SourceExists(Source))
                EventLog.CreateEventSource(Source, LogType);

            EventLog.WriteEntry(Source, LogEvent, EventLogEntryType.Information);
        }

Monday, November 8, 2010

Cancel / Delay Shutting Down of Windows

In the previous post I showed, how to discover whether the computer is shutting down.
This post shows how to cancel / delay the shutting down to save data before, example.
In the previous post the function FormClosing() was used, in which the reason for quitting was determined. With this function we also can easily delay the shutting down by setting e.Cancel = true. Now though the shutting down is not fully cancelled, but just delayed until the program is stopped - now for example the application has time to save unsaved data.
In Windows Vista and higher a dialog pops up saying that a program is delaying the shutdown.

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    if (e.CloseReason == CloseReason.WindowsShutDown)
        e.Cancel = true;
}

Another possibility to delay the shutting down of Windows is to use the Microsoft.Win32.SystemEvents.SessionEnding event, which is called when the system is shutting down. The event can be assigned a function handler like other events (e.g. a click on a button), which is called when the event occurs.
The following code sample shows this:

        private void Form1_Load(object sender, EventArgs e)
        {
            Microsoft.Win32.SystemEvents.SessionEnding += new Microsoft.Win32.SessionEndingEventHandler(this.WinShutdown);
        }

        private void WinShutdown(object sender, Microsoft.Win32.SessionEndingEventArgs e)
        {
            e.Cancel = true;
        }

Saturday, November 6, 2010

Discover If System Is Shutting Down

Some people want to get to know via C#, whether the system is currently being shutdown.
Logically, when doing that, all running programs have to be terminated. We will use that and ask for the reason of quitting in the function FormClosing() of the main form, which is called when the form is closed.
The reason can be determined via CloserReason of the argument e. If a shutdown is the reason, this is CloseReason.WindowsShutDown.
The following code snippet displays a message, when the computer is shutting down:

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    if (e.CloseReason == CloseReason.WindowsShutDown)
        MessageBox.Show("PC wird heruntergefahren.");
}

The use of the event Microsoft.Win32.SystemEvents.SessionEnding, which only occurs when the PC is shut down, is explained in the next post.

Saturday, October 30, 2010

A General Error Occurred In GDI+

The error message "A general error occurred in GDI+"is pretty tricky in C# - it occurs sporadically and the reasons are often not clear on the first view. Therefore I want to devote this post to this error and its causes and list the in my experience 2 most common causes of this graphic error.

Above error can occur, when one tries to save the image via the function Save() to a non existing path.
The following code tries to save the content of the pictureBox as an image and then throws the exception, given the path really is not available:

pictureBox1.Image.Save("C:\\NonExistingPath\\test.jpg");

When loading an image, a wrong path leads to a FileNotFoundException.

Another most common cause of "A Generic Error occurred in GDI+" is the not disposing of an image object.
If you for example load the image via the function Image.FromFile(), this stays open in the memory, until it is colleccted. Especially, before disposing no saving with Save() is possible.
The following code example loads an image to the pictureBox and then tries to save it - but the error described here occurs, since it was (most likely) not disposed (I say most likely, as the garbage collector could do that in the mean time):

pictureBox1.Image = Image.FromFile("C:\\test\\test1.jpg");
pictureBox1.Image.Save("C:\\test\\test2.jpg");

For correctness, the image has to be "recopied" again interally:

pictureBox1.Image = Image.FromFile("C:\\test\\test1.jpg");
Image Copy = pictureBox1.Image;
Copy.Save("C:\\test\\test2.jpg");

I hope the post explained the basic difficulty of this error, for further reasons etc. I am always thankful!

Thursday, October 28, 2010

Save Image to Byte Array

To convert an image to a byte array in C#, we first save the image to a stream and then copy the contents of this stream to a byte array.
In general, every stream can be used for that, the easiest one will probably be to use a MemoryStream, which simply writes data temporarily in the RAM or reads from it.
An image can be written by the function Save() in the as parameter specified value, as second parameter  the desired format is passed over, in which the bitmap is to be saved. (so e.g. Gif or Bmp).
The following sample code saves the content of the pictureBox on the form in a byte array:

            MemoryStream TempStream = new MemoryStream();
            byte[] ImageInBytes;

            pictureBox1.Image.Save(TempStream, System.Drawing.Imaging.ImageFormat.Gif);
            ImageInBytes = TempStream.ToArray();

Tuesday, October 26, 2010

Structure Code With #region

In bigger code projects the code view can quickly become confusing, if all functions etc. are extended, there is quite some scrolling to do, to reach the bottom of the code when starting from the top.
But blocks of code can luckily be structured very easily with the code word #region. This code word starts a block, which is finished by #endregion. The defined regions can then retracted and extended as for example functions:

Default (extended):










Retracted:

Wednesday, October 20, 2010

Compression With C#, Part 3 - Archives

In the 3rd and (for now) last post to the topic "Compression with C#" I want to show a possibility, to compress multiple files at once in an archive and to unpack this again.
To understand this post, an understanding of the posts of part 1 and part 2 is helpful.
To pack multiple files to an archive can only be done with a little trick in C#, because by default the gzip format only supports the compression of single files. Collections of multiple files are then mostly first compressed with tar and then with gzip, which leads to the ending .tar.gz.
Although we cannot access the tar format directly in .Net, we still can implement the compression of multiple files: We just take the many files as one big file, in which the single files are split by special characters. The big file is then compressed and when decompressing split again in many files by paying respect to the separator characters.

To the technique:
As an identifier between the files I wrote the following header before every file in the stream:
|*START*OF*HEADER*|*||SIZE_OF_FILE||NAME_OF_FILE*|*END*OF*HEADER*|
There could be problems in the unlikely cause, if the content of a file resembles this structure. This can be solved, but for simplicity I will just stay with this simple header generation.

Creating / Compressing an Archive:
The path to the files to be compressed are given to the compress function as string arrays, as well as the name and path of the archive. The files are then iterated and the corresponding header followed by the content are then written to a MemoryStream.
The data from this are then written to a byte array and this is written via a GZipStream to the archive file.
Due to the fact, that all files are first collected and then written with the GZipStream, the archive can be compressed much more then in the case that all files are written one by one to the archive.
The code:

        private void CreateArchive(string[] files, string archiv)
        {
            GZipStream CompressStream = new GZipStream(new FileStream(archiv, FileMode.Create), CompressionMode.Compress);
            FileStream NormalFileStream;
            byte[] Content;

            ASCIIEncoding encoder = new ASCIIEncoding();
            byte[] HeaderStart = encoder.GetBytes("|*START*OF*HEADER*|*");
            byte[] HeaderEnd = encoder.GetBytes("*|*END*OF*HEADER*|");
            byte[] FileSize;  // size of the current file
            byte[] Separator = encoder.GetBytes("||");
            byte[] FileName; // name of the current file

            MemoryStream TempStream = new MemoryStream();

            foreach (string file in files)
            {
                NormalFileStream = new FileStream(file, FileMode.Open);
                FileSize = encoder.GetBytes(NormalFileStream.Length.ToString());
                FileName = encoder.GetBytes(file.Substring(file.LastIndexOf('\\') + 1));

                TempStream.Write(HeaderStart, 0, HeaderStart.Length);
                TempStream.Write(Separator, 0, Separator.Length);
                TempStream.Write(FileSize, 0, FileSize.Length);
                TempStream.Write(Separator, 0, Separator.Length);
                TempStream.Write(FileName, 0, FileName.Length);
                TempStream.Write(HeaderEnd, 0, HeaderEnd.Length);

                Content = new byte[NormalFileStream.Length];
                NormalFileStream.Read(Content, 0, Content.Length);
                NormalFileStream.Close();
                TempStream.Write(Content, 0, Content.Length);
            }

            byte[] BigFileContent = new byte[TempStream.Length];
            TempStream.Position = 0;
            TempStream.Read(BigFileContent, 0, BigFileContent.Length);
            CompressStream.Write(BigFileContent, 0, BigFileContent.Length);
            CompressStream.Close();
        }


Unpacking / Decompressing the Archive:
The compression was the easy part, decompressing is a bit harder. The function to decompress gets the path and name of the archive, as well as the path to where to files should be extracted to according to their original names.
First we have to decompress the archive, for that it is read by a GZipStream.
The content of this is then copied to a MemoryStream which writes its content to a byte array (this is easier from a MemoryStream, therefor this way around).
The byte array is then converted with an instance of the class ASCIIEncoder to a string. The evaluation of this is done by a loop, in every iteration one file is treated. There are 2 pointers, which point to a position in the string. The first saves the current position, the second the current searching position.
The first always points to the position, on which the current header starts, the second points to a position 22 bytes further. Since the structure of the header is known, file size and name can be readout by starting a search from the searching position for "||" and then incrementing the positions.
With a FilesStream then the parts of the byte array containing the current file (so from the current position to the current position + file size) are written to a new file, which is created in the target directory under the original name.
The code:
        private void OpenArchive(string archiv, string decompressPath)
        {
            GZipStream DecompressStream = new GZipStream(new FileStream(archiv, FileMode.Open), CompressionMode.Decompress);
            FileStream NormalFileStream;
            MemoryStream TempStream = new MemoryStream();

            ASCIIEncoding decoder = new ASCIIEncoding();
            ASCIIEncoding Encoder = new ASCIIEncoding();

            string StringFromBytes; // string representation of the read bytes
            int EndSize; // position in the header, where the field file size ends
            long FileLength; // size of the current file
            int StartFileName; // position in the header, where the field file name starts
            int EndFileName; // position in the header, where the field file name ends
            string FileName; // name of the current file
            string EmptyHeader = "|*START*OF*HEADER*|*||||*|*END*OF*HEADER*|"// "prototype" of the header
            byte[] EmptyHeaderBytes = Encoder.GetBytes(EmptyHeader); // prototype in bytes

            long CurrentPosition = 0; // current position in the file
            long CurrentSearchPosition = 22; // current searching position in the file

            DecompressStream.CopyTo(TempStream);
            byte[] BigFileContent = new byte[TempStream.Length];
            TempStream.Position = 0;
            TempStream.Read(BigFileContent, 0, BigFileContent.Length);
       
            StringFromBytes = decoder.GetString(BigFileContent);

            while (true)
            {
                EndSize = StringFromBytes.IndexOf("||", (int)CurrentSearchPosition);
                FileLength = long.Parse(StringFromBytes.Substring((int)CurrentSearchPosition, EndSize - (int)CurrentSearchPosition)); // the file size is written in the bytes from position 22 in the header to EndSize

                StartFileName = EndSize + 2;
                EndFileName = StringFromBytes.IndexOf("*|*", StartFileName);
                FileName = StringFromBytes.Substring(StartFileName, EndFileName - StartFileName); // readout file name
             
                CurrentPosition += EmptyHeaderBytes.Length + Encoder.GetBytes(FileLength.ToString()).Length + Encoder.GetBytes(FileName).Length;
               
                NormalFileStream = new FileStream(decompressPath + "\\" + FileName, FileMode.Create);
                NormalFileStream.Write(BigFileContent, (int)CurrentPosition, (int)FileLength);

                CurrentPosition += FileLength;
                CurrentSearchPosition = CurrentPosition + 22;
                NormalFileStream.Close();

                if (CurrentSearchPosition > BigFileContent.Length)
                    break;
            }

            DecompressStream.Close();
        }

Monday, October 18, 2010

Compression With C#, Part 2 - Files

Today I want to continue the tutorial regarding compression methods in C# from the previous post.
While in the previous post I explained the bascis of compressing / decompressing simple strings, I show today an advanced use to compress / decompress whole files.

Compressing:
As in the compression of strings we create a new instance of the class GZipStream and set in its constructor a FileStream pointing to the target file.
Furthermore we create another FileStream pointing to the source file. With it we now read the content of the old file to a byte array.
If we write this then with the function Write() of the GZipstream, this writes the compressed content of the source file to the target file - we have compressed the file, just like WinZip or a similiar program would do.
The corresponding code:

        private void CompressFile(string normalFile, string compressedFile)
        {
            GZipStream CompressStream = new GZipStream(new FileStream(compressedFile, FileMode.Create), CompressionMode.Compress);
           
            FileStream NormalFileStream = new FileStream(normalFile, FileMode.Open);
            byte[] Content = new byte[NormalFileStream.Length];
            NormalFileStream.Read(Content, 0, Content.Length);
            NormalFileStream.Close();

            CompressStream.Write(Content, 0, Content.Length);
            CompressStream.Close();
        }

Now to the decompression:
Therefor we hand a FileStream over to the GZipStream, which points to the compressed file which is to be readout.
Furthermore a new FileStream is created, which points to the decompressed file which is to be written.
As also in the previous post we use a loop to read the compressed file.
Therefor we use the function Read() of the GZipStream, which reads the designated number of bytes and then decompresses them. The result is saved in a buffer, which is then written via a FileStream to the new file.
If the end of the file is reached, the function Read() reads less bytes than fit in the buffer and the program notices the end.
The compressed file was now decompressed and written to a readable file again.
The corresponding code is:

        private void DecompressFile(string compressedFile, string normalFile)
        {
            GZipStream DecompressStream = new GZipStream(new FileStream(compressedFile, FileMode.Open), CompressionMode.Decompress);

            FileStream NormalFileStream = new FileStream(normalFile, FileMode.Create);

            int BytesReadCount = 0;
            byte[] Buffer = new byte[4096];

            while (true)
            {
                BytesReadCount = DecompressStream.Read(Buffer, 0, Buffer.Length);
                if (BytesReadCount != 0)
                {
                    NormalFileStream.Write(Buffer, 0, BytesReadCount);
                }
                if (BytesReadCount < Buffer.Length)
                    break;
            }

            NormalFileStream.Close();
            DecompressStream.Close();

        }

Sunday, October 17, 2010

Webcam Chat

Due to a reader's request I recently took some time to realize a bit bigger project, the development of a webcam chat implemented in C#.
I want to present this in this post, for understanding it some previous knowledge is needed:
- The code to use a webcam in C# I took with allowance from net-blog.
- The connection between the partners is set up via TCP / IP, I posted about that before here.
- Many want to probably connect with friends over the internet, how the connection via that works and which IP addresses are to be used, you can find here.
- How an image is "streamed" is explained in this post.
- And here finally general information on the error "A general error occured in GDI+", which occurs sporadically at graphical applications.

This is the user interface of the program:


In the left PictureBox the image of the own webcam is displayed, in the right that of the partner.
Of course the chat partner has to run the same program.
To its functionality:
With a click on the button "Connect" 2 threads are started, one of them to send the own image, the other one to receive the image of the partner.
The send thread runs on the in "eigener Port" inputted port, the receive thread on the port inputted in "Partner Port".
To connect logically both partners have to enter the IP address of the other one and matching parts, so partner A port x for "eigener Port" and Port y for "Partner Port", partner B has to input this reversed.
In the send thread a server is created, to which the client from the receive thread connects to.
So in every instance of the program there are 2 server - client connections, which communicate over their own network stream.
In the send thread an infinite loop is running, which tries to send the image of the own PictureBox via the function WriteImage().
In this the image is first written with a temporary stream to a byte array. Then the size of this array is written to a 20 character string, the remaining characters are filled up with "x".
The size and content of the image are then send over the network stream.
In the receive thread an infinite loop is running, trying to readout that stream via the function ReadImage().
First here in every iteration the first 20 bytes are read, which determine the size of the image.
If the stream can be converted to some valid number g, the next g bytes from the network stream are read and the image is reproduced by that.
If the 20 bytes cannot be converted, there was a transmission error. In order for the server and client to synchronise again, the stream has to be deleted again. For this simply bytes are readout, until the stream is empty.
In general I must say the implementation of the data transfer was pretty hard, server and client got out of sync a lot and many transmission errors occured.
In the version published here when sending and receiving images succesfully, these images are saved in variables. If an error occurs, the saved image is reused.

Okay, that should suffice with the description, as I said, when reading the above mentioned tutorials the single program parts should be good to understand.
I know, this version is just a start and was just designed for sample purposes and could be improved in any way.
For hints and advice I am always thankful.
Have fun with the program!

A setup to intall the webcam chat is available over this link.
The complete source code can be found here.
Now the source code, first the content of the file Form1.cs and then the content of the file Form1.Designer.cs, if you also copy this one in your project, you get exactly the same user interface as me.

Form1.cs:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

// for Webcam
using AForge.Video;
using AForge.Video.DirectShow;

// for Netzwerk
using System.IO;
using System.Net.Sockets;
using System.Net;

using System.Threading;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        #region Webcam

        // our webcam object
        VideoCaptureDevice videoSource;

        void InitWebCam(int nr)
        {
            // enumeration of all webcams / video devices
            FilterInfoCollection videosources = new FilterInfoCollection(FilterCategory.VideoInputDevice);

            // check if at least one webcam was found
            if (videosources != null)
            {
                // bind the webcam "nr" to out project
                videoSource = new VideoCaptureDevice(videosources[nr].MonikerString);

                try
                {
                    // check if webcam has technical abilites
                    if (videoSource.VideoCapabilities.Length > 0)
                    {
                        string lowestSolution = "10000;0";
                        // look for the profile with the lowest resolution
                        for (int i = 0; i < videoSource.VideoCapabilities.Length; i++)
                        {
                            if (videoSource.VideoCapabilities[i].FrameSize.Width < Convert.ToInt32(lowestSolution.Split(';')[0]))
                                lowestSolution = videoSource.VideoCapabilities[i].FrameSize.Width.ToString() + ";" + i.ToString();
                        }
                        // pass the webcam object the lowest resolution
                        videoSource.DesiredFrameSize = videoSource.VideoCapabilities[Convert.ToInt32(lowestSolution.Split(';')[1])].FrameSize;
                    }
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.ToString());
                }

                // Assign the webcam object the event hanlder NewFrame
                // this event is triggered for every new image
                videoSource.NewFrame += new AForge.Video.NewFrameEventHandler(videoSource_NewFrame);

                // activate the webcam
                videoSource.Start();
            }
        }

        void videoSource_NewFrame(object sender, AForge.Video.NewFrameEventArgs eventArgs)
        {
            // assign every incoming image to the picture box
            pictureBoxVideoSelf.BackgroundImage = (Image)eventArgs.Frame.Clone();
        }

        #endregion

        Image LastImageSent = null// last correctly send image
        Image LastImageReceived = null// last correctly received image

        Thread Sender; // thread for sending images
        Thread Receiver; // thread for receiving images

        bool Closing = false// true if form is to be quit
        String ClosingString = "FORM#CLOSING"// message, which is sent when closing
        byte[] ClosingBytes; // byte coding of this message
        ASCIIEncoding ByteConverter = new ASCIIEncoding(); // object to convert strings to byte arrays and other way

        private void button1_Click(object sender, EventArgs e)
        {
            // start thread for sending the own image
            Sender = new Thread(new ParameterizedThreadStart(this.Send));
            Sender.Start(int.Parse(textBox4.Text));

            // start thread for receiving the partner image
            Receiver = new Thread(new ParameterizedThreadStart(Receive));
            Receiver.Start(textBox2.Text + "-" + textBox1.Text);

            ClosingBytes = ByteConverter.GetBytes(ClosingString);
        }

        private void Send(object port)
        {
            InitWebCam(int.Parse(textBox3.Text)); // start webcam

            TcpListener Server = new TcpListener(int.Parse(port.ToString()));
            Server.Start();

            TcpClient Client = Server.AcceptTcpClient();

            NetworkStream ClientStream = Client.GetStream();

            while (true)
            {
                if (Closing)
                    break// quit
                try
                {
                    // try to send the image to the partner, then save it as backup
                    WriteImage((Image)pictureBoxVideoSelf.BackgroundImage.Clone(), ClientStream);
                    LastImageSent = (Image)pictureBoxVideoSelf.BackgroundImage.Clone();
                    Thread.Sleep(100);
                }
                catch
                {   // if the current image could not be send, take the backup
                    WriteImage(LastImageSent, ClientStream);
                }
            }

            try
            {
                ClientStream.Write(ClosingBytes, 0, ClosingBytes.Length);
            }
            catch { };
        }

        private void Receive(object portip)
        {
            // portip has the form "port-ip"
            string[] Parameter = portip.ToString().Split('-');
            System.Net.IPAddress IP = System.Net.IPAddress.Parse(Parameter[1]);

            TcpClient Exchange = new TcpClient();
            NetworkStream ExchangeStream = null;

            Image TempImage;

            // try to setup a connection every 3 seconds
            while (true)
            {
                try
                {
                    Exchange.Connect(IP, int.Parse(Parameter[0]));
                    ExchangeStream = Exchange.GetStream();
                    break;
                }
                catch
                {
                    Thread.Sleep(3000);
                }
            }

            while (true)
            {
                if (Closing)
                    break// quit

                try
                {
                    // try to display the received image
                    // in case of success
                    TempImage = ReadImage(ExchangeStream);
                    if (TempImage == null)
                        throw new Exception();

                    pictureBoxVideoPartner.BackgroundImage = TempImage;
                    LastImageReceived = (Image)pictureBoxVideoPartner.BackgroundImage.Clone();
                    Thread.Sleep(100);
                }
                catch
                {
                    try
                    {   // in case of error use backup image
                        pictureBoxVideoPartner.BackgroundImage = LastImageReceived;
                    }
                    catch { }
                }
            }
        }

        private void WriteImage(Image image, NetworkStream stream)
        {
            ASCIIEncoding Encoder = new ASCIIEncoding();
            MemoryStream TempStream = new MemoryStream();
            byte[] Buffer;

            try
            {
                // write the handed over image to the stream
                image.Save(TempStream, System.Drawing.Imaging.ImageFormat.Gif);
            }
            catch
            {
            }

            Buffer = TempStream.ToArray();

            // encode the size of the image as a 20 character string, fill up with x
            string ImageSize = Buffer.Length.ToString();
            while (ImageSize.Length < 20)
                ImageSize += "x";

            // write its size plus content to an array
            byte[] FittedImageSize = Encoder.GetBytes(ImageSize);
            byte[] ImagePlusSize = new byte[FittedImageSize.Length + Buffer.Length];
            Array.Copy(FittedImageSize, ImagePlusSize, FittedImageSize.Length);
            Array.Copy(Buffer, 0, ImagePlusSize, FittedImageSize.Length, Buffer.Length);

            try
            {
                // write the array
                stream.Write(ImagePlusSize, 0, ImagePlusSize.Length);
                stream.Flush();
            }
            catch
            {
                // if the stream cannot be written anymore, the partner has quit
                Closing = true;
            }
        }

        private Image ReadImage(NetworkStream stream)
        {
            Image Result;
            int BytesRead;

            // read the first 20 bytes of the stream, since they encode the size of the image
            byte[] ImageSize = new byte[20];
            BytesRead = stream.Read(ImageSize, 0, 20);

            /* if only 12 bytes can be read and they contain the closing string, a termination is requested */
            if (BytesRead == 12)
            {
                if (ByteConverter.GetString(ImageSize, 0, 12) == "FORM#CLOSING")
                {
                    Closing = true;
                    return null;
                }
            }

            byte[] ErrorBuffer = new byte[100000000];

            ASCIIEncoding Decoder = new ASCIIEncoding();
            string ImageSizeString = Decoder.GetString(ImageSize).Replace("x""");

            int TestSize;

            if (!int.TryParse(ImageSizeString, out TestSize))
            {
                stream.Read(ErrorBuffer, 0, ErrorBuffer.Length);
                return null;
            }

            byte[] ImageFile = new byte[int.Parse(ImageSizeString)];

            stream.Read(ImageFile, 0, ImageFile.Length);

            MemoryStream temps = new MemoryStream();

            try
            {
                temps.Write(ImageFile, 0, ImageFile.Length);
                Result = Image.FromStream(temps);
                return Result;
            }
            catch
            {
                return null;
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            // dispose webcam at closing
            if (videoSource != null && videoSource.IsRunning)
            {
                videoSource.SignalToStop();
                videoSource = null;
            }

            Closing = true;

            Thread.Sleep(3000);

            if (Sender != null && Sender.IsAlive)
                Sender.Abort();

            if (Receiver != null && Receiver.IsAlive)
                Receiver.Abort();
        }
    }
}


Form1.Designer.cs:

namespace WindowsFormsApplication1
{
    partial class Form1
    {
        /// <summary>
       /// Erforderliche Designervariable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
       /// Verwendete Ressourcen bereinigen.
        /// </summary>
        /// <param name="disposing">True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Vom Windows Form-Designer generierter Code

        /// <summary>
       /// Erforderliche Methode für die Designerunterstützung.
       /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
        /// </summary>
        private void InitializeComponent()
        {
            this.pictureBoxVideoSelf = new System.Windows.Forms.PictureBox();
            this.button1 = new System.Windows.Forms.Button();
            this.pictureBoxVideoPartner = new System.Windows.Forms.PictureBox();
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.label3 = new System.Windows.Forms.Label();
            this.label4 = new System.Windows.Forms.Label();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.textBox2 = new System.Windows.Forms.TextBox();
            this.textBox3 = new System.Windows.Forms.TextBox();
            this.textBox4 = new System.Windows.Forms.TextBox();
            this.label5 = new System.Windows.Forms.Label();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBoxVideoSelf)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBoxVideoPartner)).BeginInit();
            this.SuspendLayout();
            //
            // pictureBoxVideoSelf
            //
            this.pictureBoxVideoSelf.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
            this.pictureBoxVideoSelf.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.pictureBoxVideoSelf.Location = new System.Drawing.Point(47, 26);
            this.pictureBoxVideoSelf.Name = "pictureBoxVideoSelf";
            this.pictureBoxVideoSelf.Size = new System.Drawing.Size(331, 210);
            this.pictureBoxVideoSelf.TabIndex = 0;
            this.pictureBoxVideoSelf.TabStop = false;
            //
            // button1
            //
            this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.button1.Location = new System.Drawing.Point(326, 263);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(154, 46);
            this.button1.TabIndex = 1;
            this.button1.Text = "Verbinden";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            //
            // pictureBoxVideoPartner
            //
            this.pictureBoxVideoPartner.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
            this.pictureBoxVideoPartner.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.pictureBoxVideoPartner.Location = new System.Drawing.Point(398, 26);
            this.pictureBoxVideoPartner.Name = "pictureBoxVideoPartner";
            this.pictureBoxVideoPartner.Size = new System.Drawing.Size(319, 210);
            this.pictureBoxVideoPartner.TabIndex = 3;
            this.pictureBoxVideoPartner.TabStop = false;
            //
            // label1
            //
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(25, 281);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(0, 13);
            this.label1.TabIndex = 4;
            //
            // label2
            //
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(47, 249);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(54, 13);
            this.label2.TabIndex = 5;
            this.label2.Text = "Partner IP";
            //
            // label3
            //
            this.label3.AutoSize = true;
            this.label3.Location = new System.Drawing.Point(47, 273);
            this.label3.Name = "label3";
            this.label3.Size = new System.Drawing.Size(63, 13);
            this.label3.TabIndex = 6;
            this.label3.Text = "Partner Port";
            //
            // label4
            //
            this.label4.AutoSize = true;
            this.label4.Location = new System.Drawing.Point(47, 321);
            this.label4.Name = "label4";
            this.label4.Size = new System.Drawing.Size(73, 13);
            this.label4.TabIndex = 7;
            this.label4.Text = "Webcam - Nr.";
            //
            // textBox1
            //
            this.textBox1.Location = new System.Drawing.Point(128, 249);
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(158, 20);
            this.textBox1.TabIndex = 8;
            //
            // textBox2
            //
            this.textBox2.Location = new System.Drawing.Point(128, 273);
            this.textBox2.Name = "textBox2";
            this.textBox2.Size = new System.Drawing.Size(158, 20);
            this.textBox2.TabIndex = 9;
            //
            // textBox3
            //
            this.textBox3.Location = new System.Drawing.Point(128, 321);
            this.textBox3.Name = "textBox3";
            this.textBox3.Size = new System.Drawing.Size(158, 20);
            this.textBox3.TabIndex = 10;
            this.textBox3.Text = "0";
            //
            // textBox4
            //
            this.textBox4.Location = new System.Drawing.Point(128, 297);
            this.textBox4.Name = "textBox4";
            this.textBox4.Size = new System.Drawing.Size(158, 20);
            this.textBox4.TabIndex = 10;
            //
            // label5
            //
            this.label5.AutoSize = true;
            this.label5.Location = new System.Drawing.Point(47, 297);
            this.label5.Name = "label5";
            this.label5.Size = new System.Drawing.Size(64, 13);
            this.label5.TabIndex = 11;
            this.label5.Text = "eigener Port";
            //
            // Form1
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(758, 370);
            this.Controls.Add(this.textBox4);
            this.Controls.Add(this.label5);
            this.Controls.Add(this.textBox3);
            this.Controls.Add(this.textBox2);
            this.Controls.Add(this.textBox1);
            this.Controls.Add(this.label4);
            this.Controls.Add(this.label3);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.pictureBoxVideoPartner);
            this.Controls.Add(this.button1);
            this.Controls.Add(this.pictureBoxVideoSelf);
            this.Name = "Form1";
            this.Text = "C# Webcam Chat";
            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);
            ((System.ComponentModel.ISupportInitialize)(this.pictureBoxVideoSelf)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBoxVideoPartner)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.PictureBox pictureBoxVideoSelf;
        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.PictureBox pictureBoxVideoPartner;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.Label label3;
        private System.Windows.Forms.Label label4;
        private System.Windows.Forms.TextBox textBox1;
        private System.Windows.Forms.TextBox textBox2;
        private System.Windows.Forms.TextBox textBox3;
        private System.Windows.Forms.TextBox textBox4;
        private System.Windows.Forms.Label label5;
    }
}