Getting Started : Chapter 2 Building the Libraries : Extending Stingray Libraries
Extending Stingray Libraries
Stingray classes extend Microsoft Foundation Class (MFC) classes. You, in turn, can create classes that build on and extend the Stingray classes. To create these extensions, you need to understand the mechanism for importing and exporting symbols. This section first describes how Stingray itself does this, and then describes what you must do when creating Stingray extensions.
How Stingray Imports and Exports Symbols
In an extension library, symbols (functions and variables) must be exported so they have visibility to an application that is linking to the library. When an application links to an extended library, it imports the functions for use. Previously in Stingray Studio products there was no need to mark functions and variables for exportation, but this is now a requirement. Definition files (.def) are no longer used by the Stingray libraries to export functions and variables. Instead the C++ __declspec keyword is used to export and import functions and variables.
__declspec(dllexport) is used by the library to export.
__declspec(dllimport) is used by the application to import.
The Stingray libraries use a header file that defines common macros for exporting and importing functions and variables. Each product contains a header file called <product_name>ExportDefs.h that is located in the <stingray-installdir>\Include\<product_name> directory. During installation, a master header file called StingrayExportDefs.h is updated to #include each purchased and selected product's *ExportDefs.h header file to the master header file. The master header file is located in the <stingray-installdir>\Include directory.
The Stingray product implementation files define one of the following preprocessor symbols to indicate in our code that we are going to export symbols:
 
#define _SFL_EXPORT_IMPL
#define _OC_EXPORT_IMPL
#define _OE_EXPORT_IMPL
#define _OG_EXPORT_IMPL
#define _OT_EXPORT_IMPL
#define _OV_EXPORT_IMPL
 
#define _SFLEX_EXPORT_IMPL
#define _OCEX_EXPORT_IMPL
#define _OEEX_EXPORT_IMPL
#define _OGEX_EXPORT_IMPL
#define _OTEX_EXPORT_IMPL
#define _OVEX_EXPORT_IMPL
Versions of Stingray Studio older than Stingray Studio 2006 v3 have the following implementation preprocessor commands defined for backward compatibility:
 
#define _SFLDLL_IMPL _SFL_EXPORT_IMPL
#define _OC_DLL_IMPL _OC_EXPORT_IMPL
#define _OE_DLL_IMPL _OE_EXPORT_IMPL
#define _GXDLL_IMPL _OG_EXPORT_IMPL
#define _SECDLL_IMPL _OT_EXPORT_IMPL
#define _OV_DLL_IMPL _OV_EXPORT_IMPL
The Stingray libraries use preprocessor definitions to indicate a function’s or variable’s export/import status. StingrayExportDefs.h defines whether a function or variable is exported or imported by defining one of the following:
 
FOUNDATION_API
CHART_API
CHART_UT_API
EDIT_API
GRID_API
TOOLKIT_API
VIEWS_API
 
FOUNDATIONEX_API
CHARTEX_API
EDITEX_API
GRIDEX_API
TOOLKITEX_API
VIEWSEX_API
NOTE >> The above <product>_API macros should not be used in an extension library as they will cause symbol conflicts with exporting and importing. See the section below on using CustExtDefs.h to create extension libraries.
For example, the Foundation preprocessor is defined as:
 
#ifdef _SFLDLL // Dynamic Link Library
#ifdef _SFLDLL_IMPL
#define FOUNDATION_API __declspec( dllexport )
#define FOUNDATION_GLOBAL_FUNC_API(rettype)
extern "C" rettype __declspec(dllexport)
#else
#define FOUNDATION_API __declspec( dllimport )
// Reference to a GLOBAL Function
#define FOUNDATION_GLOBAL_FUNC_API(rettype)
extern "C" rettype __declspec(dllimport)
#endif
#else // !_SFLDLL // Static Link Library
#define FOUNDATION_API
// Reference to a GLOBAL Function: these must be inline
// definitions with no function prototype.
#define FOUNDATION_GLOBAL_FUNC_API(rettype)
extern "C" rettype __cdecl // Don't mangle the name.
// Must use __cdecl here.
#endif
A Stingray class might look like the following:
 
// StingrayClass.h
#pragma once
#include “StingrayExportDefs.h”
 
class CFoundationFoo
{
public:
FOUNDATION_API CFoundationFoo();
FOUNDATION_API virtual ~CFoundationFoo();
FOUNDATION_API virtual void Add(int iVal);
 
// FOUNDATION_DECLARE_MESSAGE_MAP() etc.
Private:
// Note private variables don’t need to be exported.
int m_Sum;
};
 
FOUNDATION_GLOBAL_FUNC_API(void) DoSomethingGlobal();
 
// StingrayClass.cpp
#include “StingrayClass.h”
 
#define _SFL_EXPORT_IMPL
 
CFoundationFoo::CFoundationFoo()
{
m_Sum = 0;
}
 
CFoundationFoo::~CFoundationFoo()
{
}
 
Void CFoundationFoo::Add(int iVal)
{
m_Sum += iVal;
}
 
FOUNDATION_GLOBAL_FUNC_API(void) DoSomethingGlobal()
{
// Do something.
}
When the above class is compiled, it will export the functions in the class because __declspec(dllexport) will be defined internal to the Stingray library that the class extends.
When a sample or application uses the CFoundationFoo class, it only needs to include its header file. Because _SFL_EXPORT_IMPL is not defined in the sample or application, the functions and variables will be imported for use because __declspec(dllimport) will be defined.
Using CustExtDefs.h in Stingray Extensions
Stingray extension libraries can be created as static libraries (lib) or dynamic link libraries (dll). Extension libraries are then linked to an application. An extension library needs to expose its functionality to the application by exporting its functions and variables so the application can import them for use.
Like the Stingray libraries themselves, there needs to be some code mechanism to switch between exporting and importing. The *_API macros that are defined for the Stingray libraries should not be used in a Stingray Extension library as they are meant for internal use by the Stingray libraries only.
To make it easier to create Stingray extension libraries, Stingray Studio provides the file CustExtDefs.h in the <stingray-installdir>\Include directory. Including CustExtDefs.h and using its predefined *_API preprocessors will remove the need to create a mechanism in code for switching between importing and exporting.
NOTE >> CustExtDefs.h provides some overrides of the basic MFC macros. For example, the override for DECLARE_MESSAGE_MAP() is CUSTEXT_DECLARE_MESSAGEMAP(). If there are macros that are needed but not provided by the header file, they need to be overridden in the extension library.
If the extension library is to be configured as a dynamic link library (dll), then the preprocessor settings in the library should include _CUSTEXTDLL so the appropriate export/import syntax is used. Static (lib) build configurations should not include this preprocessor symbol.
An extended Stingray class might look like the following:
 
// ExtendedClass.h
 
#pragma once
 
#include “CustExtDefs.h”
#include “StingrayClass.h”
 
#define MY_CUSTOMEXTENSION_MFC_OVERRIDE() \
CUSTEXT_GLOBAL_VAR_API static int x; \
CUSTEXT_CLASS_FUNC_API void MyMFCFunc() \
{ \
x = 4;
} \
 
class CSuperFoo: public CFoundationFoo
{
public:
CUSTEXT_CLASS_FUNC_API inline CSuperFoo(){}
CUSTEXT_CLASS_FUNC_API inline virtual CSuperFoo(){}
CUSTEXT_CLASS_FUNC_API void DoSomethingSuper();
 
// CUSTEXT_DECLARE_MESSAGE_MAP(), etc.
// CUSTEXT_DECLARE_DYNAMIC(CSuperFoo), etc.
// MY_CUSTEXTENSION_MFC_OVERRIDE(), etc.
 
};
 
// ExtendedClass.cpp
 
#define _CUSTEXT_EXPORT_IMPL
 
#include “ExtendedClass.h”
 
// Extension/Wrapper of the CFoundationFoo class.
CSuperFoo::DoSomethingSuper()
{
// Call Foo from the base Stingray class.
::Foo(5);
}
Extending Stingray Classes Locally in an Executable
Classes in a Stingray library can be extended in an executable locally, meaning that there is no further need to expose functionality outside of the executable. Classes that are extended locally in an executable do not need to use CustExtDefs.h or StingrayExportDefs.h.
The macros listed below are exceptions to the above rule. They need to use the overrides in StingrayExportDefs.h even in a locally extended executable.
GRID_DECLARE_CONTROL()
GRID_IMPLEMENT_CONTROL()
GRID_DECLARE_REGISTER()
GRID_IMPLEMENT_REGISTER()
TOOLKIT_DECLARE_BUTTON()
TOOLKIT_IMPLEMENT_BUTTON()
An extension class can be derived from a Stingray class without having to export/import symbols. In this case standard MFC macros can be used, such as DECLARE_MESSAGE_MAP(), without any export/import customization. Here is an example:
 
// MyExtensionClass.h
 
#pragma once
 
// Includes ...
 
class MyExtendedFoo : public CFoundationFoo
{
public:
// DECLARE_DYNAMIC(MyExtendedFoo) etc.
...
// Constructor
MyExtendedFoo() {}
};
 
// MyExtensionApp.cpp
 
int main()
{
// Using the extension class
MyExtendedFoo mf;
mf.Add(10);
 
return 0;
}