(How)
C^# You Are - 14 October 2007
This week's questions are related to program design and architecture. Unlike
other areas the answers here can vary. There is not necessary one correct
answer.
- You have been tasked writing an application that should process a directory of files
once a day. You know how to write console applications but you've heard about
Windows services that can run continuously. Which is a better option here? Answer
This question comes up a lot in the forums. Many people lean toward a Windows
service but rarely is the service the correct answer. For this particular
case a console application scheduled to run daily using Task Scheduler is the better
option.
A Windows service (generally) runs when the system starts and continues to run until
it shuts down. Because no user's may be logged on at the time a service always
runs in the context of a fixed user account. Because of this services have
special requirements when it comes to security and interacting with users.
Furthermore a service can be more difficult to test and debug because a service
is controlled by the Service Control Manager which is part of the operating system.
In general when you need to run a task at a fixed interval (say at least an hour)
then a service just takes up processing and memory resources for no viable benefits.
Creating a standard console or Windows application and then scheduling it to run
at a fixed interval using Task Scheduler is the better approach. This is even
more important when the interval is configurable or has some special rules determined
during installation.
- You need to query some information from the system such as memory, processor and
drive information. What can you use to accomplish this? Answer
If your answer included various classes in .NET such as Directory
or Registry then give yourself partial credit. That is what
they are there for. But there is a lot of information that is stored in the
registry or on disk that is not documented or difficult to interpret. Furthermore
there is a lot more than is only accessible via Win32 calls. Having to pull
information from different sources can be an indication of a design problem.
This is especially true if you do not isolate your application from the various
sources through a standardized interface.
WMI is the Windows Management Implementation of the CIM system
adopted by other operating systems. WMI gives you broad access to a lot of
disparent information in the operating system including hardware information such
as drives, processors and USB devices; software information such as installed applications,
version information and machine information; and current settings such as memory
usage, processor utilization and open network connections. WMI does this through
a common interface where you can write SQL-style queries to retrieve information
of interest (including filtering). The returned objects map to a schema defined
by CIM and implemented by Microsoft. For example the Win32_Processor
class contains information about the processor(s) available on the machine while
Win32_Process contains information about a running process.
In general when you want to know something about a machine you should start with
WMI. The hardest part of working with WMI is getting a hang of the SQL-style
syntax and determining what code to write. The best source of sample scripts
for WMI is available at the Microsoft Script Center.
- You need to create an application that can be deployed to hundreds of machines in
a company. The application will evolve rapidly early on to meet new demands
but will eventually stabilize. It is important that all clients have access
to the latest version. The application relies on a database back end for data
storage but most of the application will consist of what-if scenarios against the
data. What type of application would fit in well here? Answer
In general you have the choice of a Windows or a web application. With a Windows
application you have full access to the client's hardware capabilities so you can
create impressive applications with immense functionality. However keeping
a Windows application up to date requires a lot of work on the part of the IT department
especially if the application is running under a limited user account. With
each user running their own copy of the application the database will have to support
a lot of different connections. This increases the workload of the database
server. However as connections are generally short-lived the bulk of the workload
will be done on the client machine where scalability is not an issue.
A web application gives everyone access to the latest version of the application
with no deployment costs (on the client). However you are now limited to the
functionality available through the browser. Feature rich applications, while
possible, require a lot more development time and effort than equivalent Windows
applications. A web application can provide a single point of contact to a
back end database server so, through caching, the workload of the database server
can be lowered at the cost of memory on the web server. However as the web
server has to do more work and keep more information in memory the web server will
become less scalable.
Trade-offs no matter which way you go. For this particular application a web
application would be a better choice if deployment and database server connections
is of paramount concern. Using ActiveX controls, heavy client-side scripting
and caching can reduce the workload of the web server to increase scability as needed.
If the what-if scenarios take significant processing power to perform then caching
or background processing through web services could also be done.
- You need to create a Windows application that does intensive graphics calculations
and displays animated scenes. A web application is not really an option but
deployment must be simple as the users are not technical, do not have administrative
rights to their machines and want to have the latest version of the application
when it becomes available? Answer
First you tell the IT department to set up an SMS server to allow your application
to be remotely deployed. You then explain how to push your application out
to the client machines. After the IT department laughs in your face you realize
a Windows application, while required, may not be the best option...
Indeed there is a hybrid approach between web and Windows applications, a smart
client. A smart client has the deployment behavior (for the most part) of
a web application while still being a Windows application. A smart client
is installed, without administrative privileges, by going to a particular URL in
a web browser. The user is then prompted to download and run the latest version
of the software. The application can place an icon on the user's desktop and
their Start Menu to allow them to run the application as though it is a normal Windows
application. Each time the application runs it can determine if a new vesrion
is available and optionally auto-install itself. This almost eliminates the
deployment issues. Because the application is a standard Windows application
you gain all the benefits thereof.
Sound too good to be true? It is, a little. The caveats. A smart
client runs as a normal user account and therefore can not do everything, security
wise, that a normal application can. Registry access outside the current user,
for example, is not allowed. A smart client can also not install any components
that require administrative privileges such as assemblies that must go into the
GAC or the .NET framework. Other than that a smart client is a great option
when deployment of a Windows application is a big concern. For the scenario
given above this is probably the best option.
- You are writing a Windows calculator application for use in school. While
you intend to expose new functions as they are needed you only have about 20 functions
right now. You would prefer to be able to release new "functionality packs"
as they become available without having to update the main program. What is
the best way to accomplish this task? Answer
The addin model is the best approach. With the addin model you write your
application to be extended over time through third-party code. Your application
exposes core objects through an object model. Generally the object model consists
of interfaces that represent each core object in your application. The object
model is housed in a standalone assembly that can be used by addins. Additionally
you define interfaces to allow your application to find and load addins when the
application runs. Each addin, when loaded, extends the functionality of the
application. Depending upon the design of the object model the addin may be
able to modify only existing functionality or add entirely new functionality.
Addins introduce reliability, security and usability concerns. A poorly written
addin can corrupt the data. A hacker could create an addin that sends sensitive
data to a machine. When designing an extensible application these issues must
be resolved.
For this particular scenario the object model does not need to be that big so security
and usability might not be an issue. A simple interface, IFunction, could
allow the addition of new functions which can calculate a variety of different values.
Depending upon how the application handles parameters and displaying of the results
the interface might just be used to calculate the value. This would also partially
eliminate the reliability concerns.
Designing through interfaces or abstract base classes is a good approach in all
applications. Over time new implementations may be needed and interfaces/base
classes make changing implementations a lot easier.
- You have written a client-server application for issue tracking. The client
displays issues to a user and allows them to add or edit issues. When a change
is made the request is sent to a back end server for processing and, ultimately,
updating of a database. What mechanism(s) can you use to communicate between
the client and the server? Answer
There are many options available here: HTTP, web services, MSMQ, remoting, etc.
Which one is best depends on several factors including security, knowledge and environment.
For mixed environments web services or HTTP are probably the best option.
For Windows environments MSMQ and remoting are good choices.
A web service is the best option in general because it is standardized, all the
code already exists, holes in firewalls have already been made and it is relatively
easy to debug. The downside to web services is that it uses XML which can
result in larger network utilization for even the simplest requests. Furthermore
web services don't do as well with data that can't be easily serialized, such as
binary data, unless you can guarantee that both the client and the server are running
in the same environment where byte ordering and alignment are not issues.
HTTP can be used in the rare cases where web services are not an option. HTTP
can also prove to be faster since it avoids the overhead of SOAP processing.
However the sending and receiving code must be written and tested before it can
be used. The code must be written for each environment that the client/server
will run on.
MSMQ is a good choice for corporate applications where an MSMQ server can be run.
MSMQ has the nice advantage of being able to send messages to an application even
when the application is not running. While not always useful, when it is MSMQ
is a clear winner. The biggest downside to MSMQ is that receiving notification
of the completion of a task from the server can take a long time (perhaps days).
For some things, like adding an issue, the user needs to know shortly after submitting
the request. In these cases MSMQ is not the best option.
Remoting is a great communication option when you must share .NET objects between
a client and a server. Both the client and the server must be running .NET
and configuration can be a little difficult but remoting eliminates the need for
writing the serialization/deserialization code. Furthermore remoting allows
you to work with regular .NET objects rather than with representations of the objects
(like XML). The downside is that not everything can be remoted, remoting (while
faster than web services) is still slow and requires careful planning to ensure
that only the required data is remoted.
For the scenario given remoting or web services is probably the best option.
You would gain nothing by using a custom HTTP communication channel. When
an issue is submitted the user needs a confirmation pretty quickly so MSMQ is not
an option. Ultimately it boils down to whether you want to run a web server
for hosting the web service or use remoting.
- You have written a Windows service to mirror a set of directories in the file system.
When an error occurs you want to notify the user (if any) and provide access to
the log of file activity. How can you do this? Answer
There are a few golden rules in Windows. One of the biggest is that you can
not modify a UI element on any thread other than the thread that created the UI
element. Up near there is the rule that a Windows service can not have a UI.
Old Windows developers will mention something about the "interact with desktop"
option that Windows has for services. Alas this option was never really recommended
and has been deprecated in Vista. Vista changed, yet again, how services work
so interactive services are all but useless.
To associate a UI with a service you need to remember two things. Firstly
each user gets their own session and desktop. Services run in either the first
session/desktop (pre-Vista) or a special session/desktop (Vista). Cross-desktop
communication is very limited and rarely an option. Secondly services are
designed to run without having a user log on. Hence there might not even be
a desktop to display any messages to. Even if there is each service must be
run as a specific user. Since the user can be any user account it would be
a security violation to allow a user to view another user's desktop.
Because of the above situations the only real option you have is to create two applications:
the service and a Windows UI application. The service can be started when
the operating system starts up or manually. The Windows application would
be added to a user's Startup group in the Start Menu. The user can close the
application if they want but then they would receive no messages. Whenever
the UI application starts it would use some communication technique such as remoting,
named pipes or RPC. An attempt would be made to connect to the service.
If successful the application and the service would communicate through the out-of-band
channel to share information such as the messages. While the service does
support custom commands through the SCM they are generally too limited to be useful.
When application terminates it drops its connection to the service.
While the service is running it would send messages to any channel that is open.
This allows messages to appear in any application associated with any logged in
user. The biggest obstacle to this solution is determining what happens when
the service is stopped or restarted. In this case the service will not be
able to enumerate the desktops and determine if the application is running.
Instead an external indicator would be the better approach. A globally, named
semaphore would be a good choice. The semaphore is created by the application
when it starts (and each application increments the count). If the service
finds the semaphore count to be greater than zero then at least one application
is running. Finding the running instances is another issue entirely...
- You want to log various information about your application as it runs. For
whatever reason you need to create a specific file (named by date) for each day
your application runs. You have a multi-threaded application and performance
is important How can you implement the log? Answer
Ideally you will use Trace to implement logging. By creating
a custom trace listener (deriving from TextWriterTraceListener)
you can change the underlying file to write to eacch day. Trace
is threadsafe. There is a known initialization issue with trace listeners
when configured through a configuration file. It is possible, although rare,
that a listener would be initialized twice if the initialization occurs on two separate
threads simultaneously.
If, for whatever reason, you can not use Trace or decide to write
your own then be aware that it can be called on multiple threads simultaneously.
Therefore your writer must be threadsafe. While instance variables are thread
safe, creating an instance of a writer, opening a file, writing to the file and
closing it is not. The problem is not with the writer but with the target
file. A file can only be written to by one thread at a time. Therefore
there can be only one writer at a time. All access to the target file must
be serialized to avoid exceptions. Using a static class like Trace
can resolve the issue by serializing trace messages. This will have a negative
performance impact. Alternatively queuing the message will allow the tracing
code to have minimal performance impact. A secondary thread can then be used
to write out the trace messages in the background.
|
|