Windows


An Automated Process Shutdown DLL

Paul Carlson

It's easy to communicate among Win32 processes through a shared DLL, once you get all the machinery in place.


Introduction

Have you ever worked on an application that comprised more than one executable, each running in its own process space, that all needed to be shut down when one of the executables ran into trouble? If the answer is yes, this article is for you! What follows is a simple mechanism to shut down all related processes when one other process runs into trouble. This article also demonstrates some of the power of global memory segments under Win32 and how to spawn a separate thread of execution within a process.

This article assumes that the reader is familiar with Windows programming, with creating DLLs, and is knowledgeable about the general concepts of processes and threads. For a very thorough introduction to all these topics, please see Charles Petzold's landmark publication Programming Windows [1].

All code in this article was developed under Windows NT using the Microsoft Visual C++ 5.0 compiler.

Creating a Shared Memory Segment

The Shutdown DLL is shown in its entirety in Figure 1. The first thing to note are the #pragma statements toward the top of the listing. This particular pragma pairing creates a data section named "shared." The shared data segment is crucial to the functioning of the shutdown dll.

Using the data_seg pragma, you can name sections whatever you wish, but the linker will use only the first eight characters. All initialized variables after the first #pragma will go into the shared section. It is important to initialize the variables; otherwise, the linker will place them in the normal uninitialized section rather than in the shared section [1, p. 979]. The second #pragma marks the end of the section.

Notice that the pragma simply creates a named data segment. The next thing to do is to tell the linker that this data segment is to be shared with all executables that load this DLL. Using the Microsoft line of compilers, this is done with the /SECTION linker flag. In this case, the shared section should be marked as readable, writable, and shared.

To mark the section via the Microsoft Visual C++ IDE (Integrated Development Environment), simply select Project, then Settings. Select the Link tab. In the Project Options dialog at the bottom of the window, enter the following:

/SECTION:shared,rws

At this point, the shared memory segment is properly set up. All processes that load this DLL will have access to the same variables initialized in this segment (namely, the shutdownProcess flag).

Spawning a Thread in Win32

Spawning a separate thread to execute under Win32 is simple — just make a call to CreateThread. CreateThread has the following method signature:

HANDLE
CreateThread
    (
    LPSECURITY_ATTRIBUTES
        lpThreadAttributes,
    DWORD dwStackSize,
    LPTHREAD_START_ROUTINE
        lpStartAddress,
    LPVOID lpParameter,
    DWORD dwCreationFlags,
    LPDWORD lpThreadId
    );

For the purposes of this article, the only parameters that need to be specified are the startup routine (the third parameter) and the thread id (the last parameter). All other parameters can be either zero or null. (See the Win32 SDK documentation for details regarding this function.)

The call to CreateThread will look like this:

CreateThread
    (NULL,
     0,
     WaitForShutdown,
     0,
     0,
     &ThreadId
    );

(see Figure 1).

The WaitForShutdown startup routine (the third parameter above) must conform to the following signature, as specified by the Win32 SDK:

DWORD WINAPI ThreadFunc( LPVOID );

So the signature for WaitForShutdown is as follows:

DWORD WINAPI WaitForShutdown(LPVOID);

The implementation of WaitForShutdown is very simple. It simply monitors the status of the global shutdown flag, shutdownProcess. If that flag becomes equal to 1, the function terminates the process by calling ExitProcess [2].

Note the call to Sleep. This call causes the thread to check the shutdown flag only once every second. I added the call to Sleep to minimize CPU usage. Of course, there may be more elegant mechanisms than this polling mechanism for monitoring shutdown status. The suitability of this mechanism depends on the application.

Pulling It All Together

Making an executable (process) subject to "global" shutdown (being shut down by another process) involves the following steps.

1. The process to be subject to global shutdown loads the shutdown DLL into its address space via the LoadLibrary call.

2. The process invokes the LoadDLL function exported by the shutdown DLL. The LoadDLL function spawns a thread that monitors the global memory segment to see if the shutdown flag has been turned on. If it has, the thread calls ExitProcess and the process is terminated.

To perform a global shutdown of all monitoring processes, the process initiating the shutdown calls the shutdown DLL's ShutdownProcess method. This call sets the flag that all interested processes are monitoring on their separate threads of execution. When each process sees that the flag is on, it will call ExitProcess as described above.

Sample Usage

Figures 2, 3, and 4 provide two sample MFC applications that demonstrate the loading of the DLL and invoking shutdown. The DLL is loaded in the application object's (derived from CApp) constructor via LoadLibrary. The address of the DLL functions to be called (LoadDLL and ShutdownProcess) are assigned to public instance variables. The constructor calls LoadDLL, which starts the shutdown monitoring process. The destructor frees the DLL.

Whenever a user presses the Shutdown button on one of the test applications, all processes that have invoked the LoadDLL function will be terminated. Pushing the Shutdown button simply invokes the ShutdownProcess method in the CApp-derived class.

In the sample code available for download from the CUJ website, I've included two MFC-based applications as well as a Win32 console-mode application. If you have all three running at the same time and press the Shutdown button on either of the GUI-based applications, the console application, as well as both GUI applications, will shut down.

Additional Considerations

Since the shutdown logic is wrapped up in a DLL, the DLL could be used in several different programming environments, including not only C++ but Powerbuilder, Visual Basic, and others as well.

Application shutdown is subject to many considerations and this article provides only a simplistic mechanism for shutting down applications. The intent of this article was to demonstrate the use of shared memory segments and how to spawn threads to monitor system status. The techniques used in the shutdown DLL could easily be extended to accommodate a variety of application needs.

Notes and References

[1] Charles Petzold, et. al, Programming Windows 95 (Microsoft Press, 1996).

[2] WaitForShutdown does not use TerminateProcess because it is unsafe. The Win32 SDK documentation states:

The TerminateProcess function is used to unconditionally cause a process to exit. Use it only in extreme circumstances. The state of global data maintained by dynamic-link libraries (DLLs) may be compromised if TerminateProcess is used rather than ExitProcess.

Paul Carlson is currently employed as an independent consultant in St. Paul, Minnesota. He holds a B.S. in Computer Science from the University of Wisconsin, River Falls and a Masters degree in Software Engineering from the University of St. Thomas. He has been programming in C/C++ for over ten years and can be reached at p.carlson@computer.org.