Calling O/S API Functions
By using the PASS and CALL statements in APPX, O/S libraries and functions can easily be accessed from APPX subroutines.
This paper discusses some of the issues involved in invoking Windows API functions from within an APPX application, however the concepts are the same for Linux/Unix, etc.
The TAPI functions act as a link between the computer and the phone system. In our example, we will demonstrate how to place a simple call to an alpha numeric pager using functions exported by the TAPI API.
Documentation for these functions can be found online at:
http://search.microsoft.com/us/dev/default.asp or is available as part of the MSDN library.
Below is a sample of the documentation you can find at Microsoft's MSDN website:
lineClose - The lineClose function closes the specified open line device.
LONG WINAPI lineClose(
HLINEhLine
);
Parameters
hLine
A handle to the open line device to be closed. After the line has been successfully closed, this handle is no longer valid.
Return Values - Returns zero if the request succeeds or a negative error number if an error occurs. Possible return values are:
- LINEERR_INVALLINEHANDLE
- LINEERR_RESOURCEUNAVAIL
- LINEERR_NOMEM
- LINEERR_UNINITIALIZED
- LINEERR_OPERATIONFAILED
- LINEERR_OPERATIONUNAVAIL Remarks
If an application calls
lineClose while it still has active calls on the opened line, the application's ownership of these calls is revoked. If the application was the sole owner of these calls, the calls are dropped as well. It is good programming practice for an application to dispose of the calls it owns on an opened line by explicitly relinquishing ownership and/or by dropping these calls prior to closing the line.
If the line was closed successfully, a LINE_LINEDEVSTATE message is sent to all applications that are monitoring the line status of open/close changes. Outstanding asynchronous replies are suppressed.
Service providers may find it useful or necessary to forcibly reclaim line devices from an application that has the line open. This can be useful to prevent an application from monopolizing the line device for too long. If this happens, a LINE_CLOSE message is sent to the application, specifying the line handle of the line device that was closed.
The
lineOpen function allocates resources to the invoking application, and applications can be prevented from opening a line if resources are unavailable. Therefore, an application that only occasionally uses a line device (such as for making outgoing calls) should close the line to free resources and allow other applications to open the line.
Requirements
- Windows NT/2000: Requires Windows NT 4.0 SP3 or later.
- Windows 95/98: Requires Windows 95 or later.
- Version: Requires TAPI 1.3 or later.
- Header: Declared in Tapi.h.
- Library: Use Tapi32.lib. See Also
- TAPI 2.2 Reference Overview
- Basic Telephony Services Reference
- LINE_CLOSE
- LINE_LINEDEVSTATE
- lineOpen Notice in the Requirements section that there is an entry called "Library". In this case, the Library is "Tapi32.lib" - if you substitute ".dll" for ".lib", you know where the lineClose function is located - it's in Tap32.dll. If the data required by the function includes a string, the function name will be terminated with an A (ascii) or U (unicode). To determine if an A or U is required is to right click on the DLL file in %WINDIR%\system32, and use QuickView to view the function names associated with the DLL. If you see "lineCloseA" and "lineCloseU", use the "lineCloseA" choice. If you see "lineClose" all by itself, don't include a suffix. To call the lineClose function, your CALL statement would specify "TAPI32.DLL,lineClose".
Data Types and their Appx Counterparts
Most Windows functions use a small set of data types. It is convenient to define a few domains which correspond to these data types. The advantage of using Domains is obvious - should the definition of a DWORD change, is is easier to change the domain definition than to have to change each DWORD that would be defined as a 10 digit Binary Number. It is also for this reason that we follow the Windows standards and define a seperate Domain for each Data Type even though the definitions may be the same - should one change at a later date, we can update our program easily. The data types required by each Windows function can be found in the technical documentation for each function. The most commonly used data types are:
DWORD |
10 digit |
Binary Storage |
Numeric |
HANDLE |
10 digit |
Binary Storage |
Numeric |
POINTER |
10 digit |
Binary Storage |
Numeric |
CSTR |
Size varies with |
usage |
Alpha |
LPCSTR |
10 digit |
Binary Storage |
Numeric |
LPDWORD |
10 digit |
Binary Storage |
Numeric |
The prefix "LP" means a 'pointer to' or 'address of', so a LPCSTR is a 'pointer to a CSTR'. A CSTR is a null-terminated string, so an LPCSTR is a pointer to a null-terminated string. An LPDWORD would be a 'pointer to a DWORD'.
LPCSTR and LPSTR are a little tricky - if an LPCSTR is a function parameter, you should pass a (null-terminated) shared alpha field: if an LPCSTR appears within a structure, you will need to put the address of an alpha field in the structure (see below).
Other handy domains to create are FUNCTION NAME and ACCESS TYPE - alpha fields, 75 characters long. Storing function names and access types in the default value of work fields makes them easy to find and use.
Access Types and Security Codes
Many of the API's in windows require an access type be passed as part of the argument. An access type tells Windows what you plan on doing. For example, if you open a file, you must tell Windows whether you are going to read from or write to the file (or both). This access type can be obtained by searching C header files (included with a C compiler) for the numeric value (usually in hex). The permission varies depending on the users needs. If more than one permission is required (ie. both Create and Delete rights), the hex values of each are searched, converted to decimal, and summed to generate the value to be passed. Specific functions have specific requirements - a handle opened to change a service might not have sufficient permissions to delete a service. An easy way to organize and pass access types is to create a work field for the access type with a default value equal to the value that must be passed. Prefacing the field names with a ":" makes them easy to identify in a large program.
Structures
Often a function requires a structure (or usually the address of a structure) bepassed as one of the parameters. The structure required as a parameter by the lineInitializeEx function is of the type LINEINITIALIZEEXPARAMS (documentation from the MSDN library shown below).
The LINEINITIZALIZEEXPARAMS structure describes parameters supplied when making calls using LINEINITIALIZEEX.
typedef struct lineinitializeexparams_tag {
DWORD dwTotalSize;
DWORD dwNeededSize;
DWORD dwUsedSize;
DWORD dwOptions;
union
{
HANDLE hEvent;
HANDLE hCompletionPort;
} Handles;
DWORD dwCompletionKey;
} LINEINITIALIZEEXPARAMS, FAR *LPLINEINITIALIZEEXPARAMS;
Members
dwTotalSize
The total size in bytes allocated to this data structure.
dwNeededSize
The size in bytes for this data structure that is needed to hold all the returned information.
dwUsedSize
The size in bytes of the portion of this data structure that contains useful information.
dwOptions
One of the LINEINITIALIZEEXOPTION_ constants. Specifies the event notification mechanism the application desires to use.
hEvent
If
dwOptions specifies LINEINITIALIZEEXOPTION_USEEVENT, TAPI returns the event handle in this field.
hCompletionPort
If
dwOptions specifies LINEINITIALIZEEXOPTION_USECOMPLETIONPORT, the application must specify in this field the handle of an existing completion port opened using
CreateIoCompletionPort.
dwCompletionKey
If
dwOptions specifies LINEINITIALIZEEXOPTION_USECOMPLETIONPORT, the application must specify in this field a value that is returned through the
lpCompletionKey parameter of
GetQueuedCompletionStatus to identify the completion message as a telephony message.
Remarks
See lineInitializeEx for further information on these options.
QuickInfo
Version: Requires TAPI 2.0 or later.
Windows CE: Unsupported.
Header: Declared in tapi.h.
This structure is defined by creating an Appx file - LIPARAMS. It is of a One-Rec, Working Storage Type (as we'll only be passing one occurence of the data), and contains a field for each member in the TAPI structure.
LIPARAM ONE-REC WORKING-STORAGE
LIPARAMS DWTOTALSIZE DOMAIN DWORD
We set the default value of the field at 24 - the size of the
structure.
LIPARAMS DWNEEDEDSIZE DOMAIN DWORD
LIPARAMS DWUSEDSIZE DOMAIN DWORD
LIPARAMS DWOPTIONS DOMAIN DWORD (Default Value - 2)
We set the default value of the field to 2 - One of the LINEINITIALIZAEXOPTIONS_ constants. Using the Microsoft Documentation, look up LINEINITIALIZEEXOPTIONS_ constants - our choice is to use the option, LINEINITIALIZEEXOPTION_USEEVENT. If you have a C compiler, the numeric value for this option is easy to find (just search) - otherwise, a web search is probably your best option.
LIPARAMS HEVENT DOMAIN HANDLE
Note - this field will return a handle for our use, as we have
specified in the field above to _USEEVENT.
LIPARAMS HCOMPLETIONPT SYNONYM UNION
Note - as this is a Union, it is represented in Appx by a data
type of Synonym. If included in a structure, the synonym does
not add to the total length of the structure - often a parameter
required by a function.
LIPARAMS DWCOMPLETIONK DOMAIN DWORD
It our example, it is convenient to assign default values to the fields which pass data to the function. As some of the fields will return values to us, we will just leave them blank. Not all fields will contain a value, but all fields must be included in the structure.
In Summary...
- You can build a struct by defining a matching File in the Appx data dictionary.
- If you only need one struct, make it a one-record, working storage file.
- If you need more than one, make it an indexed file.
- Passing a field shared in a Struct file passes the address of the value rather than the value of the value.
- The file must contain a field for each member of the Structure, even though some of the fields in the struct may not be used.
- A struct may need to contain a null terminated field - this must be done prior to PASSing the record to the CALL statement.
- If the length of the struct is to be passed as an additional argument, that length can be determined (easily) by accessing Print Technical Documentation, in Utilities.
PASSing, Shareing and CALLing
Most of the Windows functions require that parameters be PASSed, either to provide or return data. If a parameter is a pointer, you must PASS it shared. If a parameter is not a pointer, you pass it non-shared. Usually, Microsoft will require a pointer if a parameter is used to give data back to us. The Microsoft documentation should be referred to when deciding which fields to pass shared. Strings are always passed shared (because a string is represented by the address of the first character). Structures are PASSed as RECORD with the Shared? flag set to Y.
Sometimes a parameter in a function is reserved for later use, or may not be of value to us in using the function. We can set these parameters as NULL. Refer to the documentation - usually these fields are noted as "can be passed as Null if...". For these parameters it is handy to have a work field to use as a place marker - TST WORK NULL. In functions, as in structures, all parameters must be included - even if we don't use them.
CSTR's are strings - defined in Appx as an Alpha field. Strings are almost always Null Terminated (in other words a NULL (0) character marks the end of the string). Simple subroutines to add and remove Null Terminators are included at the end of this document.
If you've stored the function name as the default value in a work field, it is easy to assemble a series of PASS and CALL statements to use that function:
PASS ---TEMP 32 FIELD SHARE? Y
GOSUB CAP NULL TERMINATE
*
PASS CAP LINE HANDLE FIELD SHARE? N
PASS CAP CALL HANDLE FIELD SHARE? Y
PASS --- TEMP 32 FIELD SHARE? Y
PASS CAP NULL FIELD SHARE? N
PASS CAP NULL FIELD SHARE? N
CALL CAP *LINE MAKE CALL
In the above example, the field ---TEMP 32 is a string, and must be passed Null Terminated. Before PASSing it to the CALL statement, we pass it to a Subroutine to be null terminated. Pass it shared - the value of the string must change to accomodate the Null Terminator.
The default value of CAP *LINE MAKE CALL is: TAPI32.DLL,lineMakeCall
How results are returned.
Results for a function are returned in either a shared field, or the ---RETURN CODE pre-defined field. Use MSDN documentation for each function to determine what kind of information is to be found in return code.
Sometimes, the *Get Last Error function must be used to return a specific error code. To use GetLastError:
PASS *Your fields here
CALL *A function
*
IF --- RETURN CODE EQ (Error code)
T CALL CAP *GetLastError
T TRAP
T SET --- RETURN CODE EQ --- RETURN CODE
If you are in the debugger, prior to reaching the CALL statement, you must opt to continue to the next valid TRAP. This is to ensure that the Return Code is for the function called rather than for the Appx keystroke. The function that you called will affect GetLastError - you want to make sure that Appx itself doesn't call any Windows functions between your CALL statement and the call to GetLastError - otherwise, GetLastError will reflect the result of Appx work, not your CALL statement. It's also a good idea to call GetLastError before you do anything else (and ignore the result) - Appx must call a few Windows functions in order to load GetLastError into memory - you don't want that to happen later when you really need an error code.
Note that --- RETURN CODE is a 32bit field and only the bytes explicitly returned by the Windows function should be examined. The remaining byte(s) will contain undefined values. For example, if the Windows function returns a short integer (16 bits), only the first 16 bits will contain the return code, the remaining 16 bits are undefined. To do this, define a working storage file that contains a numeric field defined by the domain 0LA RET CODE. This gives your numeric field the same characteristics as --- RETURN CODE, ie, a 32 bit binary number. Now put a group field around your numeric field so that you can refer to it by the group name. When moving group fields, Appx does not try to convert types, it simply does a byte by byte copy, which is what we want. Now you can define additional group fields, breaking down the 4 bytes in different ways. For example:
Dsp -Opts--
Seq No Field Name Field Type Seq Format T DL AA
100 RET CODE FULL GROUP HEADER
200 RET FULL DOMAIN AA
500 RET CODE FULL END GROUP TRAILER
600 RET CODE SHORT GROUP HEADER
700 RET S1 NUMERIC 9(5) Oc 2 AA
800 RET CODE SHORT END GROUP TRAILER
900 RET CODE BOOL GROUP HEADER
1000 RET BOOL NUMERIC 9(3) Oc 4 AA
1100 RET CODE BOOL END GROUP TRAILER
1200 RET BYTES GROUP HEADER
1300 RET BYTE ALPHA X(1) Oc 4 AA
1400 RET BYTES END GROUP TRAILER
You would first SET --- RETURN CODE into RET FULL, then you can move the Group RET CODE FULL to any of the other groups in order to refer to the individual parts of RET FULL. For example, SET RET CODE SHORT = RET CODE FULL would allow you to examine RET S1(1) to get the short integer value. RET S1 is defined as 9(5), range check of LT 65535, storage type Binary. This causes Appx to define it as a 16 bit field. RET BOOL is defined as 9(3), range check of LE 255, storage type Binary. This causes Appx to define it as an 8 bit field, and we can refer to RET BOOL (1) to get the value of the first 8 bits of --- RETURN CODE.
Handles
Many Windows functions make use of handles. A handle is just a number that Windows gives to you to represent an object. For example, if you intend to read data from a file, you first open the file - Windows gives you back a file handle. When you need to call the ReadFile function, you give the file handle back to Windows. When you request a handle, you tell Windows what type of access you need (see access types above). If you don't have permissions to access the object in the way you requested, you won't get a handle.
Closing Handles
If your function opens a handle, the handles must be closed when you reach the end of your application. If you must use a handle multiple times, open it once, use it and then close the handle rather than repeatedly opening and closing the handle to ease error handling. If you forget to close your handles, they will close upon exiting Appx. Sometimes, an operation won't appear to complete until the handle is closed - if you delete an entry from the registry, you won't see it disappear until you close your registry handle.
Sample
Our sample application will call a pager, wait 15 seconds, and relay a number. It cheats a bit - it doesn't actually verify that the phone has been answered - it just assumes. It also doesn't include error handling, or other features that a real application probably should - it's just a sample...
The first function we will call in our sample application is lineInitializeEx. This function initializes a line and returns a handle for a TAPI call, as well as a count of the devices that are suitable for use with TAPI functions. The Microsoft Documentation (shown) defines the function call - also included in the documentation is detailed information about each field, error/success codes and other relevant functions.
LONG lineInitializeEx(
LPHLINEAPPlphLineApp,
HINSTANCEhInstance,
LINECALLBACKlpfnCallback,
LPCSTRlpszFriendlyAppName,
LPDWORDlpdwNumDevs,
LPDWORDlpdwAPIVersion,
LPLINEINITIALIZEEXPARAMSlpLineInitializeExParams
);
We need to define Work Fields which are passed to the function either to carry or return information. From the documentation we know we need:
CAP SYSTEM HANDLE HANDLE This will return a valid usage handle for TAPI.
CAP NULL DWORD The 2nd & 3rd parameters can beNULL (for our purposes).
CAP APPLICATION NAME LPCSTR This is used for call logging purposes.
CAP DEVICE COUNT LPDWORD This will return the number of valid devices.
CAP :API VERSION LPDWORD This has a default value of 131072 for Version 2.0 (hex 20000)
The last parameter we pass is a pointer to a structure (remember, a structure is passed as a shared record).
The structure is of the type LINEINITIALIZEEXPARAMS, and is defined by creating an Appx file - LIPARAMS. It is of a One-Rec, Working Storage Type (as we'll only be passing one occurence of the data), and contains a field for each member in the TAPI structure.
LIPARAM ONE-REC WORKING-STORAGE
LIPARAMS DWTOTALSIZE DOMAIN DWORD (Default Value - 24)
LIPARAMS DWNEEDEDSIZE DOMAIN DWORD
LIPARAMS DWUSEDSIZE DOMAIN DWORD
LIPARAMS DWOPTIONS DOMAIN DWORD (Default Value - 2)
LIPARAMS HEVENT DOMAIN HANDLE
LIPARAMS HCOMPLETIONPT SYNONYM UNION
LIPARAMS DWCOMPLETIONK DOMAIN DWORD
In our example, it is convenient to assign default values to the fields that must pass data to the function. As some of the fields will return values to us, we will just leave them blank. The field CAP LIPARAMS HCOMPLETIONPT is a synonym, and as such doesn't add to the size of the structure.
We want to be sure that our handle and device counts don't have any leftover data, and we will hard code in an application name - just a friendly identifier.
* Subroutine - Initialize Line
SET CAP SYSTEM HANDLE =
SET CAP DEVICE COUNT =
SET CAP APPLICATION NAME = APPX Pager Application
The member, CAP APPLICATION NAME is of a data type, LPCSTR, an address to a string. As strings are passed Null Terminated, we first pass it to a subroutine to add the Null Terminator.
PASS CAP APPLICATION NAME FIELD SHARE? Y
GOSUB CAP NULL TERMINATE
The parameters for the call are passed shared if so noted in the documentation for the call - usually when data is to be returned, or changed. Some parameters are passed as NULL as that parameter may not be required in our use of that function, but the space must be reserved. The structure that we created earlier is passed as a RECORD, and is shared.
PASS CAP SYSTEM HANDLE FIELD SHARE? Y
PASS CAP NULL FIELD SHARE? N
PASS CAP NULL FIELD SHARE? N
PASS CAP APPLICATION NAME FIELD SHARE? Y
PASS CAP DEVICE COUNT FIELD SHARE? Y
PASS CAP :API VERSION FIELD SHARE? Y
(default value 131072)
PASS CAP LIPARAMS RECORD SHARE? Y
CALL CAP *LINE INITIALIZE EX RESIDENT? N END? N FAIL 0
(the work field *LINE INITIALIZE EX contains default value: TAPI32.DLL,lineInitializeEx)
If the function call succeeds, a value of 0 is returned in ---RETURN CODE. The meaning of the return value is documented for each function - sometimes a '0' might indicate failure, other times it would indicate success. We test for this, and decide how to continue.
IF ---RETURN CODE = 0
T PASS CAP SYSTEM HANDLE FIELD SHARE? N
T PASS DEVICE COUNT FIELD SHARE? N
T GOSUB CAP NEGOTIATE TAPI VERSIONS
F ERROR An Available Device Has Not Been Found!
For the next subroutine, we need a structure, of the LINEEXTENSIONID type. We create an Appx file, LINEXTEN. We aren't setting any values in this structure - it just needs to exist.
LINEXTEN ONE-REC WORKING-STORAGE
LINEXTEN EXTENSION ID0 DOMAIN (DWORD)
LINEXTEN EXTENSION ID1 DOMAIN (DWORD)
LINEXTEN EXTENSION ID2 DOMAIN (DWORD)
LINEXTEN EXTENSION ID3 DOMAIN (DWORD)
Assuming that we have a handle and that we have found devices, we continue on to the subroutine CAP NEGOTIATE TAPI VERSIONS. This subroutine searches the machine to find a device which supports the TAPI version we require.
* Subroutine - Negotiate TAPI Versions
RECEIVE CAP SYSTEM HANDLE FIELD
RECEIVE CAP DEVICE COUNT FIELD
SET ---BI = CAP DEVICE COUNT
The valid Device ID will be a number equal to or greater than 0, and less than the Device Count.
BEG LOOP AI = 000 TO BI STEP 001
SET CAP DEVICE ID = --- AI
SET CAP SYSTEM TAPI VERSION =
We pass the low version (131071) and a high version (131072) of TAPI that our application will work with. These values are derived from the hex values for the version - '131071' is 1FFFF in hex (Version 1.0) while the value '131072' is 20000 in hex (Version 2.0). TAPI returns the CAP SYSTEM TAPI VERSION to tell us which version is installed on our machine.
PASS CAP SYSTEM HANDLE FIELD SHARE? N
PASS CAP DEVICE ID FIELD SHARE? N
PASS CAP :API LOW VERSION FIELD SHARE? N
PASS CAP :API HIGH VERSION FIELD SHARE? N
PASS CAP SYSTEM TAPI VERSION FIELD SHARE? Y
PASS CAP LINEXTEN RECORD SHARE? Y
CALL CAP *LINE NEGOTIATE API RESIDENT? N END? N FAIL 0
(the work field *LINE NEGOTIATE API contains the default value: PI32.DLL,lineNegotiateAPIVersion)
If a DEVICE ID is useable, end the loop and use the current device. If not, return and try the next device.
IF --- RETURN CODE EQ 0
T PASS CAP SYSTEM HANDLE FIELD SHARE? N
T PASS CAP DEVICE ID FIELD SHARE? N
T PASS CAP SYSTEM TAPI VERSION FIELD SHARE? N
T GOSUB CAP LINE OPEN
T RETURN
If we get through all the devices and none are found to be suitable, display an error message.
END LOOP AI
ERROR No valid devices have been found!
RETURN
Assuming we have at least one usable device:
The next subroutine opens the available device.
* Subroutine - Line Open
RECEIVE CAP SYSTEM HANDLE FIELD FAIL N
RECEIVE CAP DEVICE ID FIELD FAIL N
RECEIVE CAP SYSTEM TAPI VERSION FIELD FAIL N
The sixth parameter to lineOpen is a number that might be used in other applications, but we don't need it. We just pass an arbitrary value.
SET --- AI = 43
PASS CAP SYSTEM HANDLE FIELD SHARE? N
PASS CAP DEVICE ID FIELD SHARE? N
PASS CAP LINE HANDLE FIELD SHARE? Y
PASS CAP SYSTEM TAPI VERSION FIELD SHARE? N
PASS CAP NULL FIELD SHARE? N
PASS --- AI FIELD SHARE? N
PASS CAP :LINECALLPRIVILEGENONE FIELD SHARE? N
(default value - 1)
PASS CAP NULL FIELD SHARE? N
PASS CAP NULL FIELD SHARE? N
CALL CAP *OPEN LINE RESIDENT? N END? N FAIL 0
(the default value of CAP *OPEN LINE is: TAPI32.DLL,lineOpen)
If the return code is 0, we have a valid line handle, and will continue on to place our call. If not, display an error.
IF --- RETURN CODE EQ 0
T PASS CAP LINE HANDLE FIELD SHARE? N
T GOSUB CAP DIAL PHONE
F ERROR The line is not available.
Assuming we have a handle for our line, we continue:
* Subroutine - Dial Phone
RECEIVE CAP LINE HANDLE FIELD FAIL N
The phone number to dial is followed by ",,,", indicating a pause, then the number to relay.
SET ---TEMP 32 = 5551234,,,5554321
The phone number is passed Null Terminated - in the documentation, we can see it is of the data type LPCSTR - a null-terminated string.
PASS --- TEMP 32 FIELD SHARE? Y
GOSUB CAP NULL TERMINATE
PASS CAP LINE HANDLE FIELD SHARE? N
PASS CAP CALL HANDLE FIELD SHARE? Y
PASS --- TEMP 32 FIELD SHARE? Y
PASS CAP NULL FIELD SHARE? N
PASS CAP NULL FIELD SHARE? N
CALL CAP *LINE MAKE CALL
(the default value of CAP *LINE MAKE CALL is: TAPI32.DLL,lineMakeCall)
At this point a pop-up should appear as the modem dials. The lineMakeCall function has many variables, and can be configured to handle international calls, and to relay call tracking information. This example is the most basic use of the function.
Don't forget to close your handles...
PASS CAP LINE HANDLE FIELD SHARE? N
CALL CAP *LINE CLOSE
*
PASS CAP SYSTEM HANDLE FIELD SHARE? N
CALL CAP *CLOSE HANDLE
*
PASS CAP CALL HANDLE FIELD SHARE? N
CALL CAP *CLOSE HANDLE
(the default value of CAP *LINE CLOSE IS: TAPI32.DLL,lineClose)
(the default value of CAP *CLOSE HANDLE IS: KERNEL32.DLL,CloseHandle)
Null Terminate
It is generally noted in the Microsoft documentation if a field is expected to be Null Terminated before being passed to the function. Pass this subroutine the field to be null terminated (Shared? = Y). The value is then updated to include the null terminator.
RECEIVE --- TEMP 32K FIELD FAIL N
CNV BIN --- TEMP 1 = 0
APPEND --- TEMP 32K 0 --- TEMP 1
RETURN
Remove Null Terminator
Strings returned by the API may be null terminated. Pass this subroutine a field (Shared? = Y), and it will clean up the value.
* Subroutine - API Remove Null Terminator
*
* This subroutine will remove the null terminator from a field.
* It will also put spaces in the 998 characters following the
* null terminator, just in case there is garbage in there.
*
RECEIVE --- TEMP 2K FIELD FAIL N
*
CNV BIN --- TEMP 1 = 0
SET --- TEMP 8K =
IF --- TEMP 2K IN --- TEMP 1
T SET --- NI = --- TEXT AT POSITION
T SET TEMP 2K AT NI FOR 999 FROM 001 OF --- TEMP 8K
RETURN
Comments:
Read what other users have said about this page or add your own comments.
To define a 'short' field, define a binary field with a range check of −32,768 to 32,767 for a signed binary or to 65,535 for unsigned.
--
JeanNeron - 2014-01-29