CapeSoft.Com
Clarion Accessories
GUTS
Documentation
CapeSoft Logo

CapeSoft GUTS
Documentation

Download Latest Version JumpStart FAQ History
Installed Version Latest Version

Introduction

GUTS (Global UnThreaded object Store) is a low-level class which can be used by other classes. By itself it does nothing and adds no value to a program.
However it allows other objects to communicate with each other, across threads, even when the object is not the topmost window on the thread. This is a required layer for some other CapeSoft Accessories, and is provided free of charge.

ClarionLive

An introduction to this class, and the way in which it works, was described in ClarionLive session #234 , recorded on 1 November 2013.

During this webinar a small example class, LeftRight was created. The LeftRight class is included in the GUTS install. The LeftRight class is not documented here, for more discussion on this class see the webinar.

Installation

Run the supplied installation file:

Jump Start

Add the global extension to your app file.

Support

Your questions, comments and suggestions are welcome. Check our web page (www.capesoft.com) for new versions. You can also contact us in one of the following ways:

CapeSoft Support
Support Page Find support page with various options here
Email support@capesoft.com
Telephone+27 21 715 4000
Fax +27 21 715 2535
Post PO Box 511, Plumstead, 7801, South Africa

Distribution

No additional files are required for shipping.

Basic Description

A single GUTS object, called ThisGuts is added to the application. This object is unthreaded, and thus is available to other objects on all threads.

The object maintains a queue of Interfaces. Other objects can add or remove themselves to this queue. Objects can Post an Event to other objects in the queue. Events are just a number, and any number can be used. It is envisaged that a number of "common events" may be added to GUTS at some point in the future, so it is recommended that custom events are equal to Guts:User or more.

Each object in the queue has a family name, the number of the thread it is on and an ID. Messages can be posted to all objects in a family, all objects on a thread, or a single object (if you know its ID).

Classes implement the Interface, and when an event is posted into ThisGuts it is passed on to the class via this interface. What the class does with the event is entirely up to the class.

How to use GUTS in a class

Implement the Interface in the Class

  1. In INC file: Add Include('Guts.inc'),Once near the top of the file.
  2. In INC file: Add ,IMPLEMENTS(GutsInterface) to the Class declaration
  3. In INC file: Add two new properties;
    Guts &Guts
    GutsId Long
  4. In CLW File: Add classname.GutsInterface.TakeEvent method. For Example;

    something.GutsInterface.TakeEvent PROCEDURE(Long pEvent, <Long Param1>, <Long Param2>, <Long Param3>, <Long Param4>, <String Param5>, <String Param6>, <String Param7>, <String Param8>)
      CODE
      case pEvent
      of Guts:User+1     
    ! make up your own events here
        Self.Refresh()    ! do whatever you like here
      End
  5. In CLW File: In DESTRUCT method add

    If not self.Guts &= Null and Self.GutsId <> 0
      self.Guts.Delete(self.GutsId)
    End
  6. In CLW File: Change the  INIT method declaration, adding the Guts parameter
    (Guts pGuts) 
  7. In the CLW file: In the INIT method set the new properties as follows;

    self.Guts &= pGuts
    self.GutsId = self.Guts.Add('familyName',self.GutsInterface)
    ! the ADD method returns the ID

    The first parameter in the call above is the "familyname" for the object. The Post (as described below) will usually limit events to objects in the same family. The family name can be anything you like, although name clashes with other classes are undesirable.

Post an Event

The PostEvent method of the GUTS Class takes a number of parameters. The first parameter is the event number. The next 3 allow the scope of the Post to be restricted, and the following 8 are optional parameters that are passed to the interface. The method returns the number of interfaces which received the event.

The parameters to this method are as follows;
ParameterDescription
Event LongThe event number that is passed to the interfaces. Any number can be used, but for future compatibility use numbers equal to or more than Guts:User
Family String(20)Limit the Post to all objects of this family
Thread Long (optional)Limit the Post to all objects on this thread. (This can be combined with the previous parameter to limit to objects of a specific family, on a specific thread)
ID Long (optional)Limit the post only to the object with this ID. If this parameter is set then the previous two parameters are ignored.
Param1..Param4 Long (optional)Four optional parameters of type LONG which will be passed unaltered to the Interfaces.
Param5..Param8 String (optional)Four optional parameters of type STRING which will be passed unaltered to the Interfaces.
The method can be called from wherever you like in your code. Here are some possible calls;

ThisGuts.PostEvent(Guts:User+1,'familyname') ! post to all objects of the same family
ThisGuts.PostEvent(Guts:User+1,'familyname',thread()) ! post to all objects, on this thread, with the same family
ThisGuts.PostEvent(Guts:EventRefresh,'')
! post to all objects regardless of family
ThisGuts.PostEvent(Guts:User+1,'',0,x) ! post only to the object with the ID = x, regardless of thread.
ThisGuts.PostEvent(Guts:User+1,'familyname',0,0,x,y,z) ! post to all objects of the same family, passing in the x, y and z values at the same time.

Care with Pointers

An object which hooks into GUTS in the above way has one characteristic which is different from a normal class, and needs to be allowed for.

By passing a pointer to its interface to GUTS, you are implicitly implying that the object can be accessed from thread 1. In other words, your object code can run on thread 1 even though your object is instantiated on thread 2.

This is not a problem, unless your object in turn contains a pointer to a global THREADED variable, like a file handle. There may be cases where this access of "thread 2" memory, from thread 1 may(*) cause a problem. Fortunately there is a solution.

Ideally your class should store two pointers when dealing with a global, threaded, variables. The "INSTANCE 0" of that variable, and the "current thread" INSTANCE for general use. The following example shows this in action.

FirstRecord Class()
MyFile0       &file
MyFile        &file
Init          Procedure(File pFile)
Get           Procedure()
            end

FirstRecord.Init Procedure(File pFile)
  Code
  self.Myfile0 &= INSTANCE(pFile,0)

FirstRecord.Get Procedure()
  Code
  self.MyFile &= INSTANCE(self.Myfile0,Thread())
  Get(self.MyFile,1)


In the above code the class is making sure the "Instance 0" pointer is stored by the Init method, and then the "Current Instance" is set before the pointer is used.

(*) In our experience of using GUTS so far, pointers to memory fields are not a problem in this context (outside of normal memory syncronisation). The problem is when you are pointing to data structures (like Files) which are then used by code (File Drivers) which do not support this context switch.

Getting a GPF?

The most likely reason for a GPF when POSTing an Event is if an object has added itself to the Interface Queue, but not removed itself from the queue when it goes out of scope.
In other words the line of code

self.Guts.Delete(self.GutsId)

has not been added to the DESTRUCT method of the class.

A second common reason for a GPF when POSTing an event is if the object contains File handling code, and that code does not take sufficient care over the thread change. See the section Care with Pointers for more information on this.

Examples

There are four examples included with the Install. They show how to apply the template an ABC or Legacy app, as a simple Exe or in a Multi-DLL setup.

The single EXE, ABC demo app has an additional bit of code showing how the GUTS Object might be called - in this case by a very small class called LeftRight. An example of posting events is in the Main procedure, and an instance of the LeftRight class has been added (manually) to each window. You can see the LeftRight class in action in this example by using the items in the Tools menu.

This example, with LeftRight added completely by hand is a good example of adding any class to an app by hand. there are a few things to take note of;
  1. Global Embeds, After Global Includes, is the line
    include('leftright.inc'),Once
    This makes the class visible to the application.
  2. Project Defines include;
    LeftRightLinkMode=>1
    LeftRightDLLMode=>0

    This is important so that the application does not GPF when it encounters the class. these settings will change in a multi-dll situation, if in doubt set the values the same as the GUTSLinkMode and the GUTSDllMode.
  3. In a procedure an instance of the class is created with the line
    LR LeftRight
    LR is the name of the Object. LeftRight is the name of the class.
  4. After the window is opened the LR Object is initialized with
    LR.Init(ThisGuts)
A Post is set to all the objects from the MAIN procedure, and looks something like this;
ThisGuts.PostEvent(guts:user,'lefttoright',0,0,1)

Template Reference

Global Extension

This template adds the Global ThisGuts object to the application.

General Tab

Disable All GUTS Features
If this is ticked on the object will not be generated.

Multi-DLL Tab

This is part of a Multi-DLL program
Tick this on if this app is part of a multiple-app system. This is turned on for all the DLL's and the EXE's.
Export Class from this DLL
Tick this on in the app which will export the class. This is almost always the Data DLL app. In all other apps, DLLs and EXE's, leave this off.

Classes Tab

Class Version
The last date when the class was parsed from the LibSrc folder. Used internally. Do not change.
Object Name
The name of the object to create, usually ThisGuts.
Class Name
The name of the class to use, usually Guts.

Object Reference

Purpose

The Guts class maintains a Queue of interfaces. Other objects can add or remove themselves from this queue. A method is provided so that notifications can be sent to all the interfaces in the queue.

GUTS Interface

TakeEvent

TakeEvent(Long pEvent, <Long Param1>, <Long Param2>, <Long Param3>, <Long Param4>, <String Param5>, <String Param6>, <String Param7>, <String Param8>)

Description

Receives an event posted by the Guts class. How an object processes the event is completely up to the class implementing the interface.

Note that this isn't a Clarion or Windows event, but rather just a notification to the interface.

Parameters

ParameterDescription
pEventAn event number that has meaning to the receiving object.
Param1...Param4Four optional LONG parameters containing additional information for the object.
Param5...Param8 Four optional STRING parameters containing additional information for the object.

Return Value

none

Example

Leftright.GutsInterface.TakeEvent Procedure(Long pEvent, <Long Param1>, <Long Param2>, <Long Param3>, <Long Param4>, <String Param5>, <String Param6>, <String Param7>, <String Param8>)
  CODE
  case pEvent
  of Guts:User
    Self.SetDirection(Param1)
  End

Properties

PropertyDescription
iQueue   &GutsQueueTypeA queue of interfaces.
LastId   LongAs each item is added to the queue it is given a unique id. This property contains the number of the last id issued.
cs   &ICriticalSectionA critical section to manage access to the global, unthreaded queue safely.

Methods

Add

Add(String pFamily, GutsInterface pInterface)

Description

Adds an interface to the queue. Generally called by a class which implements the interface when it wishes to add itself to the global queue.

Parameters

ParameterDescription
pFamilyA short string containing a unique family name for the class which is registering itself. Events can be posted by family, so this allows an object to post events only to objects of the same family.
pInterfaceThe GutsInterface as implemented in the calling class.

Return Value

Long. The unique ID of the object in the Guts Queue is returned. The calling object should store this, and use it as the parameter to the Delete method when the calling object goes out of scope.

Example

Leftright.Init Procedure(Guts pGuts)
  CODE
  If not pGuts &= Null
    self.Guts &= pGuts
    self.GutsId = self.Guts.Add(family:leftright,self.GutsInterface)
  End

See Also

Delete

Construct

Construct()

Description

Automatically called when the Guts object comes into scope. Does some basic initialization of the object.

Parameters

None

Return Value

None


Delete

Delete(long pId)

Description

Allows an object to delete itself from the Interface queue. It's very important that an object does this when it goes out of scope, as leaving it in the queue, once the object has gone out of scope, will lead to a GPF the next time an event is posted.

Parameters

ParameterDescription
pIDThe Unique ID of the object to delete. The unique ID was returned to the object when the Add method was called.

Return Value

None

Example

Leftright.Destruct Procedure()
  CODE
  If not self.guts &= Null
    self.Guts.Delete(self.GutsId)
  End

See Also

Add

Destruct

Destruct()

Description

Automatically called when the Guts object goes out of scope. Does some basic clean up of the object.

Parameters

None

Return Value

None


PostEvent

PostEvent(long pEvent, String pFamily, Long pThread=0,Long pId=0, <Long Param1>, <Long Param2>, <Long Param3>, <Long Param4>, <String Param5>, <String Param6>, <String Param7>, <String Param8>)

Description

Used to post a notification to one or more items in the interface queue.

The items being posted to can be identified by ID, or alternatively by a combination of Family and Thread. If the Family is set then only objects of this family will receive the event. Likewise if the Thread is set then only objects on that thread will receive the event. If both Family and Thread are set then only objects of that family, on that one specific thread, will receive the event.

Parameters

ParameterDescription
pEventThe number of the event to send to the objects. Note this is not a Clarion EVENT - you can use any number you like. Numbers >= Guts:User are recommended for future-compatibility.
pFamilyIf this parameter is set, then only objects in this family will receive the event. An object sets its family when calling the Add method.
pThreadIf this parameter is 0 then the event is sent to all the threads. If it contains a thread number, then the event will only be sent to that specific thread.
pIdIf this parameter is set then only this object will receive the event, regardless of the Thread or Family parameters.
Param1...Param8Additional optional parameters that will be passed on to the receiving object.

Return Value

The number of objects that received the event is returned.

Example

ThisGuts.PostEvent(guts:user,family:leftright,0,0,1)

See Also

Post An Event

Release

Release(Long pId)

Description

Releases the CriticalSection. Calls to Release MUST be paired with a call to the WAIT method. Each WAIT requires 1, and only 1, RELEASE. Failure to balance WAIT and RELEASE calls will cause bugs in your program that are extremely difficult to find.
This method is used inside the class itself, and is not normally called from outside the class.

Parameters

ParameterDescription
pIdUsed for debugging purposes. If each call to Wait and Release has a unique pId, then it's easier to detect mismatched Wait/Release pairs.

Return Value

None

Example

self.Release(3)

See Also

Wait

Trace

Trace(string pStr)

Description

Sends a string to the Windows Debug Output, where it can be monitored using Debugview. Useful when debugging the class.

Parameters

ParameterDescription
pStrThe String to send to Debugview. The string will be prepended with the identifier [guts]

Return Value

None

Example

Self.Trace('Hello World')

Wait

Wait(Long pId)

Description

Waits the CriticalSection. Calls to Wait MUST be paired with a call to the RELEASE method. Each WAIT requires 1, and only 1, RELEASE. Failure to balance WAIT and RELEASE calls will cause bugs in your program that are extremely difficult to find.
This method is used inside the class itself, and is not normally called from outside the class.

Parameters

ParameterDescription
pIdUsed for debugging purposes. If each call to Wait and Release has a unique pId, then it's easier to detect mismatched Wait/Release pairs.

Return Value

None

Example

self.Wait(3)

See Also

Release

Version History

1.08 - 1 December 2015 1.07 - 16 April 2015 1.06 - 25 February 2015 1.05 - 30 July 2014 1.04 - 21 January 2014 1.03 - 13 January 2014 1.02 - 6 January 2014
1.01 - 4 November 2013
1.00 - 1 November 2013