Archive
  • Ripsaw Series
  • Accelerated C++

Parks Computing

Pedagogy for the Autodidactic Programmer

Welcome to the personal web site of Paul M. Parks. This is where I keep my web development skills current while sharing snippets of code, utilities, libraries, and what little development wisdom I've gathered in my career. It's also where I show off my family, talk about my hobbies, or just pontificate from time to time.

If you find the articles interesting, or if you'd like more information on a particular topic, let me know [paul@parkscomputing.com].

Buy Some Stuff!

Ever since I was in high school, I've been drawing this little guy you see pictured here:



My wife has named him "Smart Sam". I finally applied for a trademark on the drawing, but the funny thing about trademarks is that you have to actually be involved in some kind of trade. So, now you can own your very own Smart Sam Stuff! I opened up a shop on Cafepress a while back but never bothered to publicize it until now. So, go grab a t-shirt, or a coffee mug, or wall clock, or maybe a onesie for Junior. If there is some other bit of clothing or gadget you'd like to see this beautiful face stamped on, let me know!

Labels: , ,

Posted February 3, 2010 0 Comments

New Contract

I've been signed to a new contract with my current client, on a new project working on self-service grocery again. This one should carry me well into next year, but I'm always keeping my eyes open just in case.

Labels: , ,

Posted December 4, 2009 0 Comments

GWA Football: 2009 State Champs!

The George Walton Academy Bulldogs are GISA state football champions for the 2009 season! They defeated Deerfield-Windsor School in Albany, GA in the class AAA championship game yesterday, with a final score of 42-24. I am so proud of all the parents, grandparents, students, alumni, faculty, and friends who made the trip down to Albany, some of whom (like us) left Thanksgiving vacation early to be there. It felt like a home game: the fans were loud, the cowbells were clanging, the band played, the Dark Army ran up and down by the field, and the cheerleaders nearly fell over from doing high-kicks for every point scored. Hats off to Deerfield-Windsor for playing tough to the very end, and never giving up. Congratulations to both teams for making it to the championship.

Next year George Walton Academy will move to the GHSA, and will be returning most of their current team, so we look forward to more exciting football next season.

GO DAWGS!

Labels: , ,

Posted November 28, 2009 0 Comments

Act Now Before Price Increase

Talk about a hot sale price! If you're getting a little tired of waiting around, now might be a good time to act. Of course, you probably won't care about the price increase when the time comes.

Labels:

Posted November 24, 2009 0 Comments

Ripsaw Library Implementation

In this installment of the Ripsaw article series we'll start putting some actual code into an implementation of the COM interfaces that we specified in the last article. For those of you who just joined the series, Ripsaw is a log viewer for Windows, similar to the Unix tail utility. This series of articles details my rewrite of the application from the ground up.

So far, we have a test script implemented in JavaScript (or JScript, if you want to be really picky) that is intended to exercise our COM interfaces. There is a base interface that represents a generic log that a client might wish to follow, a more specific interface representing a file, and a dispinterface declaring the events that a Ripsaw library object can fire. In this article we'll start implementing these interfaces, with the goal of providing just enough functionality to allow us to execute the test script.

(Click the "Read the full article" link below for more.)
I'm implementing this project with Visual Studio 2010, which is still in beta as of this writing. You may download the solution, projects, and source code if you'd like to follow along, but if you haven't downloaded and installed the 2010 beta yet, you'll need to adapt the code to your IDE and C++ compiler of choice if you wish to build it (I recommend Visual Studio 2008).

Implementing the COM Interfaces

To briefly recap the design, there will be a Ripsaw COM library that exports objects representing sources of updates, and a Ripsaw client (such as a log viewer or a script) will create objects that fire events whenever the source changes its status. Now that we've specified the interfaces, it's time to implement them just enough to run the test script that we created in the last installment. I like to use ATL to implement the low-level, boilerplate code that's necessary for IUnknown and IDispatch, though once the wizard is done I usually take over and move all the generated code around. I've never liked round-trip development with code generators.

There are three interfaces (for now) that need to be implemented: IRipsawObject, IRipsawFile, and _IRipsawEvents. The first interface is the base interface for not only IRipsawFile but other potential interfaces that might be added later on as the library is developed. That means I want to create the implementation in such a way that I can easily extend it to other sources of data that Ripsaw might use. Unfortunately, if I took the code that the ATL wizard spat out, I'd have a really difficult time reusing it. I need to split the ATL code into a generic interface implementation and a concrete object implementation.

In ATL, when implementing an object derived from IDispatch, you usually get a class definition that looks like this:

class ATL_NO_VTABLE RipsawFile : 
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<RipsawFile, &CLSID_RipsawFile>,
public ISupportErrorInfo,
public IConnectionPointContainerImpl<RipsawFile>,
public CProxy_IRipsawEvents<RipsawFile>,
public IDispatchImpl<IRipsawFile, &IID_IRipsawFile, &LIBID_RipLib, /*wMajor =*/ 1, /*wMinor =*/ 0>

That works fine, until it doesn't. If you ever want to derive a class from RipsawFile, you're going to run into problems. What I usually do is replace IDispatchImpl with my own template class that derives from IDispatchImpl and implements the interface that my object is going to expose. In this case, I want my object to expose both IRipsawFile and IRipsawObject, so I'll start by creating a generic template implementation of the base IRipsawObject interface. The template will be defined in a header file named IRipsawObjectImpl.h.

#pragma once
#include <comutil.h>

template <class Root, class T, const IID* piid = &__uuidof(T),
const GUID* plibid = &ATL::CAtlModule::m_libid,
WORD wMajor = 1, WORD wMinor = 0,
class tihclass = ATL::CComTypeInfoHolder>
class IRipsawObjectImpl :
public Root,
public ATL::IDispatchImpl<T, piid, plibid, wMajor, wMinor, tihclass>
{
public:
/* Internal public implementation */
IRipsawObjectImpl() : isOpen(FALSE)
{
}

virtual ~IRipsawObjectImpl()
{
}

public:
/* IRipsawObject interface */
HRESULT STDMETHODCALLTYPE get_Name(
__out BSTR* pName)
{
if (!pName)
{
return E_INVALIDARG;
}

{
ObjectLock(this);
*pName = NULL;
}
return S_OK;
}

HRESULT STDMETHODCALLTYPE get_IsOpen(
__out VARIANT_BOOL* pRetVal)
{
if (!pRetVal)
{
return E_INVALIDARG;
}

LONG retVal = 0;
InterlockedExchange(&retVal, isOpen);
*pRetVal = retVal ? VARIANT_TRUE : VARIANT_FALSE;
return S_OK;
}

HRESULT STDMETHODCALLTYPE Close()
{
_bstr_t fileName;

VARIANT_BOOL tmpIsOpen = VARIANT_FALSE;
get_IsOpen(&tmpIsOpen);

if (tmpIsOpen)
{
ObjectLock(this);
put_IsOpen(FALSE);
BSTR name = 0;
get_Name(&name);
Notify_Close(name);
SysFreeString(name);
}

return S_OK;
}

HRESULT STDMETHODCALLTYPE WaitForUpdate(
__in ULONG timeout,
__out BSTR* pNewData)
{
if (!pNewData)
{
return E_INVALIDARG;
}

*pNewData = NULL;
return S_OK;
}

protected:
/* Internal protected implementation */
HRESULT STDMETHODCALLTYPE put_IsOpen(
__in VARIANT_BOOL value)
{
InterlockedExchange(&isOpen, value);
return S_OK;
}

virtual HRESULT Notify_Open(BSTR fileName)
{
return S_OK;
}

virtual HRESULT Notify_Close(BSTR fileName)
{
return S_OK;
}

virtual HRESULT Notify_Update(BSTR updateData)
{
return S_OK;
}

LONG isOpen;
};


There is quite a bit going on in this template, so let's go over its various parts.

The Root Type

If you look back at the example class definition for RipsawFile, you'll see that it derived from CComObjectRootEx<CComMultiThreadModel>. This is because, when the wizard generated the code, the user selected the option to generate an object that was free-threaded. This is an implementation detail of the object, not of the interface, so that's the first thing we need to abstract away from our generic implementation of the IRipsawObject interface. The problem, however, is that CComObjectRootEx provides a lot of useful types that we should take advantage of, such as methods for guarding blocks of code that need to be serialized when accessed from multiple threads. What I decided to do was make Root a template parameter and derive the implementation from that type. The concrete implementation may provide any type for this parameter as long as it implements the definitions expected from that type, such as ObjectLock.

The IDispatchImpl Type

This is a class that is provided by ATL to implement the ugly details of IDispatch. While the average COM developer can (usually) implement a workable IUnknown rather easily, implementing IDispatch is a good deal more complicated. We know, based on our IDL, that IRipsawObject is derived from IDispatch, so this is an implementation detail that's appropriate for our generic implementation. Classes that derive from IRipsawObject will also pick up the IDispatch implementation. All of the template parameters to IRipsawObjectImpl after Root are passed along to IDispatchImpl.

Notify_Open, Notify_Close, and Notify_Update

All objects that implement the IRipsawObject interface are expected to fire events to notify clients about open, close, and update events. How these events are generated is an implementation detail best left to the concrete object implementation, but I may need to fire events from the generic implementation. I decided to create stub methods that will be overridden in the concrete implementation.

The Rest of the Code

The remainder of the header is devoted to minimal implementations of the methods and properties defined in the IRipsawObject interface. Most of these will be overridden in IRipsawFile and other derived interfaces. Note that the implementations use InterlockedExchange and ObjectLock to serialize access to internal data. This is because we want to allow the generic implementation to work for free-threaded object implementations, which may be called by more than one thread at a time. The ObjectLock type is expected to be provided by the Root template parameter.

I Could Use Some More Cowbell

This still isn't enough code to create a usable IRipsawFile implementation, but since that's the default interface that we need to expose from our COM object we'll have to derive another generic implementation from IRipsawObject that will let us finally execute our test script. We'll tackle that template class in the next installment in the series. If you want to cheat and peek ahead, look at the file IRipsawFileImpl.h in the source code.

Labels: , , , ,

Posted November 23, 2009 0 Comments

Android is Nice


This post was created with Android, though not with a phone. I'm planning to upgrade to a Motorola Droid next month, so I've been playing with the SDK and getting familiar with the OS using the emulator. It's pretty nice, except the emulator is painfully slow. I may write a couple of applications for it, since I've been needing a good excuse to fiddle with Java again.

Labels: ,

Posted November 18, 2009 0 Comments

GWA Band Marches at Walt Disney World

To cap off a fantastic competition season, the George Walton Academy Marching Bulldog Band marched in a parade at Walt Disney World's Magic Kingdom. My elder daughter, Madeline, is a member of the color guard, and my wife and I traveled down to Florida and watched the band march. It was a wonderful experience.

I tried to get some pictures (click the photo below to see them), but they didn't turn out so well. Fortunately another band dad got some good video of the band (below).

Most of the band parents were running along the parade route, taking pictures and video, and when we ran out of sidewalk we just fell in behind the band. In the second video below you'll see Jennifer and I sharing a laugh with some other parents about plowing our way through the crowd to keep up with the band. I sure hope Disney lets us come back!





Labels: , , ,

Posted November 14, 2009 0 Comments

It's Not a DOS Prompt!

I've been hearing this a lot lately (you know who you are), so rather than pull all of you aside privately and give this lecture, I thought I'd do it once, publicly. You're not running a DOS prompt.

When you click on that shortcut that says, "Command Prompt" in Windows XP, or you run cmd.exe from the "Run" box, you're not starting a "DOS prompt." What you are starting is a command line interface, or just "command line" if you prefer. If you haven't actually run COMMAND.COM, it's not DOS!.

So, please, next time don't tell me to run your favorite utility "at the DOS prompt." Let DOS rest in peace.

The first person to call Powershell a DOS prompt will get the lecture in real time.

Labels: ,

Posted November 13, 2009 0 Comments

Ripsaw COM Interface, First Pass

In this installment of the Ripsaw article series we'll finally get to write some code. We've already gotten a pretty good idea about how we want to implement the core Ripsaw library, so now we're going to define enough of the COM interface that we can create a simple test script that will eventually be used to exercise the library.

(This is a long article, so click the "Read full article" link below for more.)
To be callable from script, the object's interface should be OLE-automation compatible, so we'll declare it as such in our IDL:

[
oleautomation,
uuid(2D486A73-E912-4078-9F38-678226E4A0BD),
dual,
pointer_default(unique)
]
interface IRipsawFile : IDispatch
{
}

The interface is also derived from IDispatch to support late-binding, which means that scripting languages can discover the methods and properties of the interface at runtime.

Before we can write the test script we'll need decide what basic methods a Ripsaw object needs to implement. A single Ripsaw object will represent a connection to a file, and the object will fire events whenever the file changes so that listeners can take an action based on the update (change the display, parse the update, etc.). At a minimum, then, we need to be able to open a file and close a file.

interface IRipsawFile : IDispatch
{
[id(1)] HRESULT Open(
[in] BSTR fileName,
[out,retval] VARIANT_BOOL* pSuccess);

[id(2)] HRESULT Close();
}

The Open method will accept a path to a file and return a Boolean indicating the success or failure of the method call. The Close method will close any existing connection, and we won't bother to check for a return code from that method.

Now, it would probably be useful to be able to query a property of the object for the name of the file to which it is connected, and perhaps another property to check if the object is currently opened or closed. Let's add those properties:

interface IRipsawFile : IDispatch
{
[id(1), propget] HRESULT Name(
[out,retval] BSTR* pName);

[id(2), propget] HRESULT IsOpen(
[out,retval] VARIANT_BOOL* pRetVal);

[id(3)] HRESULT Open(
[in] BSTR fileName,
[out,retval] VARIANT_BOOL* pSuccess);

[id(4)] HRESULT Close();
}

Okay, not bad so far. We can open a file, query for the status, query for the name, and close the file. That's no good if we can't catch events fired by the object. We need to define a dispinterface that specifies the events that the object can fire at its clients:

[ uuid(6671C129-C761-42F3-AB2F-D12C33D95160) ]
dispinterface _IRipsawEvents
{
properties:
methods:
[id(1)] HRESULT Open([in] BSTR fileName);
[id(2)] HRESULT Close([in] BSTR fileName);
[id(3)] HRESULT Update([in] BSTR updateData);
};

The Open event fires when a file is opened successfully, of course. Close fires when a file is closed, and Update naturally fires when the file changes. The Update notification will also pass along the data that was gathered from the last file update.

Okay, based on those interfaces, our script might look like this:

var ripsaw = WScript.CreateObject("PMP.RipsawFile", "ripsaw_");

if (ripsaw)
{
ripsaw.Open("testfile.log");

if (ripsaw.IsOpen)
{
WScript.Echo("Ripsaw file is open: '" + ripsaw.Name + "'");

/* Uh-oh... */

ripsaw.Close();

if (!ripsaw.IsOpen)
{
WScript.Echo("Ripsaw file is closed: '" + ripsaw.Name + "'");
}
else
{
WScript.Echo("ERROR: Ripsaw file reported open");
}
}
else
{
WScript.Echo("ERROR: Ripsaw file reported closed");
}
}
else
{
WScript.Echo("ERROR: Failed to create Ripsaw file");
}

function ripsaw_Open(fileName)
{
WScript.Echo("ripsaw_Open(" + fileName + ")")
}

function ripsaw_Close(fileName)
{
WScript.Echo("ripsaw_Close(" + fileName + ")")
}

function ripsaw_Update(data)
{
WScript.Echo("ripsaw_Update(" + data + ")")
}

First of all, I'll explain what all this means for those of you that are new to JavaScript — err, JScript — under Windows Script Host (WSH). The first line creates a COM object based on its ProgID, or programmatic identifier. This is a string that resolves, through the registry, to a GUID that uniquely identifies a COM library that provides the requested functionality.

The second parameter to CreateObject tells the scripting engine to wire events provided by the object to functions that start with the specified prefix. In our script, we want to handle the Open, Close, and Update notifications, so we created functions named ripsaw_Open, ripsaw_Close, and ripsaw_Update to catch these events. Since they all start with the "ripsaw_" prefix, the script engine will call them when any of the corresponding events are fired by our object.

If you looked carefully at the script, you noticed that I inserted a comment: "Uh-oh." That's because the script has no way of sitting and waiting for an update other than looping in a tight loop, and that is almost never a good idea. The script would waste CPU time doing nothing while we wait for the file to be updated. In other languages we have access to message loops, WaitForSingleObject, and other means of yielding CPU until an event occurs, but not in WSH. We need something better. What we'll do is add a method to our object which, when called, will do absolutely nothing until an update occurs. We'll call it WaitForUpdate.

interface IRipsawFile : IDispatch
{
[id(1), propget] HRESULT Name(
[out,retval] BSTR* pName);

[id(2), propget] HRESULT IsOpen(
[out,retval] VARIANT_BOOL* pRetVal);

[id(3)] HRESULT Open(
[in] BSTR fileName,
[out,retval] VARIANT_BOOL* pSuccess);

[id(4)] HRESULT Close();

[id(5)] HRESULT WaitForUpdate(
[in] ULONG timeout,
[out,retval] BSTR* pNewData);
}

The first parameter is a timeout, in milliseconds, so that the function will return if no updates arrive in the specified amount of time. We'll take a page from Win32 and say that a value of 0xFFFFFFFF will tell the method to wait forever. If the timeout expires, the method will return a null BSTR; otherwise, the return value will be the new data from the last file update. We'll modify the heart of our script to call the new method:

      WScript.Echo("Ripsaw file is open: '" + ripsaw.Name + "'");

var timeout = 5000;
WScript.Echo("Waiting for data...");
var newData = ripsaw.WaitForUpdate(timeout);

if (newData)
{
WScript.Echo("Waited for data:", newData);
}
else
{
WScript.Echo("Timed out after waiting for data for " + timeout + " milliseconds");
}

ripsaw.Close();

In a real script we'll probably call WaitForUpdate in a loop, but for now we'll just wait for one update before exiting.

Generalizing the Interface

This looks good for files, but perhaps later on we'd like to extend Ripsaw to listen to other sources of events, like the Windows Event Log (the first Ripsaw actually did that for a while, but I backed out the feature to focus on files). It would be a good idea to go ahead and factor out the common methods and properties into a base interface that could be extended by other event sources.

[
oleautomation,
uuid(0270FDFF-56AF-42ec-9971-AE8DCB0DAB36),
dual,
pointer_default(unique)
]
interface IRipsawObject : IDispatch
{
[id(1), propget] HRESULT Name(
[out,retval] BSTR* pName);

[id(2), propget] HRESULT IsOpen(
[out,retval] VARIANT_BOOL* pRetVal);

[id(3)] HRESULT Close();

[id(4)] HRESULT WaitForUpdate(
[in] ULONG timeout,
[out,retval] BSTR* pNewData);
};

[
oleautomation,
uuid(2D486A73-E912-4078-9F38-678226E4A0BD),
dual,
pointer_default(unique)
]
interface IRipsawFile : IRipsawObject
{
[id(10)] HRESULT Open(
[in] BSTR fileName,
[out,retval] VARIANT_BOOL* pSuccess);
};

Later on, if we define an interface for listening to event logs, we'll derive that interface from IRipsawObject and define an Open method specific to event logs. We'll also assign a special ProgID to event log objects so our client applications can create them:

var ripsaw = WScript.CreateObject("PMP.RipsawEventLog", "ripsaw_");

Next Steps

Now we have an interface, and we have a script to exercise that interface. What we need next is some real code to implement the interface and a binary that the script can load and run. We'll start on a preliminary implementation in the next article. Until then, you can download the IDL and the script file and look them over. Please leave a comment with any suggestions you may have.

Labels: , , ,

Posted November 9, 2009 0 Comments

Refining Ripsaw's Design

In my last entry in the Ripsaw article series, I discussed some of the design goals for Ripsaw. In this article I'll flesh out the design a little more and discuss specific implementation possibilities.

To bring you up to speed, Ripsaw is a log-viewing utility for Windows that I initially wrote about six years ago, but never released widely. I've decided to rewrite it and discuss each step of the rewrite here.

Implementing the API

The initial Ripsaw implementation was a monolithic (albeit small) application, but this time I want to separate the log-reading mechanism from the log-viewing UI. There are times when I'd like to use the log-reading capabilities of Ripsaw from JavaScript or PowerShell, and I'd also like to write more than one viewer application around the log reader. There are at least three possible technologies for this API:
  1. C DLL
  2. COM
  3. .NET
Okay, there are a lot more that I could choose from, but for the machines I'm targeting these are the most likely candidates. A DLL exporting C functions would be the simplest to write and would be easy to integrate with most programming languages, but in order to use that sort of API from JavaScript (or similar languages) I'd have to write a COM wrapper. A .NET implementation would be COM-callable, but it would require having .NET installed on the target machine, and sometimes the systems I debug don't have .NET installed (hard to believe, I know, but true). COM, however, is ubiquitous on Windows, fairly straightforward, can be used from JavaScript with ease, and is easily callable from .NET if I had a reason to do so later on. Therefore, I'll implement Ripsaw's non-UI behavior in a COM library.

What About Registration?

I'd still like to be able to run Ripsaw from a USB drive without having to install anything on the machine I'm debugging or examining, which means I'd like to avoid the hassle of having to run regsvr32 on the target machine before I can use Ripsaw. Fortunately, there is registration-free COM, which will let me run a Ripsaw viewer that loads the Ripsaw COM library with the aid of a manifest. This will let me keep a viewer and the Ripsaw library in a directory on my USB drive so I can just plug it in and run the viewer without having to register or install anything.

Supporting Scripting in the Viewer

Since Ripsaw's log-reading capability will be implemented in COM, I'll be able to write JavaScript apps that can watch for data from a log file, and these scripts can either parse the data or take actions based on the data. Not only would this be useful apart from a viewer, but it might also be useful inside a viewer. I could define script actions to operate on log lines, highlight them, parse them, etc., while I'm watching a log in the viewer. To take advantage of this I'll add JavaScript support to the Windows Ripsaw viewer, along with the capability to load pre-defined scripts that act on log data.

Viewer Implementation

In keeping with my requirement for making Ripsaw light and portable, I don't want to use .NET for the main viewer implementation. I might create a Windows Forms or WPF viewer later on once .NET becomes more widespread on the systems I mainly work on (point-of-sale terminals and servers for grocery stores), or for when I'm debugging in a controlled environment, but for the main console and GUI viewers I'll use C++ as the development language. COM is still a bit of a pain to use from C++, at least compared to JavaScript or .NET, but it won't be too bad.

Multiprocessor Support

Fortunately, the newer POS terminals and servers that are being installed today have multi-core processors, and I definitely want to take advantage of that today. I want to architect the core Ripsaw library to take advantage of multiple threads of execution spread across multiple processors or processor cores. I already write multi-threaded, multi-process systems for these machines, and these systems tend to be heavily instrumented, so sometimes I'm watching several different log files at once during a testing or debugging session. I don't want to slow a system down too much when I start up the viewer, and loading down one core of a multi-core processor with a log viewer would certainly be a bad thing.

Okay, Can We Start Coding Now?

Actually, I already have been doing some prototyping, which helped me decide on the details I've described above. Now that I've sorted out some of the lower-level requirements, in the next article I'll start defining the Ripsaw log-reader COM interface.

Labels: , ,

Posted November 6, 2009 0 Comments

Busy Until January

I've been assigned to a new project through the end of 2009 so I'm unavailable for now, but if I'm not picked up for an extension I'll be looking for new projects in 2010. Take a look at my resume and let me know if I might be a good fit for any contract or consulting opportunities in your organization. I'm currently W-2 with a small consulting company, but I'm open to 1099 or corp-to-corp starting next year.

Labels: , ,

Posted November 2, 2009 0 Comments

George Walton Academy: Champs!


Congratulations to the George Walton Academy Marching Bulldog Band for taking first place in the Group VI Open division of the USSBA Southern States band competition this past weekend in Chattanooga. My elder daughter, Madeline, is a member of the color guard. They put on a fantastic show, winning caption awards for overall effect, percussion, and music. They also won the Marine Corps Esprit de Corps award. Way to go, band!

Update: Photos here.

Labels: , ,

Posted October 27, 2009 0 Comments

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]