Introduction
Please Note: OddJob
requires StringTheory
OddJob provides a set of classes and templates for running and managing
processes from within your application (executables, batch files etc.).
Oddjob will easily help you:
- Run a process (like batch files and external exes) asynchronously
from within your app and monitor the output (think of FTPing files via
an external FTP client)?
- Handle scripting languages (like PHP scripts) synchronously or
asynchronously?
- Run a user interface to control your service (and bypass UAC)?
OddJob also allows you to pass data to started processes and retrieve data
from them. You use it to send and receive data via the StdIn and StdOut of
any started process, and OddJob works excellently with NetTalk to
communicate with processes via TCP/IP.
OddJob also provides the ability to pass data to a started process (via
the Standard Input as well as on the command line), which allows command
line applications to be run and data passed to them, as well as retrieving
the result back from the process. This opens up a huge number of command
line tools to your application, from simple command line clients, to
command line server, compilers and even batch files.
We strongly recommend starting with the example applications, which are
described below in the Example section.
Jobs and Features
Not only can you run, monitor, terminate and manage any processes that you
start, you can also group them into Jobs.
A Job allows groups of processes to be managed as a unit. Job objects are
nameable, securable, sharable objects that control attributes of the
processes associated with them.
Operations performed on the job object affect all processes associated
with the job object. If you have used applications such as Google Chrome
or Internet Explorer then you will have experienced Jobs in action, as
each tab creates a new process that is a member of the same Job.
This allows tasks normally performed by threads to be performed by
processes, providing improved stability and isolation.
Using CapeSoft OddJob
Add OddJob to your application in a few
Easy Steps!
- If this is a Multi-App system, then add the OddJob global extension
to the data (root) DLL. On the Multi-DLL tab in this app tick on both
options (This is part of a Multi-DLL program and Export OddJob classes
from this DLL.)
If this app is not part of a Multi-DLL system then skip this step
- In the app where you want to create and use an OddJob object, add
the global extension to the app. If this is part of a Multi-DLL
solution then go tothe Multi-DLL tab and only tick the first option on
(This is part of a Multi-DLL program)
- Add the Local extension to the procedure that you wish to use OddJob
in.
This adds a JobObject object to the procedure.
- Add code to initialize and kill the object
Initializing the object passing a Job name is option. You can call Init without specifying a Job name, in which
case the object will be initialized, but no Job will be created. This
is useful when you want to use the functionality that doesn't require
a JobObject (for example starting a process, enumerating processed on
the machine, or killing a process).
- Initialize the object (for example before the window opens in ThisWindow.Init(), or at the start of the
procedure).
Job.Init('MyJob')
- Kill the object and clean up:
Job.Kill()
Note: You can also call Job.Kill(false)
to clean up all memory and handles, but leave any processes in the
job running. By default calling Kill will
end all processes started, but by passing False
to the method all processes will be left running and the
Windows JobObject will remain until all processes have closed.
Using the JobObject
The code below demonstrates three different ways of creating and
interacting with new processes
Starting Processes
output StringTheory
code
Job.CreateProcess('', 'notepad.exe', '', '')
Job.CreateProcess('takeaction.BAT', jo:SW_HIDE, false, , , , , output)
if not Job.CreateProcess('grep.com ?',jo:SW_HIDE , , , , , , output)
Message('CreateProcess failed: ' & output.GetVal())
else
if not output.SaveFile('output.txt')
Message('Failed to save the output. Error ' & |
output.winErrorCode & ': ' & |
output.FormatMessage(output.winErrorCode))
else
Job.ExecuteFile('output.txt')
end
end
Kill all processes associated with a Job
Job.CloseAll()
Get information about the a Job
Job.GetInformation(jo:JobObjectBasicAccountingInformation)
How to call a command line application and get the output
This example demonstrates the best practice for calling applications on
the command line. It retrieves the output for StdOut if any is written,
but works equally well for applications that don't actually write anything
to StdOut. It has the added benefit of essentially synchronizing the
process class, as it reads from the output on a timer until the handle is
close and the process is complete. In almost every case, when you call a
command line application and need to retrieve output, a synchronous
behaviour is desirable. Not that if you call this on a thread that display
a user interface, the interface will be unresponsive while the StdOut pipe
is being read from, as the read is handled on a separate Accept loop.
exitVal long
st StringTheory
commandLine string(512)
jo Class(JobObject) ! added by the template
end
CODE
commandLine = 'Dcdirdmp.exe -if dicomdir' st.Free()
jo.CreateProcess(commandLine, jo:SW_HIDE, 1, , , , exitVal, st)
Examples
Demo
This is the main OddJob example application.
It enables you to run a batch file or exe and retrieve the output data. It
also creates a job and allows you to add as many instances of Notepad to
the job as desired, as well as listing information about the job and
processes running on the machine. Any application can be added to a Job,
regardless of whether it is an application that you have created, or an
application such as Notepad from a third party.
Html Editor
This provides a simple HTML editor and demonstrates the use of NetTalk for
inter process communication. NetTalk is required to compile this example.
The HTML editor is a completely standalone application that is integrated
with the example using a Job to control the process and NetTalk.
Note: The feHtmlEditor.app file does not
need to be compiled, the shipped executable will be used by default, and
it provides a standalone HTML editor that is fully integrated with the
example application. In order to compile the actual editor executable
NetTalk and FileExplorer are required.
The OddJob Templates
The Global Extension template:
There are no options for the Global extension.
The OddJob local extension template
Add this extension to a window procedure where you want to control any
started processes. This window must exist as long as the processes are
running, or at least as long as you are interacting with them in any way.
The local extension allows you to specify the type of object being used.
By far the most common is the standard Job object, which provides the
foundation for OddJob's functionality.
Version History
Download latest version
here
Version 1.45 - 24 May 2021
Version 1.44 - 28 September 2020
- Fix: Function being called as Procedure
Version 1.43 - 13 September 2018
- Add: Clarion 11 to install.
Version 1.42 - 24 July 2018
- Change: Conditional Compile, _ODDJOB_=>1,
is added to the project if OddJob is included in the app (and not
disabled.) This allows code dependent on OddJob to know that OddJob is
available.
Version 1.41 - 5 September 2017
- DebugOutput method renamed to Trace.
- Calls to ErrorTrap always sent to Trace.
Version 1.40 - 28 September 2016
- BindToCPU method extended so
that the program can be bound to any subset of CPUs, a specific CPU,
or a random CPU. (Thanks to Alison Peters.)
Version 1.39 - 28 April 2015
- Removed CREATE_BREAKAWAY_FROM_JOB and
added CREATE_PRESERVE_CODE_AUTHZ_LEVEL to
CreateProcess flags. (Thanks to Carlos Gutiérrez and Bijan
Hosseinian.)
Version 1.38 - 3 March 2015
- Added procq.hProcess = self.Processes.hProcess
after deep assign. (Thanks to Rick Martin.)
Version 1.37 - 25 February 2015
- Installer supports Clarion 10.
- Removed unnecessary use of %cwversion
Version 1.36 - 7 March 2014
- Build for use with StringTheory 2.00 and later.
Version 1.34 - 3 February 2014
- Update: Installer detects Clarion 9.1
Version 1.33 - 14 May 2013
- Update: Installer detects Clarion 9.
Version 1.32 - 14 March 2013
- Changed to Ver4 object/template management system. IMPORTANT
READ
THIS.
- Add: support for Multi-Proj in C8
Version 1.31 - 28 November 2011 Requires
StringTheory 1.52 or later
- Added: New classes and example
application for named pipe handling. Simplifies the creation of named
pipes and transfer between a server and a client for both Clarion
applications and application written in other languages which use
pipes.
- PipeCore class: Provides the core reading
and writing to and from pipes. Typically not used directly - the
PipeServer and PipeClient inherit from this class.
- Properties:
- Methods:
- Read Procedure (long hPipe,
*string binData, long binLen, *long bytesRead), long, proc,
virtual
- Write Procedure (long hPipe,
*string binData, long binLen, *long bytesWritten), long, proc,
virtual
- PeekPipe Procedure (long hPipe,
*string binData, long binLen, *long bytesRead, <*long
bytesAvailable>, <*long bytesMessage>), long, virtual
- PipeServer Class: A Multi-threaded pipe
server class. Allows the handling of any number of pipe clients (for
each client an OS thread is created and destroyed when no longer
needed).
- Properties:
- _running bool ! Set while the server is active.
- _threadID ulong ! ID of the listen thread
- _hpipe long ! Handle to the current pipe
- Methods:
- Run Procedure (), virtual ! Start
the listening thread to wait for new client connections
- Stop Procedure (), virtual
- CreatePipe Procedure (<string
pipeName>), long, proc, virtual
- ConnectPipe Procedure (long
hPipe), long, virtual
- DisconnectPipe Procedure (long
hPipe), long, virtual
- TakeRequest Procedure (*string
request, long requestBytes, *string reply, *long replyBytes),
virtual
- PipeClient class:
- Properties
- hPipe long ! Handle to the current pipe
- Methods:
- OpenPipe Procedure (<string
pipeName>, long timeOut = 0), long, virtual
- ClosePipe Procedure (), virtual
- Write Procedure (*string
writeData, *long dataLen), long, proc, virtual
- Read Procedure (*string readData,
*long dataLen), long, proc, virtual
- SetPipeState Procedure (long
dwMode = jo:PIPE_READMODE_MESSAGE), long, proc, virtual
- WaitForPipe Procedure (long
timeOut), long, virtual
- Added: a Pipes
example which has a pipe server and pipe client app demonstrating the
usage of the new Pipe classes.
Version 1.30 - 09 September 2011
- Changed: Moved all equates and types to
the OddJobEq.inc file
- Added: JobObject.BindToCPU
- Binds the process to a single CPU (can be called at any point after
process creation and applies to all threads and child processes)
- Added: ProcessAffinity example.
Demonstrates the new process affinity methods such as BindToCPU,
SetProcessAffinity, GetProcessAffinity
etc.
- Added: JobObject.GetSystemInformation()
method to retrieve system information
- Added: joSYSTEM_INFO type for retrieving
system information
- Added: JobObject.CountProcessors()
method to return the number of processors for the current system
- Added: JobObject.SetProcessAffinity()
method to allow the process to be limited to a specific subset of the
processors available on the system
- Added: JobObject.GetProcessAffinity()
method to allow the current processor mask to be retrieved along with
the system mask (which show which processors are available and can be
used in setting the process affinity mask).
Version 1.29 - 29 June 2011
- New: Added the following methods:
- HandleFromPID Procedure(ulong PID),
unsigned, virtual
Returns the Process Handle when passed the Process ID (PID)
- GetProcessPath
Procedure(*joProcessQType processQ), string, virtual
Returns the full path for the EXE identified by the current
Process record in the passed queue. Note that the path returned
uses the Windows NT Device name format, not the DOS style drive
paths. Use the GetDrives method to map
between device names and paths.
- DeviceNameForDrive Procedure(string
sDrive), string, virtual
Returns the full device name when passed the drive identifier (for
example 'C:' or 'D:')
- GetDrives Procedure(*joDrivesQType
drivesQ), bool, proc, virtual
Populates a queue with a list of all drives and their full NT
Device Names.
- New: Extended the example application to
demonstrate the new methods - populate a list of all drives and their
NT device names; get the full path of a process based on the process
ID of the selected item in the processes list etc.
Version 1.28 - 9 April 2011
- New: Added a KillProcess
method. This kills one or more instances of a process when passed the
process name (or optionally a substring of the name).
- New: KillProcess
example application. Demonstrates creating a small command line
application using a hand coded project. This application kills all
matching processes when passed a process name and can also optionally
kill just the first instance found, or all processes where the name
matches a substring passed.
Version 1.27 - 13 December 2010
- New: Replaced the RunAsUser
method. This method can be used from a service to run an application
as the currently logged in user and bypasses UAC on system that have
it enabled.
Example:
Job.RunAsUser('C:\Windows\Notepad.exe', '')
- Note: RunAsUser requires the ability to
duplicate the token used by the WinLogon process associated with the
current user. This is generally only available to services running in
the LocalSystem account.
- Fix: The GetUserToken, GetSessions
and related methods have been modified to allow zero session IDs
(which are valid).
- Improvement: The Init
method can be called multiple times without causing any undesirable
behaviour.
- Improvement: The object is initialized on
creation, and the calling Init explicitly is only required to create
an actual Job (all non Job functionality can be used without calling
Init manually).
- Improvement: Kill
is called on destruction to clean up, and no longer needs to be called
manually.
- Improvement: Calling Kill
multiple times no longer results in undesirable behaviour.
- Improvement: Calling LoadLibs
multiple times now only loads libraries that have no already been
loaded and will not cause any undesirable behaviour.
- Fix: The Userenv.dll library was not being
loaded correctly and the handle ended up pointing at the wrong
library.
- Fix: The CreateEnvironmentBlock
and DestroyEnvironmentBlock functions
causing a GPF as a result of an incorrect library handle.
- New: The ErrorTrap
method now allows the method name to be passed as an optional
parameter
- New: LogonUser
method returns a token for the specific user using the passed user
name and password to authenticate.
- New: CloseHandle
method. Closes the passed handle if it is valid.
- New: DuplicateToken
method duplicates the passed token, and can convert handles to
impersonation tokens into a primary token handles.
- New: RunAsUser
example
Version 1.26 - 16 September 2010
- Fix: joOpenProcess() was calling the wrong API as the result of a
typographical error.
Version 1.26 - 16 September 2010
- Updated the installer to ensure that the RED file is correctly
updated to include .c and .h files for the MD5 functionality.
- StringTheory is now provided as separate install, so please ensure
that you download the current StringTheory install when updating
OddJob.
- Fixed a potential GPF in the CreateProcess method where a pointer
was assigned without the &= operator.
- Template fix for Clarion 7 (was no sheet and tab on extension).
- Template tweak - assert for installing StringTheory.
- Template 01.tpw - includes version 1.66 of the 01.tpw
Version 1.25 - 15 June 2010
- StringTheory Class
- Fixed Between() method including the left hand delimiter in the
returned string.
- Additional handling of boundary cases in the Replace() method.
Version 1.24 - 01 June 2010
- StringTheory Class
- Fixed a bug in the Replace method, when the string being
replaced is the end of the text being searched.
Version 1.23 - 01 June 2010
- StringTheory Class
- Added bounds checking and corrected unhandled boundary cases for
the following methods:
- Replace (handles replacement from
the start and end of the string, where the resultant string is
empty, where the resultant string is a single character etc.)
- PathOnly (handles the case where
the path contains only a forward or back slash)
- Split (handles if a "line" is
blank)
- ToBlob (handles the string being
assigned to the blob being empty, or being a single character)
- Verified that Between and Before are both safe.
Version 1.22 - 26 May 2010
- Added optional clip parameter to StringTheory Append
method.
- Updated docs for the StringTheory SetValue
method
Version 1.21 - 17 May 2010
- Added handling to SetValue for passing
zero length strings (the StringTheory value is disposed of and the
Length methods will return zero).
- Added GetSessions method that fills a
queue with all sessions.
- Added FindActiveSession method.
- Added an optional accessToken process
to the RunAsUser method to allow the method
to run a process as any user, not just the current one.
- Added an optional dontStore parameter
to the RunAsUser method, which allows the
process information to not be stored. This is set to True (1) by
default to maintain the same behaviour as previous version that did
not support this functionality.
- RunAsUser now creates the processes,
associates it with the current job (if the breakAwayFromJob
parameter is not set to True, and the
.dontAssociateProcesses property is not set to True), and
stores the created process information in to .processes queue (if the
dontStore parameter is not set to False (0)).
- Added a GetToken method to retrieve the
Access Token associated with the passed session.
- Added a RunAsUser example that demonstrates running processes with a
variety of credentials.
- Added APIs for token manipulation (runtime loaded):
- joAccessCheck(), joAdjustTokenGroups(),
joAdjustTokenPrivileges(), joGetTokenInformation(),
joSetThreadToken(), joSetTokenInformation()
Version 1.20 - 17 May 2010
- Fixed a potential memory leak when Calling CreateProcess and reading
data from a process.
- Reduced memory usage when calling a processes that the StdOut will
be read from.
- Fixed the length of the string created when using Base64 encoding to
ensure that the encoded string is always padded onto a 4 byte
boundary.
- Added the base64NoWrap property, which when
set will not add line wrapping to the Base64 encoded string.
- Fixed errors in the Base64 encoding.
- Improved efficiency when assigning new values to the string that
have the same length as the string stored
- Add a new csBlowfish class to provide support for the Blowfish
cipher.
- Added Blowfish encryption and decryption support to StringTheory,
via the following methods:
- InitBlowfish
- KillBlowfish
- Encrypt
- Decrypt
- EncryptString
- DecryptString
- SetKey
- Note: Future releases will support CBC (Cipher Block Chaining) to
allow data of arbitrary length to be encrypted. The current version
requires that that the data encrypted has a length that is a multiple
of 8 (in bytes).
- Documented base properties and methods available to all classes,
including:
- Base Methods
Debug |
Conditionally output to the Windows debug output depending
on whether the .debug property is set to true or not. |
DebugOutput |
Unconditionally output a string to the Windows debug output. |
ErrorTrap |
Callback method used to trap errors. |
FormatMessage |
Converts an API error code into the associated error
message. |
ToCstring |
Creates a new cstring from the passed string. |
FreeCstring |
Safely frees a cstring reference variable. |
Ulong64ToReal |
Converts an unsigned 64 bit integer to a Real |
GetReg |
Returns the value of the specified registry key. |
PutReg |
Sets the value of the specified registry key. |
Base Properties
displayErrors |
Sets whether or not the class displays errors using the
Message function. |
logErrors |
Sets whether or not errors are sent to the Windows Debug
Output |
errorCode |
The last error code |
errorMessage |
The last error message |
debug |
Determines whether or not debug output is produced |
debugPrefix |
The prefix used for debug output |
- New GetRegand PutReg
methods (see above).
- Documented new methods and properties.
- New main example app demonstrates a far wider variety of ways in
which to start and interact with processes.
Version 1.10 - 06 April 2010
- New StringTheory methods:
- Before - returns the portion of the
string that occurs before the specified string seperator.
- After - returns the portion of the
string that occurs after the specified string seperator.
- Between - returns the portion of the
string that occurs between the two specified string seperators.
- Base64Encode/Base64Decode
- Base64 encode and decode either a passed string, or the
value stored by the StringTheory object.
- MD5 - Create an MD5 hash of the
string.
- New string delimiter processing methods:
- Split - splits the string into
sub strings, which are stored in the .lines property.
Optionally allows the "line" start and end delimeters to be
specified.
- Join - joins the all the lines
(after the string has been slit using Split, and optionally
wraps each line in the specified delimiters).
- Slice - returns a string containing
the data between the specified start and end characters (a
"slice").
- Crop - Crops the stored value using
the pass start and end parameters.
- Sub - returns a sub-string of the
specified length, starting at the specified character.
- Fixed the ClipLen method infinitely loop
as it was calling itself rather than calling ClipLength().
- Fixed the Base64Encode method adding
extra spaces in the encoded string.
- Added: FromBlob method, stores the
contents of the passed BLOB in the StringTheory object.
- Added ToBlob method, saves the contents
of the string into the passed BLOB.
Version 1.03 - 20 October 2009
- Added: Additional optional parameter to the CreateProcess methods to
allow the initial window mode to be specified. This allows the process
to be started as shown, hidden, minimised, maximised etc. without
having to call the more advanced CreateProcess method that supported
this.
Version 1.02 - 20 October 2009
- Fixed: CreateProcess causing a GPF when used to start a process that
was hidden and did not require any IO (no StringTheory object was
passed).
- Added: Additional logging to CreateProcess for simpler debugging.
- Fixed: CreateProcess could potentially call the AddProcess method
twice, resulting in it failing the second time. This would not affect
the application's behaviour, but would display an erroneous error in
the debug log if logging was enabled and being captured.
Version 1.01 - 12 October 2009
- StringTheory Class
- Fixed: the PathOnly method, which was
returning a blank string.
- Optimised: the Replace method in the
case where both strings are a single character
- Fixed: Replace to allow an empty
replacement string to be specified (which removes the searched for
string)
- Fixed: FileNameFromPath method.
- Renamed: the ProcessQType to joProcessQType to ensure that it
does not conflict with other tools using the same type.
- Added: GetProcessMemoryInfo method to
get the memory usage information for any process on the system.
- Fixed: Missing Editor folder in the Demo example, causing the
HTML editor to not function correctly.
Version 1.00 - 11 August 2009