Using VI Server to Interact with Executables

We all want to be able to reuse code and a good way of accomplishing that goal is by repurposing executables that you wrote for other projects. The problem is how you control them. Last week we started addressing this challenge by looking at some of the general tools that are at our disposal for manipulating executables — regardless of where you got them. This time out we will complete the discussion by looking at some of the things you can do that are specific to LabVIEW-created executables.

First, we need an executable

As the title says, if we are going to talk about making VI Server calls to an executable, the first thing we need is an executable — and an executable, we have. Although the functionality it implements is, to be honest, rather sparse, it is sufficient to demonstrate what we need. Here’s what its front panel looks like:

A Small Test Executable - FP

Starting in the top left, it sports an indicator where you can see the command line that was used to launch it. Immediately below that string is an indicator showing the current time, a button for stopping the program manually, and a pair of LEDs that indicate when two different events are triggered: Application Instance Close? and Panel Close?.

To the right is a path indicator that displays one of two different paths depending on the state of the checkbox that is next to it. Below the path indicator are two numerics. One is the PID of the instance that is running. The other is the TCP/IP port number that was assigned to the executable when it was launched. Note that if you don’t provide a port number in the command line parameters, the executable will terminate almost immediately — though you may see it briefly appear in the Windows Task Manager.

Handling the front panel

Most of the code that makes this interface work is pretty straight forward, so I won’t take the time to describe it. The one exception is this bit in the initialization logic:

Intialization Logic

The reason that I’m pulling it aside for special attention is that it illustrates (at least part of) the solution for a problem that you will encounter the first time you create a VI that is designed to run entirely in the background. The heart of this problem is mismatched expectation: When LabVIEW runs an executable it expects to open the window associates with the top-level VI. You, on the other hand, wanting the executable is run unseen in the background, expect the window to stay closed. Consequently, what happens is that LabVIEW opens the window and starts the VI running in that order and your code immediately hides the front panel. What the user sees is a windows that open and then immediately closes without explanation, and if there is one things that worries users more than things happening too slowly, its things happening too fast — like windows flashing open and then closing.

The solution lies in a VI property called Transparency. The setting to control it can be found in the custom appearance dialog.

Transparency Dialog

When the box is checked and the percentage is set to 100, the window will be open but totally transparent. Hence, when the runtime engine launches the application, the window will still open but it will be invisible. A moment later, the code above will hide the window and set the transparency to 0 so that when we do decide to open it, we will be able to see it.

VI Server Operations

Last time, I presented this block of settings from the application’s INI file. Before we continue, we need to take a moment all consider what these settings mean — at least to the extent that anyone knows what they mean…

server.tcp.enabled=True
; server.tcp.port=3363
; server.tcp.serviceName=""
server.tcp.acl="290000000A000000010000001D00000003000000010000002A10000000030000000000010000000000"
server.vi.access="+*"
server.vi.callsEnabled=True
server.app.propertiesEnabled=True
server.vi.propertiesEnabled=True
server.control.propertiesEnabled=True

The first four parameters in this list control overall access to the application via TCP/IP. Consequently, they are the four that you are most likely need to muck with:

  • server.tcp.enabled=True: This setting enables the TCP/IP interface that VI Server uses. If this setting is False, nothing is happening.
  • ; server.tcp.port=3363: This setting specifies the port that the associated TCP/IP listener will be monitoring for a connection. Note that I have this line commented out because we will be assigning this value via command line parameter.
  • ; server.tcp.serviceName="": Also commented out, this optional parameter allows you to define a name that you can then use to reference the application, instead of a Port number.
  • server.tcp.acl=???: This setting defines the TCP/IP access control list (ACL) — or who is allowed to connect to the application. Already I can hear you wondering, what is the deal with that long string? Well, if you ever find out be sure and let me know. The bottom line is that the original interface included an ACL that simply listed the IP addresses that were and were not allowed to access the application. For reasons unknown, NI decided to change this common sense approach to something more enigmatic. So how do you generate this string? Glad you asked. According to LabVIEW’s documentation, you need to set up your development environment to have the same access list as you want your application to have, and then copy and paste the resulting string from LabVIEW’s INI file to your application’s INI file, seriously…

The remaining parameters specify in one way or another the specific resources that the remote program can access in the target application.

  • server.vi.access="+*": This parameter contains a list of VIs that are accessible through the VI Server interface. The default value shown allows access to all VIs.
  • server.vi.callsEnabled=True: This parameter specifies whether the remote program is allowed to run VIs contained in the executable. We leave this True so I can demonstrate that ability.
  • server.app.propertiesEnabled=True: This parameter gives the remote program access to the executable’s application-class properties — like the names of all the VIs currently in memory.
  • server.vi.propertiesEnabled=True: This parameter gives the remote program access to the VI-class properties of individual VIs. This category include things like a reference to the VI’s front panel.
  • server.control.propertiesEnabled=True: This parameter gives the remote program access to the properties associated with individual controls on VI front panels. For example, you need to have this parameter enabled to do things like programatically set the value of a control. This value is True as I will be demonstrating this ability as well.

Finally, I want to state one thing that I hope is obvious. All these “security” settings are contained in a plain text file that can be edited by anyone who knows how to use a simple text editor. The point here is that while recent versions of Windows are making it harder and harder to modify files in the “Programs” directories, it is not by any stretch of the imagination bullet-proof. Hence, if there are truly sensitive things that need restricted access, don’t depend on these settings.

What we can do with these controls

So let’s put some of what we have been learning about into action. If you download the code from the SVN repository you will find, in addition to the source code, a compiled executable. For the following tests, you can either run the executable I have included, or compile it on your own — it’s up to you. You will also want to be sure to update your copy of the toolbox as I have added a couple useful VIs. One of the executable management VIs is where we will start:

Launching the Executable

Start by opening small test executable.lvproj and then open the routine Launch 3x.vi. It’s job is to launch three copies of the test application (small test executable.exe) so on the front panel click the path browser button next to the path control and navigate to and then select the test application. Now, run the VI.

When it finishes, launch the Windows Task Manager. Nothing new under Apps, or Programs (depending on your version of Windows), so look in the Background Processes. Ah, there’s the executable, but why is it listed here? And why is there only one instance? The VI clearly looped 3 times, and there were no errors. Go to the directory where the test application is located and open its INI file. There are your answers. There is only one instance running because the INI file has multiple instances turned off, and the one executable that did launch shows up as a background process because the INI file also says to hide the root window. Leave the root window setting the way it is, but change the AllowMutipleInstances to True, and save and close the file.

Now back in the Task Manager, abort the one instance of the test application that is running now, and rerun Launch 3x.vi. You should see when it finishes that there are now 3 instances of the test application running. The instances were given sequential TCP/IP port numbers from 3365 to 3367.

Firing Remote Events

The next thing I want to do is open the front panels of the 3 instances so we can observe their operation. Now if you look at the test application’s source code, there is a UDE that will make the front panel visible, so all I need to do is fire that event. But wait, those are three instances of a compiled executable — you can’t fire events in other executables! Well actually, you can. To see how, open the test VI, Open the Executable’s Window.vi.

Open Executable Front Panel

No magic here. All the code is doing is dynamically running a VI. But check out the function before Open VI Reference, it’s called Open Application Reference. Its job is to open a reference to a copy of LabVIEW or the LabVIEW runtime engine that is running somewhere else. That “somewhere else” is defined in terms of a machine name and a port number or service name. The machine name can be a DNS name, an IP address or (as in our case here) localhost to point to the local computer.

By the way, if you think it sounds like I just said that you could make this same code access an executable residing on a remote computer by simply changing localhost to an IP address, your right. I did just say that.

But as cool as that feature might be, how does it allow me to fire an event in a compiled executable? Look at the name of the file being run: Open Window.lvlib:Generate Event.vi. It’s the VI that fires the event, and since VIs called in this way actually execute in the remote LabVIEW environment, the event gets fired in the targeted executable.

To see this code in action, run it three times with the port number 3365, 3366 and 3367. Three windows will open.

Setting Control Values

Another way of interacting with an executable is to directly manipulate controls on its front panel. However, if the target VI is event-driven like our test application, we need to remember that there is a difference between setting a value and firing any value change events associated with that control. If all you need to do is set a value, there is a VI method called Control Value:Set that will do the job nicely. However, if you want to fire the value change event you have to set the control’s Value(Signaling) property — which frankly is a bit more work.

set the selector control value with signalling

This picture is the block diagram of the test VI Toggle the selector checkbox.vi, but the good news is that for this little bit of extra effort, you can set (or read) any control property that can be changed while a VI is running.

Shutting Down the Executable

Finally, we need to be able to stop an application that is running. But the problem here is figuring out how to test it such that we can see that it really did what it was supposed to do. The solution is to turn to the trace technique we discussed a while back when we were learning about command line arguments. I have written the code such that if the executable is run with the argument “d1” in the command line, the code will write a line to the trace file saying how the instance was stopped. And to help demonstrate how this works, I have created a test VI (Stop the Executable.vi) that can execute some of these termination paths.

To start off, leave both controls in their default state, and run the VI. This example stops the targeted executable by clicking the Stop button on its front panel. The instance with the port number 3365 will immediately close.

Now increment the Port Number to 3366 and set the Method control to Windows shutdown - Forced. This example stops the targeted executable by telling to Windows to abort it. The instance with the port number 3366 will immediately close.

Finally, we want to test the remaining instance’s response to the Application Instance Close event. To do that, restart your computer now. (That’s right, restart your computer. Don’t stop anything, don’t shut anything down — just restart.) When your computer is restarted and you are logged back in, go to the directory where the test application is installed and open the trace file. You will see two lines that look something like this:

07:29:39 05/24/2015 -- Shutdown 3365 -- Just Stop
07:42:52 05/24/2015 -- Shutdown 3367 -- Appl Inst Close

The second line shows that when you restarted your computer Windows did in fact generate the Application Instance Close? event and the application caught the event. You’ll note that there is no entry for the instance with the port number 3366. Remember, we stopped it by forcing an abort and a Windows abort is very much like clicking the red abort button when LabVIEW is running a VI: It just stops. No orderly shutdown. No deinitialization.

A Small Test Executable — Release 1
Toolbox — Release 9

The Big Tease

So that was, I hope, interesting. Starting next time I’m going to start delving into how to use LabVIEW code as the data collection and control backend for an application that has as its only customer-facing interface a web site. While there are many companies offering options that claim to be developer-friendly I have found that many of the marketing claims are largely based on FUD (Fear, Uncertainty and Doubt). Simply put, they build up a “strong man” of supposed complexity and complication, and then tell you that the best (and perhaps, only) way to get past this obstacle is to buy their product. The truth, however, is that their “strong man” is really made of straw, and if you understand how it all fits together, doing it yourself isn’t really very hard.

Until Next Time…

Mike…

Managing Standalone Executables

A while back when we were discussing opportunities for code modularization, I made the comment that it is even possible to incorporate processes into a new application that have already been compiled into standalone executables. At the time we didn’t have the time to go into detail on the point, but now we will take the time to consider the various tasks required to manage an existing standalone executable that you wish to leverage for a new application.

So What’s to Manage?

When I talk about managing an executable, I’m including all those tasks needed to control the executable from an operating system level. Therefore, to complete these tasks successfully we need a certain basic understanding of Windows and how it interacts with the programs that it is running. As you might imagine, most of these tasks will involve simply starting and stopping programs, but there are nuances that are important too. For example, when I double-click on a program the first time, Windows will launch it, but what if I double-click on it again? In this situation Windows has two possible options: launch another instance of the program or activate (bring to the front) the instance that is already running. One of the things that we will look at is how to specify that action for the executables we create,

Likewise, sometimes it isn’t enough to simply launch a program, sometimes we need to take high level look at its operation to determine such things as: How many instances of it are currently running or how much memory it is using. This information can help us decide how to best husband the computer resources that our application as a whole uses. Consequently, we will spend a little time considering the alternatives.

Finally, there can even be subtleties when it comes to stopping an executable. Can we simply signal the executable that it needs to stop, or do we need to force it to stop Now.

Getting Things Started

The first thing we want to cover is how to programmatically launch standalone applications. So to state the obvious, this topic is first and foremost about how to tell the operating system to launch the application for us. Luckily, this task is easily handled by the built-in System Exec.vi. This function allows you to do from within LabVIEW anything you would do from a command prompt — though at times the operations aren’t done in the exact the same way. In fact, for some older versions of Windows the differences can be significant. But even in Windows 8 there can be small inconsistencies. For example, say you have a program called Small Test Executable.exe that can accept a couple of command-line arguments like p3365 fpd2. You could launch this program from the command line like so:

C:\"Program Files\Test Executable\Small Test Executable.exe" -- p3365 fpd2

Unfortunately, this doesn’t work with System Exec.vi, which simply returns an (undocumented) error. To get the command to work you have to either write it to a temporary batch file and then execute the batch file, or modify the command like this:

"C:\Program Files\Test Executable\Small Test Executable.exe" -- p3365 fpd2

Don’t see a difference? Check out the location of the first double quote… To simplify dealing with these variations, I have created a subVI to encapsulate the functionality called Launch Executable w-Command Line Parameters.vi and included it in the new release of the toolbox.

Launch EXE with Parameters

The other thing about getting an executable running is that there are a few important launch parameters that we can only set in the application INI file, so we’ll deal with those next.

A Windows Convention

One of the conventions that Windows imposes is that the INI for an executable will have the name as the executable (but with the file extension ini) and the standard configuration parameters will reside is a section with the same name as the file (but minus the file extension). It is in this section that the runtime engine expects to find the parameters that it needs to successfully launch the executable.

The first such parameter we need to discuss controls the behavior when the executable is called more than once. By default, when you create an executable with LabVIEW only one copy of it will launch. If you call it again, Windows will simple bring to the front the one instance of the program that is already running. However, you can change this behavior by including this line in the INI file:

AllowMultipleInstances = TRUE

With this parameter set as shown, Windows will launch another instance of the program each time it is called. An important point to remember, however, is that no matter how many instances you launch, they all share the same INI file. Consequently, if you want to pass unique parameters to each instance, you will have to do so through the command line when the instances are launched.

The second INI file parameter I want to mention controls the program’s visibility to the computer user. Regardless of whether its GUI is visible, when a program normally launches it has a tab that appears on the taskbar. But what if you are creating a program that is intended to run unseen in the background? This setting handles that case.

HideRootWindow = True

When this setting is true, the program can still have a user interface but there is no tab associated with its window on the taskbar. As a side effect, the program also doesn’t appear in the task manager as an “App”, but rather lists it as a “Background Process”. Please note that this terminology is what Windows 8.x uses, older versions make the same distinction but use the word “Programs” and “Processes” to describe it.

The third INI file setting to discuss, is really not so much of a setting, as it is a family of settings that work together to control the program’s ability to respond to external connections via TCP/IP and VI Server.

server.tcp.enabled=True
server.tcp.port=3363
server.tcp.serviceName=""
server.tcp.acl="290000000A000000010000001D00000003000000010000002A10000000030000000000010000000000"
server.vi.access="+*"
server.vi.callsEnabled=True
server.app.propertiesEnabled=True
server.vi.propertiesEnabled=True
server.control.propertiesEnabled=True

Some of these settings are easy to understand, while others are rather obtuse. For right now, don’t worry about what these settings all mean. Next week we will.look at these parameters in detail.

Passing Parameters

To reiterate a point I made earlier, creating a standalone executable that will support multiple instances pretty much necessitates the use of command line arguments. In some ways, this situation is analogous to the situation concerning reentrant clones. The instance needs to be able to identify itself in the midst of a cloud of identical instances.

Given that point, one of the things you should consider when creating such an application is how the program should respond if the required command line parameters are missing. The answer to that question depends, to a large extent, on the nature of the parameters. If the input is essentially a customization for which there is a valid default value, it might be acceptable to simply accept that default and go on. However, in some situations there is no default value possible, so you should consider shutting down the program if the command line parameter is invalid or missing.

Getting Status

After we have gotten the instance (or instances) of a program up and running, one of the things that we might want to do is check on how, and perhaps what, it is doing. For now we will consider this matter in terms of the performance aspects that are visible to Windows. It is certainly possible to access the executable programmatically to obtain internal status information, and even control its operation, but that discussion will have to wait until next week.

General Information

One of the most basic indicators of the health of a program is its memory usage. You should expect the memory that a program has allocated will rise and fall over time. It should not treat trend steadily upwards without regard for what the program is actually doing. If a program’s memory consumption does consistently increase over time, that condition is referred to as a “memory leak” and it is a condition that needs to be addressed. Typically this remediation takes the form of figuring out why the program wants ever-increasing amounts of memory (like programming errors resulting in file or I/O being left open) and fixing the problem. Occasionally however, you will find that the leak is occurring in a place where you have no visibility — like inside LabVIEW itself. In those situations all you can do is try forcing Windows to deallocate the memory or change the way you are doing something so the problem doesn’t arise.

A simple way to read memory usage is to call the simple command line function tasklist and parse the result. Here is the code I wrote to do the job. Its name is Read Task List.vi.

Read Task List

If you leave the Image Name control empty, the call returns information on all the tasks currently running. However, if you populate that control with the name of a program, it returns a list of all the programs with that name. For example, on my computer right now LabVIEW.exe returns 1 item, while chrome.exe returns 13. (Why does Chrome need 13 instances of itself running in the background?)

The two most valuable pieces of information that you can get from this command is the application’s memory usage (expressed in kilobytes) and its PID. The PID is a numeric tag that uniquely identifies each program that is currently running. It is important because other commands or system calls will often require the PID of the program that you are wanting to check.

Digging for the Details

Beyond this basic information there is a variety of other details that you can find, For example, here you can find a community-developed VI that makes direct calls to a .NET assembly in order to return a plethora of information about your computer in general, and the programs that are running in particular. From the standpoint of accessing this information programmatically, this VI has two big problems:

  1. The code is at least 7 years old: This is a problem because there are places where the same operations could be done much more efficiently using techniques that LabVIEW has introduced in the intervening time.
  2. It’s essentially undocumented: To figure out how to really use it you will have to spend time researching the calls and the specific parameters — which can vary from one version of Windows to the next.
  3. It was designed for manual operation: In other words, it was designed to be used interactively, and not programmatically.

Because we need something that is usable from within a program, I created two subVIs that essentially repackage the functionality in a more program-friendly form. The first subVI is called Get Available Instances.vi. To see why this VI is needed, think back to the output of the tasklist function. In that output multiple instances of the same executable (like the 13 copies of chrome) all had the same name. They were distinguished from one another by a numeric id number called a PID. The designers of the .NET interface must have thought this approach confusing because they took a different approach that did away with the PID in favor of a name that was modified to make it unique — as in chrome through chrome#12. Likewise, any file extension was also dropped from the name.

Get Available Instances

The other subVI (Get Task Performance.vi) accepts as inputs an instance name and an enumeration that lists the available performance parameters that the VI can fetch under Windows 8.

Get Task Performance

But what is the deal with the loop and all the “extra” logic? This loop is needed because, while some of the parameters are instantaneous values that can be read at any time, others are not. As a case in point, consider the processor usage measurements. Everyone knows (or at least everyone in this business should know) that a CPU can really only do one thing at a time. In fact, the illusion that is central to much of modern technology is the programmatic slight of hand that makes it look like computers are doing multiple things at once.

Now since a CPU can only do one thing at a time, logically there are only two possible immediate answers to the question of how much processor are you’re using. Either you are not executing (in which case you have 0%) or you are executing (resulting in 100% usage). In short, for this type of measurement to have any sort of meaning it has to be approached as a statistic, not an absolute measurement. In the VI, this task is implemented by making two calls to the same measurement counter. The first call — which always returns a 0 — starts the measurement process, with the second and subsequent reads returning the statistical results for the previous measurement period.

Oh, and the logic for stopping the loop after just one iteration? That’s to optimize operation for parameters that are absolute.

Stopping: Fast and Half-Fast

It has been said that, “All good things must come to an end” and this is true for programs as well. However Windows actually provides two different types of shutdown events. The first causes Windows to simply abort the targeted program. This approach, while very fast, is logically equivalent to clicking the abort button on a LabVIEW program — and carries with it all the same dangers. The other type of shutdown event takes longer, but is much safer. It basically sends a message to the targeted program that requests it to stop. However, to reiterate something we learned before, for this messaging to work properly the executable must be written such that the Application Instance Close event will always initiate an orderly shut down.

In addition to using these messages for its own purposes, Windows also provides a command line function that allows users and individual programs to generate these messages as well. I implement both types of shut down requests in the subVI Task Killer.vi.

As a polymorphic VI, the routine incorporates instances that can shut down processes by name and by PID. You simply pick the version that gives you the level of control that you need. For instance, if I wanted to shut down all 13 instances of chrome, using the name version of the code would get the job done all at once. However, if I wanted to stop just a few select instances, I would use the version that uses the PID input. Here is the name version of the code.

task killer

But why start and stop at all?

Before ending this time, there’s one more idea that we should consider. To this point we have been thinking about the management of executables in a rather conventional sort of way: Start something just before we use it and close it when we are done. But might not there be situations where the “conventional” doesn’t make sense? I would assert that there are more than you might at first imagine.

The whole point of interacting with a process that is deployed as a separate standalone executable is that it obviously implements some bit of functionality that more than one application would need. Given that fact, starting and stopping it — especially stopping it — can get complicated. Just because one application is stopping and no longer needs it, that doesn’t mean there might not still be another application that does. One obvious solution to this problem is to simply let Windows handle it all. Install the background task such that it starts when either the computer boots, or the user logs in; and then stops when Windows shuts down. This solution might seem wasteful, but what is the real impact of such a solution? Oh sure, the program will be in memory, but if it isn’t being used it will probably be shuffled off to the page file pretty quickly. Likewise, if the executable is written correctly, it will be event driven which means that unless it is accessed by a program that is using it, the process won’t be burning up any CPU cycles either — so what is there to lose?

Just something to be thinking about.

Toolbox Release 8

The Big Tease

Ok, this is all for now. Next time I’ll introduce a little executable that will let us practice some of the theory we have examined today — and expand the conversation to include how to use VI Server to interact with executables created in LabVIEW.

Until Next Time…

Mike…

Handling Shutdown Situations

The very essence of event-driven programming in LabVIEW is the ability to handle occurrences in the application that are not directly related to, or synchronized with, other things happening in the system. As we have seen in previous posts, the proper handling of these asynchronous events is critical to an application’s success. So far we have covered events arising from such things as the user changing a control value, one part of an application using an event to signal another process to do something, and time-based events that fire periodically.

However there are other sources for events that you need to be aware of, not the least of which are other parts of LabVIEW and even Windows itself.

Incoming Windows Events

Starting with Windows, this event source shouldn’t be too surprising. After all every modern operating system incorporates some type of messaging scheme for communicating with the applications that they are running. Now to tell the truth, most of the messaging that Windows does is not very useful for what we do, but there is one very large exception. When a user tells Windows to shut down or restart, one of the first things that Windows does is broadcast a message that tells all applications currently running that they need to quit. This message (which, by the way, also fires when the task manager tells a program to stop) is important because it allows programs to come to a controlled, graceful stop. When all you’re doing is calculating a spreadsheet, a “controlled, graceful stop” is a convenience. However if your application is controlling the operation of potentially dangerous equipment, an orderly shutdown is an absolute necessity.

Because LabVIEW applications tend to be doing this sort of thing on a regular basis, the language designers have chosen to expose this message to us in the form of the Application Close? filter event. Basically what happens is that when Windows generates the shutdown message, LabVIEW, or the LabVIEW runtime engine, catches the message and passes it on to any user-program that has registered to receive the associated event. From this point on, the event is handled however that code sees fit. If no program has registered for the event, LabVIEW performs an abort to stop all running code. The thing to remember is that using abort to stop a program is like stopping your car by running it into a tree: It works, but there can be negative consequences.

To prevent these “consequences”, every application should have a process that is registered to catch the Application Close? event. The good news is that this logic is very easy to implement — assuming of course that you have written your program such that it is easy to stop. The only other thing you really have to decide is where to put the code. There are arguments that can be made for putting this event handler in a variety of places, but the spot I prefer is in the Exception Handler process. It is after all, the centralized spot for handling things that would interfere with what the program is normally doing. You know, things like errors or shutdown commands from the OS.

This is what the event handler looks like added to Exception Handler.vi in our testbed application.

Application Close Event

As you should expect, there isn’t a whole lot to look at: When Windows sends a shutdown message, the error handler fires the internal Stop Application UDE to shut down our application. In addition, it discards the original Application Close? event to prevent it from having any further effect in the LabVIEW world.

Closing the GUI

The other shutdown situation that you should always handle is the case where the user tries to stop the application by clicking on the close box in the upper right-hand corner of the screen. Conceptually, the case is very similar to the previous one, but with one big exception: When the operator tries top close the window, actually stopping is optional. When Windows starts closing, your application will be shutting down. The only question is the circumstances: will it be a systematic shutdown or an abort.

Due to this difference, it is not uncommon for the logic that handles this event include a prompt to the user asking them if they are sure that they want to quit. If you do decide to include a dialog box, make the prompt short and easy to understand. I once saw a dialog that gave stated:

“Are you sure you don’t want to not abort?”

In any case, here is the code for implementing a handler for the Panel Close? event. It is, of course located in the GUI process — the only place it would make sense, right?

Panel Close Event

You will notice, however, that there is a slight problem here. Namely, as soon as the dialog box opens the screen updates stop. You might not think that this is much of a problem because you are, after all, stopping the application. But what if the user realizes their mistake and decides to continue? Now you have lost (or at the very least, distorted the timing of) perhaps several seconds of data. Moreover, what happens if during that time something important happens to which the software needs to respond? Well, that doesn’t happen either. The problem is that the dialog box is “blocking”. In other words, until the user responds to the dialog, further execution of the loop is blocked.

Never fear though, there are ways around this problem that retains both your ability to prompt users for input, and prevents the blocking of your program’s execution. And we’ll start looking at those solutions in the next post. Once said fix is implemented I’ll update SVN.

Until next time…

Mike…