Calling Windows API Functions

By using the PASS and CALL statements in APPX, the WIN32 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. 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, Share?ing 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 funtion 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:

<div id="_mcePaste">                                              Dsp                   -Opts--</div>
<div id="_mcePaste">Seq No  Field Name             Field Type     Seq Format            T DL AA     </div>
<div id="_mcePaste">100     RET CODE FULL          GROUP HEADER     </div>
<div id="_mcePaste">200     RET FULL               DOMAIN                                    AA</div>
<div id="_mcePaste">500     RET CODE FULL END      GROUP TRAILER</div>
<div id="_mcePaste">600     RET CODE SHORT         GROUP HEADER</div>
<div id="_mcePaste">700     RET S1                 NUMERIC            9(5) Oc 2         AA</div>
<div id="_mcePaste">800     RET CODE SHORT END     GROUP TRAILER</div>
<div id="_mcePaste">900     RET CODE BOOL          GROUP HEADER</div>
<div id="_mcePaste">1000    RET BOOL               NUMERIC            9(3) Oc 4         AA</div>
<div id="_mcePaste">1100    RET CODE BOOL END      GROUP TRAILER</div>
<div id="_mcePaste">1200    RET BYTES              GROUP HEADER</div>
<div id="_mcePaste">1300    RET BYTE               ALPHA              X(1) Oc 4       AA</div>
<div id="_mcePaste">1400    RET BYTES END          GROUP TRAILER</div>

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

Edit | Attach | Watch | Print version | History: r5 < r4 < r3 < r2 < r1 | Backlinks | Raw View | Raw edit | More topic actions...
Topic revision: r3 - 2014-02-03 - JeanNeron
 
  • Edit
  • Attach
This site is powered by the TWiki collaboration platform Powered by PerlCopyright © 2008-2024 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback