C# Code Example, Omnipressent

Omnipressent was an experiment I worked on, the idea was to use varius services like vnc and file transfers to give the impression that multiple computers were one system. Allowing users to drag files from one desktop to another or take control of the speakers on another computer. To make the development manageable I couldn't write each service from scratch so I worked out a way of wrapping services others had developed and coming up with a constant way of passing messages to them and even allowing services to pass messages between each other.

Below is the OmniService class that any service we want to use in our program needs to extend. Mainly it needs to provide a way of getting messages and sending messages.



using System;  
 using System.Collections.Generic;  
 using System.Text;  
 using System.Collections;  
 using System.Diagnostics;  
 namespace Omnipressent {  
   public abstract class OmniService {  
     protected ArrayList messageQue;  
     public int targetHost = -2;  
     public OmniService() {  
       messageQue = new ArrayList();  
     }  
     public void generateMessage(Message msg) //From this class out. giveMessage isward.   
     {  
       messageQue.Add(msg);  
     }  
     public Message getMessage() {  
       Message returnMessage = null;  
       if (messageQue.Count != 0) {  
         returnMessage = (Message)messageQue[0];  
         messageQue.RemoveAt(0);  
       }  
       // #if DEBUG //---------------------------------------------------------------  
      /* debugLog(this.GetType().Name + " MESSAGES. Count = " + messageQue.Count);  
       for (int i = 0; i < messageQue.Count; i++) {  
         debugLog("i=" + i + "] " + messageQue[i].ToString());  
       }*/  
       //#endif //------------------------------------------------------------------  
       return returnMessage;  
     }  
     public abstract void giveMessage(Message message);  
     protected void setTargetHost(int host)  
     {  
       //debugLog(this.GetType().Name + "Changed TargetHost to >> " + host.ToString());  
       targetHost = host;  
     }  
     protected void debugLog(string s)  
     {  
       Debug.WriteLine(s);  
       generateMessage(new Message(0, messageTypeEnum.ServiceGUI, messageEnum.PrintDebug, s));  
     }  
     protected void warningLog(string s)  
     {  
       generateMessage(new Message(0, messageTypeEnum.ServiceGUI, messageEnum.PrintWarning, s));  
     }  
     protected void infoLog(string s)  
     {  
       generateMessage(new Message(0, messageTypeEnum.ServiceGUI, messageEnum.PrintInfo, s));  
     }  
   }  
 } 
It actually took a while to figure out how to do make this this simple. The message itself is below





using System;
using System.Collections.Generic;
//using System.Linq;
using System.Text;
//using System.Threading.Tasks;
using System.Diagnostics;

/**************************************************
 * TargetHost numbers:
 * -3 To the Webservice for the services consumption
 * -2 Null, just a safe place to point things
 * -1 Write to file
 * 0 Use internaly, pass to service
 * >0 Pass to host via webservice
 * */
namespace Omnipressent {
    public class Message {
        public messageTypeEnum messageType;
        /*{ //Fix this
            get { return messageType; }
            set { messageType = value; }
        }*/
        public int targetHost = -2; //-2: Unset/nowhere, -1: To log, 0 Internal, n host number.
        public messageEnum message;
        public String arguments;
        public int key = -2;  //-2 unset
        public int requestingHost = -2; //-2 unset. This host, get by ServiceWebService on Authentication with WebService.

        public Message(int targetHost, messageTypeEnum mt, messageEnum msg, String args) {
            this.targetHost = targetHost;
           // messageType = new messageTypeEnum();
            messageType = mt;   message = msg;  arguments = args;
        }


        public Message(string msg)
        {

            /***************************
             * Takes a message in string form. 
             * This string could be from the console, 
             * the network or a script file.
             ***************************/


            string[] splitMessage = msg.Split(','); //Split up parameters.
            try
            {
                //SomeEnum enum = (SomeEnum)Enum.Parse(typeof(SomeEnum), "EnumValue");
                 targetHost = (Convert.ToInt32(splitMessage[0]));
                   messageType= (messageTypeEnum)Enum.Parse(typeof(messageTypeEnum), splitMessage[1]);
                   message = (messageEnum)Enum.Parse(typeof(messageEnum), splitMessage[2]);
                    arguments = splitMessage[3];

                

                if(splitMessage.Length > 5) //Long form
                {
                    try
                        {
                    requestingHost = Convert.ToInt32( splitMessage[4]);
                    key = Convert.ToInt32( splitMessage[5]);
                        }
                        catch(Exception ex)
                    {
                           Debug.Write("MESSAGE INIT EX : \n" + msg);
                        }
                }
                
                //dbgLog("Create message from string : " + m.ToString());
            }
            catch (Exception ex)
            {

               Debug.WriteLine("Exception parsing message ex : " + ex.ToString());
               // throw new ArgumentException("Invalid message string exception", msg);

                
                //dbgLog("Message from string exception ex : " + ex.ToString());
                try
                {
                     /*debugLog("Received string message : " + msg.ToString());
                     debugLog("S0 = " + splitMessage[0]);
                     debugLog("S1 = " + splitMessage[1]);
                     debugLog("S2 = " + splitMessage[2]);
                     debugLog("S3 = " + splitMessage[3]);*/
                }
                catch (Exception ex2)
                {
                    //dbgLog("Could not print values.");
                }

                //dbgLog("\n\n");
               //printEnumValues();
            }
        }

        public override string ToString() {
            return targetHost + "," + messageType.ToString() + "," + message.ToString() + "," + arguments + "," + requestingHost + "," + key;
        }
        //public Message messageType
    }

    public enum messageTypeEnum {
        /*******************************************
         * WARNING: Setting up of the services in theHub is strongly linked to these values
         *******************************************/
        ServiceInfo = 0,
        ServiceCommandClient = 1,
        ServiceScriptSetter = 2,
        ServiceScriptGetter = 3, //Not used
        ServiceVNCClient = 4,
        ServiceVNCServer = 5,
        ServiceClipboardSetter = 6,
        ServiceClipboardGetter = 7,
        ServiceHIDGetter = 8,
        ServiceHIDSetter = 9,
        ServiceScreenSlide = 10,
        ServiceScreenBlocker = 11,
        ServiceGUI = 12,
        ServiceController = 13
   
        

        /*
            services[4] = new ServiceVNCClient();
            services[5] = new ServiceVNCServer();
            services[6] = new ServiceClipboardSetter();
            services[7] = new ServiceClipboardGetter();
            services[8] = new ServiceHIDGetter();
            services[9] = new ServiceHIDSetter();
            services[10] = new ServiceScreenSlide();
            services[11] = new ServiceScreenBlocker();
         */
        
    }

    public enum messageEnum {
        //Common
        MessageParseProblem,
        SetTarget,

        //Info
        Version, PrintHelp, GiveHostInfoXML, GetHostInfoXML, GetLocalHostInfoXML, UpdateInfo,setUsernamePassword,
        //HID Messages
        StopGettingMouse, StartGettingMouse, StopGettingKeyboard, StartGettingKeyboard,
        KeyUp, KeyDown, MouseX, MouseY, MousePos, LeftDown, MidDown, RightDown, LeftUp,MidUp,RightUp,MouseMove, ScrollUp, ScrollDown,


        //ServiceScriptSetter
        SaveToFile,

        //ServiceScriptGetter
        ReadScript,SetPath,

        //Net Messages
        //TODO

        //RDP Messages
        //TODO
        //GUI
        PrintInfo, PrintDebug, PrintWarning,

        //Blockerform
        Block,Unblock,
        //VNC Client Messages
        Connect, Disconnect, Centre, GetStatus, ScrollCenter, ScrollLeft, ScrollRight, SetClipText,
        //SlideScreen
        GetScreen, HideScreen, ShowScreen,
        //VNC Server Messages
        Start, Stop, SetPort,
        //ScriptOut
        Delete,
        //Clipper
        SetClipboardText, 

        //CommandService
        GetMessages, Register, StartClientAuth, CheckClientAuth, StartServerAuth, CheckServerAuth, ConfirmClientAuth, ConfirmServerAuth
    }}


Last but not least the Hub class. The Hub class actually took a lot of work. I tried developing this project before and the whole thing became too unwieldy to continue and I had to find a new route. The Hub is the controller (In the MVC sense) for the whole program. It's pretty small but it took a lot of planning and re-planning to get it that small and if I wasn't coding this whole thing by myself I probably wouldn't have put so much time into making this so small but I needed it to be manageable. I replaced the old controller with more of a message router and by doing that reduced a lot of code I had to write. Any startup functions I needed were outsourced to a script that ran, a happy byproduct of the way messages worked was they could be stored in a text file, essentially creating a scripting language with no extra effort. 



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

namespace Omnipressent
{
    class Hub
    {
        bool running = true;
        string startupScriptPath = "startup.os";
        OmniService[] services; //Array containing all services.
        ArrayList messagesWaiting;
        GUI theGUI;

        public Hub()
        {
            int noOfMessageTypes = 0;
            messagesWaiting = new ArrayList();
            

            //This counts the number of message types to set the size of the services array.
            foreach (messageTypeEnum messageType in (messageTypeEnum[])Enum.GetValues(typeof(messageTypeEnum)))
                noOfMessageTypes++;  //There has to be a better way


            services = new OmniService[noOfMessageTypes];

            /*I have to say, I'm pretty proud of this. Almost everything is a service.
             *Enumerators matching the service names are used for routing messages (see sexy mainloop code).
             *The same enumerators are used for indexes here. Because I abscrated nicely you can be fairly agnostic when
             *Dealing this services and as such this control is fairly simple. Collects, routes and gives messages */

            services[messageTypeEnum.ServiceInfo.GetHashCode()] = new ServiceInfo();
            services[messageTypeEnum.ServiceController.GetHashCode()] = new ServiceController();
            services[messageTypeEnum.ServiceScriptGetter.GetHashCode()] = new ServiceScriptGetter();
            services[messageTypeEnum.ServiceScriptSetter.GetHashCode()] = new ServiceScriptSetter();
            services[messageTypeEnum.ServiceVNCClient.GetHashCode()] = new ServiceVNCClient();
            services[messageTypeEnum.ServiceVNCServer.GetHashCode()] = new ServiceVNCServer();
            services[messageTypeEnum.ServiceClipboardSetter.GetHashCode()] = new ServiceClipboardSetter();
            services[messageTypeEnum.ServiceClipboardGetter.GetHashCode()] = new ServiceClipboardGetter();
            services[messageTypeEnum.ServiceHIDGetter.GetHashCode()] = new ServiceHIDGetter();
            services[messageTypeEnum.ServiceHIDSetter.GetHashCode()] = new ServiceHIDSetter();
            services[messageTypeEnum.ServiceScreenSlide.GetHashCode()] = new ServiceScreenSlide();
            services[messageTypeEnum.ServiceScreenBlocker.GetHashCode()] = new ServiceScreenBlocker();
            services[messageTypeEnum.ServiceGUI.GetHashCode()] = new ServiceGUI();
            services[messageTypeEnum.ServiceCommandClient.GetHashCode()] = new ServiceCommandClient();
            RunStartupScript();
            while(running)
                mainLoop();
        }

        private void RunStartupScript()
        {
            messagesWaiting.Add(
                new Message(0,messageTypeEnum.ServiceScriptGetter,messageEnum.SetPath,startupScriptPath)
                );
            messagesWaiting.Add(
                new Message("0, ServiceScriptGetter, ReadScript, 0")
                );
        }

        public void setGUI(GUI g)
        {
            theGUI = g;
        }

        public void mainLoop()
        {
            dbgLog("Main Loop Started ************************************************************");
               
            foreach (OmniService s in services)
            {   //Get messages from each service
                    while (true)
                    {
                        dbgLog(s.GetType().Name.ToString());
                        Message m = s.getMessage();
                        if (m == null)
                            break; //This feels a little wrong but it works well
                        messageIn(m);
                    }
            }
            foreach (Message msg in messagesWaiting)
            {   //Deliver messages
                    //msg.targetHost == -2 goes nowhere intentionaly. This is like /dev/null
                    if(msg.targetHost == -2)
                    {
                        //System.Windows.Forms.MessageBox.Show("targetHost set to -2. Service running without being configured. Message : " + msg.ToString());
                    }
                    if (msg.targetHost == -1) //To Log / Script
                        //this.messagesWaiting.Add( //Apparently you can't add to this in the foreach

                        services[messageTypeEnum.ServiceScriptSetter.GetHashCode()].giveMessage(
                        new Message(0, messageTypeEnum.ServiceScriptSetter, messageEnum.SaveToFile, msg.ToString())
                        );
                       // );
                    /* The above line might be a little confusing. 
                     * If hostID == -1, that means print to file, 
                     * this is used mainly for logging out HID info so it can be read back in.
                     * What I'm doing is nesting a Sting of a message in a message destied for Serviceoutput.
                     * The message I'm saving is the argument in anouther message telling ServiceOutput to Save To File
                     */

                    if(msg.targetHost == 0) //Internal / Local
                    services[msg.messageType.GetHashCode()].giveMessage(msg);

                    if (msg.targetHost > 0 | msg.targetHost == -3) //>0 to other host routed though webservice. -3 of webservice itself;
                    {
                        services[messageTypeEnum.ServiceCommandClient.GetHashCode()].giveMessage(msg); 
                    }
            }
            //Asume all delivered or undelivable. Drop all messages
            ((ServiceController)services[messageTypeEnum.ServiceController.GetHashCode()]).Tick();
            messagesWaiting.Clear();
            addStandingOrders();
            dbgLog("MainLoop finished-----------------------------------------------------------------------");
            omniWait();
        }
        public void addStandingOrders()
        {
            //This is for adding messages that are run each loop
            //messagesWaiting.Add(new Message(0, messageTypeEnum.ServiceInfo, messageEnum.UpdateInfo, ""));
            //messagesWaiting.Add(new Message("0,ServiceCommandClient,GetMessages,,"));
        }

        public void omniWait()
        {
            //This will eventualy become more complex, hence moving it out onto it's own method
            System.Threading.Thread.Sleep(5000);
        }

        public string info()
        {
            //printEnumValues();
            return "Super alpha V0.0 turbo edition.";
        }
        public void messageIn(Message msg)
        {
            /***************************
             * Takes a message in message object form.
             * This is internal and already processed
             * ready for routing
             ***************************/
            messagesWaiting.Add(msg);
            //debugLog("Received Message Message : " + msg.ToString());
        }
        public void messageIn(String msg)
        {
            messageIn(new Message(msg));
        }
        /*public void printEnumValues()
        {
 
            string messageTypes = "";
            string messages = "";

            foreach (messageTypeEnum messageType in (messageTypeEnum[])Enum.GetValues(typeof(messageTypeEnum)))
                messageTypes += messageType.ToString() + "\n";

            foreach (messageEnum message in (messageEnum[])Enum.GetValues(typeof(messageEnum)))
                messages += message.ToString() + "\n";

            dbgLog("Message Types available:");
            dbgLog(messageTypes);
            dbgLog("Messages available:");
            dbgLog(messages);

        }*/
        public void debugLog(string s)
        {
            messagesWaiting.Add(new Message(0, messageTypeEnum.ServiceGUI, messageEnum.PrintDebug, s));
            //debugLog(s);
           // theGUI.consoleWrite(s);
        }
        public void dbgLog(String s)
        {
            Debug.WriteLine("S:" + System.DateTime.Now.Second.ToString() + "." + System.DateTime.Now.Millisecond.ToString() + " > " + s);
        }
    }
}