static int SwarmOpenConnection(FConnectionCallback CallbackFunc, IntPtr CallbackData, ELogFlags LoggingFlags, IntPtr OptionsFolder)
		{
			try
			{
				return GInstance.OpenConnection(CallbackFunc, CallbackData, (ELogFlags)LoggingFlags, FStringMarshaler.MarshalNativeToManaged(OptionsFolder));
			}
			catch(Exception Ex)
			{
				DebugLog.Write(Ex.Message + "\n" + Ex.ToString());
				return 0;
			}
		}
		Int32 TryOpenConnection(FConnectionCallback CallbackFunc, IntPtr CallbackData, ELogFlags LoggingFlags)
		{
			try
			{
				// Allocate our new connection wrapper object
				Connection = new IAgentInterfaceWrapper();

				// Make sure the agent is alive and responsive before continuing
				EditorLog(EVerbosityLevel.Informative, "[TryOpenConnection] Testing the Agent");
				Hashtable InParameters = null;
				Hashtable OutParameters = null;
				bool AgentIsReady = false;
				while (!AgentIsReady)
				{
					try
					{
						// Simply try to call the method and if it doesn't throw
						// an exception, consider it a success
						Connection.Method(0, InParameters, ref OutParameters);
						AgentIsReady = true;
					}
					catch (Exception ex)
					{
						// Wait a little longer
						EditorLog(EVerbosityLevel.Critical, "[TryOpenConnection] Waiting for the agent to start up ...");
						EditorLog(EVerbosityLevel.Critical, ex.ToString());
						Thread.Sleep(5000);
					}
				}

				// Request an official connection to the Agent
				EditorLog(EVerbosityLevel.Informative, "[TryOpenConnection] Opening Connection to Agent");
				EditorLog(EVerbosityLevel.Informative, "[TryOpenConnection] Local Process ID is " + Process.GetCurrentProcess().Id.ToString());

				StartTiming("OpenConnection-Remote", false);
				ConnectionHandle = Connection.OpenConnection(AgentProcess, AgentProcessOwner, Process.GetCurrentProcess().Id, LoggingFlags, out ConnectionConfiguration);
				StopTiming();

				if (ConnectionHandle >= 0)
				{
					Log(EVerbosityLevel.Informative, ELogColour.Green, "[Interface:TryOpenConnection] Local connection established");

					// Spawn a thread to monitor the message queue
					MessageThreadData ThreadData = new MessageThreadData();
					ThreadData.Owner = this;
					ThreadData.Connection = Connection;
					ThreadData.ConnectionHandle = ConnectionHandle;
					ThreadData.ConnectionCallback = CallbackFunc;
					ThreadData.ConnectionCallbackData = CallbackData;
					ThreadData.ConnectionConfiguration = ConnectionConfiguration;

					// Launch the message queue thread
					ConnectionMessageThread = new Thread(new ParameterizedThreadStart(MessageThreadProc));
					ConnectionMessageThread.Name = "ConnectionMessageThread";
					ConnectionMessageThread.Start( ThreadData );

					// Launch the agent monitor thread
					ConnectionMonitorThread = new Thread(new ParameterizedThreadStart(MonitorThreadProc));
					ConnectionMonitorThread.Name = "ConnectionMonitorThread";
					ConnectionMonitorThread.Start(ThreadData);

					// Save the user's callback routine
					ConnectionCallback = CallbackFunc;
					ConnectionCallbackData = CallbackData;
					ConnectionLoggingFlags = LoggingFlags;
				}
			}
			catch (Exception Ex)
			{
				EditorLog(EVerbosityLevel.Critical, "[TryOpenConnection] Error: " + Ex.Message);
				EditorLog(EVerbosityLevel.Critical, Ex.ToString());
				ConnectionHandle = Constants.INVALID;
				Connection = null;
			}

			return ConnectionHandle;
		}
        /*
         * Clean up all of the remainders from a closed connection, including the network channels, etc.
         */
        Int32 CleanupClosedConnection()
        {
            Monitor.Enter(CleanupClosedConnectionLock);

            // NOTE: Do not make any calls to the real Connection in here!
            // If, for any reason, the connection has died, calling into it from here will
            // end up causing this thread to hang, waiting for the dead connection to respond.
            DebugLog.Write("[Interface:CleanupClosedConnection] Closing all connections to the Agent");

            // Reset all necessary assigned variables
            AgentProcess = null;
            AgentProcessOwner = false;

            if (Connection != null)
            {
                // Notify the connection wrapper that the connection is gone
                Connection.SignalConnectionDropped();
                Connection = null;
            }
            ConnectionHandle = Constants.INVALID;
            ConnectionConfiguration = null;

            // Clean up and close up the connection callback
            if (ConnectionCallback != null)
            {
                IntPtr QuitMessage = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(FMessage)));
                Marshal.StructureToPtr(new FMessage(ESwarmVersionValue.VER_1_0, EMessageType.QUIT), QuitMessage, false);
                ConnectionCallback(QuitMessage, ConnectionCallbackData);
            }
            ConnectionCallback = null;
            ConnectionCallbackData = IntPtr.Zero;

            // Close all associated channels
            if (OpenChannels.Count > 0)
            {
                foreach (ChannelInfo NextChannel in OpenChannels.Values)
                {
                    // Just close the handle and we'll clear the entire list later
                    if (NextChannel.ChannelFileStream != null)
                    {
                        NextChannel.ChannelFileStream.Close();
                        NextChannel.ChannelFileStream = null;
                    }
                }
                OpenChannels.Clear();
            }

            // Unregister the primary network communication channel
            if (NetworkChannel != null)
            {
                ChannelServices.UnregisterChannel(NetworkChannel);
                NetworkChannel = null;
            }

            Monitor.Exit(CleanupClosedConnectionLock);

            return Constants.SUCCESS;
        }
		/**
		 * Opens a new connection to the Swarm
		 *
		 * @param CallbackFunc The callback function Swarm will use to communicate back to the Instigator
		 *
		 * @return An INT containing the error code (if < 0) or the handle (>= 0) which is useful for debugging only
		 */
		public virtual Int32 OpenConnection(FConnectionCallback CallbackFunc, IntPtr CallbackData, ELogFlags LoggingFlags, string OptionsFolder)
		{
			// Checked here so we can time OpenConnection
			if ((LoggingFlags & ELogFlags.LOG_TIMINGS) == ELogFlags.LOG_TIMINGS)
			{
				PerfTimerInstance = new PerfTimer();
			}

			StartTiming("OpenConnection-Managed", true);

			// Establish a connection to the local Agent server object
			ConnectionHandle = Constants.INVALID;
			Int32 ReturnValue = Constants.INVALID;
			try
			{
				EditorLog(EVerbosityLevel.Informative, "[OpenConnection] Registering TCP channel ...");

				// Start up network services, by opening a network communication channel
				NetworkChannel = new TcpClientChannel();
				ChannelServices.RegisterChannel(NetworkChannel, false);

				// See if an agent is already running, and if not, launch one
				EnsureAgentIsRunning(OptionsFolder);
				if (AgentProcess != null)
				{
					EditorLog(EVerbosityLevel.Informative, "[OpenConnection] Connecting to agent ...");
					ReturnValue = TryOpenConnection(CallbackFunc, CallbackData, LoggingFlags);
					if (ReturnValue >= 0)
					{
						AgentCacheFolder = ConnectionConfiguration.AgentCachePath;
						if (AgentCacheFolder.Length == 0)
						{
							EditorLog(EVerbosityLevel.Critical, "[OpenConnection] Agent cache folder with 0 length.");
							CloseConnection();
							ReturnValue = Constants.ERROR_FILE_FOUND_NOT;
						}
					}
				}
				else
				{
					EditorLog(EVerbosityLevel.Critical, "[OpenConnection] Failed to find Swarm Agent");
                    ReturnValue = Constants.ERROR_FILE_FOUND_NOT;
				}
			}
			catch (Exception Ex)
			{
				EditorLog(EVerbosityLevel.Critical, "[OpenConnection] Error: " + Ex.Message);
				ReturnValue = Constants.ERROR_EXCEPTION;
			}

			// Finally, if there have been no errors, assign the connection handle 
			if (ReturnValue >= 0)
			{
				ConnectionHandle = ReturnValue;
			}
			else
			{
				// If we've failed for any reason, call the clean up routine
				CleanupClosedConnection();
			}

			StopTiming();
			return ReturnValue;
		}
        FSwarmInterface()
        {
            AgentProcess = null;
            AgentProcessOwner = false;
            Connection = null;
            ConnectionHandle = Constants.INVALID;
            ConnectionMessageThread = null;
            ConnectionMonitorThread = null;
            ConnectionConfiguration = null;
            ConnectionCallback = null;
            BaseChannelHandle = 0;
            PendingTasks = null;
            NetworkChannel = null;
            PerfTimerInstance = null;

            OpenChannels = new ReaderWriterDictionary<Int32, ChannelInfo>();
            FreeChannelWriteBuffers = new Stack<byte[]>();
            CleanupClosedConnectionLock = new Object();

            // TODO: Delete old files
        }