me

System Architect

I am a system architect, developer, entrepreneur, father, thinker and a Dane all wrapped up in one.

I am always interested in hearing about challenging projects so feel free to check out my work and see if I might be what you need for your next project.

You can find me socially on one of these popular sites.

articles
A Plugin Architecture
2008 M.Nielsen Plugins

Preface

I have mentioned on my blog, I plan to bring back my article section where I write about stuff that interest me. In a previous incarnation of this site I had an article on Plugin Architecture, which I am republishing on here now. Granted it is from way back in 2008 and therefore a little old, but the concept still holds true. I will try to get an updated version which also contains multiple plugins to build on the concept but for now you can enjoy some .Net 2.0 code.

Read more →

Introduction

If you have ever completed a professional project I'm sure you have encountered the need for additional features 6 months down the road. Normally that would mean that you would have to refresh your memory on what you did and then try to fit the changes into the existing program.

The need for applications to continuously adapt to changes in requirements have made the plugin architecture much more popular. As a matter of fact most of the applications you use in your daily endeavors use plugins in one way or another.

Isn't it too difficult?

As you will see the time spent up front is minimal in comparison to the benefits achieved later. The plugins will be loaded at runtime so you will be able to easily extend your applications. The architecture is based on interfaces so even if you have to hand it over to someone else they should able to pick up where you left off with no problem.

Before we start

This project was made in C# using Microsoft Visual Studio 2005 and the .NET 2.0 runtime. If for some reason you are using a different environment you will have to figure out what/how to replace on your own. Even if you don't have VS 2005 you should be able to walk away with the general idea of how plugins work.

I'm not going to spend much time on the settings of all the components I use since I have included all the project files at the end of this article.

Interfaces

If you haven't yet worked with interfaces don't worry its easy. An interface is merely an abstract class with purely public methods, properties and events. You can consider it the architectural drawing of the application.

Using interfaces makes it easy to enforce specific implementations since any class which inherits from an interface has to implement all methods of all the interfaces it implements( yes multiple interfaces can be inherited in one class).

Our Interface

The plugin we are about to make will assume that the main application has 2 panels (1 left and 1 right) which each can contain a UserControl. To create the interface just create a new project and select class library. Get rid of the autogenerated class and add your interface instead.

using System;
using System.Collections;

/// 
/// Plugin interface
/// 
namespace PluginInterface
{    
    public interface IPluginBase
    {
        string Name { get; set;}
        string Version { get; set;}
        string Author { get; set;}
        string Description { get; set;}
    };

    public enum MenuType
    { 
        mtMenuItem,
        mtToolbar,
        mtBoth,
        mtUnknown
    }

    public interface IPluginMenuItem : IPluginBase
    {
        string Text { get; set;}
        string ToolTip { get; set;}
        MenuType Type { get; set;}
        IPluginMenuItem[] PluginMenu { get; set;}
        System.EventHandler onclick { get; set;}
    };

    public interface IPlugin : IPluginBase
    {
        IPluginMenuItem[] PluginMenu { get;}
        System.Windows.Forms.UserControl LeftPanel { get;}
        System.Windows.Forms.UserControl RightPanel { get;}
    }
}
		
	  

As you can see we have created a IPluginBase interface first which contains properties we wish all our plugins to contain.

Next we have created a IPluginMenuItem which will allow us to add menu items and toolbar items to the host application. Please note the line "IPluginMenuItem[] PluginMenu { get; set;}". This will make it possible for anyone using our plugin to create the desired structure in the menu.

Finally the actual interface contains the collection of menuitems and the property for the left and right panels.

The Plugin

The plugin we will make here is a IE clone. We will make a web browser with a listbox on the left panel for the bookmarks and the browser window in the right panel. Since this will be an assembly we will create a project with a class library. I named mine WebbrowserPlugin and the class webbrowserplgin but you can call yours whatever you want. We need to add the reference to the interfaces which can be done by right clicking on your project in the solution explorer and then selecting "Add Reference". You can then browse to the interface project you created earlier. Next I have set the class to inherit from IPlugin. (Tip : in VS 2005 you can right click on the interface and select implement and the IDE will create the methods and properties for you).

After filling out the methods for set and get I added 2 forms to the project (Rightclick the project in the solution explore then select "Add" and "Windows Form").

The first form I named BookmarkFm and to that one I added a listbox named bookmarkslistBox and a toolstrip named addtoolStri. I also added a button to the toolstrip which I named "Add".

BookmarkFm

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace WebbrowserPlugin
{
    public partial class BookmarkFm : System.Windows.Forms.UserControl
    {
        private DataSet bookmarksds;
        private string Path;
        public WebBrowserPlgIn host;
        public BookmarkFm()
        {
            InitializeComponent();
            Path = System.IO.Path.GetDirectoryName(
			System.Reflection.Assembly.GetExecutingAssembly().Location);
            bookmarksds = new DataSet();
            LoadBookmarks();
        }

        private void LoadBookmarks()
        {
            bookmarksds.ReadXml(Path + @"\bookmarks.xml");
            bookmarkslistBox.DataSource = bookmarksds.Tables["Bookmark"];
            bookmarkslistBox.DisplayMember = "URL";
        
        }
        private void bookmarkslistBox_DoubleClick(object sender, EventArgs e)
        {
            if (host.RightPanel is BrowserFm)
            {
                string url = bookmarkslistBox.Text;
                (host.RightPanel as BrowserFm).LoadURL(url);
            }            
        }

        private Boolean ValueExists(DataSet ds, string FieldName, string Value)
        {
            int FieldIdx = -1;
            Boolean Res = false;            
                        
            if (ds.Tables[0].Columns.Contains(FieldName))
            {
                FieldIdx = ds.Tables[0].Columns.IndexOf(FieldName);
                foreach (DataRow dr in ds.Tables[0].Rows)
                {
                    if (dr[FieldIdx].Equals(Value))
                    {
                        Res = true;
                        break;
                    }
                }
            }
            return Res;
        }

        private void AddBtn_Click(object sender, EventArgs e)
        {
            if (!ValueExists(bookmarksds,"URL",(host.RightPanel as BrowserFm).CurrentURL))
            {
                DataTable dt = bookmarksds.Tables["Bookmark"];            
                dt.Rows.Add(new object[] { (host.RightPanel as BrowserFm).CurrentURL });
                bookmarksds.WriteXml(Path + @"\bookmarks.xml");
            }
        }
    }
}  

As you can see the bookmarkslistBox contains the bookmarks we have. Those bookmarks are stored in a XML which is then bound to the control. The "Add" button will add the value in the url (from the browser form) to the xml if the url hasn't already been added (the ValueExists method). Also note that we added a "host" variable so that we can get access to the browser form. This is needed to allow us to load a page when we double click an item in the bookmarkslistBox.

So not really much going on here :)

The second form I named BrowserFm and on that one I added a Webbrowser control named webBrowser and a toolstrip named wbtoolStrip. To the toolstrip was added a button and a textbox.

BrowserFm

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace WebbrowserPlugin    
{
    public partial class BrowserFm : System.Windows.Forms.UserControl
    {
        public BrowserFm()
        {
            InitializeComponent();
           
        }

        public string Path;
        public string CurrentURL
        {
            get { return urlTextBox.Text; }
        }

        public void LoadURL(string url)
        {
            if (url.Trim() != "")
            {
                webBrowser.Navigate(url);
                urlTextBox.Text = url;                
            }
        }
        public void RefreshSite()
        {
            webBrowser.Refresh();
        }

        public void StopSite()
        {
            webBrowser.Stop();
        }

        private void gobtn_Click(object sender, EventArgs e)
        {
            LoadURL(urlTextBox.Text);
        }

        private void urlTextBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)13)
            {
                LoadURL(urlTextBox.Text); 
            }
        }
    }
}	  
	            

This form has even less code. Besides the 3 public methods for loading,stopping and refreshing a page we have implemented the click event of the button (to load the url) and the keypress of the textbox (to load the url on hitting enter)

Lets finally get to the main code of this plugin.

WebBrowserPlgIn

Before looking at the constructor we need to do one more thing. If you guessed I was talking about the Menuitems you were right. We need to create a class for the MenuItem which inherits from the IPluginMenuItem interface. Again just rightclick on the interface and select "Implement". There is no need for a constructor here since we will fill it all from the WebBrowserPlgIn constructor so just fill out the autogenerated methods.

All we are left with now is making the constructor of the WebBrowserPlgIn.

        private void OnReload (object sender, EventArgs e)
        {
            (rightpnl as BrowserFm).RefreshSite();
        }
        private void OnStop (object sender, EventArgs e)
        {
            (rightpnl as BrowserFm).StopSite();
        }
        public WebBrowserPlgIn()
        { 
            this.Author = @"Mogens Nielsen"; 
            this.Name = @"Webbrowser"; 
            this.Version = @"1.0.0";
            this.Description = "Plugin for browsing the web";
            leftpnl = new LeftFm();
            (leftpnl as LeftFm).host = this;
            rightpnl = new BrowserFm();
            (rightpnl as BrowserFm).LoadURL("www.msn.com");
            menuitems = new IPluginMenuItem[1];
            menuitems[0] = new Menuitem();
            menuitems[0].Name = "View";            
            menuitems[0].Text = "View";
            menuitems[0].Type = MenuType.mtMenuItem;

            menuitems[0].PluginMenu = new IPluginMenuItem[2];
            menuitems[0].PluginMenu[0] = new Menuitem();
            menuitems[0].PluginMenu[0].Name = "Stop";
            menuitems[0].PluginMenu[0].Text = "Stop";
            menuitems[0].PluginMenu[0].ToolTip = "Stop loading site";
            menuitems[0].PluginMenu[0].Type = MenuType.mtBoth;
            menuitems[0].PluginMenu[0].onclick += OnStop;

            menuitems[0].PluginMenu[1] = new Menuitem();
            menuitems[0].PluginMenu[1].Name = "Refresh";
            menuitems[0].PluginMenu[1].Text = "Refresh";
            menuitems[0].PluginMenu[1].ToolTip = "Refresh page";
            menuitems[0].PluginMenu[1].Type = MenuType.mtBoth;
            menuitems[0].PluginMenu[1].onclick += OnReload;
        }
	        

Please note that leftpnl,rightpnl and menuitems are private variables for the globals LeftPanel,RightPanel and PluginMenu respectably. The OnReload and OnStop events are assigned to the eventhandler of each menuitem so that when a user click on the menu on the host it will execute the event we need.

I know this seems like a lot but it really isn't. Most of the code in this assembly is from the interface. Anyway its time for the host.

Host

After creating a normal windows application project I have added the windows reference as well as the reference to our interfaces.

We can now layout the form and for that I added a splitcontainer named MainSplitCon which I set to horizontal split and locked the panel1 control. Next I added another splitcontrol named PluginSplitCon to the panel2 of MainSplitCon. On PluginSplitCon I again locked panel1 (the left). I also added a menustrip which I named MainMenu and a toolstrip named MainToolStrip which was added to the panel1 of MainSplitCon. Now that we have a nice form to work with lets add some code.

First we need to create a couple of variables we need later on

	  IPlugin CurrPlugin;
	  IPlugin[] Plugins;
	  string DataPath = Path.GetDirectoryName(Application.ExecutablePath) + @"\Plugins\";
	  

CurrPlugin will hold the current selected plugin. Plugins is a collection of all the plugins we have. Finally we need a datapath for were to load the plugins from, which in this case is a folder named "plugins" right under the execution folder of the host application.

private void GetPlugins(string Path)
{
    if (!Directory.Exists(Path))
    {
        return;
    }

    Plugins = new IPlugin[Directory.GetFiles(Path, "*.dll").Length];            
    int counter = 0;

    foreach (string f in Directory.GetFiles(Path, "*.dll"))
    {
        Assembly asm = null;                

        asm = Assembly.LoadFrom(f);
        if (asm != null)
        {
            foreach (Type pluginType in asm.GetTypes())
            {
                if ((pluginType.IsPublic) || (!pluginType.IsAbstract))
                {
                    Type PluginClass = pluginType.GetInterface("PluginInterface.IPlugin", true);
                    
                    if (PluginClass != null)
                    {
                        Plugins[counter] = (IPlugin)Activator.CreateInstance(
						asm.GetType(pluginType.ToString()));
                        counter++;
                        AddPluginToMenu(Plugins[counter - 1].Name);
                    }
                }
            }                    
        }                                             
    }
}	  

Here we run through all the *.dll files in the selected folder. If the file has has a public non abstract type that implements our interface "PluginInterface.IPlugin" we load it into our collection. With this check we make sure that we don't load something we can't use.

Please note that in this project I load all the plugins at one time. If you have several plugins it may be better to load them on demand.

If you look at the code you probably wonder what the "AddPluginToMenu" method is for so lets look at that

  private void AddPluginToMenu(string itemname)
  {
      ToolStripItem newitem;
      newitem = fileToolStripMenuItem.DropDownItems.Add(itemname, null, PluginMenuItem_Click);
      fileToolStripMenuItem.DropDownItems.Insert(0, newitem);
  }	  

When I created the form I added 2 items to the MainMenu "fileToolStripMenuItem" and "aboutToolStripMenuItem". To the fileToolStripMenuItem I added a subitem "Exit" in which the click method was implemented to exit the application. Back to the code above you can see that I create a new subitem for the fileToolStripMenuItem with the name of plugin (itemname) and set its click event to "PluginMenuItem_Click".

So lets see what happens in "PluginMenuItem_Click"

   private void PluginMenuItem_Click(object sender, EventArgs e)
   {
       ToolStrip parent = (sender as ToolStripMenuItem).Owner;
       ToolStripMenuItem item = (sender as ToolStripMenuItem);

       int index = parent.Items.IndexOf(item);

       if (index < Plugins.Length)
       {
           InitializePlugin(index);
       }            
   } 

Here we locate the index of the item that was clicked and if its less than the total numbers of plugins loaded we call the method "InitializePlugin" with the index.

So lets look at that method.

	  
	    private void InitializePlugin(int idx)
        {
            // clear current display
            PluginsplitCon.Panel1.Controls.Clear();
            PluginsplitCon.Panel2.Controls.Clear();
            MaintoolStrip.Items.Clear();            
            MainMenu.Items.Clear();
            // reattach file  menuitem
            MainMenu.Items.Add(fileToolStripMenuItem);
            

            CurrPlugin = Plugins[idx];
            CurrPlugin.LeftPanel.Dock = DockStyle.Fill;
            CurrPlugin.RightPanel.Dock = DockStyle.Fill;
            PluginsplitCon.Panel1.Controls.Add(CurrPlugin.LeftPanel);
            PluginsplitCon.Panel2.Controls.Add(CurrPlugin.RightPanel);
            foreach (IPluginMenuItem pm in CurrPlugin.PluginMenu)
            {
                ToolStripMenuItem tsi = new ToolStripMenuItem();
                tsi.Name = pm.Name;
                tsi.Text = pm.Text;
                tsi.ToolTipText = pm.ToolTip;
                switch (pm.Type)
                {                
                    case MenuType.mtToolbar  :   MaintoolStrip.Items.Add(tsi);
                                                 break;    
                    case MenuType.mtMenuItem :   MainMenu.Items.Add(tsi);
                                                 break;  
                    case MenuType.mtBoth     :   MaintoolStrip.Items.Add(tsi);
                                                 MainMenu.Items.Add(tsi);   
                                                 break;      
                }
                AddSubitems(pm, tsi);
            }

            // reattach file  menuitem
            MainMenu.Items.Add(aboutToolStripMenuItem);
        }

First we need to remove the old plugin from our form as well as clearing the the menu and toolstrip (first 4 lines). However we still need the "File" menuitem so we re-attach that one. Next we set the current plugin "CurrPlugin" to the plugin in our collection matching the index that was passed. After making sure the panels fill the client (DockStyle.Fill) we can assign the 2 panels. Now comes the time to run through all the plugins menuitems and attach them to the hosts menu and/or toolstrip respectably.

Unfortunately Microsoft decided to make the subitems of a menu item different than the top item so we have to create a separate method to handle that (AddSubitems).

private void AddSubitems(IPluginMenuItem pMenuItem, 
ToolStripMenuItem parentitem)
{
    ToolStripItem newitem = null;            

    foreach (IPluginMenuItem pm in pMenuItem.PluginMenu)
    {               
        switch (pm.Type)
        {
            case MenuType.mtToolbar: 
              newitem = new ToolStripButton();
              newitem.Name = pm.Name;
              newitem.Text = pm.Text;
              newitem.ToolTipText = pm.ToolTip;                                                
              MaintoolStrip.Items.Add(newitem);
              break;
            case MenuType.mtMenuItem:   
              newitem = new ToolStripMenuItem();                
              newitem.Name = pm.Name;
              newitem.Text = pm.Text;
              newitem.ToolTipText = pm.ToolTip;
              parentitem.DropDownItems.Add(newitem);
              break;
            case MenuType.mtBoth:       
              newitem = new ToolStripButton();
              newitem.Name = pm.Name;
              newitem.Text = pm.Text;
              newitem.ToolTipText = pm.ToolTip;
              newitem.Click += pm.onclick;
              MaintoolStrip.Items.Add(newitem);
              newitem = new ToolStripMenuItem();
              newitem.Name = pm.Name;
              newitem.Text = pm.Text;
              newitem.ToolTipText = pm.ToolTip;
              newitem.Click += pm.onclick;
              parentitem.DropDownItems.Add(newitem);
              break;
        }
    }
}  

This method is very similar to the code in InitializePlugin() except here we add the items to the "DropDownItems" rather than "Items"

Well we made it to the finish line so lets complete the constructor.

        public MainFm()
        {
            
            InitializeComponent();
            GetPlugins(DataPath);
            InitializePlugin(0);
            this.WindowState = FormWindowState.Maximized;
        }  

Nothing to this : We load the plugins from the DataPath then call InitializePlugin(0) to show the first plugin. Finally we maximize the application. That's it we are DONE! Build the application and run it. Don't forget to move the plugin (and XML file) to the correct folder.

Conclusion

Granted this was a small application and it is not ready to be a commercial product. It also lacked error handling (which was left out on purpose for making this demo). However I still believe it proved how little work is needed up front to get great benefits later. Anytime you want to extend your application you only need to worry about implementing the interface and the host application will handle the rest. You can even publish your interface to allow your users or other developers to extend your application to fit their needs.

So what are you waiting for get out there and code!

You can Download This Code Here

resume
Summary
  • 15 years experience as System Architect / Software developer, of which 6 was in leading roles
  • Highly self-motivated strategic thinker
  • Excels at problem-solving and decision-making
  • Communicates well with all levels of management, staff, fellow employees and clients/customers
Experience
12/10 – present
Founder
FlatLeaf, Orangevale, CA
FlatLeaf is an eBook subscription service with a social network encouraging content exploration and discovery through sharing, reviews, discussions and personalized social recommendation.
  • Founded – including creation of complete business plan, incorporation, and launch of FlatLeaf
  • Designed and implemented social network site, incorporating Twitter-style following system and LinkedIn groups and member messaging system
  • Designed and implemented a unique personalized social recommendation feature where members receive book recommendations based on follower preferences
  • Used various SEO techniques to bring FlatLeaf to top of search engines
  • Was selected to be one of only 10 startups to present at O'Reilly Tools of Chance startup showcase in New York
09/08 – 08/10
Sr. Software Developer
NEC Corporation of America, Rancho Cordova, CA
Started as a consultant and converted to direct employment after 6 months; primarily responsible for AFIS Fingerprint Identification workflows.
  • Shortened project turnaround from 3 months to 3 weeks
  • Redesigned, created, implemented 60+ workflow activities and 50+ workflows, including maintenance of separate window application for administration and customization
  • Worked with business analysts in drafting new requirement documents to identify static project features and eliminate ambiguity
  • Spearheaded new TFS structure to handle the complexity of multiple customers while creating processes for QA release procedure
  • Used Visual Studio 2005-2010 to create various data support libraries for Oracle/SQL Server in .NET 3.5/4.0
  • Created and maintained several tables, store procedures and packages on Oracle 10/11
09/07 – 08/08
CTO
Cotere Corporation, Sacramento, CA
Returned for a re-launch of the company, taking a more active role in executive duties.
  • Established a business plan with short and long term goals, including a development roadmap with job requirements and skill-set for future employees
  • Designed and implemented an API for the product Relay Point, a unique integration technology as a web service in C#
  • Added new Plug-ins and features to the existing product, and multi-threading to data-pump
  • Held several online presentations of the product
09/06 – 08/07
Software Developer
HFS, Elk Grove, CA
HFS delivers financial software to the health industry via a suite of products made in Delphi, C# and COBOL.
  • Designed and implemented a multi-product security system, using COM and XML, for restricting access rights based on several custom criteria
  • Designed and implemented an ASP.NET application in C# and ADO.NET for online management of client information and users access rights
  • Created plug-in in Delphi, allowing users to modify worksheets for the hospital system
  • Optimized customer Access databases for improved reporting
05/06 – 09/06
Sr. Software Developer
RSI, Roseville, CA
After successfully acquiring a competitor’s desktop application for Tax auditing RSI needed to re-brand, upgrade and integrate the application with proprietary applications.
  • Upgraded and enhanced Delphi 5 application to Delphi 2006
11/03 – 01/06
Team-Lead/Chief Architect
Cotere Corporation, Sacramento, CA
Led, directed, and managed development of company’s two products Access Point, a web enabled records delivery application, and Relay Point an ETL/EAI integration methodology /software.
  • Designed, as Chief architect on Relay Point, a unique XML-based ETL/EAI integration technology
  • Led projects, directing 5 employees in product development, including: production timetable, status reporting, quality assurance
  • Designed and engineered a development tool for Relay Point in Delphi 7 wherein the IDE utilizes plug-ins for the various designers needed to create the required documents of an integration
  • Developed backend for Access Point a web enabled records delivery application
  • Created web services and windows applications for clients in C#
  • Created a number of Com applications, along with various ADO.NET connections for the backend for Access Point an ASP.NET public records portal
  • Created and maintained more that 250 tables and 650 views, stored procedures and triggers in various databases related to company’s products, all in SQL Server
  • Integrated data streams from large record sets including the California DMV, Texas DMV, Vantage Data and TransUnion, containing driver, vehicle, criminal and credit records
03/03 – 06/03
Software Developer
Rapidigm Inc., Los Angeles, CA
Consolidated company databases for Client, a Security company, which had acquired two of its competitors - including employee, client and patrol data into one new Sybase database.
  • Created Delphi 6 application for converting / mapping data from PICK and SQL Anywhere to Sybase
  • Utilized Client data sets for backup / maintenance of old data
  • Created and modified Crystal reports for the new system, including billing, schedules, vacations and more
12/99 – 12/02
Software Developer
Ivillage Inc. / Astrology.com, San Francisco, CA
In addition to maintenance of existing applications, led the architecture, implementation and testing of a new on-line Astrology software for Ivillage (the number 1 women’s web-site on the internet). Primary responsibilities included design, architecture and configuration of the complete system (application servers, fileserver and database server, router and load-balancing system).
  • Created and maintained various applications using Delphi 5/6 and paradox, Interbase / Firebird
  • Utilized .NET framework to develop several web services serving a number of astrology business functions
  • Designed and implemented an e-commerce multi-threaded astrology server for delivering on-line real-time astrology content to subscribers and non-subscribers
  • Created components for atlas lookup, web services interfacing and application reporting
  • Created application for dynamically generation of output in various formats (HTML, e-mail, Palm)
  • Created and maintained several databases in Paradox, Interbase/Firebird
  • Developed and on-line editing application for remote editorial team
08/98 – 07/99
Software Developer
SE Electronic, Moldrup, Denmark
Redesigned, for the company’s largest client, a Delphi application for testing vehicles on suspension, brakes and sideslip; the application captured data from hardware and displayed real-time information to operator. Application contained paradox database with customer, vehicle and test data.
  • Created applications in Delphi 2/3 and paradox and Object Oriented Methodology
  • Utilized threads, charts and third party tools to display real-time data. Created databases for customer/vehicle/test data
01/97 – 05/97
Software Developer/ Software Analyst (Internship)
Library of North Jylland, Aalborg, Denmark
Designed, for large library, a prototype of a new book ordering system; project included analyzing requirements and work procedures before implementing the prototype and providing extensive recommendations and documentation.
  • Analyzed library’s need for book ordering system using OOA, OOD, OOSE and EER
  • Developed prototype of book ordering system for NT 4.0 platform
  • Used Object Oriented programming and testing
09/96 – 05/97
Programming Teacher
Aalborg Youth School, Aalborg, Denmark

  • Provided instructed for children age 14-17 in programming using Pascal and Borland Delphi 2 in 2-hour weekly evening sessions

Education
09/97 – 05/98
BA Computer Science
Pacific Lutheran University, Tacoma, WA

Included courses in communication and broadcast Journalism.

01/95 – 05/97
Computer Science. Equivalent to BA/Master
Aalborg Computer Science School, Aalborg, Denmark

Classes: Programming, Machine architecture and operating systems, System development, Data modeling, System development methods, Programming methods, Communication and resource sharing, Object Oriented system development, Algorithm writing, Distributed systems and integration.

08/90 – 05/93
Personnel and Organizational Management
Management Academy, Viborg, Denmark

08/85 – 06/88
Business
Viborg Business School, Viborg, Denmark

One year short of American BA

Certificates
08/07-08/07
Hands On Technology Transfer
San Jose, CA

ASP.NET 2.0 Programming Using C#

Skills
C#
SQL Server
Delphi
HTML
Oracle
Mogens is a world class Software Architect, Developer, Leader, and Entrepreneur!- Dan Anderson, CIO, Cotere Corporation
contact
Contact info
  • Name: Mogens Nielsen
  • Address: 8272 Northwind Way
  • City: Orangevale, CA 95662
  • E-mail: mogens@mogensnielsen.com
  • Phone: 925-890-2976