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].

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

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

Design Goals for Ripsaw

In this installment of the Ripsaw project series I'll sketch out some of my design goals for the new version of Ripsaw, and the rationale for those goals.

Split the App Into DLLs

The original Ripsaw was a 32-bit Windows application contained in a single executable. This made it easy to carry around on a USB drive and run on machines that I needed to debug. The downsides were that I had to link the C runtime library into the executable, and application was one monolithic entity that couldn't be easily extended.

For the new Ripsaw I'm going to build it to use the C runtime in a DLL. For most of the machines I work with this will require carrying around the C runtime distributable, but I'm willing to do that as long as I don't have to install anything on the terminal. I'm not completely up to speed with side-by-side assemblies; I prefer to just place the VS 2010 C runtime DLL in the same directory as the executable and run the app, but I'll have to verify that's still supported. (Edit: It is. Duh!) So far SxS has caused me a lot of grief, but that's probably because I haven't bothered to really understand it.

Create a Ripsaw API

Since I'm splitting out the C runtime, I'm also going to separate most of Ripsaw's non-visual functionality into a separate API DLL. Not only will this make the core functionality more testable and enforce separation of UI, but I'll be able to build more than one user interface around it. I'd like to build a command-line application as well as a graphical application. Sometimes you just can't beat raw text.

Embrace 64-bit

Even though nearly all of my professional development is still 32-bit, I want to build Ripsaw to compile for both 32-bit and 64-bit architectures. I'll need to do this eventually anyway, and this is a good time to start.

Support Extensibility

My original plans for Ripsaw included the ability to create filters for processing log output as it arrived in the application. Perhaps only lines containing certain values would be displayed, or maybe certain words would be shown in a particular color or font. Rather than trying to build every possible behavior into Ripsaw, I'll publish an extensibility interface so that I can add features later on (or you can add them) without having to change the core API or UI.

Create a Windows 7 UI

The main UI that I'll use will be a 32-bit graphical application targeting Windows XP, but I'd also like to take advantage of some of the new Windows 7 user interface controls such as the ribbon. I'm not inclined to try to serve both interface styles from one application, so after I finish the primary UI I'll create a version that is specific to Windows 7. Since I'm putting most of the non-visual behavior into a separate DLL I'll be free to experiment with different interfaces anyway.

Coming Up

In the next few articles I'll delve into some more development specifics such as language and library choices.

Labels: , ,

Posted October 27, 2009 0 Comments

A New Article Series: Ripsaw

Several years ago I wrote a Windows application called "Ripsaw" that implemented the basic functionality of the Unix tail utility in a graphical application, with a few twists of my own. I had intended to release the application as an open-source project, and although I still use the tool quite a bit I never got around to giving it the necessary polish for a public release. I've only shared it with a few friends and co-workers.

I've just downloaded Beta 2 of Microsoft Visual Studio 2010, and I've decided to create a new version of Ripsaw from the ground up so that I can become familiar with the new IDE and compiler. Besides being a chance to finally get Ripsaw right, this will also be an opportunity to create a series of articles on how I develop a complete application, from the first ideas through design, implementation, testing, and release. I'll walk you through all of the design decisions and trade-offs, the problems I run into along the way, and the development methodologies I use.

I would really appreciate your feedback, ideas, suggestions, and criticisms. This is going to be fun!

Labels: , , , ,

Posted October 26, 2009 0 Comments

An API is Forever

An API is an interface. Those of you that have worked with COM already know that once an interface is published, it can never, ever change. Ever. Not until the end of time. The reason is that some bit of code somewhere is going to be using that interface, and if you change it you've just broken that code. Of course, not changing an interface also means not deleting a portion of it.

At my job I have worked on, and currently maintain, a few APIs implemented in Windows DLLs, some of which have been around for a long time. I had to rewrite significant portions of the internal implementation of one particular DLL in order to add some new features, fix several bugs, and improve the overall performance of the DLL. The old API exposed from the DLL wasn't able to take advantage of the new features, and it wasn't designed all that well to begin with. Since nearly all of the use of that API was through a C++ template wrapper implemented a header, I just added a new set of API functions and changed the wrapper to use the new API.

I did some digging around in the code base (it's a huge code base) to see what else was using the class wrapper around the DLL, and I found out that the component was in far more widespread use than I had anticipated. There were components in all corners of the code base using it now.

The component was still desperately in need of a new API, so what I did was write a new one and forward the old API to the new API. I changed the header file that declared the API functions to look like this:

FICTIONAL_API void NaDoStuff(
HANDLE hFict,
DWORD flags,
LPCSTR name,
LPCVOID bytes,
DWORD byteCount,
LPCSTR desc);

// The rest of the new API was declared here...
// ...
// ...

/************************************************************************
Old, deprecated API. This API is still supported, but calls to all of
these functions are now forwarded to the new API described above.
Please use only the new API in new code.
************************************************************************/

/* DEPRECATED: Use NaDoStuff instead. */
FICTIONAL_API BOOL OaDoStuff(
HANDLE hFict,
DWORD flags,
LPCSTR desc,
LPCVOID bytes,
DWORD byteCount);

// The rest of the old API was declared here...
// ...
// ...

I hoped that the comments would make clear that the use of the old API is discouraged, and that any clients should move to the new API. Likewise, in the implementation, I forwarded the old API functions to the new API functions:

FICTIONAL_API void NaDoStuff(
HANDLE hFict,
DWORD flags,
LPCSTR name,
LPCVOID bytes,
DWORD byteCount,
LPCSTR desc)
{
// Go do stuff
}

// Elsewhere in the code..

FICTIONAL_API BOOL OaDoStuff(
HANDLE hFict,
DWORD flags,
LPCSTR desc,
LPCVOID bytes,
DWORD byteCount)
{
NaDoStuff(hFict, flags, "", bytes, byteCount, desc);
return TRUE;
}

In some cases, forwarding the old API to the new API meant that I had to make the new API implement the semantics of the old API whenever it was being called through the old API functions. For example, the old API had a nasty habit of writing sub-keys all over the company's registry key under HKLM\SOFTWARE. I made the new API put these settings into a tidy sub-key that belonged to the API, like HKLM\SOFTWARE\ComanyName\Fictional. It would even hunt down the all of the old keys created by the old API and move them into the new sub-key. In a later story I'll explain why I had to preserve that behavior in the old API, but for now just remember that I did.

Recently, a co-worker changed a customer-specific branch of the archive so that it would point to the newest implementation of this component. He was writing an application using some libraries that called this component's API. He asked me to help with at a problem he was having, and when I went to look in the registry at some of the settings the API had written, I was dismayed to find that the settings were in the old location, all over the root of the company's HKLM\SOFTWARE key. At first, I thought that he was still somehow using the old DLL, but when I checked the version information on the DLL it was, indeed, the new one. How could this be?

As I mentioned above, most clients use this DLL through a C++ wrapper, since most of the archive is now C++. Only a handful of older C modules call this API directly. The wrapper is a C++ template class, which means it is implemented in a header file. This customer archive was still pointing at the old version the header that called the old API. As I mentioned above, the new API preserves the semantics of the old API when it is called through the old API, and that's what we were seeing here.

My first suggestion, without really thinking about it, was to change the archive to point at the newer header. My co-worker pointed out that this would mean rebuilding all of the components in the archive that used the header, not just the one he was working on.

We left it alone, and everything is working as it should be. At some point in the future we'll move all of the other components up to the newer version of the wrapper, and they'll start seeing the behavior associated with the new API. In the meantime, the customer still benefit from a less buggy and more efficient component.

Labels: , , ,

Posted May 8, 2009 2 Comments

Pre-emptive Snarkiness

It's all about the sense of humor, people. First of all, read the "pre-emptive snarky comment" that I wrote in the earlier entry about the dragsens utility. It's pretty clear that, first of all, I'm tipping my hat to Raymond Chen by even adding a pre-emptive snarky comment. Second, it should also be clear that I'm mentioning the size only because I'm trying to head off any snarky comments about it. Alas, that just doesn't stop some people.

In my first comment to Raymond's article I mentioned that it took me only eleven minutes to write the utility. That was the entire point of the exercise, really. If the utility happens to be useful as well, that's fantastic. In fact, the reason that I bothered to take an extra 30 seconds and link in the C runtime was to make it easier to use. Just copy it to your favorite USB drive, and off you go. If you don't have 84KB free on your drive for the ZIP file, then that's still okay. You can download the source and VS project and build it any way you like. Not a problem.

So, despite the fact that I clearly stated that I was aware that I could make the utility very tiny if I wanted to take the time to do so (when I said, "Of course you could. So could I."), someone still didn't get it. I guess quoting me out of context, or perhaps even putting words in my mouth, is more fun.

If I wanted to make it smaller, perhaps I could dispense with the CRT altogether. Perhaps I could write it in assembly language. Who knows. It doesn't matter. I just wasn't inclined to take any more time gold-plating what I regarded as a very simple exercise. The utility does what it is supposed to do, with a very tiny investment in programming time. That's all I was out to prove. I made the source freely available, with no strings, in case anyone wanted to tweak it to handle some situation that I didn't think about, or perhaps wanted a different implementation (like setting the width and height separately).

As Raymond might say, I can't believe I had to write that.

Labels: ,

Posted April 15, 2009 0 Comments

Windows Drag Sensitivity Utility

Inspired by an article by Raymond Chen about how to correctly change the Windows mouse drag sensitivity, I wrote a quick utility called dragsens. It's a small command-line utility that will allow you to change the number of pixels the mouse has to travel before a drag operation is initiated. Just download and unzip the utility, then run it at the command line, supplying a single parameter that is the number of pixels for the mouse to travel.

In Raymond's honor, I'll provide my own pre-emptive snarky comment: "That executable is 164KB! I could write that in only 4KB!"

Of course you could. So could I. I linked in the C runtime library so you wouldn't have to install the Visual Studio 2008 distributable package just to run a simple command-line utility. I don't normally do that with typical desktop applications, but for small utilities like this is saves a lot of trouble.

If you'd like modify the utility, or examine its source, you may download the Visual Studio 2008 project. If you just want the utility itself, you may download a ZIP of the executable.

About the Utility

The utility is actually very simple. It accepts a single parameter, which is the number of pixels the mouse must travel with a button depressed before the motion registers as a drag action. Rather than separate out the width and height, I just set both to the same number since this is generally all that's necessary.

BOOL success = FALSE;

success = SystemParametersInfo(SPI_SETDRAGWIDTH, numPixels, NULL, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);

if (!success)
{
DWORD error = GetLastError();
std::wcout << L"Error " << std::hex << error << std::dec << " while setting drag width." << std::endl;
return 1;
}

success = SystemParametersInfo(SPI_SETDRAGHEIGHT, numPixels, NULL, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);

if (!success)
{
DWORD error = GetLastError();
std::wcout << L"Error " << std::hex << error << std::dec << " while setting drag height." << std::endl;
return 1;
}


It would be fairly simple to modify the application to set the width and height independently, if you so desired.

Update

I'm already at version 1.1. I decided to add a version resource and support for a "/?" parameter.

About the Snarky Comments

Please see the entry entitled Pre-emptive Snarkiness for my response to the comments about my utility that were posted on Raymond's blog.

Labels: , ,

Posted April 10, 2009 3 Comments

A New, New Wiki

I've replaced the old Wiki with a new Wiki based on the Mediawiki engine. This is now the official Wiki for the Undernet IRC channels #c++ and #c++/cli. It's still in the beginning stages, so feel free to create an account and start adding articles.

Labels: ,

Posted June 7, 2007 0 Comments

Lisp for C++ Templates

I'm not working quite as many hours as I was earlier in the year, so I'm able to concentrate on more long-term projects of my own in addition to my regular work responsibilities. Before I get back to the Accelerated C++ Solutions I'm going to play around a little bit with extreme template meta-programming in C++. Toward this end, I am implementing Lisp primitives with C++ templates for manipulating typelists. So far I only have the primitives online, but I'll hopefully have some examples of their usage up soon.

Labels: ,

Posted December 16, 2004 0 Comments

Updated MMP Mancala

I've received a few complaints from people who downloaded my Mancala game, telling me that certain DLLs were missing. I've rebuilt the game and repackaged the installation file to include all necessary files. I apologize for this oversight.

Labels: , ,

Posted December 3, 2003 0 Comments

More on Accelerated C++ Solutions

For those of you that have been kind enough to write with regard to the Accelerated C++ Solutions page, I haven't forgotten you. I will be updating the site with new chapters Real Soon Now. If you have any suggestions on how to improve the solutions site, please let me know [paul@parkscomputing.com] .

Labels: ,

Posted 0 Comments

Accelerated C++ Solutions

I often recommend the book Accelerated C++ to people who are just beginning to learn the C++ language. One complaint I often hear is that there are no solutions provided for the exercises. This page exists to remedy that problem.

Labels: ,

Posted June 18, 2003 0 Comments

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

Subscribe to Posts [Atom]