TekGuard Online Guardian

 
Register  Login 
Dot

   HomeMail ServerOutlook PluginWeb MailUseful Links

Contact Us

EMail Interceptor Outlook PlugIn Technologies

Architecture:

EMail Interceptor Outlook PlugIn is an Anti-Spam Microsoft .Net C# Outlook PlugIn. Outlook PlugIn runs stand-alone, with EMail Interceptor Server, and even provides an open architecture for developers. The package includes source code for programmers that wish to customize or integrate our product into their environment. This version includes the Outlook PlugIn central source code.

 TekGuard Email Interceptor System Architecture
TekGuard Email Interceptor System Architecture

Technologies

The following outlines the technologies used to produce TekGuard Email Interceptor Outlook PlugIn:

  • MS Office Extensibility Services

  • SOAP Programming

  • ADO (Active Data Objects) Access with .NET

    • Datasets

    • Adapters

    • Command Builder

    • Navigating Multiple Related Tables in an ADO.NET Dataset

    • Multiple Result Sets

    • Populating a DataSet from Multiple DataAdapters

    • Identity and Autonumber Values

  • ADO & Binding Context

    • CurrencyManager and Binding Context

    • .NET DataGrid Control

  • Serialization

  • Outlook Application Events

  • Outlook COM Add-Ins

  • Threading

  • XML Usage

  • Windows "HTML Help" System

Windows Services

Email Interceptor can be run as an application or as a Windows Service. A service application is designed to be long running. As such, it usually polls or monitors something in the system. The monitoring is set up in the OnStart method. However, OnStart does not actually do the monitoring. The OnStart method must return to the operating system once the service's operation has begun. It must not loop forever or block. To set up a simple polling mechanism, you can use the System.Timers.Timer component. In the OnStart method, you would set parameters on the component, and then you would set the Timer.Enabled property to true. The timer then raises events periodically, at which time the service performs the monitoring.

The basic steps required to create and use a service include:

Dot Create a project using the Windows Service application template. This template creates a class for you that inherits from ServiceBase and writes much of the basic service code, such as the code to start the service.
Dot Write the code for the OnStart and OnStop procedures, and override any other methods that you want to redefine.
Dot Add the necessary installers for your service application. By default, a class containing two or more installers is added to your application when you click the Add Installer link: one to install the process, and one for each of the associated services your project contains.
Dot Build your project.
Dot Create a setup project to install your service, and then install it.
Dot Access the Windows 2000 Services Control Manager and start your service.

SOAP Programming

This application provides an asynchronous socket implementation utilizing the "System.NET.Sockets" namespace. It uses the "TCPListener" and "TCPClient" classes from the "System.NET.Sockets" namespace.

The "Server" class uses the "TCPListener" class to accept requests from new clients. Once the client is connected with the server, the server will send it the appropriate commands, then disconnect and listen for more clients. The "Client" class uses the "TCPClient" class. It connects to the server on the specified port and send the clients name to the server upon connection.

EMail Interceptor demonstrates how to create both ends of a TCP/IP socket connection between applications. Connected applications may run on the same machine, on machines connected by a local area network, or even machines communicating across the internet. A key feature of this method is that it uses threads and sockets in a non-blocking mode. A server is created that listens for clients to connect. Once a client connects it is added to a list of active clients. These connections are maintained and tracked internally as "Sessions".

ADO (Active Data Objects) Access with .NET

ADO.NET is an evolution of the ADO data access model that directly addresses user requirements for developing scalable applications. It was designed specifically for the web with scalability, statelessness, and XML in mind.

ADO.NET uses some ADO objects, such as the Connection and Command objects, and also introduces new objects. Key new ADO.NET objects include the DataSet, DataReader, and DataAdapter.

The important distinction between this evolved stage of ADO.NET and previous data architectures is that there exists an object — the DataSet — that is separate and distinct from any data stores. Because of that, the DataSet functions as a standalone entity. The DataSet is like an always disconnected recordset that knows nothing about the source or destination of the data it contains. Inside a DataSet, much like in a database, there are tables, columns, relationships, constraints, views, and so on.

A DataAdapter is the object that connects to the database to fill the DataSet. Later, the DataAdapter connects back to the database for updates, based on operations performed while the DataSet held the data. In the past, data processing has been primarily connection-based. Now, in an effort to make multi-tiered apps more efficient, data processing is turning to a message-based approach that revolves around chunks of information. At the center of this approach is the DataAdapter, which provides a bridge to retrieve and save data between a DataSet and its source data store. It accomplishes this by means of requests to the appropriate SQL commands made against the data store.

The disconnected nature of the DataSet makes life for developers more complex, but it greatly improves its versatility. Developers can now fill a DataTable with data taken from any data source—whether it’s SQL Server, a text file, or a mainframe—and process it with the same routines, regardless of its origin. The decoupled architecture based on the DataSet and the DataAdapter makes it possible to read data from one source and send updates to another source, should it be necessary. Developers have a lot more freedom when working with ADO.NET but also many more responsibilities.

Datasets

The most common way access method is through the DataSet, which is the primary object in ADO.NET. The DataSet object can be regarded as a virtual database, which will reside in the client memory. Unlike an ADO RecordSet, DataSet object provides disconnected access to underlying data source, i.e. any modification or insertion of data will not be made to underlying data source until we explicitly call the DataSetCommand object's Update method, thus improving scalability of the data source.

The DataSet represents a complete set of data including tables, constraints, and relationships among the tables. Because the DataSet is independent of the data source, a DataSet can include data local to the application, as well as data from multiple data sources. Interaction with existing data sources is controlled through the DataAdapter.

The XML-based DataSet object provides a consistent programming model that works with all models of data storage: flat, relational, and hierarchical. It does this by having no 'knowledge' of the source of its data, and by representing the data that it holds as collections and data types. No matter what the source of the data within the DataSet is, it is manipulated through the same set of standard APIs exposed through the DataSet and its subordinate objects.

DataSets, in turn, will talk to a data source through Managed Providers, using a DataSetCommand. Each Managed provider will effectively implement the methods of DataSetCommand, Command, Connection, DataReader etc, appropriate to their data source. In case of changing the data source, developers don't need to alter the program, just the appropriate connection string.

The DataSetCommand is a mediator between DataSet and Data Source. This object can be used to fill the DataSet with the appropriate tables and other stuff such as relations, constraints, etc. Once a DataSet is filled with tables, it can be accessed freely with DataSet object hierarchy.

Adapters

While the DataSet has no knowledge of the source of its data, the managed provider has detailed and specific information. The role of the managed provider is to connect, fill, and persist the DataSet to and from data stores. The OLE DB and SQL Server .NET Data Providers (System.Data.OleDb and System.Data.SqlClient) are part of the .NET Framework and provide four basic objects: the Command, Connection, DataReader and DataAdapter.

ADO.NET provides disconnected access to any data source for which a Managed Provider exists. A Managed Provider is nothing but set of objects that talks to a data source directly. Each RDBMS vendors will provide appropriate Managed Providers.

For now, Microsoft has provided two Managed Providers:

Dot SQL Managed Provider — Provides access to SQL Server version 7 or later.
Dot ADO Managed Provider — Provides access to any data source through OLEDB Providers. This is for compatibility with old data source for which no Managed Providers exists.

Each .NET data provider included with the .NET Framework has a DataAdapter object: the OLE DB .NET Data Provider includes an OleDbDataAdapter object, and the SQL Server .NET Data Provider includes a SqlDataAdapter object. All DataProvider objects expose the same set of properties and methods because they inherit from the DbDataAdapter abstract class. All the .NET data providers that are to be released in the future will include their own DataAdapter because the DataAdapter must know how to read from and update a specific data source. Except for their names and a few other details—such as how they deal with parameters—developers can use the OleDbDataAdapter and the SqlDataAdapter in exactly the same way.

The DataAdapter also resolves changes made to the DataSet back to the data source. The DataAdapter uses the Connection object of the .NET data provider to connect to a data source, and Command objects to retrieve data from and resolve changes to the data source.

The SelectCommand property of the DataAdapter is a Command object that retrieves data from the data source. The InsertCommand, UpdateCommand, and DeleteCommand properties of the DataAdapter are Command objects that manage updates to the data in the data source according to modifications made to the data in the DataSet. These properties are covered in more detail in later in this document.

The Fill method of the DataAdapter is used to populate a DataSet with the results of the SelectCommand of the DataAdapter. Fill takes as its arguments a DataSet to be populated, and a DataTable object, or the name of the DataTable to be filled with the rows returned from the SelectCommand.

The Fill method uses the DataReader object to implicitly return the column names and types used to create the tables in the DataSet, as well as the data to populate the rows of the tables in the DataSet. Tables and columns are only created if they do not already exist; otherwise Fill uses the existing DataSet schema. Column types are created as .NET Framework types according to the tables in Mapping .NET Data Provider Data Types to .NET Framework Data Types. Primary keys are not created unless they exist in the data source and DataAdapter.MissingSchemaAction is set to MissingSchemaAction.AddWithKey. If Fill finds that a primary key exists for a table, it will overwrite data in the DataSet with data from the data source for rows where the primary key column values match those of the row returned from the data source. If no primary key is found, the data is appended to the tables in the DataSet. Fill uses any TableMappings that may exist when populating the DataSet (see Setting Up DataTable and DataColumn Mappings).

Command Builder

The DataAdapter has four properties that are used to retrieve data from and update data to the data source. The SelectCommand property returns data from the data source. The InsertCommand, UpdateCommand, and DeleteCommand properties are used to manage changes at the data source. The SelectCommand property must be set before calling the Fill method of the DataAdapter. The InsertCommand, UpdateCommand, or DeleteCommand properties must be set before the Update method of the DataAdapter is called, depending on what changes were made to the data in the DataSet. For example, if rows have been added, the InsertCommand must be set before calling Update. When Update is processing an inserted, updated, or deleted row, the DataAdapter uses the appropriate Command property to process the action. Current information about the modified row is passed to the Command object through the Parameters collection.

For cases where the SelectCommand is dynamically specified at runtime, such as through a query tool that takes a textual command from the user, it may not be possible to specify the appropriate InsertCommand, UpdateCommand, or DeleteCommand at design time. If the DataTable maps to or is generated from a single database table, you can take advantage of the CommandBuilder object to automatically generate the DeleteCommand, InsertCommand, and UpdateCommand of the DataAdapter.

As a minimum requirement, you must set the SelectCommand property in order for automatic command generation to work. The table schema retrieved by the SelectCommand determines the syntax of the automatically generated INSERT, UPDATE, and DELETE statements.

The CommandBuilder must execute the SelectCommand in order to return the metadata necessary to construct the insert, update, and delete commands. As a result, an extra trip to the data source is necessary which can hinder performance. Optimal performance is achieved when commands are specified explicitly rather than using the CommandBuilder. The SelectCommand must also return at least one primary key or unique column. If none are present, an InvalidOperation exception is generated, and the commands are not generated.

When associated with a DataAdapter, the CommandBuilder automatically generates the InsertCommand, UpdateCommand, and DeleteCommand properties of the DataAdapter if they are null references. If a Command already exists for a property, the existing Command is used.

Database views that are created by joining two or more tables together are not considered a single database table. In this instance you will not be able to use the CommandBuilder to automatically generate commands and will need to specify your commands explicitly.

You might want to map output parameters back to the updated row of a DataSet. One common task would be retrieving the value of an automatically generated identity field (SQL Identity or Access Autonumber values) or time stamp from the data source. The CommandBuilder will not map output parameters to columns in an updated row by default. In this instance you will need to specify your command explicitly.

The automatic command generation logic generates INSERT, UPDATE, or DELETE statements for standalone tables without taking into account any relationships to other tables at the data source. As a result you may encounter a failure when calling Update to submit changes for a column that participates in a foreign key constraint in the database. To avoid this exception, do not use the CommandBuilder for updating columns involved in a foreign key constraint and instead, explicitly specify the statements used to perform the operation.

Navigating Multiple Related Tables in an ADO.NET Dataset

A dataset in ADO.NET is an in-memory representation of data that can contain multiple related data tables. Therefore it is often necessary to navigate these related data tables within a dataset. A typical .NET Windows application returns related records based on a selected record, as well as compiles aggregate information for related records using expression columns.

Because datasets can contain several related tables, understanding how to navigate from parent to child records and back is a fundamental task that may not be intuitive — especially in situations where you are trying to access data that is several tables deep in the relational hierarchy.

Here is a basic explanation of how to access data between two tables in a dataset that participate in a one-to-many relationship. After you select a data row, you can return its related records by calling the GetChildRows or GetParentRow method and passing it the appropriate data relation. Note that the GetChildRows method will return data in an array of DataRow objects, whereas the GetParentRow method will return a single data row.

An example would be adding code to an application that returns all the orders (child rows) for the customer selected in the combo box. Changing the selected customer in the combo box raises the ComboBox.SelectedIndexChanged event and fills the list box with the OrderID of each order for the selected customer. Then call the GetChildRows method, based on the customer selected in the combo box. All of the related records from the Orders table are assigned to the array of data rows returned by the function call.

Multiple Result Sets

If the DataAdapter encounters multiple result sets, it will create multiple tables in the DataSet. The tables will be given an incremental default name of TableN, starting with "Table" for Table0. If a table name is passed as an argument to the Fill method, the tables will be given an incremental default name of TableNameN, starting with "TableName" for TableName0.

Populating a DataSet from Multiple DataAdapters

Any number of DataAdapters can be used in conjunction with a DataSet. Each DataAdapter can be used to fill one or more DataTable objects and resolve updates back to the relevant data source. DataRelation and Constraint objects can be added to the DataSet locally, enabling you to relate data from multiple dissimilar data sources. For example, a DataSet can contain data from a Microsoft SQL Server database, an IBM DB2 database exposed via OLE DB, and a data source that streams XML. One or more DataAdapter objects can handle communication to each data source.

Identity and Autonumber Values

You can set a column in a DataTable to be an auto-incrementing primary key in order to ensure a unique value for each row in the table. However, you may have multiple clients for your application, and each of those clients may be working with a separate instance of the DataTable. In this case, you might end up with duplicate values between the separate instances of the DataTable. Because all your clients will be working with a single data source, you can resolve this conflict by letting the data source define the auto-incremented value. To accomplish this you use Identity fields in Microsoft SQL Server, or Autonumber fields in Microsoft Access.

Using the data source to populate an Identity or Autonumber column for a new row added to a DataSet creates a unique situation because the DataSet has no direct connection to the data source. As a result, the DataSet is unaware of any values generated automatically by the data source. However, with a data source that can create stored procedures with output parameters, such as Microsoft SQL Server, you can specify the automatically generated values, such as a new identity value, as an output parameter and use the DataAdapter to map that value back to the column in the DataSet.

Your data source may not support stored procedures with output parameters. In this case you may be able to use the RowUpdated event to retrieve an automatically generated value and place it in the inserted or updated row in the DataSet. This section includes a sample that shows how, with Microsoft Access 2000 or later, and using the Jet 4.0 OLE DB Provider, you can add code to the RowUpdated event to determine if an insert has occurred and to retrieve the auto-incremented value and store it in the currently updated row.

ADO & Binding Context

CurrencyManager and BindingContext

The ADO datasource does not have a position (unlike ADO or Delphi), which requires the creation of a distinct object which keeps track of the position and gets the data from the datasource and manages the updates to and from a control.

Since the data binding infrastructure (databinding) creates CurrencyManagers automatically there has to be a mechanism which makes it possible that every control bound to the same datasource (table) is synchronized. So there is a BindingContext object which keeps tracks of the CurrencyManagers and gives the same CurrencyManager to the controls bound to the same datasource. Of course the controls have to get the same BindingContext to get the same CurrencyManager.

.NET DataGrid Control

It is possible to extend the DataGridTextBoxColumn control so that you can custom-format your data for display and editing in a Windows Form DataGrid.

The DataGrid control is used to display data from the datasource defined by its DataSource property. There are tons of features provided by DataGrid in displaying data. Sorting each column when clicked on the name of the column in the DataGrid is one of the best features. We will concentrate on sorting XML.

Displaying XML data using DataGrid and achieving the same tasks is a little bit different. The reason is, as such an XML file when loaded by a DataGrid will only display the XML content as "text". On the other hand, data from a database table will have all the constraints (like what type of data — int, float, double,...) embedded into the DataGrid instance. So you don't have to emphasize in your code that "Col1 of my data is of type integer (and not text)".

But if you display XML you have to specify somehow or other that what type of data each of your column contains. Otherwise, all values are considered as type "text". Remember, you have to do this only if you want to get certain type-specific features of DataGrid. If you don't need these features, but simply want to display your XML in a DataGrid you don't have to worry at all. You can proceed in the same way as you do for displaying your SQL data.

Email Interceptor demonstrates how to add three "dots" to IP addresses and how to parse entries that have three "dots" inserted within them. This technique can be extended to any data type, including enumerations and user-defined types. To display data, you override the GetColumnValueAtRow method. To parse data, you override the Commit method.

Serialization

More information...

Outlook Application Events

More information...

Outlook COM Add-Ins

More information...

Threading

EMail Interceptor uses thread pooling to make efficient use of multiple threads. Many applications use multiple threads, but often those threads spend a great deal of time in the sleeping state waiting for an event to occur. Other threads might enter a sleeping state and be awakened only periodically to poll for a change or update status information before going to sleep again. Using thread pooling provides your application with a pool of worker threads that are managed by the system, allowing you to concentrate on application tasks rather than thread management. In fact, if you have a number of short tasks that require more than one thread, using the ThreadPool class is the easiest and best way to take advantage of multiple threads. Using a thread pool enables the system to optimize this for better throughput not only for this process but also with respect to other processes on the computer, something your application will know nothing about. Using a thread pool enables the system to optimize thread timeslices taking into account all the current processes on your computer.

The .NET Framework uses thread pools for several purposes: asynchronous calls, System.NET socket connections, asynchronous I/O completion, and timers and registered wait operations, among others.

XML Usage

XML is rapidly becoming the de facto standard for data exchange over the Internet and across multiple disparate platforms. The ability to populate database tables with data contained in XML documents is a common application requirement.

The Microsoft .NET XML Designer allows you to easily create schemas and datasets based on any table, stored procedure, or view that can be browsed in the data connection area of Server Explorer. Additionally, you can pick and choose individual columns if the entire data structure is not needed.

You create a schema based on existing data sources when the existing source contains the desired structure for your application. This is a quick way to build up your schema without having to define each individual element.

Schemas and datasets can also be created programmatically from any data source that you have access to at run time.

HTML Help

There has been an industry move to HTML-based Help files. This change, initiated by Microsoft, makes possible a wide range of new and better help systems.

The primary benefit of this new system is that it is truly "write once, use everywhere." Once an HTML Help file is built, which is actually a compiled program, it can be mounted on disk, on the Internet, or on an intranet. The same application can be launched from these platforms without any need for modification or intervention on your part.

Another benefit of HTML Help is simplicity. Previous flavors of WinHelp used RTF (Rich Text Format) files, identified by their .rtf extension. RTF files could be cantankerous, with their many curly brace delimiters, etc. It is much easier to edit an HTML file than an RTF file. Furthermore, Microsoft has announced that it will continue to support RTF-based Help files, and the HTML Help compiler/development kit even allows you to port existing RTF-based systems to the new standard. The Help files built using HTML are much more robust than RTF-based files. You can easily drop in nice little goodies like multimedia Java applets that add spit and polish to even the most prosaic help file.

Finally, one of the biggest benefits afforded by this system is the way it augments information searches. You can designate topics as members of a given type; this is similar to the concept of a set in mathematics. As an example, you can tag topics as being conceptual, tutorial, procedural, or any other category you care to imagine. Suppose you're writing a Help file for two closely related, but slightly different, products. By using these topic tags, you could write one Help file for both products. When the user sought help for one of the products, only those topics would appear. Partitioning in this manner makes for a much easier maintenance cycle too.

There are drawbacks to HTML Help, depending on your perspective. First, you must have Microsoft's Internet Explorer web browser installed on the target platform. Microsoft has a run-time product for non-IE users which eliminates this requirement. Second, every topic requires its own HTML file, which can make project maintenance somewhat of a pain. Third, the new standard supports popup windows (which aren't as elegant as those in the old WinHelp system) and they are somewhat difficult to implement. Last, the help author must know at least the basics of HTML, which probably most developers know already.

 

Copyright © 1996-2007 by VINFO, all rights reserved  Last modified: 08/02/2007   Terms Privacy Policy

wwwTekGuardCom - Anti-Spam .NET C# SMTP POP3 Mail Server Outlook PlugIn Search Engine Free Source Code