Jump to PiXCL Home page

PiXCL Extension Command Library API

Release v11.0 Last update: 04-Jan-2019

PiXCL provides the means for users to add extra commands to the available list that ships with the product. Some typical examples might be:

PiXCL maintains two primary internal symbol tables for Fixed Argument List commands (the large majority), and Variable Argument List commands, and a third, the User Command List, that is created when User commands are registered. User defined commands can be created with other programming tools such as C/C++, as DLLs so long as the appropriate argument format is used.

When PiXCL parses a keyword, it first checks the Fixed Argument List, and if the command is not found, it then checks the Variable Argument List, followed by the User Command List if it exists. If the command keyword is still not found, a syntax error is generated.

User commands are dynamically stored in the User Command List in alphabetical order using the RegisterUserCommand or RegisterExtLibCmdSet described below. There is no limit to the number of user commands that can be added to PiXCL.

How to set up an extension command in a DLL

PiXCL extension commands are created in DLLs with a specific, mandatory arglist sequence, as follows:

int  UserCommandName (int iArgs, int  anArgBuffer[ ], 
HINSTANCE hInst,
HWND hwnd,
BITMAPRECORD FirstBitmapRecord,
LPBITMAPRECORD lpBitmapRecord,
HINSTANCE hdibCurrent,
HPALETTE hpalCurrent,
LPBITMAPINFOHEADER lpbiCurrent);
 

This arg list provides all necessary pointer access into the PiXCL internals. An extension command does not have to, and generally will not, access all these arguments.

The user command handler calling code within PiXCL is similar to:

typedef int (*gpxUSERPROC)(int, int*, HINSTANCE, HWND, BITMAPRECORD,
LPBITMAPRECORD, HINSTANCE,
HPALETTE, LPBITMAPINFOHEADER);

HMODULE  hUserModule = NULL;
gpxUSERPROC fpUserFunction = NULL;
NUMVARRECORD *lpNumVarRec;
int iResult = 0;
hUserModule = LoadLibraryEx(lpDLLName, NULL,
LOAD_WITH_ALTERED_SEARCH_PATH);
 
fpUserFunction = (gpxUSERPROC)GetProcAddress(hUserModule,lpUserCommand);

iResult = (int)(fpUserFunction) (// set up the arg list
iArgs,                  // number of valid anArgbuffer entries

&anArgBuffer[0], // pointer to the first geoPiXCL arg buffer

hInst, // PiXCL app handle
hwnd, // PiXCL app window handle

&FirstBitmapRecord, // pointer to image list

lpBitmapRecord, // current bitmap record

hdibCurrent, // the current bitmap handle
hpalCurrent, // the current pallette
lpbiCurrent); // the current bitmap header
 
FreeLibrary(hUserModule);
  

Return Values

Each user command function within the DLL must return an integer value. If the function fails in some way, the return must be 0 or some negative number defined by you, and documented in your user command notes.

Using RegisterUserCommand in a PiXCL application

All user written commands have to be registered with a PiXCL application before they can be used, or else a syntax error will occur. Registering a command automatically enables user command processing.

RegisterUserCommand is generally used somewhere at the start of a program, but can be anywhere before the user command is to be parsed and executed. It registers one command at a time. User commands are implemented in DLL's, with multiple commands within a DLL possible.
Syntax: RegisterUserCommand(DLLname$, CmdName$,arg_type#1, . . . ,

arg_type#n,P_NUM_VARIABLE)

Parameters:

DLLname$

The name of the DLL that contains CmdName$. DLLname$ can be either just the name of the DLL without path or extension (which requires it to be in either the current directory, windows or system directory, in the PATH environment string), or the full path and extension.

CmdName$

The string that is user for the command e.g. "UserCommand1". Commands have the same constraints as built in commands: must start with "a-z", upper or lower case. This must not be the same as any predefined command or keyword, or else a syntax error will occur.

arg_type

token_value

meaning

buffer entries

C/C++ meaning

P_NUMBER

static integer

1

int

P_NUM_VARIABLE

integer pointer

1

See NUMVARRECORD

P_FP_NUMBER

static float

1

See FLOAT2INT

P_FP_VARIABLE

float pointer

1

See FPVARRECORD

P_STRING

static string

2

LPSTR, length less NULL.

P_STR_VARIABLE

string pointer

1

See STRVARRECORD

P_COORDINATE

integer pair

2

int, int

P_RGB

integer triplet

3

int, int, int (0-255)

P_RGBA

integer quad

4

int, int, int, int (0-255)

P_RECTANGLE

integer quad

4

int, int, int, int

P_VARIANT

input argument integer, string or fp

1

int | LPSTR | float

P_VARIANT_VARIABLE

output argument integer, string or fp

1

See Note #3

P_VARIANT_ARRAY

An array variable

While the number of arguments is arbitrary, some argument types and position are mandatory. Insertion of left and right round brackets enclosing the command arguments and comma delimiters are automatic.

Notes:

  1. The last argument MUST be a P_NUM_VARIABLE, as this is the default error return variable for the user command.

  2. It is not necessary to indicate the number of arguments in your user command definition, as this is calculated automatically.

  3. P_VARIANT_VARIABLE is a rarely used arg type, where your command requires an integer or float or string as the returned type. The PiXCL 10 interpreter takes care of checking the type. Your code has to pass the correct type back in the return pointer.

  4. P_VARIANT_ARRAY is used when defining commands that use arrays, along with the P_SQ_BRACKET_LEFT and P_SQ_BRACKET_RIGHT types. The array type is defined by the array name. Hence ArrayName is an integer array, ArrayName$ is a string array, ArrayName& is a floating point array.

  5. Array index containers ( [ ] ) are used in commands where arrays are used, such as when you want to pass an array index as part of the new command. For example, the CopyArray command is defined (less the enclosing round brackets) as

P_VARIANT_ARRAY, P_SQ_BRACKET_LEFT, P_NUMBER, P_SQ_BRACKET_RIGHT,
P_DELIMITER, P_VARIANT_ARRAY, P_SQ_BRACKET_LEFT, P_NUMBER, P_SQ_BRACKET_RIGHT,
P_DELIMITER, P_NUMBER, P_DELIMITER, P_NUM_VARIABLE

the P_NUMBER arguments are the array index values, and become part of the argument buffer.

A typical PiXCL command might be

RegisterUserCommand(DLL$, "UserCmd1", P_STRING, P_COORDINATE, P_NUM_VARIABLE)

This user command has four arguments, and could appear in your PiXCL script as, say,

UserCmd1("a string", 12,71, Result) or perhaps

UserCmd1(Name$, 12,71, Result) or perhaps

UserCmd1(Name$, Xcoord, Ycoord, Result)

The CopyArray command above might appear in a user command as

RegisterUserCommand(DLL$,"UserCopyArray",P_VARIANT_ARRAY, P_SQ_BRACKET_LEFT, P_NUMBER, P_SQ_BRACKET_RIGHT, P_DELIMITER, P_VARIANT_ARRAY, P_SQ_BRACKET_LEFT, P_NUMBER, P_SQ_BRACKET_RIGHT, P_DELIMITER, P_NUMBER, P_DELIMITER, P_NUM_VARIABLE)

Using RegisterExtLibCmdSet in a PiXCL application

This command is an alternative to RegisterUserCommand, and is designed to load all the commands in a command extension library at once. UnregisterUserCmds is the compimentary command.

RegisterExtLibCmdSet is generally used somewhere at the start of a program, but can be anywhere before the user commands are to be parsed and executed. User commands are implemented in DLL's, with multiple commands within a DLL possible. The DLL is loaded to access the command load function that is written in the DLL, then usually, but not always, unloaded.

Syntax: RegisterExtLibCmdSet(DLLName$,LOADDLLNOW | LOADONDEMAND, Result)

Parameters:

DLLName$

The name of the DLL that contains the extension commands. DLLname$ can be either just the name of the DLL without path or extension (which requires it to be in either the current directory, windows or system directory, or a directory in the PATH environment string), or the full path and extension.

LOADDLLNOW

LOADONDEMAND

This token instructs PiXCL to load the extension command DLL immediately, and leave it attatched to the PiXCL process.

This token instructs PiXCL to load the DLL only when it is needed, and unload once the command has been executed.

Result

The number of commands successfully registered, otherwise 0.

An extension command DLL must always include an exported function called RegisterExtLibCmdSet that contains the alphabetical list of functions and calling sequences, even if only one command is in the DLL. Within this function, a loop makes a call the PiXCL command RegisterUserCommand described above.

Sample user command DLL in Visual Studio project

We have provided a working project that compiles with Microsoft Visual Studio 2008 or later that implements several commands and custom dialog boxes. This User Command DLL is named "PXLucmds.dll" and includes

The "gpxlucmd.h" file

This file should be included into your custom library project. The details are listed here for convenience.

 
/* defines */
#define P_BRACKET_LEFT 0
#define P_BRACKET_RIGHT 1
#define P_SQ_BRACKET_LEFT 2 // array subscript container
#define P_SQ_BRACKET_RIGHT 3 // array subscript container
#define P_DELIMITER 4
#define P_NUMBER 5 // 1 integer
#define P_COORDINATE 6 // 2 integers
#define P_RGB 7 // 3 integers 0-255
#define P_RGBA 8 // 4 integers 0-255
#define P_RECTANGLE 9 // 4 integers
// Reserved values 10 - 19
#define P_NUMBER64 10
#define P_NUM64_VARIABLE 11
#define P_FP64_NUMBER 12
#define P_FP64_VARIABLE 13
 
#define P_STRING 20
#define P_STR_VARIABLE 21
#define P_NUM_VARIABLE 22
#define P_FP_VARIABLE 23
#define P_FP_NUMBER 24
#define P_VARIANT 25 // input arg string, int or fp, static or variable
#define P_VARIANT_VARIABLE 26 // output arg string, integer or floating point variable
#define P_VARIANT_ARRAY 27 // arrays, for CopyArray command + similar
#define P_LABEL 28
#define P_NUM_EQUALITY 29
#define P_FP_EQUALITY 30
#define P_STR_EQUALITY 31
#define P_TOKEN 32
#define P_DWORD 33
#define P_END 34
// Next must be the same as in geoPiXCL
#define ARG_BUFFER_SIZE    64 // DO NOT CHANGE THIS OR YOU WILL BREAK PIXCL
// characters //
#define CHAR_CR 13
#define CHAR_LF 10
#define CHAR_EOF 26
#define CHAR_COMMENT_START '{'
#define CHAR_COMMENT_END '}'
#define CHAR_BRACKET_LEFT '('
#define CHAR_BRACKET_RIGHT ')'
#define CHAR_SQBRACKET_LEFT '['
#define CHAR_SQBRACKET_RIGHT ']'
#define CHAR_DELIMITER ','
#define CHAR_STRING '"'
#define CHAR_LABEL_END ':'
#define CHAR_TAB 9
#define CHAR_SPACE 32
#define CHAR_HATCH '#'
#define CHAR_AMPERSAND	 '@' // char used to indicate defined constant
#define CHAR_MODULUS	 '%' // char used to indicate modulus operator
#define CHAR_END_STRVAR '$' // Char that identifies a string variable
#define CHAR_END_FPVAR '&' // Char that identifies a float variable
#define CHAR_CONCAT '+' // String concatenation char
#define CHAR_DELIM_FILTER ',' // Char that delimits filters in FileOpen
#define CHAR_VKEY_START '{' // Char preceeding a virtual key in SendKeys
#define CHAR_VKEY_END '}' // Char following a virtual key in SendKeys
#define CHAR_SHIFT '+' // Char representing Shift key in SendKeys
#define CHAR_ALT '%' // Char representing Alt key in SendKeys
#define CHAR_CTRL '^' // Char representing Ctrl key in SendKeys
#define CHAR_ENTER '~' // Shorthand char for Enter key in SendKeys
#define CHAR_DELIM_ENVIRON ',' // Char that delims. environment strs in RunExt
 
/* typedefs */
//
typedef struct _NUMVARRECORD { // Numeric variable record structure
LPSTR lpNumVarOffset; // Offset of var's identifier within script
int nValue; // Variable's current numeric value
struct _NUMVARRECORD *Next; // Pointer to next variable record
} NUMVARRECORD;


//
typedef struct _FPVARRECORD { // Floating point variable record structure
LPSTR lpFpVarOffset; // Offset of var's identifier within script
float fpValue; // Variable's current numeric value
struct _FPVARRECORD *Next; // Pointer to next variable record
} FPVARRECORD;

//
typedef struct _STRVARRECORD { // String variable record structure
LPSTR lpStrVarOffset; // Offset of var's identifier within script
LPSTR lpBlockContents; // Pointer to mem block holding contents
UINT nSize; // Str var content's size (inc. ending 0)
struct _STRVARRECORD *Next; // Pointer to next string variable record
struct _STRVARRECORD *Previous;// Pointer to prev string variable record
} STRVARRECORD;


// Array variable record structure contains the pointers to the first
// element of an array of size uArraySize and type uArrayType. Each element
// in the array is pointer to a variable of the specified type. Any number
// of array variables can be created.
 
typedef struct _ARRAYVARRECORD {
LPSTR lpArrayVarOffset; // Offset of var's identifier within script
UINT uArraySize;
UINT uArrayType; // 1 = string$; 3 = integer; 5 = float&
union {
STRVARRECORD *lpStrArrayRec; // pointer to string var array[0]
NUMVARRECORD *lpNumArrayRec; // pointer to integer var array[0]
FPVARRECORD *lpFpArrayRec; // pointer to fp var array[0]
};
struct _ARRAYVARRECORD *Next; // pointer to next variable
} ARRAYVARRECORD;
 
typedef struct _BITMAPRECORD { // Bitmap record structure
LPSTR lpBlockFilename; // Pointer to mem block holding filename
HGLOBAL hbm; // Handle to block holding packed DIB
HBITMAP hDS; // Handle to DIBSection. Used as a flag
RGBQUAD *rgbmap; // Colourmap data for paletted DIBSections
HPALETTE hpal; // Handle to bitmap's logical palette
intiColorSpace; // Current color space of image
BOOL bPreview; // TRUE = approx 256x256 preview image
PGeoTIFF*lpGeoTIFF; // pointer to geotiff tag data
struct _BITMAPRECORD *Next; // Pointer to next bitmap record
struct _BITMAPRECORD *Previous;// Pointer to prev bitmap record
} BITMAPRECORD;

/* FLOAT2BIN is required because PiXCL floating point numbers are 32 bit
single precision numbers, stored in an integer. The union is used to convert
between types when storing the results into the relevent anArgBuffer[n] element.
*/
typedef union {
intiValue;
floatfpValue;
} FLOAT2BIN;


#define EXPORTED_FUNCTION extern __declspec(dllexport)

BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID);

EXPORTED_FUNCTION int gpxUser_1(int, int[], HINSTANCE, HWND, BITMAPRECORD,
LPBITMAPRECORD, HINSTANCE, HPALETTE, LPBITMAPINFOHEADER);
EXPORTED_FUNCTION int gpxUser_2(int, int[], HINSTANCE, HWND, BITMAPRECORD, 
LPBITMAPRECORD, HINSTANCE, HPALETTE, LPBITMAPINFOHEADER);
EXPORTED_FUNCTION int gpxUser_3(int, int[], HINSTANCE, HWND, BITMAPRECORD, 
LPBITMAPRECORD, HINSTANCE, HPALETTE, LPBITMAPINFOHEADER);
EXPORTED_FUNCTION int gpxUser_4(int, int[], HINSTANCE, HWND, BITMAPRECORD, 
LPBITMAPRECORD, HINSTANCE, HPALETTE, LPBITMAPINFOHEADER);
EXPORTED_FUNCTION int gpxUser_5(int, int[], HINSTANCE, HWND, BITMAPRECORD, 
LPBITMAPRECORD, HINSTANCE, HPALETTE, LPBITMAPINFOHEADER);
/* Globals */
HINSTANCE ghinstDLL = NULL;
 

Setting the DLL function exports

The functions you develop in your DLL must be exported using the same name as the user command name you want to use in the RegisterUserCommand, or the user command won't be accessible, and will cause a syntax error when you run the PiXCL application.

Please note that while PiXCL internal commands names are NOT case-sensitive, user commands ARE, because your user DLL exported function names are case-sensitive.

Visual C/C++ Sample User Command Project

We have provided a sample project that implements the above commands. You need Visual Studio 2017 to build the sample library PXLucmds.dll .


Copyright 2011-2019 PiXCL Automation Technologies Inc. All Rights Reserved.