Interface Pointers

Dynamic memory areas are required for TwinCAT Vision in order, for example, to save images and containers. These images and containers are represented by variables of type ITcVnImage and ITcVnContainer. Since they are interfaces pointing to a dynamic memory area, these variables are called interface pointers.

With the TwinCAT Vision API functions, the associated memory management is automatically controlled within the functions; nothing needs to be observed here.

For example, a new image is created (800 x 600 pixels of the type TCVN_ET_USINT with one channel):

VAR
    hr        : HRESULT;
    ipImage   : ITcVnImage;
END_VAR

hr := F_VN_CreateImage(ipImage, 800, 600, TCVN_ET_USINT, 1, hr);

this image is now to be converted into an RGB image with 3 channels. Therefore 3 times as much memory is required. The following function call is sufficient for this:

hr := F_VN_ConvertColorSpace(ipImage, ipImage, TCVN_CST_GRAY_TO_RGB, hr);

Internally it frees up the previous memory area and allocates new memory in a size to suit. Interface pointers are thus a simple way of managing objects with dynamically required memory in the PLC.

WARNING

Consequences of misuse

If you do not follow the following instructions for the use of interface pointers, this can lead to complete system crashes and to memory leaks!

Reference counter

As soon as the variables are no longer exclusively used as function parameters for the TwinCAT Vision API functions, however, it is necessary to familiarize yourself more precisely with the concept of interface pointers.

It is important to know that the interface pointer variable is only a pointer to an object in memory. If you assign one variable to another, only the pointer to this memory area is copied, not the data itself.

This means that if the data in one of the two variables is changed, it is also automatically changed in the other.
If the memory in one of the variables is released (as in the above example F_VN_ConvertColorSpace) and the second variable still has its pointer to this memory area, accessing this already released memory tends to result in a system crash.

For this reason, the objects in the memory all have a reference counter. As soon as a variable is assigned to another, this reference counter must be increased. If a new pointer is assigned to a variable, the reference counter must be decreased again.

When the reference counter reaches 0, the memory is released. The corresponding data are thus completely deleted.

In order to indicate whether a pointer variable points to a valid memory area, this pointer must always be set to 0 if it is no longer in use (and the reference counter was thus reduced). By default, all created variables (and thus the pointers too) in der PLC are initially set to 0 when the program starts.

Interface Pointers 1:

Management of interface pointers

In the TwinCAT Vision API functions (marked by F_VN_...), the reference counter is automatically managed by interface pointers. Thus, nothing special needs to be observed when using them. However, if interface pointers are manually copied or used, for example, as function parameters, manual management is necessary (see below).

Checking the validity of interface pointers

Since the value 0 for an interface pointer basically corresponds to just one invalid memory area – and access to it also leads to a critical system error – it is important to check before each use whether the value is non-zero.

In den TwinCAT Vision API functions (F_VN_...), this check and the corresponding handling take place internally and automatically. However, this is not possible with interface methods, such as TcRelease() in the following sample, since their call already leads to an error if the variable is 0. The variable must therefore be checked explicitly beforehand.

WARNING

Method access to invalid memory area leads to system crash

An internal check is not possible for interface methods, since the corresponding objects must exist to call the method.

Even outside the method call, make sure that the corresponding object exists (the interface pointer should be non-zero).

In addition, a system crash does not necessarily always occur. If the invalid memory area coincidentally remains unused, a system crash may not occur.

Enabling an interface pointer

The enablement of an interface pointer variable consists of checking whether this is already 0 and otherwise reducing the reference counter and setting the variable to 0. As this is a frequent use case, a function FW_SafeRelease is provided that carries out all necessary steps with a single call:

hr := FW_SafeRelease(ADR(ipImage));

The internal mode of operation of the FW_SafeRelease function is illustrated by the following code snippet:

IF ipImage <> 0 THEN       // Check if interface pointer is already = 0
                           // If not, call .TcRelease() and set it to 0
    ipImage.TcRelease();   // decrement reference count
    ipImage := 0;          // set to 0
END_IF

Copying of an interface pointer

When copying an interface pointer, its reference counter must be manually incremented. A direct assignment by means of the := operator, as well as function/method/function block calls in which an interface pointer is transferred as an input, output or return value, count as a copying procedure. A transfer by means of I/O image also counts. The following method of incrementing the reference counter is available:

ipImage.TcAddRef(); // increment reference count

In addition, it must be ensured that the interface pointer to be written is enabled before copying. Otherwise memory leaks will occur. A manual procedure for copying an interface pointer could look like the following, for example:

IF ipSrcImage <> 0 THEN                // check if source pointer is valid
    FW_SafeRelease(ADR(ipDestImage));  // release destination pointer
    ipDestImage := ipSrcImage;         // assign source pointer to destination pointer
    ipSrcImage.TcAddRef();             // increment reference count
END_IF

Please note that only the interface pointer, not the underlying image is copied.

Rules of thumb

Stick to the following rules of thumb when handling interface pointers.

It is irrelevant whether the interface pointers are (displayable) images, containers, iterators, etc. – they are mutually interchangeable in the samples.

If you call a method on an interface pointer

It must be ensured that the interface pointer is valid
(check for <> 0):

IF ipImage <> 0 THEN
    ipImage.GetImageInfo(stImageInfo);
END_IF

In general it is recommended to use the alternative F_VN_ function (if it exists) instead of an interface method, because the check will then be automatically performed internally:

hr := F_VN_GetImageInfo(ipImage, stImageInfo, hr);

If you carry out the check and the call of the interface method in the same expression, you must use AND_THEN instead of AND. Only in that case will the rear part be executed if the front part returns TRUE:

IF ipIterator <> 0 AND_THEN ipIterator.CheckIfEnd() <> S_OK THEN
    ipIterator.GetContainer(ADR(ipElement));
END_IF

Manually copy an interface pointer

Make sure beforehand that the interface pointer to be written has been enabled. Increment the reference counter of the interface pointer after copying:

FW_SafeRelease(ADR(ipDestImage)); 
IF ipSrcImage <> 0 THEN
    ipDestImage := ipSrcImage;
    ipSrcImage.TcAddRef();
END_IF

Declare interface pointer in VAR_INPUT

Nothing needs to be observed for functions and methods. For programs and function blocks, the pointer in the variables is retained after the call and should therefore be set to 0 at the end of the POU:

VAR_INPUT
    ipSrcImage: ITcVnImage;
END_VAR
//… functional code
ipSrcImage := 0;

This ensures that the memory area can no longer be accessed the next time it is called, since it could have already been released outside. Increasing the reference counter within the POU is not a solution, since a new assignment overwrites the previous pointer and therefore cannot be released, resulting in a memory leak. If the interface pointer is to be kept, it should be copied to a local variable.

Declare interface pointer as reference in VAR

Release the interface pointer at the end of functions and methods. The interface pointer no longer exists after ending the POU; therefore the reference counter must be decremented beforehand. For programs and function blocks, the interface pointer should be released as soon as it is no longer required:

VAR 
    ipImageWork: ITcVnImage;
END_VAR
//… functional code
hr := FW_SafeRelease(ADR(ipImageWork));

Not recommended - declare interface pointer in VAR_OUTPUT

The declaration of an interface pointer in VAR_OUTPUT is not recommended, since the use of outputs is optional and thus memory leaks can occur.
In programs and function blocks, the pointer in the variables is retained even after the call, so that in the event of changes outside, a released memory area can be accessed in the function block.
To prevent this, you must always ensure that the outputs are used and, in the case of programs and function blocks, that the reference counter is incremented or the output variable is set to 0 after the output variable has been copied.
This is very laborious and error-prone, since you have to take care yourself to implement correctly at every point in the code. Therefore, transferring it as a reference in VAR_INPUT is recommended instead.

Notice

Memory leaks

The use of an interface pointer in VAR_OUTPUT is not recommended. Failure to observe the above points may result in memory leaks and system crashes.

Declare interface pointer as reference in VAR_INPUT

The transfer as a reference should be used if a change to the interface pointer (e.g. assigning a new image/container) is to be passed to the outside within the POU for further use there.

VAR_INPUT 
    ipDestImage: REFERENCE TO ITcVnImage;
END_VAR

The sample Self-written functions explains the correct use of interface pointers in self-written functions and methods.

Consequences of misuse

If an interface pointer variable is copied, but the reference counter is not adjusted at the same time with TcAddRef(), there are two pointers; however, only one of them is known to the program. Therefore, the data to which both interface pointer variables point will then be deleted as soon as one of the two variables is released. The release can take place both by a manually executed FW_SafeRelease and by a TwinCAT Vision API function that rewrites the variable. If the remaining interface pointer variable is then accessed by a method call, the system crashes as described above.

If, contrary to this, interface pointer variables are rewritten without prior release (e.g. through a := assignment), this leads to memory leaks. This can occur, for example, if interface pointers are used as method variables and not released before ending the method. An indication for memory leaks can be found in the information window of the AMS router (see Router memory). If the memory marked there as available decreases over time, this indicates a memory leak.