Introduction
This program was developed to complement Tor on the Windows platform. Along the way, a few issues presented themselves. The solutions to those issues prompted writing this article for CodeProject.
Background
For those of you not familiar with Tor, visit its homepage. The concept behind Tor is "onion skin routing". The idea is that to protect anonymity, network traffic is encrypted then routed through a series of hosts. Each host only knows about the host it is receiving packets from and the host it is forwarding those packets on to. No records are kept and the path varies from connection to connection. The result is a network within the internet that offers almost perfectly anonymous access to any resource, and one that can traverse virtually all firewalls, routers, and web proxies, such as WebSense.
After using Tor for a while, we found the console window a bit annoying. There is no way to hide it. One of the installation options is to run Tor as a system service. Although this works, Tor can get itself into an odd state if the connection drops. The only way around this is to restart the service. Our solution was to write a program that sits in the system tray and launches Tor in a subprocess. The console window is completely hidden, yet all output from this subprocess is captured and displayed in the application's main form. The tray icon's context menu offers options for starting, stopping, and restarting Tor.
Please note that this is not intended as a finished product. We wanted to distribute the application and its source code in its present state to help others write applications that use these techniques.
Points of Interest
1. Initially Hidden Main Form
When the application starts, you will notice that the main form is never displayed. .NET 2.0 handles the application class a little differently than .NET 1.1. The default application class looks like this:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace TorTray
{
static class Program
{
[STAThread]
static void Main( )
{
Application.EnableVisualStyles( );
Application.SetCompatibleTextRenderingDefault( false );
Application.Run( new TorTray( ) );
}
}
}
The problem is that there is no way to prevent the window from being displayed. At best there will be a flash as it appears and disappears. But it is always there. This can be solved with one small change to the class:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace TorTray
{
static class Program
{
[STAThread]
static void Main( )
{
Application.EnableVisualStyles( );
Application.SetCompatibleTextRenderingDefault( false );
TorTray tt = new TorTray( );
Application.Run( );
}
}
}
The Application.Run
function calls the form's Show
function as part of its normal execution. Short of re-writing the Application
class, there is no way to prevent it from doing so. However, that is the only function in the form ever called by Application.Run
. Since the main form's class has to be created, removing it to its own line accomplishes exactly what we needed: a form that is initially invisible. It is still fully instantiated and can be used without modification. It is just never made visible.
2. Creating an invisible subprocess
This is actually quite straightforward: create an instance of the Process
class and set the Process.StartInfo.CreateNoWindow
member to true
. However, we wanted to capture output from the console application to display it in the main form. The lines of interest from TorControl
are:
m_processTor.StartInfo.RedirectStandardOutput = true;
m_processTor.StartInfo.RedirectStandardError = true;
m_processTor.StartInfo.UseShellExecute = false;
m_processTor.OutputDataReceived += new
DataReceivedEventHandler( m_processTor_OutputDataReceived );
m_processTor.ErrorDataReceived += new
DataReceivedEventHandler( m_processTor_ErrorDataReceived );
According to the MSDN, output cannot be redirected unless UseShellExecute
is false
. The Process
class has the facility to send each line of output, as it is received, to registered event listeners. We took advantage of this so that we didn't have to poll for output.
That brings us to the next problem: receiving those events!
No matter what we did, the event handlers just did not want to fire. There was nothing obvious in the MSDN describing this issue but after much reading and experimenting, we hit on the following solution:
Just after the process is started, the Process.BeginErrorReadLine
and Process.BeginOutputReadLine
functions are called. Apparently these start the asynchronous pump so that the events fire. The lines are:
m_started = m_processTor.Start( );
m_processTor.BeginErrorReadLine( );
m_processTor.BeginOutputReadLine( );
Of course this means that prior to killing the process, the asynchronous pumps have to be shut down, otherwise the application will hang waiting for the output to end:
m_processTor.CancelErrorRead( );
m_processTor.CancelOutputRead( );
m_processTor.Kill( );
m_processTor.WaitForExit( );
3. Displaying the text in the main form
Now that we had the text being sent to our event handlers, we needed to send it along to the main form. We could have made the ListView
control publicly accessible and just added each line to it directly, but it is just plain ugly. That couples the classes too closely. The solution is to create an event handler of our own!
.NET 2.0 introduces the idea of a delegate
. For all intents and purposes, this is a function pointer prototype. The delegate has the exact signature of the function that will ultimately be used to handle the events. The code to create an event handler is:
class TorControl
{
...
public event TorOutputReceivedEventHandler TorOutputReceived;
...
}
TorOutputReceivedEventHandler
is the delegate. It takes the following form:
public delegate void TorOutputReceivedEventHandler( object sender,
TorOutputReceivedEventArgs e );
You will notice that this is actually defined outside of the class to be consistent with the way the .NET Framework defines delegates. You can just as easily define it within your class, but the client event handler would have to reference it as TorControl.TorOutputReceivedEventHandler
.
The reason for creating a custom delegate is that we can create a subclass of EventArgs
that allows data to be sent from the event trigger to the event handler. In this case, the class is TorOutputReceivedEventArgs
:
public class TorOutputReceivedEventArgs : EventArgs
{
public TorOutputReceivedEventArgs( ) { }
public TorOutputReceivedEventArgs( string output )
{
m_output = output;
}
public string Output
{
get
{
return ( m_output );
}
}
private string m_output = string.Empty;
}
Now the main form can subscribe to events in order to display them:
public partial class TorTray : Form
{
...
public TorTray( )
{
InitializeComponent( );
listBox.Items.Clear( );
m_torControl = new TorControl( );
m_torControl.TorOutputReceived += new
TorOutputReceivedEventHandler( m_torControl_TorOutputReceived );
}
void m_torControl_TorOutputReceived( object sender, TorOutputReceivedEventArgs e )
{
listBox.Items.Add( e.Output );
}
}
"Great!", we thought, "We're done!". As we ran it in the debugger, an exception was thrown stating that a worker thread could not modify GUI elements in the main form thread. What? We've written code like that before. Plus, the event handler is in the main form class, so why would there be a problem?
Well, just because a function is in a particular class doesn't mean that it cannot be called by another thread. That is the situation here. The event handler is actually called in the context of the Process
thread, not the GUI thread. According to the MSDN, GUI elements have never been thread safe, but there has never been a way of preventing it. Now in .NET 2.0 it is quite easy. This is a job for another delegate, this time being used in its pure form, as a function pointer:
delegate void SetItemCallback( string text );
This creates the prototype for a callback function that will accept a string of text. Now, we need a callback with the same signature that can differentiate between the threads:
private void SetItemText( string text )
{
if( listBox.InvokeRequired == true )
{
SetItemCallback callback = new SetItemCallback( SetItemText );
Invoke( callback, new object[] { text } );
}
else
{
listBox.Items.Add( text );
}
}
The idea here is that the execution of the worker thread can be detected through checking the InvokeRequired
property on the control that needs to be updated. The delegate is used to create a pointer to the real function, which is then invoked within the thread that the overall class belongs to. Essentially this allowed us to copy data from one thread's local storage to the other, and then manipulate the GUI. For this to work, the event handler is modified to use the callback:
void m_torControl_TorOutputReceived( object sender,
TorOutputReceivedEventArgs e )
{
SetItemText( e.Output );
}
Conclusion
That's it for our .NET 2.0 tips! We hope that you find them useful in your applications. We also hope that you find the TorTray application useful as well. We intend to expand its functionality in the future, so stay tuned!
History
- 2005-Sep-29 - Initial release.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.