C# — Load a TreeView from JSON, Save a TreeView to JSON

Here are a few snippets of code I needed for internal tools…

TreeView –> JSON

private void recurseTree(TreeNode treeNode, Chilkat.JsonObject json)
    {
    string tag;
    if (treeNode.Tag == null)
        {
        tag = treeNode.Name;
        }
    else
        {
        tag = treeNode.Tag.ToString();
        if (tag.Length == 0)
            {
            tag = treeNode.Name;
            }
        }

    int numChildren = treeNode.Nodes.Count;
    if (numChildren == 0)
        {
        json.AppendString(tag, treeNode.Text);
        return;
        }

    Chilkat.JsonObject jObj = json.AppendObject(tag);

    foreach (TreeNode tn in treeNode.Nodes)
        {
        recurseTree(tn,jObj);
        }
    }

private void btnTreeViewToJson_Click(object sender, EventArgs e)
    {
    // Convert treeview1 to JSON.
    Chilkat.JsonObject json = new Chilkat.JsonObject();

    TreeNodeCollection nodes = treeView1.Nodes;
    foreach (TreeNode n in nodes)
        {
        recurseTree(n,json);
        }

    json.EmitCompact = false;
    textBox1.Text = json.Emit();
    }

JSON –> TreeView

void buildTreeView(TreeNode node, Chilkat.JsonObject json)
    {
    int i;
    int n = json.Size;
    for (i = 0; i < n; i++)
        {
        if (json.TypeAt(i) == 3)
            {
            TreeNode tn = new TreeNode();
            
            tn.Tag = json.NameAt(i);
            tn.Name = json.NameAt(i);
            tn.Text = json.NameAt(i);
            node.Nodes.Add(tn);

            Chilkat.JsonObject jObj = json.ObjectAt(i);
            buildTreeView(tn, jObj);
            }
        else
            {
            // This is a string.
            TreeNode tn = new TreeNode();
            tn.Tag = json.NameAt(i);
            tn.Name = json.NameAt(i);
            tn.Text = json.StringAt(i);
            node.Nodes.Add(tn);
            }
        }
    }

private void loadTreeView()
    {
    treeView1.BeginUpdate();

    Chilkat.JsonObject json = new Chilkat.JsonObject();

    json.LoadFile("c:/ck2000/appData/builder/treeView.json");

    // recursively add the JSON nodes to the treeview.
    TreeNode tn = new TreeNode();
    tn.Tag = json.NameAt(0);
    tn.Name = json.NameAt(0);
    tn.Text = json.NameAt(0);
    treeView1.Nodes.Add(tn);

    Chilkat.JsonObject jObj = json.ObjectAt(0);
    buildTreeView(tn, jObj);

    treeView1.EndUpdate();
    }

treeView1.Nodes.Clear();
loadTreeView();
...

Never Try to Handle Binary Data as a String

This issue comes up frequently, and hopefully this C# and VB.NET example will help people to understand what not to do..

Here’s the C# example (the VB.NET example is further below..)


// Never try to store non-text binary data as a string.
// This applies to all programming languages where the string data type is
// an object, such as C#, VB.NET, Java, VB6, FoxPro, etc.
//
// If the semantics of the programming language are such that a "string"
// is just a sequence of bytes terminated by a 0 byte, such as in C/C++,
// there are still problems because the 1st 0 byte in the non-text data (such as JPG, or PDF)
// would terminate the "string".

// Both C# and VB.NET are languages where strings are objects.  If you wish to
// set the contents of a C# or VB.NET string from a byte array, you MUST tell .NET
// the character encoding of the byte array -- otherwise it does not know how
// to interpret the bytes.  (For example, the bytes might be utf-8, iso-8859-1, Shift_JIS, etc.)
// The bytes don't actually represent characters (such as image data, or a zip archive), then
// it makes no sense to be trying to convert the bytes into "chars" because there will be innumerable
// sequences of bytes that don't represent any possible char in the charset encoding.

// For example, this is OK:

byte[] utf8Bytes = System.IO.File.ReadAllBytes("utf8_sampler.htm");
byte[] jpgBytes = System.IO.File.ReadAllBytes("starfish.jpg");

textBox1.Text = "num utf8 bytes = " + utf8Bytes.Length.ToString() + "\r\n";
textBox1.Text += "num JPG bytes = " + jpgBytes.Length.ToString() + "\r\n";

// Interpret the bytes according to the utf-8 encoding and return the string object:
// The number of chars in the string may be different than the number of bytes if there
// were chars with multi-byte utf-8 representations.
string s1 = Encoding.UTF8.GetString(utf8Bytes);
textBox1.Text += "num chars = " + s1.Length + "\r\n";

// This is garbage, because the JPG bytes don't represent chars in the utf-8 encoding.
string s2 = Encoding.UTF8.GetString(jpgBytes);
textBox1.Text += "num chars = " + s2.Length + "\r\n";

// Go back to utf-8 bytes:
byte[] utf8Bytes2 = Encoding.UTF8.GetBytes(s1);
byte[] jpgBytes2 = Encoding.UTF8.GetBytes(s2);

textBox1.Text += "num utf8 bytes 2 = " + utf8Bytes2.Length.ToString() + "\r\n";
textBox1.Text += "num JPG bytes 2 = " + jpgBytes2.Length.ToString() + "\r\n";

// Here's the output of this program:
//num utf8 bytes = 62417
//num JPG bytes = 6229
//num chars = 55731
//num chars = 5962
//num utf8 bytes 2 = 62417
//num JPG bytes 2 = 10710

VB.NET Example:

' Never try to store non-text binary data as a string.
' This applies to all programming languages where the string data type is
' an object, such as C#, VB.NET, Java, VB6, FoxPro, etc.
'
' If the semantics of the programming language are such that a "string"
' is just a sequence of bytes terminated by a 0 byte, such as in C/C++,
' there are still problems because the 1st 0 byte in the non-text data (such as JPG, or PDF)
' would terminate the "string".

' But C# and VB.NET are languages where strings are objects.  If you wish to 
' set the contents of a C# or VB.NET string from a byte array, you MUST tell .NET
' the character encoding of the byte array -- otherwise it does not know how
' to interpret the bytes.  (For example, the bytes might be utf-8, iso-8859-1, Shift_JIS, etc.)
' The bytes don't actually represent characters (such as image data, or a zip archive), then
' it makes no sense to be trying to convert the bytes into "chars" because there will be innumerable
' sequences of bytes that don't represent any possible char in the charset encoding.

' For example, this is OK:

Dim utf8Bytes As Byte() = System.IO.File.ReadAllBytes("utf8_sampler.htm")
Dim jpgBytes As Byte() = System.IO.File.ReadAllBytes("starfish.jpg")

textBox1.Text = "num utf8 bytes = " + utf8Bytes.Length.ToString() + vbCr & vbLf
textBox1.Text += "num JPG bytes = " + jpgBytes.Length.ToString() + vbCr & vbLf

' Interpret the bytes according to the utf-8 encoding and return the string object:
' The number of chars in the string may be different than the number of bytes if there
' were chars with multi-byte utf-8 representations.
Dim s1 As String = Encoding.UTF8.GetString(utf8Bytes)
textBox1.Text += "num chars = " + s1.Length + vbCr & vbLf

' This is garbage, because the JPG bytes don't represent chars in the utf-8 encoding.
Dim s2 As String = Encoding.UTF8.GetString(jpgBytes)
textBox1.Text += "num chars = " + s2.Length + vbCr & vbLf

' Go back to utf-8 bytes:
Dim utf8Bytes2 As Byte() = Encoding.UTF8.GetBytes(s1)
Dim jpgBytes2 As Byte() = Encoding.UTF8.GetBytes(s2)

textBox1.Text += "num utf8 bytes 2 = " + utf8Bytes2.Length.ToString() + vbCr & vbLf
textBox1.Text += "num JPG bytes 2 = " + jpgBytes2.Length.ToString() + vbCr & vbLf

' Here's the output of this program:
'num utf8 bytes = 62417
'num JPG bytes = 6229
'num chars = 55731
'num chars = 5962
'num utf8 bytes 2 = 62417
'num JPG bytes 2 = 10710

C# Streaming Decompression Example (from System.IO.Stream –> System.IO.Stream)

// Streaming decompression from System.IO.Stream --> System.IO.Stream

// In this example, the source and sink streams are files.
// However, they can be any type of System.IO.Stream, such as a System.Net.Sockets.NetworkStream
System.IO.FileStream fsSource = File.OpenRead("qa_data/image.compressed");
Chilkat.StreamConnector scSource = new Chilkat.StreamConnector();

System.IO.FileStream fsSink = File.OpenWrite("qa_data/image.bmp");
Chilkat.StreamConnector scSink = new Chilkat.StreamConnector();

Chilkat.Compression compress = new Chilkat.Compression();
compress.Algorithm = "deflate";

// Create a Chilkat.Stream object, and set the source/sink to the System.IO.Stream objects.
Chilkat.Stream stream = new Chilkat.Stream();
scSource.SetAsSource(fsSource, stream);
scSink.SetAsSink(fsSink, stream);

// Do the streaming decompress -- reading from fsSource and writing to fsSink
bool success = compress.DecompressStream(stream);

C# SFTP Upload from Byte[]

This example demonstrates how to open a remote file on an SSH/SFTP server,
write to the file, and then close it.  This is analogous to opening a local file,
writing to it, and closing it.  The SFTP protocol (i.e. Secure File Transfer over SSH),
follows the same concepts as typical file I/O programming -- i.e. open a file,
read, write, close, etc.).

// Assume at this point we already have a Chilkat.SFtp object
// connected to a server, authenticated, and InitializeSftp has
// already been called..


// To upload a binary file from Byte[], the procedure is to open the remote file,
// write the data, and then close the file.

// Open a remote file, returning a handle to the open file.
string handle = sftp.OpenFile("test.dat", "readWrite", "createTruncate");
if (!sftp.LastMethodSuccess)
    {
    textBox2.Text = sftp.LastErrorText;
    return;
    }

string text = "To live is the rarest thing in the world. Most people exist, that is all. -- Oscar Wilde\r\n";
byte[] bytes= System.Text.Encoding.UTF8.GetBytes(text);

// Write some data to the file.
bool success;
for (int i = 0; i < 20; i++)
    {
    // It is possible to write bytes:
    success = sftp.WriteFileBytes(handle, bytes);
    // It is also possible to write the string:
    if (success) success = sftp.WriteFileText(handle, "utf-8", text);

    if (!success)
        {
        textBox2.Text = sftp.LastErrorText;
        return;
        }
    }

// Close the remote file.
if (!sftp.CloseHandle(handle))
    {
    textBox2.Text = sftp.LastErrorText;
    return;
    }
 

C# TaskCompleted Event — Updating the UI from a Background Thread

This sample C# snippet demonstrates how to use the TaskCompleted event to be notified when an asynchronous method completes. Given that the event callback is in the background thread, any UI updates must occur on the main thread. This example demonstrates how to do it:

	private void taskCompletedToolStripMenuItem_Click(object sender, EventArgs e)
	    {
	    // Demonstrate an asynchronous Chilkat method call with a TaskCompleted event.
	    Chilkat.Http http = new Chilkat.Http();

	    http.OnTaskCompleted += http_OnTaskCompleted;

	    Chilkat.Task task = http.QuickGetStrAsync("http://www.chilkatsoft.com/");

	    // We can set the task.UserData property to identify this particular asynchronous call in the callback.
	    task.UserData = "chilkatHomePage";

	    // Runs the HTTP GET asynchronously on a background thread..
	    task.Run();
	    }

	// This event fires in the background thread. 
	// 
	void http_OnTaskCompleted(object sender, Chilkat.TaskCompletedEventArgs args)
	    {
	    Chilkat.Task task = args.Task;

	    // This event callback is running in the background thread.
	    // To update a UI element, we must be on the UI thread..
	    this.Invoke((MethodInvoker)delegate
		{
		    // The task.UserData can be used to identify the particular asynchronous method call
		    // that this callback belongs to.
		    if (task.UserData.Equals("chilkatHomePage"))
			{
			// An asychronous method is simply calling the corresponding synchronous method
			// in a background thread.  In this case, it is a call to QuickGetStr,
			// which returned a string, or it returned Nothing for failure.  This result is available
			// via task.GetResultString.  The LastErrorText for the background call 
			// is available in task.ResultErrorText
			string htmlPage = task.GetResultString();
			if (htmlPage == null)
			    {
			    textBox2.Text = task.ResultErrorText;
			    }
			else
			    {
			    textBox2.Text = htmlPage;
			    }
			}
		});
	    }
	}

Is Calling .Dispose() Recommended for C# and VB.NET?

Question:

In your examples online, we have noticed that .Dispose() is never
called after using Chilkat classes (we are writing in C#).

Is calling .Dispose() recommended?  Does your code not need to destroy
any native resources or handles that would normally be cleaned up in
the Dispose method (the IDisposeable interface) ?

Answer:

For objects that manage a TCP/IP socket connection with a server (FTP2, SSH, SFTP, POP3, SMTP, IMAP, HTTP, etc.) then calling Dispose will close the connection.  Aside from memory-usage and socket connections, there are no other resources used by Chilkat classes.  Dispose may also deallocate some internal memory.

The important thing to realize is that once Dispose is called, your code should not use that particular instance of the object again.  I always prefer a more explicit approach.  For example, instead of calling Chilkat.MailMan.Dispose to ensure that a connection to the SMTP server is closed, call Chilkat.MailMan.CloseSmtpConnection.  (If the mailman is not actually connected, calling CloseSmtpConnection does NOT result in an error.  It is harmless.)

Also, when an Chilkat object’s finalizer is called (i.e. when it is garbage-collected), any existing socket connections are closed and internal memory is (of course) deallocated.   You may force garbage collection by doing this (in C#)

 GC.Collect();
 GC.WaitForPendingFinalizers();

BASE64 Decode with Charset GB2312

Question:
I have a Base64 decode error, as follows:

CkString str;
str.setString("16q");
str.base64Decode("gb2312");
const char *strResult = str.getString();

convert result is { cb f2 }
But the correct result should be { d7 aa}

What’s wrong?

The platform is WinCE 6.0, use Chilkat_PPC_M5.lib

Answer:

The following code shows how to do it correctly:

    CkString str;
    str.setString("16q");

    // The following line of code tells the CkString object to 
    // decode the base64 to raw bytes, then interpret those bytes as
    // GB2312 encoded characters and store them within the string.
    // Internally, the string is stored as utf-8.
    str.base64Decode("gb2312");

    // This is an implicit conversion to ANSI, because
    // getString returns either ANSI or utf-8,
    // depending on the setting of get_Utf8/put_Utf8
    const char *strAnsi = str.getString();

    // Instead, fetch the string as GB2312 bytes:
    const char *strGb2312 = str.getEnc("gb2312");

    const unsigned char *c = (const unsigned char *) strGb2312;
    while (*c != '\0') {  printf("%02x ",*c); c++; }
    printf("\n");

    // The output is "d7 aa "


    // Another way to decode using CkByteData...
    CkByteData data;
    data.appendEncoded("16q","base64");
    c = data.getData();
    unsigned long i;
    unsigned long sz = data.getSize();
    for (i=0; i<sz; i++) { printf("%02x ",*c); c++; }
    printf("\n");

    // The output is "d7 aa "

SFTP Progress Monitoring and Abort (C#)

Here is an example for monitoring the progress of an SFTP file transfer:

        void sftp_OnPercentDone(object sender, Chilkat.PercentDoneEventArgs args)
        {
            progressBar1.Value = args.PercentDone;

            // To abort at any point, you may set args.Abort = true  
            // args.Abort = true;
        }

        void sftp_OnAbortCheck(object sender, Chilkat.AbortCheckEventArgs args)
        {
            // See http://cknotes.com/?p=149  for more information about 
            // using the AbortCheck event...
           
        }

private void button2_Click(object sender, EventArgs e)
{
	// SFTP upload with progress monitoring.
	Chilkat.SFtp sftp = new Chilkat.SFtp();

	// IMPORTANT: Enable event callbacks:
	sftp.EnableEvents = true;

	// Unlock the component.
	bool success = sftp.UnlockComponent("Anything for 30-day trial");
	if (!success)
	{
	textBox1.Text = sftp.LastErrorText;
	return;
	}

	// Connect to the SSH/SFTP server. (use a domain name or IP address)
	success = sftp.Connect("192.168.1.117", 22);
	if (!success)
	{
	textBox1.Text = sftp.LastErrorText;
	return;
	}

	// Authenticate..
	success = sftp.AuthenticatePw("chilkat", "myPassword");
	if (!success)
	{
	textBox1.Text = sftp.LastErrorText;
	return;
	}

	success = sftp.InitializeSftp();
	if (!success)
	{
	textBox1.Text = sftp.LastErrorText;
	return;
	}

	// Establish some event handlers:
	sftp.OnAbortCheck += new Chilkat.SFtp.AbortCheckEventHandler(sftp_OnAbortCheck);
	sftp.OnPercentDone += new Chilkat.SFtp.PercentDoneEventHandler(sftp_OnPercentDone);

	// See http://cknotes.com/?p=149  for more information about 
	// using the AbortCheck event...

	// Do an upload.
	// Note: If the file to be uploaded or downloaded is too small, 
	// the PercentDone callback will not be called for each 1%.
	// It's possible if the file is so small relative to the speed
	// of the connection that you may get a single 100% callback if
	// the data transfer happens almost instantaneously.
	string localFilepath = "c:/temp/hamlet.xml";
	string remoteFilepath = "hamlet.xml";
	progressBar1.Value = 0;
	success = sftp.UploadFileByName(remoteFilepath, localFilepath);
	if (!success)
	{
	textBox1.Text = sftp.LastErrorText;
	return;
	}
	MessageBox.Show("OK!");
}

C# Encrypting/Decrypting with Stream

This C# example demonstrates how to use Chilkat.Crypt2 to encrypt and decrypting using the .NET FileStream class:

    Chilkat.Crypt2 crypt = new Chilkat.Crypt2();
    bool success = crypt.UnlockComponent("Anything for 30-day trial");
    if (!success)
    {
        MessageBox.Show(crypt.LastErrorText);
        return;
    }

    crypt.CryptAlgorithm = "aes";
    crypt.CipherMode = "cbc";
    crypt.KeyLength = 128;

    crypt.SetEncodedIV("0000000000000000", "hex");
    crypt.SetEncodedKey("abcdefghijklmnop", "ascii");

    // Open input and output files as file streams...
    FileStream fsIn = File.OpenRead("hamlet.xml");
    FileStream fsOut = File.Create("encrypted.dat");

    crypt.FirstChunk = true;
    crypt.LastChunk = false;
    byte [] encryptedChunk;

    // Encrypt the stream in 1024 byte chunks.
    byte[] b = new byte[1024];

    int n;
    while ((n = fsIn.Read(b, 0, b.Length)) > 0)
    {
        if (n < b.Length)
        {
            // Don't encrypt the full 1024 bytes, only the amount read...
            byte[] tmp = new byte[n];
            int i;
            for (i = 0; i < n; i++) tmp[i] = b[i];
            encryptedChunk = crypt.EncryptBytes(tmp);
        }
        else
        {
            encryptedChunk = crypt.EncryptBytes(b);
        }

        fsOut.Write(encryptedChunk, 0, encryptedChunk.Length);
        crypt.FirstChunk = false;
    }
    fsIn.Close();

    // Flush any remaining encrypted data.
    crypt.LastChunk = true;
    byte[] empty = { };
    encryptedChunk = crypt.EncryptBytes(empty);
    if (encryptedChunk.Length > 0)
    {
        fsOut.Write(encryptedChunk, 0, encryptedChunk.Length);
    }
    fsOut.Close();


    // Now decrypt in chunks.  The decryptor must know when the last
    // block is being processed so it may unpad it correctly.

    System.IO.FileInfo fi = new System.IO.FileInfo("encrypted.dat");
    long fileSize = fi.Length;
    int numChunks = (int)fileSize / b.Length;

    fsIn = File.OpenRead("encrypted.dat");
    fsOut = File.Create("decrypted.xml");

    crypt.FirstChunk = true;
    crypt.LastChunk = false;
    byte[] decryptedChunk;

    int idx;
    for (idx = 0; idx <= numChunks; idx++)
    {
        n = fsIn.Read(b, 0, b.Length);

        // Is this the last chunk?
        if (idx == numChunks)
        {
            // Decrypt only the amount read...
            byte[] lastBlock = new byte[n];
            int i;
            for (i = 0; i < n; i++) lastBlock[i] = b[i];
            crypt.LastChunk = true;
            decryptedChunk = crypt.DecryptBytes(lastBlock);
        }
        else
        {
            decryptedChunk = crypt.DecryptBytes(b);
        }

        fsOut.Write(decryptedChunk, 0, decryptedChunk.Length);
        crypt.FirstChunk = false;
    }
    fsIn.Close();
    fsOut.Close();

    MessageBox.Show("Finished!");