Platform Integration Aspects: Implementing Prototyper intrinsics

Embedded Wizard provides an environment to develop and test highly platform independent GUI applications. In practice, based on one and the same project you can generate code for any target system. This is achieved by Embedded Wizard own programming language Chora designed to strictly conceal all platform specific constructs while being expressive and universal enough to allow you to formulate any application specific operation.

From this high claim for platform independence results the consequence, that applications developed with Embedded Wizard have per default no access to the underlying device, be it the hardware, middleware software or the operating system. You can consider the application as running enclosed in a box. In order to interact or exchange data with the target device, the target specific dependencies have to be implemented within so-called native statements and inline code members. These contents are thereupon taken over when generating code and can be compiled by your target compiler. For more details concerning the integration with the target device please see the section Integrating with the device.

The restrictions resulting from the platform independence also affect the prototyping environment. This environment allows you to test and debug individual GUI components or the entire GUI application directly within the Embedded Wizard Studio without having to generate code for the target system, compile the code and upload the resulting binary to the target device. During prototyping Embedded Wizard interprets the Chora code only. No further C compiler nor extern build processes are used. Since native statements and inline code members contain target specific code, Embedded Wizard can't interpret, these statements and members are simply ignored when prototyping the application.

Ignoring the native statements and inline code members during the prototyping is in most cases acceptable. You can still test all GUI components. During prototyping, however, the components will use default values for all the target specific data and no target specific operation can be performed. If you consider this behavior as a limitation, this chapter explains how you extend Embedded Wizard by target specific functionality and so use real target data and perform target specific operations even during the prototyping. For example, you can extend Embedded Wizard to operate on a data base or exchange data via network.

In order to extend Embedded Wizard by target specific functionality you have to implement so-called Intrinsics Module. From technical point of view, such module is an ordinary Microsoft Windows DLL (dynamically loadable library) containing C functions where you accommodate the target specific functionality. Embedded Wizard recognizes and loads the intrinsics modules automatically. Once loaded, the functions implemented in a module are exposed in Chora and can be invoked similarly as you invoke any other built-in Chora function. These functions are called Intrinsics.

Let's assume, your GUI application has to read text files stored on a SD card. The necessary target specific code to open and read the files is consequently implemented in a native statement as mentioned above. If you want similar operations to be performed during the prototyping, you have to implement an intrinsics module containing a C function to open and read a file. The following figure demonstrates the both cases marked with the numbers 1 and 2:

When the user presses the push button Load text file contents ... the Chora code found below the button is executed. This code contains $if .. $else .. $endif preprocessor directives to distinguish between the prototyping and the non-prototyping cases:

1.When generating code for the target system, the macro $prototyper results in the value false. Consequently, the $if $prototyper condition is not fulfilled causing the code section below the $else directive to be taken in account. This section contains a native statement, which in turn includes an invocations of a C function GetTextFileContent implemented in some C module compiled and linked when building the binary. At the runtime in the target device this function takes care of loading the file contents from the SD card.

2.When testing the GUI component in the prototyping environment of Embedded Wizard Studio, the macro $prototyper results in the value true. Consequently, the $if $prototyper condition is fulfilled causing the immediately following code section to be taken in account. This section contains an invocations of a function GetTextFileContent in Chora syntax. This function is implemented in an intrinsics module. At its execution time, the function accesses Windows file system to open and read the requested file.

The above example demonstrates an ideal case, where the intrinsics module can reuse the source code (here the C function GetTextFileContent) implemented originally for the target system. If this is not possible, you can invoke from the intrinsics module any Win32 (Microsoft Windows API) function or use third-party libraries providing the necessary functionality on Microsoft Windows. In this way you are very flexible to extend Embedded Wizard functionality. The following sections provide more details concerning the creation and usage of intrinsics modules.

Create a new Visual Studio project

An Embedded Wizard intrinsics module is an ordinary Win32 DLL (dynamically loadable library). You create a new DLL by using Microsoft Visual Studio C++. The free Expression edition should be fully sufficient for this task.

IMPORTANT

Please note, this document and all included screenshots are based on the version Microsoft Visual Studio Express 2012. Be aware of eventual differences if you are using a newer version.

If you have not done it yet, please visit Microsoft website, register and download the Visual Studio C++ Express edition.

Start Microsoft Visual Studio Express.

From the FILE menu select the command to create a new project e.g. New project.... Thereupon a New Project dialog appears.

In the New Project dialog select Visual C++, Win32 Project, specify the name and the location for the new project. In our example we name the project IntrinsicsModule:

Confirm the dialog by clicking on the OK button. Thereupon the Win32 Application Wizard dialog is shown. Press the button Next to switch to the next page:

Select DLL as Application type. Also activate Empty project otherwise the new project will contain unnecessary dummy files:

Complete the project configuration by clicking on the Finish button.

With the above steps you have created a new Visual Studio project for a Win32 DLL. In the Solution Explorer you can see that the project is still empty. When you would start the build now, you will get a DLL file with no functionality inside. Before we start to add new functionality we have to adjust few parameters in the project settings.

In the Solution Explorer click with the right mouse button on item representing the intrinsics module project and from the context menu select the command Properties:

In the thereupon shown IntrinsicsModule Property Pages select the configuration All configurations:

Still in the same dialog ensure that the page General is selected and change there the property Target Extension to the value .ewi. This will ensure, that the resulting DLL has the file extension *.ewi as expected by Embedded Wizard in order to be recognized as a valid intrinsics module:

In the page VC++ Directories enhance the property Include Directories by $(EMWI_DIR)\Chora\Sdk. This is necessary for Visual Studio to find include files needed for the intrinsics module development. Please note the semicolon separating the just entered text from the text stored originally in the property. Don't delete nor overwrite the original contents of the property:

In the page C/C++, Language change the property Threat WChar_t As Built In Type to No. This is necessary to suppress warnings when working with wide-char (16-bit) C strings:

Confirm the made modifications by clicking on the OK button.

So far the project configuration is completed. In the next step you should add a new C file where later the functionality of the intrinsics module will be implemented. For this purpose:

In the Solution Explorer click with the right mouse button on item representing the intrinsics module project and from the context menu select the commands Add and in the sub-menu New Item ...:

In the thereupon shown Add New Item dialog select Visual C++, Code, C++ File and enter the name of the new file main.c to add:

Confirm the operation by clicking on the Add button.

The new file has been added and Visual Studio shows now the file main.c empty without any contents. Add following #include directive to the file so declarations common for all intrinsics are available:

In the last step add to your project the C file ewrte.c found in the folder Chora\Sdk just below your Embedded Wizard installation directory. This file implements functionality common for intrinsics and as such has to belong to each intrinsics module Visual Studio project. For this purpose:

In the Solution Explorer click with the right mouse button on item representing the intrinsics module project and from the context menu select the commands Add and in the sub-menu Existing Item ...:

In the thereupon shown Add Existing Item dialog navigate to your Embedded Wizard installation directory and then to its sub-folder Chora\Sdk. Within this folder select the file ewrte.c and confirm by clicking on the button Add:

Implement the C functions to export

An intrinsics module enhances the prototyping environment of Embedded Wizard Studio by functionality not available there per default. That could be, for example, the access to a file system. By implementing an intrinsics module you allow the GUI application to perform the respective file operations even when testing the application directly within Embedded Wizard Studio.

In the first step you have to specify which operations are needed and should be implemented in the intrinsics module. Our example will demonstrate this on the already mentioned access to a file system. The intrinsics module will contain functions to enumerate files stored in a folder and to read the contents of a text file. In the real target system, the respective files would be stored on a SD card or another embedded file system. The intrinsics module, in turn, will access the files from a folder found below the directory of your Embedded Wizard project. We see following five C functions as adequate to create such interface:

void OpenFolder( XString aFolder, XString aPattern ) void CloseFolder( void ) XInt32 GetNoOfFiles( void ) XString GetFileName( XInt32 aFileNo ) XString GetTextFileContent( XString aFilePath )

The function OpenFolder() has the job to collect all files stored in the given folder aFolder and matching the file name pattern aPattern. The resulting information is stored internally in the intrinsics module. This information remains valid until the function CloseFolder() has been.

The function GetNoOfFiles() returns how many files have been collected during the preceding OpenFolder() invocation. With the function GetFileName(), the names of the collected files can be queried. The parameter aFileNo identifies in this case the respective file. The first file has the number 0, the second has the number 1, and so far. This simple interface permits the GUI application to query folder contents and enumerate files stored there.

The last function GetTextFileContent() has the job to open and read the content of a text file specified in the parameter aFilePath. The content is returned as a string the GUI application can evaluate, display, etc.

Please note the data types used in the above C functions. These types correspond to instant data types available in Chora. This is in so far important as the functions are intended to be called from Chora interpreter. The declaration of each individual function has to conform consequently the data type system of Chora. Following table gives you an overview of Chora data types and the corresponding C types available when creating intrinsics:

Chora data type

C representation

void

void

int8, int16, int32

typedef signed char XInt8; typedef signed short XInt16; typedef signed int XInt32;

uint8, uint16, uint32

typedef unsigned char XUInt8; typedef unsigned short XUInt16; typedef unsigned int XUInt32;

float

typedef float XFloat;

bool

typedef char XBool;

char

typedef unsigned short XChar;

string

typedef XChar* XString;

handle

typedef unsigned long XHandle;

point

typedef struct { XInt32 X; XInt32 Y; } XPoint;

rect

typedef struct { XPoint Point1; XPoint Point2; } XRect;

color

typedef struct { XUInt8 Red; XUInt8 Green; XUInt8 Blue; XUInt8 Alpha; } XColor;

IMPORTANT

When implementing your own intrinsic functions you have to limit their declaration to the above listed data types. No other types can be used! By the way, the above listed C types are equal to the found in the generated C code and in the native code implemented for integration purpose with the target system. Accordingly, when implementing an intrinsics module you should be able to reuse the code implemented for the target system.

Let's start implementing the both functions OpenFolder() and CloseFolder(). In Microsoft Visual Studio edit the file main.c as follows:

#include "ewrte.h" /* Include VC++ header files needed in this example. */ #include <stdio.h> #include <wchar.h> #include <io.h> #include <windows.h> /* Following variables will store the collected file names. */ static int NoOfFiles = 0; static wchar_t** Files = 0; /* The function frees the information collected by the preceding OpenFolder() invocation. */ static void CloseFolder( void ) { int fileNo = 0; /* Release the memory reserved for the collected file names. */ for ( ; fileNo < NoOfFiles; fileNo++ ) if ( Files[ fileNo ]) EwFree( Files[ fileNo ]); /* Release the memory reserved for the array with file names. */ EwFree( Files ); /* No collected data anymore. */ Files = 0; NoOfFiles = 0; } /* The function evaluates the given folder and collects all files matching aPattern in the global array 'Files'. The folder is searched relative to the directory where the Embedded Wizard project is found. */ static void OpenFolder( XString aFolder, XString aPattern ) { wchar_t path[ MAX_PATH ]; long handle; struct _wfinddata_t fileInfo; int fileNo = 0; /* First ensure that names of evtl. previously collected files are freed. */ CloseFolder(); /* The folder is searched relative to the current working directory. This corresponds to the directory of the opened Embedded Wizard project. Limit to files matching the pattern. */ wcscpy_s( path, MAX_PATH, aFolder ); wcscat_s( path, MAX_PATH, L"\" ); wcscat_s( path, MAX_PATH, aPattern ); /* Phase 1: count the files matching the pattern. */ if (( handle = _wfindfirst( path, &fileInfo )) != -1 ) { do { /* Is this a file? */ if ( !( fileInfo.attrib & _A_SUBDIR )) NoOfFiles++; } while (( _wfindnext( handle, &fileInfo ) == 0 )); /* Terminate the search operation and release unused memory. */ _findclose( handle ); } /* Phase 2: Allocate memory for an array containing the file names. */ Files = EwAlloc( NoOfFiles * sizeof( XString )); memset( Files, 0, NoOfFiles * sizeof( XString )); /* Phase 3: Collect the file names matching the pattern. */ if (( handle = _wfindfirst( path, &fileInfo )) != -1 ) { do { /* Is this a file? Collect it in the array 'Files' */ if ( !( fileInfo.attrib & _A_SUBDIR ) && ( fileNo < NoOfFiles )) { int len = wcslen( fileInfo.name ); Files[ fileNo ] = EwAlloc(( len + 1 ) * sizeof( wchar_t )); wcscpy_s( Files[ fileNo++ ], len + 1, fileInfo.name ); } } while (( _wfindnext( handle, &fileInfo ) == 0 )); /* Terminate the search operation and release unused memory. */ _findclose( handle ); } }

Now enhance the implementation in main.c by the next both functions to query the number and the names of the collected files:

/* The following function just returns how many files have been collected by the preceding OpenFolder() invocation. */ static XInt32 GetNoOfFiles( void ) { return NoOfFiles; } /* The following function returns the collected file with the given number. If the file is not available, the function returns an empty string. */ static XString GetFileName( XInt32 aFileNo ) { /* The requested file is not existing. Return an empty string. */ if (( aFileNo < 0 ) || ( aFileNo >= NoOfFiles )) return 0; /* Return an Embedded Wizard string containing a copy of the collected file name. */ return EwNewString( Files[ aFileNo ]); }

In the code above, please note the usage of the function EwNewString(). This function ensures that the returned value is a correct string, as expected by Embedded Wizard. Returning directly the pointer stored in the array Files or any other pointer to a string created by your own implementation would cause Embedded Wizard Studio to behave unpredictably or even to crash the prototyping environment. Due to the memory management strings should always be handled carefully.

Similarly to the programming language C, Embedded Wizard manages strings as arrays of character codes with 0 (zero) code as terminator at the end of a string. Accordingly, when you implement intrinsic functions you can access and evaluate the string content easily. You should however know, that in Embedded Wizard created applications the characters within a string are generally 16-bit wide (16-bit C wchar_t type). Moreover, empty strings can optionally be represented by a NULL pointer. Your implementation should thus be ready to handle the situation of a string being NULL. Accordingly, a function, which returns an empty string, can return a NULL pointer.

The function EwNewString() belongs to the Runtime Environment (RTE) of the C Platform Package providing common functionality to run Embedded Wizard generated applications in the target system. Few of the RTE functions are also available when you implement an intrinsics module. The table below provides an overview of the functions you can invoke from the intrinsics implementation similarly as you do from the native code in the target device:

Runtime Environment function

Description

EwAlloc()

C Platform Package function to reserve a memory block from the memory heap used by Embedded Wizard.

EwFree()

C Platform Package function to free a memory block reserved on Embedded Wizard own memory heap.

EwNewString()

C Platform Package function to create a duplicate of a wide-character (16-bit) string.

EwNewStringAnsi()

C Platform Package function to create a wide-character (16-bit) string from an ANSI (8-bit) coded source string.

EwNewStringUtf8()

C Platform Package function to create a wide-character (16-bit) string from an UTF-8 coded source string.

EwNewStringChar()

C Platform Package function to create a string filled with a character.

EwGetStringLength()

C Platform Package function to query the length (the number of characters) of a string.

Finally add to main.c the implementation for the remaining intrinsic function GetTextFileContent():

/* The following function opens the given text file, reads its content and returns it as a string. The parameter aFilePath specifies the location of the file relative to the directory where the Embedded Wizard project is found. */ static XString GetTextFileContent( XString aFilePath ) { FILE* file; long size; XString result; unsigned char* buf; /* Try to open the file. If the operation fails, return an empty string. The file is searched relative to the current working directory. This corresponds to the directory of the opened Embedded Wizard project. */ if ( _wfopen_s( &file, aFilePath, L"rt" ) != 0 ) return 0; /* Query the size of the file in bytes (character). */ fseek( file, 0, SEEK_END ); size = ftell( file ); fseek( file, 0, SEEK_SET ); /* Reserve temp. memory for the file to load. */ buf = EwAlloc( size ); /* Load the file content into the buffer and close the file. */ fread( buf, 1, size, file ); fclose( file ); /* Assuming, the loaded content is encoded in Utf8 format, create from it a new Embedded Wizard string. */ result = EwNewStringUtf8( buf, size ); /* Free the temp. used memory and ... */ EwFree( buf ); /* ... return the just created string. */ return result; }

With the above steps the intrinsics module contains all C functions intended to be called from Embedded Wizard. As you learned, you implement the functions using C and you can include all the libraries provided in Microsoft Visual Studio as well as invoke functions of the Win32 API. In other words, you implement the functions exact as you do with any other program module intended to run on Microsoft Windows. The unique two limitations are the strict usage of Embedded Wizard conform data types in the declaration of each function and the usage of provided RTE functions. The last aspect is especially important when the function returns a string.

Provide description for the Intrinsics Module

In order to be recognized as a valid intrinsics module its implementation has to include an EW_MODULE(...) section as shown in the code below:

EW_MODULE ( INTRINSICS_IFC_VERSION, L"IntrinsicsModule", L"Description of the module" )

Except the last string literal the contents of this section are always equal. In the last string literal you can specify optional description for the intrinsics module. Although this information is not evaluated in the actual version, it is a good style to describe the module properly.

In our example, add the following code to the file main.c:

EW_MODULE ( INTRINSICS_IFC_VERSION, L"IntrinsicsModule", L"Intrinsics module to access the file system" )

Declare the interface for the Intrinsics Module

In the above section we explained how to implement the functionality of an intrinsics module. As you learned, intrinsics are ordinary C functions using Embedded Wizard conform data types in their declaration. As next the functions have to be exported so they can be seen from the Embedded Wizard prototyping environment. For this purpose it is necessary to enhance the implementation of the intrinsics module by an EW_DEFINE_INTRINSICS..EW_END_OF_INTRINSICS table. This table refers all affected C functions and provides information about their parameters and return values. Knowing this, Embedded Wizard can find and invoke the functions.

In our example, add the following code to the file main.c. Ensure the code is arranged below the C functions:

/* Table describing all intrinsics implemented in this module */ EW_DEFINE_INTRINSICS EW_INTRINSIC ( L"IntrinsicOpenFolder", L"void", 2, L"string,string", L"aFolder,aPattern", OpenFolder ) EW_INTRINSIC ( L"IntrinsicCloseFolder", L"void", 0, L"", L"", CloseFolder ) EW_INTRINSIC ( L"IntrinsicGetNoOfFiles", L"int32", 0, L"", L"", GetNoOfFiles ) EW_INTRINSIC ( L"IntrinsicGetFileName", L"string", 1, L"int32", L"aFileNo", GetFileName ) EW_INTRINSIC ( L"IntrinsicGetTextFileContent", L"string", 1, L"string", L"aFilePath", GetTextFileContent ) EW_END_OF_INTRINSICS

The EW_DEFINE_INTRINSICS..EW_END_OF_INTRINSICS table consists of EW_INTRINSIC(...) sections, one for each function you want to expose to Embedded Wizard as an intrinsic. In our example case, we have specified an interface consisting of 5 functions. Accordingly, the table contains 5 EW_INTRINSIC(...) sections. Each section expects 6 parameters described below:

Parameter

Description

1

16-bit wchar_t string literal specifying the name of the intrinsic how it should be exposed in Embedded Wizard prototyping environment. This name has to start with a letter A..Z or a..z followed by optional digits 0..9, underscore signs _ and further letters A..Z, a..z.

2

16-bit wchar_t string literal specifying the Chora data type of the value returned by the intrinsic. This can be one of int8, int16, int32, uint8, uint16, uint32, float, bool, char, string, point, rect, color according to the declaration of the respective C function. If the intrinsic does not return any value, specify void in this parameter.

3

An integer specifying the number of parameters expected by the intrinsic. This value should correspond to the number of parameters declared in the respective C function. If the function does not expect any parameter, specify the value 0 here.

4

16-bit wchar_t string literal containing a comma separated list of Chora data types matching the parameters of the respective C function and without any white spaces in-between. Valid data types are int8, int16, int32, uint8, uint16, uint32, float, bool, char, string, point, rect, color. The number of data types found in this list has to correspond to the integer value specified in the third parameter of EW_INTRINSIC(). If the function does not expect any parameter, the string is empty.

5

16-bit wchar_t string literal containing a comma separated list with parameter names according to the declaration of the respective C function and without any white spaces in-between. Each parameter name has to start with a letter A..Z or a..z followed by optional digits 0..9, underscore signs _ and further letters A..Z, a..z. The number of names found in this list has to correspond to the integer value specified in the third parameter of EW_INTRINSIC(). If the function does not expect any parameter, the string is empty.

6

The C function to be associated to the intrinsic.

Compile the Intrinsics Module

Once all C functions are implemented and the interface is declared, the intrinsics module is ready to be compiled:

In Microsoft Visual Studio select the menu command BUILDBuild Solution:

In the case, your implementation contains some errors, Visual Studio will report this accordingly. If this occurs, review your code and verify whether the project configuration is correct.

Use the Intrinsics Module in an Embedded Wizard project

If you have followed all steps described in preceding sections and Visual Studio compiled the project without any error, the intrinsics module is ready to be used. Following are the steps how to add the intrinsics module to your Embedded Wizard project:

Use Windows Explorer to navigate to the directory containing the Visual Studio project. The intrinsics module *.ewi file is stored in the sub-folder Debug or Release depending on the selected configuration in Visual Studio:

Copy the file and paste it into the directory containing your Embedded Wizard project.

From now Embedded Wizard will load the intrinsics module automatically each time you start the prototyping environment. This is also the case when you switch to a Composer page containing a GUI component. Accordingly, even when editing the GUI component in Composer all the functionality provided in the intrinsics module can be used. When loading an intrinsics module Embedded Wizard displays a message in the Log window:

The usage of the functionality implemented in the intrinsics module is similar to how you invoke Chora built-functions or even how you invoke functions in C. The following code snippet uses the intrinsics module from our example to enumerate all files stored within a folder SVG_Icons and to create from the file names a string. Please note the names of the functions used in this implementation (e.g. IntrinsicOpenFolder). These correspond to the names specified in the intrinsics table:

var string names = ""; $if $prototyper var int32 fileNo = 0; // Open the folder 'SVG_Icons' and collect all text files found there. IntrinsicOpenFolder( "SVG_Icons", "*.txt" ); // Enumerate all collected files and prepare a string with their names. for ( ; fileNo < IntrinsicGetNoOfFiles(); fileNo = fileNo + 1 ) names = names + ", " + IntrinsicGetFileName( fileNo ); // Close the folder and release the collected names. IntrinsicCloseFolder(); $endif // Display all the names in the Log window trace names;

Please note in the code above the usage of the $if .. $endif preprocessor directives. These ensure that the enclosed code section is executed only in the prototyping environment. It is not taken in account when generating code for the target system as there is no intrinsics module functionality available. When implementing code intended to invoke an intrinsic you should alway enclose the code between a pair of such $if .. $endif directives, otherwise Embedded Wizard will report errors.

To demonstrate practically the creation and the usage of intrinsics modules we have prepared the following example. It contains a Visual Studio C++ 2012 project to create an intrinsics module intended to enumerate files found within a folder. This is exact the intrinsics module we analyzed and created in the preceding sections. This example also contains an Embedded Wizard project using this intrinsics module:

DOWNLOAD EXAMPLE

Please note, the example presents eventually features available as of version 8.20

The GUI component implemented in this project limits to display a list with collected file names and the content of the actually selected file. For demonstration purpose, the files contain SVG path data for simple icons. By using the Up/Down keys you can navigate in the list and scroll it. When you try out this example you will find that the intrinsics modules are not limited to the Prototyper window, but their functionality is also available in the Composer window when you edit the GUI component. Please note the file names listed in Composer:

Understand the life cycle of an Intrinsics Module

From technical point of view intrinsics modules are ordinary Microsoft Windows DLLs (dynamically loadable libraries). In order to use the functionality implemented in such module, Embedded Wizard Studio loads it into its own address space. Later, when the module is not needed anymore, Embedded Wizard Studio unloads it again. This section explains this process step by step:

1.Just in the moment when the user starts the Prototyper or switches to a Composer page containing a GUI component, Embedded Wizard Studio initiates a new instance of its prototyping environment. During this step Embedded Wizard searches the project directory as well as all directories of units belonging to the project for files with the extension *.ewi. This results in a list of potential intrinsics modules.

2.Thereupon Embedded Wizard Studio tries to load the estimated *.ewi files into its address space. The order in which the files are processed is not predetermined. Files that have the right file extension *.ewi but are not valid intrinsics modules are recognized and excluded from the loading. A respective warning is shown in the Log window in such case.

3.Immediately after loading an intrinsics module, the affected module is initialized. To handle the initialization the module can implement the EwInitModule() function. The function can return a pointer to a data structure associated to this instance of the intrinsics module or it returns NULL pointer if such structure is not needed. This data structure is called context. Following is the declaration of the EwInitModule() function:

EW_ENTRYPOINT void* EwInitModule( void ) { /* Perform initialization code for the module. */ ... return 0; }

4.Once all intrinsics modules have been loaded and initialized, the functions EwLinkModule() are invoked for each module implementing such function. Since these functions are invoked after all modules are loaded, they are ideal to finalize the initialization and (as its name suggest) to link the modules with each other. For example, if module A depends on the functionality implemented in module B, module A can implement the function EwLinkModule() and search for the module B. In its first parameter aList, the function receives a list with all loaded modules. The usage of this parameter as well as the application case of linking modules are explained in the separate section Access Intrinsics implemented in foreign Modules. The second parameter aContext contains the value returned from the corresponding EwInitModule() function. It can be used to identify the instance of the intrinsics module:

EW_ENTRYPOINT void EwLinkModule( XModuleContext* aList, void* aContext ) { /* Finalize the initialization and/or search for other modules to link with. */ ... }

5.After all intrinsics modules have performed their EwLinkModule() invocations, the modules are ready to be used. Concrete, the prototyping environment can invoke the intrinsics implemented in the modules.

6.When the user closes the Prototyper window or switches the Composer page, the associated prototyping environment is de-initialized again. As first the functions EwUnlinkModule() are invoked for each module implementing such function. This is ideal to resolve relations established between the modules during the preceding EwLinkModule() invocation. The function EwUnlinkModule() can also be used to perform the first de-initialization phase. The function receives in its single parameter aContext the value returned from the corresponding EwInitModule() function. This value can be used to identify the instance of the intrinsics module:

EW_ENTRYPOINT void EwUnlinkModule( void* aContext ) { /* Resolve previously established relations to other modules and/or de-initialize this module. */ ... }

7.Finally, all intrinsics modules are unloaded from the address space of Embedded Wizard Studio. The order in which the modules are unloaded is not predetermined. If a module implements the function EwDoneModule(), the function is invoked shortly before the module is unloaded giving it the last chance to release eventually used resources and finalize its de-initialization. Following is the declaration of the EwDoneModule() function. Please note the parameter aContext containing the value returned from the corresponding EwInitModule() function. This value can be used to identify the instance of the intrinsics module:

EW_ENTRYPOINT void EwDoneModule( void* aContext ) { /* De-initialize this module. */ ... }

The usage of a context is optional. It allows you to associate data to each instance of the intrinsics module. This data can thereupon be used in each invocation of the above explained functions. You should consider, tht at the runtime Embedded Wizard Studio can manage two separate prototyping environments (1: Prototyper and 2: Composer). Accordingly two instances of an intrinsics module can exist. By using the context you are able to distinguish them properly. Within the context you can also store any state information associated to the instance. The following code demonstrates the usage of the context:

/* Definition of a structure to store context data associated to each instance of the intrinsics module. */ typedef struct { int StateA; int StateB; ... } ModuleContext; EW_ENTRYPOINT void* EwInitModule( void ) { /* Reserve memory for the context. */ ModuleContext* context = malloc( sizeof( ModuleContext )); /* Perform initialization code for the module. */ context->StateA = ... context->StateB = ... return context; } EW_ENTRYPOINT void EwLinkModule( XModuleContext* aList, void* aContext ) { ModuleContext* context = (ModuleContext*)aContext; /* Use the context data. */ context->StateA = ... context->StateB = ... } EW_ENTRYPOINT void EwUnlinkModule( void* aContext ) { ModuleContext* context = (ModuleContext*)aContext; /* Use the context data. */ context->StateA = ... context->StateB = ... } EW_ENTRYPOINT void EwDoneModule( void* aContext ) { /* De-initialize this module and free the context data. */ free( aContext ); }

Besides the above explained functions each intrinsics module can also implement the functions EwIntrinsicsProlog() and EwIntrinsicsEpilog(). These functions are invoked automatically just before and shortly after the usage of intrinsic functions. Especially in case of multiple instances of one and the same intrinsics module (1: Prototyper, 2: Composer) the functions help to store and restore the state information individual to each instance. The functions are declared as follows. Again, the functions receive in the parameter aContext the context information associated to the particular instance:

EW_ENTRYPOINT void EwIntrinsicsProlog( void* aContext ) { ... } EW_ENTRYPOINT void EwIntrinsicsEpilog( void* aContext ) { ... }

Use Intrinsics Modules conditionally

Per default, Embedded Wizard tries to load all *.ewi files found in the project directory and all directories of units belonging to the project. As long as the *.ewi file does implement a valid intrinsics module, the file will be loaded. By explicitly specifying a condition in the name of the *.ewi file you can control this process more precisely. To specify the condition you append at the end of the *.ewi file name a pair of square brackets [...]. Between the brackets you specify identifiers for the desired condition. Following conditions are possible:

Condition

Description

composer

The intrinsics module will be loaded only for the prototyping environment used in Composer window. For example: IntrinsicsModule[composer].ewi.

prototyper

The intrinsics module will be loaded only for the prototyping environment used in Prototyper window. For example: IntrinsicsModule[prototyper].ewi.

Profile

The intrinsics module will be loaded only when the specified profile is actually selected. For example: IntrinsicsModule[Win32].ewi will be loaded only when the profile Win32 is selected.

composer+ (plus) Profile

The intrinsics module will be loaded only for the prototyping environment used in Composer window and only when the specified profile is actually selected. For example: IntrinsicsModule[composer+Win32].ewi will be loaded only when switching in a Composer page with a GUI component and the profile Win32 is selected.

prototyper+ (plus) Profile

The intrinsics module will be loaded only for the prototyping environment used in Prototyper window and only when the specified profile is actually selected. For example: IntrinsicsModule[prototyper+Win32].ewi will be loaded only when starting the Prototyper and the profile Win32 is selected.

If necessary, multiple conditions separated by a , (comma) sign can be specified. The affected intrinsics module will then be loaded if one of the conditions is fulfilled. For example: IntrinsicsModule[Win32,iOS].ewi will be loaded if the profile Win32 or iOS is selected. In all other cases the intrinsics module will be ignored.

Be careful with global variables

When you play with the example application provided in the section Use the Intrinsics Module in an Embedded Wizard project you will find that it has a problem. Concrete, when you switch the Composer page during the Prototyper was active and then you interact with the Prototyper, it does not update the file names anymore. The reason for this problem is the usage of globale variables in the intrinsics module to store the collected files:

/* Following variables will store the collected file names. */ static int NoOfFiles = 0; static wchar_t** Files = 0;

Being global, these variables are shared by the both instances of the intrinsics module. One instance used by the Composer window and the other by the Prototyper window. Just in the moment you switch the Composer page, the instance used by it is de-initialized and the intrinsic IntrinsicCloseFolder() is invoked. This in turn discards all previously collected file names and sets the global variables to the value 0. This modification affects also the Prototyper window, which can't access the file names anymore.

In our simple case, the interference between the Composer and Prototyper is less critical. It produces incorrect screen contents. Nothing more. In other cases, however, the problem can become grave. Especially when one instance changes/frees memory still referenced by the second instance. This can lead to a crash of Embedded Wizard Studio or at least to behave unpredictably. The following sub-sections explain different approaches how to deal with the problem of global variables.

Approach 1: Use two copies of the same Intrinsics Module

This is the simplest workaround. As explained in the section Use Intrinsics Modules conditionally you can specify in the name of the *.ewi file a condition when the module should be taken in account. Accordingly, you can duplicate the file and specify the condition composer for the first file and prototyper for the second file. At the runtime, Embedded Wizard will treat the both files as separate intrinsics modules. One module will be used only when working in the Composer window and the other will be used when working in the Prototyper window. The both do not share any global variables anymore. Following figure demonstrates the resulting files in Windows Explorer:

Approach 2: Store and restore the global variables

As explained in the section Understand the life cycle of an Intrinsics Module when you implement the functions EwIntrinsicProlog() and EwIntrinsicEpilog(), Embedded Wizard will invoke them automatically just before and shortly after the usage of an intrinsic. This is ideal to restore and store the global variables within the context associated to the respective instance of the intrinsics module. The following code demonstrates the necessary modification of the main.c file:

/* Definition of a structure to store context data associated to each instance of the intrinsics module. It will contain a copy of the global variables. */ typedef struct { int NoOfFiles; wchar_t** Files; } ModuleContext; /* This function will be called when initializing an instance of the intrinsics module. It creates a context data. */ EW_ENTRYPOINT void* EwInitModule( void ) { /* Reserve memory for the context. */ ModuleContext* context = EwAlloc( sizeof( ModuleContext )); /* Perform initialization code for the module. */ context->NoOfFiles = 0; context->Files = 0; return context; } /* This function will be called when de-initializing an instance of the intrinsics module. It frees the context data. */ EW_ENTRYPOINT void EwDoneModule( void* aContext ) { /* De-initialize this module and free the context data. */ EwFree( aContext ); } /* This function will be called before the usage of an intrinsic. It restores the global variables from the context. */ EW_ENTRYPOINT void EwIntrinsicsProlog( void* aContext ) { ModuleContext* context = (ModuleContext*)aContext; /* Restore the global variables from the context. */ NoOfFiles = context->NoOfFiles; Files = context->Files; } /* This function will be called shortly after the usage of an intrinsic. It stores the global variables in the context. */ EW_ENTRYPOINT void EwIntrinsicsEpilog( void* aContext ) { ModuleContext* context = (ModuleContext*)aContext; /* Store the global variables in the context. */ context->NoOfFiles = NoOfFiles; context->Files = Files; }

If you prefer this approach, just implement the above demonstrated functions and ensure to store properly all global variables in the context. No further handling is necessary. Below you can download the file explorer example, this time using the here explained approach to avoid the global variables problem:

DOWNLOAD EXAMPLE

Please note, the example presents eventually features available as of version 8.20

Approach 3: Avoid global variables

The third approach is the most consequent: don't use global variables. Instead implement the intrinsic functions so that they exchange the state information enclosed in data structures via parameters and return values. This expects the declaration of the C functions to be adapted as shown below:

XHandle OpenFolder( XString aFolder, XString aPattern ) void CloseFolder( XHandle aHandle ) XInt32 GetNoOfFiles( XHandle aHandle ) XString GetFileName( XHandle aHandle, XInt32 aFileNo ) XString GetTextFileContent( XString aFilePath )

The first modification affects the OpenFolder() function. In its original implementation the function stores the collected file names within a global array. The new version uses a dynamically allocated memory area for this purpose and returns the pointer to this area as handle data type. All other functions intended to evaluate the collected file names have been enhanced by an additional aHandle parameter where the pointer to the memory containing the collected file names is passed - the pointer returned by the preceding OpenFolder() invocation. The following code shows the modified version of the first 4 functions. The last function GetTextFileContent() is not affected by this adaptation:

/* Following type definition determines the data structure where collected file names are stored. */ typedef struct { int NoOfFiles; wchar_t** Files; } FileNames; /* The function frees the information collected by the preceding OpenFolder() invocation and passed in the parameter aHandle. */ static void CloseFolder( XHandle aHandle ) { FileNames* fileNames = (FileNames*)aHandle; int fileNo = 0; /* No collected files. */ if ( !fileNames ) return; /* Release the memory reserved for the collected file names. */ for ( ; fileNo < fileNames->NoOfFiles; fileNo++ ) if ( fileNames->Files[ fileNo ]) EwFree( fileNames->Files[ fileNo ]); /* Release the memory reserved for the array with file names as well as for the data structure. */ EwFree( fileNames->Files ); EwFree( fileNames ); } /* The function evaluates the given folder and collects all files matching aPattern in the global array 'Files'. The folder is searched relative to the directory where the Embedded Wizard project is found. The function returns a pointer to a data structure containing the collected names. */ static XHandle OpenFolder( XString aFolder, XString aPattern ) { wchar_t path[ MAX_PATH ]; long handle; struct _wfinddata_t fileInfo; int noOfFiles = 0; int fileNo = 0; FileNames* fileNames = 0; /* The folder is searched relative to the current working directory. This corresponds to the directory of the opened Embedded Wizard project. Limit to files matching the pattern. */ wcscpy_s( path, MAX_PATH, aFolder ); wcscat_s( path, MAX_PATH, L"\" ); wcscat_s( path, MAX_PATH, aPattern ); /* Phase 1: count the files matching the pattern. */ if (( handle = _wfindfirst( path, &fileInfo )) != -1 ) { do { /* Is this a file? */ if ( !( fileInfo.attrib & _A_SUBDIR )) noOfFiles++; } while (( _wfindnext( handle, &fileInfo ) == 0 )); /* Terminate the search operation and release unused memory. */ _findclose( handle ); } /* Phase 2: Allocate memory for a data structure and an array where the file names will be stored. */ fileNames = EwAlloc( sizeof( FileNames )); fileNames->Files = EwAlloc( noOfFiles * sizeof( XString )); fileNames->NoOfFiles = noOfFiles; memset( fileNames->Files, 0, noOfFiles * sizeof( XString )); /* Phase 3: Collect the file names matching the pattern. */ if (( handle = _wfindfirst( path, &fileInfo )) != -1 ) { do { /* Is this a file? Collect it in the array 'Files' */ if ( !( fileInfo.attrib & _A_SUBDIR ) && ( fileNo < noOfFiles )) { int len = wcslen( fileInfo.name ); fileNames->Files[ fileNo ] = EwAlloc(( len + 1 ) * sizeof( wchar_t )); wcscpy_s( fileNames->Files[ fileNo++ ], len + 1, fileInfo.name ); } } while (( _wfindnext( handle, &fileInfo ) == 0 )); /* Terminate the search operation and release unused memory. */ _findclose( handle ); } /* Return a pointer to the data structure containing the collected files. */ return (XHandle)fileNames; } /* The following function just returns how many files have been collected by the preceding OpenFolder() invocation and passed in data structure aHandle. */ static XInt32 GetNoOfFiles( XHandle aHandle ) { FileNames* fileNames = (FileNames*)aHandle; /* No collected files. */ if ( !fileNames ) return 0; return fileNames->NoOfFiles; } /* The following function returns the collected file with the given number. If the file is not available, the function returns an empty string. */ static XString GetFileName( XHandle aHandle, XInt32 aFileNo ) { FileNames* fileNames = (FileNames*)aHandle; /* No collected files. */ if ( !fileNames ) return 0; /* The requested file is not existing. Return an empty string. */ if (( aFileNo < 0 ) || ( aFileNo >= fileNames->NoOfFiles )) return 0; /* Return an Embedded Wizard string containing a copy of the collected file name. */ return EwNewString( fileNames->Files[ aFileNo ]); }

Since the declaration of the functions have been changed, it is also obligatory to adapt the table describing the exported intrinsics accordingly. Concrete, the first intrinsic returns now a handle value and other intrinsics expect one aHandle parameter more. Following is the new adapted version:

/* Table describing all intrinsics implemented in this module */ EW_DEFINE_INTRINSICS EW_INTRINSIC ( L"IntrinsicOpenFolder", L"handle", 2, L"string,string", L"aFolder,aPattern", OpenFolder ) EW_INTRINSIC ( L"IntrinsicCloseFolder", L"void", 1, L"handle", L"aHandle", CloseFolder ) EW_INTRINSIC ( L"IntrinsicGetNoOfFiles", L"int32", 1, L"handle", L"aHandle", GetNoOfFiles ) EW_INTRINSIC ( L"IntrinsicGetFileName", L"string", 2, L"handle,int32", L"aHandle,aFileNo", GetFileName ) EW_INTRINSIC ( L"IntrinsicGetTextFileContent", L"string", 1, L"string", L"aFilePath", GetTextFileContent ) EW_END_OF_INTRINSICS

The modified declaration of intrinsics affects also the usage of them within the Embedded Wizard project. The simple loop used to enumerate text files stored within a folder SVG_Icons will look as follows. Please note the new variable fileNames intended to store the pointer to the list with collected file names:

var string names = ""; $if $prototyper var int32 fileNo = 0; // Open the folder 'SVG_Icons' and collect all text files found there. // Pointer to data structure containing the collected files are stored // in the variable 'fileNames'. var handle fileNames = IntrinsicOpenFolder( "SVG_Icons", "*.txt" ); // Enumerate all collected files and prepare a string with their names. // Please note the usage of the variable 'fileNames' in the invocation of the // intrinsic functions. for ( ; fileNo < IntrinsicGetNoOfFiles( fileNames ); fileNo = fileNo + 1 ) names = names + ", " + IntrinsicGetFileName( fileNames, fileNo ); // Close the folder and release the collected names. IntrinsicCloseFolder( fileNames ); $endif // Display all the names in the Log window trace names;

Below you can download the third version of the file explorer example, this time using the here explained approach to store the collected files in dynamically created data structures instead of global variables:

DOWNLOAD EXAMPLE

Please note, the example presents eventually features available as of version 8.20

Access Intrinsics implemented in foreign Modules

Usually intrinsics modules have no dependencies to any other module. Our above example of an intrinsics module to access and enumerate files found on the local files system demonstrates this application case. In particular cases, however, it is necessary for one module to access and invoke functions implemented in foreign modules. Typically, this is the case if your intrinsics module uses Graphics Engine functionality.

Graphics Engine implements functions needed to create and display GUI contents. In the target system it is available as a static library or as source code depending on your license. Accordingly, your implementation can access and use this functionality without difficulties. In order to have the same functionality available in the prototyping environment, Embedded Wizard Platform Packages include intrinsics modules containing the appropriate version of the Graphics Engine.

When you start the Prototyper or you switch to a Composer page containing a GUI component, the Graphics Engine intrinsics module found in the Platform Package is loaded automatically. Your own intrinsic modules can thereupon link to the Graphics Engine intrinsics module and use its functionality. Let's assume, you implement an intrinsics module to create bitmaps and fill them with a dynamically generated QR-code image. For this purpose, your implementation will use at least the Graphics Engine functions EwCreateBitmap(), EwLockBitmap() and EwUnlockBitmap().

To link with the Graphics Engine module you implement in your own intrinsics module the function EwLinkModule() (Please see the section Understand the life cycle of an Intrinsics Module for more details). Within this function you search for the desired module (e.g. GraphicsEngine) and once you found it, you search within the module for the desired functions (e.g. EwCreateBitmap()). This operation returns a pointer to a function (an entry point) your implementation can thereupon invoke as it does with any other function. Following code demonstrates these operations:

/* In order to create and load bitmaps the intrinsics module will need an access to the Graphics Engine API. This API is implemented in a separate intrinsics module. To use it, pointers to the API functions will be obtained and stored. */ static void* EwCreateBitmapProc = 0; static void* EwLockBitmapProc = 0; static void* EwUnlockBitmapProc = 0; /* The function is called after all intrinsics modules has finalized their initialization. It determines the entry points for few intrinsics found in the foreign module 'GraphicsEngine' and needed to create bitmaps. */ EW_ENTRYPOINT void EwLinkModule( XModuleContext* aList, void* aContext ) { /* First search all loaded intrinsics modules for the 'GraphicsEngine'. */ XModuleContext* module = EwFindModule( aList, "GraphicsEngine" ); /* Then query the desired entry points and store them within global variables. */ if ( module ) { EwCreateBitmapProc = EwFindProc( module, "EwCreateBitmap" ); EwLockBitmapProc = EwFindProc( module, "EwLockBitmap" ); EwUnlockBitmapProc = EwFindProc( module, "EwUnlockBitmap" ); } }

To find the desired module you use the function EwFindModule() as shown above. In its second parameter the function expects the name of the searched module. If the module is available (it has been loaded into the prototyping environment), the function returns a pointer identifying the module. If the module was not found, NULL is returned consequently. Knowing the pointer to the module it is possible as next to search for its exported functions (entry points). For this purpose the function EwFindProc() is used. If it could be found, the function EwFindProc() returns a pointer to the searched entry point. Otherwise it returns NULL.

Knowing the entry point it is possible to invoke the corresponding function. For this purpose you have to implement wrapper functions. The declaration of the wrapper functions have to be equal to the original declaration of the respective function found in the foreign module. This ensures that all parameters and return values are passed in the function invocation as expected. Following is the implementation of such wrapper functions for the three Graphics Engine functions mentioned above::

/* Wrapper to the Graphics Engine API function EwCreateBitmap(). This function is implemented in the separate 'GraphicsEngine' intrinsics module. Its job is to create a new empty bitmap. */ XBitmap* EwCreateBitmap( int aFormat, XPoint aFrameSize, XInt32 aFrameDelay, XInt32 aNoOfFrames ) { typedef XBitmap* (*tmp)( int aFormat, XPoint aFrameSize, XInt32 aFrameDelay, XInt32 aNoOfFrames ); /* Only, if the entry point to the API function is known */ if ( EwCreateBitmapProc ) return ((tmp)EwCreateBitmapProc)( aFormat, aFrameSize, aFrameDelay, aNoOfFrames ); return 0; } /* Wrapper to the Graphics Engine API function EwLockBitmap(). This function is implemented in the separate 'GraphicsEngine' intrinsics module. Its job is to lock the bitmap for direct memory access. */ XBitmapLock* EwLockBitmap( XBitmap* aBitmap, XInt32 aFrameNo, XRect aArea, XBool aRead, XBool aWrite ) { typedef XBitmapLock* (*tmp)( XBitmap* aBitmap, XInt32 aFrameNo, XRect aArea, XBool aRead, XBool aWrite ); /* Only, if the entry point to the API function is known */ if ( EwLockBitmapProc ) return ((tmp)EwLockBitmapProc)( aBitmap, aFrameNo, aArea, aRead, aWrite ); return 0; } /* Wrapper to the Graphics Engine API function EwUnlockBitmap(). This function is implemented in the separate 'GraphicsEngine' intrinsics module. Its job is to unlock the bitmap after the direct memory access is done. */ void EwUnlockBitmap( XBitmapLock* aLock ) { typedef void (*tmp)( XBitmapLock* aLock ); /* Only, if the entry point to the API function is known */ if ( EwUnlockBitmapProc ) ((tmp)EwUnlockBitmapProc)( aLock ); }

Depending on your application case you will eventually need to include one or more header files belonging to the implementation of the foreign intrinsics module. In the above example, the functions use diverse data types (e.g. XBitmap or XBitmapLock) known in the Graphics Engine itself. Accordingly, in the file containing the implementation of your intrinsics module enhance the include directives by ewgfx.h:

#include "ewrte.h" /* Include the Graphics Engine header file */ #include "ewgfx.h"

Also adapt the include paths in Visual Studio project properties to explicitly involve the Platform Package folder containing the Graphics Engine header files. Ensure to select the folder for the Graphics Engine matching the color format used in your project (e.g. RGBA8888). These folders are found below the setup directory of Embedded Wizard. Also add the current folder (.\) to the include path:

To demonstrate practically how to access functionality found in a foreign intrinsics module we have prepared the following example. It contains a Visual Studio C++ 2012 project to create an intrinsics module intended to render from a given text the corresponding QR-code image and return it via Extern Bitmap interface. Since this intrinsics module depends on the functionality available in the Graphics Engine, it uses the above explained approach to find and access the functions EwCreateBitmap(), EwLockBitmap() and EwUnlockBitmap():

DOWNLOAD EXAMPLE

Please note, the example presents eventually features available as of version 8.20

This example contains also an Embedded Wizard project with a GUI component where you can enter some text. When you start the GUI component in the Prototyper and edit the text, you will see how the displayed QR-code image changes dynamically:

Take in account security aspects

Intrinsics modules contain code to enhance the prototyping environment by user specific functionality available in the target system only. This code is executed in context and with privileges of the Embedded Wizard Studio process. Be careful if you don't trust the author of the intrinsics module. In order to prevent an intrinsics module from being loaded you can delete the corresponding *.ewi file or rename its file extension to e.g. *.bak. In this case be aware of eventual error messages resulting from the missing functionality.