Under the Hood : Running Ahead
By Bruce Johnson

     
This article was first published in Clarion Online Volume 1, issue 1 and is reproduced here with permission

In this column I’ll be discussing the Windows 32 bit Application Programming Interface (Win32 API). Generally Clarion does a good job of hiding most of the API complexity from you but there are still functions in the API that are available, and useful, to the programmer who is prepared to dig a little deeper.

As we progress with this journey though the API we’ll build up a function library that you can use in your everyday applications. We’ll make full use of templates to make using these functions easier and more reusable from one application to another. We’ll also see some of the benefits of 32-bit programming.

In this first column we’ll explore the 32-bit function called CreateProcess, along with some related functions. Apart from creating a process we’ll also be able to stall the current process until the child shuts down. In addition to this our function will return a handle which we can use to terminate the process as well. We’ll do this using the TerminateProcess function.

The Clarion RUN function is a simpler version of CreateProcess. CreateProcess however has some additional functionality that can come in handy when used at the right time. Essentially CreateProcess, and Run, allow you to start a program from within another program. In CW2 this was all that RUN allowed you to do. In the new Clarion 4 (C4) one parameter has been added which allows you to wait until the program you call has finished running before continuing with the caller program. CreateProcess, can however do even more.

Now the idea is to create a wrapper function, called ARUN, in our library to remove some of the complexity. What we’re aiming for is a function that allows us more power than the Run function, but still isn’t as complex as the full CreateProcess function. For example in calling our wrapper we’ll use strings instead of cstrings, we’ll allow omittable parameters (with suitable defaults) and we’ll remove parameters that don’t interest us for now.

Although CreateProcess can do a lot, most of it is beyond what we need. For example if you’re writing a debugger then CreateProcess has flags that you would need. It’s unlikely though that you would write a debugger in Clarion so we don’t need to worry about things like that. I’m not going to go into all the flags that can be set, but I’m going to focus on the ones you’d most likely use. Specifically ARUN will offer the following features: In addition to this, inside ARUN I will use the GetLastError and WaitForSingleObject functions (both Win32 API functions) to provide the following additional functionality; To round it off ARUN will return a process handle (if it is successful) that will contain a handle to the new process. We can use this handle, and the TerminateProcess function, to terminate the child process if we want to. TerminateProcess is a bit of a mouthful to remember so we’ll create a wrapper function, called ENDRUN to end a program started with the RUN statement.

An obvious sidebar here: a logical step from here is to be able to terminate the current program from within itself. So you might be wondering how to get hold of the handle of the current program. However the solution here is to remember the HALT command provided by the Clarion language, which performs that functionality for us already.

Here is the prototype of CreateProcess as it appears in the Win32 SDK. (This is using a C compatible prototype statement).

BOOL CreateProcess(
  LPCTSTR lpApplicationName,                 // pointer to name of executable module
  LPTSTR lpCommandLine,                      // pointer to command line string
  LPSECURITY_ATTRIBUTES lpProcessAttributes, // pointer to process security attributes
  LPSECURITY_ATTRIBUTES lpThreadAttributes,  // pointer to thread security attributes
  BOOL bInheritHandles,                      // handle inheritance flag
  DWORD dwCreationFlags,                     // creation flags
  LPVOID lpEnvironment,                      // pointer to new environment block
  LPCTSTR lpCurrentDirectory,                // pointer to current directory name
  LPSTARTUPINFO lpStartupInfo,               // pointer to STARTUPINFO
  LPPROCESS_INFORMATION lpProcessInformation // pointer to PROCESS_INFORMATION
  );


At first this looks a real mouthful !! However lets go through it piece by piece and see that it’s actually not that bad.

Before looking at what everything does, lets translate this into a more familiar Clarion prototype. Firstly in C the type the function returns is placed before the function name, in this case BOOL. (Incidentally if there’s nothing before the name then it’s assumed the function returns a Long in 32 bit and a Short in 16 bit. If the function returns nothing, i.e. it’s a procedure, then the return type is VOID).

Another point to bear in mind is that the Win API uses Pascal parameter passing techniques and you also need to use the RAW attribute since this is a non-Clarion DLL. Actually the Raw attribute is only necessary in certain cases, but it does no harm on the others, so I put it in on all API calls to make thinking easier. The Name attribute isn’t strictly necessary yet, but you’ll see in a minute that it comes in handy. So far our prototype looks like this..

CreateProcess ( ?? ), Byte, Pascal, Raw, Name(‘CreateProcess’)

Now let's look at the parameters that it takes. Actually although it looks complicated the types tell you what they are. For example LP stands for long pointer, things ending in STR are a string and so on. Actually when dealing with the API strings are always Cstrings, and are usually passed using a pointer in this way - so get used to the LPSTR construction ! As you can see in the above they come in a variety of flavors, but in Clarion all of these flavors become a *CSTRING.

The two Security Attribute parameters are only used in Windows NT. As we want our function to work for both Windows 95 and Windows NT we’ll set these to Null, i.e. 0. This means that in Windows NT the process we create will get default security settings.

Bool as we mentioned earlier is a BYTE, and Dword stands for Double Word. A Word in C is the same as a USHORT in Clarion, and a Dword is the same as a ULONG in Clarion. The LPVoid type is also a pointer, but in this case it’s a pointer to a group of strings. We’ll examine this one more closely in a moment. The last two parameters are pointers to groups that contain a number of options and flags and things like that. It’s in these groups (which Windows is very keen on) that most of the complexity, and indeed most of the power, of API functions is revealed. The groups are documented inside the library source code for those that are interested. Actually the first group, SRARTUPINFO, sets a lot of the options for CreateProcess, and PROCESS_INFORMATION contains a number of handles that are returned by the function.

So our eventual prototype looks like this:

CreateProcess ( *CString, *CString, Long, Long, Byte, Ulong, *CString, *CString, |
*StartupInfo, *ProcessInfo ), Byte, Pascal, Raw, Name(‘CreateProcess’)

The name of the wrapper function is called ARUN. It is prototyped as...

ARUN (String, <String>, <String>, <Long>, <Long>, <Long>, <Long>, <Long>, <Long>,<Long>), Long, Proc

... and is documented as...

ARUN (Program, StartInDirectory, EnvironmentBlock, Priority, InitialState, Xpos, Ypos, Width, Height, Wait)

Program

This is the name of the program, including its path (if necessary) and any command line parameters it may take.

Start In Directory

This is the directory in which the program should initially start.

Environment Block

Some programs, especially legacy DOS and Windows 3.1 programs use the Environment space to store startup settings. This can be a problem when running from Windows as these settings usually require changes to the AutoExec.Bat, which in Windows 95 is something to avoid.

CreateProcess lets you specify an environment space specifically for a program. This is passed to the function as a list of null-terminated strings, terminated itself by a null. For example:

EnvStr String(512) ! note we use a clarion string here
  code
  EnvStr = (‘path=c:\dos;c:\windows<0>clavm0=e:\<0><0>’)


Note that each setting is separated by a <0> and the whole string is terminated by a <0> as well.

Priority

You set the priority of the new process to one of 0,1,2,3 or 4.
  1. Idle priority. This is the lowest priority. The new process will only run if the system is idle. This is ideal for housekeeping type programs, and background process type programs.
  2. Normal priority. This is the default if this parameter is omitted. If the parameter is 0 then this is also the priority used. This is the same priority as other Windows programs.
  3. High priority. This preempts other windows programs and gets most of the available CPU time. This should be used with care as it stops other programs from getting time.
  4. RealTime priority. This is the highest priority possible. It preempts all other windows processes including the mouse and hard disk cache. This should only be used with extreme care.

Initial State

You can set the new program to open in one of the following modes (valid values 0,1,2,3).
  1. Normal ( new program gets the focus). This is the default if the parameter is omitted or set to 0.
  2. Maximized (new program gets the focus )
  3. Minimized ( calling program keeps the focus )
Note that some programs, especially Clarion programs, may not respond to this requested behavior.

Xpos, Ypos, Width, Height

These parameters determine the starting position, and size of the program. Note that although these apply they may not be obvious immediately. For example if the program is open maximized, then these positions will only apply when the window is "Restored".

Wait

This determines if the program must wait or not for the new program to finish before carrying on. If this is set to 0 (or omitted) then the program doing the calling (the Parent) carries on immediately after running the child. If this is set to 1 then the Parent waits until the Child has terminated before it continues.

ARUN Returns

The function returns a long containing one of the following: Example

result long
  code
  result = aRun ('c:\windows\notepad.exe','d:\secwin') ! run the notepad starting in the d:\Secwin dir.
  result = aRun (‘c:\windows\notepad.exe’,,,,,,,,,1) ! start the notepad, and wait for it to finish.
  result = aRun (‘c:\windows\calc.exe’,,,,3) ! run the calculator, but start it minimized.
  EndRun (Result) ! Close the calculator opened in the above line


Next let’s look at the TerminateProcess Function, and the wrapper we’ll make called ENDRUN.

Not surprisingly TerminateProcess is a lot simpler than CreateProcess, and requires far fewer parameters. Nevertheless we can still simplify it a little.

BOOL TerminateProcess(
  HANDLE hProcess,  // handle to the process
  UINT uExitCode   // exit code for the process
  );


By now you should be used to the layout as well as the BOOL data type. Both HANDLEs and UINTs are ULONGS in the Win32 API. So a Clarion prototype of the function is;

TerminateProcess(ULong, ULong), Byte, Pascal, Name(‘TerminateProcess’)

The first parameter is a process identifier, as returned by the CreateProcess function. The second is the exit code that the program should return when it closes. Return codes are almost never used in Windows programs, so our wrapper function, ENDRUN, will simplify the call by setting this to 0. ENDRUN is prototyped as follows:

ENDRUN ( Ulong )

and documented as follows

ENDRUN ( ProcessID )

ProcessID

A Process Identifier as returned by the ARUN function.

Example

result long
  code
  result = aRun ('c:\windows\notepad.exe','d:\secwin') ! run the notepad starting in the d:\Secwin dir.
  ! do some code here
  ENDRUN(result)


The included files include a project library with source code for the ARUN and ENDRUN functions, a simple template for using in your apps, and the WinError.Clw errors file. The template defines a single global extension which adds the prototypes to your app for you, and includes the library in your project. You will need to register the template in your template registry, as well as compile the project and copy the API.LIB file from your \cw20\obj directory to your \cw20\lib directory.

As a last note, remember that ARUN and ENDRUN are 32 bit functions. Thus they cannot be used in 16 bit programs.

© 1997, Online Publications, Inc. Reproduced with permission.

horizontal rule

© 2012 CapeSoft Software CC