Click here to Skip to main content
15,662,659 members
Articles / Desktop Programming / Win32
Posted 31 Mar 2008


23 bookmarked

Building Image-Based Workflows with Windows Workflow Foundation

31 Mar 2008CPOL22 min read
Find out how to build a small document imaging workflow application using WF and Pegasus Imaging’s activity libraries (Part 1). Then discover how Pegasus’ activities may be used to add imaging workflows to MOSS using Visual Studio 2008 (Part 2).

This article is in the Product Showcase section for our sponsors at CodeProject. These articles are intended to provide you with information on products and services that we consider useful and of value to developers.

Image 1

Pegasus Imaging Corporation

With the release of Windows Workflow Foundation (WF), a new paradigm for workflow-based application development has been introduced. Instead of embedding business logic in code, workflows are built declaratively, within a visual designer, using building blocks of functionality called activities. Application developers must still depend upon domain experts to accurately describe the business processes they are automating. However, those experts don’t get left behind the moment the first line of code is written. Domain experts and stakeholders benefit from the WF graphical designer, which provides visibility into the application not possible with code; not only can they see the process followed, but they can validate the workflow for accuracy as well. For developers, WF provides significant amounts of infrastructure typically required for long-running workflow applications including process and thread agility, compensation, and transactions. In essence, WF provides a bridge between the development and business sides of the house while providing enough technical services to make the technology worthwhile. So worthwhile Microsoft has chosen to utilize WF as the workflow engine within Microsoft Office SharePoint Server (MOSS) and will be adding the engine in the next version of BizTalk.

Of course, it’s impossible to build complex workflows in WF without activities which actually do the work. Many useful activities ship with WF and .NET 3.0; however, most are general purpose and do not solve specific business problems. Microsoft has left the task of building domain-specific activities to those who know how to build them best — individual developers and domain experts. Within the imaging domain, Pegasus Imaging Corporation is drawing upon over 15 years of experience as an imaging toolkit provider to deliver WF activities. Over the coming year, activities will be published for document image cleanup, barcode reading and writing, and OCR for use in your own workflow applications or applications that host the WF runtime such as MOSS.

To demonstrate the power and flexibility of WF and Pegasus Imaging’s activity libraries, Part 1 of this article will show how to build a small, imaging workflow application using WF. Part 2 will show how Pegasus’ activities may be used to add imaging workflows to MOSS using Visual Studio 2008.

Part 1: Using Pegasus’ Imaging Activities to Build Workflow Applications

One common scenario where an imaging workflow makes sense is folder monitoring. In this scenario, a folder path is constantly monitored for newly created image files, typically coming from a scanner or other network location. The images arriving at this location might have some sort of damage caused by the capture process (the page could be skewed, the image is dirty due to specks of dust on the scanner bed, etc.) and requires cleanup prior to further processing. The biggest challenge in building an application to automate this cleanup is determining the actual cleanup required — not all cleanup steps are appropriate for all images. However, WF makes the actual processing definition very easy, especially once we have our application’s framework written.

Application Framework — Hosting the WF Runtime

To demonstrate a potential solution for the folder monitoring scenario, we’re going to build a small Sequential Workflow Console Application using Visual Studio 2008. This application will take a folder path to monitor from the command line, begin watching that folder for new files, and upon the arrival of each new file, run a cleanup workflow using WF. To build our application, we’re going to have to host the WF runtime ourselves, so we have some setup and coding to get through before we hit the visual designer.

To get started, launch Visual Studio 2008, and bring up the New Project dialog. Under the Visual C# heading, select Workflow as the project type, and then select Sequential Workflow Console Application as the project template. Enter a name for your project, and we’re on our way.


Figure 1: Defining the WF Project

Once the project has been created, you might see a few things new to you, particularly if this is your first time using WF. We’re not going to go into the details of all things WF, but there are a few things to be aware of. The Sequential Workflow Console Application template caused two classes to get created — Program and Workflow1 — and contain the application’s main entry point and default WF workflow respectively. As shown in Listing 1 below, the generated Main function inside Program creates an instance of the WorkflowRuntime — the runtime engine which manages our workflows — and creates a new instance of our default workflow, Workflow1, returning an instance of WorkflowInstance to represent our loaded workflow. Finally, the loaded instance is started, and our workflow begins execution. One word of caution though: the instance is created on a worker thread, so the call to Start will return immediately. As a result, we have to use a wait handle, signaled in the WorkflowCompleted or WorkflowTerminated handler, to ensure we don’t kill the process by returning from Main before the workflow has an opportunity to complete.

Listing 1: Visual Studio Generated Main using Sequential Workflow Console Application Template

namespace ImageCleaner
    class Program
        static void Main(string[] args)
            using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())
                AutoResetEvent waitHandle = new AutoResetEvent(false);

                workflowRuntime.WorkflowCompleted += 
                    delegate(object sender, WorkflowCompletedEventArgs e) 

                workflowRuntime.WorkflowTerminated += 
                    delegate(object sender, WorkflowTerminatedEventArgs e)


                WorkflowInstance instance = 


With our very brief introduction to WF in hand, it’s time to build something real. To start, we’re going to need to rearrange the default code in Main so we’re able to handle the path passed via the command line and set up the folder monitor. Luckily, Microsoft has made watching folders very easy for us by including the FileSystemWatcher type with the .NET Framework. The only thing we need to do to watch the folder is set the path we wish to monitor, then install an event handler on the Created event. Other events are available for modifications, deletions, and attribute changes, but we’re going to ignore those for now. Once the FileSystemWatcher is initialized, we can instantiate our WorkflowRuntime instance, register our WorkflowCompleted and WorkflowTerminated handlers, and finally start watching for new file creations. Listing 2 below shows the modifications made to Main, but don’t worry when you see some of the Visual Studio generated pieces missing. They’ll make their reappearance soon.

Listing 2: Modifications to Main to Process Command Line and Setup Folder Monitor

// our workflow runtime instance - this object must be disposed
static WorkflowRuntime workflowRuntime = null;

static void Main(string[] args)
    // validate our command line for proper usage
    if (args.Length != 1)
        Console.WriteLine("Usage: ImageCleaner.exe <Full Path of Folder to Watch>");

    // get the path to watch and ensure it is a valid path
    // before we move forward...
    string folderToWatch = args[0];
    if (System.IO.Directory.Exists(folderToWatch) != true)
        Console.WriteLine("Command line argument \'" +
            folderToWatch + "\' is not a valid path");

    // create a file system watcher to begin watching the folder 
    // passed via the command line
    FileSystemWatcher fileSystemWatcher = new FileSystemWatcher();
    fileSystemWatcher.Path = folderToWatch;

    // setup our listener to monitor changes
    fileSystemWatcher.Created +=
        new FileSystemEventHandler(OnFileSystemWatcherCreated);

    // initialize the runtime in this program
    using (workflowRuntime = new WorkflowRuntime())
        // ensure we handle the completed and terminated events
        workflowRuntime.WorkflowCompleted +=
            new EventHandler<WorkflowCompletedEventArgs>(OnWorkflowCompleted);

        workflowRuntime.WorkflowTerminated +=
            new EventHandler<WorkflowTerminatedEventArgs>(OnWorkflowTerminated);

        // start watching
        fileSystemWatcher.EnableRaisingEvents = true;

        // the watcher will continue watching until we are told to stop watching
        Console.WriteLine("Press \'q\' to quit watching the folder...");
        while (Console.Read() != 'q')
            ; // do nothing - continue to process and wait

Now that Main is complete, we need to focus on fleshing out the actions required whenever the Created event is fired. When using the FileSystemWatcher type, it is extremely important to think through how you’ll know the file is completely present at the folder location being monitored. The event fires as soon as the first byte gets written to the file, followed by OnChanged events to notify us of additional file actions like appends. As a result, you have to use some sort of heuristic to know the file has been completely written to disk. Listing 3 below shows our handler and the heuristic chosen — code that looks for an exclusive lock on the file being written. Once we acquire the lock, we know the file is available. If we don’t acquire the lock within the timeout specified (we chose 5 minutes), we exit the loop and throw an exception. This heuristic doesn’t support many common cases—file writes which take longer than 5 minutes for one—so you should modify as appropriate to handle your specific needs.

Listing 3: Handlers and Workflow Instance Creation

static void OnFileSystemWatcherCreated(object sender, FileSystemEventArgs e)
    // Get the current time to test timeout
    DateTime t0 = DateTime.Now;

    // By default, the file is not completely available
    bool fileAvailable = false;

    // Stop checking the file if the Timeout duration is exceeded
    TimeSpan Timeout = new TimeSpan(0, 5, 0);
    while (DateTime.Now - t0 < Timeout)
            // If the file can be opened exclusively, then it is available
            using (FileStream fs = new FileStream(e.FullPath, FileMode.Open,
                FileAccess.Read, FileShare.None))
                fileAvailable = true;
        catch (IOException)
            // The file is still being written, wait and try again

    // If the file is still not available, then a timeout occurred
    if (!fileAvailable)
        throw new ApplicationException(
            string.Format("File lock timeout: {0}", e.FullPath));

    // the file is, we can process that file

Once the file is available, we call ProcessFile, which is shown in Listing 4. As promised, the code that creates the WorkflowInstance representing our workflow makes its reappearance. The code is largely unchanged from the code Visual Studio generated for us as we began this project. The primary difference is we pass the path of the newly created file (InputFile), and the path to the “cleaned” file we wish to create (OutputFile), to our workflow instance. These values (InputFile and OutputFile) are used to set properties contained in the ImageCleanerWF type, and the names much match exactly, or an exception will be thrown. Finally, because our workflow is creating a new file, we need to return immediately if the newly created file in the folder is the one we just created.

Listing 4: ProcessFile

// our wait handle to ensure we don't exit the application prematurely
static AutoResetEvent waitHandle = new AutoResetEvent(false);

// the last file we created via the workflow
static string lastWorkflowOutputFile;

static void ProcessFile(string file)
    // we don't want to process the file we just created
    if (file == lastWorkflowOutputFile)

    // make sure our wait handle is non-signaled

    // determine the file extension of the file so we can 
    // create a new one for our sample output
    string fileExtension = Path.GetExtension(file);
    string newFileText = "_clean" + ".pdf";

    // create a dictionary to pass the path to our workflow
    Dictionary<string, object> myParams = new Dictionary<string, object>();
    myParams.Add("InputFile", file);
    myParams.Add("OutputFile", file.Replace(fileExtension, newFileText));

    // create a new instance of our workflow and begin to process
    WorkflowInstance instance =
        workflowRuntime.CreateWorkflow(typeof(ImageCleaner.ImageCleanerWF), myParams);

    // wait until we're signaled to continue - we only want to process one workflow
    // at a time for now

An important aspect of our application we haven’t touched upon is threading. We’re not going to go too deep into the topic; however, you should know WF is not built with a specific threading model in mind. Instead, it allows the host application to dictate how a WorkflowInstance is to run via a scheduler. By default, the DefaultSchedulerService is installed when the WorkflowRuntime is created, and results in each workflow instance running on a thread from the thread pool. This default is perfectly fine for applications which are starting workflows from the application’s primary thread. However, if I’m starting a workflow from a thread pool thread (not the application’s primary thread), using the DefaultSchedulerService would cause a second thread to be claimed from the thread pool, which is not very scalable. To fix this, you would use the ManualSchedulerService to run the workflow on the calling thread. It is common to host the WF runtime using this technique in ASP.NET applications, and if you look closely, our folder monitor could also benefit from this optimization. FileSystemWatcher events are handled on a thread pool thread, so our current implementation causes a second thread pool thread to be claimed. Fine for demonstration purposes, but limiting when you’re trying to scale.

With our threading discussion out of the way, finally, Listing 5 shows the handlers for WorkflowCompleted and WorkflowTerminated. We must handle these events so our wait handle can be properly signaled. Otherwise, a deadlock would result.

Listing 5: WorkflowCompleted and WorkflowTerminated Handlers

static void OnWorkflowCompleted(object sender, WorkflowCompletedEventArgs e)
    // get the names of the files we just processed\created
    string inputFile = (string)e.OutputParameters["InputFile"];
    string outputFile = (string)e.OutputParameters["OutputFile"];

    // store the name of the output file, so we make sure we don't create again
    lastWorkflowOutputFile = outputFile;

    Console.WriteLine("\"" + inputFile + "\" has been cleaned and output as \"" +
        outputFile + "\"");

static void OnWorkflowTerminated(object sender, WorkflowTerminatedEventArgs e)
    Console.WriteLine(e.Exception.Message + ": " + 

Enough of the framework goo! Actually, if you take a step back and think about what we’ve accomplished so far, we’ve done an awful lot with little code. We’ve got a working application that monitors a folder of our choosing, and hosts a robust workflow engine that takes our workflow definition, loads it into memory, creates a thread for our workflow to run on, and schedules it for execution. Whew! But I digress; now it’s time for the fun part — dragging and dropping our way to imaging workflow nirvana.

Defining the Imaging Workflow with the WF Designer

Of all the benefits WF brings to the table, perhaps the greatest advantage is the bridge built beween the technical and business sides of the house. It is possible to build WF workflows in code; however, a picture is worth a thousand words–representing a business process in a graphical designer, using flowchart-like layout, allows a level of visibility simply not possibly in code. So, let’s get started…

When we created our Sequential Workflow Console Application project, the project template created a code-based workflow named Workflow1, which we later renamed ImageCleanerWF. When you double-click on our workflow in the solution explorer, you’ll see the design surface loaded and a list of WF activities available in the toolbox.


Figure 2: The WF Design Surface

To define a workflow in WF, we organize reusable pieces of functionality called activities and link them together through the inputs and outputs of those activities. The activities available out of the box are fairly general in nature, and while useful, do not solve domain-specific problems. However, third-parties like Pegasus Imaging are encouraged to supplement those out of the box activities with their own domain-specific solutions. Figure 2 also shows a partial set of activities delivered by Pegasus Imaging with our ScanFix Xpress 5.1 document image cleanup toolkit. For a complete list of the activities included in ScanFix Xpress 5.1 and other Pegasus Imaging toolkits, please visit us here

Now, let’s get started building our imaging workflow.

In our scenario, we’re watching a folder for new incoming files from a scanner or other source. Those images need some sort of cleanup — in our case deskew and despeckle — then we want to save the output as a PDF file. To get started, we’ll drag some activities onto our designer surface, starting with the LoadImage activity. Then, because ScanFix Xpress 5.1 only works on bitonal (i.e. black and white) images, we’ll need to add an IfElse activity to check for a proper image type. If the image loaded can be processed by ScanFix, we’ll process it using the Deskew and Despeckle activities, then save to disk as a PDF using the SaveImagePDF activity. Otherwise, we’ll skip the cleanup, and save. With the activities arranged in the proper manner, the designer surface will look like that shown in Figure 3 below.


Figure 3: Designer Surface before Binding

At this point, the control flow of our business process has been defined through our arrangement of WF activities on the design surface. But, we’re not done yet. If you look at Figure 3 closely, you’ll see a series of exclamation marks with drop downs telling you something isn’t quite right. This feedback is provided by individual activities through something called a validator. In our case, the validators for our activities are telling us to link our activities together so the data flows between them. We link our activities together using data binding.

Data binding allows us to specify, in a declarative fashion, where a specific piece of data should come from. To demonstrate how binding works, we’ll set the FileName property on the LoadImage activity. If you recall from Listing 4 above, we passed two paramaters to the workflow — InputFile and OutputFile. We want the FileName property to be set to the value passed in the InputFile parameter, and this is done via the dialog shown in Figure 4. To launch this dialog from Visual Studio, select the LoadImage activity, then in the property designer, double click on the Bind Property icon in the FileName header box.


Figure 4: Bind Dialog for FileName Property

At this point, intuition takes over and explanation becomes secondary. All you have to do is select the InputFile item in the tree, and click OK. Voila! We’ve now linked the FileName property of our LoadImage activity to the InputFile parameter. For those following along in Visual Studio, you’ll also see the exclamation mark go away in the designer once the FileName property has been set. This occurs because the validator for the LoadImage activity knows the activity has been properly initialized.

Now that we have the LoadImage activity ready to go, we can continue with the other activities by linking together inputs and outputs. We won’t go through each data bind, but there is one important aspect of this workflow we haven’t covered — the Condition activity for the ifElseBranchActivity1. The validator for this activity requires the Condition property be set so that a decision can be made on which branch to go down. It is possible to define this condition via code, or declaratively using a rule. Just to keep things simple, and to avoid a deep discussion of declarative rules in WF, we’re going to use code. All you do is select Code Condition in the Condition drop-down, and specify a method signature. The actual code for this condition is shown in Listing 5.

Listing 6: Code for Condition Evaluation

private void OnBitonal(object sender, ConditionalEventArgs e)
    if (this.loadImage.OutputImage.PixelFormat
        == System.Drawing.Imaging.PixelFormat.Format1bppIndexed)
        e.Result = true;

As shown, the method determines if the image loaded is a 1-bit image. If it is, we’ll execute those activities contained within ifElseBranchActivity1; otherwise, those activities in ifElseBranchActivity2 will run. With this last piece in place, we end up with a complete, graphical representation of our business process — and all for 8 lines of code that wouldn’t have even been required if we used a declarative rule. How’s that for efficient process design!


Figure 5: Completed Workflow

Part 2: Using Pegasus’ Imaging Activities in SharePoint via Visual Studio 2008

Part 1 showed how there are minimally two pieces required for a WF workflow to run — an application host for the WF runtime, and the workflow itself. For those who are writing their own WF applications from scratch, you’re going to have to write the code to host the WF runtime in some form or fashion. You might choose to host the runtime in a console application like we did in Part 1, a Windows service, or perhaps even an ASP.NET application. However, if your goal is to write workflows for existing applications that host the WF runtime for you, we don’t have to worry about runtime hosting at all. All we have to worry about is developing our workflow and being a good citizen while integrating with the application we’re targeting. Today, our primary target is Microsoft Office SharePoint Server 2007 (MOSS), which uses WF for its workflow functionality. In the future, our target might be BizTalk, or applications that decide to use WF as their workflow technology. To demonstrate how WF and Pegasus Imaging’s activities can be used in an application that hosts the WF runtime for us, we’re going to rebuild the workflow from Part 1 targeted for MOSS. We won’t go into all the details of how WF and MOSS work, but we’ll whet the appetite just enough to show how WF makes workflow definition within MOSS easy.

The following sections assume you have a properly configured MOSS development environment. This typically includes an installation of MOSS either on your development machine or some readily accessible location. For the steps to succeed, the project template will need access to MOSS .NET assemblies — primarily Microsoft.SharePoint.

Setting up the Project

Just as we did in Part 1, launch Visual Studio 2008, and bring up the New Project dialog. Under the Visual C# heading, select Workflow as the project type, and then select SharePoint 2007 Sequential Workflow as the project template. Enter a name for your project, click OK, and the project template wizard will startup.


Figure 6: New Project Wizard for SharePoint 2007 Workflow

Prior to Visual Studio 2008, deployment of SharePoint workflows to the server was a bit cumbersome, but the new wizards simplify the process greatly. First, the project template wizard will prompt you for the name you wish to use for the workflow within SharePoint along with the SharePoint server you’ll use to debug and deploy the workflow.


Figure 7: Workflow Name and Project Site

Once the name and project site have been established, the template will ask you if you want Visual Studio to automatically associate the workflow with a library or list in SharePoint. We won’t go into a detailed explanation of the SharePoint object model here, but you do need to know that you have to tie your workflow to one of the two. SharePoint uses this association to pass data to the workflow when it runs. For example, if you associate the workflow to the Documents library, the workflow will run on a specific item within that library. Also, workflows in SharePoint log details about their operation using a history list and track tasks per workflow participant, so you can choose which of these you wish to use for your workflow in the same dialog.


Figure 8: Associate Workflow with SharePoint Lists

Finally, the last step of the project template wizard will ask how you’d like the workflow to be started. There are three options — you can start it manually, you can start the workflow when a new item is created in the list specified, or you can start the workflow when an item is modified. For simplicity, we’re just going to have our workflow start manually.


Figure 9: SharePoint Conditions to Start Workflows

At this point, Visual Studio has everything it needs to generate the project, so we can click Finish, let the tool do its thing, and begin concentrating on what we want the workflow to do.

Default Workflows and Deployment

Once the project has been generated, you have everything you need to build and deploy your workflow without the worry of hosting the WF runtime. A default workflow is created as part of the project along with a couple of XML files that contain SharePoint deployment information and a strong name key file to sign the assembly. The first XML file, feature.xml, exists to inform SharePoint of a new feature being installed into the system and allows us to set a title and description for that feature. This file also contains a reference to workflow.xml, which lists the details for the workflow being installed. As you can probably imagine, there’s an awful lot of detail being left out of this description, but luckily we don’t have too much to worry about. The wizard took care of things for us. For now, and to keep things simple, we’re not going to rename the default workflow to something more meaningful. If we did rename the workflow, we’d have to worry about updating the project in several places, so for now, we won’t confuse matters.

To get started, make sure the WF designer surface is visible by double-clicking on Workflow1.cs in the solution explorer. The first thing you’ll notice when the designer is visible is an activity named onWorkflowActivated1 of type OnWorkflowActivated already present on the surface. This activity acts as the bridge between SharePoint and your custom workflow, and through its WorkflowProperties property, provides information regarding the item and list the workflow should operate upon. The default designer surface is shown in Figure 10 below.


Figure 10: SharePoint Default Workflow

At this point, we have a workflow which doesn’t do anything, but is fully deployable to our SharePoint site. To do so, all we have to do is hit Ctrl + F5 within Visual Studio 2008 (or F5 if you wish to run in the debugger) and let the tools do their work. Once your workflow is deployed, the system will navigate to the site you specified when we created the project so you can select the item you wish to use as input to the workflow. To run the workflow, you need to select Workflows from the item drop-down as shown in the figure below.


Figure 11: SharePoint Site Ready for Workflow Execution

Clicking on Workflows will bring up a list of workflows which can be run for this SharePoint item. All you need to do to run is click ImageCleanerSP. When the workflow is complete, the item’s view in its library will show a status of Completed for ImageCleanerSP.


Figure 12: ImageCleanerSP Completed

Before we continue, let’s recap the steps we’ve covered so far:

  • We started by creating a new SharePoint 2007 Sequential Workflow project. We provided information via the wizard to associate our workflow with a SharePoint library or list.
  • Next, we investigated the project layout, briefly examined features.xml and workflow.xml, and learned the importance of the OnWorkflowActivated activity.
  • Finally, we learned how to deploy our workflow to the SharePoint server using F5 deployment, and how to manually run our workflow for a specific item in our SharePoint library or list.

As you can see, there is quite a bit to understand, but this time, there was no code to write — only a bit of configuration to complete. But now that’s done, we get to the fun part.

Adding Imaging to the Workflow Mix

To build our workflow, we’ll follow the same drag-and-drop sequence we used in Part 1 to build our workflow for the command-line application. However, when you add activities to the designer surface, we must make sure all activities are added after the OnWorkflowActivated activity. Once we add our activities to the surface, it will look like that shown in the figure below. Just as we saw in Part 1, we’re not done after we organize the activities on the design surface. We also have to connect the inputs and outputs of each activity, and this is exactly the same as it was in Part 1, with a slight twist.


Figure 13: Initial SharePoint Workflow

If you recall from Part 1, we received the input file name and output file name as parameters in the call to CreateInstance. The LoadImage and SaveImagePDF activities bound to those parameters to resolve the FileName property each activity requires to run. But in our SharePoint implementation, we don’t have a command line application passing parameters. Instead, we have WorkflowProperties from the OnWorkflowActivated activity that provides information required for the workflow to run, including the name of the item we’re to process. There is a bit of work to transform the item passed by SharePoint into an input the LoadImage activity can accept (LoadImage only loads from a file in the initial release). For brevity’s sake, we won’t explain the details here, but to learn more, have a look at the samples which accompany this article. We basically take the SharePoint item, write the item to a temp file we can load from, do our imaging work, then upload the result back to SharePoint.

Once the additional activities have been added to transform SharePoint items into inputs acceptable for Pegasus’ activities, your designer should look like the figure below.


Figure 14: Completed SharePoint Workflow

To run our completed workflow, simply use F5 deployment, select your item, and run the workflow as we did before. If everything works as it should, you should have a new document in your SharePoint library which has been cleaned (if 1-bit) and converted to PDF. To continue your exploration into WF, you might try modifying the workflow slightly by adding in an additional activity. All you have to do is drag the desired activity to the surface, hook its inputs and outputs, and redeploy.

Wrapping Up

We have covered an awful lot, and I hope this explanation of WF and examples of what can be accomplished have piqued your interest in the technology. Ultimately, WF is only going to be as successful as the development community decides to make it — by hosting the WF runtime in their applications and developing domain-specific activities. Pegasus Imaging Corporation has committed to deliver imaging activities as the market demands, so please let us know what types of activities you’d like to see. In the meantime, check out our current offerings here.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Written By
Program Manager Pegasus Imaging Corporation
United States United States
Since early 2007, Casey has been responsible for building Pegasus Imaging’s Atlanta engineering team. He also contributes to and supervises product design, implementation, quality, and release for several products. In previous positions at Pegasus, Casey has contributed to software development, technology integration strategies, development organizational structure, and product architecture. In addition, he has served as a technical lead for Autodesk, working on DWF applications including Autodesk Design Review. Casey earned a Masters in Business Administration with Honors from the University of Tampa, and a Bachelor of Science in Information Systems (Magna Cum Laude) from the University of South Florida.

Comments and Discussions

QuestionWonderful Coding Pin
jegannath from Coimbatore18-Dec-13 3:39
jegannath from Coimbatore18-Dec-13 3:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.