Compiled Language Extensions
 

Newsgroups: comp.lang.c and comp.lang.c++ and comp.lang.c++.leda and comp.lang.c++.moderated and comp.lang.cobol and comp.lang.fortran


Table of Contents

Introduction
Structure of a Compiled Extension
Creating Your Libraries
Managing Your Libraries
Debugging Your Libraries
Examples
Function Definitions

Introduction
PowerWeb's Compiled API Extensions provide the fastest executing method of extending the functionality of PowerWeb Server++. With the generic non-language-specific API Interface Functions you can use any language that can be compiled into a DLL, such as C, C++, COBOL or FORTRAN. PowerWeb's consistency is also carried across to interpretive languages such as Perl and Rexx which can also use the same API functions.

Compiled API Extensions are stored within dynamic link libraries (DLLs) which are loaded upon demand by PowerWeb.

Compiled API Extensions can be used as a faster and more flexible alternative to CGI, providing a tight integration with PowerWeb. The API Interface functions allow you access to PowerWeb's internal engine and configuration settings, giving you a powerful robust server application development environment.

PowerWeb also allows you to extend its internal engine by defining API Hooks which selectively override aspects of its behaviour, such as user authentication and access control. When you write these hooks in a compiled language, you are extending PowerWeb with no performance penalty - your extensions are seamlessly integrated into the main engine.

With PowerWeb's multi-language API support, you can prototype your application or extension within Perl or Rexx, and then choose a final delivery language. In most cases, Perl or Rexx will be fast enough, but for time-critical applications, a compiled language such as C has not only the lowest load-time overhead, but also executes faster.


Structure of a Compiled Extension
PowerWeb calls Compiled Extensions with a "parcel" that is a magic cookie for further communication between PowerWeb and the DLL.

All C-compatible functions called by PowerWeb Server++ have the following definition:

typedef long (*pfnAPI)(void* parcel);
By using this "parcel", you can gain access to any PowerWeb variable through the PowerWeb API functions, which are documented in the PowerWeb API Function Reference Manual.

Your Compiled Extension is expected to return an integer result indicating the success or failure of your script, according to the following table:


Creating Your Libraries
If you are using IBM's CSet++ compiler, you can compile with dynamic linking to the runtime library (as shown in our "\powerweb\tour\source\*" sample source code directory), otherwise you should compile with static linking to your C runtime library to prevent conflicts. You should compile with multi-threading enabled and specify that a DLL should be generated, and not a standalone executable.

You should also ensure that "\powerweb\system\include" is on your INCLUDE path and that "\powerweb\system\lib" is on your LIB path. Refer to the file "\powerweb\system\tour\source\tour.nmk" for a sample NMAKE script.

You will need to link your DLL with the "PowerAPI.Lib" library module which is found in the "\powerweb\system\lib" directory. Function prototypes for all the PowerWeb API interface functions are found in "\powerweb\system\include\PowerAPI.hpp".

IBM CSet++ or VisualAge C++ compiler:

IBM's CSet++ compiler uses the compiler switches:

/Gd+ /Ge- /Gm+

For a complete example, see the "\powerweb\system\tour\source" directory which includes make files and header files.

Watcom C++ version 10 compiler:

Watcom 10.x uses the following Compiler switches:

-s no stack checking (a good idea inside a DLL)

-bm multithreaded

-bd dll

-otexan maximum optimisation

-5r Pentium optimized and register calling convention


and the following Linker .DEF file template:
system os2v2 dll initinstance terminstance
#
# object file(s)
#
file YourFile.obj
#
# the DLL name
#
name YourLib.dll
library powerapi.lib
#
# the module name in upper case
#
option modname=YOURLIB
#
# The following are VERY IMPORTANT to ensure
# your DLL does not conflict with the main
# PowerWeb DLLs.
#
option manyautodata
option caseexact
segment type code loadoncall shared
segment type data loadoncall nonshared readwrite
#
# the exported functions... case sensitive!
#
export YourFunctionCall

Managing Your Libraries
PowerWeb Scripts can be stored in any directory, but the guidelines below will help you manage all your modules. The PowerWeb Server++ is initially configured during installation to support extensions in the following suggested directory.

Suggested Location for C Modules:
Assuming your server root is "\powerweb", put your library DLL in the directory "\powerweb\bin", so that you can load it with the URL "/bin/module!function" where "module" is the name of your DLL (without the file extension) and "function" is the name of the function within your DLL to call.

Example URL: "/bin/property!ShowMenu"

PowerWeb automatically adds the "bin" sub-directory to your LIBPATH environment setting on PowerWeb startup.

PowerWeb automatically associates files within the bin directory as compiled DLL extensions. This is set up through the URL Alias Editor. If you create sub-directories under this directory, they will also be recognised as compiled DLL extension directories.

You can create further directories in other locations by simply defining an alias to that directory and giving it an object type of "API Script". Remember to include that directory on your LIBPATH.


Debugging Your Libraries
When you need to debug your compiled API extension, you can do so with the following method under IBM's VisualAge C++ (similar approaches will work with other compilers and other languages):

Compile your program with IBM's VisualAge C++ version 3 (with at least fixpack CTC304 installed) with debugging on, then load powerweb through IPMD (also known as ICSDEBUG) as follows (assuming PowerWeb 4.01 release 3 is installed on the E drive):

e:
cd \powerweb\sys401.r3\bin
ipmd pwchain -de:\powerweb
Then put a "load occurrence" breakpoint on your DLL (mentioning the FULL path in the edit box).
Then run the program and submit your HTTP request.

IPMD will stop PowerWeb when your DLL is loaded. You can then set breakpoints within your DLL.


Examples
Perform Access Control via the Remote User's IP Address:
This particular example limits access to a single IP address. It is obviously easier to set up a security rule in the configuration to provide this type of protection, but the example shows the basic components of writing an API Extension.
long AccessControlHook(void *parcel)
{
  char   ip[32];

  ServerReadText(parcel, "Connect:/RemoteAddress", ip, sizeof(ip));

  if (strcmp(ip, "12.34.56.78") != 0) {
    ServerWriteInteger(parcel, "Request:/StatusCode", 403);
    return HOOK_ERROR;
  }

  return HOOK_OK;
}
Guided Tour Sample Code:
The Guided Tour has a set of complete examples(view the source code) of API hooks for forms processing, dynamic document translation, simple direct-call hooks, and a tree view of your server settings.

Function Definitions
You will need to read the Power API Function Reference Manual before writing code to call these functions.

The PowerWeb Server++ C/C++ Interface Function Definitions Are:

Return
Value
Function
Name
Function
Parameters

long ServerGetConfig (void* parcel, void** handle);
long ServerGetServer (void* parcel, void** handle);
long ServerGetConnect (void* parcel, void** handle);
long ServerGetRequest (void* parcel, void** handle);
long ServerGetParameters (void* parcel, void** handle);
long ServerGetArguments (void* parcel, void** handle);

long ServerFind (void* parent, const char* pszName, void** item);
long ServerKind (void* handle, unsigned long* type);
long ServerSize (void* handle, unsigned long* size);
long ServerName (void* handle, char* value, unsigned long size);
long ServerFlush (void* handle);

long ServerReadInteger (void* parent, const char* pszName, long* value);
long ServerReadFloat (void* parent, const char* pszName, double* value);
long ServerReadText (void* parent, const char* pszName, char* value, unsigned long size);
long ServerReadBinary (void* parent, const char* pszName, unsigned char* value, unsigned long size);

long ServerWriteInteger (void* parent, const char* pszName, long value);
long ServerWriteFloat (void* parent, const char* pszName, double value);
long ServerWriteText (void* parent, const char* pszName, const char* value);
long ServerWriteBinary (void* parent, const char* pszName, const unsigned char* value, unsigned long size);

long ServerAppendText (void* parent, const char* pszName, const char* value);
long ServerAppendBinary (void* parent, const char* pszName, const unsigned char* value, unsigned long size);

long ServerNewInteger (void* parent, const char* pszName, long value);
long ServerNewFloat (void* parent, const char* pszName, double value);
long ServerNewText (void* parent, const char* pszName, const char* value);
long ServerNewBinary (void* parent, const char* pszName, const unsigned char* value, unsigned long size);
long ServerNewList (void* parent, const char* pszName);
long ServerNewSemaphore (void* parent, const char* pszName);
long ServerDelete (void* handle);

long ServerParent (void* here, void** handle);
long ServerChild (void* here, void** handle);
long ServerSibling (void* here, void** handle);

- ServerInterpret (void* parcel, const char* code);

Parameters:

void* parcel
The parcel pointer is the same one that was passed to the API hook from PowerWeb Server++. It should be treated as a "magic cookie" which identifies the current context. The parcel is unique within the system, and allows for easy and safe multi-threading.

void** handle
The handle pointer is another "magic cookie", this time one that refers to a specific PowerWeb variable directory or individual variable. It is used to query a specific variable or to navigate through the hierarchy. It is a pointer to a pointer to allow PowerWeb to write the value of the handle pointer into it.

void* parent
The parent pointer is either the "parcel" pointer or a "handle" pointer. When the pszName contains a fully qualified global name (ie. has a prefix of "Request:/", "Server:/", etc) then the "parent" pointer is the "parcel" pointer, otherwise the pszName is a relative reference to a "handle" pointer.