Title: Advanced Programming Tips 5
1Advanced Programming Tips (5)
2Plugins are easy now, so well do NIMP the Net
Image Manipulation Program
- You start with a pre-defined interface (say
IPictureTransformer) which is the contract
between the host and the plug-ins. - The host dynamically loads some assemblies (DLLs
or EXEs), - It can inspect and see which types in the
Assembly support the Interface, and instantiate
them, - And then it can use them...
3An Interface for NIMP plugins
using System using System.Drawing namespace
PlugIns public interface IPictureTransformer
string MenuCaption get
void Transform(Image im)
4A plugin class which implements the interface,
and that we will load dynamically
public class ToGrey IPictureTransformer
public ToGrey() public string
MenuCaption get return Make it Grey"
public void Transform(System.Drawing.Image
im) Bitmap bm (Bitmap) im
for (int i 0 i lt bm.Height i)
for (int j 0 j lt bm.Width
j) Color p
bm.GetPixel(j, i) int g
(int) (255 p.GetBrightness())
bm.SetPixel(j, i, Color.FromArgb(255,g, g, g))
5When the host initializes
void loadPlugIns() string
dllNames Directory.GetFiles(".", ".dll")
foreach (string fileName in dllNames)
string assm
Path.GetFileNameWithoutExtension(fileName)
string className "PlugIns." assm
try
IPictureTransformer theplugin
(IPictureTransformer)
AppDomain.CurrentDomain.CreateInstanceAndUnwrap
(assm, className)
//
if the object instantiation worked, we have a
plugin. // dynamically add it to the tools
menu addToToolsMenu(theplugin.
MenuCaption, theplugin)
catch // ignore this dll we
cannot instantiate a plugin here.
6Notice how we extend the menu on the fly for each
plugin. The plugin itself supplies the caption.
void addToToolsMenu(string caption,
IPictureTransformer theplugin)
ToolStripMenuItem mi
new ToolStripMenuItem(caption, null, new
EventHandler(toolClicked)) mi.Tag
theplugin // save the instantiated object,
well call it later
toolsToolStripMenuItem.DropDownItems.Add(mi)
void toolClicked(object sender, EventArgs
e) ToolStripMenuItem mi
(ToolStripMenuItem)sender
IPictureTransformer thePlugIn
(IPictureTransformer) mi.Tag Image
im pictureBox1.Image
thePlugIn.Transform(im)
pictureBox1.Refresh()
7Some notes
- This works, but weve assumed only one plug-in
per dll, and that its name is the same as the
name of the dll. More sophistated options are
available. - We test for the interface by instantiating and
casting. Well it could be more elegant. - We keep the instantiated object alive in case the
user requires its functionality. And we store it
in the menus tag! ?
8Observations
- Any class supporting the right kind of interface
can be used as a plug-in. - There is nothing special that you need to do in
the hosted class. - Usually these classes implement some interfaces,
and the host works via the interface rather than
via some concrete type. - We need one statement in the host to load and
instantiate a plug-in object. - Plug-in technology lets you open your core
application to add-on extensions.
9Observations 2
- It is easy to write good apps and good modules
in the small. - Its in the large that represents our 21CN
challenges in CompSci. - The winners are going to be the guys who have
better ways of tying together components. - Late binding loading plugins, is a potent tool
for this.
10The Holy Grail for Deployment
- Web apps greatest advantage is that they can be
centrally updated, - All issues of having old and new versions of apps
in the field, with incompatibilities, disappear, - So a goal is to write Rich Applications that can
update parts of themselves (like Acrobat or Media
Player), - Interfaces and plug-in dlls get you quite far on
this quest you can easily instant upgrade a
product by providing plug-ins. - But 1-Click deployment may be an even better way
to accomplish this. (1-Click doesnt open the
market to outside providers creating the
plug-ins.)
11Just for fun Create new classes on the fly
- It is even possible to create your own new
assembly on the fly, emit some class definitions
into the assembly (in MSIL intermediate code), - and then load and create the class instance and
run it. - See the wild example under Help,
CreateInstanceAndUnwrap and look at the crazy
example they give for the first constructor
12Creating new Classes on the fly could have a real
use
- Algorithms needing very high-performance (e.g.
DNA matching, looking up IP addresses against
tables in a router or firewall, searching large
datapools for regular expressions) can be made
much more efficient if you generate a specific,
specialized search for the data you currently
have. - get the search parameters or search expression,
- dynamically generate the MSIL opcodes (like the
sample in the help file) to create a class that
does only this search, but does it fast! - Load and run it. The Just-In-Time compiler
(JIT) can take account of processor /
architetural features, and even statistical info
that is unavailable to traditional complier
back-ends to produce very high performance native
code.
13Tracing and Debugging
- The framework already provides
- Debug and Trace classes with static members to
Write, WriteLine, Flush, Close, etc. - These publish their output to listeners.
- Listeners can be plugged in via the config file,
- Some listeners (e.g. TextWriterTraceListener
which writes to a file are already implemented.)
14The client writes to Trace or Debug
using System.Diagnostics class myClass
public bool logon(string user)
Trace.WriteLine(Logon event for user )
- All Trace/Debug objects are thread-safe.
- Without configuration, they write to the console.
- Debug output goes away when you compile for
Release
15You can plug in other existing listeners via a
new node at the bottom of the config file
ltconfigurationgt . ltsystem.diagnosticsgt
lttrace autoflushtruegt ltlistenersgt
ltadd name"myListener"
type"System.Diagnostics.TextWriterTraceListener"
initializeData"c\myLi
stener.log" /gt lt/listenersgt
lt/tracegt lt/system.diagnosticsgt lt/configurationgt
- You can write and configure your own listener
which sends trace data to a web service, or a
database, or sends an SMS to the administrator.
16The administrator can disable the default
listener too
- ltlistenersgt
- ltremove name"Default" /gt
- lt/listenersgt
17Filtering your output messages
- Tracing and Debugging use 5 levels of severity,
18Filtering Trace and Debug Output
- You can create TraceSwitch objects to provide
categories of tracing - TraceNetworking
- TraceObjectCreation
- TraceNewLogins
- These automatically initialize their severity
level from the configuration file, - Your code tests a switch and conditionally writes
the trace/debug message,
19The configuration file allows the administator to
initialize the switches
ltconfigurationgt ltsystem.diagnosticsgt ltswitchesgt
ltadd nameTraceNewLogin" value"3"
/gt lt/switchesgt lt/system.diagnosticsgt lt/config
urationgt
20The code creates a switch instance and tests
against it
static TraceSwitch theSwitch new
TraceSwitch(TraceNewLogin", blah blah") //
severity level initialized from config file
public bool logon(string user) if
(theSwitch.TraceInfo)
Trace.WriteLine(Logon event for user)
21Programming Tips
- If youre going to need tracing, statistics, or
debugging, and want to report errors back to some
central collection, - And you want it to be easily configurable by the
administrator, - Use the built-in Trace and Debug classes in
System.Diagnostics.
22Principles and generalizations
- Components, deployments, and programs are relying
more on flexible configuration options that are
late bound - The general plug-in architecture for tracing and
debugging allows an application to publish
sophisticated diagnostic and tracing capabilities
in an abstract way, with event categorization and
verbosity settings - The admin can provide specialized listeners to
hook your programs up to the local management
systems - Programs that publish through Trace can use the
same listeners to process data via the same
management system (think NNOC). - For example if Nunit published test results to
Trace, we could set a configuration in the daily
build to plug in our own listener class. That
could greatly simplify our processing of the
results of the daily build. (Presently we use a
DOS batch file that calls on grep to search
through output text files! )