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;
    }
}

Friday, October 15, 2010

Compression with C#, Part 1 - Simple Strings

The .Net studio provides a standard class for compressing and decompression data in the gzip format, System.IO.Compression.GZipStream.
gzip is a good, free available format, which - for people interested in theory - uses the Deflate algorithm, similiar to the ZIP format.
The class GZipStream is a stream, as regular stream with it files can be written and read, just that they are immediately compressed or decompressed.
As a first example I want to show an easy method to (de-)compress strings in (or from) a file.
For all following code examples the commands

using System.IO.Compression;
using System.IO;

are needed.

First to compression: The class GZipStream expects a stream in the constructor, which will be used for writing the compressed data to a file, as well as the compression mode (hier CompressionMode.Compress).
As stream we here use a FileStream, which points to the target file. The class GZipStream now can read and write bytes, therefor the string, which is to be compressed, has to be converted to a byte array, what is done by the class ASCIIEncoding.
The resulting array is then written with Write(), the GZipStream compresses the bytes and writes them via the FileStream to the file.
The code looks as follows:

private void CompressString(string uncompressedString)
{
    GZipStream CompressStream = new GZipStream(new FileStream(Application.StartupPath + "\\CompressedString.gz", FileMode.Create), CompressionMode.Compress);
    ASCIIEncoding Encoder = new ASCIIEncoding();
    byte[] UncompressedStringInBytes = Encoder.GetBytes(uncompressedString);
    CompressStream.Write(UncompressedStringInBytes, 0, UncompressedStringInBytes.Length);
    CompressStream.Close();
}

The string passed over to CompressString() is written to the file "CompressedString.gz" in the application folder, .gz is the file extension for gzip files.
The file can decompressed with common compress applications (e.g. WinZip), when opening the decompressed file a text editor one finds the original string.

Now to the reversed case, the decompression of strings from the file:
The class GZipStream now expects again a stream in the constructor, which this time denotes the stream, out of which the compressed data should be read, as well as the compression mode (hier CompressionMode.Decompress).
Again, we pass a FileStream over as stream. The reading of the file is done by a loop, in which every time a byte array is read to a buffer via the function Read() of the GZipStream. The read bytes are then converted to a string via the class ASCIIEncoding, if in one iteration less bytes are read than the buffer can take, the file is at its end and the loop terminates.
The code:

        private string DecompressString(string compressedFile)
        {
            GZipStream DecompressStream = new GZipStream(new FileStream(Application.StartupPath + "\\CompressedString.gz", FileMode.Open), CompressionMode.Decompress);
            byte[] Buffer = new byte[4096];
            ASCIIEncoding Decoder = new ASCIIEncoding();
            int BytesReadCount = 0;
            string DecompressedString = "";

            while (true)
            {
                BytesReadCount = DecompressStream.Read(Buffer, 0, Buffer.Length);
                if (BytesReadCount != 0)
                {
                    DecompressedString += Decoder.GetString(Buffer, 0, BytesReadCount);
                }
                if (BytesReadCount < Buffer.Length)
                    break;
            }

            DecompressStream.Close();
            return (DecompressedString);
        }

The call of this 2 methods could for example look like this:

CompressString("Dies ist ein Test 123456.");
string DecompressedString = DecompressString(Application.StartupPath + "\\CompressedString.gz");