LONG WINAPI lineClose(Parameters
HLINEhLine
);
hLineReturn Values - Returns zero if the request succeeds or a negative error number if an error occurs. Possible return values are:A handle to the open line device to be closed. After the line has been successfully closed, this handle is no longer valid.
| 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 |
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... 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,lineMakeCallPASS ---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
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 TRAILERYou 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.
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.
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 DWORDIn 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 RETURNIf 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,,,5554321The 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)
RECEIVE --- TEMP 32K FIELD FAIL N
CNV BIN --- TEMP 1 = 0
APPEND --- TEMP 32K 0 --- TEMP 1
RETURN
* 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
Copyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.