═══ 1. Data Types ═══ The following are all the data types used by the Control Program. They are listed in alphabetical order. ═══ 1.1. APIRET ═══ Unsigned integer in the range 0 through 4 294 967 295. Type APIRET = LONGINT; ═══ 1.2. AVAILDATA ═══ Four-byte buffer in which the system returns the number of bytes that were available in the named pipe. Type AVAILDATA = Record cbpipe: SmallWord; { bytes left in the pipe } cbmessage: SmallWord; { bytes left in the current message } End; Type PAVAILDATA = ^AVAILDATA; ═══ AVAILDATA Field - cbpipe ═══ cbpipe (USHORT) The number of bytes that were buffered in the pipe, including the message-header bytes and bytes that have been examined. ═══ AVAILDATA Field - cbmessage ═══ cbmessage (USHORT) Number of bytes in the current message. A value of 0 indicates a byte-stream pipe. ═══ 1.3. BANKINFO ═══ Bank information data structure. Type BANKINFO = Record ulBankLength: ULong; usBank: SmallWord; usVideoModeType: SmallWord; usReadWriteMode: SmallWord; End; ═══ BANKINFO Field - ulBankLength ═══ ulBankLength (ULONG) Length of the bank. Represents the combined length of all parameter packet fields. This is a required field for all calls, including those made by way of the 32-bit DosDevIOCtl, with a minimum packet length of 10 bytes. ═══ BANKINFO Field - usBank ═══ usBank (USHORT) Current bank. The current bank value to be set/returned. The bank size is 64KB regardless of the value specified for the video mode type field. ═══ BANKINFO Field - usVideoModeType ═══ usVideoModeType (USHORT) Current video mode type. Defines the adapter video mode type to the device driver, and has one of the following values: 0 Text Mode 1 Planar Mode 2 Linear Mode This field is required. ═══ BANKINFO Field - usReadWriteMode ═══ usReadWriteMode (USHORT) Read/Write bank mode. Specifies what bank type is to be set/returned, and has one of the following values: 0 Read 1 Write This field is required. ═══ 1.4. BIOSPARAMETERBLOCK ═══ BIOS Parameter Block (BPB). Type BIOSPARAMETERBLOCK = Record usBytesPerSector: SmallWord; bSectorsPerCluster: Byte; usReservedSectors: SmallWord; cFATs: Byte; cRootEntries: SmallWord; cSectors: SmallWord; bMedia: Byte; usSectorsPerFAT: SmallWord; usSectorsPerTrack: SmallWord; cHeads: SmallWord; cHiddenSectors: ULong; cLargeSectors: ULong; abReserved: array[0..5] of Byte; cCylinders: SmallWord; bDeviceType: Byte; fsDeviceAttr: SmallWord; End; Type PBIOSPARAMETERBLOCK = ^BIOSPARAMETERBLOCK; ═══ BIOSPARAMETERBLOCK Field - usBytesPerSector ═══ usBytesPerSector (USHORT) Number of bytes per sector. ═══ BIOSPARAMETERBLOCK Field - bSectorsPerCluster ═══ bSectorsPerCluster (BYTE) Number of sectors per cluster. ═══ BIOSPARAMETERBLOCK Field - usReservedSectors ═══ usReservedSectors (USHORT) Number of reserved sectors. ═══ BIOSPARAMETERBLOCK Field - cFATs ═══ cFATs (BYTE) Number of FATs. ═══ BIOSPARAMETERBLOCK Field - cRootEntries ═══ cRootEntries (USHORT) Number of root directory entries. ═══ BIOSPARAMETERBLOCK Field - cSectors ═══ cSectors (USHORT) Number of sectors. ═══ BIOSPARAMETERBLOCK Field - bMedia ═══ bMedia (BYTE) Media descriptor. ═══ BIOSPARAMETERBLOCK Field - usSectorsPerFAT ═══ usSectorsPerFAT (USHORT) Number of secctors per FAT. ═══ BIOSPARAMETERBLOCK Field - usSectorsPerTrack ═══ usSectorsPerTrack (USHORT) Number of sectors per track. ═══ BIOSPARAMETERBLOCK Field - cHeads ═══ cHeads (USHORT) Number of heads. ═══ BIOSPARAMETERBLOCK Field - cHiddenSectors ═══ cHiddenSectors (ULONG) Number of hidden sectors. ═══ BIOSPARAMETERBLOCK Field - cLargeSectors ═══ cLargeSectors (ULONG) Number of large sectors. ═══ BIOSPARAMETERBLOCK Field - abReserved[6] ═══ abReserved[6] (BYTE) Reserved. ═══ BIOSPARAMETERBLOCK Field - cCylinders ═══ cCylinders (USHORT) Number of cylinders defined for the physical device. ═══ BIOSPARAMETERBLOCK Field - bDeviceType ═══ bDeviceType (BYTE) Physical layout of the specified device. 0 48 TPI low-density diskette drive 1 96 TPI high-density diskette drive 2 Small (3.5-inch) 720KB drive 3 8-inch single-density diskette drive 4 8-inch double-density diskette drive 5 Fixed disk 6 Tape drive 7 Other (includes 1.44MB 3.5-inch diskette drive) 8 R/W optical disk 9 3.5-inch 4.0MB diskette drive (2.88MB formatted) ═══ BIOSPARAMETERBLOCK Field - fsDeviceAttr ═══ fsDeviceAttr (USHORT) A bit field that returns flag information about the specified drive. Bit 0 Removable Media flag. 0 Media is removable. 1 Media cannot be removed. Bit 1 Changeline flag. 0 The physical device driver returns the value 0, Unsure if media has changed, from the Media Check function. 1 Device support determines that the media was removed since the last I/O operation. ═══ 1.5. BOOL ═══ Boolean. Valid values are:  FALSE, which is 0  TRUE, which is 1 Type BOOL = LONGBOOL; ═══ 1.6. BOOL32 ═══ Boolean. Valid values are:  FALSE, which is 0  TRUE, which is 1 Type BOOL32 = LONGBOOL; ═══ 1.7. BYTE ═══ A byte. Stupid hotlink... - A byte is a byte! ═══ 1.8. CHAR ═══ Single-byte character. ═══ 1.9. COLOR ═══ Color value. Type COLOR = LONG; ═══ 1.10. CONTEXTRECORD ═══ This is the machine specific register contents for the thread at the time of the exception. Note that only the register sets specified by ContextFlags contain valid data. Conversely, only registers specified in ContextFlags will be restored if an exception is handled. Type CONTEXTRECORD = Record { The flags values within this flag control the contents of a ContextRecord. If the ContextRecord is used as an input parameter, then for each portion of the ContextRecord controlled by a flag whose value is set, it is assumed that that portion of the ContextRecord contains valid context. If the ContextRecord is being used to modify a thread's context, then only that portion of the thread's context will be modified. If the ContextRecord is used as an Input/Output parameter to capture the context of a thread, then only those portions of the thread's context corresponding to set flags will be returned. } ContextFlags: ULong; { This section is specified/returned if the ContextFlags contains the flag context_Floating_Point. } ctx_env: array [0..6] of ULong; { coprocessor environment } ctx_stack: array [0..7] of FpReg; { register set } { This section is specified/returned if the ContextFlags contains the flag context_Segments. } ctx_SegGs: ULong; ctx_SegFs: ULong; ctx_SegEs: ULong; ctx_SegDs: ULong; { This section is specified/returned if the ContextFlags contains the flag context_Integer. } ctx_RegEdi: ULong; ctx_RegEsi: ULong; ctx_RegEax: ULong; ctx_RegEbx: ULong; ctx_RegEcx: ULong; ctx_RegEdx: ULong; { This section is specified/returned if the ContextFlags contains the flag context_Control. } ctx_RegEbp: ULong; ctx_RegEip: ULong; ctx_SegCs: ULong; ctx_EFlags: ULong; ctx_RegEsp: ULong; ctx_SegSs: ULong; End; Type PCONTEXTRECORD = ^CONTEXTRECORD; ═══ CONTEXTRECORD Field - ContextFlags ═══ ContextFlags (ULONG) Flags which control the contents of the context record. Possible values are shown in the following list: CONTEXT_CONTROL (0x00000001) SS:ESP, CS:EIP, EFLAGS and EBP CONTEXT_INTEGER (0x00000002) EAX, EBX, ECX, EDX, ESI and EDI CONTEXT_SEGMENTS (0x00000004) DS, ES, FS, and GS CONTEXT_FLOATING_POINT (0x00000008) numeric coprocessor state CONTEXT_FULL CONTEXT_CONTROL │ CONTEXT_INTEGER │ CONTEXT_SEGMENTS │ CONTEXT_FLOATING_POINT If the context record is used as an input parameter, then for each portion of the context record controlled by a flag whose value is set, it is assumed that the portion of the context record contains valid context. If the context record is being used to modify a thread's context, then only that portion of the thread's context will be modified. If the context record is used as an input/output parameter to capture the context of a thread, then only those portions of the thread's context corresponding to set flags will be returned. ═══ CONTEXTRECORD Field - ctx_env[7] ═══ ctx_env[7] (ULONG) This parameter is used only when ContextFlags is set to CONTEXT_FLOATING_POINT. ═══ CONTEXTRECORD Field - ctx_stack[8] ═══ ctx_stack[8] (FPREG) This parameter is used only when ContextFlags is set to CONTEXT_FLOATING_POINT. ═══ CONTEXTRECORD Field - ctx_SegGs ═══ ctx_SegGs (ULONG) GS register. This parameter is used only when ContextFlags is set to CONTEXT_SEGMENTS. ═══ CONTEXTRECORD Field - ctx_SegFs ═══ ctx_SegFs (ULONG) FS register. This parameter is used only when ContextFlags is set to CONTEXT_SEGMENTS. ═══ CONTEXTRECORD Field - ctx_SegEs ═══ ctx_SegEs (ULONG) ES register. This parameter is used only when ContextFlags is set to CONTEXT_SEGMENTS. ═══ CONTEXTRECORD Field - ctx_SegDs ═══ ctx_SegDs (ULONG) DS register. This parameter is used only when ContextFlags is set to CONTEXT_SEGMENTS. ═══ CONTEXTRECORD Field - ctx_RegEdi ═══ ctx_RegEdi (ULONG) EDI register. This parameter is used only when ContextFlags is set to CONTEXT_INTEGER. ═══ CONTEXTRECORD Field - ctx_RegEsi ═══ ctx_RegEsi (ULONG) ESI register. This parameter is used only when ContextFlags is set to CONTEXT_INTEGER. ═══ CONTEXTRECORD Field - ctx_RegEax ═══ ctx_RegEax (ULONG) EAX register. This parameter is used only when ContextFlags is set to CONTEXT_INTEGER. ═══ CONTEXTRECORD Field - ctx_RegEbx ═══ ctx_RegEbx (ULONG) EBX register. This parameter is used only when ContextFlags is set to CONTEXT_INTEGER. ═══ CONTEXTRECORD Field - ctx_RegEcx ═══ ctx_RegEcx (ULONG) ECX register. This parameter is used only when ContextFlags is set to CONTEXT_INTEGER. ═══ CONTEXTRECORD Field - ctx_RegEdx ═══ ctx_RegEdx (ULONG) EDX register. This parameter is used only when ContextFlags is set to CONTEXT_INTEGER. ═══ CONTEXTRECORD Field - ctx_RegEbp ═══ ctx_RegEbp (ULONG) EBP register. This parameter is used only when ContextFlags is set to CONTEXT_CONTROL. ═══ CONTEXTRECORD Field - ctx_RegEip ═══ ctx_RegEip (ULONG) EIP register. This parameter is used only when ContextFlags is set to CONTEXT_CONTROL. ═══ CONTEXTRECORD Field - ctx_SegCs ═══ ctx_SegCs (ULONG) CS register. This parameter is used only when ContextFlags is set to CONTEXT_CONTROL. ═══ CONTEXTRECORD Field - ctx_EFlags ═══ ctx_EFlags (ULONG) EFLAGS register. This parameter is used only when ContextFlags is set to CONTEXT_CONTROL. ═══ CONTEXTRECORD Field - ctx_RegEsp ═══ ctx_RegEsp (ULONG) ESP register. This parameter is used only when ContextFlags is set to CONTEXT_CONTROL. ═══ CONTEXTRECORD Field - ctx_SegSs ═══ ctx_SegSs (ULONG) SS register. This parameter is used only when ContextFlags is set to CONTEXT_CONTROL. ═══ 1.11. COUNTRYCODE ═══ Country code and code page. Type COUNTRYCODE = Record country: ULong; codepage: ULong; End; Type PCOUNTRYCODE = ^COUNTRYCODE; The following table shows the country, country code, primary code page, and secondary code page identifier values: ┌──────────────────────────────┬──────────┬──────────┬──────────┐ │Country │Country │Primary │Secondary │ │ │Code │ │ │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Asian English │099 │437 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Australia │061 │437 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Belgium │032 │437 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Canadian French │002 │863 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Czechoslovakia │042 │852 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Denmark │045 │865 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Finland │358 │437 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │France │033 │437 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Germany │049 │437 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Hungary │036 │852 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Iceland │354 │850 │861 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Italy │039 │437 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Japan │081 │932 │437, 850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Japan SAA │081 │942 │437, 850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Korea │082 │934 │437, 850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Korea SAA │082 │944 │437, 850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Latin America │003 │437 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Netherlands │031 │437 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Norway │047 │865 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │People's Republic of China │086 │936 │437, 850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │People's Republic of China │086 │946 │437, 850 │ │SAA │ │ │ │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Poland │048 │852 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Portugal │351 │860 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Spain │034 │437 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Sweden │046 │437 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Switzerland │041 │437 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Taiwan │088 │938 │437, 850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Taiwan SAA │088 │948 │437, 850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Turkey │090 │857 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │United Kingdom │044 │437 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │United States │001 │437 │850 │ ├──────────────────────────────┼──────────┼──────────┼──────────┤ │Yugoslavia │038 │852 │850 │ └──────────────────────────────┴──────────┴──────────┴──────────┘ Note: Code pages 932, 934, 936, 938, 942, 944, 946, and 948 are supported only with the Asian version of the operating system on Asian hardware. ═══ COUNTRYCODE Field - country ═══ country (ULONG) The binary value of the selected country code. If country is set to 0, the country information of the default system country code is used. ═══ COUNTRYCODE Field - codepage ═══ codepage (ULONG) The binary value of the selected code page identifier. If codepage is set to 0, the country information for the current process code page of the caller is used. ═══ 1.12. COUNTRYINFO ═══ Country information. Type COUNTRYINFO = Record country: ULong; codepage: ULong; fsDateFmt: ULong; szCurrency: array [0..4] of Char; szThousandsSeparator: array [0..1] of Char; szDecimal: array [0..1] of Char; szDateSeparator: array [0..1] of Char; szTimeSeparator: array [0..1] of Char; fsCurrencyFmt: Byte; cDecimalPlace: Byte; fsTimeFmt: Byte; abReserved1: array [0..1] of SmallWord; szDataSeparator: array [0..1] of Char; abReserved2: array [0..4] of SmallWord; End; Type PCOUNTRYINFO = ^COUNTRYINFO; ═══ COUNTRYINFO Field - country ═══ country (ULONG) Country code. ═══ COUNTRYINFO Field - codepage ═══ codepage (ULONG) Code page. ═══ COUNTRYINFO Field - fsDateFmt ═══ fsDateFmt (ULONG) Date format. Possible values are shown in the following list: 0 mm/dd/yy 1 dd/mm/yy 2 yy/mm/dd ═══ COUNTRYINFO Field - szCurrency[5] ═══ szCurrency[5] (CHAR) Currency indicator, null terminated. ═══ COUNTRYINFO Field - szThousandsSeparator[2] ═══ szThousandsSeparator[2] (CHAR) Thousands separator, null terminated. ═══ COUNTRYINFO Field - szDecimal[2] ═══ szDecimal[2] (CHAR) Decimal separator, null terminated. ═══ COUNTRYINFO Field - szDateSeparator[2] ═══ szDateSeparator[2] (CHAR) Date separator, null terminated. ═══ COUNTRYINFO Field - szTimeSeparator[2] ═══ szTimeSeparator[2] (CHAR) Time separator, null terminated. ═══ COUNTRYINFO Field - fsCurrencyFmt ═══ fsCurrencyFmt (UCHAR) Bit fields for currency format. This field contains the following bit fields: Bit Description 0 Placement of the currency indicator. 0 currency indicator precedes money value 1 currency indicator follows money value 1 Number of spaces (0 or 1) between currency indicator and money value. 2 When this bit is set, ignore the first two bits; the currency indicator replaces the decimal indicator. ═══ COUNTRYINFO Field - cDecimalPlace ═══ cDecimalPlace (UCHAR) Binary number of decimal places used in the currency indication. ═══ COUNTRYINFO Field - fsTimeFmt ═══ fsTimeFmt (UCHAR) Time format for file directory presentation. The following values are possible: 0 12 hour with "am" or "pm" 1 24 hour ═══ COUNTRYINFO Field - abReserved[2] ═══ abReserved[2] (USHORT) Reserved; set to 0. ═══ COUNTRYINFO Field - szDataSeparator[2] ═══ szDataSeparator[2] (CHAR) Data list separator, null terminated. ═══ COUNTRYINFO Field - abReserved2[5] ═══ abReserved2[5] (USHORT) Reserved; set to 0. ═══ 1.13. CPID ═══ Code page identification data structure. Type CPID = Record idCodePage: SmallWord; Reserved: SmallWord; End; Type PCPID = ^CPID; ═══ CPID Field - idCodePage ═══ idCodePage (USHORT) Current code page number. ═══ CPID Field - reserved ═══ reserved (USHORT) Reserved. Set to 0. ═══ 1.14. DATETIME ═══ Date and time data structure. Type DATETIME = Record hours: Byte; minutes: Byte; seconds: Byte; hundredths: Byte; day: Byte; month: Byte; year: SmallWord; timezone: SmallInt; weekday: Byte; End; Type PDATETIME = ^DATETIME; ═══ DATETIME Field - hours ═══ hours (UCHAR) Current hour, using values 0 through 23. ═══ DATETIME Field - minutes ═══ minutes (UCHAR) Current minute, using values 0 through 59. ═══ DATETIME Field - seconds ═══ seconds (UCHAR) Current second, using values 0 through 59. ═══ DATETIME Field - hundredths ═══ hundredths (UCHAR) Current hundredths of a second, using values 0 through 99. ═══ DATETIME Field - day ═══ day (UCHAR) Current day of the month, using values 1 through 31. ═══ DATETIME Field - month ═══ month (UCHAR) Current month of the year, using values 1 through 12. ═══ DATETIME Field - year ═══ year (USHORT) Current year. ═══ DATETIME Field - timezone ═══ timezone (SHORT) The difference in minutes between the current time zone and Greenwich Mean Time (GMT). This value is positive for time zones west of Greenwich, England, and negative for time zones east of Greenwich. A value of -1 indicates that the time zone is undefined. ═══ DATETIME Field - weekday ═══ weekday (UCHAR) Current day of the week, using values 0 through 6. Sunday is equal to 0. ═══ 1.15. DCBINFO ═══ Device control block information data structure. Type DCBINFO = Record usWriteTimeout: SmallWord; usReadTimeout: SmallWord; fbCtlHndShake: Byte; fbFlowReplace: Byte; fbTimeout: Byte; bErrorReplacementChar: Byte; bBreakReplacementChar: Byte; bXONChar: Byte; bXOFFChar: Byte; End; Type PDCBINFO = ^DCBINFO; ═══ DCBINFO Field - usWriteTimeout ═══ usWriteTimeout (USHORT) Time period used for Write Timeout processing. Specifies the time period used for Write Timeout processing. See Note 8 of ASYNC_SETDCBINFO. The value is in .01 second units based on zero (where 0 = .01 seconds). ═══ DCBINFO Field - usReadTimeout ═══ usReadTimeout (USHORT) Time period used for Read Timeout processing. Specifies the time period used for Read Timeout processing. See Note 9 of ASYNC_SETDCBINFO. The value is in .01 second units based on zero (where 0 =.01 seconds). ═══ DCBINFO Field - fbCtlHndShake ═══ fbCtlHndShake (BYTE) HandShake Control flag. Has the following bits: Bits 0-1 DTR Control mode. Has the following: Bit 1 Bit 0 Description 0 0 Disable 0 1 Enable 1 0 Input handshaking 1 1 Invalid input. Results in a general failure error. Bit 2 Reserved. Must be 0. Bit 3 Enable output handshaking using CTS Bit 4 Enable output handshaking using DSR Bit 5 Enable output handshaking using DCD Bit 6 Enable input sensitivity using DSR Bit 7 Reserved. Must be 0. ═══ DCBINFO Field - fbFlowReplace ═══ fbFlowReplace (BYTE) Flow Control flag. Has the following bits: Bit 0 Enable Automatic Transmit Flow Control (XON/XOFF) Bit 1 Enable Automatic Receive Flow Control (XON/XOFF) Bit 2 Enable error replacement character Bit 3 Enable null stripping (remove null bytes) Bit 4 Enable break replacement character Bit 5 Automatic Receive Flow Control: 0 = Normal 1 = Full-Duplex Bits 6-7 RTS Control mode. Has the following: Bit 7 Bit 6 Description 0 0 Disable 0 1 Enable 1 0 Input handshaking 1 1 Toggling on transmit ═══ DCBINFO Field - fbTimeout ═══ fbTimeout (BYTE) Timeout flag. Has the following bits: Bit 0 Enable Write Infinite Timeout processing Bits 1-2 Read Timeout processing. Has the following: Bit 2 Bit 1 Description 0 1 Normal Read Timeout processing 1 0 Wait-For-Something, Read Timeout processing 1 1 No-Wait, Read Timeout processing Bits 3-4 Extended Hardware Buffering. Has the following: Bit 4 Bit 3 Description 0 0 Not supported 0 1 Extended Hardware Buffering Disabled 1 0 Extended Hardware Buffering Enabled 1 1 Automatic Protocol Override Bits 5-6 Receive Trigger Level. Has the following: Bit 6 Bit 5 Description 0 0 1 character 0 1 4 characters 1 0 8 characters 1 1 14 characters Bit 7 Transmit Buffer Load Count 0 = 1 character 1 = 16 characters See ASYNC_SETDCBINFO for field definitions. ═══ DCBINFO Field - bErrorReplacementChar ═══ bErrorReplacementChar (BYTE) Error Replacement Character. Value in the range 00h-FFh. See note 5 of ASYNC_SETDCBINFO ═══ DCBINFO Field - bBreakReplacementChar ═══ bBreakReplacementChar (BYTE) Break Replacement Character. Value in the range 00h-FFh. See note 7 of ASYNC_SETDCBINFO ═══ DCBINFO Field - bXONChar ═══ bXONChar (BYTE) Character XON. Value in the range 00h-FFh. See note 2 of ASYNC_SETDCBINFO ═══ DCBINFO Field - bXOFFChar ═══ bXOFFChar (BYTE) Character XOFF. Value in the range 00h-FFh. See note 2 of ASYNC_SETDCBINFO ═══ 1.16. DENA2 ═══ DENA2 data structure. Type DENA2 = FEA2; ═══ 1.17. DEVICEPARAMETERBLOCK ═══ Device Parameter Block data structure. Type DEVICEPARAMETERBLOCK = Record reserved1: SmallWord; cCylinders: SmallWord; cHeads: SmallWord; cSectorsPerTrack: SmallWord; reserved2: SmallWord; reserved3: SmallWord; reserved4: SmallWord; reserved5: SmallWord; End; Type PDEVICEPARAMETERBLOCK = ^DEVICEPARAMETERBLOCK; ═══ DEVICEPARAMETERBLOCK Field - reserved1 ═══ reserved1 (USHORT) Reserved. ═══ DEVICEPARAMETERBLOCK Field - cCylinders ═══ cCylinders (USHORT) Number of Cylinders on the physical drive. ═══ DEVICEPARAMETERBLOCK Field - cHeads ═══ cHeads (USHORT) Number of Heads on the physical drive. ═══ DEVICEPARAMETERBLOCK Field - cSectorsPerTrack ═══ cSectorsPerTrack (USHORT) Number of Sectors per track on the physical drive. ═══ DEVICEPARAMETERBLOCK Field - reserved2 ═══ reserved2 (USHORT) Reserved. ═══ DEVICEPARAMETERBLOCK Field - reserved3 ═══ reserved3 (USHORT) Reserved. ═══ DEVICEPARAMETERBLOCK Field - reserved4 ═══ reserved4 (USHORT) Reserved. ═══ DEVICEPARAMETERBLOCK Field - reserved5 ═══ reserved5 (USHORT) Reserved. ═══ 1.18. DosDebug Buffer ═══ DosDebug buffer structure. Type _DosDebug buffer = Record ULONG Pid; /* Debuggee Process ID */ ULONG Tid; /* Debuggee Thread ID */ LONG Cmd; /* Command or Notification */ LONG Value; /* Generic Data Value */ ULONG Addr; /* Debuggee Address */ ULONG Buffer; /* Debugger Buffer Address */ ULONG Len; /* Length of Range */ ULONG Index; /* Generic Identifier Index */ ULONG MTE; /* Module Table Entry Handle */ ULONG EAX; /* Register Set */ ULONG ECX; ULONG EDX; ULONG EBX; ULONG ESP; ULONG EBP; ULONG ESI; ULONG EDI; ULONG EFlags; ULONG EIP; ULONG CSLim; /* Byte Granular Limits */ ULONG CSBase; /* Byte Granular Base */ UCHAR CSAcc; /* Access Bytes */ UCHAR CSAtr; /* Attribute Bytes */ USHORT CS; ULONG DSLim; ULONG DSBase; UCHAR DSAcc; UCHAR DSAtr; USHORT DS; ULONG ESLim; ULONG ESBase; UCHAR ESAcc; UCHAR ESAtr; USHORT ES; ULONG FSLim; ULONG FSBase; UCHAR FSAcc; UCHAR FSAtr; USHORT FS; ULONG GSLim; ULONG GSBase; UCHAR GSAcc; UCHAR GSAtr; USHORT GS; ULONG SSLim; ULONG SSBase; UCHAR SSAcc; UCHAR SSAtr; USHORT SS; End; Type DBUGBUF = ^DosDebug Buffer; ═══ DosDebug Buffer Field - Pid ═══ Pid (ULONG) Debuggee Process ID ═══ DosDebug Buffer Field - Tid ═══ Tid (ULONG) Debuggee Thread ID ═══ DosDebug Buffer Field - Cmd ═══ Cmd (LONG) Command or Notification ═══ DosDebug Buffer Field - Value ═══ Value (LONG) Generic Data Value ═══ DosDebug Buffer Field - Addr ═══ Addr (ULONG) Debuggee Address ═══ DosDebug Buffer Field - Buffer ═══ Buffer (ULONG) Debugger Buffer Address ═══ DosDebug Buffer Field - Len ═══ Len (ULONG) Length of Range ═══ DosDebug Buffer Field - Index ═══ Index (ULONG) Generic Identifier Index ═══ DosDebug Buffer Field - MTE ═══ MTE (ULONG) Module Table Entry Handle ═══ DosDebug Buffer Field - EAX ═══ EAX (ULONG) Register Set ═══ DosDebug Buffer Field - ECX ═══ ECX (ULONG) ═══ DosDebug Buffer Field - EDX ═══ EDX (ULONG) ═══ DosDebug Buffer Field - EBX ═══ EBX (ULONG) ═══ DosDebug Buffer Field - ESP ═══ ESP (ULONG) ═══ DosDebug Buffer Field - EBP ═══ EBP (ULONG) ═══ DosDebug Buffer Field - ESI ═══ ESI (ULONG) ═══ DosDebug Buffer Field - EDI ═══ EDI (ULONG) ═══ DosDebug Buffer Field - EFlags ═══ EFlags (ULONG) ═══ DosDebug Buffer Field - EIP ═══ EIP (ULONG) ═══ DosDebug Buffer Field - CSLim ═══ CSLim (ULONG) Byte Granular Limits ═══ DosDebug Buffer Field - CSBase ═══ CSBase (ULONG) Byte Granular Base ═══ DosDebug Buffer Field - CSAcc ═══ CSAcc (UCHAR) Access Bytes ═══ DosDebug Buffer Field - CSAtr ═══ CSAtr (UCHAR) Attribute Bytes ═══ DosDebug Buffer Field - CS ═══ CS (USHORT) ═══ DosDebug Buffer Field - DSLim ═══ DSLim (ULONG) ═══ DosDebug Buffer Field - DSBase ═══ DSBase (ULONG) ═══ DosDebug Buffer Field - DSAcc ═══ DSAcc (UCHAR) ═══ DosDebug Buffer Field - DSAtr ═══ DSAtr (UCHAR) ═══ DosDebug Buffer Field - DS ═══ DS (USHORT) ═══ DosDebug Buffer Field - ESLim ═══ ESLim (ULONG) ═══ DosDebug Buffer Field - ESBase ═══ ESBase (ULONG) ═══ DosDebug Buffer Field - ESAcc ═══ ESAcc (UCHAR) ═══ DosDebug Buffer Field - ESAtr ═══ ESAtr (UCHAR) ═══ DosDebug Buffer Field - ES ═══ ES (USHORT) ═══ DosDebug Buffer Field - FSLim ═══ FSLim (ULONG) ═══ DosDebug Buffer Field - FSBase ═══ FSBase (ULONG) ═══ DosDebug Buffer Field - FSAcc ═══ FSAcc (UCHAR) ═══ DosDebug Buffer Field - FSAtr ═══ FSAtr (UCHAR) ═══ DosDebug Buffer Field - FS ═══ FS (USHORT) ═══ DosDebug Buffer Field - GSLim ═══ GSLim (ULONG) ═══ DosDebug Buffer Field - GSBase ═══ GSBase (ULONG) ═══ DosDebug Buffer Field - GSAcc ═══ GSAcc (UCHAR) ═══ DosDebug Buffer Field - GSAtr ═══ GSAtr (UCHAR) ═══ DosDebug Buffer Field - GS ═══ GS (USHORT) ═══ DosDebug Buffer Field - SSLim ═══ SSLim (ULONG) ═══ DosDebug Buffer Field - SSBase ═══ SSBase (ULONG) ═══ DosDebug Buffer Field - SSAcc ═══ SSAcc (UCHAR) ═══ DosDebug Buffer Field - SSAtr ═══ SSAtr (UCHAR) ═══ DosDebug Buffer Field - SS ═══ SS (USHORT) ═══ 1.19. EAOP2 ═══ EAOP2 data structure. Type EAOP2 = Record fpGEA2List: PGea2List; { GEA set } fpFEA2List: PFea2List; { FEA set } oError: ULong; { offset of FEA error } End; Type PEAOP2 = ^EAOP2; ═══ EAOP2 Field - fpGEA2List ═══ fpGEA2List (PGEA2LIST) GEA set. ═══ EAOP2 Field - fpFEA2List ═══ fpFEA2List (PFEA2LIST) FEA set. ═══ EAOP2 Field - oError ═══ oError (ULONG) Offset of FEA error. ═══ 1.20. EASIZEBUF ═══ Maximum size of extended attributes (EAs). Type EASIZEBUF = Record cbMaxEASize: SmallWord; { max. size of one EA } cbMaxEAListSize: ULong; { max size of the full EA List } End; Type PEASIZEBUF = ^EASIZEBUF; ═══ EASIZEBUF Field - cbMaxEASize ═══ cbMaxEASize (USHORT) Maximum size of an EA. ═══ EASIZEBUF Field - cbMaxEAListSize ═══ cbMaxEAListSize (ULONG) Maximum size of the full EA list. ═══ 1.21. ERRORID ═══ Error identity. Type ERRORID = ULONG; ═══ 1.22. EXCEPTIONREGISTRATIONRECORD ═══ These structures are linked together to form a chain of exception handlers that are dispatched upon receipt of an exception. Exception handlers should not be registered directly from a high level language such as "C". This is the responsibility of the language runtime routine. Type EXCEPTIONREGISTRATIONRECORD = Record Prev_Structure: PExceptionRegistrationRecord; ExceptionHandler: Err; End; Type PEXCEPTIONREGISTRATIONRECORD = ^EXCEPTIONREGISTRATIONRECORD; ═══ EXCEPTIONREGISTRATIONRECORD Field - prev_structure ═══ prev_structure (STRUCT _EXCEPTIONREGISTRATIONRECORD *) Nested exception registration record structure. This field should be treated as a C-language volatile field. That is, even though this field may be changed in ways unknown to your program, the intent of the original expression will be maintained. ═══ EXCEPTIONREGISTRATIONRECORD Field - ExceptionHandler ═══ ExceptionHandler (_ERR *) Pointer to the ERR function. This field must be treated as a C-language volatile field. That is, even though this field may be changed in ways unknown to your program, the intent of the original expression will be maintained. The ERR function is defined below: typedef ULONG APIENTRY _ERR (PEXECPTIONREPORTRECORD, struct _EXCEPTIONREGISTRATIONRECORD *, PCONTEXTRECORD, PVOID); PErr - Not defined ═══ 1.23. EXCEPTIONREPORTRECORD ═══ This structure contains machine-independent information about an exception or unwind. No system exception will ever have more parameters than the value of EXCEPTION_MAXIMUM_PARAMETERS. User exceptions are not bound to this limit. Type ExceptionReportRecord = Record ExceptionNum: ULong; { exception number } fHandlerFlags: ULong; NestedExceptionReportRecord: PExceptionReportRecord; ExceptionAddress: Pointer; cParameters: ULong; { Size of Exception Specific Info } ExceptionInfo: array [0..exception_Maximum_Parameters-1] of ULong; end; { Exception Specfic Info } Type PEXCEPTIONREPORTRECORD = ^EXCEPTIONREPORTRECORD; ═══ EXCEPTIONREPORTRECORD Field - ExceptionNum ═══ ExceptionNum (ULONG) Exception number. ═══ EXCEPTIONREPORTRECORD Field - fHandlerFlags ═══ fHandlerFlags (ULONG) Handler flags. ═══ EXCEPTIONREPORTRECORD Field - NestedExceptionReportRecord ═══ NestedExceptionReportRecord (STRUCT _EXCEPTIONREPORTRECORD *) Nested exception report record structure. ═══ EXCEPTIONREPORTRECORD Field - ExceptionAddress ═══ ExceptionAddress (PVOID) Address of the exception. ═══ EXCEPTIONREPORTRECORD Field - cParameters ═══ cParameters (ULONG) Size of exception specific information. ═══ EXCEPTIONREPORTRECORD Field - ExceptionInfo[EXCEPTION_MAXIMUM_PARAMETERS] ═══ ExceptionInfo[EXCEPTION_MAXIMUM_PARAMETERS] (ULONG) Exception specific information. ═══ 1.24. FDATE ═══ Date data structure for file-system functions. Type FDATE = Record const mfdDay = $001F; sfdDay = 0; mfdMonth = $01E0; sfdMonth = 5; mfdYear = $FE00; sfdYear = 9; type PVolumeLabel = ^VolumeLabel; VolumeLabel = String[12]; PFsInfo = ^FsInfo; FsInfo = record fdateCreation: FDate; ftimeCreation: FTime; vol: VolumeLabel; End; Type PFDATE = ^FDATE; ═══ FDATE Field - day:5 ═══ day:5 (USHORT) Binary day for directory entry. ═══ FDATE Field - month:4 ═══ month:4 (USHORT) Binary month for directory entry. ═══ FDATE Field - year:7 ═══ year:7 (USHORT) The number of years since 1980 for this directory entry. ═══ 1.25. FEA2 ═══ FEA2 defines the format for setting the full extended attributes in the file. Type FEA2 = Record oNextEntryOffset: ULong; { new field } fEA: Byte; cbName: Byte; cbValue: SmallWord; szName: Char; { new field } End; Type PFEA2 = ^FEA2; Extended attributes (EAs) are non-critical by default. A non-critical EA is one that is not necessary to the functionality of the application. If a non-critical EA is lost, the system continues to operate correctly. For example, losing the icons associated with data files does not generally cause any ill effect other than the inability to show the icon. A critical extended attribute is one which is necessary for the correct operation of the operating system or of a particular operation. EAs should be marked as critical if their loss would cause the system or program to perform incorrectly. For example, a mail program might store mail headers in EAs. The loss of the header from a message would normally render the mail program unable to further use that message. This would be unacceptable, so the mail program should mark this EA as critical. ═══ FEA2 Field - oNextEntryOffset ═══ oNextEntryOffset (ULONG) Offset to next entry. ═══ FEA2 Field - fEA ═══ fEA (BYTE) Extended attributes flag. FEA_NEEDEA Extended attributes are critical. If this flag is set, this file cannot be copied to a file system that does not support extended attributes. This flag should only be set if extended attributes are critical to the processing of this file. 0 Extended attributes are not critical. ═══ FEA2 Field - cbName ═══ cbName (BYTE) Length of szName, not including nil. This value must be greater than 0. ═══ FEA2 Field - cbValue ═══ cbValue (USHORT) Value length. Sending an EA with cbValue set to 0 in the FEA2 data structure causes that attribute to be deleted, if possible. Receiving an EA with cbValue set to 0 in the FEA2 data structure indicates that the attribute is not present. ═══ FEA2 Field - szName[1] ═══ szName[1] (CHAR) Extended attribute name. ═══ 1.26. FEA2LIST ═══ FEA2 data structure. Type FEA2LIST = Record cbList: ULong; list: array [0..0] of Fea2; End; Type PFEA2LIST = ^FEA2LIST; ═══ FEA2LIST Field - cbList ═══ cbList (ULONG) Total bytes of structure including full list. ═══ FEA2LIST Field - list[1] ═══ list[1] (FEA2) Variable-length FEA2 structures. ═══ 1.27. FHLOCK ═══ Unsigned integer in the range 0 through 4 294 967 295. Type FHLOCK = ULong; ═══ 1.28. FILEFINDBUF ═══ Find file buffer data structure. Type FILEFINDBUF = Record fdateCreation: FDate; ftimeCreation: FTime; fdateLastAccess: FDate; ftimeLastAccess: FTime; fdateLastWrite: FDate; ftimeLastWrite: FTime; cbFile: ULong; cbFileAlloc: ULong; attrFile: SmallWord; achName: String[cchMaxPathComp-1]; End; Type PFILEFINDBUF = ^FILEFINDBUF; ═══ FILEFINDBUF Field - fdateCreation ═══ fdateCreation (FDATE) Date of file creation. ═══ FILEFINDBUF Field - ftimeCreation ═══ ftimeCreation (FTIME) Time of file creation. ═══ FILEFINDBUF Field - fdateLastAccess ═══ fdateLastAccess (FDATE) Date of last access. ═══ FILEFINDBUF Field - ftimeLastAccess ═══ ftimeLastAccess (FTIME) Time of last access. ═══ FILEFINDBUF Field - fdateLastWrite ═══ fdateLastWrite (FDATE) Date of last write. ═══ FILEFINDBUF Field - ftimeLastWrite ═══ ftimeLastWrite (FTIME) Time of last write. ═══ FILEFINDBUF Field - cbFile ═══ cbFile (ULONG) Size of file. ═══ FILEFINDBUF Field - cbFileAlloc ═══ cbFileAlloc (ULONG) Allocated size. ═══ FILEFINDBUF Field - attrFile ═══ attrFile (USHORT) File attributes. ═══ FILEFINDBUF Field - cchName ═══ cchName (UCHAR) Length of file name. ═══ FILEFINDBUF Field - achName[CCHMAXPATHCOMP] ═══ achName[CCHMAXPATHCOMP] (CHAR) File name including null terminator. ═══ 1.29. FILEFINDBUF3 ═══ Level 1 (32-bit) information (used without EAs). Type FILEFINDBUF3 = Record oNextEntryOffset: ULong; { new field } fdateCreation: FDate; ftimeCreation: FTime; fdateLastAccess: FDate; ftimeLastAccess: FTime; fdateLastWrite: FDate; ftimeLastWrite: FTime; cbFile: ULong; cbFileAlloc: ULong; attrFile: ULong; { widened field } achName: String[cchMaxPathComp-1]; End; Type PFILEFINDBUF3 = ^FILEFINDBUF3; ═══ FILEFINDBUF3 Field - oNextEntryOffset ═══ oNextEntryOffset (ULONG) Offset of next entry. ═══ FILEFINDBUF3 Field - fdateCreation ═══ fdateCreation (FDATE) Date of file creation. ═══ FILEFINDBUF3 Field - ftimeCreation ═══ ftimeCreation (FTIME) Time of file creation. ═══ FILEFINDBUF3 Field - fdateLastAccess ═══ fdateLastAccess (FDATE) Date of last access. ═══ FILEFINDBUF3 Field - ftimeLastAccess ═══ ftimeLastAccess (FTIME) Time of last access. ═══ FILEFINDBUF3 Field - fdateLastWrite ═══ fdateLastWrite (FDATE) Date of last write. ═══ FILEFINDBUF3 Field - ftimeLastWrite ═══ ftimeLastWrite (FTIME) Time of last write. ═══ FILEFINDBUF3 Field - cbFile ═══ cbFile (ULONG) Size of file. ═══ FILEFINDBUF3 Field - cbFileAlloc ═══ cbFileAlloc (ULONG) Allocation size. ═══ FILEFINDBUF3 Field - attrFile ═══ attrFile (ULONG) File attributes. ═══ FILEFINDBUF3 Field - cchName ═══ cchName (UCHAR) ═══ FILEFINDBUF3 Field - achName[CCHMAXPATHCOMP] ═══ achName[CCHMAXPATHCOMP] (CHAR) File name including null terminator. ═══ 1.30. FILEFINDBUF4 ═══ Level 2 (32-bit) information (used with EAs). Type FILEFINDBUF4 = Record oNextEntryOffset: ULong; { new field } fdateCreation: FDate; ftimeCreation: FTime; fdateLastAccess: FDate; ftimeLastAccess: FTime; fdateLastWrite: FDate; ftimeLastWrite: FTime; cbFile: ULong; cbFileAlloc: ULong; attrFile: ULong; { widened field } cbList: ULong; achName: String[cchMaxPathComp-1]; End; Type PFILEFINDBUF4 = ^FILEFINDBUF4; ═══ FILEFINDBUF4 Field - oNextEntryOffset ═══ oNextEntryOffset (ULONG) Offset of next entry. ═══ FILEFINDBUF4 Field - fdateCreation ═══ fdateCreation (FDATE) Date of file creation. ═══ FILEFINDBUF4 Field - ftimeCreation ═══ ftimeCreation (FTIME) Time of file creation. ═══ FILEFINDBUF4 Field - fdateLastAccess ═══ fdateLastAccess (FDATE) Date of last access. ═══ FILEFINDBUF4 Field - ftimeLastAccess ═══ ftimeLastAccess (FTIME) Time of last access. ═══ FILEFINDBUF4 Field - fdateLastWrite ═══ fdateLastWrite (FDATE) Date of last write. ═══ FILEFINDBUF4 Field - ftimeLastWrite ═══ ftimeLastWrite (FTIME) Time of last write. ═══ FILEFINDBUF4 Field - cbFile ═══ cbFile (ULONG) Size of file. ═══ FILEFINDBUF4 Field - cbFileAlloc ═══ cbFileAlloc (ULONG) Allocated size. ═══ FILEFINDBUF4 Field - attrFile ═══ attrFile (ULONG) File attributes. ═══ FILEFINDBUF4 Field - cbList ═══ cbList (ULONG) Size of the file's extended attributes. The size is measured in bytes and is the size of the file's entire extended attribute set on the disk. ═══ FILEFINDBUF4 Field - cchName ═══ cchName (UCHAR) Length of file name. ═══ FILEFINDBUF4 Field - achName[CCHMAXPATHCOMP] ═══ achName[CCHMAXPATHCOMP] (CHAR) File name including null terminator. ═══ 1.31. FILELOCK ═══ FILELOCK data structure. Type FILELOCK = Record lOffset: Long; lRange: Long; End; Type PFILELOCK = ^FILELOCK; ═══ FILELOCK Field - lOffset ═══ lOffset (LONG) Offset to the beginning of the lock (or unlock) range. ═══ FILELOCK Field - lRange ═══ lRange (LONG) Length, in bytes, of the lock (or unlock) range. A value of 0 indicates that locking (or unlocking) is not required. ═══ 1.32. FILESTATUS ═══ Use for Level 1 (FIL_STANDARD) file information for:  DosQueryFileInfo  DosQueryPathInfo  DosSetFileInfo  DosSetPathInfo Type FILESTATUS = Record fdateCreation: FDate; ftimeCreation: FTime; fdateLastAccess: FDate; ftimeLastAccess: FTime; fdateLastWrite: FDate; ftimeLastWrite: FTime; cbFile: ULong; cbFileAlloc: ULong; attrFile: SmallWord; End; Type PFILESTATUS = ^FILESTATUS; ═══ FILESTATUS Field - fdateCreation ═══ fdateCreation (FDATE) Date of file creation. ═══ FILESTATUS Field - ftimeCreation ═══ ftimeCreation (FTIME) Time of file creation. ═══ FILESTATUS Field - fdateLastAccess ═══ fdateLastAccess (FDATE) Date of last access. ═══ FILESTATUS Field - ftimeLastAccess ═══ ftimeLastAccess (FTIME) Time of last access. ═══ FILESTATUS Field - fdateLastWrite ═══ fdateLastWrite (FDATE) Date of last write. ═══ FILESTATUS Field - ftimeLastWrite ═══ ftimeLastWrite (FTIME) Time of last write. ═══ FILESTATUS Field - cbFile ═══ cbFile (ULONG) File size (end of data). ═══ FILESTATUS Field - cbFileAlloc ═══ cbFileAlloc (ULONG) File allocated size. ═══ FILESTATUS Field - attrFile ═══ attrFile (USHORT) Attributes of the file. ═══ 1.33. FILESTATUS3 ═══ Level 1 (32-bit) (FIL_STANDARD) information. Type FILESTATUS3 = Record fdateCreation: FDate; ftimeCreation: FTime; fdateLastAccess: FDate; ftimeLastAccess: FTime; fdateLastWrite: FDate; ftimeLastWrite: FTime; cbFile: ULong; cbFileAlloc: ULong; attrFile: ULong; End; Type PFILESTATUS3 = ^FILESTATUS3; ═══ FILESTATUS3 Field - fdateCreation ═══ fdateCreation (FDATE) Date of file creation. ═══ FILESTATUS3 Field - ftimeCreation ═══ ftimeCreation (FTIME) Time of file creation. ═══ FILESTATUS3 Field - fdateLastAccess ═══ fdateLastAccess (FDATE) Date of last access. ═══ FILESTATUS3 Field - ftimeLastAccess ═══ ftimeLastAccess (FTIME) Time of last access. ═══ FILESTATUS3 Field - fdateLastWrite ═══ fdateLastWrite (FDATE) Date of last write. ═══ FILESTATUS3 Field - ftimeLastWrite ═══ ftimeLastWrite (FTIME) Time of last write. ═══ FILESTATUS3 Field - cbFile ═══ cbFile (ULONG) File size (end of data). ═══ FILESTATUS3 Field - cbFileAlloc ═══ cbFileAlloc (ULONG) File allocated size. ═══ FILESTATUS3 Field - attrFile ═══ attrFile (ULONG) Attributes of the file. ═══ 1.34. FILESTATUS4 ═══ Level 2 (32-bit) (FIL_QUERYEASIZE) information. Type FILESTATUS4 = Record fdateCreation: FDate; ftimeCreation: FTime; fdateLastAccess: FDate; ftimeLastAccess: FTime; fdateLastWrite: FDate; ftimeLastWrite: FTime; cbFile: ULong; cbFileAlloc: ULong; attrFile: ULong; cbList: ULong; End; Type PFILESTATUS4 = ^FILESTATUS4; ═══ FILESTATUS4 Field - fdateCreation ═══ fdateCreation (FDATE) Date of file creation. ═══ FILESTATUS4 Field - ftimeCreation ═══ ftimeCreation (FTIME) Time of file creation. ═══ FILESTATUS4 Field - fdateLastAccess ═══ fdateLastAccess (FDATE) Date of last access. ═══ FILESTATUS4 Field - ftimeLastAccess ═══ ftimeLastAccess (FTIME) Time of last access. ═══ FILESTATUS4 Field - fdateLastWrite ═══ fdateLastWrite (FDATE) Date of last write. ═══ FILESTATUS4 Field - ftimeLastWrite ═══ ftimeLastWrite (FTIME) Time of last write. ═══ FILESTATUS4 Field - cbFile ═══ cbFile (ULONG) File size (end of data). ═══ FILESTATUS4 Field - cbFileAlloc ═══ cbFileAlloc (ULONG) File allocated size. ═══ FILESTATUS4 Field - attrFile ═══ attrFile (ULONG) Attributes of the file. ═══ FILESTATUS4 Field - cbList ═══ cbList (ULONG) Length of entire EA set. ═══ 1.35. FPREG ═══ Coprocessor stack register element. Type FPREG = Record losig: ULong; hisig: ULong; signexp: SmallWord; End; Type PFPREG = ^FPREG; A floating point register is 80 bits wide and consists of three fields. The following graphic shows the layout of the floating point register: 79 78 64 63 0 ┌───@>@────────────@>@───────────────────────────────────────┐ │ │ │ Significand │ └─@>@─@>@──────@>@─────@>@───────────────────────────────────────┘ │ │ │ Exponent Sign ═══ FPREG Field - losig ═══ losig (ULONG) Low 32-bits of the significand. The low 32-bits of the number's significant digits are held in the lower part of the significand field. ═══ FPREG Field - hisig ═══ hisig (ULONG) High 32-bits of the significand. The high 32-bits of the number's significant digits are held in the higher part of the significand field. ═══ FPREG Field - signexp ═══ signexp (USHORT) Sign and exponent. The exponent field (bits 64-78) locates the binary point within the significand field (bits 0-63). The 1-bit sign field (bit 79) indicates whether the number is positive or negative ═══ 1.36. FRAME ═══ Frame control data structure. Type FRAME = Record bCharsPerLine: Byte; bLinesPerInch: Byte; End; Type PFRAME = ^FRAME; ═══ FRAME Field - bCharsPerLine ═══ bCharsPerLine (BYTE) Characters per line. Valid values are 80 and 132. ═══ FRAME Field - bLinesPerInch ═══ bLinesPerInch (BYTE) Lines per inch. Valid values are 6 and 8. ═══ 1.37. FSALLOCATE ═══ File-system device allocation. Type FSALLOCATE = Record idFileSystem: ULong; cSectorUnit: ULong; cUnit: ULong; cUnitAvail: ULong; cbSector: SmallWord; End; Type PFSALLOCATE = ^FSALLOCATE; ═══ FSALLOCATE Field - idFileSystem ═══ idFileSystem (ULONG) File system identification. ═══ FSALLOCATE Field - cSectorUnit ═══ cSectorUnit (ULONG) Number of sectors per allocation unit. ═══ FSALLOCATE Field - cUnit ═══ cUnit (ULONG) Number of allocation units. ═══ FSALLOCATE Field - cUnitAvail ═══ cUnitAvail (ULONG) Number of allocation units available. ═══ FSALLOCATE Field - cbSector ═══ cbSector (USHORT) Number of bytes per sector. ═══ 1.38. FSINFO ═══ File-system information data structure. Type FSINFO = Record fdateCreation: FDate; ftimeCreation: FTime; vol: VolumeLabel; End; Type PFSINFO = ^FSINFO; ═══ FSINFO Field - ulVSN ═══ ulVSN (ULONG) Volume Serial Number. Volume Serial Number is a unique 32-bit number that the operating system uses to identify its disk or diskette volumes. The hard error prompts the user for an unmounted removable volume by displaying both the Volume Serial Number (an 8-digit hexadecimal number) and the Volume Label. ═══ FSINFO Field - vol ═══ vol (VOLUMELABEL) Volume label. Trailing blanks in the time the volume label are not considered part of the label, and are not returned as valid label data. The volume label is limited to a length of 11 bytes. ═══ 1.39. FSQBUFFER2 ═══ Data structure for information about an attached file system (local or remote), or about a character device or pseudocharacter device attached to the file system. Type FSQBUFFER2 = Record iType: SmallWord; cbName: SmallWord; cbFSDName: SmallWord; cbFSAData: SmallWord; szName: array [0..0] of Char; szFSDName: array [0..0] of Char; rgFSAData: array [0..0] of Byte; End; Type PFSQBUFFER2 = ^FSQBUFFER2; { FSQBUF DATA STRUCTURE FOR QFSATTACH} ═══ FSQBUFFER2 Field - iType ═══ iType (USHORT) Type of item. Possible values are described in the following list: 1 FSAT_CHARDEV Resident character device 2 FSAT_PSEUDODEV Pseudocharacter device 3 FSAT_LOCALDRV Local drive 4 FSAT_REMOTEDRV Remote drive attached to the file-system driver. ═══ FSQBUFFER2 Field - cbName ═══ cbName (USHORT) Length, in bytes, of the item name, not including null. ═══ FSQBUFFER2 Field - cbFSDName ═══ cbFSDName (USHORT) Length, in bytes, of the file-system driver name, not including null. ═══ FSQBUFFER2 Field - cbFSAData ═══ cbFSAData (USHORT) Length, in bytes, of the file-system driver Attach data returned by the file-system driver. ═══ FSQBUFFER2 Field - szName[1] ═══ szName[1] (UCHAR) Item name. The name is an ASCIIZ string. ═══ FSQBUFFER2 Field - szFSDName[1] ═══ szFSDName[1] (UCHAR) Name of the file-system driver that the item is attached to. The name is an ASCIIZ string. ═══ FSQBUFFER2 Field - rgFSAData[1] ═══ rgFSAData[1] (UCHAR) File-system driver Attach data returned by the file-system driver. ═══ 1.40. FTIME ═══ Time data structure for file-system functions. Type FTIME = Record const mftTwoSecs = $001F; sftTwoSecs = 0; mftMinutes = $07E0; sftMinutes = 5; mftHours = $F800; sftHours = 11; (* typedef struct _FDATE { UINT day : 5; UINT month : 4; UINT year : 7; } FDATE; *) type PFDate = ^FDate; FDate = SmallWord; const mfdDay = $001F; sfdDay = 0; mfdMonth = $01E0; sfdMonth = 5; mfdYear = $FE00; sfdYear = 9; type PVolumeLabel = ^VolumeLabel; VolumeLabel = String[12]; PFsInfo = ^FsInfo; FsInfo = record fdateCreation: FDate; ftimeCreation: FTime; vol: VolumeLabel; End; Type PFTIME = ^FTIME; ═══ FTIME Field - twosecs:5 ═══ twosecs:5 (USHORT) Binary number of two-second increments. ═══ FTIME Field - minutes:6 ═══ minutes:6 (USHORT) Binary number of minutes. ═══ FTIME Field - hours:5 ═══ hours:5 (USHORT) Binary number of hours. ═══ 1.41. GEA2 ═══ Level 3 (32-bit) (FIL_QUERYEASFROMLIST) file information - get extended attributes. Type GEA2 = Record oNextEntryOffset: ULong; { new field } cbName: Byte; szName: array [0..0] of Char; End; Type PGEA2 = ^GEA2; ═══ GEA2 Field - oNextEntryOffset ═══ oNextEntryOffset (ULONG) Offset to next entry. ═══ GEA2 Field - cbName ═══ cbName (BYTE) Name length not including nil. ═══ GEA2 Field - szName[1] ═══ szName[1] (CHAR) Attribute name. ═══ 1.42. GEA2LIST ═══ Get extended attributes list. Type GEA2LIST = Record cbList: ULong; list: array[0..0] of Gea2; End; Type PGEA2LIST = ^GEA2LIST; ═══ GEA2LIST Field - cbList ═══ cbList (ULONG) Total bytes of structure including full list. ═══ GEA2LIST Field - list[1] ═══ list[1] (GEA2) Variable-length GEA2 structures. ═══ 1.43. HDC ═══ Device-context handle. Type HDC = LHANDLE; ═══ 1.44. HDIR ═══ Value (32-bit) used as a directory handle. Type HDIR = LHANDLE; ═══ 1.45. HEV ═══ Value (32-bit) used as an event semaphore handle. Type HEV = ULONG; ═══ 1.46. HFILE ═══ File handle. Type HFILE = SHANDLE; ═══ 1.47. HKBD ═══ Keyboard handle. Type HKBD = BYTE; ═══ 1.48. HMODULE ═══ Module handle. Type HMODULE = LHANDLE; ═══ 1.49. HMTX ═══ Value (32-bit) used as a mutex semaphore handle. Type HMTX = ULONG; ═══ 1.50. HMUX ═══ Value (32-bit) used as a muxwait semaphore handle. Type HMUX = ULONG; ═══ 1.51. HOTKEY ═══ Session Manager Hot Key data structure. Type HOTKEY = Record fsHotKey: SmallWord; uchScancodeMake: Byte; uchScancodeBreak: Byte; idHotKey: SmallWord; End; Type PHOTKEY = ^HOTKEY; ═══ HOTKEY Field - fsHotKey ═══ fsHotKey (USHORT) State Key Flag. Has the following settings: High Byte Bit settings are as follows: Bit 15 Reserved = 0 Bit 14 Reserved = 0 Bit 13 Reserved = 0 Bit 12 Reserved = 0 Bit 11 Right Alt key down Bit 10 Right Ctrl key down Bit 9 Left Alt key down Bit 8 Left Ctrl key down Low Byte Bit settings are as follows: Bit 7 Reserved = 0 Bit 6 Reserved = 0 Bit 5 Reserved = 0 Bit 4 Reserved = 0 Bit 3 Reserved = 0 Bit 2 Reserved = 0 Bit 1 Left Shift key down Bit 0 Right Shift key down ═══ HOTKEY Field - uchScancodeMake ═══ uchScancodeMake (UCHAR) The Scan Code of the hot key, Make. ═══ HOTKEY Field - uchScancodeBreak ═══ uchScancodeBreak (UCHAR) The Scan Code of the hot key, Break. ═══ HOTKEY Field - idHotKey ═══ idHotKey (USHORT) Hot Key Id. The Hot Key Id value is set by the caller. Notice that ID value FFFFh is reserved and must not be used as a Hot Key ID. See Remarks in KBD_SETSESMGRHOTKEY. A maximum of two of the above bit selections can be selected for a given hot key definition. If more than two bits are selected or if a reserved bit is selected, the result is an INVALID_PARAMETER error code returned to the caller. ═══ 1.52. HPIPE ═══ Value (32-bit) used as a pipe handle. Type HPIPE = LHANDLE; ═══ 1.53. HQUEUE ═══ Value (32-bit) used as a system queue handle. Type HQUEUE = LHANDLE; ═══ 1.54. HRGN ═══ Region handle. Type HRGN = LHANDLE; ═══ 1.55. HSEM ═══ Semaphore handle. Type HSEM = POINTER; ═══ 1.56. HSPINLOCK ═══ A value (32-bit) used as a handle to a spin lock. ═══ 1.57. HTIMER ═══ Value (32-bit) used as a timer handle. Type HTIMER = LHANDLE; ═══ 1.58. HVDD ═══ 32-bit value used as a virtual device driver handle. Type HVDD = LHANDLE; ═══ 1.59. KBDKEYINFO ═══ The Character data structure of the API function KbdCharIn. Type KBDKEYINFO = Record chChar: Char; { ASCII char (0 or $E0 = extended ASCII) } chScan: Byte; { Scan code (or extended ASCII char) } fbStatus: Byte; { Miscellaneous status; key recieved flag } bNlsShift: Byte; { NLS Shift Status } fsState: SmallWord; { State of shift keys } time: ULong; { TimeStamp (unique number of milliseconds) } End; Type PKBDKEYINFO = ^KBDKEYINFO; ═══ KBDKEYINFO Field - chChar ═══ chChar (UCHAR) ASCII Character code. The scan code received from the keyboard is translated to the ASCII character code. ═══ KBDKEYINFO Field - chScan ═══ chScan (UCHAR) Code received for the keyboard. Scan code received from the keyboard is translated to the ASCII character code. ═══ KBDKEYINFO Field - fbStatus ═══ fbStatus (UCHAR) State of the keystroke event flag. Bits 7-6 Has the following values: 00 Undefined. 01 Final character; interim character flag is turned off. 10 Interim character. 11 Final character; interim character flag is turned on. Bit 5 If set to 1, immediate conversion requested. Bits 4-2 Reserved. Bit 1 Has the following values: 0 Scan code is a character 1 Scan code is not a character; instead it is an extended key code from the keyboard. Bit 0 If set to 1, shift status returned without a character. ═══ KBDKEYINFO Field - bNlsShift ═══ bNlsShift (UCHAR) NLS shift status. Reserved, must be 0. ═══ KBDKEYINFO Field - fsState ═══ fsState (USHORT) Shift key status flag. Values are: Bit 15 SysReq key down Bit 14 Caps Lock key down Bit 13 NumLock key down Bit 12 Scroll Lock key down Bit 11 Right Alt key down Bit 10 Right Ctrl key down Bit 9 Left Alt key down Bit 8 Left Ctrl key down Bit 7 Insert on Bit 6 Caps Lock on Bit 5 NumLock on. Bit 4 Scroll Lock on Bit 3 Either Alt key down Bit 2 Either Ctrl key down Bit 1 Left Shift key down Bit 0 Right Shift key down ═══ KBDKEYINFO Field - time ═══ time (ULONG) Time stamp in milliseconds. Time stamp indicating when a key was pressed. It is specified in milliseconds from the time the system was started. ═══ 1.60. LDTADDRINFO ═══ LDT Address Information data structure. Type LDTADDRINFO = Record pulPhysAddr: PULong; cb: SmallWord; End; Type PLDTADDRINFO = ^LDTADDRINFO; ═══ LDTADDRINFO Field - pulPhysAddr ═══ pulPhysAddr (PULONG) Physical address. ═══ LDTADDRINFO Field - cb ═══ cb (USHORT) Length. ═══ 1.61. LINECONTROL ═══ Line characteristics. Type LINECONTROL = Record bDataBits: Byte; bParity: Byte; bStopBits: Byte; fTransBreak: Byte; End; Type PLINECONTROL = ^LINECONTROL; ═══ LINECONTROL Field - bDataBits ═══ bDataBits (BYTE) Data bits flag. 0x00-0x04 Reserved 0x05 5 data bits 0x06 6 data bits 0x07 7 data bits (initial value) 0x08 8 data bits 0x09-0xFF Reserved ═══ LINECONTROL Field - bParity ═══ bParity (BYTE) Parity flag. 0x00 No parity 0x01 Odd parity 0x02 Even parity (initial value) 0x03 Mark parity (parity bit always 1) 0x04 Space parity (parity bit always 0) 0x05-0xFF Reserved ═══ LINECONTROL Field - bStopBits ═══ bStopBits (BYTE) Stop bit flag. 0x00 1 stop bit (initial value) 0x01 1.5 stop bits (valid with 5-bit WORD length only) 0x02 2 stop bits (not valid with 5-bit WORD length) 0x03-0xFF Reserved ═══ LINECONTROL Field - fTransBreak ═══ fTransBreak (BYTE) Transmitting break flag. 0 Not currently transmitting break. 1 Currently transmitting break. ═══ 1.62. LONG ═══ Signed integer in the range -2 147 483 648 through 2 147 483 647. Note: Where this data type represents a graphic coordinate in world or model space, its value is restricted to -134 217 728 through 134 217 727. A graphic coordinate in device or screen coordinates is restricted to -32 768 through 32 767. The value of a graphic coordinate may be further restricted by any transforms currently in force, including the positioning of the origin of the window on the screen. In particular, coordinates in world or model space must not generate coordinate values after transformation (that is, in device or screen space) outside the range -32 768 through 32 767. ═══ 1.63. MODEMSTATUS ═══ Modem Control data structure. Type MODEMSTATUS = Record fbModemOn: Byte; fbModemOff: Byte; End; Type PMODEMSTATUS = ^MODEMSTATUS; ═══ MODEMSTATUS Field - fbModemOn ═══ fbModemOn (BYTE) Modem Control Signals ON Mask. The physical device driver sets the modem control signals as defined in this field. Bit 0 is DTR; bit 1 is RTS. If any other bits are set/reset by the masks, then a general failure error results. The OFF mask contains a mask (with all bits equal to 0) to turn the modem control signal off. The ON mask contains a mask (with all bits equal to 1) to turn the modem control signal on. If the Parameter Packet indicates both turn off and turn on on the same bit, the bit will be turned on. For example: ┌─────────┬─────────┬───────────────┐ │Mask ON │Mask OFF │Description │ ├─────────┼─────────┼───────────────┤ │ 01h │ FFh │Set DTR │ ├─────────┼─────────┼───────────────┤ │ 00h │ FEh │Clear DTR │ ├─────────┼─────────┼───────────────┤ │ 02h │ FFh │Set RTS │ ├─────────┼─────────┼───────────────┤ │ 00h │ FDh │Clear RTS │ ├─────────┼─────────┼───────────────┤ │ 03h │ FFh │Set DTR and RTS│ ├─────────┼─────────┼───────────────┤ │ 00h │ FCh │Clear DTR and │ │ │ │RTS │ └─────────┴─────────┴───────────────┘ If DTR Control mode input handshaking, RTS Control mode input handshaking or toggling on transmit is set, then this request cannot try to change the state of the modem control signals, that are being used for input handshaking or toggling on transmit. If the request tries to modify a modem control signal that is being used for input handshaking or toggling on transmit, a general failure error results. ═══ MODEMSTATUS Field - fbModemOff ═══ fbModemOff (BYTE) Modem Control Signals OFF Mask. See Modem Control Signals ON Mask above. ═══ 1.64. MONITORPOSITION ═══ Monitor Position data structure. Type MONITORPOSITION = Record fPosition: SmallWord; index: SmallWord; pbInBuf: ULong; offOutBuf: SmallWord; End; Type PMONITORPOSITION = ^MONITORPOSITION; ═══ MONITORPOSITION Field - fPosition ═══ fPosition (USHORT) Placement flag. The DosMonReg function call parameter that is used by an application to indicate:  Where its monitor buffers are to be placed within a monitor chain relative to monitors already registered on the monitor chain  What special processing requirements need to be supported by the monitor dispatcher. Refer to the DosMonReg function description in the OS/2 Control Program Programming Reference for valid parameter values. ═══ MONITORPOSITION Field - index ═══ index (USHORT) Index. Used by an application to indicate on which monitor chain its monitor buffers are being registered. The accepted values for this parameter vary for each device driver. See information on the physical mouse device driver, the physical keyboard device driver and the physical parallel port device driver in the OS/2 Input/Output Device Driver Reference to determine the valid parameter value for each device driver. Refer to the descriptions of the DosMonReg, DosMonRead, and DosMonWrite functions in the OS/2 Control Program Programming Reference for more information. ═══ MONITORPOSITION Field - pbInBuf ═══ pbInBuf (ULONG) Address of the Input Buffer. Specifies the address of a monitor input buffer allocated by an application, initialized by the monitor dispatcher, and used by the DosMonRead function. ═══ MONITORPOSITION Field - offOutBuf ═══ offOutBuf (USHORT) Offset of the Output Buffer. Specifies the offset of a monitor output buffer allocated by an application in the same data segment as the input buffer, initialized by the monitor dispatcher, and used by the DosMonWrite function. ═══ 1.65. NPCH ═══ Pointer (32-bit) to a value or array of values. Not defined ═══ 1.66. NPCHAR ═══ Near pointer to a character. Not defined ═══ 1.67. NPFN ═══ Pointer to a procedure. Not defined. In the header file, this is a two-part definition as shown below: Not defined. Not defined. ═══ 1.68. NPSZ ═══ Pointer (32-bit) to a null-terminated string. Not defined. ═══ 1.69. OEMINFO ═══ OEM information data structure. Type OEMINFO = Record OEMLength: ULong; Manufacturer: SmallWord; ManufacturerData: ULong; End; ═══ OEMINFO Field - OEMLength ═══ OEMLength (ULONG) Length Represents the combined length of all parameter packet fields. This is a required field for all calls, including those made by way of the 32-bit DosDevIOCtl, with a minimum packet length of 10 bytes. ═══ OEMINFO Field - Manufacturer ═══ Manufacturer (USHORT) Manufacturer Specific Data Identifies the name of the manufacturer. ═══ OEMINFO Field - ManufacturerData ═══ ManufacturerData (ULONG) Manufacturer specific data Manufacturer-specific data is in a format designated by the manufacturer. ═══ 1.70. OEMSVGAINFO ═══ OEM graphic adapter information data structure. Type OEMSVGAINFO = Record AdapterType: SmallWord; ChipType: SmallWord; Memory: ULong; End; ═══ OEMSVGAINFO Field - AdapterType ═══ AdapterType (USHORT) Adapter type. Value of the chip set manufacturer. ═══ OEMSVGAINFO Field - ChipType ═══ ChipType (USHORT) Chip Type. Chip type value according to the specific manufacturer. ═══ OEMSVGAINFO Field - Memory ═══ Memory (ULONG) Video memory. Memory size of the video adapter detected. ═══ 1.71. PCH ═══ Pointer to a character string. Not defined. ═══ 1.72. PCSZ ═══ Pointer to a constant null-terminated string. Not defined. ═══ 1.73. PFHLOCK ═══ Pointer to an unsigned integer in the range 0 through 4 294 967 295. Not defined ═══ 1.74. PFN ═══ Pointer to a procedure. Type PFN = POINTER; In the header file, this is a two-part definition as shown below: Not defined Not defined ═══ 1.75. PFNEXITLIST ═══ Address of a routine to be executed. Not defined In the header files, the FNEXITLIST structure is defined as shown below: Not defined ═══ 1.76. PFNSIGHANDLER ═══ Pointer (32-bit) to a function with pascal calling type. Not defined ═══ 1.77. PFNTHREAD ═══ Address of the code to be executed when the thread begins execution. Not defined In the header files, the FNTHREAD structure is defined as shown below: Not defined ═══ 1.78. PIB ═══ Process Information Block structure. Type PIB = Record Pib_ulPid: ULong; { Process I.D. } Pib_ulPPid: ULong; { Parent process I.D. } Pib_hMte: ULong; { Program (.EXE) module handle } Pib_pchCmd: PChar; { Command line pointer } Pib_pchEnv: PChar; { Environment pointer } Pib_flStatus: ULong; { Process' status bits } Pib_ulType: ULong; { Process' type code } End; Type PPIB = ^PIB; An OS/2 application that has been loaded into memory and prepared for execution is called a process. A process is the code, data, and other resources of an application, such as file handles, semaphores, pipes, queues, and so on. The OS/2 operating system considers every application it loads to be a process. Information about a process is kept in a read/write area of the process address space, called the Process Information Block (PIB). The operating system creates and maintains a PIB for every process in the system. An application can access the PIB of a specific process using DosGetInfoBlocks. ═══ PIB Field - pib_ulpid ═══ pib_ulpid (ULONG) Process identifier. ═══ PIB Field - pib_ulppid ═══ pib_ulppid (ULONG) Parent process identifier. ═══ PIB Field - pib_hmte ═══ pib_hmte (ULONG) Module handle of executable program. ═══ PIB Field - pib_pchcmd ═══ pib_pchcmd (PCHAR) Command line pointer. This is the address of the ASCIIZ argument strings passed to the program. This string represents command parameters. The convention used by CMD.EXE is that the first of these strings is the program name (as entered from the command prompt or found in a batch file), and the second string consists of the parameters for the program. The second ASCIIZ string is followed by an additional byte of zeros. A value of zero for the address of pib_pchcmd means that no arguments are to be passed to the program. ═══ PIB Field - pib_pchenv ═══ pib_pchenv (PCHAR) Environment pointer. These strings represent environment variables and their current values. An environment string has the following form: variable=value The last ASCIIZ environment string must be followed by an additional byte of zeros. ═══ PIB Field - pib_flstatus ═══ pib_flstatus (ULONG) Process' status bits. A value of 1 in this bit flag indicates that the current process is in exit list processing. ═══ PIB Field - pib_ultype ═══ pib_ultype (ULONG) Process' type code. The following process' type codes are available: 0 Full screen protect-mode session 1 Requires real mode. Dos emulation. 2 VIO windowable protect-mode session 3 Presentation Manager protect-mode session 4 Detached protect-mode process. ═══ 1.79. PID ═══ Process identity. Type PID = LHANDLE; ═══ 1.80. PIPEINFO ═══ DosQueryNPipeInfo uses this data structure for level-1 information. Type PIPEINFO = Record cbOut: SmallWord; { length of outgoing I/O buffer } cbIn: SmallWord; { length of incoming I/O buffer } cbMaxInst: Byte; { maximum number of instances } cbCurInst: Byte; { current number of instances } cbName: Byte; { length of pipe name } szName: array [0..0] of Char; { start of name } End; Type PIPEINFO = RECORD ═══ PIPEINFO Field - cbOut ═══ cbOut (USHORT) Actual size of the buffer for outbound data. ═══ PIPEINFO Field - cbIn ═══ cbIn (USHORT) Actual size of the buffer for inbound data. ═══ PIPEINFO Field - cbMaxInst ═══ cbMaxInst (BYTE) Maximum number of pipe instances. ═══ PIPEINFO Field - cbCurInst ═══ cbCurInst (BYTE) Current number of pipe instances. ═══ PIPEINFO Field - cbName ═══ cbName (BYTE) Length of szName. ═══ PIPEINFO Field - szName[1] ═══ szName[1] (CHAR) Name of the pipe. The name of the pipe (including \\ComputerName if the pipe is on a remote system). ═══ 1.81. PIPESEMSTATE ═══ Data structure for the status of a named pipe that is attached to a semaphore. Type PIPESEMSTATE = Record fStatus: Byte; { type of record, 0 = EOI, 1 = read ok, } { 2 = write ok, 3 = pipe closed } fFlag: Byte; { additional info, 01 = waiting thread } usKey: SmallWord; { user's key value } usAvail: SmallWord; { available data/space if status = 1/2 } End; Type PPIPESEMSTATE = ^PIPESEMSTATE; ═══ PIPESEMSTATE Field - fStatus ═══ fStatus (BYTE) A coded value that indicates the status of the named pipe. The following codes are available: 0 NPSS_EOI End of information buffer. No more information records follow, and subsequent fields in this information record have no defined value. 1 NPSS_RDATA Read data is available. 2 NPSS_WSPACE Write space is available. 3 NPSS_CLOSE The pipe is closed. ═══ PIPESEMSTATE Field - fFlag ═══ fFlag (BYTE) Additional information about the state of the named pipe. This parameter contains the following bit fields: Bit Description 7-1 Reserved 0 NPSS_WAIT If set, a thread is waiting at the other end of the pipe. ═══ PIPESEMSTATE Field - usKey ═══ usKey (USHORT) A key value that distinguishes events arriving on different named pipes that are attached to the same semaphore. This value is specified in the key field when DosSetNPipeSem is issued. ═══ PIPESEMSTATE Field - usAvail ═══ usAvail (USHORT) Number of bytes available in the pipe. If fStatus has a value of 1, this field contains the number of bytes of data that are available to read from the pipe. If fStatus has a value of 2, this field contains the number of bytes of write space that are available in the pipe. ═══ 1.82. PSZ ═══ Pointer to a null-terminated string. If you are using C++ **, you may need to use PCSZ. Not defined ═══ 1.83. PTRDRAWDATA ═══ Pointer Draw Data structure. Type PTRDRAWDATA = Record cb: SmallWord; usConfig: SmallWord; usFlag: SmallWord; End; Type PPTRDRAWDATA = ^PTRDRAWDATA; ═══ PTRDRAWDATA Field - cb ═══ cb (USHORT) Length of the data structure. The length of the data structure is equal to 6. ═══ PTRDRAWDATA Field - usConfig ═══ usConfig (USHORT) Display Configuration Number. The display configuration number on which the pointer draw routine should draw. ═══ PTRDRAWDATA Field - usFlag ═══ usFlag (USHORT) Caller flag. Specifies whether this call is made for an application or the Base Video Subsystem (BVS), where: 0 Application 1 BVS ═══ 1.84. PTRDRAWFUNCTION ═══ Pointer Draw Routine access data structure. Type PTRDRAWFUNCTION = Record usReturnCode: SmallWord; pfnDraw: PFn; pchDataSeg: PChar; End; Type PPTRDRAWFUNCTION = ^PTRDRAWFUNCTION; ═══ PTRDRAWFUNCTION Field - usReturnCode ═══ usReturnCode (USHORT) Return Code. ═══ PTRDRAWFUNCTION Field - pfnDraw ═══ pfnDraw (PFN) Pointer to Draw Routine Entry Point (Selector:offset). ═══ PTRDRAWFUNCTION Field - pchDataSeg ═══ pchDataSeg (PCH) Pointer to Draw Routine Data Segment Selector. ═══ 1.85. PVOID ═══ Pointer to data type of any type. Type PVOID = Pointer; ═══ 1.86. QWORD ═══ Quad word structure. Type QWORD = Record Lo: ULong; Hi: ULong; End; Type PQWORD = ^QWORD; ═══ QWORD Field - qw_ulLo ═══ qw_ulLo (ULONG) Low word. ═══ QWORD Field - qw_ulHi ═══ qw_ulHi (ULONG) High word. ═══ 1.87. RATEDELAY ═══ Typematic Rate and Delay data structure. Type RATEDELAY = Record usDelay: SmallWord; usRate: SmallWord; End; Type PRATEDELAY = ^RATEDELAY; ═══ RATEDELAY Field - usDelay ═══ usDelay (USHORT) Typematic delay. Specifies the typematic delay in milliseconds. A value greater than the maximum value defaults to the maximum value. ═══ RATEDELAY Field - usRate ═══ usRate (USHORT) Typematic rate. Specifies the typematic rate in characters per second. A value greater than the maximum value defaults to the maximum value. ═══ 1.88. REQUESTDATA ═══ REQUESTDATA data structure. Type REQUESTDATA = Record Pid: Pid; Data: ULong; End; Type PREQUESTDATA = ^REQUESTDATA; ═══ REQUESTDATA Field - pid ═══ pid (PID) Process identifier of the process that placed the element into the queue. ═══ REQUESTDATA Field - ulData ═══ ulData (ULONG) An event code that is specified by the application. This data is understood by both the thread that is adding the element to the queue (the client thread) and the thread that receives the queue element (server thread). It has no special meaning, and is not altered by the operating system. ═══ 1.89. RESULTCODES ═══ RESULTCODES data structure. Type RESULTCODES = Record codeTerminate: ULong; codeResult: ULong; End; Type PRESULTCODES = ^RESULTCODES; ═══ RESULTCODES Field - codeTerminate ═══ codeTerminate (ULONG) Termination code or process identifier. For asynchronous requests, this field contains the process identifier of the child process. For synchronous requests, this field contains the termination code furnished by the system describing why the child process ended. The values of the termination code are shown in the following list: 0 TC_EXIT Normal exit. 1 TC_HARDERROR Hard-error halt 2 TC_TRAP Trap operation for a 16-bit child process 3 TC_KILLPROCESS Unintercepted DosKillProcess 4 TC_EXCEPTION Exception operation for a 32-bit child process ═══ RESULTCODES Field - codeResult ═══ codeResult (ULONG) Exit code. Result code specified by the terminating synchronous process on its last call to DosExit. ═══ 1.90. RXQUEUE ═══ Receive/Transmit Queue Information data structure. Type RXQUEUE = Record cch: SmallWord; cb: SmallWord; End; Type PRXQUEUE = ^RXQUEUE; ═══ RXQUEUE Field - cch ═══ cch (USHORT) Number of characters in the queue. Binary integer with the number of characters received/to be transmitted in the physical device driver receive/transmit queue. This is a memory buffer between the memory pointed to by the READ/WRITE request packet and the receive/transmit hardware for this COM device. Note: The behavior of data movement between the READ/WRITE request and the receive/transmit queue can change with each release of the physical device driver. Applications should not be dependent on this information. ═══ RXQUEUE Field - cb ═══ cb (USHORT) Size of receive/transmit queue. Binary integer with the size of the physical device driver receive/transmit queue. Applications should be independent of the receive/transmit queue size (fixed or not fixed). The information in this field allows the application to get the size of the receive/transmit queue, but is subject to change. The current size of the receive queue is approximately 1KB. Using this information, the application should avoid device driver receive queue overruns by using an application-to-application block protocol with the system communicating with the application. ═══ 1.91. SEMRECORD ═══ Muxwait semaphore data structure. Type SEMRECORD = Record hsemCur: HSem; ulUser: ULong; End; Type PSEMRECORD = ^SEMRECORD; ═══ SEMRECORD Field - hsemCur ═══ hsemCur (HSEM) Handle of the semaphore. ═══ SEMRECORD Field - ulUser ═══ ulUser (ULONG) User-defined value. ═══ 1.92. SEL ═══ Selector. Not defined ═══ 1.93. SGID ═══ This data type is specific to versions, higher than version 3, of the OS/2 operating system. Value used to hold a screen group ID. #Ifdef _PPC_ Type SGID = WORD; #Else Type SGID = WORD; #endIf ═══ 1.94. SHIFTSTATE ═══ Shift State data structure. Type SHIFTSTATE = Record fsState: SmallWord; fNLS: Byte; End; Type PSHIFTSTATE = ^SHIFTSTATE; ═══ SHIFTSTATE Field - fsState ═══ fsState (USHORT) Shift state flag. High Byte Has the following settings: Bit 15 SysReq key down Bit 14 Caps Lock key down Bit 13 NumLock key down Bit 12 ScrollLock key down Bit 11 Right Alt key down Bit 10 Right Ctrl key down Bit 9 Left Alt key down Bit 8 Left Ctrl key down Low Byte Has the following: Bit 7 Insert on Bit 6 Caps Lock on Bit 5 NumLock on Bit 4 ScrollLock on Bit 3 Either Alt key down Bit 2 Either Ctrl key down Bit 1 Left Shift key down Bit 0 Right Shift key down ═══ SHIFTSTATE Field - fNLS ═══ fNLS (BYTE) NLS shift status flag. ═══ 1.95. SHORT ═══ Signed integer in the range -32 768 through 32 767. ═══ 1.96. STARTDATA ═══ Start session data structure. Type STARTDATA = Record Length: SmallWord; Related: SmallWord; FgBg: SmallWord; TraceOpt: SmallWord; PgmTitle: PChar; PgmName: PChar; PgmInputs: PChar; TermQ: PChar; Environment: PChar; InheritOpt: SmallWord; SessionType: SmallWord; IconFile: PChar; PgmHandle: ULong; PgmControl: SmallWord; InitXPos: SmallWord; InitYPos: SmallWord; InitXSize: SmallWord; InitYSize: SmallWord; Reserved: SmallWord; ObjectBuffer: PChar; ObjectBuffLen: ULong; End; Not defined ═══ STARTDATA Field - Length ═══ Length (USHORT) The length of the data structure, in bytes, including Length itself. A length of at least 32 bytes must be used to start a DOS session with the session type specified. A length greater than 32 is not allowed if the Session Manager detects that the Presentation Manager is not present. When a Length of 24 or 30 bytes is specified, DosStartSession initializes the missing parameters to 0. This allows the Shell to provide values for the missing information, based on the installation file entry for the program being started. Specify a Length of 30 bytes to use the environment and inheritance features of the system. Specify a Length of 50 bytes to specify the type of session to start, and to define data for windows. A Length of 60 bytes allows you to use all of the functions provided by DosStartSession. ═══ STARTDATA Field - Related ═══ Related (USHORT) An indicator which specifies whether the session created is related to the calling session. The values of this field are as follows: 0 SSF_RELATED_INDEPENDENT New session is an independent session (not related). 1 SSF_RELATED_CHILD New session is a child session (related). An independent session cannot be controlled by the calling program. It may not be specified as the target of DosSelectSession, DosSetSession, or DosStopSession. The TermQ field is ignored for independent sessions. Refer to "Parent/Child Relationship" in the Remarks section in DosStartSession for additional information about related sessions. ═══ STARTDATA Field - FgBg ═══ FgBg (USHORT) An indicator which specifies whether the new session should be started in the foreground or background. If a windowed session is started in the foreground, the new session will be given the window focus. The values of this field are described in the following list: 0 SSF_FGBG_FORE Start session in foreground 1 SSF_FGBG_BACK Start session in background ═══ STARTDATA Field - TraceOpt ═══ TraceOpt (USHORT) An indicator which specifies whether the program started in the new session should be executed under conditions for tracing. The values of this field are described in the following list: 0 SSF_TRACEOPT_NONE No trace. 1 SSF_TRACEOPT_TRACE Trace with no notification of descendants. 2 SSF_TRACEOPT_TRACEALL Trace all descendant sessions. Related equals 1 and a termination queue must be supplied when this value is specified. Refer to "Debugger Considerations" in the Remarks section in DosStartSession for additional information about a tracing descendant sessions. ═══ STARTDATA Field - PgmTitle ═══ PgmTitle (PSZ) Address of an ASCIIZ string that contains the program title. The string can be up to 61 bytes long, including the terminating byte of 0. If the address specified is 0, or if the ASCIIZ string is null, then the initial title is PgmName minus any leading drive and path information. ═══ STARTDATA Field - PgmName ═══ PgmName (PSZ) The address of an ASCIIZ string that contains the file specification of the program to be loaded. If the address specified is 0, or if the ASCIIZ string is null, then the program identified by the PgmHandle is started in the new session. Refer to "PgmName and PgmInputs Considerations" in the Remarks section in DosStartSession for additional information about a 0 PgmName address. ═══ STARTDATA Field - PgmInputs ═══ PgmInputs (PBYTE) Either 0 or the address of an ASCIIZ string that contains the input arguments to be passed to the program. ═══ STARTDATA Field - TermQ ═══ TermQ (PBYTE) Either 0 or the address of an ASCIIZ string that contains the file specification of a system queue. This field is ignored when Related is set to SSF_RELATED_INDEPENDENT. Refer to "Parent/Child Termination Considerations" in the Remarks section in DosStartSession for additional information about the TermQ field. For more information about system queues, refer to DosCreateQueue. ═══ STARTDATA Field - Environment ═══ Environment (PBYTE) The address of an environment string to be passed to the program started in the new session. This parameter may be used for independent or related DosStartSession functions. When the Environment field is 0, the program in the new session inherits the environment of the Shell if the InheritOpt field is equal to SSF_INHERTOPT_SHELL, or the environment of the program issuing DosStartSession if the InheritOpt field is equal to SSF_INHERTOPT_PARENT. The DOS session always processes the AUTOEXEC.BAT file on the DOS startup drive. You can define DOS environment variables in the AUTOEXEC.BAT file or in this parameter. See the pEnv parameter in DosExecPgm for additional information about environment settings. ═══ STARTDATA Field - InheritOpt ═══ InheritOpt (USHORT) Specifies whether the program started in the new session should inherit the calling program's environment and open file handles. The values of this field are described in the following list: 0 SSF_INHERTOPT_SHELL Inherit the Shell's environment. 1 SSF_INHERTOPT_PARENT Inherit the environment of the program issuing the DosStartSession call. The InheritOpt field may be used for independent or related DosStartSession functions. Therefore, a DosStartSession function with the InheritOpt field equal to SSF_INHERTOPT_PARENT is equivalent to DosExecPgm, except that the new program does not inherit the priority of the parent process, or the keyboard and video characteristics associated with the parent session. Also, a parent process/child process relationship is not established. Refer to "Parent/Child Relationship" in the Remarks section in DosStartSession for additional information about related sessions. The InheritOpt field for a DOS session is different than the InheritOpt field for a non-DOS session. An InheritOpt value of SSF_INHERTOPT_PARENT for a DOS session only inherits the parent's current drive and path. It does not inherit the parent's environment. ═══ STARTDATA Field - SessionType ═══ SessionType (USHORT) The type of session that should be created for this program. The values of this field are shown in the list below: 0 SSF_TYPE_DEFAULT Use the PgmHandle data, or allow the Shell to establish the session type. 1 SSF_TYPE_FULLSCREEN Start the program in a full-screen session. 2 SSF_TYPE_WINDOWABLEVIO Start the program in a windowed session for programs using the Base Video Subsystem. 3 SSF_TYPE_PM. Start the program in a windowed session for programs using the Presentation Manager services (including AVIO calls). 4 SSF_TYPE_VDM Start the program in a full-screen DOS session. 7 SSF_TYPE_WINDOWEDVDM Start the program in a windowed DOS session. ═══ STARTDATA Field - IconFile ═══ IconFile (PSZ) Either 0 or the address of an ASCIIZ string that contains the file specification of an icon definition. If you do not provide an icon file name with DosStartSession, the system looks for an associated icon file (with a file extension of .ICO, or an extended attribute of .ICON). The system provides a default icon if an icon file name is not provided with DosStartSession. ═══ STARTDATA Field - PgmHandle ═══ PgmHandle (ULONG) Either 0 or the program handle. The program handle identifies the program in the installation file to be started, the program title, the session type, and the initial window size and position. However, information may be specified with DosStartSession to override the information in the installation file for this invocation of the program. DosStartSession does not support program groups. Refer to "Program Handle Considerations" in the Remarks secion of DosStartSession for additional information about program handles. ═══ STARTDATA Field - PgmControl ═══ PgmControl (USHORT) An indicator which specifies the initial state for a windowed application. This field is ignored for full-screen sessions. The bits in this field have the following values: Bit Description 15 SSF_CONTROL_SETPOS (0x8000) Use specified size and position 4-14 Reserved 3 SSF_CONTROL_NOAUTOCLOSE (0x0008) No Auto Close 2 SSF_CONTROL_MINIMIZE (0x0004) Minimize 1 SSF_CONTROL_MAXIMIZE (0x0002) Maximize 0 SSF_CONTROL_INVISIBLE (0x0001) Invisible 0 SSF_CONTROL_VISIBLE (0x0000) Visible Note: The "No Auto Close" bit is used only for VIO Windowable applications, and is ignored for all other types of applications. ═══ STARTDATA Field - InitXPos ═══ InitXPos (USHORT) The initial x-coordinate, in pels, for the initial session window. Coordinates (0,0) indicate the bottom left corner of the display. This field is ignored for full-screen sessions. ═══ STARTDATA Field - InitYPos ═══ InitYPos (USHORT) The initial y-coordinate, in pels, for the initial session window. Coordinates (0,0) indicate the bottom left corner of the display. This field is ignored for full-screen sessions. ═══ STARTDATA Field - InitXSize ═══ InitXSize (USHORT) The initial x extent, in pels, for the initial session window. This field is ignored for full-screen sessions. ═══ STARTDATA Field - InitYSize ═══ InitYSize (USHORT) The initial y extent, in pels, for the initial session window. This field is ignored for full-screen sessions. ═══ STARTDATA Field - Reserved ═══ Reserved (USHORT) Reserved; must be zero. ═══ STARTDATA Field - ObjectBuffer ═══ ObjectBuffer (PSZ) Buffer in which the name of the object that contributed to the failure of DosExecPgm is returned. The name of the object is usually the name of a dynamic link library that could not be loaded. DosStartSession calls DosExecPgm to start all full-screen, VIO windowed, and Presentation Manager programs. ═══ STARTDATA Field - ObjectBuffLen ═══ ObjectBuffLen (ULONG) The length, in bytes, of the buffer pointed to by ObjectBuffer. ═══ 1.97. STATUSDATA ═══ Status data structure. Type STATUSDATA = Record Length: SmallWord; SelectInd: SmallWord; BondInd: SmallWord; End; Type PSTATUSDATA = ^STATUSDATA; ═══ STATUSDATA Field - Length ═══ Length (USHORT) The length of the data structure, in bytes, including Length itself. ═══ STATUSDATA Field - SelectInd ═══ SelectInd (USHORT) An indicator that specifies whether the target session should be flagged as selectable or non-selectable. Possible values are shown in the following list: 0 SET_SESSION_UNCHANGED Leaves the current setting unchanged. 1 SET_SESSION_SELECTABLE Makes the target session selectable. 2 SET_SESSION_NON_SELECTABLE Makes the target session non-selectable. A non-selectable session is not selectable from the Shell switch list, nor can the user jump to it via the system hot key. The operator may continue to select a non-selectable windowed session by pressing a mouse button within a visible part of the window. ═══ STATUSDATA Field - BondInd ═══ BondInd (USHORT) An indicator that specifies which session to bring to the foreground the next time the parent session is selected. Possible values are shown in the following list: 0 SET_SESSION_UNCHANGED Leaves the current setting unchanged. 1 SET_SESSION_BOND Establishes a bond between the parent session and the child session. The child session is brought to the foreground the next time the parent session is selected. If the child session is selected, the child session is brought to the foreground. 2 SET_SESSION_NO_BOND Specifies bringing the parent session to the foreground the next time the parent session is selected, and bringing the child session to the foreground if the child is selected. Any bond previously established with the child session specified is broken. ═══ 1.98. TRACKLAYOUT ═══ Track layout. Type TRACKLAYOUT = Record bCommand: Byte; usHead: SmallWord; usCylinder: SmallWord; usFirstSector: SmallWord; cSectors: SmallWord; TrackTable: array[0..0] of record usSectorNumber: SmallWord; usSectorSize: SmallWord; End; Type PTRACKLAYOUT = ^TRACKLAYOUT; ═══ TRACKLAYOUT Field - bCommand ═══ bCommand (BYTE) Command information. Bit 0 If clear (0), the track layout contains nonconsecutive sectors or does not start with Sector 1. If set (1), the track layout starts with Sector 1 and contains only consecutive sectors. All other bits are reserved and must be set to 0. ═══ TRACKLAYOUT Field - usHead ═══ usHead (USHORT) Physical head on the device that performs the operation. ═══ TRACKLAYOUT Field - usCylinder ═══ usCylinder (USHORT) Cylinder to be written, read, or verified. ═══ TRACKLAYOUT Field - usFirstSector ═══ usFirstSector (USHORT) Logical sector number within the Track Layout Table that starts the I/O operation. Note that all the sector numbers start with 0. For example the third sector is number 2. ═══ TRACKLAYOUT Field - cSectors ═══ cSectors (USHORT) Number of sectors to read, write, or verify. This value must be less than or equal to the maximum number number of sectors specified in the track table. The IOCtl subfunctions do not step heads or tracks. ═══ TRACKLAYOUT Field - usSectorNumber ═══ usSectorNumber (USHORT) Sector number. There must be one usSectorNumber and usSectorSize in the track layout table for every sector, up to cSectors sectors. ═══ TRACKLAYOUT Field - usSectorSize ═══ usSectorSize (USHORT) Sector size. There must be one usSectorNumber and usSectorSize in the track layout table for every sector, up to cSectors sectors. ═══ 1.99. TIB ═══ Thread Information Block structure. Type TIB = Record Tib_PExchain: Pointer; { Head of exception handler chain } Tib_PStack: Pointer; { Pointer to base of stack } Tib_PStackLimit: Pointer; { Pointer to end of stack } Tib_PTib2: PTib2; { Pointer to system specific TIB } Tib_Version: ULong; { Version number for this TIB structure} Tib_Ordinal: ULong; { Thread ordinal number } End; Type PTIB = ^TIB; A thread is a dispatchable unit of executable code that consists of set of instructions, related CPU register values, and a stack. Every process has at least one thread and can have many threads running at the same time. The application runs when the operating system gives control to a thread in a process. A thread is the basic unit of execution. Information about a thread is kept in a read/write area of the process address space, called the Thread Information Block (TIB). The operating system creates and maintains a TIB for every thread in the system. An application can access the TIB of a specific thread using DosGetInfoBlocks. Note: The fields of this data structure should not be modified unless you are switching stacks, in which case only the tib_pstack and tib_pstacklimit should changed. ═══ TIB Field - tib_pexchain ═══ tib_pexchain (PVOID) Head of exception handler chain. ═══ TIB Field - tib_pstack ═══ tib_pstack (PVOID) Pointer to the base of the stack. ═══ TIB Field - tib_pstacklimit ═══ tib_pstacklimit (PVOID) Pointer to the end of the stack. ═══ TIB Field - tib_ptib2 ═══ tib_ptib2 (PTIB2) Pointer to a system-specific thread information block. ═══ TIB Field - tib_version ═══ tib_version (ULONG) Version number for this Thread Information Block. ═══ TIB Field - tib_ordinal ═══ tib_ordinal (ULONG) Thread ordinal number. ═══ 1.100. TIB2 ═══ System-specific Thread Information Block structure. Type TIB2 = Record Tib2_ulTid: ULong; { Thread I.D. } Tib2_ulPri: ULong; { Thread priority } Tib2_Version: ULong; { Version number for this structure } Tib2_usMCCount: SmallWord; { Must Complete count } Tib2_fMCForceFlag: SmallWord; { Must Complete force flag } End; Type PTIB2 = ^TIB2; { THREAD INFORMATION BLOCK (TIB) } ═══ TIB2 Field - tib2_ultid ═══ tib2_ultid (ULONG) Current thread identifier. ═══ TIB2 Field - tib2_ulpri ═══ tib2_ulpri (ULONG) Current thread priority. The low byte of the low word is a hexadecimal value representing a rank (value 0 to 31) within a priority class. Class values, found in the high byte of the low word, are as follows: 0x01 Idle. 0x02 Regular. 0x03 Time critical. 0x04 Server. ═══ TIB2 Field - tib2_version ═══ tib2_version (ULONG) Version number for this system-specific Thread Information Block. ═══ TIB2 Field - tib2_usMCCount ═══ tib2_usMCCount (USHORT) Must-complete count. ═══ TIB2 Field - tib2_fMCForceFlag ═══ tib2_fMCForceFlag (USHORT) Must-complete force flag. ═══ 1.101. TID ═══ Thread identity. Type TID = LHANDLE; ═══ 1.102. UCHAR ═══ Single-byte unsigned character or unsigned integer in the range 0 through 255. Not defined ═══ 1.103. ULONG ═══ Unsigned integer in the range 0 through 4 294 967 295. Type ULONG = LONGINT; ═══ 1.104. USHORT ═══ Unsigned integer in the range 0 through 65 535. Type USHORT = WORD; ═══ 1.105. VOID ═══ A data area of undefined format. ═══ 1.106. VOLUMELABEL ═══ Volume label structure. Type VOLUMELABEL = Record PFsInfo = ^FsInfo; FsInfo = record fdateCreation: FDate; ftimeCreation: FTime; vol: VolumeLabel; End; Type PVOLUMELABEL = ^VOLUMELABEL; ═══ VOLUMELABEL Field - cch ═══ cch (BYTE) Length of the volume label, not including the null. ═══ VOLUMELABEL Field - szVolLabel[12] ═══ szVolLabel[12] (CHAR) Volume label. This is an ASCIIZ string. ═══ 2. Errors ═══ For errors 0 through 499, the following shows the numerical value of an error, its symbolic name, and a brief description of the error. For errors 500 and above, the following shows the numerical value of an error and its symbolic name. Select a range of Control Program error return codes: 0 to 99 100 to 199 200 to 299 300 to 399 400 to 499 500 to 599 600 to 1999 2000 to 59999 60000 to 65079 ═══ Errors 0 to 99 ═══ The following shows the numerical value of an error, its symbolic name, and a brief description of the error. 0 NO_ERROR No error occurred. 1 ERROR_INVALID_FUNCTION Invalid function number. 2 ERROR_FILE_NOT_FOUND File not found. 3 ERROR_PATH_NOT_FOUND Path not found. 4 ERROR_TOO_MANY_OPEN_FILES Too many open files (no handles left). 5 ERROR_ACCESS_DENIED Access denied. 6 ERROR_INVALID_HANDLE Invalid handle. 7 ERROR_ARENA_TRASHED Memory control blocks destroyed. 8 ERROR_NOT_ENOUGH_MEMORY Insufficient memory. 9 ERROR_INVALID_BLOCK Invalid memory-block address. 10 ERROR_BAD_ENVIRONMENT Invalid environment. 11 ERROR_BAD_FORMAT Invalid format. 12 ERROR_INVALID_ACCESS Invalid access code. 13 ERROR_INVALID_DATA Invalid data. 14 Reserved. 15 ERROR_INVALID_DRIVE Invalid drive specified. 16 ERROR_CURRENT_DIRECTORY Attempting to remove current directory. 17 ERROR_NOT_SAME_DEVICE Not same device. 18 ERROR_NO_MORE_FILES No more files. 19 ERROR_WRITE_PROTECT Attempt to write on write-protected diskette. 20 ERROR_BAD_UNIT Unknown unit. 21 ERROR_NOT_READY Drive not ready. 22 ERROR_BAD_COMMAND Unknown command. 23 ERROR_CRC Data error - cyclic redundancy check. 24 ERROR_BAD_LENGTH Invalid request structure length. 25 ERROR_SEEK Seek error. 26 ERROR_NOT_DOS_DISK Unknown media type. 27 ERROR_SECTOR_NOT_FOUND Sector not found. 28 ERROR_OUT_OF_PAPER Printer is out of paper. 29 ERROR_WRITE FAULT Write fault. 30 ERROR_READ_FAULT Read fault. 31 ERROR_GEN_FAILURE General failure. For DosGetShrSeg and DosGetNamedSharedMem, indicates that a segment's maximum reference count of 65535 has been exceeded. 32 ERROR_SHARING_VIOLATION Sharing violation. 33 ERROR_LOCK_VIOLATION Lock violation. 34 ERROR_WRONG_DISK Invalid disk change. 35 ERROR_FCB_UNAVAILABLE FCB unavailable. 36 ERROR_SHARING_BUFFER_EXCEEDED Sharing buffer overflow. 37 ERROR_CODE_PAGE_MISMATCHED Code page does not match. 38 ERROR_HANDLE_EOF End of file reached. 39 ERROR_HANDLE_DISK_FULL Disk is full. 40-49 Reserved. 50 ERROR_NOT_SUPPORTED Network request not supported. 51 ERROR_REM_NOT_LIST Remote network node is not online. 52 ERROR_DUP_NAME Duplicate file name in network. 53 ERROR_BAD_NETPATH Network path not found. 54 ERROR_NETWORK_BUSY Network is busy. 55 ERROR_DEV_NOT_EXIST Device is not installed in network. 56 ERROR_TOO_MANY_CMDS Network command limit reached. 57 ERROR_ADAP_HDW_ERR Network adapter hardware error. 58 ERROR_BAD_NET_RESP Incorrect response in network. 59 ERROR_UNEXP_NET_ERR Unexpected error in network. 60 ERROR_BAD_REM_ADAP Remote network adapter error. 61 ERROR_PRINTQ_FULL Network printer queue is full. 62 ERROR_NO_SPOOL_SPACE No space in print spool file. 63 ERROR_PRINT_CANCELLED Print spool file deleted. 64 ERROR_NETNAME_DELETED Network name deleted. 65 ERROR_NETWORK_ACCESS_DENIED Access to network denied. 66 ERROR_BAD_DEV_TYPE Device type invalid for network. 67 ERROR_BAD_NET_NAME Network name not found. 68 ERROR_TOO_MANY_NAMES Network name limit exceeded. 69 ERROR_TOO_MANY_SESS Network session limit exceeded. 70 ERROR_SHARING_PAUSED Temporary pause in network. 71 ERROR_REQ_NOT_ACCEP Network request denied. 72 ERROR_REDIR_PAUSED Pause in network print disk redirection. 73 ERROR_SBCS_ATT_WRITE_PROT Attempted write on protected disk. 74 ERROR_SBCS_GENERAL_FAILURE General failure, single-byte character set. 75 ERROR_XGA_OUT_MEMORY XGA is out of memory. 76-79 Reserved. 80 ERROR_FILE_EXISTS File exists. 81 ERROR_DUP_FCB Reserved. 82 ERROR_CANNOT_MAKE Cannot make directory entry. 83 ERROR_FAIL_I24 Failure on INT 24. 84 ERROR_OUT_OF_STRUCTURES Too many redirections. 85 ERROR_ALREADY_ASSIGNED Duplicate redirection. 86 ERROR_INVALID_PASSWORD Invalid password. 87 ERROR_INVALID_PARAMETER Invalid parameter. 88 ERROR_NET_WRITE_FAULT Network device fault. 89 ERROR_NO_PROC_SLOTS No process slots available. 90 ERROR_NOT_FROZEN System error. 90 ERROR_SYS_COMP_NOT_LOADED System error. 91 ERR_TSTOVFL Timer service table overflow. 92 ERR_TSTDUP Timer service table duplicate. 93 ERROR_NO_ITEMS No items to work on. 95 ERROR_INTERRUPT Interrupted system call. 99 ERROR_DEVICE_IN_USE Device in use. ═══ Errors 100 to 199 ═══ The following shows the numerical value of an error, its symbolic name, and a brief description of the error. 100 ERROR_TOO_MANY_SEMAPHORES User/system open semaphore limit reached. 101 ERROR_EXCL_SEM_ALREADY_OWNED Exclusive semaphore already owned. 102 ERROR_SEM_IS_SET DosCloseSem found semaphore set. 103 ERROR_TOO_MANY_SEM_REQUESTS Too many exclusive semaphore requests. 104 ERROR_INVALID_AT_INTERRUPT_TIME Operation invalid at interrupt time. 105 ERROR_SEM_OWNER_DIED Previous semaphore owner terminated without freeing semaphore. 106 ERROR_SEM_USER_LIMIT Semaphore limit exceeded. 107 ERROR_DISK_CHANGE Insert drive B disk into drive A. 108 ERROR_DRIVE_LOCKED Drive locked by another process. 109 ERROR_BROKEN_PIPE Write on pipe with no reader. 110 ERROR_OPEN_FAILED Open/create failed due to explicit fail command. 111 ERROR_BUFFER_OVERFLOW Buffer passed to system call too small to hold return data. 112 ERROR_DISK_FULL Not enough space on the disk. 113 ERROR_NO_MORE_SEARCH_HANDLES Cannot allocate another search structure and handle. 114 ERROR_INVALID_TARGET_HANDLE Target handle in DosDupHandle invalid. 115 ERROR_PROTECTION_VIOLATION Invalid user virtual address. 116 ERROR_VIOKBD_REQUEST Error on display write or keyboard read. 117 ERROR_INVALID_CATEGORY Category for DevIOCtl not defined. 118 ERROR_INVALID_VERIFY_SWITCH Invalid value passed for verify flag. 119 ERROR_BAD_DRIVER_LEVEL Level four driver not found. 120 ERROR_CALL_NOT_IMPLEMENTED Invalid function called. 121 ERROR_SEM_TIMEOUT Time-out occurred from semaphore API function. 122 ERROR_INSUFFICIENT_BUFFER Data buffer too small. 123 ERROR_INVALID_NAME Illegal character or invalid file-system name. 124 ERROR_INVALID_LEVEL Non-implemented level for information retrieval or setting. 125 ERROR_NO_VOLUME_LABEL No volume label found with DosQueryFSInfo function. 126 ERROR_MOD_NOT_FOUND Module handle not found with DosQueryProcAddr(), DosQueryModAddr(). 127 ERROR_PROC_NOT_FOUND Procedure address not found with DosQueryProcAddr(). 128 ERROR_WAIT_NO_CHILDREN DosWaitChild finds no children. 129 ERROR_CHILD_NOT_COMPLETE DosWaitChild children not terminated. 130 ERROR_DIRECT_ACCESS_HANDLE Handle operation invalid for direct disk-access handles. 131 ERROR_NEGATIVE_SEEK Attempting seek to negative offset. 132 ERROR_SEEK_ON_DEVICE Application trying to seek on device or pipe. 133 ERROR_IS_JOIN_TARGET Drive has previously joined drives. 134 ERROR_IS_JOINED Drive is already joined. 135 ERROR_IS_SUBSTED Drive is already substituted. 136 ERROR_NOT_JOINED Cannot delete drive that is not joined. 137 ERROR_NOT_SUBSTED Cannot delete drive that is not substituted. 138 ERROR_JOIN_TO_JOIN Cannot join to a joined drive. 139 ERROR_SUBST_TO_SUBST Cannot substitute to a substituted drive. 140 ERROR_JOIN_TO_SUBST Cannot join to a substituted drive. 141 ERROR_SUBST_TO_JOIN Cannot substitute to a joined drive. 142 ERROR_BUSY_DRIVE Specified drive is busy. 143 ERROR_SAME_DRIVE Cannot join or substitute a drive to a directory on the same drive. 144 ERROR_DIR_NOT_ROOT Directory must be a subdirectory of the root. 145 ERROR_DIR_NOT_EMPTY Directory must be empty to use join command. 146 ERROR_IS_SUBST_PATH Path specified is being used in a substitute. 147 ERROR_IS_JOIN_PATH Path specified is being used in a join. 148 ERROR_PATH_BUSY Path specified is being used by another process. 149 ERROR_IS_SUBST_TARGET Cannot join or substitute a drive that has a directory that is the target of a previous substitute. 150 ERROR_SYSTEM_TRACE System trace error. 151 ERROR_INVALID_EVENT_COUNT DosWaitMuxWaitSem errors. 152 ERROR_TOO_MANY_MUXWAITERS System limit of 100 entries reached. 153 ERROR_INVALID_LIST_FORMAT Invalid list format. 154 ERROR_LABEL_TOO_LONG Volume label too big. 155 ERROR_TOO_MANY_TCBS Cannot create another TCB. 156 ERROR_SIGNAL_REFUSED Signal refused. 157 ERROR_DISCARDED Segment is discarded. 158 ERROR_NOT_LOCKED Segment is not locked. 159 ERROR_BAD_THREADID_ADDR Invalid thread-identity address. 160 ERROR_BAD_ARGUMENTS Invalid environment pointer. 161 ERROR_BAD_PATHNAME Invalid path name passed to exec. 162 ERROR_SIGNAL_PENDING Signal already pending. 163 ERROR_UNCERTAIN_MEDIA Error with INT 24 mapping. 164 ERROR_MAX_THRDS_REACHED No more process slots. 165 ERROR_MONITORS_NOT_SUPPORTED Error with INT 24 mapping. 166 ERROR_UNC_DRIVER_NOT_INSTALLED Default redirection return code. 167 ERROR_LOCK_FAILED Locking failed. 168 ERROR_SWAPIO_FAILED Swap I/O failed. 169 ERROR_SWAPIN_FAILED Swap in failed. 170 ERROR_BUSY Segment is busy. 171-172 Reserved. 173 ERROR_CANCEL_VIOLATION A lock request is not outstanding for the specified file range, or the range length is zero. 174 ERROR_ATOMIC_LOCK_NOT_SUPPORTED The file-system driver (FSD) does not support atomic lock operations. Versions of OS/2 prior to version 2.00 do not support atomic lock operations. 175 ERROR_READ_LOCKS_NOT_SUPPORTED The file system driver (FSD) does not support shared read locks. 176-179 Reserved. 180 ERROR_INVALID_SEGMENT_NUMBER Invalid segment number. 181 ERROR_INVALID_CALLGATE Invalid call gate. 182 ERROR_INVALID_ORDINAL Invalid ordinal. 183 ERROR_ALREADY_EXISTS Shared segment already exists. 184 ERROR_NO_CHILD_PROCESS No child process to wait for. 185 ERROR_CHILD_ALIVE_NOWAIT NoWait specified and child alive. 186 ERROR_INVALID_FLAG_NUMBER Invalid flag number. 187 ERROR_SEM_NOT_FOUND Semaphore does not exist. 188 ERROR_INVALID_STARTING_CODESEG Invalid starting code segment, incorrect END (label) directive. 189 ERROR_INVALID_STACKSEG Invalid stack segment. 190 ERROR_INVALID_MODULETYPE Invalid module type - dynamic-link library file cannot be used as an application. Application cannot be used as a dynamic-link library. 191 ERROR_INVALID_EXE_SIGNATURE Invalid EXE signature - file is a DOS mode program or an improper program. 192 ERROR_EXE_MARKED_INVALID EXE marked invalid - link detected errors when the application was created. 193 ERROR_BAD_EXE_FORMAT Invalid EXE format - file is a DOS mode program or an improper program. 194 ERROR_ITERATED_DATA_EXCEEDS_64k Iterated data exceeds 64KB - there is more than 64KB of data in one of the segments of the file. 195 ERROR_INVALID_MINALLOCSIZE Invalid minimum allocation size - the size is specified to be less than the size of the segment data in the file. 196 ERROR_DYNLINK_FROM_INVALID_RING Dynamic link from invalid privilege level - privilege level 2 routine cannot link to dynamic-link libraries. 197 ERROR_IOPL_NOT_ENABLED IOPL not enabled - IOPL set to NO in CONFIG.SYS. 198 ERROR_INVALID_SEGDPL Invalid segment descriptor privilege level - can only have privilege levels of 2 and 3. 199 ERROR_AUTODATASEG_EXCEEDS_64k Automatic data segment exceeds 64KB. ═══ Errors 200 to 299 ═══ The following shows the numerical value of an error, its symbolic name, and a brief description of the error. 200 ERROR_RING2SEG_MUST_BE_MOVABLE Privilege level 2 segment must be movable. 201 ERROR_RELOC_CHAIN_XEEDS_SEGLIM Relocation chain exceeds segment limit. 202 ERROR_INFLOOP_IN_RELOC_CHAIN Infinite loop in relocation chain segment. 203 ERROR_ENVVAR_NOT_FOUND Environment variable not found. 204 ERROR_NOT_CURRENT_CTRY Not current country. 205 ERROR_NO_SIGNAL_SENT No signal sent - no process in the command subtree has a signal handler. 206 ERROR_FILENAME_EXCED_RANGE File name or extension is greater than 8.3 characters. 207 ERROR_RING2_STACK_IN_USE Privilege level 2 stack is in use. 208 ERROR_META_EXPANSION_TOO_LONG Meta (global) expansion is too long. 209 ERROR_INVALID_SIGNAL_NUMBER Invalid signal number. 210 ERROR_THREAD_1_INACTIVE Inactive thread. 211 ERROR_INFO_NOT_AVAIL File system information is not available for this file. 212 ERROR_LOCKED Locked error. 213 ERROR_BAD_DYNALINK Attempted to execute a non-family API in DOS mode. 214 ERROR_TOO_MANY_MODULES Too many modules. 215 ERROR_NESTING_NOT_ALLOWED Nesting is not allowed. 216 ERROR_CANNOT_SHRINK System error. 217 ERROR_ZOMBIE_PROCESS Zombie process. 218 ERROR_STACK_IN_HIGH_MEMORY Stack is in high memory. 219 ERROR_INVALID_EXITROUTINE_RING Invalid exit routine ring. 220 ERROR_GETBUF_FAILED Get buffer failed. 221 ERROR_FLUSHBUF_FAILED Flush buffer failed. 222 ERROR_TRANSFER_TOO_LONG Transfer is too long. 223 ERROR_FORCENOSWAP_FAILED System error. 224 ERROR_SMG_NO_TARGET_WINDOW The application window was created without the FCF_TASKLIST style, or the application window not yet been created or has already been destroyed. 228 ERROR_NO_CHILDREN No child process. 229 ERROR_INVALID_SCREEN_GROUP Invalid session. 230 ERROR_BAD_PIPE Non-existent pipe or invalid operation. 231 ERROR_PIPE_BUSY Pipe is busy. 232 ERROR_NO_DATA No data available on non-blocking read. 233 ERROR_PIPE_NOT_CONNECTED Pipe was disconnected by server. 234 ERROR_MORE_DATA More data is available. 240 ERROR_VC_DISCONNECTED Session was dropped due to errors. 250 ERROR_CIRCULARITY_REQUESTED Renaming a directory that would cause a circularity problem. 251 ERROR_DIRECTORY_IN_CDS Renaming a directory that is in use. 252 ERROR_INVALID_FSD_NAME Trying to access nonexistent FSD. 253 ERROR_INVALID_PATH Invalid pseudo device. 254 ERROR_INVALID_EA_NAME Invalid character in name, or invalid cbName. 255 ERROR_EA_LIST_INCONSISTENT List does not match its size, or there are invalid EAs in the list. 256 ERROR_EA_LIST_TOO_LONG FEAList is longer than 64K-1 bytes. 257 ERROR_NO_META_MATCH String does not match expression. 258 ERROR_FINDNOTIFY_TIMEOUT System error. 259 ERROR_NO_MORE_ITEMS DosQueryFSAttach ordinal query. 260 ERROR_SEARCH_STRUC_REUSED DOS mode findfirst/next search structure reused. 261 ERROR_CHAR_NOT_FOUND Character not found. 262 ERROR_TOO_MUCH_STACK Stack request exceeds system limit. 263 ERROR_INVALID_ATTR Invalid attribute. 264 ERROR_INVALID_STARTING_RING Invalid starting ring. 265 ERROR_INVALID_DLL_INIT_RING Invalid DLL INIT ring. 266 ERROR_CANNOT_COPY Cannot copy. 267 ERROR_DIRECTORY Used by DOSCOPY in doscall1. 268 ERROR_OPLOCKED_FILE Oplocked file. 269 ERROR_OPLOCK_THREAD_EXISTS Oplock thread exists. 270 ERROR_VOLUME_CHANGED Volume changed. 271 ERROR_FINDNOTIFY_HANDLE_IN_USE Handle in use. 272 ERROR_FINDNOTIFY_HANDLE_CLOSED Handle closed. 273 ERROR_NOTIFY_OBJECT_REMOVED Object removed. 274 ERROR_ALREADY_SHUTDOWN System is already shut down. 275 ERROR_EAS_DIDNT_FIT Buffer is not big enough to hold the EAs. 276 ERROR_EA_FILE_CORRUPT EA file has been damaged. 277 ERROR_EA_TABLE_FULL EA table is full. 278 ERROR_INVALID_EA_HANDLE EA handle is invalid. 279 ERROR_NO_CLUSTER No cluster. 280 ERROR_CREATE_EA_FILE Cannot create the EA file. 281 ERROR_CANNOT_OPEN_EA_FILE Cannot open the EA file. 282 ERROR_EAS_NOT_SUPPORTED Destination file system does not support EAs. 283 ERROR_NEED_EAS_FOUND Destination file system does not support EAs, and the source file's EAs contain a need EA. 284 ERROR_DUPLICATE_HANDLE The handle already exists. 285 ERROR_DUPLICATE_NAME The name already exists. 286 ERROR_EMPTY_MUXWAIT The list of semaphores in a muxwait semaphore is empty. 287 ERROR_MUTEX_OWNED The calling thread owns one or more of the mutex semaphores in the list. 288 ERROR_NOT_OWNER Caller does not own the semaphore. 289 ERROR_PARAM_TOO_SMALL Parameter is not large enough to contain all of the semaphore records in the muxwait semaphore. 290 ERROR_TOO_MANY_HANDLES Limit reached for number of handles. 291 ERROR_TOO_MANY_OPENS There are too many files or semaphores open. 292 ERROR_WRONG_TYPE Attempted to create wrong type of semaphore. 293 ERROR_UNUSED_CODE Code is not used. 294 ERROR_THREAD_NOT_TERMINATED Thread has not terminated. 295 ERROR_INIT_ROUTINE_FAILED Initialization routine failed. 296 ERROR_MODULE_IN_USE Module is in use. 297 ERROR_NOT_ENOUGH_WATCHPOINTS There are not enough watchpoints. 298 ERROR_TOO_MANY_POSTS Post count limit was reached for an event semaphore. 299 ERROR_ALREADY_POSTED Event semaphore is already posted. ═══ Errors 300 to 399 ═══ The following shows the numerical value of an error, its symbolic name, and a brief description of the error. 300 ERROR_ALREADY_RESET Event semaphore is already reset. 301 ERROR_SEM_BUSY Semaphore is busy. 302 Reserved 303 ERROR_INVALID_PROCID Invalid process identity. 304 ERROR_INVALID_PDELTA Invalid priority delta. 305 ERROR_NOT_DESCENDANT Not descendant. 306 ERROR_NOT_SESSION_MANAGER Requestor not session manager. 307 ERROR_INVALID_PCLASS Invalid P class. 308 ERROR_INVALID_SCOPE Invalid scope. 309 ERROR_INVALID_THREADID Invalid thread identity. 310 ERROR_DOSSUB_SHRINK Cannot shrink segment - DosSubSetMem. 311 ERROR_DOSSUB_NOMEM No memory to satisfy request - DosSubAllocMem. 312 ERROR_DOSSUB_OVERLAP Overlap of the specified block with a block of allocated memory - DosSubFreeMem. 313 ERROR_DOSSUB_BADSIZE Invalid size parameter - DosSubAllocMem or DosSubFreeMem. 314 ERROR_DOSSUB_BADFLAG Invalid flag parameter - DosSubSetMem. 315 ERROR_DOSSUB_BADSELECTOR Invalid segment selector. 316 ERROR_MR_MSG_TOO_LONG Message too long for buffer. 317 ERROR_MR_MID_NOT_FOUND Message identity number not found. 318 ERROR_MR_UN_ACC_MSGF Unable to access message file. 319 ERROR_MR_INV_MSGF_FORMAT Invalid message file format. 320 ERROR_MR_INV_IVCOUNT Invalid insertion variable count. 321 ERROR_MR_UN_PERFORM Unable to perform function. 322 ERROR_TS_WAKEUP Unable to wake up. 323 ERROR_TS_SEMHANDLE Invalid system semaphore. 324 ERROR_TS_NOTIMER No timers available. 326 ERROR_TS_HANDLE Invalid timer handle. 327 ERROR_TS_DATETIME Date or time invalid. 328 ERROR_SYS_INTERNAL Internal system error. 329 ERROR_QUE_CURRENT_NAME Current queue name does not exist. 330 ERROR_QUE_PROC_NOT_OWNED Current process does not own queue. 331 ERROR_QUE_PROC_OWNED Current process owns queue. 332 ERROR_QUE_DUPLICATE Duplicate queue name. 333 ERROR_QUE_ELEMENT_NOT_EXIST Queue element does not exist. 334 ERROR_QUE_NO_MEMORY Inadequate queue memory. For DosOpenQueue, DosCreateQueue, and DosWriteQueue, the following applies: These calls use a system-wide pool of memory. Every DosOpenQueue and DosCreateQueue uses up 34 bytes of memory, which is freed on close. Every DosWriteQueue uses 24 bytes of memory, which is freed on read. If too many elements are written to queues, further opens, creates, reads, or writes fail with this error code. 335 ERROR_QUE_INVALID_NAME Invalid queue name. 336 ERROR_QUE_INVALID_PRIORITY Invalid queue priority parameter. 337 ERROR_QUE_INVALID_HANDLE Invalid queue handle. 338 ERROR_QUE_LINK_NOT_FOUND Queue link not found. 339 ERROR_QUE_MEMORY_ERROR Queue memory error. 340 ERROR_QUE_PREV_AT_END Previous queue element was at end of queue. 341 ERROR_QUE_PROC_NO_ACCESS Process does not have access to queues. 342 ERROR_QUE_EMPTY Queue is empty. 343 ERROR_QUE_NAME_NOT_EXIST Queue name does not exist. 344 ERROR_QUE_NOT_INITIALIZED Queues not initialized. 345 ERROR_QUE_UNABLE_TO_ACCESS Unable to access queues. 346 ERROR_QUE_UNABLE_TO_ADD Unable to add new queue. 347 ERROR_QUE_UNABLE_TO_INIT Unable to initialize queues. 349 ERROR_VIO_INVALID_MASK Invalid function replaced. 350 ERROR_VIO_PTR Invalid pointer to parameter. 351 ERROR_VIO_APTR Invalid pointer to attribute. 352 ERROR_VIO_RPTR Invalid pointer to row. 353 ERROR_VIO_CPTR Invalid pointer to column. 354 ERROR_VIO_LPTR Invalid pointer to length. 355 ERROR_VIO_MODE Unsupported screen mode. 356 ERROR_VIO_WIDTH Invalid cursor width value. 357 ERROR_VIO_ATTR Invalid cursor attribute value. 358 ERROR_VIO_ROW Invalid row value. 359 ERROR_VIO_COL Invalid column value. 360 ERROR_VIO_TOPROW Invalid TopRow value. 361 ERROR_VIO_BOTROW Invalid BotRow value. 362 ERROR_VIO_RIGHTCOL Invalid right column value. 363 ERROR_VIO_LEFTCOL Invalid left column value. 364 ERROR_SCS_CALL Call issued by other than session manager. 365 ERROR_SCS_VALUE Value is not for save or restore. 366 ERROR_VIO_WAIT_FLAG Invalid wait flag setting. 367 ERROR_VIO_UNLOCK Screen not previously locked. 368 ERROR_SGS_NOT_SESSION_MGR Caller not session manager. 369 ERROR_SMG_INVALID_SGID Invalid session identity. 369 ERROR_SMG_INVALID_SESSION_ID Invalid session ID. 370 ERROR_SMG_NOSG No sessions available. 370 ERROR_SMG_NO_SESSIONS No sessions available. 371 ERROR_SMG_GRP_NOT_FOUND Session not found. 371 ERROR_SMG_SESSION_NOT_FOUND Session not found. 372 ERROR_SMG_SET_TITLE Title sent by shell or parent cannot be changed. 373 ERROR_KBD_PARAMETER Invalid parameter to keyboard. 374 ERROR_KBD_NO_DEVICE No device. 375 ERROR_KBD_INVALID_IOWAIT Invalid I/O wait specified. 376 ERROR_KBD_INVALID_LENGTH Invalid length for keyboard. 377 ERROR_KBD_INVALID_ECHO_MASK Invalid echo mode mask. 378 ERROR_KBD_INVALID_INPUT_MASK Invalid input mode mask. 379 ERROR_MON_INVALID_PARMS Invalid parameters to DosMon. 380 ERROR_MON_INVALID_DEVNAME Invalid device name string. 381 ERROR_MON_INVALID_HANDLE Invalid device handle. 382 ERROR_MON_BUFFER_TOO_SMALL Buffer too small. 383 ERROR_MON_BUFFER_EMPTY Buffer is empty. 384 ERROR_MON_DATA_TOO_LARGE Data record is too large. 385 ERROR_MOUSE_NO_DEVICE Mouse device closed (invalid device handle). 386 ERROR_MOUSE_INV_HANDLE Mouse device closed (invalid device handle). 387 ERROR_MOUSE_INV_PARMS Parameters invalid for display mode. 388 ERROR_MOUSE_CANT_RESET Function assigned and cannot be reset. 389 ERROR_MOUSE_DISPLAY_PARMS Parameters invalid for display mode. 390 ERROR_MOUSE_INV_MODULE Module not valid. 391 ERROR_MOUSE_INV_ENTRY_PT Entry point not valid. 392 ERROR_MOUSE_INV_MASK Function mask invalid. 393 NO_ERROR_MOUSE_NO_DATA No valid data. 394 NO_ERROR_MOUSE_PTR_DRAWN Pointer drawn. 395 ERROR_INVALID_FREQUENCY Invalid frequency for beep. 396 ERROR_NLS_NO_COUNTRY_FILE Cannot find COUNTRY.SYS file. 397 ERROR_NLS_OPEN_FAILED Cannot open COUNTRY.SYS file. 398 ERROR_NLS_NO_CTRY_CODE Country code not found. 398 ERROR_NO_COUNTRY_OR_CODEPAGE Country code not found. 399 ERROR_NLS_TABLE_TRUNCATED Table returned information truncated, buffer is too small. ═══ Errors 400 to 499 ═══ The following shows the numerical value of an error, its symbolic name, and a brief description of the error. 400 ERROR_NLS_BAD_TYPE Selected type does not exist. 401 ERROR_NLS_TYPE_NOT_FOUND Selected type is not in file. 402 ERROR_VIO_SMG_ONLY Valid from session manager only. 403 ERROR_VIO_INVALID_ASCIIZ Invalid ASCIIZ length. 404 ERROR_VIO_DEREGISTER VioDeRegister not allowed. 405 ERROR_VIO_NO_POPUP Pop-up window not allocated. 406 ERROR_VIO_EXISTING_POPUP Pop-up window on screen (NoWait). 407 ERROR_KBD_SMG_ONLY Valid from session manager only. 408 ERROR_KBD_INVALID_ASCIIZ Invalid ASCIIZ length. 409 ERROR_KBD_INVALID_MASK Invalid replacement mask. 410 ERROR_KBD_REGISTER KbdRegister not allowed. 411 ERROR_KBD_DEREGISTER KbdDeRegister not allowed. 412 ERROR_MOUSE_SMG_ONLY Valid from session manager only. 413 ERROR_MOUSE_INVALID_ASCIIZ Invalid ASCIIZ length. 414 ERROR_MOUSE_INVALID_MASK Invalid replacement mask. 415 ERROR_MOUSE_REGISTER Mouse register not allowed. 416 ERROR_MOUSE_DEREGISTER Mouse deregister not allowed. 417 ERROR_SMG_BAD_ACTION Invalid action specified. 418 ERROR_SMG_INVALID_CALL INIT called more than once, or invalid session identity. 419 ERROR_SCS_SG_NOTFOUND New session number. 420 ERROR_SCS_NOT_SHELL Caller is not shell. 421 ERROR_VIO_INVALID_PARMS Invalid parameters passed. 422 ERROR_VIO_FUNCTION_OWNED Save/restore already owned. 423 ERROR_VIO_RETURN Non-destruct return (undo). 424 ERROR_SCS_INVALID_FUNCTION Caller invalid function. 425 ERROR_SCS_NOT_SESSION_MGR Caller not session manager. 426 ERROR_VIO_REGISTER Vio register not allowed. 427 ERROR_VIO_NO_MODE_THREAD No mode restore thread in SG. 428 ERROR_VIO_NO_SAVE_RESTORE_THD No save/restore thread in SG. 429 ERROR_VIO_IN_BG Function invalid in background. 430 ERROR_VIO_ILLEGAL_DURING_POPUP Function not allowed during pop-up window. 431 ERROR_SMG_NOT_BASESHELL Caller is not the base shell. 432 ERROR_SMG_BAD_STATUSREQ Invalid status requested. 433 ERROR_QUE_INVALID_WAIT NoWait parameter out of bounds. 434 ERROR_VIO_LOCK Error returned from Scroll Lock. 435 ERROR_MOUSE_INVALID_IOWAIT Invalid parameters for IOWait. 436 ERROR_VIO_INVALID_HANDLE Invalid VIO handle. 437 ERROR_VIO_ILLEGAL_DURING_LOCK Function not allowed during screen lock. 438 ERROR_VIO_INVALID_LENGTH Invalid VIO length. 439 ERROR_KBD_INVALID_HANDLE Invalid KBD handle. 440 ERROR_KBD_NO_MORE_HANDLE Ran out of handles. 441 ERROR_KBD_CANNOT_CREATE_KCB Unable to create kcb. 442 ERROR_KBD_CODEPAGE_LOAD_INCOMPL Unsuccessful code-page load. 443 ERROR_KBD_INVALID_CODEPAGE_ID Invalid code-page identity. 444 ERROR_KBD_NO_CODEPAGE_SUPPORT No code page support. 445 ERROR_KBD_FOCUS_REQUIRED Keyboard focus required. 446 ERROR_KBD_FOCUS_ALREADY_ACTIVE Calling thread has an outstanding focus. 447 ERROR_KBD_KEYBOARD_BUSY Keyboard is busy. 448 ERROR_KBD_INVALID_CODEPAGE Invalid code page. 449 ERROR_KBD_UNABLE_TO_FOCUS Focus attempt failed. 450 ERROR_SMG_SESSION_NON_SELECT Session is not selectable. 451 ERROR_SMG_SESSION_NOT_FOREGRND Parent/child session is not foreground. 452 ERROR_SMG_SESSION_NOT_PARENT Not parent of requested child. 453 ERROR_SMG_INVALID_START_MODE Invalid session start mode. 454 ERROR_SMG_INVALID_RELATED_OPT Invalid session start related option. 455 ERROR_SMG_INVALID_BOND_OPTION Invalid session bond option. 456 ERROR_SMG_INVALID_SELECT_OPT Invalid session select option. 457 ERROR_SMG_START_IN_BACKGROUND Session started in background. 458 ERROR_SMG_INVALID_STOP_OPTION Invalid session stop option. 459 ERROR_SMG_BAD_RESERVE Reserved parameters are not zero. 460 ERROR_SMG_PROCESS_NOT_PARENT Session parent process already exists. 461 ERROR_SMG_INVALID_DATA_LENGTH Invalid data length. 462 ERROR_SMG_NOT_BOUND Parent is not bound. 463 ERROR_SMG_RETRY_SUB_ALLOC Retry request block allocation. 464 ERROR_KBD_DETACHED This call is not allowed for a detached PID. 465 ERROR_VIO_DETACHED This call is not allowed for a detached PID. 466 ERROR_MOU_DETACHED This call is not allowed for a detached PID. 467 ERROR_VIO_FONT No font is available to support the mode. 468 ERROR_VIO_USER_FONT User font is active. 469 ERROR_VIO_BAD_CP Invalid code page specified. 470 ERROR_VIO_NO_CP System displays do not support code page. 471 ERROR_VIO_NA_CP Current display does not support code page. 472 ERROR_INVALID_CODE_PAGE Invalid code page. 473 ERROR_CPLIST_TOO_SMALL Code page list is too small. 474 ERROR_CP_NOT_MOVED Code page was not moved. 475 ERROR_MODE_SWITCH_INIT Mode switch initialization error. 476 ERROR_CODE_PAGE_NOT_FOUND Code page was not found. 477 ERROR_UNEXPECTED_SLOT_RETURNED Internal error. 478 ERROR_SMG_INVALID_TRACE_OPTION Invalid start session trace indicator. 479 ERROR_VIO_INTERNAL_RESOURCE VIO internal resource error. 480 ERROR_VIO_SHELL_INIT VIO shell initialization error. 481 ERROR_SMG_NO_HARD_ERRORS No session manager hard errors. 482 ERROR_CP_SWITCH_INCOMPLETE DosSetProcessCp is unable to set a KBD or VIO code page. 483 ERROR_VIO_TRANSPARENT_POPUP Error during VIO pop-up window. 484 ERROR_CRITSEC_OVERFLOW Critical section overflow. 485 ERROR_CRITSEC_UNDERFLOW Critical section underflow. 486 ERROR_VIO_BAD_RESERVE Reserved parameter is not zero. 487 ERROR_INVALID_ADDRESS Invalid physical address. 488 ERROR_ZERO_SELECTORS_REQUESTED At least one selector must be requested. 489 ERROR_NOT_ENOUGH_SELECTORS_AVA Not enough GDT selectors to satisfy request. 490 ERROR_INVALID_SELECTOR Not a GDT selector. 491 ERROR_SMG_INVALID_PROGRAM_TYPE Invalid program type. 492 ERROR_SMG_INVALID_PGM_CONTROL Invalid program control. 493 ERROR_SMG_INVALID_INHERIT_OPT Invalid inherit option. 494 ERROR_VIO_EXTENDED_SG 495 ERROR_VIO_NOT_PRES_MGR_SG 496 ERROR_VIO_SHIELD_OWNED 497 ERROR_VIO_NO_MORE_HANDLES 498 ERROR_VIO_SEE_ERROR_LOG 499 ERROR_VIO_ASSOCIATED_DC ═══ Errors 500 to 599 ═══ The following shows the numerical value of an error, and its symbolic name. 500 ERROR_KBD_NO_CONSOLE 501 ERROR_MOUSE_NO_CONSOLE 502 ERROR_MOUSE_INVALID_HANDLE 503 ERROR_SMG_INVALID_DEBUG_PARMS 504 ERROR_KBD_EXTENDED_SG 505 ERROR_MOU_EXTENDED_SG 506 ERROR_SMG_INVALID_ICON_FILE 507 ERROR_TRC_PID_NON_EXISTENT 508 ERROR_TRC_COUNT_ACTIVE 509 ERROR_TRC_SUSPENDED_BY_COUNT 510 ERROR_TRC_COUNT_INACTIVE 511 ERROR_TRC_COUNT_REACHED 512 ERROR_NO_MC_TRACE 513 ERROR_MC_TRACE 514 ERROR_TRC_COUNT_ZERO 515 ERROR_SMG_TOO_MANY_DDS 516 ERROR_SMG_INVALID_NOTIFICATION 517 ERROR_LF_INVALID_FUNCTION 518 ERROR_LF_NOT_AVAIL 519 ERROR_LF_SUSPENDED 520 ERROR_LF_BUF_TOO_SMALL 521 ERROR_LF_BUFFER_CORRUPTED 521 ERROR_LF_BUFFER_FULL 522 ERROR_LF_INVALID_DAEMON 522 ERROR_LF_INVALID_RECORD 523 ERROR_LF_INVALID_TEMPL 523 ERROR_LF_INVALID_SERVICE 524 ERROR_LF_GENERAL_FAILURE 525 ERROR_LF_INVALID_ID 526 ERROR_LF_INVALID_HANDLE 527 ERROR_LF_NO_ID_AVAIL 528 ERROR_LF_TEMPLATE_AREA_FULL 529 ERROR_LF_ID_IN_USE 530 ERROR_MOU_NOT_INITIALIZED 531 ERROR_MOUINITREAL_DONE 532 ERROR_DOSSUB_CORRUPTED 533 ERROR_MOUSE_CALLER_NOT_SUBSYS 534 ERROR_ARITHMETIC_OVERFLOW 535 ERROR_TMR_NO_DEVICE 536 ERROR_TMR_INVALID_TIME 537 ERROR_PVW_INVALID_ENTITY 538 ERROR_PVW_INVALID_ENTITY_TYPE 539 ERROR_PVW_INVALID_SPEC 540 ERROR_PVW_INVALID_RANGE_TYPE 541 ERROR_PVW_INVALID_COUNTER_BLK 542 ERROR_PVW_INVALID_TEXT_BLK 543 ERROR_PRF_NOT_INITIALIZED 544 ERROR_PRF_ALREADY_INITIALIZED 545 ERROR_PRF_NOT_STARTED 546 ERROR_PRF_ALREADY_STARTED 547 ERROR_PRF_TIMER_OUT_OF_RANGE 548 ERROR_PRF_TIMER_RESET 549-599 Reserved. ═══ Errors 600 to 1999 ═══ The following shows the numerical value of an error, and its symbolic name. 600-638 Reserved. 639 ERROR_VDD_LOCK_USEAGE_DENIED 640 ERROR_TIMEOUT 641 ERROR_VDM_DOWN 642 ERROR_VDM_LIMIT 643 ERROR_VDD_NOT_FOUND 644 ERROR_INVALID_CALLER 645 ERROR_PID_MISMATCH 646 ERROR_INVALID_VDD_HANDLE 647 ERROR_VLPT_NO_SPOOLER 648 ERROR_VCOM_DEVICE_BUSY 649 ERROR_VLPT_DEVICE_BUSY 650 ERROR_NESTING_TOO_DEEP 651 ERROR_VDD_MISSING 691 ERROR_IMP_INVALID_PARM 692 ERROR_IMP_INVALID_LENGTH 693 MSG_HPFS_DISK_ERROR_WARN 730 ERROR_MON_BAD_BUFFER 731 ERROR_MODULE_CORRUPTED 732-1476 Reserved. 1477 ERROR_SM_OUTOF_SWAPFILE 1478-1999 Reserved. ═══ Errors 2000 to 59999 ═══ The following shows the numerical value of an error, and its symbolic name. 2000-2054 Reserved. 2055 ERROR_LF_TIMEOUT 2057 ERROR_LF_SUSPEND_SUCCESS 2058 ERROR_LF_RESUME_SUCCESS 2059 ERROR_LF_REDIRECT_SUCCESS 2060 ERROR_LF_REDIRECT_FAILURE 32768 ERROR_SWAPPER_NOT_ACTIVE 32769 ERROR_INVALID_SWAPID 32770 ERROR_IOERR_SWAP_FILE 32771 ERROR_SWAP_TABLE_FULL 32772 ERROR_SWAP_FILE_FULL 32773 ERROR_CANT_INIT_SWAPPER 32774 ERROR_SWAPPER_ALREADY_INIT 32775 ERROR_PMM_INSUFFICIENT_MEMORY 32776 ERROR_PMM_INVALID_FLAGS 32777 ERROR_PMM_INVALID_ADDRESS 32778 ERROR_PMM_LOCK_FAILED 32779 ERROR_PMM_UNLOCK_FAILED 32780 ERROR_PMM_MOVE_INCOMPLETE 32781 ERROR_UCOM_DRIVE_RENAMED 32782 ERROR_UCOM_FILENAME_TRUNCATED 32783 ERROR_UCOM_BUFFER_LENGTH 32784 ERROR_MON_CHAIN_HANDLE 32785 ERROR_MON_NOT_REGISTERED 32786 ERROR_SMG_ALREADY_TOP 32787 ERROR_PMM_ARENA_MODIFIED 32788 ERROR_SMG_PRINTER_OPEN 32789 ERROR_PMM_SET_FLAGS_FAILED 32790 ERROR_INVALID_DOS_DD 32791 ERROR_BLOCKED 32792 ERROR_NOBLOCK 32793 ERROR_INSTANCE_SHARED 32794 ERROR_NO_OBJECT 32795 ERROR_PARTIAL_ATTACH 32796 ERROR_INCACHE 32797 ERROR_SWAP_IO_PROBLEMS 32798 ERROR_CROSSES_OBJECT_BOUNDARY 32799 ERROR_LONGLOCK 32800 ERROR_SHORTLOCK 32801 ERROR_UVIRTLOCK 32802 ERROR_ALIASLOCK 32803 ERROR_ALIAS 32804 ERROR_NO_MORE_HANDLES 32805 ERROR_SCAN_TERMINATED 32806 ERROR_TERMINATOR_NOT_FOUND 32807 ERROR_NOT_DIRECT_CHILD 32808 ERROR_DELAY_FREE 32809 ERROR_GUARDPAGE 32900 ERROR_SWAPERROR 32901 ERROR_LDRERROR 32902 ERROR_NOMEMORY 32903 ERROR_NOACCESS 32904 ERROR_NO_DLL_TERM 32905-59999 Reserved. ═══ Errors 60000 to 65079 ═══ The following shows the numerical value of an error, and its symbolic name. 6000-65025 Reserved. 65026 ERROR_CPSIO_CODE_PAGE_INVALID 65027 ERROR_CPSIO_NO_SPOOLER 65028 ERROR_CPSIO_FONT_ID_INVALID 65029-65032 Reserved. 65033 ERROR_CPSIO_INTERNAL_ERROR 65034 ERROR_CPSIO_INVALID_PTR_NAME 65035-65036 Reserved. 65037 ERROR_CPSIO_NOT_ACTIVE 65038 Reserved. 65039 ERROR_CPSIO_PID_FULL 65040 ERROR_CPSIO_PID_NOT_FOUND 65041-65042 Reserved. 65043 ERROR_CPSIO_READ_CTL_SEQ 65044 Reserved. 65045 ERROR_CPSIO_READ_FNT_DEF 65046 Reserved. 65047 ERROR_CPSIO_WRITE_ERROR 65048 ERROR_CPSIO_WRITE_FULL_ERROR 65049 ERROR_CPSIO_WRITE_HANDLE_BAD 65050-65073 Reserved. 65074 ERROR_CPSIO_SWIT_LOAD 65075-65076 Reserved. 65077 ERROR_CPSIO_INV_COMMAND 65078 ERROR_CPSIO_NO_FONT_SWIT 65079 ERROR_ENTRY_IS_CALLGATE ═══ 3. Debugging ═══ Debugging is the process of detecting, diagnosing, and eliminating errors in programs. A debugger application is designed to interact with and control the application that it is debugging. Because of the protected mode architecture of OS/2, special steps must be taken to enable a debugger application to perform its functions in the application being debugged (for example, to examine and manipulate memory locations in the address space of another process). The following topic is related to the information in this chapter:  Program execution and control ═══ 3.1. About Debugging ═══ DosDebug enables one application to control the execution of another application for debugging purposes. An application is selected for debugging when it is started. DosExecPgm and DosStartSession both have flags that can be used to specify that the application being started is to be controlled by the starting application. DosExecPgm starts an application within a new process. DosStartSession starts a new session within which one or more processes can be executing. See DosStartSession and DosExecPgm for details on how to start an application for debugging purposes. For information on processes and sessions, see Program Execution Control in this book. Once a process has been selected for debugging, DosDebug is used to control its execution and to examine and manipulate its variables. DosDebug provides a full set of debugging commands, including execution control commands-like single stepping and setting watchpoints-and commands to examine and manipulate the memory and registers of the process being debugged. The debugger process can access specific threads within a process being debugged and specific processes within a session being debugged. DosDebug also has a rich set of notification messages to keep the debugger application informed of activities occurring during the execution of the application being debugged. The debugger application can use the session and process control functions described in Program Execution Control to control the child process or session being debugged. For example, the debugger can use DosSelectSession to switch itself, or the session being debugged, to the foreground. ═══ 3.2. Using the Debugging Function ═══ DosDebug provides a set of commands that permit one process to control another process for debugging. In the following code fragment, the calling process uses DosDebug to modify a word in a controlled process. All the necessary steps have already been taken so that the calling process controls the second process-the process identifier of the controlled process has been placed into PID, the address of the word to be modified in the controlled process has been placed into Addr, and the value to be substituted in the controlled process has been placed into Value. (Due to the size of the debug_buffer data structure, the code fragment has been divided into two figures. If you were actually entering this into a program, the information would be together as if it were all one figure.) Note: In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred. Type Debug_Buffer = record Pid: Longint; { Debuggee Process ID } Tid: Longint; { Debuggee Thread ID } Cmd: Longint; { Command or Notification } Value: Longint; { Generic Data Value } Addr: Longint; { Debuggee Address } Buffer: Longint; { Debugger Buffer Address } Len: Longint; { Length of Range } Index: Longint; { Generic Identifier Index } MTE: Longint; { Module Handle } EAX: Longint; { Register Set } ECX: Longint; EDX: Longint; EBX: Longint; ESP: Longint; EBP: Longint; ESI: Longint; EDI: Longint; EFlags: Longint; EIP: Longint; CSLim: Longint; { Byte Granular Limits } CSBase: Longint; { Byte Granular Base } CSAcc: Byte; { Access Rights } CSAtr: Byte; { Attributes } CS: SmallWord; DSLim: Longint; DSBase: Longint; DSAcc: Byte; DSAtr: Byte; DS: SmallWord; ESLim: Longint; ESBase: Longint; ESAcc: Byte; ESAtr: Byte; ES: SmallWord; FSLim: Longint; FSBase: Longint; FSAcc: Byte; FSAtr: Byte; FS: SmallWord; GSLim: Longint; GSBase: Longint; GSAcc: Byte; GSAtr: Byte; GS: SmallWord; SSLim: Longint; SSBase: Longint; SSAcc: Byte; SSAtr: Byte; SS: SmallWord; end; Uses Crt,Dos,Os2Def,Os2Base; Var UlPID : ULONG; (* Process ID of the controlled process *) UlAddr : ULONG; (* Address in the controlled process *) LValue : LONG; (* Value to be substituted in the *) (* controlled process *) Ulrc : APIRET; (* Return code *) DbgBuf : debug_buffer; (* Debug buffer *) Begin DbgBuf.Cmd := DBG_C_WriteMem; (* Indicate that a Write Word *) (* command is requested *) DbgBuf.Pid := ulPID; (* Place PID of controlled process *) (* into the debug buffer *) DbgBuf.Addr := ulAddr; (* Place the word address (within the *) (* controlled process) into the debug *) (* buffer *) DbgBuf.Value := lValue; (* Place the value to be updated into *) (* the specIfied word of the controlled *) (* process *) ulrc := DosDebug(DbgBuf); If (ulrc <> 0) Then Begin Write('DosDebug error: return code = ', ulrc); Halt; End; (* Be sure to check DbgBuf.Cmd for the notIfication returned by DosDebug *) End. The Cmd field in the debug buffer is used for two purposes. On input, the Cmd field is used to pass the commands that direct DosDebug's activities. On output, the Cmd field is used by DosDebug to return a notification indicating the events and activities that occurred during the call. If DosDebug returns no error, a notification resides in the Cmd field of the debug buffer. The data returned with the notification varies, depending on the command passed in the Cmd field of the debug buffer data structure when DosDebug was called. Not all fields in the debug buffer have to be defined on every DosDebug command. The same field can have a different meaning in different DosDebug commands. Some notifications (such as DBG_N_ModuleLoad and DBG_N_NewProc) might require multiple returns to the debugger. These additional, pending notifications will be returned before the process being debugged is permitted to execute any more user code, and will be returned on the Go, Single Step, or Stop commands. Additional notifications can be pending at any time, so a debugger must be ready to handle any notification any time a Go, Single Step, or Stop command is called. ═══ 3.3. Debugging on OS/2 Warp (PowerPC Edition) ═══ The following are the specific features available for debugging for OS/2 Warp (PowerPC Edition): 1. PowerPC-specific debug buffer: The debug buffer is specified in the value field of DBG_C_Connect command as follows:  Intel: DBG_L_386 This is not defined in bsedos.h.  PowerPC: DBG_L_PPC This is defined in bsedos.h. 2. PowerPC-specific functions: a. DBG_C_ATTACH: This debug command has the following parameters: PID Process ID of debuggee CMD DBG_C_Attach VALUE DBG_L_PPC This command is defined as follows:  For clients in bsedos.h  For servers in server\include\debug_types.h as follows: This command returns: DBG_N_Success Attachment made DBG_N_Error Any OS/2-defined error This command allows attaching to a currently running task. Note: A DBG_C_Connect does not need to be issued because DBG_C_Attach will perform the connection. Also, because the debugger did not start the task, it will not have a parent/child relationship as in a DBG_C_Connect. DosDebug generates the following notifications: 1. DBG_N_ModuleLoad notifications for all loaded modules 2. DBG_N_ThreadCreate notifications for all active threads in task Note: Unlike DBG_C_Connect, there will not be any DBG_N_ModuleInit notifications because the task is most likely already in _main. b. DBG_C_Detach: This debug command has the following parameters: PID Process ID of debuggee CMD DBG_C_Detach This command returns: DBG_N_Success Attachment made DBG_N_Error Any OS/2-defined error This command detaches from attached and connected tasks. The specified task is resumed and all debugging hooks are removed. Note: This is the only call that will cleanly turn off debugging and resume the specified task. DBG_C_Term will kill the task whether it was connected or attached. 3. PowerPC-specific notifications: are as follows: a. DBG_N_ModuleInit: Module initialization routine about to run. This notification is the same format as DBG_N_ModuleLoad. CMD DBG_N_ModuleInit Value MTE (module handle) Addr 0 b. DBG_N_ReadyToRunMain: Debuggee thread 1 ready to run _start. Issued after all import DLL Init/Term routines have completed. This allows the debugger to know that the debugged task has finished loading and is ready to run. Note: For descendant debuggee tasks, DBG_N_NewProc will be sent instead. CMD DBG_N_ReadyToRunMain Value Process ID of debuggee 4. DBG_C_Go and DBG_C_SStep: will stop and return any pending notifications. 5. Serialization of command execution: All notifications other than DBG_N_Success and DBG_N_Error must be acknowledged through DBG_C_Continue before processing resumes or another notification can be grabbed. Thus, only GO/SStep and Stop (to prevent retrieving a pending notification) will be prevented from running. All other subcommands can be executed. DosDebug informs the debugger that a notification has been returned, but not acknowledged. If you issue a Go/SStep/Stop and get the following synchronous notification, then you must issue a DBG_C_Continue before resuming execution. rc := DosDebug() = 0; puDB^.Cmd := DBG_N_Error; puDB^.Value := ERROR_INVALID_FUNCTION; The following code segments illustrate the use of various commands: (*********************************************************************) (* Connect and grab all pending DBG_N_ThreadCreate, DBG_N_ModuleInit *) (* and DBG_N_ModuleLoad notIfications. Note, Stop returns *) (* DBG_N_Success when there are no more notIfications. *) (*********************************************************************) DBG_C_Connect DBG_C_Stop While ( puDB^.Cmd <> DBG_N_Success ) Do Begin DBG_C_Continue w XCPT_CONTINUE_STOP DBG_C_Stop End; (**********************************************************************) (* Go until certain notIfication occurs. This loop will also grab any *) (* pending notIfications that are outstanding since DBG_C_Go won't *) (* execute If any notifications are pending. *) (**********************************************************************) DBG_C_Go While (debug_buffer^.Cmd <> NotIfication your looking for) Do Begin DBG_C_Continue w XCPT_CONTINUE_STOP DBG_C_GO End; (*********************************************************************) (* Clear all pending notIfications and acknowledge each notification *) (*********************************************************************) DBG_C_STOP While (debug_buffer^.Cmd <> DBG_N_Success) Do Begin DBG_C_Continue w XCPT_CONTINUE_STOP DBG_C_STOP End; 6. Per task serialization of all DosDebug subcommands: A debugger can be multi-threaded. If the debugger issues subcommands for different tasks they can run concurrently. If the debugger issues subcommands for the same task they will be serialized. 7. Breakpoint exceptions (trap word instruction): Debugger must increment IP to next instruction (+4) whenever a breakpoint exception occurs. 8. DBG_N_Exception notification: Returns Thread ID of thread taking exception in debug_buffer^.TID field. When responding to exception notifications use the proper Thread ID in the DBG_C_Continue call. 9. Unsupported or not working for the PowerPC: a. Calls to these commands will result in a DBG_C_Null:  DBG_C_XchngOpcode  DBG_C_RangeStep  DBG_C_MapRWAlias  DBG_C_UnMapAlias  DBG_C_MapROAlias  DBG_C_LinToSel  DBG_C_SelToLin b. Global scope watchpoints: Watchpoint effective in the context of any task currently not allowed. c. Descendant (task or session) debugging not working. 1. EXEC_ASYNCRESULTDB: Tasking flag will act like EXEC_TRACE 2. SSF_TRACEOPT_TRACEALL: Session flag will act like SSF_TRACEOPT_TRACE 3. DBG_N_ProcNew notification never sent 10. DBG_C_NumToAddr and DBG_AddrToNum: these functions are not fully supported on the PowerPC as there meaning differs between Intel and PowerPC architectures. The following has been observed: a. On Intel, this should be data segment. It appears to be data segment on the PowerPC: PBuf^.Cmd := DBG_C_NumToAddr; PBuf^.Value := 1; b. On Intel, this should be a code segment. On the PowerPC, this is invalid: PBuf^.Cmd := DBG_C_NumToAddr; PBuf^.Value := 2; c. On Intel, this should be invalid. On the PowerPC, this appears to be code segment: PBuf^.Cmd := DBG_C_NumToAddr; PBuf^.Value := 0; ═══ 3.3.1. Summary of bsedos.h ═══ The following summarizes the contents of the header file bsedos.h. ═══ 3.3.1.1. Debug Buffer for the PowerPC ═══ TYPE uDB = Record Pid : ULONG; (* Debuggee Process id *) Tid : ULONG; (* Debuggee Thread id *) Cmd : LONG; (* Command or Notification *) Value : LONG; (* Generic Data Value *) Addr : ULONG; (* Debuggee Address *) Buffer : ULONG; (* Debugger Buffer Address *) Len : ULONG; (* Length of Range *) Index : ULONG; (* Generic Identifier Index *) MTE : ULONG; (* Module Table Entry Handle *) Ctr : ULONG; (* Count register *) Lr : ULONG; (* Link register *) Xer : ULONG; (* Integer Exception register *) Msr : ULONG; (* Machine State Register *) Cr : ULONG; (* Condition Register *) Iar : ULONG; (* Instruction pointer *) GP_REGS : Array[0..31] of ULONG; (* General Purpose registers *) End; ═══ 3.3.1.2. DosDebug Command Numbers ═══ The following are the DosDebug command numbers: (* 16 is reserved *) The following defines are not implemented for the PowerPC and are translated to DBG_C_nil: The following defines are not yet implemented for the PowerPC: ═══ 3.3.1.3. DosDebug Notification Numbers ═══ The following are the DosDebug notification numbers for the PowerPC: (*** NEW to PowerPC ***) Init routines completed *) ═══ 3.3.1.4. DBG_T_ TState Values ═══ These are the possible values that can be returned in the TState field of the TStat structure. These values identify scheduler state information: ═══ 3.3.1.5. DosDebug Hardware-Specific Subcommands ═══ The following are the DosDebug PowerPC-specific subcommands: 1. Connect: uDB.Value := DBG_L_PPC (* Debugging level number *) DBG_L_PPC is defined in bsedos.h. 2. ReadRegs and WriteRegs: Uses the debug buffer defined in bsedos.h. 3. ReadCoRegs and WriteCoRegs: PPC_FLOAT_STATE_SIZE is defined in public uKernel release tree in file thread_status.h: uDB.Value := DBG_CO_PPC (* Coprocessor type identIfier *) uDB.Len ;= PPC_FLOAT_STATE_SIZE (* Size of coprocessor *) (* register context buffer *) Note: Not defined in bsedos.h. 4. ThrdStat. uDB.Len is the length of the thread status buffer, in bytes. This field is four bytes (same as Intel). Thread Status Buffer is the same as Intel and is not stored in bsedos.h. TStat = record DbgState: Byte; { Thread's Debugging State } TState: Byte; { Thread's Scheduler State } TPriority: SmallWord; { Thread's Scheduler Priority } end; The following are defined in this Guide: The following are defined in bsedos.h and in this Guide: 5. SetWatch: uDB.Addr Starting Address of Watchpoint uDB.Len Length of Watchpoint, in bytes uDB.Index Reserved (0) uDB.Value Watchpoint Type and Scope (same as Intel) Scopes Only DBG_W_Local allowed. No DBG_W_Global allowed. Types DBG_W_Execute, DBG_W_Write, and DBG_W_ReadWrite. All defines are the same as the Intel defines, as described in this Guide. Note: There are two types of Watchpoints: a. Instruction (DBG_W_Execute). Four bytes long and four byte aligned. b. Data (DBG_W_Write, DBG_W_ReadWrite). Eight bytes long and eight byte aligned. 601, 604 : 1 instruction WP, 1 data WP 603 : 1 instruction WP 6. Attach and Detach: DBG_C_ATTACH - Debug command: Parameters: PID Process ID of debuggee CMD DBG_C_Attach VALUE DBG_L_PPC (DBG_L_PPC - defined in bsedos.h) Returns: Note: A DBG_C_Connect does not need to be issued because DBG_C_Attach will perform the connection. Also, because the debugger did not start the task, it will not have a parent/child relationship as in a DBG_C_Connect. DBG_N_Success Attachment made. DBG_N_Error All errors (for example, bad process id) DosDebug will generate the following notifications: a. DBG_N_ModuleLoad notifications for all loaded modules. b. DBG_N_ThreadCreate notifications for all active threads in task. Currently, descendant debugging is not working. DBG_C_Detach - Debug command. Parameters: PID Process ID of debuggee CMD DBG_C_Detach Returns: DBG_N_Success Attachment made DBG_N_Error All errors (for example, bad process id) Will detach from an attached or connected task. Note: This is the only call that will normally turn off debugging and resume the specified task. DBG_C_Term will kill the task whether it was connected or attached. ═══ 3.4. DosDebug Commands ═══ The following table list describes the available commands. ┌─────┬────────────────────┬───────────────────────────────────┐ │Cmd │Command Name │Description │ │No. │ │ │ ├─────┼────────────────────┼───────────────────────────────────┤ │0 │DBG_C_Null │Null │ ├─────┼────────────────────┼───────────────────────────────────┤ │1 │DBG_C_ReadMem │Read Word │ ├─────┼────────────────────┼───────────────────────────────────┤ │1 │DBG_C_ReadMem_I │Read Word │ ├─────┼────────────────────┼───────────────────────────────────┤ │2 │DBG_C_ReadMem_D │Read Word (same as 1) │ ├─────┼────────────────────┼───────────────────────────────────┤ │3 │DBG_C_ReadReg │Read Register Set │ ├─────┼────────────────────┼───────────────────────────────────┤ │4 │DBG_C_WriteMem │Write Word │ ├─────┼────────────────────┼───────────────────────────────────┤ │4 │DBG_C_WriteMem_I │Write Word │ ├─────┼────────────────────┼───────────────────────────────────┤ │5 │DBG_C_WriteMem_D │Write Word (same as 4) │ ├─────┼────────────────────┼───────────────────────────────────┤ │6 │DBG_C_WriteReg │Write Register Set │ ├─────┼────────────────────┼───────────────────────────────────┤ │7 │DBG_C_Go │Go │ ├─────┼────────────────────┼───────────────────────────────────┤ │8 │DBG_C_Term │Terminate │ ├─────┼────────────────────┼───────────────────────────────────┤ │9 │DBG_C_SStep │Single Step │ ├─────┼────────────────────┼───────────────────────────────────┤ │10 │DBG_C_Stop │Stop │ ├─────┼────────────────────┼───────────────────────────────────┤ │11 │DBG_C_Freeze │Freeze Thread │ ├─────┼────────────────────┼───────────────────────────────────┤ │12 │DBG_C_Resume │Resume Thread │ ├─────┼────────────────────┼───────────────────────────────────┤ │13 │DBG_C_NumToAddr │Object Number to Address │ ├─────┼────────────────────┼───────────────────────────────────┤ │14 │DBG_C_ReadCoRegs │Read Coprocessor Registers │ ├─────┼────────────────────┼───────────────────────────────────┤ │15 │DBG_C_WriteCoRegs │Write Coprocessor Registers │ ├─────┼────────────────────┼───────────────────────────────────┤ │16 │Reserved │Reserved │ ├─────┼────────────────────┼───────────────────────────────────┤ │17 │DBG_C_ThrdStat │Get Thread Status │ ├─────┼────────────────────┼───────────────────────────────────┤ │18 │DBG_C_MapROAlias │Map Read-Only Alias │ ├─────┼────────────────────┼───────────────────────────────────┤ │19 │DBG_C_MapRWAlias │Map Read-Write Alias │ ├─────┼────────────────────┼───────────────────────────────────┤ │20 │DBG_C_UnMapAlias │Unmap Alias │ ├─────┼────────────────────┼───────────────────────────────────┤ │21 │DBG_C_Connect │Connect to Debuggee │ ├─────┼────────────────────┼───────────────────────────────────┤ │22 │DBG_C_ReadMemBuf │Read Memory Buffer │ ├─────┼────────────────────┼───────────────────────────────────┤ │23 │DBG_C_WriteMemBuf │Write Memory Buffer │ ├─────┼────────────────────┼───────────────────────────────────┤ │24 │DBG_C_SetWatch │Set Watchpoint │ ├─────┼────────────────────┼───────────────────────────────────┤ │25 │DBG_C_ClearWatch │Clear Watchpoint │ ├─────┼────────────────────┼───────────────────────────────────┤ │26 │DBG_C_RangeStep │Range Step │ ├─────┼────────────────────┼───────────────────────────────────┤ │27 │DBG_C_Continue │Continue After an Execution │ ├─────┼────────────────────┼───────────────────────────────────┤ │28 │DBG_C_AddrToObject │Address to an Object │ ├─────┼────────────────────┼───────────────────────────────────┤ │29 │DBG_C_XchngOpcode │Exchange Opcode and Go │ ├─────┼────────────────────┼───────────────────────────────────┤ │30 │DBG_C_LinToSel │Translate Linear Address to │ │ │ │Segment:Offset │ ├─────┼────────────────────┼───────────────────────────────────┤ │31 │DBG_C_SelToLin │Translate Segment:Offset to Linear │ │ │ │Address │ └─────┴────────────────────┴───────────────────────────────────┘ Not all fields must be defined for every DosDebug command. The same field can have a different meaning in different DosDebug commands. Fields in the DosDebug Buffer structure that are not listed in the Parameter section of each command is not useful for that command. Error cases for commands are not listed. The listed return values from commands are valid only if the DBG_N_Success notification is given. ═══ 3.4.1. DBG_C_Null ═══ Debug Command 0 - Null Command Parameters Pid = Process ID of debuggee Cmd = DBG_C_Null No operation is performed on the debuggee. You can issue this command to verify the process ID of the debuggee, and to check if the debuggee is active. Pid must be valid, or an error is returned. ═══ 3.4.2. DBG_C_ReadMem ═══ Debug Command 1 and 2 - Read Word Command Parameters Pid = Process ID of debuggee Addr = Address to read from Cmd = DBG_C_ReadMem_I, or DBG_C_ReadMem_D, or DBG_C_ReadMem The commands DBG_C_ReadMem_I, DBG_C_ReadMem_D, and DBG_C_ReadMem are identical. Returns The word at the desired address is read, and stored into Value. Value = Word read from the specified address. Restrictions You are unable to read from any memory outside user space. The high-order word of Value is set to zero. ═══ 3.4.3. DBG_C_ReadMem_I ═══ Debug Command 1 and 2 - Read Word Command Parameters Pid = Process ID of debuggee Addr = Address to read from Cmd = DBG_C_ReadMem_I, or DBG_C_ReadMem_D, or DBG_C_ReadMem The commands DBG_C_ReadMem_I, DBG_C_ReadMem_D, and DBG_C_ReadMem are identical. Returns The word at the desired address is read, and stored into Value. Value = Word read from the specified address. Restrictions You are unable to read from any memory outside user space. The high-order word of Value is set to zero. ═══ 3.4.4. DBG_C_ReadMem_D ═══ Debug Command 1 and 2 - Read Word Command Parameters Pid = Process ID of debuggee Addr = Address to read from Cmd = DBG_C_ReadMem_I, or DBG_C_ReadMem_D, or DBG_C_ReadMem The commands DBG_C_ReadMem_I, DBG_C_ReadMem_D, and DBG_C_ReadMem, are identical. Returns The word at the desired address is read, and stored into Value. Value = Word read from the specified address. Restrictions You are unable to read from any memory outside user space. The high-order word of Value is set to zero. ═══ 3.4.5. DBG_C_ReadReg ═══ Debug Command 3 - Read Register Set Command Parameters Pid = Process ID of debuggee Tid = Thread ID of register set to read Cmd = DBG_C_ReadReg If Tid is zero and the debuggee is stopped, the register set comes from the active debuggee thread. If Tid is zero and the debuggee is executing, ERROR_INVALID_THREADID is returned. Returns The register set in the DosDebug Buffer structure is updated, including the selector information as follows: Tid = Thread ID corresponding to the register set MTE = Program module's MTE (Module Table Entry) handle ═══ 3.4.6. DBG_C_WriteMem ═══ Debug Command 4 and 5 - Write Word Command Parameters Pid = Process ID of debuggee Addr = Address to write to Value = Word to write Cmd = DBG_C_WriteMem_I, or DBG_C_WriteMem_D or DBG_C_WriteMem The commands DBG_C_WriteMem_I, DBG_C_WriteMem_D, and DBG_C_WriteMem are identical. Returns The word in Value is written to the specified address. In the case of a write to shared read-only memory, the memory is converted to private, and any set dynamic RAS tracepoints are removed from that memory, before the write is performed. The area will continue to be shared by other processes, if any. In this way, breakpoints may be set in the debuggee without affecting the other modules. Restrictions You are unable to write to any memory outside user space. The high-order word of Value is ignored. ═══ 3.4.7. DBG_C_WriteMem_I ═══ Debug Command 4 and 5 - Write Word Command Parameters Pid = Process ID of debuggee Addr = Address to write to Value = Word to write Cmd = DBG_C_WriteMem_I, or DBG_C_WriteMem_D, or DBG_C_WriteMem_I The commands DBG_C_WriteMem_I, DBG_C_WriteMem_D, and DBG_C_WriteMem_I are identical. Returns The word in Value is written to the specified address. In the case of a write to shared read-only memory, the memory is converted to private, and any set dynamic RAS tracepoints are removed from that memory, before the write is performed. The area will continue to be shared by other processes, if any. In this way, breakpoints may be set in the debuggee without affecting the other modules. Restrictions You are unable to write to any memory outside user space. The high-order word of Value is ignored. ═══ 3.4.8. DBG_C_WriteMem_D ═══ Debug Command 4 and 5 - Write Word Command Parameters Pid = Process ID of debuggee Addr = Address to write to Value = Word to write Cmd = DBG_C_WriteMem_I or DBG_C_WriteMem_D, or DBG_C_WriteMem, and The commands DBG_C_WriteMem_I, DBG_C_WriteMem_D, and DBG_C_WriteMem, and are identical. Returns The word in Value is written to the specified address. In the case of a write to shared read-only memory, the memory is converted to private, and any set dynamic RAS tracepoints are removed from that memory, before the write is performed. The area will continue to be shared by other processes, if any. In this way, breakpoints may be set in the debuggee without affecting the other modules. Restrictions You are unable to write to any memory outside user space. The high-order word of Value is ignored. ═══ 3.4.9. DBG_C_WriteReg ═══ Debug Command 6 - Write Register Set Command Parameters Pid = Process ID of debuggee Tid = Nonzero Thread ID of register set to write Cmd = DBG_C_WriteReg The register set in the DosDebug Buffer should contain the desired values. Returns Tid Thread ID corresponding to the register set. All registers are updated to the stored values. The access rights, limits, and Eflags are also updated to match the current values. An error is returned if the selectors are not accessible by user space code, or are not valid memory segments. Restrictions Reserved system or processor flags bits are not modified via this method, but are masked to their correct values. The selector access rights and limits cannot be modified. The flags, access rights, and limits in the DosDebug Buffer structure are updated to the actual values. ═══ 3.4.10. DBG_C_Go ═══ Debug Command 7 - Go Command Parameters Pid = Process ID of debuggee Cmd = DBG_C_Go Returns All non-frozen threads of the debuggee are allowed to execute user code at once. If all of the debuggee threads are frozen, an error is returned. The DBG_C_Go command completes when a DosDebug event (such as a Breakpoint) occurs. This event can be any one of the DosDebug notifications. See DosDebug Notifications for more information. When the next DosDebug event occurs, all threads in the debuggee process are marked to not execute any additional user code until the next DBG_C_Go command is issued. This provides a stable environment for debugging. When the DBG_C_Go command returns, the register set is automatically updated to reflect the thread that detected the event. ═══ 3.4.11. DBG_C_Term ═══ Debug Command 8 - Terminate Command Parameters Pid = Process ID of debuggee Cmd = DBG_C_Term Returns The debuggee process is terminated immediately. No additional DosDebug commands or notifications will be allowed to this process. Outstanding memory aliases and watchpoints will be invalidated automatically. Debuggee DosExecPgm processing will be attempted, but any unexpected DosDebug event (such as a Breakpoint) during this period will cause the process to terminate without completing DosExitList processing. For this reason, data watchpoints will automatically be cleared before attempting DosExitList processing. If the Terminate command is issued during DosExitList processing, DosExitList processing will terminate immediately, without continuing the DosExitList routines. ═══ 3.4.12. DBG_C_SStep ═══ Debug Command 9 - Single Step Command Parameters Pid = Process ID of debuggee Tid = Thread ID of thread to single-step Cmd = DBG_C_SStep If Tid is zero, all threads will be marked to single-step at once, and the first thread to be scheduled to execute user-space code will single-step. No other threads will single-step. Returns Usually, the DBG_N_Exception notification is returned, but any notification may be returned. See DosDebug Notifications for more information. Callgates that result in a privilege level transition to ring 0 will appear to single-step as a single instruction, with the single-step occurring just after the function completes. This hides ring 0 execution from debuggers. Attempting to single-step any thread that is frozen results in an error. Restrictions The DBG_C_SStep command has two modes of operation, as follows:  If Tid is zero, the current thread is single-stepped while allowing all other threads to execute.  If Tid is nonzero, a specific thread is selected for single-stepping. Only that thread is executed, even if it is single-stepping a kernel function that can potentially cause a deadlock condition. The single-step exception (XCPT_SINGLE_STEP) is not lost if the single-step operation causes a notification to be sent to DosDebug. In this case, the single-step exception is queued. The single-step operation is not lost if other notifications were queued before the DBG_C_SStep command was issued. The Debug DBG_C_Continue command will clear the notifications one at a time until DosDebug has been completely notified. On the last DBG_C_Continue command, the single-step operation will take place as originally requested. When a single-step operation is interrupted by an exception, the EIP (instruction pointer) should be moved to the next RING3 instruction. This may be in ring 3 system code. The single-step notification will be issued at this time. The DBG_C_SStep command correctly single-steps most instructions. Single-stepping some REP instructions may not work correctly due to errors in the 80386 processor. ═══ 3.4.13. DBG_C_Stop ═══ Debug Command 10 - Stop Command Parameters Pid = Process ID of debuggee Cmd = DBG_C_Stop Returns The function performed by this command depends on the current state of the debuggee process, as follows:  If the debuggee is already stopped: If there is a pending notification from the current thread, it is returned. See DosDebug Notifications for information about pending notifications. If there is no pending notification from the current thread, DBG_N_Success is returned.  If the debuggee is executing user code: The debuggee is marked to stop before the next time it is ready to execute user-space (ring 2 or 3) code. This is known as an asynchronous stop. Kernel operations will not be interrupted for this DBG_C_Stop That is, threads blocked in the kernel (via a semaphore or internal operation) will not be interrupted. However, an infinite loop in user space will be stopped. Note: The asynchronous variation of the stop command implies a debugger with a minimum of two threads; one waits for a DBG_C_Go or DBG_C_SStep command to finish, and another executes the DBG_C_Stop command. ═══ 3.4.14. DBG_C_Freeze ═══ Debug Command 11 - Freeze Thread Command Parameters Pid = Process ID of debuggee Tid = Thread ID of thread to freeze Cmd = DBG_C_Freeze If Tid is zero, all debuggee threads will be frozen. Returns The desired threads are prevented from executing user code on a DBG_C_Go or DBG_C_SStep command. By using the DBG_C_Freeze and DBG_C_Resume commands, a given set of threads can be executed at once, while keeping the other threads suspended. No error is returned if the thread was previously frozen; it just remains frozen. DBG_C_Freeze and DBG_C_Resume commands cannot be nested. If the Tid is specified as zero, it will be set to the thread ID of the debuggee thread most recently scheduled to execute. ═══ 3.4.15. DBG_C_Resume ═══ Debug Command 12 - Resume Thread Command Parameters Pid = Process ID of debuggee Tid = Thread ID of thread to thaw Cmd = DBG_C_Resume If Tid is zero, all debuggee threads will be thawed. Returns The DBG_C_Resume command complements the DBG_C_Freeze command. A thread that has been resumed will function as if it were never frozen. No error is returned if the thread was not previously frozen. If the Tid is specified as zero, it will be set to the thread ID of the debuggee thread most recently scheduled to execute. ═══ 3.4.16. DBG_C_NumToAddr ═══ Debug Command 13 - Convert Object Number to Address Command Parameters Pid = Process ID of debuggee Cmd = DBG_C_NumToAddr Value = Logical object number in module MTE = Module handle of module of interest Returns Addr = Starting address of object Value = Logical object number MTE = Module handle of module of interest The logical object number in Value is converted into an address that points to the starting address of the desired logical object of the specified module in the debuggee's memory space. This address is then stored in the Addr field, in the form of a linear address. The Value and MTE fields are left unchanged. The logical object numbers for a module are generated at link time. By using this function, a debugger can discern the relationship between addresses and logical object numbers. Once the logical object number is known, symbols can be generated for an address via a map or symbol file, for symbolic debugging. ═══ 3.4.17. DBG_C_ReadCoRegs ═══ Debug Command 14 - Read Coprocessor Registers Command Parameters Pid = Process ID of debuggee processor Tid = Thread ID of Coprocessor register set to read Cmd = DBG_C_ReadCoRegs Value = Coprocessor Type Identifier Buffer = Pointer to Coprocessor Register Context Buffer Len = Size of Coprocessor Register Context Buffer Index = Reserved, must be zero If Tid is zero and the debuggee is stopped, the register set comes from the active debuggee thread. If Tid is zero and the debuggee is executing, ERROR_INVALID_THREADID is returned. The coprocessor type identifier is a number that identifies the format of the coprocessor register context buffer. The buffer length must correspond exactly to the requested buffer format. The supported coprocessor types, formats and lengths include the following: For the Intel 80387 NPX processor: Value = DBG_CO_387 = 1 Len = 108 The coprocessor register context buffer format is the same as that defined by the fsave/frestore instructions as executed by the appropriate processor. Returns The debugger's coprocessor register context buffer is filled in with a copy of the registers read from the appropriate coprocessor, for the thread specified in the Tid field. If an error occurs while attempting to access the coprocessor context during this command, the DBG_N_CoError notification is returned. Restrictions An error is returned if any one of the following occurs:  The debuggee process is emulating the coprocessor.  The specified debuggee thread has not yet attempted to use the coprocessor.  The wrong coprocessor type is used.  Index is not zero. ═══ 3.4.18. DBG_C_WriteCoRegs ═══ Debug Command 15 - Write Coprocessor Registers Command Parameters Pid = Process ID of debuggee processor Tid = Nonzero Thread ID of Coprocessor register set to read Cmd = DBG_C_WriteCoRegs Value = Coprocessor Type Identifier Buffer = Pointer to Coprocessor Register Context Buffer Len = Size of Coprocessor Register Context Buffer Index = Reserved, must be zero The coprocessor type identifier is a number that identifies the format of the coprocessor register context buffer. The buffer length must correspond exactly to the requested buffer format. See DBG_C_ReadCoRegs for the supported coprocessor types, formats, and lengths. The coprocessor register context buffer format is the same as that defined by the fsave/frestore instructions as executed by the appropriate processor. Returns The debuggee thread's coprocessor registers are filled with the values passed via the coprocessor register context buffer, for the thread specified in the Tid field. If an error occurs while attempting to access the coprocessor context during this command, the DBG_N_CoError notification is returned. Restrictions An error is returned if any one of the following occurs:  The debuggee process is emulating the coprocessor.  The specified debuggee thread has not yet attempted to use the coprocessor.  The wrong coprocessor type is used.  Index is not zero. The coprocessor may adjust some control register bits, but DosDebug will not return an error if a modification is attempted, nor will it mask the values. Because of internal coprocessor management, this adjustment may be delayed until the thread actually uses the coprocessor again. ═══ 3.4.19. DBG_C_ThrdStat ═══ Debug Command 17 - Get Thread Status Command Parameters Pid = Process ID of debuggee Tid = Thread ID of thread of interest Cmd = DBG_C_ThrdStat Buffer = Pointer to Thread Status buffer Len = Length of Thread Status buffer, in bytes. This value is 4. If Tid is zero, the status of the debuggee thread most recently scheduled to run will be returned. Returns Value = Thread ID of "next" active thread to examine Tid = Thread ID of thread whose status is returned Thread Status buffer = Buffer for the thread status, filled in. Thread Status buffer format is as follows: Not defined. DbgState in the Thread Status buffer contains information about the current state of debugging, and will have one of the following values upon return: 0 DBG_D_Thawed 1 DBG_D_Frozen TState in the Thread Status buffer contains information about the scheduling state of the thread, and will have one of the following values upon return: 0 DBG_T_Runnable 1 DBG_T_Suspended 2 DBG_T_Blocked 3 DBG_T_CritSec TPriority in the Thread Status buffer contains the thread's base scheduling priority. This priority will be expressed as scheduling class and delta values upon return. The Value field will be filled in with the Thread ID of the "next" thread to look at when traversing threads. By repeatedly calling the DBG_C_ThrdStat command, replacing the Tid with the last returned Value until a thread ID is repeated, all threads in the process can be traversed. When used in this way, the Tids returned by the DBG_C_ThrdStat command form a loop of the debuggee's thread IDs. ═══ 3.4.20. DBG_C_MapROAlias ═══ Debug Command 18 and 19 - Map Read-Only or Read-Write Memory Alias Command Parameters Pid = Process ID of debuggee Cmd = DBG_C_MapROAlias (Read Only) (Not always supported) Cmd = DBG_C_MapRWAlias (Read Write) Buffer = Reserved, must be zero. Addr = Start of debuggee region to alias (Page-Aligned) Len = Requested length of alias region (Page-Multiple) Returns Buffer = Address of the start of the debugger alias region An alias to the debuggee's memory region of the requested length is mapped into the debugger's memory space. This region is reserved for use as an alias region until it is unmapped. The access rights for the alias area are determined by the command number. The DBG_C_MapROAlias command maps a read-only alias region, while the DBG_C_MapRWAlias command maps a read-write alias region. For read-write aliases, if the region is shared and read-only in the debuggee's context, a private copy of the aliased pages will be created in the debuggee's context, and dynamic RAS tracepoints will be removed from that region. This prevents debugging from affecting other areas of the system, while allowing access to shared memory areas, and proper disassembly of regions where dynamic RAS tracepoints are in use. Because the read-write aliases may convert objects to private, using up system resources, it is recommended that read-only aliases be used when simply perusing memory. See the following Restrictions regarding read-only aliases on the 80386 processor. Because the entire aliased region may map both valid and invalid regions of memory, debuggers should issue DosQueryMem just before accessing the alias region to determine if the region is valid. Debuggers should not access this region while the debuggee is executing, as portions of this region may become invalid without notifying the debugger. It is possible that no valid pages will exist in the alias region. When the debuggee frees an aliased object, or shrinks the underlying object such that the alias would span a region outside the resultant object, an alias-free notification is returned to the debugger. This notification will be returned before the alias is invalidated. See DosDebug Notifications for details. These commands may be performed while the debuggee is executing code via a DBG_C_Go command. Restrictions Because debuggers can execute code at ring 2, and the read-only bit in the page tables entries is effective only at ring 3, the read-only aliases cannot be supported. When the read-only bit becomes effective at all rings, as is expected on later processors, the read-only aliases will again be supported. Most memory management calls may not be used on these aliases. DosQueryMem is permitted, but for interrogation only. The passed starting addresses must be aligned on a page boundary, and the length of the aliased region must be a multiple of the page size. This restriction is due to the underlying hardware. Aliased regions must be completely contained within a single debuggee memory object. No interface is available for moving an alias to point to another section of debuggee memory. To move an alias, the debugger must free an existing alias, and then map a new alias to the desired region. Aliases will only be permitted to the user space memory region of the debuggee. No aliases will be provided to system space. The alias region will only be provided at the linear level. No debugger Local Descriptor Table ( LTD) selector will be available to access the alias region. ═══ 3.4.21. DBG_C_MapRWAlias ═══ Debug Command 18 and 19 - Map Read-Only or Read-Write Memory Alias Command Parameters Pid = Process ID of debuggee Cmd = DBG_C_MapROAlias (Read Only) (Not always supported) Cmd = DBG_C_MapRWAlias (Read Write) Buffer = Reserved, must be zero. Addr = Start of debuggee region to alias (Page-Aligned) Len = Requested length of alias region (Page-Multiple) Returns Buffer = Address of the start of the debugger alias region An alias to the debuggee's memory region of the requested length is mapped into the debugger's memory space. This region is reserved for use as an alias region until it is unmapped. The access rights for the alias area are determined by the command number. The DBG_C_MapROAlias command maps a read-only alias region, while the DBG_C_MapRWAlias command maps a read-write alias region. For read-write aliases, if the region is shared and read-only in the debuggee's context, a private copy of the aliased pages will be created in the debuggee's context, and dynamic RAS tracepoints will be removed from that region. This prevents debugging from affecting other areas of the system, while allowing access to shared memory areas, and proper disassembly of regions where dynamic RAS tracepoints are in use. Because the read-write aliases may convert objects to private, using up system resources, it is recommended that read-only aliases be used when simply perusing memory. See the following Restrictions regarding read-only aliases on the 80386 processor. Because the entire aliased region may map both valid and invalid regions of memory, debuggers should issue DosQueryMem just before accessing the alias region to determine if the region is valid. Debuggers should not access this region while the debuggee is executing, as portions of this region may become invalid without notifying the debugger. It is possible that no valid pages will exist in the alias region. When the debuggee frees an aliased object, or shrinks the underlying object such that the alias would span a region outside the resultant object, an alias-free notification is returned to the debugger. This notification will be returned before the alias is invalidated. See DosDebug Notifications for details. These commands may be performed while the debuggee is executing code via a DBG_C_Go command. Restrictions Because debuggers can execute code at ring 2, and the read-only bit in the page tables entries is effective only at ring 3, the read-only aliases cannot be supported. When the read-only bit becomes effective at all rings, as is expected on later processors, the read-only aliases will again be supported. Most memory management calls may not be used on these aliases. DosQueryMem is permitted, but for interrogation only. The passed starting addresses must be aligned on a page boundary, and the length of the aliased region must be a multiple of the page size. This restriction is due to the underlying hardware. Aliased regions must be completely contained within a single debuggee memory object. No interface is available for moving an alias to point to another section of debuggee memory. To move an alias, the debugger must free an existing alias, and then map a new alias to the desired region. Aliases will only be permitted to the user space memory region of the debuggee. No aliases will be provided to system space. The alias region will only be provided at the linear level. No debugger Local Descriptor Table ( LDT) selector will be available to access the alias region. ═══ 3.4.22. DBG_C_UnMapAlias ═══ Debug Command 20 - UnMap Memory Alias Command Parameters Pid = Process ID of debuggee Cmd = DBG_C_UnMapAlias Buffer = Address of the debugger alias region to unmap Returns The DBG_C_UnMapAlias command is used when the debugger has finished using an alias region. Both read-only and read-write aliases may be freed in this way. Regions returned from other memory management calls may not be used. The debugger may issue this command while the debuggee is executing code via a DBG_C_Go command. When the debuggee process terminates, all aliases to its memory space will be invalidated. When a debugger program terminates, all aliases from its memory space will also be invalidated. ═══ 3.4.23. DBG_C_Connect ═══ Debug Command 21 - Connect To Debuggee Command Parameters Addr = Possible values are shown in the list below: 0x00000000 The default action is to sever the connection between the debugger and the program being debugged if a system resource is being held. 0x00000001 The Sever action is not wanted between the debugger and the program being debugged. Pid = Process ID of debuggee Tid = Reserved, must be zero Cmd = DBG_C_Connect Value = Debugging Level Number The only permitted debugging level number is shown in the following list: 1 = DBG_L_386 This must be the first DosDebug command. No other DosDebug command will be accepted until the debugging connection has been established. Returns This command establishes a debugging connection. It must be the initial command, since it verifies the buffer format for the rest of the connection. Because DosDebug usually cannot be ported to new machines without changing the format of the buffer, this command is needed to establish that the debugger is capable of handling the desired buffer format. If the requested debugging level is not supported, an error is returned, and the connection is not made. This gives the debugger a chance to try again, or to automatically start a different debugger process that uses a different buffer format. For this command, only the machine-independent portion of the buffer is examined. This portion includes the Pid, Tid, Cmd, and Value fields. This makes it possible to port the DosDebug buffer from one machine to another, without returning an error to the debugger on the initial DosDebug command. The only DosDebug notifications that are returned by this command are DBG_N_Success and DBG_N_Error. Restrictions If the connection to the debuggee is not established within a reasonable amount of time, it is assumed that the connection will never be established, and the debuggee process is terminated automatically. The current format level may or may not be supported in future versions. This is due to the machine dependence of the DosDebug function. Remarks If Addr is set to 0, the connection between the debugger and the program being debugged is not severed. If any debuggee threads, other than the thread that encountered the debug event, are holding system semaphores, they will be allowed to run until they release the semaphores. They will then be stopped and the notification will be delivered. If the thread encountering the debug event is holding a system semaphore the debugger/debuggee connection is severed by terminating the debuggee, and returning a DBG_N_Error notification to the debugger with the value field set to 0, and the register set filled in. No further DosDebug commands will be accepted by the debuggee, nor will it generate any other notifications. If a DBG_C_Stop is issued, and a thread owning a system semaphore is about to generate a DBG_N_AsyncStop notification, it will be allowed to continue execution until it releases the semaphore. It will then be stopped, and the notification delivered. This is the only exception to the severing of the debugger/debuggee rule. If Addr is set to 0, the connection between the debugger the program being debugged is severed if a system resource is being held,. in which case DosDebug returns: Tid = Thread owning semaphore Cmd = DBG_N_Error Value = ERROR_EXCL_SEM_ALREADY_OWNED If the debugger needs to present some information to the user or use the thread holding the system resource, the debugger must terminate the program being debugged. Any other action might result in a system halt. ═══ 3.4.24. DBG_C_ReadMemBuf ═══ Debug Command 22 - Read Memory Buffer Command Parameters Pid = Process ID of debuggee Cmd = DBG_C_ReadMemBuf Addr = Debuggee address to read from Buffer = Debugger address to copy to Len = Number of bytes to read Returns The number of bytes specified by Len is copied from the debuggee's user memory space starting at Addr into the debugger's Buffer. This command is not serialized with respect to the DBG_C_Go command. Restrictions You are unable to read from any memory outside user space. Both specified memory regions must be currently valid. ═══ 3.4.25. DBG_C_WriteMemBuf ═══ Debug Command 23 - Write Memory Buffer Command Parameters Pid = Process ID of debuggee Cmd = DBG_C_WriteMemBuf Addr = Debuggee address to write to Buffer = Debugger address to copy from Len = Number of bytes to write Returns The number of bytes specified by Len is copied from the debugger's Buffer into the debuggee's memory space starting at Addr. This command is not serialized with respect to the DBG_C_Go command. In the case of a write to shared read-only memory, the memory is first converted to private, and any set dynamic RAS logging points are removed from that memory, before the write is performed. Dynamic RAS logging will continue to function in that area, in the context of other processes. The area will continue to be shared by other processes, if any. In this way, breakpoints may be set in the debuggee without affecting the other modules. Restrictions You are unable to write to any memory outside user space. Both specified memory regions must be currently valid. ═══ 3.4.26. DBG_C_SetWatch ═══ Debug Command 24 - Set Watchpoint Command Parameters Pid = Process ID of debuggee Cmd = DBG_C_SetWatch Addr = Starting Address of Watchpoint Len = Length of Watchpoint, in bytes Index = Reserved, must be zero Value = Watchpoint Type and Scope The Watchpoint Type and Scope is a combination of a Scope number and a Type number. Both the Scope and Type must be specified. For example, to set a local watchpoint for either read or write access, Value should be set to (DBG_W_Local + DBG_W_ReadWrite). The Watchpoint Scopes are: DBG_W_Global (00000001h) DBG_W_Local (00000002h) The Watchpoint Types are: DBG_W_Execute (00010000h) DBG_W_Write (00020000h) DBG_W_ReadWrite (00030000h) Returns Index = Watchpoint ID Number This command sets a code or data watchpoint of the desired scope and type to cover the specified range of addresses. The Watchpoint Scope controls the context in which the watchpoint is actually effective. DBG_W_Local watchpoints are effective only in the context of the debuggee process, while DBG_W_Global watchpoints are effective in the context of any process. Both DBG_W_Local and DBG_W_Global watchpoints remain effective at interrupt time, and while executing kernel code. However, the DBG_W_Local watchpoints may miss interrupt time accesses, depending on the process context in which the interrupt occurred. Watchpoints are disabled as soon as they are hit, so that they can only be hit once. The resources used by a watchpoint will not be freed until the debugger is finally notified of the hit, or the debugger terminates. The debugger should use the DBG_C_Stop command to free resources held by any pending watchpoint hits prior to setting a watchpoint, so that these held resources will not prevent setting a new watchpoint. DBG_W_Global watchpoints should be used sparingly, as they restrict the watchpoint resources available to all processes at once. Watchpoint resources are very limited. Restrictions The watchpoints are restricted by the hardware. In the case of the 80386 processor, where debug registers are used, the available watchpoint lengths are 1, 2, and 4 bytes. The 2-byte data watchpoints must be aligned on a word boundary, and the 4-byte data watchpoints must be aligned on a doubleword boundary. DBG_W_Execute watchpoints must be exactly 1 byte in length, and they must begin on an instruction boundary to be effective. Global watchpoints are effective in v86 mode, but cannot detect DMA (direct memory access) device accesses. Global watchpoints may be set only in the shared memory region of the linear address space. Global watchpoints will remain effective even if the underlying memory has been converted to private memory via a DosDebug memory write operation. ═══ 3.4.27. DBG_C_ClearWatch ═══ Debug Command 25 - Clear Watchpoint Command Parameters Pid = Process ID of debuggee Cmd = DBG_C_ClearWatch Index = Watchpoint ID Number Returns This command clears a currently set or hit watchpoint. If the watchpoint is not currently set, an error is returned. If a debugger wishes to move a watchpoint from one location to another, it should clear the old watchpoint before setting the new one, to free any resources used by currently set watchpoints. The watchpoint will be cleared even if it is currently hit, and a notification is pending. To prevent missing the watchpoint hit in this way, you should issue the DBG_C_Stop command just before clearing the watchpoint, to pick up any pending watchpoint hit notifications. ═══ 3.4.28. DBG_C_RangeStep ═══ Debug Command 26 - Range Step Command Parameters Pid = Process ID of debuggee Tid = Thread ID of thread to range-step Cmd = DBG_C_RangeStep Value = Linear address denoting start of range (exclusive) Addr = Linear address denoting end of range (exclusive) Returns The RangeStep notification is usually returned, but any Debug notification may be returned. See DosDebug Notifications for more information. This command allows a debugger to specify a range of addresses (bounded by the linear addresses in the Value and Addr fields) through which a debuggee thread should single-step until one of the following conditions occurs:  The debuggee thread's linear EIP (instruction pointer) is outside the range.  The linear EIPs of consecutive debuggee threads are the same.  Some other notification occurs. When the DBG_C_RangeStep command returns, the register set is automatically updated to reflect the thread that detected the event. Callgates that result in a privilege level transition to ring 0 will appear to range-step as a single instruction, with the range-step continuing after the function completes. This hides ring 0 execution from debuggers. Attempting to range-step a thread that is frozen results in an error. Restrictions To accomplish callgate single-stepping, the single-step must be simulated because the flags (specifically, the TF bit) are not stored in the ring 0 callgate stack frame. Because of this, a range-step that results in leaving a ring 0 callgate will sometimes not execute any user-space code. The following range-step should function normally. Range-stepping some REP instructions may not work correctly due to errors in the 80386 processor. ═══ 3.4.29. DBG_C_Continue ═══ Debug Command 27 - Continue After an Exception Command Parameters Pid = Process ID of debuggee Tid = Thread ID Cmd = DBG_C_Continue Value = XCPT_CONTINUE_EXECUTION (0xFFFFFFFF), or XCPT_CONTINUE_SEARCH (0x00000000), or XCPT_CONTINUE_STOP (0x00716668) Returns You must issue the DBG_C_Continue command to continue after DosDebug has been given preemptive notifications or an exception notification. For such notifications, the DBG_C_Continue command is the only Debug command that will start the child process again. You can issue other Debug commands, but you must eventually issue the DBG_C_Continue command. If you issue the DBG_C_Continue command and there is no pre-existing notification or exception, the DBG_C_Continue command acts like a Debug DBG_C_Go. In single-step mode, XCPT_CONTINUE_STOP has the same effect as XCPT_CONTINUE_EXECUTION. That is, execution is always stopped after a single-step operation when DBG_N_Success is returned. Handling Preemptive Notifications The DBG_C_Continue command is used to either continue or stop the child process after a preemptive notification has been received from DosDebug. The XCPT_CONTINUE_STOP parameter can be used to stop the child process after a preemptive notification has been received. Any pending notifications will be held until execution of the child process is resumed using subsequent DosDebug commands. While the child process is stopped, you can issue other DosDebug commands, such as DBG_C_ReadMem. The XCPT_CONTINUE_SEARCH parameter allows the child process to execute until the next notification is received. The following is a list of preemptive notifications.  DBG_N_ModuleLoad  DBG_N_ModuleFree  DBG_N_NewProc  DBG_N_ProcTerm  DBG_N_ThreadCreate  DBG_N_ThreadTerm  DBG_N_AliasFree  DBG_N_Exception Handling the DBG_N_Exception Notification Note: XCPT_BREAKPOINT and XCPT_SINGLE_STEP are pre-first chance exception notifications. The XCPT_CONTINUE_STOP parameter serves two purposes. It stops the child process, and it tells DosDebug that the debugger handled the exception. The XCPT_CONTINUE_EXECUTION parameter tells DosDebug to restore the execution context of the thread that received the exception, and then continue execution of the child process. This implies that the debugger has handled the exception. The XCPT_CONTINUE_SEARCH parameter tells DosDebug to pass the exception to the exception handler because the debugger will not handle it. After receiving an exception notification other than XCPT_BREAKPOINT or XCPT_SINGLE_STEP, the DBG_C_Continue command with the XCPT_CONTINUE_SEARCH parameter resumes execution of the child process. ═══ 3.4.30. DBG_C_AddrToObject ═══ Debug Command 28 - Get Memory Object Information Command Parameters Pid = Process ID of debuggee Cmd = DBG_C_AddrToObject Addr = Debuggee Linear Address Returns Buffer = Starting address of object Len = Size of object in bytes Value = Object flags MTE = Module Table Entry handle of module if DBG_O_OBJMTE is set in the Value field object flags. The object flags are defined as follows: DBG_O_OBJMTE (10000000h) - Object is part of a module Information about the memory object containing the linear address is returned. If the linear address is not part of an object, DBG_N_Error is returned with ERROR_INVALID_OBJECT in the Value field. The Addr field will be left unchanged. ═══ 3.4.31. DBG_C_XchngOpcode ═══ Debug Command 29 - Exchange Opcode and Go Command Parameters Pid = Process ID of debuggee Tid = Thread ID of thread Cmd = DBG_C_XchngOpcode Value = Opcode 1 for Single Step Addr = Opcode 2 for Go Returns The sequence of operations for this Debug command is: 1. Replace the code at the EIP (instruction pointer) with opcode 1. 2. Single-step the thread specified by the Tid field. Do not execute other threads. If the single-step operation goes into ring 0 code, consider the single-step operation complete at the first ring 0 instruction. 3. Replace the code at the original EIP with opcode 2. 4. Issue a Debug Go command on all non-frozen threads. If an exception that DosDebug is to be notified about occurs during the single-step operation of this Debug command, opcode 2 is placed at the original EIP, and DosDebug is notified of the exception. When the debugger issues the Debug Continue command, the child process continues execution. Note: If an exception that DosDebug is not to be notified about occurs, then the DBG_C_XchngOpcode command executes as if no exception took place. If opcode 1 and opcode 2 are identical, this Debug command executes only operations 3 and 4 above. There is no need to single-step the thread specified by the Tid field. This would be a "replace opcode and go" sequence using only one DosDebug function instead of two. ═══ 3.4.32. DBG_C_LinToSel ═══ Debug Command 30 - Translate Linear Address to Selector:Offset Command Parameters Pid = Process ID of debuggee Cmd = DBG_C_LinToSel Addr = Linear address to be translated Returns Value = Selector Index = Offset The Addr field will be left unchanged. ═══ 3.4.33. DBG_C_SelToLin ═══ Debug Command 31 - Translate Selector:Offset to Linear Address Command Parameters Pid = Process ID of debuggee Cmd = DBG_C_SelToLin Value = Selector from address to be translated Index = Offset from address to be translated Returns Addr = Linear address The Value and Index fields will be left unchanged. ═══ 3.5. DosDebug Notifications ═══ For a description of the data returned with each notification, select ┌─────┬────────────────────┬───────────────────────────────────┐ │Not. │Notification Name │Description │ │No. │ │ │ ├─────┼────────────────────┼───────────────────────────────────┤ │ 0 │DBG_N_Success │Successful command completion │ ├─────┼────────────────────┼───────────────────────────────────┤ │-1 │DBG_N_Error │Error detected during command │ ├─────┼────────────────────┼───────────────────────────────────┤ │-6 │DBG_N_ProcTerm │Process termination - DosExitList │ │ │ │done │ ├─────┼────────────────────┼───────────────────────────────────┤ │-7 │DBG_N_Exception │Exception detected │ ├─────┼────────────────────┼───────────────────────────────────┤ │-8 │DBG_N_ModuleLoad │Module loaded │ ├─────┼────────────────────┼───────────────────────────────────┤ │-9 │DBG_N_CoError │Coprocessor not in use error │ ├─────┼────────────────────┼───────────────────────────────────┤ │-10 │DBG_N_ThreadTerm │Thread termination - not in │ │ │ │DosExitList │ ├─────┼────────────────────┼───────────────────────────────────┤ │-11 │DBG_N_AsyncStop │Async Stop detected │ ├─────┼────────────────────┼───────────────────────────────────┤ │-12 │DBG_N_NewProc │New Process started │ ├─────┼────────────────────┼───────────────────────────────────┤ │-13 │DBG_N_AliasFree │Alias needs to be freed │ ├─────┼────────────────────┼───────────────────────────────────┤ │-14 │DBG_N_Watchpoint │Watchpoint hit │ ├─────┼────────────────────┼───────────────────────────────────┤ │-15 │DBG_N_ThreadCreate │Thread creation │ ├─────┼────────────────────┼───────────────────────────────────┤ │-16 │DBG_N_ModuleFree │Module freed │ ├─────┼────────────────────┼───────────────────────────────────┤ │-17 │DBG_N_RangeStep │Range Step detected │ └─────┴────────────────────┴───────────────────────────────────┘ Note: References to "IP" in the data return descriptions refer to the instruction pointer address. This is the 32-bit equivalent of the CS:EIP instruction pointer, regardless of the CS selector. This is also known as a linearized instruction pointer. Some notifications (such as DBG_N_ModuleLoad and DBG_N_Watchpoint) may require multiple returns to the debugger. These additional pending notifications will be returned before the process being debugged can execute any more user code, and will be returned on the DBG_C_Go, DBG_C_SStep, or DBG_C_Stop commands. Note that more notifications might be pending at any time, so a debugger should be ready to handle any notification at any time that a DBG_C_Go, DBG_C_SStep, or DBG_C_Stop command is issued. If DosDebug returns ERROR_INTERRUPT after a command, the next notification might have been lost. If the process being debugged was executing code at that time (via a DBG_C_Go, DBG_C_SStep or DBG_C_RangeStep command), it will be stopped automatically. To prevent this, DosDebug should not be used by thread 1 while signals are being used, or the debugger should issue DosEnterMustComplete before issuing the command. ═══ 3.5.1. DBG_N_Success ═══ Debug Notification 0 - Success Notification This notification returns: Cmd = DBG_N_Success The DosDebug command was successful. Returned values depend on the command just executed. ═══ 3.5.2. DBG_N_Error ═══ Debug Notification -1 - Error Notification This notification returns: Cmd = DBG_N_Error Value = Any standard error code An error was detected while attempting a DosDebug command. Error codes are returned from DosDebug in different ways: 1. Errors can be returned via the standard method (EAX = ERROR_CODE). This is only done when the DosDebug command failed to execute at all. An example of this would be ERROR_INTERRUPT when the debugger receives a signal. If DosDebug returns a nonzero value in the EAX register, the returned Debug buffer is invalid. 2. Errors are also returned via the DBG_N_Error notification. This is used when the DosDebug command was attempted, but failed due to some internal DosDebug failure. Examples of error codes returned via this interface are ERROR_INVALID_PROCID and ERROR_INVALID_DATA. Generally, these errors are detected while in the context of the debuggee process, and may include ERROR_INTERRUPT. ═══ 3.5.3. DBG_N_ProcTerm ═══ Debug Notification -6 - Process Termination Notification This notification returns: Cmd = DBG_N_ProcTerm Value = Process Exit Code Index = Process Exit Type Addr = 0 The debuggee process is about to terminate. The debugger is still allowed to examine the debuggee's final register and memory contents at this time. Note that when the debugger has completed this examination, it should finish terminating the debuggee process with a final DBG_C_Go, DBG_C_SStep, or DBG_C_Term command. Value and Index contain the same information as that returned via a subsequent DosWaitChild call. The act of collecting this information does not terminate the process. ═══ 3.5.4. DBG_N_Exception ═══ Debug Notification -7 - General Exception Notification This notification returns:  For the pre-first chance for a breakpoint exception: Cmd = DBG_N_Exception Value = 0 (DBG_X_PRE_FIRST_CHANCE) Addr = Linear address of breakpoint Buffer = Exception number (XCPT_BREAKPOINT) (0xC000009F)  For the pre-first chance for a single-step exception: Cmd = DBG_N_Exception Value = 0 (DBG_X_PRE_FIRST_CHANCE) Addr = Linear address of instruction after Single Step Buffer = Exception number (XCPT_SINGLE_STEP) (0xC00000A0)  For the first chance for all exceptions: Cmd = DBG_N_Exception Value = 1 (DBG_X_FIRST_CHANCE) Addr = Linear address of exception Buffer = Pointer to Exception Report Record in debuggee's context Len = Pointer to Exception Context Record in debuggee's context  For the last chance for all exceptions: Cmd = DBG_N_Exception Value = 2 (DBG_X_LAST_CHANCE) Addr = Linear address of exception Buffer = Pointer to Exception Report Record in debuggee's context Len = Pointer to Exception Context Record in debuggee's context  For an invalid stack notification: Cmd = DBG_N_Exception Value = 3 (DBG_X_STACK_INVALID) Addr = Linear address of exception Buffer = Exception number The scenarios under which a debug exception is reported are pre-first, first, and last chance, and invalid stack notification. The Value field of the user debug buffer indicates the scenario. DosDebug has detected an exception (a trap or a fault) at the specified address. The exception number in the exception structure identifies the exception that was detected. Exception notifications are always returned from the context of the thread that detected the exception. That is, the exception structure reflects the state of the thread that caused the exception, at the time the exception was detected. The debugger is given a maximum of two chances to handle exceptions other than single-step or breakpoint exceptions, which have a maximum of three chances. The order of operations for handling an exception is as follows: 1. Debugger has the pre-first chance to handle the exception (for breakpoint and single-step exceptions). 2. Debugger has the first chance to handle the exception, or to invoke an exception handler if it is present. 3. Debugger has the last chance to handle the exception, or to invoke an exception handler if it is present. An exception notification is returned for all exceptions, including those raised by the user via DosRaiseException. An exception can have an informational, warning, or fatal severity. The severity is coded in the high-order three bits of the exception number for user-raised and system exceptions. The debugger may dismiss the exception by returning XCPT_CONTINUE_EXECUTION, so that the user's context is restored, and execution continues at the point where the exception occurred. Otherwise, the debugger may return XCPT_CONTINUE_SEARCH. This causes the exception to be passed to the user's exception handlers (after the debugger's first chance), or causes the default action for the exception to occur (after the debugger's last chance). For performance reasons, the single-step and breakpoint exceptions cause a "pre-first" notification. This is faster than the ordinary first exception notification. At the time of the notification, the debugger may decide if the single-step or breakpoint exception was an anticipated event. If it was anticipated, the debugger may return XCPT_CONTINUE_EXECUTION, as for an ordinary first notification. If it was not anticipated, the debugger may return XCPT_CONTINUE_SEARCH in order to raise an ordinary first notification for the single-step or breakpoint exception. With the second notification, this allows a maximum of three notifications for the single-step and breakpoint exceptions. For breakpoint exceptions, the instruction pointer (EIP) of the debuggee is decremented to point to the breakpoint instruction. Note: Do not confuse the family of floating point exceptions with the DBG_N_CoError error notification. Restrictions The error code may not be reliable in some situations for the page fault exception, due to hardware errors. ═══ 3.5.5. DBG_N_ModuleLoad ═══ Debug Notification -8 - Module Load Notification This notification returns: Cmd = DBG_N_ModuleLoad Value = MTE (Module Table Entry) handle of newly attached module Addr = 0 A module has just been loaded. This occurs either at program startup, or during a call to DosLoadModule. The newly attached module's handle is returned via Value. You can use this handle with DosQueryModuleName, or with the Debug DBG_C_NumToAddr command, for symbolic debugging. A debugger should save these handles for future reference. There may be many module attachments done at one time, but DosDebug is only able to communicate a single load during any one notification. In this case, the additional library load notifications become pending. The debugger should issue repeated DBG_C_Stop commands to be notified of these additional library loads, until Success is returned from the DBG_C_Stop command. If the DBG_C_Go, DBG_C_SStep, or DBG_C_RangeStep commands are issued instead of the DBG_C_Stop command, the pending notifications will be returned immediately, until there are no further notifications. ═══ 3.5.6. DBG_N_CoError ═══ Debug Notification -9 - Coprocessor Error Notification This notification returns: Cmd = DBG_N_CoError Value = Any standard Error Code An error was detected while attempting a DosDebug command that attempted to access a Coprocessor. DBG_N_CoError is similar to the DBG_N_Error notification, but is returned only after attempting an access to the coprocessor registers. DBG_N_CoError is returned if any one of the following occurs:  The debuggee process is emulating the coprocessor.  The specified debuggee thread has not yet attempted to use the coprocessor.  The wrong coprocessor type is used.  The requested coprocessor type is not supported or available. This notification should not be confused with any of the floating point exception notifications. ═══ 3.5.7. DBG_N_ThreadTerm ═══ Debug Notification -10 - Thread Termination Notification This notification returns: Cmd = DBG_N_ThreadTerm Value = Thread's proposed return code (from DosExit) Addr = 0 A debuggee thread is about to terminate. DosExitList processing has not yet been started. The debugger is still allowed to examine the debuggee thread' signal register contents at this time. When the debugger has completed this examination, it should finish terminating the debuggee thread with a final DBG_C_Go or DBG_C_SStep command. Value contains the return code from the thread. This is only a proposed return code passed from DosExit. Only when the process actually terminates is the return code that is passed to DosWaitChild finally known. ═══ 3.5.8. DBG_N_AsyncStop ═══ Debug Notification -11 - Asynchronous Stop Notification This notification returns: Cmd = DBG_N_AsyncStop Value = 0 Addr = 0 An Asynchronous Stop request has been detected. The asynchronous stop command is used to get the attention of some debuggee thread, so that the debugger can again control the process. Because any notification results in the debuggee's coming under control of the debugger again, the asynchronous stop notification becomes redundant in that case, and is not returned. The asynchronous stop request will be overridden and ignored if another notification can be performed instead, at the same time. An asynchronous stop notification never becomes a "pending" notification, in the sense that a library load notification becomes pending. ═══ 3.5.9. DBG_N_NewProc ═══ Debug Notification -12 - New Process Notification This notification returns: Cmd = DBG_N_NewProc Value = Process ID of the new process Addr = 0 The debuggee process has just started a child process, and that child process needs to be debugged. Note: This notification occurs only if descendant debugging was specified in the DosExecPgm call that started the process tree in which the debuggee is executing. ═══ 3.5.10. DBG_N_AliasFree ═══ Debug Notification -13 - Alias Free Notification This notification returns: Cmd = DBG_N_AliasFree Buffer = 32-bit offset to debugger alias region Addr = 0 An object that contains an aliased region is about to be freed by the debuggee. This can also occur if the underlying memory object is about to be shrunk such that the alias would span memory outside the resultant object. The alias region must be unmapped before the debugger may execute the debuggee again. The DBG_C_UnMapAlias command is the proper response to an alias free notification. If a debugger creates an alias to memory in another debugger, and that memory is itself an alias to the second debugger's debuggee, then the first debugger will not receive an alias free notification when the memory of the second debugger's debuggee is freed. The alias will be freed automatically. The second debugger will receive an alias free notification. ═══ 3.5.11. DBG_N_Watchpoint ═══ Debug Notification -14 - Watchpoint Hit Notification This notification returns: Cmd = DBG_N_Watchpoint Addr = Linearized instruction pointer of watchpoint hit Value = Process ID of process that hit the watchpoint Len = Thread ID of thread that hit the watchpoint MTE = Module Table Entry handle of process that hit the watchpoint Index = Watchpoint ID number A watchpoint has been hit. The Watchpoint ID number identifies the watchpoint that was hit. Multiple watchpoint hits become pending notifications that are returned on subsequent DBG_C_Stop, DBG_C_Go, or DBG_C_SStep commands. A watchpoint may be hit at any time, and more than one watchpoint may be hit at the same time. If a watchpoint notification is pending, the resources used by the watchpoint will not be freed until the watchpoint notification is complete, or the watchpoint is cleared. A watchpoint notification is not always returned by the same thread that caused the hit. A watchpoint may be hit by one thread, but another thread may return the notification. The thread ID of the thread that hit the watchpoint is not necessarily the value passed in the Tid field. Data Watchpoint hits are treated as faults, rather than as traps, by the 80386 processor. Therefore, the linearized instruction pointer may not point to the exact instruction that caused the fault. If a watchpoint is hit at interrupt time, the Value, Addr, MTE, and Len fields are all returned as zero. ═══ 3.5.12. DBG_N_ThreadCreate ═══ Debug Notification -15 - Thread Creation Notification This notification returns: Cmd = DBG_N_ThreadCreate Tid = Thread ID of new thread Addr = 0 A debuggee thread has just been created. The new thread has not executed any user code yet. ═══ 3.5.13. DBG_N_ModuleFree ═══ Debug Notification -16 - Module Free Notification This notification returns: Cmd = DBG_N_ModuleFree Value = MTE (Module Table Entry) handle of freed module Addr = 0 A module has just been freed. This occurs either at program termination, or during execution of the DosFreeModule. The newly attached module's handle is returned via Value. You can use this handle with DosQueryModuleName, or with the DosDebug DBG_C_NumToAddr command, for symbolic debugging. A debugger should save these handles for future reference. There may be many modules freed at one time, but DosDebug is only able to communicate the freeing of a single module during any one notification. In this case, the additional notifications of freed modules become pending. The debugger should issue repeated DBG_C_Stop commands to be notified of these additional module freeing operations, until DBG_N_Error is returned from the DBG_C_Stop command. If the DBG_C_Go or DBG_C_SStep commands are issued instead of the DBG_C_Stop command, the pending notifications will be returned immediately, until there are no further notifications. ═══ 3.5.14. DBG_N_RangeStep ═══ Debug Notification -17 - Range Step Notification This notification returns: Cmd = DBG_N_RangeStep Addr = Linearized instruction pointer at exception Value = Linearized instruction pointer of last user instruction executed The debuggee stopped range-stepping because its linearized instruction pointer was outside the original range, or because the current linearized instruction pointer is equal to the linearized instruction pointer of the previous user instruction. The DBG_N_RangeStep notification is returned independently of the DBG_N_Watchpoint notification, even though the Watchpoint fault and the RangeStep may have occurred at the exact same time. ═══ 4. Device I/O ═══ Devices used with computers include the keyboard, video display, mouse, floppy and fixed disk drives, and external systems, such as modems and printers. This chapter describes the OS/2 functions used to access and control such devices. The following topics are related to the information in this chapter:  File systems  File names  File management  Semaphores ═══ 4.1. About Device I/O ═══ OS/2 uses devices to communicate with the real world. A device is a piece of hardware used for input and output. The keyboard and screen are devices, as are serial and parallel ports. The computer's speaker, which can be made to beep using DosBeep, is a device. ═══ 4.1.1. Accessing Devices ═══ OS/2 applications usually communicate with devices through OS/2. Some devices, like the screen, have their own set of supporting functions. Most other devices can be accessed by using the standard OS/2 file system functions- DosOpen, DosRead, DosWrite, and DosClose. Using the file system functions, an application can open and access the device just as it would a disk file. Using the file system also enables applications to redirect the device's I/O stream. Sometimes however, these higher-level approaches do not suffice. For these situations, OS/2 provides several functions that interface with devices at a lower level. DosDevConfig is used to retrieve information about the devices available. DosPhysicalDisk can be used to retrieve information about a partitionable hard disk. DosDevIOCtl is used to send device-specific commands directly to a particular device driver. ═══ 4.1.2. Device Names ═══ To open a device using DosOpen, the application must supply the reserved name for that device. For example, to open the console (both keyboard and screen), you must specify the name CON. The following table shows some of the common reserved device names: Common Device Names ┌───────────┬──────────────────────────────────────────────────┐ │Device Name│Description │ ├───────────┼──────────────────────────────────────────────────┤ │CON │The system console. This device consists of both │ │ │the keyboard and the screen. You can open CON for │ │ │reading (from the keyboard), writing (to the │ │ │screen), or both. │ ├───────────┼──────────────────────────────────────────────────┤ │COM1 │Serial port 1. You can open this device for │ │ │reading, writing, or both. Other serial ports will│ │ │have names in ascending sequence-COM2, COM3, and │ │ │so on. │ ├───────────┼──────────────────────────────────────────────────┤ │PRN │The default printer port. This device corresponds │ │ │to one of the system's parallel ports, usually │ │ │LPT1. You can open it for writing but not for │ │ │reading. │ ├───────────┼──────────────────────────────────────────────────┤ │LPT1 │Parallel port 1. You can open this device for │ │ │writing but not for reading. Other parallel ports │ │ │will have names in ascending sequence-LPT2, LPT3, │ │ │and so on. │ ├───────────┼──────────────────────────────────────────────────┤ │NUL │The null device. This device provides a method of │ │ │discarding output. If you open this device for │ │ │writing, any data written to the file is │ │ │discarded. If you open the device for reading, any│ │ │attempt to read from the file returns an │ │ │end-of-file mark. │ ├───────────┼──────────────────────────────────────────────────┤ │SCREEN$ │The screen. This device can be written to but not │ │ │read from. Writing to the screen is similar to │ │ │writing to the system console. Bytes are displayed│ │ │as characters (unless the ANSI screen driver is │ │ │loaded and the character represents an ANSI escape│ │ │sequence). │ ├───────────┼──────────────────────────────────────────────────┤ │KBD$ │The keyboard. This device can be read from but not│ │ │written to. Reading from the keyboard is similar │ │ │to reading from the system console. │ └───────────┴──────────────────────────────────────────────────┘ After an application uses a device, it should close it by using DosClose. ═══ 4.1.3. Device Drivers ═══ OS/2 communicates with devices through special programs called device drivers. A device driver acts as an interface between OS/2, together with its applications, and a physical device such as the keyboard, mouse, or printer. The device driver sends data to and receives data from a device, resolving device-independent requests from applications with the device-specific attributes of the device. The primary method of communication between OS/2 and a device driver is request packets. OS/2 receives I/O requests from applications and sends data in the form of request packets to the device driver. The device driver communicates with the device either directly or through the BIOS and ABIOS interfaces. (Applications can communicate with device drivers also, by using DosDevIOCtl. See IOCtl Interface) Devices work differently depending on the device driver installed. For example, if an application writes to the system console, each byte is interpreted as a character and is displayed on the screen. If, however, the ANSI display driver is loaded, some byte sequences direct the system to carry out certain actions on the screen, such as moving the cursor or clearing the screen. These byte sequences are called ANSI escape sequences. Some devices are available to applications only if the appropriate device driver is installed. For example, an application cannot open a serial port unless a communications device driver, such as COM.SYS, has been loaded by using a DEVICE= command in CONFIG.SYS. ═══ 4.1.4. IOCtl Interface ═══ Many devices have more than one operating mode. For example, a serial port typically can operate at a variety of bit rates (sometimes called baud rates). Because the modes are unique to each device, OS/2 does not include specific functions to set or retrieve these modes. Instead OS/2 provides an I/O Control (IOCtl) interface to enable applications to control devices by communicating directly with the device driver. The IOCtl interface is a method that an application or subsystem can use to send device-specific control commands to a device driver. The IOCtl interface function for OS/2 applications is DosDevIOCtl. DosDevIOCtl provides a generic, expandable IOCtl facility. Applications send commands and data to the device driver with DosDevIOCtl. The OS/2 kernel reformats the generic IOCtl packets into request packets then calls the device driver. The device driver then carries out the specified action. IOCtl commands can be sent to both block and character device drivers. Before using DosDevIOCtl, the application or subsystem must first obtain the device handle by calling DosOpen for the device name. The opened device handle is used to specify the device the command is to go to. Refer to the Control Program Programming Reference for details of DosDevIOCtl. ═══ 4.1.5. IOCtl Commands ═══ DosDevIOCtl has many subfunctions. These are called generic IOCtl commands and typically are used to retrieve data from a device driver that is not available through standard OS/2 functions. For example, an application can use these functions to set the bit rate of a serial port or read input directly from a sector on a disk. ═══ 4.1.5.1. Category and Function Codes ═══ Each IOCtl function has a category and a function code. The category defines the type of device to be accessed. OS/2 has several predefined categories. In general, all codes in the range 0x0000 through 0x007F are reserved for predefined categories. A device driver can use additional categories, but they must be explicitly defined by the device and be in the range 0x0080 through 0x00FF. In each category, a function code defines the action to carry out, such as reading from or writing to the device and retrieving or setting the device modes. The number and meaning of each function code depend on the device driver and the specified category. ═══ 4.1.5.2. Parameter and Data Packets ═══ DosDevIOCtl uses a parameter packet and a data packet to pass information to and from the device driver. The packets can vary in format and length, depending on the IOCtl function. Simple functions might use only a single variable, while more complex functions might require a more complex data structure for the parameter packet, the data packet, or both. ═══ 4.2. Using the File System to Access Devices ═══ An application can use the OS/2 file system functions-DosOpen, DosRead, DosWrite, and DosClose-with the standard (predefined) devices. The application simply specifies the name of the device in the call to DosOpen, then uses the returned handle to read from and write to the device. When the application has finished using the device, the application should close the device handle by using DosClose. The following code fragment shows how an application can open the COM1 device (serial port 1) and write the contents of a disk file to the communications port: Uses Crt,Dos,Os2Def,Os2Base; Var AbBuf : Array[0..511] of BYTE; HfCom,HfFile : HFILE; UlAction,cbRead,cbWritten : ULONG; Begin DosOpen('COM1', hfCom, ulAction, 0, FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_READWRITE or OPEN_SHARE_DENYNONE, nil); DosOpen('testfile', hfFile, ulAction, 0, FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_READONLY or OPEN_SHARE_DENYWRITE, nil); Repeat DosRead(hfFile,abBuf,sizeof(abBuf),cbRead); DosWrite(hfCom,abBuf,cbRead,cbWritten); Until cbRead <> 0; DosClose(hfCom); DosClose(hfFile); End. Note: In this example code fragment and the ones that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred. ═══ 4.3. Using IOCtl Functions to Access Devices ═══ Many OS/2 functions communicate with devices. Usually, this communication is transparent to the application (the application has no knowledge of how the communication actually occurs). At times, however, an application requires more direct access to a device. To accommodate this need, OS/2 furnishes DosDevIOCtl. Applications can use DosDevIOCtl to send commands and data to a device driver; the device driver interprets these commands and sends the appropriate instructions to the physical device. As an example, some devices have several operating modes. A communications port can operate at one of a number of bit rates and have several data-word formats. The actual commands to set these parameters might vary, depending on the communications hardware. Named constants have been defined for the categories, functions, and commands that are passed to a device driver, to make it easier for application programmers to use DosDevIOCtl. These named constants are defined in the file BSEDEV.H. This file must be included in your application when you use the constants. This file also contains data structure definitions for the parameter and data packets commonly used with DosDevIOCtl. The following examples use the communications port to demonstrate how DosDevIOCtl works. ═══ 4.3.1. Setting Communications-Port Parameters ═══ You can use DosDevIOCtl to control the data parameters (bit rate, stop bits, parity, and data bits) of a communications port and to get the status of the COM port. The IOCTL_ASYNC category is used for communications-port control. The ASYNC_SETBAUDRATE function sets the COM port transmission rate. The ASYNC_GETCOMMSTATUS returns the COM port status-byte. ═══ 4.3.1.1. Setting the Data Rate ═══ The ASYNC_SETBAUDRATE function sets the bit rate of a communications port. The following code fragment sets the bit rate of COM1 to 9600 bits per second: Uses Crt,Dos,Os2Def,Os2Base; Var Hf : HFILE; (* File handle for the device *) UsBPS : USHORT; (* Bit rate to set the COM port to *) UlParmLen : ULONG; (* Maximum size of the parameter packet *) UlAction : ULONG; (* Action taken by DosOpen *) Ulrc : APIRET; (* Return code *) Begin UlParmLen := 2; UsBPS := 9600; ulrc := DosOpen('COM1', hf, ulAction, 0, FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_READWRITE OR OPEN_SHARE_DENYNONE, nil); ulrc := DosDevIOCtl(hf, (* Device handle *) IOCTL_ASYNC, (* Serial-device control *) ASYNC_SETBAUDRATE, (* Sets bit rate *) usBPS, (* Points at bit rate *) sizeof(usBPS), (* Maximum size of parameter list *) ulParmLen, (* Size of parameter packet *) nil, (* No data packet *) 0, (* Maximum size of data packet *) nil); (* Size of data packet *) . . (* Use the COM port here. *) . ulrc := DosClose(hf); ═══ 4.3.1.2. Getting the COM Port Transmission Status ═══ The ASYNC_GETCOMMSTATUS function get the transmission status of the specified COM port. This function has no parameter packet. The following code fragment uses the ASYNC_GETCOMMSTATUS function to get the transmission status of COM1: Uses Crt,Dos,Os2Def,Os2Base; Var Hf : HFILE; (* File handle for the device *) UcStatus : UCHAR; (* The COM port status byte *) UlStatusLen : ULONG; (* Length of status (the data packet) *) UlAction : ULONG; (* Action taken by DosOpen *) Rc : APIRET; (* Return code *) Begin rc := DosOpen('COM1', hf, ulAction, 0, FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_READWRITE or OPEN_SHARE_DENYNONE, nil); rc := DosDevIOCtl(hf, (* Device handle *) IOCTL_ASYNC, (* Serial-device control *) ASYNC_GETCOMMSTATUS, (* Get the COM status byte *) nil, (* No parameter packet *) 0, (* Maximum size of parameter packet *) nil, (* Length of parameter packet *) ucStatus, (* Data packet *) sizeof(ucStatus), (* Maximum size of data packet *) ulStatusLen); (* Length of data packet *) . . (* Use the COM port here. *) . rc := DosClose(hf); ═══ 5. Dynamic Linking ═══ This chapter describes:  Static versus dynamic linking  Dynamic link library (DLL) data  DLL initialization and termination  Building a DLL  Use of protected memory by DLLs ═══ 5.1. Static Versus Dynamic Linking ═══ Most programmers are familiar with static linking; an application calls a routine or procedure whose code is not found in the application's source file. The routine is external to your source file and is declared as such. When the source file is compiled, the compiler places an external reference for the routine in the application's object (.OBJ) file. To create the executable file (.EXE) for the application, the application's object file is linked with an object file that contains the code for the routine. The result is an EXE file that contains the application code, as well as a copy of the code for the routine. The following figure illustrates the process of building a statically linked application. My_Application.OBJ ┌────────────────────────┐ │ EXTERNAL │ │ Your_Routine │ @>@──────────────────────── │ . │ My_Application.EXE │ . │ ┌──────────────────┐ │ CALL ???; Your_Routine @>@──┐ │ . │ │ │ │ ┌──────┐ │ . │ └────────────────────────┘ @>@─│ LINK @>@───│ CALL xxx │ │ └──────┘ │ . │ Your_Library.OBJ │ │ │ ┌────────────────────────┐ │ xxx: │ Your_Routine: │ │ PUBLIC │ │ │ │ │ Your_Routine │ │ └──────────────────┘ @>@──────────────────────── │ │ │ │ │ Your_Routine: │ │ │ @>@──┘ └────────────────────────┘ Static Linking When OS/2 loads a statically linked program, all the code and data are contained in a single EXE file and the system can load it into memory all at once. The advantages and disadvantages of static linking are summarized in the following table. ┌──────────────────────────────┬──────────────────────────────┐ │Advantages │Disadvantages │ ├──────────────────────────────┼──────────────────────────────┤ │Compile in pieces │External routines built into │ │ │EXE (making EXEs larger) │ ├──────────────────────────────┼──────────────────────────────┤ │Can create libraries of │EXE cannot be changed without │ │routines that can be linked │relinking. │ │with applications. │ │ ├──────────────────────────────┼──────────────────────────────┤ │ │External routines cannot be │ │ │shared (duplicate copies of │ │ │libraries). │ └──────────────────────────────┴──────────────────────────────┘ Dynamic linking permits several applications to use a single copy of an executable module. The executable module is completely separate from the applications that use it. Several functions can be built into a DLL, and applications can use these functions as though they were part of the application's executable code. You can change the dynamically-linked functions without recompiling or relinking the application. The advantages of dynamic linking are: Reduced memory requirements Many applications can use a single DLL simultaneously. Since only one copy of the DLL is in memory, this saves memory space and, in general, the code is discardable. Simplified application modification An application can be enhanced or modified simply by changing a DLL. For example, if an application uses a DLL to support video output, several displays can be supported by different DLLs. The application can use the DLL that supports the appropriate display. Flexible software support DLLs can be used for after-market support. In the display-driver example, a new DLL can be provided to support a display that was not available when the application was shipped. Similarly, a database application can support a new data-file format with a new DLL. Transparent migration of function The DLL functions can be used by applications without any understanding of how the functions actually do their work. Future changes to the DLL are transparent to the application, as long as the input and output parameters remain the same. Multiple programming language support A function in a DLL can be used by an application written in any programming language, as long as the application understands the function's calling convention. Application-controlled memory usage Applications can make decisions about which DLL routines they want to load into memory and use. If a DLL is not used, it does not have to be loaded. DLLs can be used to implement subroutine packages, subsystems, and interfaces to other processes. In OS/2:  Some DLLs are interfaces to the kernel. The worker routines for the OS/2 API reside in the OS/2 kernel. Applications, which run at privilege level 3, usually can make direct calls to the kernel, which runs at privilege level 0. Some calls, however, must be linked through a DLL. For example, an application that calls DosOpen is linked to the DLL DOSCALL1.DDL. This library contains the definitions for some system functions. When a system function is called, OS/2 makes the call to the kernel on behalf of the application.  Some DLLs are interfaces to devices. DLL subsystems can virtualize devices and manage the device for its clients.  Some DLLs provide an open system architecture for software. Add-ons to OS/2 can be provided easily and by anyone. OS/2 provides two varieties of dynamic linking: load-time and run-time. In load-time dynamic linking, references are resolved when an application is loaded. In run-time dynamic linking, references are resolved when the application runs. ═══ 5.1.1. Load-Time Dynamic Linking ═══ Load-time dynamic linking is similar to static linking in that an application can call a routine that is not found in the application's source file. In load-time dynamic linking, however, an application is linked with a library file that contains a record that describes where the routine can be found instead of with a file that contains the code for the routine. The resulting application executable file contains this record and not a copy of the routine's code. The following figure illustrates the process of building a load-time dynamically linked application. In the example in the following figure, the LIB file contains a record that describes where the code for a set of functions can be found. In this case, the code for the function Your_Routine can be found in the file, or module, Your_Routines.DLL under the entry point name Your_Routine. (The entry point name does not have to match the function name.) The function code also can be referenced by its ordinal value. My_Application.OBJ ┌────────────────────────┐ │ EXTERNAL │ │ Your_Routine │ @>@──────────────────────── │ . │ My_Application.EXE │ . │ ┌──────────────────┐ │ CALL ???; Your_Routine @>@──┐ │ . │ │ │ │ ┌──────┐ │ . │ └────────────────────────┘ @>@─│ LINK @>@───│ CALL ??? │ │ └──────┘ │ . @>@─┐ Your_Library.LIB │ │ │ │ ┌────────────────────────┐ │ @>@────────────────── │ │ │ │ │ Reference to │ │ @>@──────────────────────── │ │ │ │ │ function name: │ │ │ Your_Routine.1 │ │ │ Your_Routine │ │ │ Your_Routine. @>@──┘ │ │ │ │ Your_Routine │ │ module name: │ │ │ │ │ Your_Routines @>@──┘ └──────────────────┘ │ │ │ entry point │ │ ordinal value: 1 │ │ entry point name │ │ Your_Routine │ @>@──────────────────────── │ │ └────────────────────────┘ Dynamic Linking When the application is loaded, the system resolves the dynamic-link references, as shown in the following figure. My_Application.EXE Your_Routine.DLL ┌─────────────────┐ ┌─────────────────┐ │ . │ │ Entry Table │ │ . │ │ 1 ─────┼──┐ │ . │ │.................│ │ │ Call ??? │─┐ │ │ │ │ . │ │ │ │ │ │ │ │ yyy: │ Your_Routine: │─┘ │ │ │ │ │ │.................│ │ │ │ │Reference to │ │ │ │ │ │ │ │ │ │Your_Routine.1 @>@──┘ └────────@>@────────┘ │Your_Routine. │ │ │ Your Routine│ │ └───────@>@─────────┘ │ │ │ └─────────────────@>@───────────────┘ │ ┌──@>@──┐ │LOAD │ └──@>@──┘ │ ┌─────────────────@>@───────────────┐ │ │ My_Application code Other code ┌─────────────────┐ ┌─────────────────┐ │ Call yyy ────┼───── yyy: │ Your_Routine │ └─────────────────┘ └─────────────────┘ Resolving Dynamic Link References If a program contains dynamically linked references, the system must process the information in the references. If the DLL already is in memory, the system adds information to the executable code so that the code can use the DLL functions. If the required DLLs are not already in memory, OS/2 searches the path specified by the LIBPATH environment variable. If the system cannot find the DLLs, it stops loading the application and reports the error. If the system finds the DLLs, it loads them. When DLLs are loaded into memory, OS/2 notifies the application where the DLL functions can be found. When a DLL is loaded into memory is determined by how the DLL was built. A DLL is built like an application, using a module-definition (.DEF) file. The CODE statement in a DEF file describes the attributes of application or DLL code. The load option for the CODE statement determines when application or DLL code is loaded: PRELOAD Code is loaded as soon as a process accesses the DLL. This leads to increased performance (in terms of accessing the DLL functions) while decreasing available memory. This option might be preferable if only one program is running. LOADONCALL Code is loaded when needed. This is the recommended choice and the default. ═══ 5.1.2. Run-Time Dynamic Linking ═══ When an application contains a reference to a DLL, the DLL is loaded into memory when the application is loaded (unless the DLL already is in memory). If the application uses functions in several DLLs, all of those DLLs are loaded into memory. Some applications might use functions in several DLLs but use only a few of them at any one time. For example, an application that supports many different printers or plotters might use functions in many DLLs but need only one of them at a time, depending on the printer or plotter the application is supporting. If the user switches to a different printer or plotter, another DLL will be used, but the first will remain in memory. Loading DLLs this way can be very wasteful. To avoid this problem, applications can take advantage of run-time dynamic linking and load and unload DLLs as they are required. The process of building a run-time dynamically linked application is similar to the process of building a load-time dynamically linked application. However, the EXE file for a run-time dynamically linked application does not contain a record describing where the external routines can be found. Instead, the application explicitly tells OS/2 when to load and free the dynamic link module. Applications load and unload DLLs and call functions whose code resides in those DLLs by: 1. Calling DosLoadModule to get a handle to the DLL module. This function also loads the DLL into memory or attaches to it, if it already is loaded. 2. Calling DosQueryProcAddr to get a pointer to a function within the DLL. 3. Calling the function indirectly through the pointer returned by DosQueryProcAddr. 4. Calling DosFreeModule to free the handle to the DLL module. When all handles to the DLL module have been freed, the DLL is unloaded from memory. An application also can request information about a DLL from the system. The application can use the DosQueryModuleHandle function to determine whether a DLL has been loaded into memory already. The DosQueryModuleName function returns full path information for the DLL file. Following are the advantages of run-time dynamic linking: Memory is consumed as needed. DLLs can be loaded and unloaded as they are used. Unused DLLs do not have to be loaded into memory, and memory can be released when the application has finished using the DLL. Applications can recover from DLL NOT FOUND. Applications can make decisions. If a load-time DLL cannot be found, the application terminates immediately. If a run-time DLL cannot be found, the application receives an error value from the DosLoadModule function, and it can use another DLL or specify a full path for the DLL. If a function is not available, the DosQueryProcAddr function returns an error value, and the application can take appropriate action. DLL and function names can be variable. An application programmer does not have to know the names of the DLLs the application will be using or the names of the functions within the DLL. The application can read the names of the DLL or the functions from a configuration file or obtain the names from user-supplied input. DLLs can be anywhere. The application can specify the full path for the DLL. Load-time DLLs must be in a directory in the path specified by the LIBPATH environment variable, but run-time DLLs can be in other directories. ═══ 5.2. DLL Data ═══ Most DLLs will contain some data. Whereas DLL code is shared by all processes that use it, DLL data can be shared or not shared by all processes that use it. Data that is specific to each process that uses the DLL (that is, to each instance of the DLL) is called instance data. Data that is shared by all processes that use the DLL is called shared or global data. The first time a process references a DLL (and it is loaded or its usage count is incremented because it is already loaded), a separate copy of the DLL's instance data is created. Modifications to the instance data for one process do not affect the instance data for any other process. The system, however, maintains only one copy of a DLL's shared data for all processes that use the DLL. Every process that uses the DLL can access the same data. If one process modifies the data (increments a count, for example), the data will be modified for all processes that use the DLL. Because changes to shared DLL data by one process are visible to the DLL code when called by another process, shared data provides DLLs with a mechanism for tracking processes that use it. This is crucial to subsystems, which are DLLs that manage resources (for example, devices, queues, and so forth). There usually is only one data and one code object, or segment, defined in a C source file. This means that a DLL that has instance and shared data is built from more than one C source file, with a default automatic data segment and with named data segments. How data is defined is determined by how the DLL is built. A DLL is built like an application, using a DEF file. The DATA statement in a DEF file describes the attributes of application or DLL data. Following is a list of the available options for the DATA statement: Options Result MULTIPLE OS/2 creates a unique copy of the automatic data segment for each process that uses the DLL. Modifications made by one process do not affect any other process. This is the default. SINGLE OS/2 creates only one automatic data segment for all processes that use the DLL. If one process modifies the data, the data will be modified for all processes that use the DLL. READWRITE Enables you to read from or write to the automatic data segment. This is the default. READONLY Enables you to read only from the automatic data segment. LOADONCALL The automatic data segment is loaded into memory as needed. This is the default. PRELOAD The automatic data segment is loaded as soon as a process accesses a DLL. ┌───────────┐ ┌───────────┐ │ Process A │ │ Process B │ └──@>@─────@>@──┘ └──@>@─────@>@──┘ │ │ │ │ │ │ │ │ ┌──┼─────┼─────────────────────────┼─────┼──┐ │ │ │ Dynamic Link │ │ │ │ │ │ Functions │ │ │ └──┼─────┼─────────────────────────┼─────┼──┘ │ │ │ │ │ │ ┌───────┐ │ │ │ │ │Shared │ │ │ │ └─────│ Data │─────────┘ │ │ └───────┘ │ │ ┌─────────┐ │ │ │Process B│ │ │ ┌─@>@───────┐ │───────────┘ │ │Process A│ │ └───────────│Instance @>@─┘ │ Data │ └─────────┘ DLL Data You also can create a DLL with both shared and instance data. For more information, see Using Shared and Instance Data ═══ 5.3. DLL Initialization and Termination ═══ A DLL is initialized and terminated by the default _DLL_InitTerm function. When a process gains access to the DLL, this function initializes the necessary environment for the DLL, including storage, semaphores, and variables. When the process frees its access to the DLL, the _DLL_InitTerm function terminates the DLL environment created for that process. The _DLL_InitTerm function is called automatically when you link to the DLL. The _DLL_InitTerm function can be executed once when the DLL is first loaded into memory, or it can be executed each time a new process first accesses the DLL. The LIBRARY statement in the module-definition file is used to specify when the _DLL_InitTerm function is to be executed. Following is a list of of the available options for the LIBRARY statement. Options Result INITINSTANCE The _DLL_InitTerm function is called the first time the DLL is loaded for each process that accesses the DLL. INITGLOBAL The _DLL_InitTerm function is called only the very first time the DLL is loaded. This is the default. TERMINSTANCE The _DLL_InitTerm function is called the last time the DLL is freed for each process that accesses the DLL. As an example, the following statement, identifies the executable file as a DLL and specifies that SAMPLE03 is the name of the DLL: LIBRARY SAMPLE03 INITINSTANCE TERMINSTANCE It also specifies that the _DLL_InitTerm function is to be called the first time the DLL is loaded for each process that calls the DLL and the last time the DLL is freed for each process that calls the DLL. When OS/2 starts executing a DLL, it sets the CPU registers to known values, but only for 16-bit DLLs. All 32-bit DLLs are called with a stack frame, like all other API calls. Initialization/termination functions can be written in a high-level language. For more information on writing your own initialization/termination function, see Creating an Initialization/Termination Function. ═══ 5.4. Building DLLs ═══ Building a DLL is not very different from building a conventional static library. The following sections show how you can use OS/2 tools and functions to create, manage, and use DLLs. ═══ 5.4.1. External Function References ═══ When you compile or assemble an application, the compiler or assembler generates an object module for the code in the application. If you use any functions that are external to your application (their code is in another object module), the compiler or assembler adds an external function reference to your application's object module. The Linker resolves these external references. If the Linker finds the external function in a library called an import library or in an IMPORTS statement in the application's module-definition file, the code for the external function is in a DLL. To resolve external references to DLLs, the Linker simply adds information to the executable file that tells the loader where to find the DLL code when the application is loaded. ═══ 5.4.2. Module-Definition Files ═══ The module-definition file is an important tool for building DLLs. This file contains information that tells OS/2 the name of the DLL, when to load the DLL, how to manage memory for the DLL, and when to initialize the DLL. When you create a DLL, the module-definition file must contain a list of all the functions in the DLL that can be called by an application (or by another DLL). You specify these external functions by using an EXPORTS statement in the module-definition file. You also must tell the Linker where to find the external functions in your application. If the functions are in a DLL, you can use an IMPORTS statement in the module-definition file for the application to tell the Linker where to find the DLL functions. You also can use an import library to tell the Linker where to find your DLL functions. ═══ 5.4.3. Import Libraries ═══ A conventional library contains object modules for a number of functions. The library is a convenient way to manage a large number of modules and use them in your executable code by linking to the library. The Linker uses the external references in your object module to determine which modules must be pulled out of the library. An import library does not contain any object modules. Instead, the import library contains information that tells the Linker what DLLs are used by your application and the location of the functions your application uses within each DLL. Like a conventional library, an import library primarily is a convenience. Instead of specifying all the functions your application imports in its module-definition file, you can link with the import library and let the Linker resolve the external references in your object module. You use import libraries every time you compile and link a program that uses the OS/2 API. All the OS/2 functions are implemented in DLLs, and OS2386.LIB is an import library that tells the Linker where to find each OS/2 function. For more information about module-definition files and import libraries, see the online Tools Reference in the OS/2 Warp Developer's Toolkit. ═══ 5.4.4. Creating a Simple DLL ═══ DLLs typically are used to provide common functions that can be used by a number of applications. The following figure shows a C source file, MYPUTS.C, for a DLL that contains a simple string-printing function. Uses Crt,Dos; Var hf_stdout : byte; Procedure MyPuts(pszmsg : PSZ); Var UlWritten : ULONG; hfstdout : BYTE; Begin hfstrout := 1; While pszmsg <> 0 Do Begin DosWrite(HF_STDOUT, pszMsg,1,ulWritten); Inc(pszMsg); End; End; The following figure shows the module-definition file, MYPUTS.DEF, for this DLL. LIBRARY myputs DATA SINGLE SHARED EXPORTS myPuts The LIBRARY statement names the DLL (MYPUTS.DLL). The DATA statement tells the system that this DLL will share all data with each process that uses the DLL. The EXPORTS statement indicates that the function myPuts can be used by applications and DLLs. The DLL is compiled and linked like any application. You can use IBM's ICC and LINK386, as shown below, to create MYPUTS.DLL. icc /C+ /Ge- myputs.c link386 /noi myputs, , nul, OS2386, myputs When the DLL has been created, you must copy it to one of the directories indicated by the LIBPATH environment variable in your CONFIG.SYS file. ═══ 5.4.5. Importing DLL Functions ═══ After you create a DLL, you can use it in an application. The following figure shows a C source file, USEPUTS.C, that uses the myPuts function contained in the DLL MYPUTS.DLL. VOID EXPENTRY myPuts(PSZ); Begin myPuts('Testing, 1,2,3'); End. The module-definition file for USEPUTS.C tells OS/2 where to find the myPuts function. This module-definition file (USEPUTS.DEF) contains the information shown in the following figure. NAME useputs IMPORTS myputs.myPuts The module-definition file tells OS/2 that USEPUTS imports the myPuts function from MYPUTS.DLL. USEPUTS.C is compiled and linked as shown below. icc /C+ useputs.c link386 /noi useputs, , nul, , useputs ═══ 5.4.6. Using an Import Library ═══ You also can create an import library for your DLL. If you do this, you can link applications with your DLL without explicitly declaring the imports for each application. OS/2 uses this technique for the application programming interface (API). When you link your applications with OS2386.LIB, you are using an import library. To create the import library STRINGS.LIB from MYPUTS.DLL, you use the Import Library Manager (IMPLIB), as shown below. implib strings.lib myputs.def You then can link your applications with STRINGS.LIB to resolve references to the myPuts function, as shown below. link386 /noi useputs, , nul, strings; A module-definition file for USEPUTS.C is optional in this example because we are linking with an import library. ═══ 5.4.7. Using Shared and Instance Data ═══ When you create a DLL, you can use the DATA statement in the module-definition file to define the default attributes for data segments within the DLL. The default condition is for the DLL to have a unique copy of the automatic data segment for each process. You can specify DATA MULTIPLE READWRITE in the module-definition file to cause OS/2 to create a separate copy of all the DLL data for each process that uses the DLL (instance data). Modifications made by one process do not affect other processes. You also can specify different attributes for different sets of data by using the #pragma data_seg and #pragma alloc_text directives to define your own data and code segments. You can list the segments in the module-definition file under the heading SEGMENTS and specify attributes for each. SEGMENTS mydata SINGLE READONLY mycode PRELOAD Any segments that you do not specify under SEGMENTS are given the attributes specified by the DATA or CODE statement, depending on the type of segment. ═══ 5.4.8. Creating an Initialization/Termination Function ═══ It might be necessary for a DLL to perform some tasks before an application accesses a DLL or after an application finishes accessing a DLL. For example, the library might need to allocate a heap or open a device prior to using a DLL, or deallocate a heap or close a device after using a DLL. You can handle these tasks in an initialization/termination function. The initialization/termination function can be called to perform initialization tasks when the DLL is first loaded or each time a new process accesses the DLL, depending on the LIBRARY statement in the module-definition file. If you specify INITGLOBAL in the LIBRARY statement, the initialization/termination function is called only once, when the DLL is first loaded into memory. This is the default setting. If you specify INITINSTANCE, the library function is called each time the DLL is accessed by a new process. In the same way, the initialization/termination function can be called on to perform termination tasks. If you specify TERMINSTANCE, the library function is called each time the DLL is freed for each process that accesses the DLL. When a thread calls DosLoadModule to load a DLL, the initialization routines of the loaded DLL (and the initialization routines of the DLLs that it loads) will run on the thread that called DosLoadModule. This initialization will complete before DosLoadModule returns. The prototype for the _DLL_InitTerm function is: Function _DLL_InitTerm(modhandle, flag : longint) : longint; If the value of the flag parameter is 0, the DLL environment is initialized. If the value of the flag parameter is 1, the DLL environment is ended. The modhandle parameter is the module handle assigned by OS/2 for this DLL. The module handle can be used as a parameter to various OS/2 API calls. For example, DosQueryModuleName can be used to return the fully-qualified path name of the DLL, which tells you where the DLL was loaded from. The return code from _DLL_InitTerm tells the loader whether the initialization or termination was performed successfully. If the call was successful, _DLL_InitTerm returns a nonzero value. A return code of 0 indicates that the function failed. If a failure is indicated, the loader will not load the program that is accessing the DLL. Before you can call any C library functions, you must first initialize the C run-time environment. To initialize the environment, use the function _CRT_init. The prototype for this function is: function _CRT_init : integer; If the run-time environment is successfully initialized, _CRT_init returns 0. A return code of -1 indicates an error. If an error occurs, an error message is written to file handle 2, which is the usual destination of stderr. To properly terminate the C run-time environment, use the function, _CRT_term. The prototype for this function is: !!!MANUAL@error@ Because _DLL_InitTerm is called by OS/2, it must be compiled using the system linkage. In the IBM C Set/2 compiler, the following #pragma directive is used to specify the system linkage: !!!MANUAL@error@ The initialization/termination function must have a specific entry point. You cannot create a function with a specific entry point in the C programming language, so the initialization function must be written in assembly language. However, you can write a very simple initialization function in assembly language and have it immediately jump to a C function. The following figure shows an Assembler language initialization function entry point. PAGE ,132 TITLE DLLSTUB NAME DLLSTUB .386 .387 EXTERN _DLL_InitTerm:NEAR END _DLL_InitTerm The following figure shows a sample initialization/termination function written in C. This code was written using the IBM C Set/2 compiler. If you use another compiler, some of the #pragmas or keywords might need to be changed. !!!MANUAL@error@ You also can write the initialization/termination function entirely in assembly language, without jumping to a C function. For this case, the library initialization registers are defined as follows: EIP Library entry address ESP User program stack CS Code selector for base of linear address space DS =ES=SS Data selector for base of linear address space Note: All 32-bit protected memory library modules will be given a GDT selector in the DS and ES registers (ProtDS) that addresses the full linear address space available to an application. This selector should be saved by the initialization routine. Non-protected memory library modules will receive a selector (FlatDS) that addresses the same amount of linear address space as an application's EXE file. FS Data selector of the base of the Thread Information Block (TIB) GS Is equal to 0 EAX=EBX Is equal to 0 ECX=EDX Is equal to 0 ESI=EDI Is equal to 0 EBP Is equal to 0 [ESP+0] Return address to system, and EAX = return code [ESP+4] Module handle for the library module [ESP+8] Is equal to 0 (for initialization) A 32-bit library can specify that its entry point address is the 16-bit object code. In this case, the entry registers are the same as for entry to a library using the segmented EXE format. This means that a 16-bit library can be relinked to take advantage of the benefits of the linear EXE format (such as more efficient paging). The library termination registers are defined as follows: EIP Library entry address ESP User program stack CS Code selector for the base of the linear address space DS=ES=SS Data selector for the base of the linear address space FS Data selector of the base of the Thread Information Block (TIB) GS Is equal to 0 EAX=EBX Is equal to 0 ECX=EDX Is equal to 0 ESI=EDI Is equal to 0 EBP Is equal to 0 [ESP+0] Return address to the system [ESP+4] Module handle for the library module [ESP+8] Is equal to 1 (for termination) Note: Library termination is not permitted for libraries with 16-bit entries. ═══ 5.4.9. Linking at Runtime ═══ So far, the examples in this chapter have used load-time dynamic linking. With load-time linking, OS/2 loads the DLL containing the imported functions when it loads the EXE file. If it cannot find the necessary DLL, it terminates the application and reports the error. Run-time dynamic linking permits an application to load a DLL into memory when it is required, and to remove the DLL when it is no longer needed. The application uses the DosLoadModule function to load the DLL into memory (if it is not loaded already). If the system cannot find the DLL, the application receives an error value and can take appropriate action. For example, the application might use another DLL or search another directory. Once the application has loaded the DLL, it can use the DosQueryProcAddr function to obtain a pointer to the required function (or functions). The application then can use the function. When the DLL is no longer required, the application can use the DosFreeModule function to remove the DLL from memory. If there are other applications using the DLL, it remains in memory until the last application frees the DLL. An application can specify a full path for the run-time DLL. If you specify the full path name, you can have two DLLs with the same name loaded at the same time, as in C:\OS2\DLLFILE.DLL and C:\OS2\DLL\DLLFILE.DLL. If the path is not specified, OS/2 assumes the DLL has the extension .DLL and looks for the file in the directories specified by the LIBPATH environment variable. The following figure uses the run-time dynamic-linking functions to access the myPuts function in the MYPUTS.DLL dynamic link library. Uses Crt,Dos,Os2Base,Os2Def; @VOID (* EXPENTRY myPuts) (PSZ); Uses Crt,Dos,Os2Def,Os2Base; Var Hmod : HMODULE; UlErr : ULONG; SzFailName : Array[0..CCHMAXPATH] of UCHAR; Begin ulErr := DosLoadModule(szFailName, (* failed module name *) sizeof(szFailName), (* size of buffer *) 'myputs', (* name of DLL *) hmod); (* module handle here *) If (ulErr) Then DosExit(EXIT_PROCESS, 1); ulErr := DosQueryProcAddr(hmod, (* DLL module handle *) 0, (* function ordinal value *) 'myPuts', (* function name *) myPuts); (* address of function pointer *) If not ulErr Then Begin (* We can use the function now. *) myPuts('does it work?'); DosFreeModule(hmod); (* frees the DLL module *) End; End. ═══ 5.5. Protected Memory Use ═══ OS/2 provides shared library support in the form of 32-bit DLLs. All 32-bit dynamic links or APIs are called using near CALL or RET instructions, so the cost of making dynamic-link calls should be significantly less than the cost of making the comparable calls in the 16-bit version of OS/2, where a far CALL is required. The DLLs execute in the context of the caller. All 32-bit DLLs are mapped into the appropriate shared memory region of the requesting processes at load time and execute at ring 3 without IOPL. This model's protection characteristics correspond closest to the ring 3 dynamic-linking model in the 16-bit version of OS/2. The following figure shows how 32-bit DLLs are implemented in the linear memory model of OS/2. 4G┌──────────────┐ │ System Area │ 512M@>@────────────── │ │ @>@────────────── │ 32 Bit DLL │ @>@────────@>@───── │  │ │ │ │ │ │ │ Call│ │Ret │ │Near │ │Near │ │ │ │ │ │ │ │ │ │ │  │ @>@─────@>@──────── │ 32 Bit EXE │ @>@────────────── │ │ 0└──────────────┘ A 32-Bit DLL However, since 32-bit EXE programs can address the entire address space with a 32-bit offset, it is easier for a 32-bit application programmer to potentially cast a bad pointer to data in the shared region than in the 16-bit segmented addressing scheme. Since many subsystems have semaphores and other shared data structures in the shared region, the potential for an inadvertently errant application to affect another process sharing a subsystem becomes an issue in the flat environment. Therefore, OS/2 provides a mechanism for DLLs to protect their critical shared global data regions from 32-bit EXEs. This mechanism prevents a thread in one process from potentially affecting other processes using the same resources (subsystems), or potentially taking down the entire workstation if the compromised subsystem is critical (such as PM). OS/2 enables existing 16-bit DLLs and new 32-bit DLLs to get their shared global data allocated into a single protected region that is not accessible by 32-bit EXEs, thereby achieving a level of protection. There is no provision for protecting DLLs from each other or from threads executing 16-bit EXE modules. The MEMMAN CONFIG.SYS line supports a "PROTECT/NOPROTECT" option, as follows, for enabling or disabling memory protection: MEMMAN=SWAP,PROTECT If neither PROTECT nor NOPROTECT is specified, the default is protection enabled (PROTECT). When protection is enabled, the memory manager reserves a 64M region of the linear address space below the 512MB line; this is called the protected region. Protected objects are allocated within the protected region. The following types of memory are considered protected: DLL Global Data Global data that is part of the DLL image when loaded. This is only global shared data, not instance data. Although DLL code is shared, it is not allocated in the protected region since it is read-only. DLL Run-Time Shared Data Global data that is allocated at runtime by a thread executing in DLL code that is a protected API. This includes 16- and 32-bit, named and unnamed, shared memory, and shared memory allocated with DosAllocSeg with the share flag set. The DS value that is used for the user address space (FlatDS) no longer references a descriptor with a 512MB limit. Instead the system exports another DS value for the user address space called the ProtDS that does have the 512MB limit-the FlatDS limit is reduced by the size of the protected region. When a 32-bit EXE is executing, it runs with the FlatDS and is unable to access protected objects created by 16-bit, 32-bit, or 16- and 32-bit DLLs. If the thread calls a 16-bit DLL API entry point, the DLL will have addressability to the protected region through the LDT. If the thread calls a 32-bit DLL entry point that is protected, the 32-bit DLL entry point contains code to switch to the ProtDS so that the protected region is accessible-the 32-bit DLL switches back to the FlatDS before completing service. A switch on the C compiler is used to generate the code sequence as shown in the following figure. DLLAPI proc push ds push es mov dx, seg FLAT:DGROUP mov ds, dx mov es, dx ...... pop es pop ds ret DLLAPI endp Although SS is not loaded with the ProtDS, a subsystem that switches stacks to a protected stack must write some assembler code to change ESP-thus the subsystem also should set up SS to be the ProtDS when performing the stack switch. When protection is not enabled, FlatDS=ProtDS and the code still works the same. Note: The system currently is not sensitive to whether parameters are being validated relative to the FlatDS or the ProtDS when ring 0 kernel APIs are called. Also the 3216 thunks do not probe 32-bit parameters before converting them and passing them to a 16-bit DLL. The grouping of protected allocations can be enabled or disabled on a per DLL basis. For 32-bit DLLs, the Linker uses the PROTECT parameter in the DEF file to provide protection information in the DLL's module flags to the loader. All 16-bit modules requiring protection must be specified with the new PROTECT16 CONFIG.SYS parameter. PROTECT16=DLLNAME1,DLLNAME2,...,DLLNAMEX Notice that the DLL suffix is not required. Only DLL files can get the protection. ═══ 5.6. DLL Side Effects ═══ Dynamic link routines are not processes. They run on the thread of the calling process and therefore do not own resources. Any resource that they obtain or use is owned by the calling process. Authors of DLLs should be careful not to needlessly allocate resources until the resource is required by the calling process to perform the requested function. They also should free the resource as soon as that resource is no longer required. A dynamic link routine that obtains and uses resources should attempt to minimize the use of a process's resources. For example, stack space should be conserved. If an application redirects file handle 5 and calls a DLL entry that expects file handle 5 to be an open handle to an associated device driver, unpredictable results can occur. If the routine opens an abundance of file handles, it might consider increasing the maximum number of file handles, so that the process maximum is not exceeded. However, increasing the maximum number of file handles for a process also increases the maximum number of file handles for all processes created by the current process. This will cause additional memory to be consumed and could cause problems for an application that assumes a limit of 20 file handles. Also, it should be noted that applications have the ability to redirect file handles. Dynamic link routines also should not make system calls that affect the calling process environment. If a DLL changes a process's current directory, another thread running under the same process could fail a file I/O call if it assumes a given working directory. Applications and DLLs should not make calls to other DLLs, including system DLLs, within a critical section. Since DLLs can use semaphores to synchronize threads within a process or between processes, calling a DLL within a critical section could cause application deadlocks. This would occur if the DLL requests a semaphore on behalf of the calling thread and another thread within the process owns the semaphore. Because the calling thread is in a critical section and is the only thread within the process that is permitted to execute, the semaphore will never be freed, causing a deadlock. ═══ 5.7. Summary ═══ There are two types of linking: static and dynamic. Static linking enables a program's code and data to be contained in a single executable file, enabling the system to load it all into memory at once. Dynamic linking permits several applications to use a single copy of an executable module, since the executable module is completely separate from the applications that use it. The advantages of dynamic linking are:  Reduced memory requirements  Simplified application modification  Flexible software support  Transparent migration of functions  Multiple programming language support  Application controlled memory usage. OS/2 provides two types of dynamic linking: load-time and run-time. In load-time dynamic linking, an application is linked with a library file that contains a record that describes where the routine can be found instead of a file that contains the code for the routine. The DLL can be loaded as soon as a process accesses the DLL or when needed. In run-time dynamic linking, the EXE for an application does not contain a record describing where the external routines can be found. Instead, the application explicitly tells OS/2 when to load and free the dynamic link module. DLL data can be shared or not shared by all processes that use it. ═══ 6. Error Management ═══ Error checking and error handling is extremely important in a multitasking operating system. The conditions in which an application is executing can change at any time due to the activity of other programs executing concurrently with the application. This section describes the functions that an application can use to manage errors that occur during processing. The following topic is related to the information in this scetion:  Exception management ═══ 6.1. About Error Management ═══ Successful completion of most Control Program functions is indicated by an error return code of 0. In the event of an error, Control Program functions usually return an error code that has a non-zero integer value. The non-zero value equates to a symbolic error identifier in the include file, BSEERR.H. The symbolic identifiers indicate the cause of the error. For example, a return code of 2 from DosOpen equates to the symbolic identifier ERROR_FILE_NOT_FOUND; the cause of the error is that the file being opened cannot be found. DosErrClass and DosError are supplied to assist in error processing.  DosErrClass takes as input a non-zero return value that was received from any control-program function. (Any return value other than 0 indicates that an error occurred.) The output is a classification of the error and a recommended action. Depending on the application, the recommended action could be followed, or a specific recovery action could be performed.  DosError enables an application to prevent OS/2 from displaying a default error message in a pop-up window when either a hard error or a software exception occurs. ═══ 6.1.1. Classifying Return Values ═══ When a control-program function has been successfully completed, a return value of 0 is returned to the calling thread. A non-zero return value indicates that an error has occurred. Each non-zero value corresponds to a symbolic error identifier that indicates the cause of the error. For example, a return value of 2 from DosOpen (indicating that the file was not found) corresponds to the symbolic identifier ERROR_FILE_NOT_FOUND. DosErrClass helps applications deal with non-zero return values by taking a return value as input and returning both an error classification and a recommended action. Depending on the application, the recommended action could be followed, or a more specific recovery routine could be executed. ═══ 6.1.2. Disabling Error Notification ═══ A hard error is typically an error (such as the opening of a disk-drive door while a diskette is being read, or any similar kind of device error) that cannot be resolved by software. When a hard error occurs, the system default action is to prompt for user input by displaying a message in a pop-up window. DosError disables the default action, foregoing the displayed message and causing an appropriate return value to be returned to whichever control-program function was running when the hard error occurred. The application must determine the appropriate response by referring to the return value. DosError also enables the application to disable end-user notification if either a program exception or an untrapped numeric-processor exception occurs. However, if one of these exceptions occurs while user notification is disabled, the application will still be ended. As with hard errors, the system default is that user notification for these exceptions is enabled. ═══ 6.2. Using Error Management ═══ OS/2 supplies DosErrClass and DosError for error processing. DosErrClass aids in determining the appropriate action that an application should take in response to an error. DosError enables applications to disable the pop-up windows used by OS/2 to inform the user of a hard-error or an exception. Note: In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred. ═══ 6.2.1. Classifying Errors ═══ DosErrClass receives a non-zero return value from another control-program function as input. It then classifies the return value, tells where in the system the error occurred, and recommends a corrective action. In the following example, an attempt is made to delete a nonexistent file. The return value is then passed to DosErrClass so that more information about the error can be obtained, including any corrective actions that can be taken. Uses Crt,Dos,Os2Def,Os2Base; Var UlError : ULONG; UlClass : ULONG; UlAction : ULONG; UlLocus : ULONG; Ulrc : APIRET; Begin ulError := DosDelete(FILE_DELETE); (* File name path *) ulrc := DosErrClass(ulError, (* Return value to be analyzed *) ulClass, (* Error classIfication *) ulAction, (* Recommended corrective action *) ulLocus); (* Where the error occurred *) End. When called by a family-mode application, this function can return a valid error classification only for errors that have actually occurred. Also, the classifications of a given return value might not be the same for family-mode and OS/2-mode applications. ═══ 6.2.2. Disabling Hard-Error and Exception Messages ═══ DosError disables or enables end-user notification of hard errors, program exceptions, or untrapped, numeric-processor exceptions. In the following example, pop-up windows for hard errors and exceptions are disabled, then enabled again. (*************************************************) (* use pre-defined constants *) (* FERR_DISABLEHARDERR ($00000000) *) (* FERR_ENABLEHARDERR ($00000001) *) (* FERR_ENABLEEXCEPTION ($00000000) *) (* FERR_DISABLEEXCEPTION ($00000002) *) (* to create constants needed for DosError calls *) (*************************************************) Uses Crt,Dos,Os2Def,Os2Base; Var Ulrc : APIRET; (* Return code *) Begin ulrc := DosError(DISABLE_ERRORPOPUPS); (* Action flag for disable *) ulrc := DosError(ENABLE_ERRORPOPUPS); (* Action flag for enable *) End. The action to take is encoded as a binary flag. The following table shows the bit-values and their meanings. Bit Values to Enable and Disable Hard-Error and Exception Pop-up Messages ┌─────┬─────┬──────────────────────────────────────────────────┐ │Bit │Value│Meaning │ ├─────┼─────┼──────────────────────────────────────────────────┤ │0 │1 │Enables hard-error pop-up messages. │ ├─────┼─────┼──────────────────────────────────────────────────┤ │0 │0 │Disables hard-error pop-up messages. │ ├─────┼─────┼──────────────────────────────────────────────────┤ │1 │0 │Enables exception pop-up messages. │ ├─────┼─────┼──────────────────────────────────────────────────┤ │1 │1 │Disables exception pop-up messages. │ └─────┴─────┴──────────────────────────────────────────────────┘ If DosError is not called, user notification for hard errors and exceptions is enabled by default. ═══ 7. Exception Management ═══ An exception is an abnormal condition that can occur during program execution. Common causes of exceptions include:  I/O errors  Protection violations  Math errors  Intervention by the user or by another process Activities that can cause exceptions include:  Trying to use memory that you do not have permission to access  Dividing by 0  The user pressing Ctrl+Break Exceptions include both unexpected errors (such as a memory protection violation) and expected errors (such as guard-page exceptions). Exceptions can be a synchronous exception, that is, caused by an action of the executing thread, or an asynchronous exception, caused by an event external to the executing thread (such as the user pressing Ctrl+Break). When an exception is caused by the user pressing Ctrl+Break or Ctrl+C, or by another process issuing DosKillProcess for your process, the exception is called a signal exception. In most cases, the default action taken by OS/2 when an exception occurs is to terminate the application that caused the exception. Rather than having OS/2 default action occur, an application can register its own subroutine to handle exceptions. These routines are called exception handlers. Exception handlers enable an application to handle some errors itself, allowing the application to avoid termination (or at least to terminate gracefully). When exception handlers are registered, they are added to an exception handler chain. The chain starts empty and each new handler is added to the head of the chain. Exceptions are passed to the exception handlers in the chain in Last-In-First-Out order, so the last exception handler to be registered is the first one to get an opportunity to handle each exception. Exception handlers have the capability to complete critical code sections without being interrupted by other asynchronous exceptions; these critical code sections are called must-complete sections. Exception handlers can be removed from the exception handler chains with DosUnsetExceptionHandler. Another way that exception handlers can be removed from the chain is with an unwind operation. When unwinding an exception handler, the exception handler is first called, then removed from the exception handler chain. The following topics are related to the information in this chapter:  Memory  Program execution and control ═══ 7.1. About Exception Management ═══ A multitasking operating system must manage applications carefully. A serious error (such as an attempt to access protected memory) occurring in one application cannot be permitted to damage any other application in the system. To manage errors that might damage other applications, OS/2 defines a class of error conditions called exceptions and defines default actions for those errors. When an exception occurs, the default action taken by OS/2 is usually to terminate the application causing the exception (unless the application has registered its own exception handling routines). In some cases, when the exception can safely be ignored, execution is allowed to continue. Rather than having OS/2 default action occur, an application can register its own exception handlers routines. An exception handler routine could be written to correct certain error conditions-when these error conditions occur, the thread's exception handler gets the exception, corrects the condition, and the thread continues executing rather than being terminated immediately by OS/2. OS/2's default action is taken if there are no user-defined exception handling routines or if all user-defined routines return without handling the exception. An application can use DosSetExceptionHandler to register an exception handling routine. DosSetExceptionHandler takes a pointer to an EXCEPTIONREGISTRATIONRECORD data structure as its only argument. The first field in this data structure is a pointer to the previous exception handler in the chain. This field is maintained by OS/2 and must never be modified by an application. The second field is a pointer to the exception handling routine that will be registered by OS/2. A single exception handler can be used to handle all the exceptions that you choose to handle. It is not necessary to have a separate exception handler for each exception. Once an exception handling routine is registered, the system will notify it when an exception occurs. OS/2 sends synchronous exceptions only to the thread causing the exception. An application must register an exception handler for each thread that is handling exceptions. When OS/2 terminates an application, however, a process-termination exception is sent to all threads used by the application to be terminated. When the user presses Ctrl+Break, an asynchronous signal exception is sent only to Thread 1, the main thread, of the executing process. The exception handling routine is passed the following four parameters that provide exception-specific information: EXCEPTIONREPORTRECORD Describes the exception and its parameters. The first field of this data structure contains the number of the exception that occurred. EXCEPTIONREGISTRATIONRECORD The EXCEPTIONREGISTRATIONRECORD data structure used to initially register the exception handler. This is a microprocessor-specific value. ContextRecord Describes the machine state at the time the exception occurred. DispatcherContext Contains state information on nested exception and collided unwinds. This information must not be modified by the application. Details of the parameters and data structures can be found in Exception Handler Interface. OS/2 places the exception handlers for each thread in an exception handler chain. Registering an exception handler adds it to the head of the chain. When an application registers an exception handler, the exception handler is added to the head of the chain. If the application calls a routine in a dynamic link library (DLL), the DLL might register an exception handler in case there is an exception while its code is executing; the DLL deregisters the exception handler before returning control to the application. The DLL's exception handler would be ahead of the application's exception handler in the chain. Exception handlers in the chain are notified of an exception in Last-In-First-Out (LIFO) order. Thus, if an exception occurs while your thread is executing, the exception handler for your thread is notified of the exception first. If your exception handler chooses to handle the exception, the earlier exception handlers in the chain never see the exception. If your exception handler chooses not to handle the exception, it is passed along to the next earlier exception handler in the chain. If no exception handler in the chain chooses to handle the exception, OS/2 takes the default action for the exception. If an exception happens while DLL code is executing, and if the DLL's exception handler chooses to handle the exception, your application's exception handlers will never be aware it. ═══ 7.1.1. System Exceptions ═══ OS/2 defines a class of error conditions called system exceptions, and specifies the default actions that are taken when these system exceptions occur. The default action taken by OS/2 in most cases is to terminate the thread that caused the system exception. System exceptions include both synchronous and asynchronous exceptions. Synchronous exceptions are caused by events that are internal to the execution of a thread. For example, synchronous exceptions could be caused by invalid parameters, or by the request of a thread to end its own execution. Asynchronous exceptions are caused by events that are external to the execution of a thread. For example, an asynchronous exception can be caused by a user entering a Ctrl+C or Ctrl+Break key sequence, or by a process calling DosKillProcess to end the execution of another process. The Ctrl+Break, Ctrl+C, and DosKillProcess-generated exceptions are also known as signals, or as signal exceptions. OS/2 delivers exceptions that occur in 16-bit as well as 32-bit code. The sequence or hierarchy for delivering exceptions is as follows:  When an exception occurs in 32-bit code, the system gives control only to the 32-bit exception handlers registered for the current thread. If the thread has not registered any 32-bit handlers, the system default action occurs.  When an exception occurs in 16-bit code, the system first gives control to the 32-bit exception handlers registered for the current thread. If the exception is not handled by one of these handlers, control is passed to the 16-bit handler, if one exists for the given exception. If there is no 16-bit handler for the exception, the system default action occurs. Notification of an exception is usually sent only to the thread that caused the exception. However, if a thread uses DosExit to terminate all the threads in the process, notification of the process-termination exception is sent to every thread in the process. The thread that used DosExit gets a XCPT_PROCESS_TERMINATE exception, all the other threads in the process get a XCPT_ASYNC_PROCESS_TERMINATE exception. Exit-list processing occurs on a per-process basis after a process-termination exception has been delivered to each thread in the process and each thread has finally ended except Thread 1 (the main thread). Therefore, any thread that handles a process-termination exception must eventually end its own execution voluntarily. Otherwise, the process-termination sequence will not conclude properly. The following tables briefly list the possible exceptions. For more detailed information about the system exceptions, including default system action, parameters, and related trap numbers, see the Control Program Programming Reference. Non-Fatal, Software-Generated Exceptions ┌──────────────────────────────────────┬──────────────────────┐ │Exception Symbolic Constant │Description │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_GUARD_PAGE_VIOLATION │A guard page has been │ │ │accessed. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_UNABLE_TO_GROW_STACK │The system is unable │ │ │to allocate the memory│ │ │page directly below │ │ │the guard page just │ │ │accessed. │ └──────────────────────────────────────┴──────────────────────┘ Fatal, Software-Generated Exceptions ┌──────────────────────────────────────┬──────────────────────┐ │Exception Symbolic Constant │Description │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_IN_PAGE_ERROR │An I/O error occurred │ │ │while reading a memory│ │ │page into memory. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_PROCESS_TERMINATE │The thread has │ │ │terminated itself with│ │ │DosExit. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_ASYNC_PROCESS_TERMINATE │Another thread in the │ │ │process has caused the│ │ │thread to terminate. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_NONCONTINUABLE_EXCEPTION │An exception handler │ │ │has attempted to │ │ │continue execution in │ │ │response to a │ │ │non-continuable │ │ │exception. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_INVALID_DISPOSITION │An exception handler │ │ │has returned an │ │ │invalid value. │ └──────────────────────────────────────┴──────────────────────┘ Fatal, Hardware-Generated Exceptions ┌──────────────────────────────────────┬──────────────────────┐ │Exception Symbolic Constant │Description │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_ACCESS_VIOLATION │An access violation or│ │ │page fault has │ │ │occurred. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_INTEGER_DIVIDE_BY_ZERO │An attempt to divide │ │ │by 0 has occurred in │ │ │an integer operation. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_FLOAT_DIVIDE_BY_ZERO │An attempt to divide │ │ │by 0 has occurred in a│ │ │floating point │ │ │operation. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_FLOAT_INVALID_OPERATION │An invalid floating │ │ │point operation was │ │ │attempted. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_ILLEGAL_INSTRUCTION │An attempt was made to│ │ │execute an instruction│ │ │that is not defined on│ │ │the host machine's │ │ │architecture. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_PRIVILEGED_INSTRUCTION │An attempt was made to│ │ │execute an instruction│ │ │that is not permitted │ │ │in the current machine│ │ │mode or that the │ │ │application does not │ │ │have permission to │ │ │execute. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_INTEGER_OVERFLOW │An integer operation │ │ │generated a carry-out │ │ │of the most │ │ │significant bit. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_FLOAT_OVERFLOW │A floating point │ │ │operation generated a │ │ │resulting exponent │ │ │that is greater than │ │ │the magnitude │ │ │permitted for the │ │ │operands. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_FLOAT_UNDERFLOW │A floating point │ │ │operation generated a │ │ │resulting exponent │ │ │that is less than the │ │ │magnitude provided for│ │ │the operands. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_FLOAT_DENORMAL_OPERAND │An attempt was made to│ │ │perform an arithmetic │ │ │operation on a │ │ │denormal operand. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_FLOAT_INEXACT_RESULT │The result of an │ │ │operation is not │ │ │exactly representable │ │ │in the target format. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_FLOAT_STACK_CHECK │An illegal stack │ │ │operation was │ │ │attempted by the │ │ │floating point │ │ │coprocessor. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_DATATYPE_MISALIGNMENT │An attempt was made to│ │ │store a data in an │ │ │address that is not │ │ │naturally aligned on a│ │ │hardware architecture │ │ │that does not provide │ │ │alignment hardware. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_BREAKPOINT │A breakpoint │ │ │instruction was │ │ │executed. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_SINGLE_STEP │One instruction has │ │ │been executed in │ │ │single-step mode. │ └──────────────────────────────────────┴──────────────────────┘ Fatal Exceptions ┌──────────────────────────────────────┬──────────────────────┐ │Exception Symbolic Constant │Description │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_INVALID_LOCK_SEQUENCE │An invalid operation │ │ │was attempted within │ │ │an interlocked section│ │ │of code. │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_ARRAY_BOUNDS_EXCEEDED │An array index outside│ │ │its upper and lower │ │ │boundary was detected.│ └──────────────────────────────────────┴──────────────────────┘ Unwind Operation Exceptions ┌──────────────────────────────────────┬────────────────────────────┐ │Exception Symbolic Constant │Description │ ├──────────────────────────────────────┼────────────────────────────┤ │XCPT_UNWIND │An unwind operation is in │ │ │process. │ ├──────────────────────────────────────┼────────────────────────────┤ │XCPT_BAD_STACK │An │ │ │EXCEPTIONREGISTRATIONRECORD │ │ │data structure was reached │ │ │that is not properly aligned│ │ │or that is not within the │ │ │current stack boundaries. │ ├──────────────────────────────────────┼────────────────────────────┤ │XCPT_INVALID_UNWIND_TARGET │The address of the target │ │ │EXCEPTIONREGISTRATIONRECORD │ │ │is below the current stack │ │ │pointer or not in the │ │ │exception handler chain. │ └──────────────────────────────────────┴────────────────────────────┘ Fatal Signal Exceptions ┌──────────────────────────────────────┬──────────────────────┐ │Exception Symbolic Constant │Description │ ├──────────────────────────────────────┼──────────────────────┤ │XCPT_SIGNAL │A signal was made to │ │ │your process (usually │ │ │to stop). All the │ │ │signal exceptions │ │ │(Ctrl+Break, Ctrl+C, │ │ │and │ │ │XCPT_SIGNAL_KILLPROC) │ │ │come under this │ │ │exception. │ └──────────────────────────────────────┴──────────────────────┘ ═══ 7.1.2. Signal Exceptions ═══ Signal exceptions are special events sent to a thread when the user presses certain key sequences or when another thread or process explicitly initiates the exception. There are three types of signal exceptions: XCPT_SIGNAL_BREAK When the user presses Ctrl+Break XCPT_SIGNAL_INTR When the user presses Ctrl+C XCPT_SIGNAL_KILLPROC When another process uses DosKillProcess to send a XCPT_SIGNAL_KILLPROC exception to your process. Signal exceptions are sent only to Thread 1 (the main thread) in the process receiving the exception. If an exception handler is registered on Thread 1, it must be prepared to receive signal exceptions. The thread 1 exception handler can always ignore the signal exception by returning XCPT_CONTINUE_SEARCH. If the thread 1 exception handler is to receive signal exceptions, it must use DosSetSignalExceptionFocus to notify OS/2 that it wants to receive the XCPT_SIGNAL_INTR (Ctrl+C) and XCPT_SIGNAL_BREAK (Ctrl+Break) signals. Otherwise, these exceptions are not passed to the exception handler and the default action-to terminate the process-is taken by OS/2. The thread will get XCPT_SIGNAL_KILLPROC signals whether it uses DosSetSignalExceptionFocus or not. All three of these signals are delivered by a single exception-XCPT_SIGNAL-and the exception handler for Thread 1 can choose to handle none, some, or all of the signals. The signal being sent can be determined by examining the exception information in EXCEPTIONREPORTRECORD. The following table provides information about each type of signal. Signal Exceptions ┌────────────┬──────────────────────────┬───────────────────────────┐ │Signal │Symbolic Constant │Description │ ├────────────┼──────────────────────────┼───────────────────────────┤ │Ctrl+Break │XCPT_SIGNAL_BREAK │This exception is sent to │ │ │ │Thread 1 in the current │ │ │ │keyboard-focus process when│ │ │ │a Ctrl+Break key sequence │ │ │ │is received from the │ │ │ │keyboard. The default │ │ │ │action taken by OS/2 for │ │ │ │this exception is forced │ │ │ │process termination. │ ├────────────┼──────────────────────────┼───────────────────────────┤ │Ctrl+C │XCPT_SIGNAL_INTR │This exception is sent to │ │ │ │Thread 1 in the current │ │ │ │keyboard-focus process when│ │ │ │a Ctrl+C key sequence is │ │ │ │received from the keyboard.│ │ │ │The default action taken by│ │ │ │OS/2 for this exception is │ │ │ │forced process termination.│ ├────────────┼──────────────────────────┼───────────────────────────┤ │Kill Process│XCPT_SIGNAL_KILLPROC │This exception is sent to │ │Signal │ │Thread 1 in the process │ │ │ │specified when an │ │ │ │application uses │ │ │ │DosKillProcess. The │ │ │ │XCPT_SIGNAL_KILLPROC signal│ │ │ │exception results from an │ │ │ │action external to the │ │ │ │process. The default action│ │ │ │taken by OS/2 for this │ │ │ │exception is forced process│ │ │ │termination. │ └────────────┴──────────────────────────┴───────────────────────────┘ ═══ 7.1.2.1. Handling Signal Exceptions ═══ To handle signal exceptions, a process must first call DosSetExceptionHandler to register a handler for the exceptions. Next, the process must call DosSetSignalExceptionFocus, with the Flag parameter set to ON, in order to receive signal exceptions. After a process calls DosSetSignalExceptionFocus, it remains the signal focus for its screen group until it calls DosSetSignalExceptionFocus again with the Flag parameter set to OFF, or until another process in the screen group makes a call to the same function with Flag set to ON. Each call to DosSetSignalExceptionFocus with Flag set to ON increments a counter in the per-task data area of the process. Each call with Flag set to OFF decrements the counter. When a signal exception occurs, the system checks to see whether the value of the counter is greater than 0. If it is, the signal is sent. DosSetSignalExceptionFocus returns ERROR_NESTED_TOO_DEEP if the value of the counter exceeds 65535. If a thread tries to turn off the signal focus when the value of the counter is 0, ERROR_ALREADY_RESET is returned. All 32-bit exception handlers that are attached to thread 1 of the process will be given an opportunity to handle the signal. If no 32-bit exception handler returns XCPT_CONTINUE_EXECUTION in response to the signal, and if a 16-bit exception handler is registered, then the 16-bit handler for the signal will be executed. If none exists, then the process will be terminated. In order to continue receiving signals, the process must either return XCPT_CONTINUE_EXECUTION from a 32-bit exception handler, or it must call the 16-bit DosSetSigHandler function, specifying SIG_ACKNOWLEDGE as the value of the Action parameter to acknowledge the signal, or it must call DosAcknowledgeSignalException. The typematic facility of the keyboard could cause a Ctrl+C or Ctrl+Break signal exception to repeat. For this reason, the system holds these exceptions until an exception handler returns XCPT_CONTINUE_EXECUTION, or calls DosAcknowledgeSignalException. However, only one signal exception is actually held; they are not queued by the system. See Must-Complete Sections for information about how a process can defer the handling of signal exceptions. ═══ 7.1.2.2. Sending Signal Exceptions ═══ A process can send the XCPT_SIGNAL signal exception to another process by calling DosSendSignalException. In order for the specified process to receive the exception, it must have an exception handler registered for Thread 1, and it must designate itself as the signal focus for its screen group by calling DosSetSignalExceptionFocus. Presentation Manager applications cannot request exception focus for Ctrl+C and Ctrl+Break. However, establishing an exception handler for Ctrl+C and Ctrl+Break is supported for Vio-Window and full-screen applications. ═══ 7.1.3. Raising Exceptions ═══ Asynchronous exceptions that have been deferred in a must-complete section are dispatched automatically by the system when the thread exits the must-complete section. However, a synchronous exception that has been deferred must be raised by calling DosRaiseException. DosRaiseException can also be used to simulate either an asynchronous or synchronous exception. For example, a floating point emulator (a program that emulates a numeric coprocessor) can use this function to simulate an NPX exception. Raising a software exception captures the machine state of the current thread in a ContextRecord data structure. The ExceptionAddress field of EXCEPTIONREPORTRECORD is set to the return address of the caller, as are the corresponding fields of the ContextRecord data structure. The system then calls each exception handler on the list, passing each a pointer to EXCEPTIONREPORTRECORD and the created ContextRecord data structures. In the case of a continuable exception for which XCPT_CONTINUE_EXECUTION is returned, DosRaiseException restores the potentially modified context back into the machine before returning. Note that control cannot return to the caller of DosRaiseException if the instruction pointer in ContextRecord has been modified. The caller of DosRaiseException can set the EH_NONCONTINUABLE bit in the flags field of the EXCEPTIONREPORTRECORD data structure. By doing so, the caller guarantees that it is never returned to after the call to DosRaiseException. Note that once set, the EH_NONCONTINUABLE bit cannot be modified by any exception handler. The system will enforce this. Following are some possible scenarios that might occur after a call to DosRaiseException has been made:  If one of the exception handlers returns from a continuable exception with a status of XCPT_CONTINUE_EXECUTION, DosRaiseException returns NO_ERROR to the caller, and the thread resumes execution.  If one of the exception handlers returns from a noncontinuable exception with a status of XCPT_CONTINUE_EXECUTION, the process is terminated, because it is illegal to return XCPT_CONTINUE_EXECUTION from a noncontinuable exception.  If none of the exception handlers in the thread's chain of handlers returns with a status of XCPT_CONTINUE_EXECUTION, then the action taken depends on the exception number: - If the exception number indicates a user-assigned exception or an unassigned system exception, the process is terminated. - If the exception number is assigned to a system exception, and CS:EIP points to 32-bit code, no 16-bit handlers are called and the system default action is taken. Depending on which system exception has been raised, the default action is either to terminate the process, or to continue execution of the thread with NO_ERROR returned to the caller. - If the exception number is assigned to a system exception that maps to a 16-bit exception and CS:EIP points to 16-bit code, a 16-bit exception handler is called, if one is registered. Otherwise OS/2 takes the default action. ═══ 7.1.4. User-Defined Exceptions ═══ Exceptions can also be defined by the application. These are called user-defined exceptions (as opposed to system-defined exceptions, which are those exceptions defined by OS/2). Applications can define an exception in the following fashion: The application then raises the exception, using DosRaiseException: Uses Crt,Dos,Os2Def,Os2Base; Var ERepRec : EXCEPTIONREPORTRECORD; Begin ERepRec.ExceptionNum := XCPT_YOUR_EXCEPTION; ERepRec.fHandlerFlags := 0; ERepRec.NestedExceptionReportRecord := nil; ERepRec.ExceptionAddress := nil; ERepRec.cParameters := 0; DosRaiseException(ERepRec); End. The exception handlers in the exception handler chain that are ahead of the application's exception handler will see the exception, but they will not recognize it, so they will return XCPT_CONTINUE_SEARCH. Only the application's exception handler will recognize the exception. The application's exception handler must return XCPT_CONTINUE_EXECUTION so that the exception will not continue to be passed down the exception handler chain. ═══ 7.1.5. Must-Complete Sections ═══ A thread can defer the handling of asynchronous exceptions by creating a must-complete section. A must-complete section is a section of code that cannot be safely interrupted; it must be allowed to complete its execution even if an asynchronous exception occurs while within its boundaries. For example, a must-complete section can be used:  When modifying shared-memory data structures that cannot be modified through an atomic operation  Across database update operations  During a remote communications operation. Creating a must-complete section ensures that the execution of critical instructions will be completed and that resources will be cleaned up before the thread ends. When used in conjunction with a mutual exclusion (mutex) semaphore, a must-complete section also ensures that a thread will have exclusive access to a resource. The boundaries of the must-complete section are defined by DosEnterMustComplete and DosExitMustComplete requests. While a thread is executing instructions in a must-complete section, the system will hold asynchronous exceptions, which include signal exceptions and asynchronous process terminations. The system increments a counter each time DosEnterMustComplete is called, and decrements the counter when DosExitMustComplete is called. Any asynchronous exceptions that have been held are dispatched when the counter reaches 0. A count greater than 1 indicates the degree of nesting of the must-complete section. If DosExitMustComplete is called when the count is already 0, ERROR_ALREADY_RESET is returned. The handling of synchronous system exceptions and user-defined exceptions is not deferred by the system. To defer the handling of these exceptions, a procedure typically registers an exception handler (by calling DosSetExceptionHandler) and initializes a local Raise Exception flag to 0 before entering the must-complete section. The flag is set to 1, and the information is stored, if the exception handler receives a synchronous exception that it wants to reraise later. If the value of the raise exception flag is 0 after the thread exits from the must-complete section, then no exceptions occurred, and the thread continues its normal operation. If the value of the flag is 1 after the must-complete section has been completed, then an exception occurred, and the thread must call DosRaiseException to raise the deferred exception for handling. Note: A thread must not call a function that is outside the scope of the must-complete section (for example, a DLL routine), because an error in the called routine could cause the process to end without returning. Keep must-complete sections as short as possible. ═══ 7.1.6. Unwinding Exception Handlers ═══ In addition to handling exceptions, exception handlers are used to clean up resources during the execution of a nonlocal GOTO instruction or during thread termination. (A nonlocal GOTO instruction jumps to a label outside the current procedure. The label is a procedure address or an address within a procedure that is on the stack, higher in the call frame chain.) DosUnwindException calls and removes exception handlers from a thread's chain of registered exception handlers up to, but not including, a specified exception handler. This is known as an unwind operation. DosUnwindException can also be used to unwind all exception handlers from the thread's exception handler chain and to terminate the thread. For example, with the C language setjmp() and longjmp() routines, the setjmp() would save the address of the current exception handler structure, along with any other information that is necessary to perform the longjmp() routine. (The address of the current exception handler structure is obtained from the head of the exception handler chain. A pointer to the head of the chain is located in the Thread Information Block.) The longjmp() routine would initiate the unwind of procedure call frames by calling DosUnwindException and passing to it the saved address of the EXCEPTIONREGISTRATIONRECORD data structure. If the address of the EXCEPTIONREGISTRATIONRECORD data structure is not found in the chain, then the XCPT_INVALID_UNWIND_TARGET exception is raised, and the chain is not unwound. The machine state at the time of the call to DosUnwindException is captured in ContextRecord. The EH_UNWINDING flag is set in the exception flags field of the EXCEPTIONREPORTRECORD data structure. The EH_EXIT_UNWIND flag is also set if the EXCEPTIONREGISTRATIONRECORD parameter is set to 0 (if the application does not provide its own EXCEPTIONREPORTRECORD parameter OS/2 will construct one). A backward walk through the procedure call frames is then performed to find the target of the unwind operation. Note: Even though a ContextRecord is used to capture the state of the machine, unwinding is not considered an exception. It is simply delivered through the exception mechanism. The first parameter to DosUnwindException is the address of an exception handler's EXCEPTIONREGISTRATIONRECORD. DosUnwindException will unwind exception handlers up to, but not including that exception handler. If a -1 is passed to DosUnwindException for this parameter, DosUnwindException will unwind all the exception handlers on the chain. If a 0 is passed to DosUnwindException for this parameter, DosUnwindException will unwind all the exception handlers on the chain and exit. There is no return from a call to DosUnwindException, unless the stack is invalid. Control is transferred to the specified instruction pointer address. If DosUnwindException encounters an error during its processing, it raises another exception rather than return control to the caller. If the target call frame is reached and an exit unwind is not being performed (that is, an EXCEPTIONREGISTRATIONRECORD is not 0), then the computed machine state is restored from ContextRecord and control is transferred to the address specified by the target-IP address parameter. Note that the stack pointer is not restored, making it possible to transfer information on the stack. It is the responsibility of the code at the target address to reset the stack pointer as necessary. DosUnwindException is called with C language calling conventions, which permits the use of a variable number of arguments. Thus, the caller can pass any amount of information on the stack, to be picked up at the target-IP address. If an exit unwind is being performed (the EXCEPTIONREGISTRATIONRECORD parameter is 0), then all call frames are unwound until the base of the stack is reached. If the EXCEPTIONREPORTRECORD parameter is specified, then each exception handler encountered during the unwind operation is called, using the specified record. If this parameter is not specified, then DosUnwindException constructs an EXCEPTIONREPORTRECORD that specifies the exception XCPT_UNWIND. Colliding Unwinds During an unwind operation, it is possible for one unwind to collide with a previous unwind. This occurs when the scope of the second unwind overlaps the scope of the first unwind. Following are two situations:  The target frame of the second unwind is a frame that has already been unwound by the first unwind.  The target frame of the second unwind is a valid frame that is positioned before or after the target frame of the first unwind. Either of these situations could occur during the following scenarios:  An unwind handler calls unwind, or  An unwind handler hits an exception that has called unwind. In the first scenario, the second unwind is attempting to unwind to an invalid target. This causes the exception XCPT_INVALID_UNWIND_TARGET to be raised. In the second scenario, the first unwind is abandoned, and the second unwind continues to its target. The second scenario is far more likely. Note: A user program that uses high level language exception mechanisms must never call DosUnwindException, because this could create conflicts with the runtime exception strategy of the high level language. Unwind operations in this case are performed through language-supported facilities such as the C language longjmp() routine. ═══ 7.1.7. Nested Exceptions ═══ A nested exception is an exception that occurs while another exception is being handled. OS/2 supports nested exceptions because an unhandled exception that occurs in an exception handler should be handled at a higher level-that is, by an ancestor of the procedure that registered the offending handler. When a nested exception occurs, the EH_NESTED_CALL flag is set in the exception structure to indicate that a nested function call is being made. The normal convention then is for the handler to return immediately without handling the exception if the EH_NESTED_CALL flag is set. Without this flag, it would be easy to create an infinitely recursive situation. For example, suppose we have the following scenario: 1. Procedure main calls procedure PA, which establishes exception handler HA. 2. Procedure PA calls procedure PB, which establishes exception handler HB. 3. Procedure PB calls procedure PC, which establishes exception handler HC. 4. Procedure PC calls procedure PD. Now suppose that procedure PD causes an exception. The system refers to the current thread's chain of exception handlers. Because procedure PD has no handler, the system calls HC, the handler for procedure PC, with the EH_NESTED_CALL flag clear. If handler HC returns CONTINUE_SEARCH, the system calls the next handler in the chain, handler HB, again with the EH_NESTED_CALL flag clear. Now suppose that exception handler HB causes an exception while it is processing the original exception. The call frames for the procedures are arranged in the following order on the stack: 1. Procedure main 2. Procedure PA 3. Procedure PB 4. Procedure PC 5. Procedure PD 6. OS/2's exception dispatcher 7. Procedure HB, which is the exception handler procedure 8. OS/2's exception dispatcher The system will now start traversing the exception handler chain again. Exception handler HB could have registered an exception handler, which would be the first handler in the chain. If it had registered a handler, it would be called with the EH_NESTED_CALL flag clear. The range of the nested exception is exception handlers HC and HB. The end of this range can be determined by the fact that exception handler HB is the currently active handler. These exception handlers have already been given a chance to handle the original exception. They are now about to be called again in a nested range. Therefore, when handlers HC and HB are called again, they will be called with the EH_NESTED_CALL flag set. If they do not handle the exception, then exception handler HA will be called with the EH_NESTED_CALL flag clear, because it is outside the nested range. ═══ 7.1.8. Process Exit Lists ═══ A process executes any routines registered in its exit list (with DosExitList) after the Process Termination exception has been delivered to each thread in the process and after each thread except Thread 1 has finally been terminated. If a thread handles the process termination exception, it must eventually voluntarily terminate, or the exit-list sequence will not finish running properly. Threads must not use DosCreateThread, DosExecPgm, DosStartSession, or DosExit when they are delivered a process termination exception. ═══ 7.1.9. Error Pop-Up Screens ═══ Some error conditions, such as general protection violations, cause OS/2 to display a pop-up screen containing information about the error. An application can use DosError to disable error pop-up screens. Typically, a Presentation Manager application would disable error pop-up screens if it sets up its own routines to handle errors that would ordinarily generate pop-up screens. DosError is also used to control and disable hard errors, which usually have to do with reading from and writing to disks. ═══ 7.2. Exception Handler Interface ═══ Exception handlers are passed four parameters. The interface for writing a 32-bit exception handler is: ExceptionHandler (ExceptionReportRecord, ExceptionRegistrationRecord, ContextRecord, DispatcherContext); The exception handler returns XCPT_CONTINUE_EXECUTION to indicate that the exception has been handled and is to be dismissed, or XCPT_CONTINUE_SEARCH to indicate that the exception has not been handled and is to be passed to the next exception handler on the chain. Note that there are no invalid exception numbers; if a handler does not recognize an exception number, it simply returns XCPT_CONTINUE_SEARCH. In addition to handling exceptions, exception handlers are used in unwind operations. An unwind operation simply calls and removes exception handlers from the exception handler chain of the thread. Unwind exceptions are not actually being delivered to the handlers, so the individual return codes are irrelevant, and they do not affect the unwind operation. A single exception handler can be used to handle all the exceptions that you choose to handle. It is not necessary to have a separate exception handler for each exception. A handler is not required to return to the system; it can handle the exception, and then continue thread execution directly. For example, when an application executes a longjmp(), the C language compiler adds code that essentially performs an unwind operation to clean up the stack. Execution then resumes at the point where the target setjmp() occurred. For synchronous exceptions, an exception handler can alter the contents of the interrupted thread's context, except for the fields that cannot normally be altered during thread execution. For asynchronous exceptions (signal and termination) changes made to the context of the thread are ignored. Some exceptions are continuable; if the thread's exception handler handles the exception, execution can continue. If the exception condition is such that execution cannot be continued safely, the exception is said to be noncontinuable. If an exception is noncontinuable the EH_NONCONTINUABLE bit is set in the exception structure, and it is an error to indicate the exception has been handled. Returning XCPT_CONTINUE_EXECUTION causes an XCPT_NONCONTINUABLE_EXCEPTION exception to be raised. Generally, exception handlers can use any function while they are handling an exception. However, while handling a process-termination exception, an exception handler must not call DosCreateThread, DosExecPgm, or DosStartSession, because unpredictable results can occur. A handler also must not call DosExit while handling a process-termination exception, because this request will cause the exception to be dispatched as a nested exception to the current thread's entire chain of handlers. ═══ 7.2.1. Exception Handler Parameters ═══ EXCEPTIONREPORTRECORD (EXCEPTIONREPORTRECORD) - input/output A pointer to the exception report record, which describes the exception and its parameters. EXCEPTIONREGISTRATIONRECORD ( EXCEPTIONREGISTRATIONRECORD) - input/output This is a microprocessor-specific value. For the 80386 microprocessor, this is a pointer to the exception registration record data structure that was used to register the current exception handler. ContextRecord (CONTEXTRECORD) - input/output A pointer to a context record, which describes the machine state at the time the exception occurred. DispatcherContext (DISPATCHERCONTEXT) - output A pointer to a reserved field that receives state information on nested exceptions and collided unwinds. This field returns information to either the exception dispatcher (in the case of nested exceptions) or to the unwind routine (in the case of collided unwinds). User code must not modify the DispatcherContext field at any time. When the system's exception handler is called (it is already registered by the exception dispatcher), the exception handler returns NESTED and fills in the DispatcherContext field with the address of the EXCEPTIONREGISTRATIONRECORD corresponding to the exception handler most recently called by the exception dispatcher. This indicates how far the exception dispatcher progressed through the call chain before the nesting occurred. The EH_NESTED_CALL bit is set in the EXCEPTIONREPORTRECORD flags field for each exception handler that is called between handler of the exception dispatcher and the establisher of the most recently called handler. In the case of a collided unwind, the exception handler registered by the unwind dispatcher will return COLLIDED_UNWIND and the DispatcherContext field will contain a pointer to the target frame of the current unwind. ═══ 7.2.2. Exception Management Data Structures ═══ Applications use three data structures for exception management (the DispatcherContext parameter is for system use).  EXCEPTIONREPORTRECORD data structure  ExceptionRegistrationRecord data structure  ContextRecord data structure. An overview of each of these data structures is presented below. ═══ 7.2.2.1. ExceptionReportRecord Data Structure ═══ The EXCEPTIONREPORTRECORD data structure describes an exception and any additional parameters associated with the exception. The data structure contains fields for the following information:  Exception number  Exception flags, describing exception attributes  A pointer to a nested exception report record, if any  The address where the exception occurred  Information for any additional parameters. For descriptions of the system exceptions see the Control Program Programming Reference. Following are the flags that are set to indicate exception attributes. Only the EH_NONCONTINUABLE flag can be set (but not cleared) by the user. All other flags are set by the system. EH_NONCONTINUABLE (0x1) The exception is not continuable, and any attempt to continue causes the exception XCPT_NONCONTINUABLE_EXCEPTION to be raised. EH_UNWINDING (0x2) The EXCEPTIONREPORTRECORD data structure describes an exception for which an unwind is in progress. EH_EXIT_UNWIND (0x4) An exit unwind operation implies that call frames are being unwound until the base of the stack is reached. Note that EH_UNWINDING is also set. EH_STACK_INVALID (0x8) Following are causes for this flag to be set:  The user stack exceeds the limits specified by the Thread Information Block. Applications can get the Thread Information Block by calling DosGetInfoBlocks.  A call frame exceeds the stack limits specified by the Thread Information Block.  A call frame is not aligned on the stack. This flag is set only when the EXCEPTIONREPORTRECORD is passed to an associated debugger. It is not possible to build exception information on the user's stack when the stack is invalid. EH_NESTED_CALL (0x10) EXCEPTIONREPORTRECORD describes an exception raised while the current exception handler was active. That is, a nested exception is in progress, and the current handler was also called to handle the previous exception. EXCEPTIONREPORTRECORD data structures can be chained together to provide additional information when nested exceptions are raised. ═══ 7.2.2.2. ExceptionRegisterRecord Data Structure ═══ The application is responsible for the creation and registration of the EXCEPTIONREGISTRATIONRECORD data structure. This is the data structure used by the application when it established the exception handler on the chain. The only restrictions are that each pointer in the linked list must either point directly to the next pointer in the list or contain END_OF_CHAIN (-1), and the field immediately following the pointer field must be the pointer to the exception handler code. No fields other than these two will be examined by OS/2. The application can keep any state information that it chooses in this data structure, as long as it does not alter either of the fields used by the system. When a procedure begins, it must create an EXCEPTIONREGISTRATIONRECORD on the stack, fill in the pointer to the exception handler routine, and link the data structure to the front of the exception handler chain by calling DosSetExceptionHandler. Similarly, when the procedure ends, it must remove EXCEPTIONREGISTRATIONRECORD from the chain by calling DosUnsetExceptionHandler. This maintains the necessary frame-exception handler correspondence. Note: For the benefit of assembly language programmers, the Thread Information Block (TIB) is located at FS:[0]. This speeds access to the TIB data structure. Because the FS is used to point to the TIB, applications that use the FS register must restore the original value when they are finished. Exception handling depends on the FS register pointing to the TIB. EXCEPTIONREGISTRATIONRECORD data structure must be created on the stack of the application. That is, it must be a data structure that is local to the routine that registers the exception handler. It cannot be stored in the application's data segment. The reason for this is that OS/2 must be able to determine the relative ordering of ExceptionRegistration records by examining their addresses. ═══ 7.2.2.3. ContextRecord Data Structure ═══ The ContextRecord data structure describes the machine state at the time of an exception. This data structure is hardware dependent and is not portable. Therefore, as a rule, software should not use the information contained in this data structure. However, hardware dependent code, such as math libraries, can make use of this information to optimize certain operations. For a hardware-initiated exception, ContextRecord contains the complete machine state at the time of the exception. For a software-initiated exception, ContextRecord contains the machine state at the time the software raised the exception. The ContextRecord data structure consists of fields for the following:  General purpose registers  Segment registers  The flags register  The floating point environment and stack. Note: With asynchronous exceptions (signal and termination exceptions), the context in ContextRecord is read-only. The exception handler can modify it, but the changes with be ignored. With synchronous exceptions, changes to ContextRecord will be used when the context is restored. ═══ 7.2.3. Exception Handler Return Values ═══ Exception handlers can return one of the following values: XCPT_CONTINUE_SEARCH (0x00000000) Indicates that the exception has not been handled. The system responds by passing the exception to the previously installed handler in the thread's chain of exception handlers. XCPT_CONTINUE_EXECUTION (0xFFFFFFFF) Indicates that the exception has been handled. OS/2 responds by dismissing the exception, restoring the context of the thread, and continuing the execution of the thread. ═══ 7.3. Using Exception Management ═══ When an exception occurs, the system default action in most cases is to end the application that caused the exception. Instead of having the system default action occur, an application can register its own exception handling routines. Exception handlers can be written to take corrective action so that a thread can continue running rather than being terminated by the system. If an exception is handled by the application's exception handler, the exception handler must return XCPT_CONTINUE_EXECUTION. If the application's exception handler does not handle the exception, the exception handler must return XCPT_CONTINUE_SEARCH. If all the exception handlers in the exception handler chain return XCPT_CONTINUE_SEARCH, OS/2 takes the default action, which is usually to terminate the process that caused the exception. Note: In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred. ═══ 7.3.1. Example Exception Handler ═══ This section of the chapter will present a simple exception handler. Because exception handlers are commonly used to handle memory faults, the example will show the exception handler working with a memory fault. Memory exceptions can occur when an application attempts to access a guard page, attempts to use memory that has been allocated but not committed (a sparse memory object), or when an application attempts to write to memory that has read-only access. Without an application-registered exception handler, some of these exceptions might cause the application to terminate. If the application registers its own exception handler, it can correct the cause of the memory fault and continue to run. If the application's exception handler handles the exception, it returns XCPT_CONTINUE_EXECUTION. If the routine does not handle the exception, it returns XCPT_CONTINUE_SEARCH so that the exception will be passed to the next handler in the chain. The following code fragment shows an exception handling routine set up to deal with memory errors: Uses Crt,Dos,Os2Def,Os2Base; Const HF_STDERR = 2 (* Standard error handle *) Function MyHandler(PEXCEPTIONREPORTRECORD pERepRec, PEXCEPTIONREGISTRATIONRECORD pERegRec, PCONTEXTRECORD pCtxRec, PVOID p):ULONG; cdecl; Var UlWritten : ULONG; UlMemSize : ULONG; FlMemAttrs : ULONG; Ulrc : APIRET; Begin (* Access violation at a known location *) If (pERepRec^.ExceptionNum = XCPT_ACCESS_VIOLATION) AND (pERepRec^.ExceptionAddress <> XCPT_DATA_UNKNOWN) Then Begin (* Page fault *) If (pERepRec^.ExceptionInfo[0] = XCPT_READ_ACCESS) OR ((pERepRec^.ExceptionInfo[0] = XCPT_WRITE_ACCESS) AND (pERepRec^.ExceptionInfo[1] <> XCPT_DATA_UNKNOWN)) Then Begin DosWrite(HF_STDERR,#13#10'Page Fault'#13#10,15,ulWritten); (* Now query the memory to find out why we faulted. *) ulMemSize := 1; DosQueryMem(pERepRec^.pExceptionInfo[1],ulMemSize,flMemAttrs); (* If the memory is free or committed, *) (* we have some other problem. *) (* If it is not free or not committed, commit it. *) If (not(flMemAttrs)) AND ( (PAG_FREE) OR (PAG_COMMIT) ) Then Begin DosWrite(HF_STDERR,#13#10'Attempt to access uncommitted memory'#13#10, 40,ulWritten); ulrc := DosSetMem(pERepRec^.ExceptionInfo[1],4096, PAG_DEFAULT OR PAG_COMMIT); If (ulrc) Then Begin DosWrite(HF_STDERR,#13#10'Error committing memory'#13#10, 27,ulWritten); MyHandler := XCPT_CONTINUE_SEARCH; Exit; End Else Begin MyHandler := XCPT_CONTINUE_EXECUTION; Exit; End; End; End; End; MyHandler := XCPT_CONTINUE_SEARCH; End; ═══ 7.3.2. Registering an Exception Handler ═══ An application uses DosSetExceptionHandler to register its own exception handling routines. More than one routine can be registered; the last routine registered will be called first. One or more exception handlers can be registered for each thread in a process. Moreover, exception handlers can be specified not only for system exceptions, but also for user-defined exceptions that are anticipated for a particular thread. Only Process Termination exceptions are sent to all threads in a process. Other exceptions (synchronous exceptions) are sent only to the exception handler registered for the thread where the exception occurred. The application must register an exception handler for each thread that is handling exceptions. The following code fragment shows how an application registers an exception handling routine: Uses Crt,Dos,Os2Def,Os2Base; ULONG _System myHandler(PEXCEPTIONREPORTRECORD, PEXCEPTIONREGISTRATIONRECORD, PCONTEXTRECORD, PVOID); Begin @error@ EXCEPTIONREGISTRATIONRECORD xcpthand = { 0, myHandler }; DosError(FERR_DISABLEEXCEPTION OR FERR_DISABLEHARDERR); DosSetExceptionHandler(xcpthand); (* . . Other processing occurs here; myHandler will handle the exceptions. . *) DosUnsetExceptionHandler(xcpthand); End. If a procedure registers an exception handler, it must deregister the handler by calling DosUnsetExceptionHandler before returning. Note: A procedure must not call DosSetExceptionHandler if it performs language-specific exception or unwind handling. This restriction is not enforced, but unpredictable results could occur if it is violated. DosSetExceptionHandler and DosUnsetExceptionHandler provide the portable means of implementing exception handlers. The non-portable approach is taken by directly manipulating the exception handler chain. High level languages generate code that abides by this restriction. Assembly language programmers must assume responsibility for verifying that handler registration and deregistration occur correctly. EXCEPTIONREGISTRATIONRECORD must be created on the application's stack. That is, it must be local to the routine that registers the exception handler, rather than a global variable. It cannot be stored in the data segment of the program. Note that in the code fragment above, the declaration is placed inside the braces (see figure below). Therefore xcpthand is local to the main() routine and is stored on the program's stack. @error@ EXCEPTIONREGISTRATIONRECORD xcpthand = { 0, myHandler }; ═══ 7.4. System Exceptions ═══ The operating system defines a class of error conditions called exceptions, and specifies the default actions that are taken when these exceptions occur. The system default action in most cases is to terminate the thread that caused the exception. Exception values have the following 32-bit format: 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 ┌───@>@─@>@─────────────────────────@>@───────────────────────────────┐ │Sev│C│ Facility │ Code │ └───@>@─@>@─────────────────────────@>@───────────────────────────────┘ Sev Severity code. Possible values are described in the following list: 00 Success 01 Informational 10 Warning 11 Error C Customer code flag. Facility Facility code. Code Facility's status code. Exceptions that are specific to OS/2 Version 2.X (for example, XCPT_SIGNAL) have a facility code of 1. System exceptions include both synchronous and asynchronous exceptions. Synchronous exceptions are caused by events that are internal to a thread's execution. For example, synchronous exceptions could be caused by invalid parameters, or by a thread's request to end its own execution. Asynchronous exceptions are caused by events that are external to a thread's execution. For example, an asynchronous exception can be caused by a user's entering a Ctrl+C or Ctrl+Break key sequence, or by a process' issuing DosKillProcess to end the execution of another process. The Ctrl+Break and Ctrl+C exceptions are also known as signals, or as signal exceptions. The following tables show the symbolic names of system exceptions, their numerical values, and related information fields. Portable, Non-Fatal, Software-Generated Exceptions ┌─────────────────────────────────────┬──────────┐ │Exception Name │Value │ ├─────────────────────────────────────┼──────────┤ │XCPT_GUARD_PAGE_VIOLATION │0x80000001│ │ ExceptionInfo[0] - R/W flag │ │ │ ExceptionInfo[1] - FaultAddr │ │ ├─────────────────────────────────────┼──────────┤ │XCPT_UNABLE_TO_GROW_STACK │0x80010001│ └─────────────────────────────────────┴──────────┘ Portable, Fatal, Hardware-Generated Exceptions ┌─────────────────────────────────────┬──────────┬─────────────┐ │Exception Name │Value │Related Trap │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_ACCESS_VIOLATION │0xC0000005│0x09, 0x0B, │ │ ExceptionInfo[0] - Flags │ │0x0C, 0x0D, │ │ XCPT_UNKNOWN_ACCESS 0x0 │ │0x0E │ │ XCPT_READ_ACCESS 0x1 │ │ │ │ XCPT_WRITE_ACCESS 0x2 │ │ │ │ XCPT_EXECUTE_ACCESS 0x4 │ │ │ │ XCPT_SPACE_ACCESS 0x8 │ │ │ │ XCPT_LIMIT_ACCESS 0x10 │ │ │ │ ExceptionInfo[1] - FaultAddr │ │ │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_INTEGER_DIVIDE_BY_ZERO │0xC000009B│0 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_FLOAT_DIVIDE_BY_ZERO │0xC0000095│0x10 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_FLOAT_INVALID_OPERATION │0xC0000097│0x10 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_ILLEGAL_INSTRUCTION │0xC000001C│0x06 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_PRIVILEGED_INSTRUCTION │0xC000009D│0x0D │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_INTEGER_OVERFLOW │0xC000009C│0x04 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_FLOAT_OVERFLOW │0xC0000098│0x10 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_FLOAT_UNDERFLOW │0xC000009A│0x10 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_FLOAT_DENORMAL_OPERAND │0xC0000094│0x10 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_FLOAT_INEXACT_RESULT │0xC0000096│0x10 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_FLOAT_STACK_CHECK │0xC0000099│0x10 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_DATATYPE_MISALIGNMENT │0xC000009E│0x11 │ │ ExceptionInfo[0] - R/W flag │ │ │ │ ExceptionInfo[1] - Alignment │ │ │ │ ExceptionInfo[2] - FaultAddr │ │ │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_BREAKPOINT │0xC000009F│0x03 │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_SINGLE_STEP │0xC00000A0│0x01 │ └─────────────────────────────────────┴──────────┴─────────────┘ Portable, Fatal, Software-Generated Exceptions ┌─────────────────────────────────────┬──────────┬─────────────┐ │Exception Name │Value │Related Trap │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_IN_PAGE_ERROR │0xC0000006│0x0E │ │ ExceptionInfo[0] - FaultAddr │ │ │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_PROCESS_TERMINATE │0xC0010001│ │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_ASYNC_PROCESS_TERMINATE │0xC0010002│ │ │ ExceptionInfo[0] - TID of │ │ │ │ terminating thread │ │ │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_NONCONTINUABLE_EXCEPTION │0xC0000024│ │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_INVALID_DISPOSITION │0xC0000025│ │ └─────────────────────────────────────┴──────────┴─────────────┘ Non-Portable, Fatal Exceptions ┌─────────────────────────────────────┬──────────┬─────────────┐ │Exception Name │Value │Related Trap │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_INVALID_LOCK_SEQUENCE │0xC000001D│ │ ├─────────────────────────────────────┼──────────┼─────────────┤ │XCPT_ARRAY_BOUNDS_EXCEEDED │0xC0000093│0x05 │ └─────────────────────────────────────┴──────────┴─────────────┘ Unwind Operation Exceptions ┌─────────────────────────────────────┬──────────┐ │Exception Name │Value │ ├─────────────────────────────────────┼──────────┤ │XCPT_UNWIND │0xC0000026│ ├─────────────────────────────────────┼──────────┤ │XCPT_BAD_STACK │0xC0000027│ ├─────────────────────────────────────┼──────────┤ │XCPT_INVALID_UNWIND_TARGET │0xC0000028│ └─────────────────────────────────────┴──────────┘ Fatal Signal Exceptions ┌─────────────────────────────────────┬──────────┐ │Exception Name │Value │ ├─────────────────────────────────────┼──────────┤ │XCPT_SIGNAL │0xC0010003│ │ ExceptionInfo[ 0 ] - Signal │ │ │ Number │ │ └─────────────────────────────────────┴──────────┘ ═══ 7.4.1. XCPT_ACCESS_VIOLATION ═══ Access Violation An access violation exception is generated when an attempt is made either to load or store data in an inaccessible location, or to execute an inaccessible instruction. This exception corresponds to both the Intel 80386 general protection fault (#13), caused by an invalid access attempt; and the page fault (#14), caused by an attempt to access an uncommitted page or a page with incorrect attributes for the desired operation. Exception Code: XCPT_ACCESS_VIOLATION (0xC0000005) Handler Information: The ExceptionAddress field in the ExceptionReportRecord points to the instruction that caused the exception. This exception is continuable. Default Action: The process is ended. Additional Parameters (2): Exception Info[ 0 ] Flags XCPT_UNKNOWN_ACCESS (0x00) XCPT_READ_ACCESS (0x01) XCPT_WRITE_ACCESS (0x02) XCPT_EXECUTE_ACCESS (0x04) XCPT_SPACE_ACCESS (0x08) XCPT_LIMIT_ACCESS (0x10) Exception Info[ 1 ] FaultAddr The virtual address (if available) of the data that is not accessible, or XCPT_DATA_UNKNOWN. ═══ 7.4.2. XCPT_BREAKPOINT ═══ Breakpoint A breakpoint exception occurs when a breakpoint instruction is executed. This exception is intended for use by debuggers. This exception is continuable. Exception Code: XCPT_BREAKPOINT (0xC0000006) Default Action: The process is ended. Additional Parameters: None. ═══ 7.4.3. XCPT_ARRAY_BOUNDS_EXCEEDED ═══ Bounds Check The bounds check exception corresponds to the Intel 80386 bounds check fault (#5), caused by a BOUND instruction that fails. Exception Code: XCPT_ARRAY_BOUNDS_EXCEEDED (0xC0000093) Handler Information: The CS:EIP in the exception context structure points to the instruction that caused the exception. This exception is continuable. Default Action: The process is ended. Additional Parameters: None. ═══ 7.4.4. XCPT_DATATYPE_MISALIGNMENT ═══ Data-Type Misalignment A data-type misalignment exception is generated when an attempt is made to load or store data in an address that is not naturally aligned on a hardware architecture that does not provide alignment hardware. For example, 16-bit entities must be aligned on two-byte boundaries, and 32-bit entities must be aligned on four-byte boundaries. This exception does not occur on the Intel 80386 processor. This exception is continuable. Exception Code: XCPT_DATATYPE_MISALIGNMENT (0xC000009E) Default Action: The process is ended. Additional Parameters (3): Exception Info[ 0 ] Read/Write Flag XCPT_READ_ACCESS, or XCPT_WRITE_ACCESS. Exception Info[ 1 ] Data-type Mask A data-type mask that specifies how many low-address bits must be zero. For example, the data-type mask for a 16-bit entity is one, a 32-bit entity three, and so on. Exception Info[ 2 ] Virtual Address The virtual address of the misaligned data. ═══ 7.4.5. XCPT_FLOAT_DIVIDE_BY_ZERO ═══ Floating Divide-by-Zero A floating divide-by-zero exception is generated when an attempt is made to divide a floating-point dividend by a floating-point divisor of zero. This exception is continuable. Exception Code: XCPT_FLOAT_DIVIDE_BY_ZERO (0xC0000095) Default Action: The process is ended. Additional Parameters: None. ═══ 7.4.6. XCPT_FLOAT_OVERFLOW ═══ Floating Overflow A floating overflow exception is generated when the resulting exponent of a floating-point operation is greater than the magnitude allowed for the respective floating point data type. This exception is continuable. Exception Code: XCPT_FLOAT_OVERFLOW (0xC0000098) Default Action: The process is ended. Additional Parameters: None. ═══ 7.4.7. XCPT_FLOAT_UNDERFLOW ═══ Floating Underflow A floating underflow exception is generated when the resulting exponent of a floating-point operation is less than the magnitude provided for the respective floating-point data type. This exception is continuable. Exception Code: XCPT_FLOAT_UNDERFLOW (0xC000009A) Default Action: The process is ended. Additional Parameters: None. ═══ 7.4.8. XCPT_FLOAT_INVALID_OPERATION ═══ Invalid Floating-Point Operation This exception usually indicates a programming error corresponding to the invalid floating-point operations defined in IEEE Standard 754. The Intel 80386 processor raises trap #16. This exception is continuable. Default Action: The process is ended. Exception Code: XCPT_FLOAT_INVALID_OPERATION (0xC0000097) Additional Parameters: None. ═══ 7.4.9. XCPT_FLOAT_DENORMAL_OPERAND ═══ Denormalized Operand A denormalized operand exception occurs when the 80387 NPX processor attempts an arithmetic operation on a denormal operand, and the user has not masked off denormal operations. This exception is continuable. Exception Code: XCPT_FLOAT_DENORMAL_OPERAND (0xC0000094) Default Action: The process is ended. Additional Parameters: None. ═══ 7.4.10. XCPT_FLOAT_INEXACT_RESULT ═══ Loss of Precision A loss of precision exception occurs when the result of an operation is not exactly representable in the destination format. For example, the fraction 1/3 cannot be exactly represented in binary form. For the Intel 80386 and 80387 processors, this corresponds to one of the class of exceptions for which the 80387 processor signals the 80386 processor to raise trap #16. This exception is continuable. Default Action: The process is ended. Exception Code: XCPT_FLOAT_INEXACT_RESULT (0xC0000096) Additional Parameters: None. ═══ 7.4.11. XCPT_FLOAT_STACK_CHECK ═══ Invalid Floating-Point Stack Operation An invalid floating-point stack check is raised when a floating-point processor attempts an illegal operation on a private stack. The Intel 80387 processor maintains eight internal 10-byte "registers" that are individually addressable and yet behave as a push-down stack under the influence of the FLD (push real) and FST (pop real to destination) instructions. Overflow and underflow are checked with each instruction, and this exception is raised when appropriate. This is one of the class of exceptions for which the Intel 80387 processor signals the Intel 80386 processor to raise trap #16. This exception is continuable. Exception Code: XCPT_FLOAT_STACK_CHECK (0xC0000099) Default Action: The process is ended. Additional Parameters: None. ═══ 7.4.12. XCPT_ILLEGAL_INSTRUCTION ═══ Illegal Instruction An illegal instruction exception is generated when an attempt is made to execute an instruction whose operation is not defined for the host machine architecture. On the Intel 80386 processor, this corresponds to the invalid opcode fault (#6), caused by any invalid instruction. This exception is continuable. Exception Code: XCPT_ILLEGAL_INSTRUCTION (0xC000001C) Default action: The process is ended. Additional Parameters: None. ═══ 7.4.13. XCPT_PRIVILEGED_INSTRUCTION ═══ Privileged Instruction A privileged instruction exception is generated when an attempt is made to execute an instruction whose operation is not allowed in the current machine mode. For example, an attempt is made to execute an instruction in user mode that is only allowed in kernel mode. This exception is continuable. Exception Code: XCPT_PRIVILEGED_INSTRUCTION (0xC000009D) Default Action: The process is ended. Additional Parameters: None. ═══ 7.4.14. XCPT_INVALID_LOCK_SEQUENCE ═══ Invalid Lock Sequence An invalid lock sequence exception is generated when an attempt is made to execute an operation within an interlocked section of code, and the sequence is invalid for the host machine architecture. This exception is continuable. Exception Code: XCPT_INVALID_LOCK_SEQUENCE (0xC000001D) Default Action: The process is ended. Additional Parameters: None. ═══ 7.4.15. XCPT_INTEGER_DIVIDE_BY_ZERO ═══ Integer Divide-by-Zero An integer divide-by-zero exception is generated when an attempt is made to divide an integer dividend by an integer divisor of zero. On the Intel 80387 processor, this is a divide by zero fault (#0), caused by a DIV or IDIV by zero operation. This exception is continuable. Exception Code: XCPT_INTEGER_DIVIDE_BY_ZERO (0xC000009B) Default action: The process is ended. Additional Parameters: None. ═══ 7.4.16. XCPT_INTEGER_OVERFLOW ═══ Integer Overflow An integer overflow exception is generated when the result of an integer operation causes a carry-out of the most significant bit of the result, which is not the same as the carry-into of the most significant bit of the result. For example, the addition of two positive integers produces a negative result. On the Intel 80387 processor, this corresponds to overflow trap (#4), caused by executing an INTO instruction with the OF flag set. This exception is continuable. Exception Code: XCPT_INTEGER_OVERFLOW (0xC000009C) Default action: The process is ended. Additional Parameters: None. ═══ 7.4.17. XCPT_SINGLE_STEP ═══ Single Step A single-step exception is generated when a trace trap or other single instruction execution mechanism signals that one instruction has been executed. This exception is intended for use by debuggers. This exception is continuable. Default Action: The process is ended Exception Code: XCPT_SINGLE_STEP (0xC00000A0) Additional Parameters: None. ═══ 7.4.18. XCPT_GUARD_PAGE_VIOLATION ═══ Guard Page Violation A guard page violation exception is generated when an attempt is made to load or store data in a location that is contained within a guard page. Memory management software immediately turns the guard page into a demand zero page and initiates a guard page violation exception. Exception Code: XCPT_GUARD_PAGE_VIOLATION (0x800001) Default Action: Execution continues. If possible, the memory page immediately below the guard page is allocated and marked as a guard page. The higher guard page is marked to no longer be a guard page, and the instruction is restarted. This allows for dynamic stack growth. If it is not possible to allocate another page below the faulting page, an XCPT_UNABLE_TO_GROW_STACK exception is raised. This exception is continuable. Additional Parameters (2): ExceptionInfo[ 0 ] Read/Write Flag XCPT_READ_ACCESS, or XCPT_WRITE_ACCESS. ExceptionInfo[ 1 ] Virtual Address The virtual address of the data within a guard page. ═══ 7.4.19. XCPT_UNABLE_TO_GROW_STACK ═══ Unable to Grow Stack The default action for a guard page violation is to attempt to allocate another page of memory immediately below the page on which the fault occurred, thereby implementing dynamic stack growth. If this attempt fails, XCPT_UNABLE_TO_GROW_STACK is generated, indicating that the thread has, at most, one more page of stack space available. This exception is continuable. Exception Code: XCPT_UNABLE_TO_GROW_STACK (0x80010001) Default Action: Execution continues. Additional Parameters: None. ═══ 7.4.20. XCPT_BAD_STACK ═══ Bad Stack This exception is raised when an ExceptionRegistrationRecord is reached that is not properly aligned or is not within the current stack boundaries. It is also raised if an unwind target is specified that does not point to an ExceptionRegistrationRecord. This exception is noncontinuable. Exception Code: XCPT_BAD_STACK (0xC0000027) Default Action: The process is ended. Additional Parameters: None. ═══ 7.4.21. XCPT_INVALID_UNWIND_TARGET ═══ Invalid Unwind Target This exception is raised when the address of the target ExceptionRegistrationRecord is below the current stack pointer. This exception is noncontinuable. Exception Code: XCPT_INVALID_UNWIND_TARGET Default Action: The process is ended. Additional Parameters: None. ═══ 7.4.22. XCPT_IN_PAGE_ERROR ═══ Page Read Error A page read error exception is generated when an attempt is made to read a page into memory and an I/O error is encountered. This exception is continuable. Exception Code: XCPT_IN_PAGE_ERROR (0xC0000006) Default Action: The process is ended. Additional Parameters (1): ExceptionInfo[ 0 ] Virtual Address A virtual address within the page that was being read. ═══ 7.4.23. XCPT_INVALID_DISPOSITION ═══ Invalid Disposition This exception is raised when an exception handler returns anything except XCPT_CONTINUE_EXECUTION or XCPT_CONTINUE_SEARCH. This exception is not continuable. Execution Code: XCPT_INVALID_DISPOSITION (0xC0000025) Default Action: The process is ended. Additional Parameters: None. ═══ 7.4.24. XCPT_NONCONTINUABLE_EXCEPTION ═══ Continuing a Noncontinuable Exception This exception is raised when an exception handler returns XCPT_CONTINUE_EXECUTION in response to a noncontinuable exception. This exception is not continuable. Execution Code: XCPT_NONCONTINUABLE_EXCEPTION (0xC0000024) Default Action: The process is ended. Additional Parameters: None. ═══ 7.4.25. XCPT_PROCESS_TERMINATE ═══ Process Termination There are two exceptions a thread may receive when it is about to end: XCPT_PROCESS_TERMINATE, and XCPT_ASYNC_PROCESS_TERMINATE. A thread receives XCPT_PROCESS_TERMINATE after it calls DosExit to end itself or the entire process. This exception is not continuable. Additional Parameters: None. ═══ 7.4.26. XCPT_ASYNC_PROCESS_TERMINATE ═══ Asynchronous Process Termination There are two exceptions a thread may receive when it is about to end: XCPT_PROCESS_TERMINATE, and XCPT_ASYNC_PROCESS_TERMINATE. A thread receives XCPT_ASYNC_PROCESS_TERMINATE when another thread in the process has caused it to end. For example, another thread has called DosExit to end the process, or has not handled a fatal exception, and so on. This exception is continuable. Additional Parameters (1): ExceptionInfo[ 0 ] TID The thread identification of the terminating thread. ═══ 7.4.27. XCPT_UNWIND ═══ Unwinding The system fills in an exception number for an unwind if the user chooses not to do so. Note that an ExceptionReportRecord containing XCPT_UNWIND does not indicate that an exception has occurred, but rather that an unwind is in progress. Exception Code: XCPT_UNWIND (0xC0000026) Default Action: Does not apply. Additional Parameters: None. ═══ 7.4.28. XCPT_SIGNAL ═══ Signal Exceptions An OS/2 Version 2.X application may receive three signals: XCPT_SIGNAL_INTR (Ctrl+C) XCPT_SIGNAL_KILLPROC (DosKillProcess) XCPT_SIGNAL_BREAK (Ctrl+Break). The signal being sent may be determined by examining the exception information in the ExceptionReportRecord. Exception Code: XCPT_SIGNAL (0xC0010003) Default Action: The process is ended. Additional Parameters (1): ExceptionInfo[ 0 ] Signal Number Number Signal 1 XCPT_SIGNAL_INTR 3 XCPT_SIGNAL_KILLPROC 4 XCPT_SIGNAL_BREAK XCPT_SIGNAL is called a "signal exception" and is sent only to thread 1 in the process receiving the exception. This is consistent with 16-bit signals, and provides greater consistency in the environment of the process for handling the various asynchronous exceptions. For example, since a repeated typematic Ctrl+C could possibly cause the thread to recursively process the exception and consume stack space without ever being able to handle the first "signal", the exception dispatcher "holds" each exception of the same type until a handler either returns XCPT_CONTINUE_EXECUTION to the exception dispatcher, or the process calls DosAcknowledgeSignalException for that signal. Only one signal or exception is actually held (they are not queued by the system). DosAcknowledgeSignalException indicates to the system that the process wants to receive the XCPT_SIGNAL_INTR and XCPT_SIGNAL_BREAK signals. Previously, when a process called DosAcknowledgeSignalException the system noted that the process was aware of the particular signal for which it was registering the handler. When a process called DosAcknowledgeSignalException, it became a candidate for the "signal focus" for its session. At any point in time, the focus for a session is the last process to register a signal handler for that signal. When the user presses Ctrl+C on the keyboard, the system delivers an XCPT_SIGNAL_INTR signal to the current keyboard focus. The user could also press Ctrl+Break to deliver an XCPT_SIGNAL_BREAK signal, but this would only work if input were in raw mode. Note that all exception handlers (on thread 1) must be prepared to "see" signal exceptions. It is always possible that a previous handler has issued DosSetSignalExceptionFocus, or that a Dos16SetSigHandler has been issued by some 16-bit code in the path. They can always be ignored by returning XCPT_CONTINUE_SEARCH to the exception dispatcher. Note that signals result in a call to the 16-bit signal handler (if installed) if all the 32-bit exception handlers return XCPT_CONTINUE_SEARCH. DosSetSession performs the function of assigning the signal focus exactly as if the application had called DosAcknowledgeSignalException twice, once for each signal. The process calls DosSetSession when it wants to indicate that it expects to receive XCPT_SIGNAL_INTR or XCPT_SIGNAL_BREAK after it has registered an exception handler to process the signal when it comes. Each call to DosSetSession increments a counter in the PTDA of the process. When the system attempts to send XCPT_SIGNAL_INTR or XCPT_SIGNAL_BREAK to a process, it first checks to see if either this counter is greater than zero, or if the process has registered a 16-bit signal handler for that signal. If either of these is true, the signal will be sent. If the process has registered both 16-bit and 32-bit handlers, the 32-bit handlers are called first. If they do not handle the signal, the 16-bit handlers are called. If the 32-bit handlers are called and do not handle the signal, and there are no 16-bit handlers, the process is terminated. ═══ 8. Extended Attributes ═══ OS/2 file systems maintain a standard set of information on file objects. This standard set of information is referred to as Level 1 file information. Level 1 file information includes the name and size of the file object, and the date and time the file object was created, last accessed, and last written to. Applications can attach additional information to a file object in the form of an extended attribute (EA). There can be many EAs associated with a file object and, because of their flexibility, almost any information about the file can be stored in one. The following topics are related to the information in this chapter:  File Systems  File Names  File Management ═══ 8.1. About Extended Attributes ═══ Level 1 file information is the basic information describing files that is stored by the file system. Level 1 file information includes the size of the file, and the date and time it was created, last written, and last accessed. A subset of this information is typically displayed by entering the DIR command on the OS/2 command line. Applications can obtain Level 1 file information by calling DosQueryPathInfo and DosQueryFileInfo. Applications can set Level 1 File Information by calling DosSetPathInfo and DosSetFileInfo. Applications can attach additional information to a file object in the form of an extended attribute (EA). Extended attributes can be used to describe the file object to another application, to OS/2, and to the file system that is managing that object. This information can be used to:  Store notes on file objects (for example, the name of the file creator)  Categorize file objects (for example, source, samples, icons, bit maps)  Describe the format of data contained in the file object (for example, a data record)  Append additional data to the file object. An application uses extended attributes to provide a description of a file or directory but the application does not place the description in the file or directory itself. Extended attributes associated with a file object are not part of the file object or its data. They are stored separately from the file they are linked to and the file system manages the storage and maintenance of the EA. Each extended attribute has two parts, a name and a value. The name is a nil-terminated string; any convenient name can be chosen. EA names are restricted to the same character set as file names. The value of the EA can be text, a bit map, binary data, anything at all. OS/2 does not check data that is associated with an EA. The application that creates the extended attributes and the applications that read them must recognize the format and meaning of the data associated with a given EA name. Applications can examine, add, and replace extended attributes at any time. Any application can read the extended attributes by using the DosQueryFileInfo or DosQueryPathInfo function. Applications can use DosFindFirst and DosFindNext to search for files that have specific extended attributes. A file can have any number of extended attributes. Each extended attribute can be up to 64KB in size. The sum of all extended attributes for a file must not exceed 64KB. So that extended attribute data can be understood by other applications, conventions have been established for naming EAs and indicating the type of data they contain. In addition, a set of Standard Extended Attributes (SEAs) have been defined. SEAs define a common set of information that can be associated with most files (for example, file type and file purpose). Through SEAs, many applications can access the same, useful information associated with files. Applications are not limited to using SEAs to associate information with files. They can define their own application-specific extended attributes. Extended attributes associated with a file object are not part of the file object or its data. Extended attributes are supported by the OS/2 High Performance File System (HPFS) and by the OS/2 FAT file system. Applications define and associate extended attributes with a file object through file system functions. The file system functions that use and manipulate EAs are:  DosOpen  DosFindFirst  DosQueryFileInfo  DosQueryPathInfo  DosSetFileInfo  DosSetPathInfo ═══ 8.1.1. Extended Attribute Data Type Conventions ═══ Extended attributes (EAs) can contain any type of data. So that applications can understand the type of information stored in an EA, the first WORD of EA data must specify one of the following data types: Extended Attribute Data Types ┌───────────────┬──────┬────────────────────────────────────────┐ │Data Type │Value │Description │ ├───────────────┼──────┼────────────────────────────────────────┤ │EAT_BINARY │FFFE │Binary (non-text) data; the first WORD │ │ │ │following the data type specifies the │ │ │ │length of the data. │ ├───────────────┼──────┼────────────────────────────────────────┤ │EAT_ASCII │FFFD │ASCII text; the first WORD following the│ │ │ │data type specifies the length of the │ │ │ │data. │ ├───────────────┼──────┼────────────────────────────────────────┤ │EAT_BITMAP │FFFB │Bit map data; the first WORD following │ │ │ │the data type specifies the length of │ │ │ │the data. │ ├───────────────┼──────┼────────────────────────────────────────┤ │EAT_METAFILE │FFFA │Metafile data; the first WORD following │ │ │ │the data type specifies the length of │ │ │ │the data. │ ├───────────────┼──────┼────────────────────────────────────────┤ │EAT_ICON │FFF9 │Icon data; the first WORD following the │ │ │ │data type specifies the length of the │ │ │ │data. │ ├───────────────┼──────┼────────────────────────────────────────┤ │EAT_EA │FFEE │ASCII name of another EA that is │ │ │ │associated with the file. The contents │ │ │ │of that EA are to be included into the │ │ │ │current EA. The first WORD following the│ │ │ │data type specifies the length of the │ │ │ │data. │ ├───────────────┼──────┼────────────────────────────────────────┤ │EAT_MVMT │FFDF │Multi-Valued, Multi-Typed data-two or │ │ │ │more consecutive extended attribute │ │ │ │values. Each value has an explicitly │ │ │ │specified type. │ ├───────────────┼──────┼────────────────────────────────────────┤ │EAT_MVST │FFDE │Multi-Valued, Single-Typed data-two or │ │ │ │more consecutive extended attribute │ │ │ │values. All values have the same type. │ ├───────────────┼──────┼────────────────────────────────────────┤ │EAT_ASN1 │FFDD │ASN.1 field data; an ISO standard for │ │ │ │describing multivalue data streams. │ └───────────────┴──────┴────────────────────────────────────────┘ Values of hex 8000 and up are reserved. Values between hex 0000 and hex 7FFF can be defined by the user. Symbolic constants are defined in BSEDOS.H and BSEDOS.INC. In all cases, the length specifies the number of bytes of data. Other values for data types, in the range hex 0000 through hex 7FFF, can be used for user-defined extended attributes. All user-defined data types should be length-preceded, meaning that a WORD indicating the length of the data (in bytes) precedes the data. For example, here is how to represent the string "Hello": EAT_ASCII 0005 Hello ═══ 8.1.1.1. Multi-Value Data Type Fields ═══ In many cases, it is desirable for extended attributes (EAs) to store more than a single piece of information. For example, an extended attribute can store a list of names to which a document was sent. The multi-value formats specify how individual pieces of data are stored. Data entries are length-preceded, making it easy to traverse a multi-valued list. In order to allow EAs of different code pages, multi-valued EAs include a field in which the EA's code page is specified. For example, the code page field could be used to indicate that the comments for a Kanji file are written in Spanish. If this value is 0, the file default is assumed. (Code page data is for use by applications. OS/2 does not examine or use EA code page information.) When the concept of a default applies to a multi-valued EA, the first entry in the list is assumed to be the default. For example, suppose an EA entry contains the strings "Text" and "C Code". "Text" is considered the default type. If "C Code" were the first entry in the list ("C Code" then "Text"), then "C Code" would be considered the default type. There are three multi-valued EA data types:  Multi-Valued, Multi-Typed Data  Multi-Valued, Single-Type Data  ASN.1 Data ═══ 8.1.1.2. Multi-Valued, Multi-Typed Data Type ═══ This data type indicates that the value of a single extended attribute (EA) contains several pieces of information, each of a different data type. It is formatted as follows: EAT_MVMT Codepage NumEntries [DataType Data] ... WORD WORD WORD WORD The first word indicates that the EA value is multi-valued, multi-typed. The second word indicates the code page associated with the language in which the EA value is written. The third word indicates the number of entries contained in this EA value. The next word indicates the data type for the first entry in this EA value, followed by the data for the first entry. The next word, if any, indicates the data type for the second entry in this EA value, followed by the data for the second entry. The pattern repeats- data type, followed by data-for any remaining entries. For example, an extended attribute can have the following value: EAT_MVMT 0000 0002 EAT_ASCII 000A Hello John EAT_BINARY 0003 $12 $21 $34 This is a multi-valued extended attribute with two entries, using the default code page. The first entry is the string "Hello John", and the second is the binary data 0x12 0x21 0x34. Whether or not the data is length-preceded is a function of the data type. ═══ 8.1.1.3. Multi-Valued, Single-Type Data Type ═══ This data type indicates that the value of a single extended attribute (EA) contains several pieces of information, each of the same data type. For example: EAT_MVST Codepage NumEntries Data_Type [data] ... WORD WORD WORD WORD The first word indicates that the EA value is multi-valued, single-typed. The second word indicates the code page associated with the language in which the EA value is written. The third word indicates the number of entries contained in this EA value. The next word indicates the data type of all the entries contained in this EA value, followed by the data for all entries. For example, the following EA value contains three ASCII names: EAT_MVST 0000 0003 EAT_ASCII 0004 Mark 0005 Ellen 0003 Liz Each name string is preceded by the length of the string. Whether or not the data is length-preceded is a function of the data type. ═══ 8.1.1.4. ASN.1 Data Type ═══ This data type indicates that the extended attribute uses the ASN.1 ISO standard to describe a multi-valued data stream. ═══ 8.1.2. Including One Extended Attribute in Another ═══ Extended attributes (EA) can contain pointers to data stored in other places. This data type indicates that the data contained in another EA associated with the file object should be included into the current EA. For example, the following EA value contains the string "Hello", followed by the data in the EA named AB.STUFF, followed by the string "Bye". EA_MVMT 0000 0003 EAT_ASCII 0005 Hello EAT_EA 0008 AB.STUFF EAT_ASCII 0003 Bye ═══ 8.1.3. Extended Attribute Naming Conventions ═══ Because many applications use text, bit maps, and other binary data in extended attributes, standard names have been adopted to help identify these formats. An application is not limited to these Standard Extended Attributes but should use them when many applications will be accessing the same data. Standard Extended Attributes (SEAs) have a dot (.) as a prefix. This identifies the extended attribute as a SEA. The leading dot is reserved, so applications should not define extended attributes that start with a dot. Also, extended attributes that start with the characters $, @, or + are reserved for system use. To ensure that its extended attributes are unique, an application should use the name of the company and the name of the application (or suitable abbreviations of each) as a prefix for application-specific extended attributes. For example, Company A has an OS/2 Application, B, that defines extended attributes STUFF, MORE_STUFF, and STILL_MORE_STUFF for its file objects. The names of these extended attributes could be represented by the following entry: AB.STUFF AB.MORE_STUFF AB.STILL_MORE_STUFF ═══ 8.1.4. Standard Extended Attributes ═══ There are nine OS/2 Standard Extended Attributes (SEAs). The name of a SEA has a dot (.) as a prefix. This identifies the extended attribute as a SEA. The values of Standard Extended Attributes can be multi- or single-valued, with formats following the data type conventions discussed previously. Where entries for Standard EAs consist of ASCII characters, case is important. The Standard EAs that have been defined are: .ASSOCTABLE .CODEPAGE .COMMENTS .HISTORY .ICON .KEYPHRASES .LONGNAME .SUBJECT .TYPE .VERSION The .TYPE and .ASSOCTABLE extended attributes (EA) are two of the most useful SEAs. The .TYPE extended attribute indicates what type of data is in a file. It also implies what programs can edit the file, and what icon is to be used for the file. OS/2 can use the .TYPE EA to determine a default application to run and a default icon for a file of a particular type (if there is a .ICON EA, it will be used instead of the icon associated with a particular data type). The .ASSOCTABLE extended attribute allows an application to indicate the type, extension, and icon for data files that it recognizes. It also contains an ownership flag. This data can be automatically installed by OS/2. When your program recognizes files created by other programs, you might want to install .ASSOCTABLE EA entries for those other programs. ═══ 8.1.4.1. The .ASSOCTABLE Standard Extended Attribute ═══ The .ASSOCTABLE extended attribute (EA) contains information that associates data files with the applications that create them or that know how to use them. The .ASSOCTABLE extended attribute enables an application to indicate the type, extension, and icon for the data files it recognizes. The .ASSOCTABLE EA also contains an ownership flag. This tells OS/2 which application to run when the user double-clicks the mouse on a given data file. Because programs can understand and reference data files generated by other programs, this EA can be used to link a program with those files. The name of this EA consists of the string ".ASSOCTABLE". The value of this EA contains application information and consists of multi-valued, multi-typed fields that link the application with:  the file type (that is, the value of a .TYPE EA),  the file extension,  and icon data for data files that it generates or references. The .ASSOCTABLE EA associates icons by file-type. The data file's file-type is indicated in the .TYPE EA, or, if the data file does not have a .TYPE EA, by the extension. This data can be installed automatically by OS/2. The format of the EA is as follows. EAT_MVMT 0000 0004 EAT_ASCII .TYPE name EAT_ASCII file extension EAT_BINARY flags EAT_ICON icon data The source for the .ASSOCTABLE EA is contained in the resource file for an application. The .ASSOCTABLE EA is created using the Resource Compiler from a table with the following form: ASSOCTABLE assoctable -id BEGIN association_name,[extension],[flags], [icon_filename] association_name,[extension],[flags], [icon_filename] . . . END The association_name is the name of a file type that the Resource Compiler understands. (This is the same name found in the .TYPE field of data files.) The extension is the three letter file extension that is used to identify files of this type, if they have no .TYPE EA entry. (Three letter extensions should be used so that FAT file systems can make use of this EA). This field can be empty. The icon_filename is the name of the file that contains the icon that is to be used to represent this file type. (This field can also be empty.) The .ASSOCTABLE flag indicates that the program is the default application for data files with the specified type. This determines the program OS/2 will start when the file is double-clicked with the mouse. If more than one program has marked itself as the EAF_DEFAULTOWNER for a particular data file .TYPE, OS/2 will not know which program to run when the file of this .TYPE is double-clicked on with the mouse. If no program is marked as the EAF_DEFAULTOWNER for a particular data file .TYPE, OS/2 will be similarly confused. In both cases, OS/2 provides the user with a list of applications that understand the file .TYPE, regardless of whether the application is the owner or not. The user selects the program to run from this list. The flag entry indicates whether the application owns the file or merely recognizes the .TYPE. If this flag is set, the entry describing data files of this type cannot be edited. This flag is specified if a previously defined icon in the ASSOCTABLE is to be reused. Entries with this flag set have no icon data defined. The icon used for this entry will be the icon used for the previous entry. EAF_ flags can be ORed together when specified in the ASSOCTABLE. The EAF_ flags are defined in PMWIN.H and PMWIN.INC. .ASSOCTABLE Example For example, My_Company's application, My_Application, generates or references data files that have the following .TYPE names: My_Company My_Application documentation My_Company My_Application macros My_Company My_Application spreadsheet My_Company My_Application chart Your_Company Your_Application forecast The source for the .ASSOCTABLE extended attribute in the resource file for My_Application could look like the following. ASSOCTABLE BEGIN 'My_Company My_Application documentation', 'DOC', EAF_DEFAULTOWNER, My_App.ICO 'My_Company My_Application macros', 'MAC', EAF_DEFAULTOWNER+EAF_REUSEICON 'My_Company My_Application spreadsheet', 'SPR', EAF_DEFAULTOWNER+EAF_REUSEICON 'My_Company My_Application chart', 'CHT', EAF_DEFAULTOWNER+EAF_REUSEICON 'Your_Company Your_Application forecast', 'FOR', 0 END My_Application can load and use some files generated by Your_Application. However, because My_Application is not the default owner of those files, OS/2 does not run My_Application when the user double-clicks on the files with the mouse. The following example illustrates how the value of the .ASSOCTABLE EA for My_Application might look. It is a multi-valued, multi-typed EA with five multi-valued, multi-typed entries (one for each file type referenced or generated by the application). EAT_MVMT 0000 0005 ; There are 5 associated file types EAT_MVMT 0000 0004 ; Description of 1st associated file type EAT_ASCII 0027 My_Company My_Application documentation ; File type EAT_ASCII 0003 DOC ; File extension EAT_BINARY flags ; Flags EAT_ASCII icon data ; Physical icon data EAT_MVMT 0000 0004 ; Description of 2nd associated file type EAT_ASCII 0020 My_Company My_Application macros EAT_ASCII 0003 MAC EAT_BINARY flags EAT_ICON icon data EAT_MVMT 0000 0004 ; Description of 3rd associated file type EAT_ASCII 0025 My_Company My_Application spreadsheet EAT_ASCII 0003 SPR EAT_BINARY flags EAT_ICON icon data EAT_MVMT 0000 0004 ; Description of 4th associated file type EAT_ASCII 001F My_Company My_Application chart EAT_ASCII 0003 CHT EAT_BINARY flags EAT_ICON icon data EAT_MVMT 0000 0004 ; Description of 5th associated file type EAT_ASCII 001F Your_Company Your_Application forecast EAT_ASCII 0003 FOR EAT_BINARY flags EAT_ICON icon data ═══ 8.1.4.2. The .CODEPAGE Standard Extended Attribute ═══ The .CODEPAGE extended attribute (EA) contains the code page for the file. If this extended attribute is not provided, the code page of the file is the system default or is defined by the application. The code page of the EA data associated with the file is assumed to be that of the file, unless the EA entry is specifically overridden in the code page field in the multi-valued extended attribute data type. ═══ 8.1.4.3. The .COMMENTS Standard Extended Attribute ═══ The .COMMENTS extended attribute (EA) contains miscellaneous notes or reminders about the file (for example, peculiarities, restrictions, or requirements). The name of this EA consists of the string ".COMMENT". The value of this EA consists of miscellaneous notes and can be multi-valued and of any type. ═══ 8.1.4.4. The .HISTORY Standard Extended Attribute ═══ The .HISTORY extended attribute (EA) contains the modification history for a file object, indicating the author of the file and all subsequent changes. Each entry is separate field in a multi-value field and consists of be ASCII characters only. The name of this EA consists of the string ".HISTORY". The value of this EA contains the modification history for a file object and can be multi-valued, with each action entry described in a separate field. Each entry in the .HISTORY field has the following format: PERSON ACTION(created, changed or printed) DATE For example, the following .HISTORY extended attribute contains two entries: EAT_MVMT 0000 0002 EAT_ASCII 0017 Joe Created 2/10/88 EAT_ASCII 0017 Harry Changed 2/11/88 This extended attribute can potentially become quite large. To avoid unwanted growth, an application can let the user decide when an entry should be added to this extended attribute. For example, there are some cases when it is important to note when a document is printed. However, it is probably unnecessary to note it every time the file is printed. ═══ 8.1.4.5. The .ICON Standard Extended Attribute ═══ The .ICON extended attribute (EA) specifies the icon to be used for the file representation, for example when the application is minimized. This extended attribute contains the physical icon data used to represent the file object. If there is no .ICON EA, OS/2 can use the .TYPE entry to determine a default icon to use for the particular file. If there is an .ICON entry, however, it is used instead of the default icon. The name of this EA consists of the string ".ICON". The value of this EA contains the physical icon data and has the following format: EAT_ICON data_length data WORD DWORD The data is of type BITMAPARRAYFILEHEADER and is used to specify an array of one device-dependent and one device-independent icon bit maps. The GpiLoadBitmap and WinLoadPointer functions support this icon file format. It is best to provide as much icon information as possible. Ideally, an icon should be 64-by-64 bits in 8-color, device-independent format. The Icon Editor is used to create the icon, which is saved in an icon file. The .ICON extended attribute for an application is created by the Resource Compiler as part of the compile process by specifying the DEFAULTICON keyword, as in: DEFAULTICON This keyword uses the icon definition contained in the specified icon file (FILENAME.ICO) to create the .ICON EA for the application. Applications store the binary icon data in this extended attribute. To install icons for data files, the applications can use the .ASSOCTABLE extended attribute, or DosSetPathInfo. ═══ 8.1.4.6. The .KEYPHRASES Standard Extended Attribute ═══ The .KEYPHRASES extended attribute (EA) contains key text phrases for the file. Such phrases can be used in performing a database-type search or in helping the user understand the nature of the file. The name of this EA consists of the string ".KEYPHRASES". The value of this EA consists of key phrases in ASCII. Key phrases are represented as ASCII characters. Multiple key phrases can be stored in the value of this extended attribute, each stored in a separate entry in a multi-valued field. For example, the following extended attribute contains three key phrases: EAT_MVST 0000 0003 EAT_ASCII 0008 ABC Inc. EAT_ASCII 000A Salesman A EAT_ASCII 000F Product X sales If there is more than one key phrase, each should be stored in a separate entry in a multi-value field. ═══ 8.1.4.7. The .LONGNAME Standard Extended Attribute ═══ When an application attempts to write a file with a long name to a file system that does not support long names, it must generate a short name for the file. The application should notify the user of the new short name and save the original (long) name in the .LONGNAME extended attribute. When a file is copied from a system that uses short names to a system that uses long names, the application should check the .LONGNAME extended attribute. If a value is present, the application should rename the file to the long name, then remove the .LONGNAME extended attribute. ═══ 8.1.4.8. The .SUBJECT Standard Extended Attribute ═══ The .SUBJECT extended attribute (EA) contains a brief summary of the content or purpose of the file object it is associated with. The name of this EA consists of the string ".SUBJECT". The value of this EA consists of a single-valued ASCII string that contains the purpose of the file object. The length of this field must be less than 40 characters. ═══ 8.1.4.9. The .TYPE Standard Extended Attribute ═══ The .TYPE extended attribute (EA) indicates the file-type of the file object it is associated with. It is similar to a file name extension. The name of this EA consists of the string ".TYPE". The value of this EA contains the file object's file-type. The following file types are predefined: Plain text OS/2 command file DOS command file Executable Metafile Bit map Icon Binary data Dynamic link library C code Pascal code BASIC code COBOL code FORTRAN code Assembler code Library Resource file Object code Data files only require identification of the file type. For data files without EAs, the file type is derived from the file extension, if there is one. File object types are represented as length-preceded ASCII strings, uniquely identifying the file object's type. This identifier is referenced within the application's .ASSOCTABLE EA in order to bind the data file type to the application. It is important that this name be a unique identifier because all file type names are public data. For example, if application A and application B both had a type name of SPREADSHEET, the filing system would not be able to identify A's SPREADSHEET from B's SPREADSHEET. The recommended convention for defining file object types is:  Company_name  Application_name  Application-specific_name For example, spreadsheet files generated by My_Application written by My_Company might have a file object type of the following. My_Company My_Application Spreadsheet Type names must be ASCII characters and case is significant. Note: The performance of extended attributes is dependent on the file system. Because some file systems store extended attributes in first-in/first-out (FIFO) order, it is important to write the .TYPE entry first so OS/2 can access that information quickly. ═══ 8.1.4.10. The .VERSION Extended Attribute ═══ The .VERSION extended attribute (EA) contains the version number of the file format, as shown below. My_Application 1.0 The name of this EA consists of the string ".VERSION". The value of this EA contains the file object version number. This attribute can be ASCII or binary. Only the application that created the file object should modify the value of this EA. It can also be used to indicate an application or dynamic link library version number. ═══ 8.1.5. Managing Extended Attributes ═══ An application can create, query, and set extended attributes (EAs) for any file object. The application can define extended attributes for a file when the file is created with DosOpen. Similarly, the application can define EAs for a directory when the directory is created using DosCreateDir. An application can define EAs for existing file objects by referencing the file object by its handle and calling DosSetFileInfo, or by referencing the file object by its name and calling DosSetPathInfo. An application can examine the EAs for a file object by referencing the file object by its handle and calling DosQueryFileInfo, or by referencing the file object by its name and calling DosQueryPathInfo. The application can also call DosEnumAttribute, using either the file object's handle or its name, to get information about the file object's EAs. In addition, an application can search for file objects and specify that certain EAs be returned by calling DosFindFirst. ═══ 8.1.5.1. Controlling Access to Extended Attributes ═══ Like the file objects they are associated with, extended attributes (EAs) can have more than one process accessing them at the same time. This means that one process could be querying EAs for a file object, while another is setting EAs for the same file object. In addition, operations on EAs are not atomic. That is, a query or set operation might not complete before another query or set operation is performed on the same object. If an error occurs before an entire list of EAs has been set, all, some, or none of them may actually remain set on the file object. This means that EAs may not remain in a consistent state unless the order in which the operations are performed can be guaranteed. Sharing protection is provided so that unpredictable results do not occur during multiple simultaneous operations on extended attributes. EA manipulation is associated with the access permission to the related file object (file or directory). Handle-based access permission is controlled by the sharing/access mode of the associated file object:  If the file object is open for read access, querying EAs (using DosQueryFileInfo) is permitted.  If the file object is open for write access, setting EAs (using DosSetFileInfo) is permitted. Path-based access permission is controlled by adding the file object to the sharing set for the duration of the call: an application requires read access and file-sharing permission must be set to deny-write.  For setting EAs (using DosSetPathInfo), an application requires write access and file-sharing permission must be deny-read-write. Note: The functions that set and query EAs fail if another process holds conflicting sharing rights to the file object. No explicit EA sharing is performed for DosEnumAttribute. Implicit sharing exists if the caller passes the handle of an open file, since sharing access to the associated file is required to modify its EA. No sharing is performed if the caller passes the path name. This means that if some other process is editing the EAs, and changes them between two calls to DosEnumAttribute, inconsistent results might be returned (for example, the same values might be returned twice, some values might be missed, and so on). To prevent the modification of EAs for the handle case, the file should be opened in deny-write mode before calling DosEnumAttribute. To prevent the modification of EAs for the path name case, the file should be open in deny-write mode before calling DosEnumAttribute. For the directory name case, no sharing is possible. ═══ 8.1.5.2. Extended Attribute Data Structures ═══ There are a series of data structures through which OS/2 functions manipulate extended attributes (EAs) for applications:  Full EAs (FEA2s)  A list of full EAs (FEA2LIST)  Get EAs (GEA2)  A list of get EAs (GEA2LIST)  EAOP2s Full Extended Attribute (FEA2) Data Structure A full EA (FEA2) data structure contains the extended attribute name and value. The name length does not include the trailing nil. The characters that form the name are legal file name characters. An FEA2LIST is a list of FEA2 structures, preceded by the length of the list (including the length itself). FEA2Lists are used for querying, adding, deleting, or changing EAs. They are required input parameters for the functions that create or set extended attributes (DosSetFileInfo and DosSetPathInfo). They are required output parameters for the functions that query extended attributes (DosQueryFileInfo, DosQueryPathInfo, and DosEnumAttribute). FEA2 data structures include the lengths of the extended attribute's names and values. EA name lengths of 0 are illegal and cause errors to be returned by EA functions. An EA value length of 0 has special meaning:  Setting an EA with a value length of 0 in the FEA2 data structure causes that attribute to be deleted, if possible.  Getting an EA with a value length of 0 in the FEA2 data structure indicates that the attribute is not present. Get Extended Attribute (GEA2) Data Structure A Get EA (GEA2) is an extended attribute name. The name length does not include the trailing nil. A GEA2LIST is a list of GEA2 structures, preceded by a length of the list (including the length itself). GEA2Lists are used for retrieving the values for a particular set of extended attributes. They are required input parameters for the functions that query extended attributes (DosQueryFileInfo, DosQueryPathInfo, and DosEnumAttribute). Note: GEA2 data structures include the lengths of extended attribute's names and values. EA name lengths of 0 are illegal and cause errors to be returned by EA functions. An EA value length of 0 has special meaning. Setting an EA with a value length of 0 in the FEA2 data structure causes that attribute to be deleted, if possible. Getting an EA with a value length of 0 in the FEA2 data structure indicates that the attribute is not present. Extended Attribute Operation ( EAOP2) Data Structure An extended attribute operation (EAOP2) data structure consists of a GEA2LIST, an FEA2LIST, and an error field. All extended attribute manipulation is performed using this structure. Before calling an extended attribute function, an application must define an EAOP2 structure, with the GEA2LIST and FEA2LIST appropriately defined. The use of GEA2LIST and FEA2LIST for each function is described further in the Control Program Programming Reference. ═══ 8.1.5.3. Preserving Extended Attributes ═══ Extended attributes (EAs) are supported in OS/2's FAT file system and High Performance File System (HPFS). Extended attributes are not supported by the FAT file system used in versions of OS/2 prior to Version 1.2, nor are they supported in the DOS operating system. EAs associated with a file object are not part of a file object or of its data, but are maintained separately and managed by the file system that manages that object. EAs reside on the volume on which the file object resides and are connected to their file object by pointers from the file object. Therefore, EAs belonging to a file object are lost when moving that file object from a storage device managed by an OS/2 FAT or installable file system to a storage device managed by the old FAT file system. EAs can be lost under other circumstances, as well:  When a program that does not recognize EAs (for example, an editor written for DOS) performs a non-truncating open on the files with which the EAs are associated  When the files they are associated with are sent over COM links So that files with EAs can be manipulated under these circumstances, without losing their EAs, OS/2 provides a utility called EAUTIL EAUTIL enables users to separate extended attributes from the specified file object, optionally disassociating them from the file object. The extended attributes are placed into a specified HoldFile. This utility also enables users to reattach the separated extended attributes with their file objects. EAUTIL can be applied to subdirectories, as well as to files. It does not support global file name characters in parameters; it operates on a single file object at each invocation. Users can use EAUTIL to:  Strip EAs off files to be edited with a program that does not recognize EAs.  Place the EAs into normal files so they can be dealt with by old tools (such as backup, restore, and so on) or easily transmitted.  Reattach EAs to files after the files have been brought back from the systems where EAs are not supported. ═══ 8.1.5.4. Protecting Extended Attributes ═══ Programs written for releases of OS/2 and DOS that do not support extended attributes (EAs) will tend to lose EAs simply because they do not know that they exist. OS/2 provides some controls that prevent old programs from destroying critical data without unduly restricting their activities. This is done by classifying programs and marking the extended attributes that are associated with files. Programs are classified as:  Those that recognize EAs. These include OS/2 Version 1.2 and later programs.  Those that do not recognize EAs. These include programs written for releases of OS/2 and DOS that do not support EAs. EAs associated with files are marked as critical or non-critical. Programs that do not recognize EAs are not permitted to manipulate files that have critical EAs associated with them. This protection does not apply to directories. EAs associated with directories cannot be marked as critical. Critical Extended Attributes Extended attributes (EAs) are non-critical by default. A non-critical EA is one that is not necessary to the functionality of the application. That is, if a non-critical EA is lost, the system continues to operate correctly. For example, losing the icons associated with data files does not generally cause any ill effect other than the inability to show the icon. A critical extended attribute is one which is necessary to the correct operation of OS/2 or of a particular application. EAs should be marked as critical if their loss would cause the system or program to perform incorrectly. For example, a mail program might store mail headers in EAs. The loss of the header from a message would normally render the mail program completely unable to further use that message. This would be unacceptable, so the mail program should mark this EA as critical. A file has critical extended attributes (EAs) if at least one EA attached to the file is marked as critical. Marking EAs as critical only prevents programs that do not recognize EAs from losing the EAs from the file. It does not prevent deletion of files by any application. Applications must be careful how they mark their EAs. If they are too aggressive marking EAs as critical, users might be prevented from accessing files that their application uses. EAs are marked as critical by setting the critical bit. The critical bit is bit 7 of the flags byte of the FEA2 data structure. If this bit is 0, the EA is a non-critical EA. If it is 1, it is a critical EA. The symbolic constant FEA_NEEDEA can be used to indicate a critical EA. The creator of the EA determines whether it is critical or not. Programs that do not recognize extended attributes (EAs) are prevented from performing certain operations on files that have critical EAs associated with them. For example, a program that does not recognize EAs is not permitted to perform a non-truncating open on a file with critical EAs associated with it, because programs cannot be permitted to read the data and ignore the EAs. Programs that do not recognize EAs are, however, permitted to perform those operations that they can do completely. For example, they can delete files with critical EAs associated with them. Programs that do not recognize EAs are not prevented from accessing files whose EAs are not critical. Programs that recognize EAs have no restrictions placed on their actions with respect to critical EAs. Programs that recognize extended attributes must identify themselves to OS/2. This is done by including the NEWFILES declaration in the program's module definition file. The NEWFILES declaration is also how programs indicate that they understand and use long file names. ═══ 8.1.5.5. Searching for Extended Attributes ═══ An application can search for file objects that have specific extended attribute names by calling DosFindFirst twice. The steps involved are: 1. Call DosFindFirst for FileInfoLevel = 2, to get the length of the buffer required to hold the EAOP2 data associated with a matching file object. 2. Call DosFindFirst for FileInfoLevel = 3, to get the EAOP2 data associated with the matching file object. ═══ 8.1.5.6. Supporting Extended Attributes ═══ To support extended attributes, applications should do the following: 1. Fill in the .ASSOCTABLE extended attribute for all major file types that the application recognizes or uses. 2. Fill in the .ICON extended attribute for executable files. 3. Set the .TYPE extended attribute for data files the application creates. 4. Fill in and use the .LONGNAME extended attribute as appropriate. 5. Support .HISTORY and .VERSION. 6. Support the other Standard Extended Attributes as appropriate. ═══ 9. File Management ═══ OS/2 provides a standard set of file system functions. This means that applications can create, open, read, write, copy, and delete files and directories by using the same functions, regardless of which file system is used. When an application calls a file system function, OS/2 passes the request to the dynamic link library that supports the file system. The dynamic link library carries out most file system processing, such as validating file names. If an error occurs, the file system returns the error to OS/2, which then passes it back to the calling application. The OS/2 file system functions identify files and directories by their names. These functions store or search for the file or directory in the current directory on the current drive unless the name explicitly specifies a different directory and drive. Occasionally, a file system has control functions in addition to the standard file system functions. The control functions are specific to the given file system. An application can call a control function by using DosFSCtl, which directs OS/2 to pass the control-function information to the corresponding Installable File System (IFS). The following topics are related to the information in this chapter:  Files systems  Files names  Extended attributes  Pipes  Device I/O ═══ 9.1. About Volumes and Drives ═══ OS/2 allows more than one file system on a single storage device. If the device can have more than one partition (or volume), each partition can be initialized as an OS/2 partition and given a valid OS/2 file system. For each volume, OS/2 determines the type of file system the first time the volume is accessed by a function or when the medium in the drive changes. After that, OS/2 manages all input and output to that volume by using the corresponding dynamic link library. OS/2 uses the volume label and serial number to ensure that the medium in the drive does not change while there are outstanding requests for input and output. Each volume has a volume label and a 32-bit volume serial number, stored in a reserved location in logical sector 0 at the time of formatting. If the volume label and serial number do not match, OS/2 signals the critical error handler to prompt the user to insert the volume that has the specified serial number and label. OS/2 maintains the connection between the medium and the volume label and serial number until all open files on the volume are closed and all search references and cache buffer references are removed. The system redetermines the type of file system and the volume label and serial number only when the medium changes. OS/2 enables applications to:  Determine and set the default drive using DosQueryCurrentDisk and DosSetDefaultDisk, respectively.  Determine and set drive information using DosQueryFSInfo and DosSetFSInfo.  Determine and set the write verification switch using DosQueryVerify and DosSetVerify. If the write verification switch is on, each time data is written to the disk, the data is verified to ensure it has been recorded correctly. These functions are provided for recording critical data. ═══ 9.2. About Directories ═══ When an application starts, it inherits the current directory and drive from the application that starts it. An application can determine which directory and drive are current by using DosQueryCurrentDir and DosQueryCurrentDisk. An application can change the current directory and drive of the file system by using DosSetCurrentDir and DosSetDefaultDisk. When an application creates a new file, the system adds a file entry to the specified directory. Each directory can have any number of entries, up to the physical limit of the disk. An application can create new directories and delete existing directories by using DosCreateDir and DosDeleteDir. Before a directory can be deleted, it must be empty; if there are files or directories in that directory, they must be deleted or moved. An application can delete a file by using DosDelete or move a file to another directory by using DosMove. ═══ 9.2.1. Creating a Subdirectory ═══ To create a new subdirectory, an application calls DosCreateDir and specifies a directory path name. If the call is successful, a new subdirectory is created at the end of the path on the specified disk. If no path name is specified, a new subdirectory is created at the end of the current directory for the process. If any subdirectories in the path do not exist, the subdirectory is not created. Because a subdirectory is a file object, an application also can define an extended attribute for the directory when it is created during this call. See Extended Attributes for more information on extended attributes. ═══ 9.2.2. Determining and Changing the Current Directory ═══ Calling DosQueryCurrentDir returns the full path name of the current directory for the specified drive. The string does not begin with a back slash (\) and it ends with a byte containing 00H, the nil character. To change the current directory, call DosQueryCurrentDir with the path name of the directory you want to make the current directory. ═══ 9.2.3. Deleting a Directory ═══ A subdirectory cannot be removed if it is the current subdirectory or if it contains hidden files or subdirectory entries other than the "." and ".." entries. When these requirements for removal are met, DosDeleteDir can be called with a path name to remove the subdirectory from the disk. ═══ 9.3. About Files ═══ A file is one or more bytes of data, usually stored on a disk. While the application that creates the file determines the format of the file, the file system determines how the file is stored on the disk and what actions can be performed on it. The file system also stores information about the file in file attributes and extended attributes. Files are accessed through the file system using file handles. File handles are returned by DosOpen when the file is opened and are used for all subsequent accesses to the file. Files can be be opened, read from, written to, closed, copied, moved, deleted, renamed, and locked. Files can be searched for based on a metacharacter template. Each open file has a file pointer that indicates the current reading or writing location within the file. The file pointer moves automatically with each read or write operation on the file and can be moved manually by the application. ═══ 9.3.1. File Attributes ═══ Each directory entry includes a set of file attributes. File attributes specify whether the directory entry is for a file, a directory, or a volume identifier. The attributes also specify if the file is read-only, hidden, archived, or a system file. A read-only file cannot be opened for writing, nor can it be deleted. A hidden file (or directory) cannot be displayed by using an ordinary directory listing. A system file is excluded from normal directory searches. The archived attribute is for use by special purpose applications to mark a file that has been changed since the last time the file was examined. An application can retrieve and set the file attributes by using DosQueryFileInfo and DosSetFileInfo. ═══ 9.3.2. File Handles ═══ OS/2 identifies each open file by assigning it a file handle when the application opens or creates the file. The file handle is a unique 32-bit integer. The application can use the handle in functions that read from and write to the file, depending on how the file was opened. The application can continue to use the handle until the file is closed. The default maximum number of file handles for a process is 50. An application can change this maximum by using DosSetMaxFH. When this call is made, all currently open file handles are preserved. In the past, the maximum number of file handles was 20. If you previously had code that increased the maximum file handles from 20 to less than 50, you can now remove this code. When an application starts, it inherits all open file handles from the process that starts it. If the system's command processor starts an application, file handles 0, 1, and 2 represent the standard input, standard output, and standard error files. The standard input file is the keyboard; the standard output and standard error files are the screen. An application can read from the standard input file and write to the standard output and standard error files immediately; it does not have to open the files first. An application can create a duplicate file handle for an open file by using DosDupHandle. A duplicate handle is identical to the original handle. Typically, duplicate handles are used to redirect the standard input and standard output files. For example, an application can open a disk file and duplicate the disk-file handle as handle 0. Thereafter, an application reading from the standard input file (handle 0) takes data from the disk file, not from the keyboard. When devices and pipes are accessed through the file system functions (using DosOpen, DosRead, and so on), the devices and pipes are treated as files and are identified using file handles. The standard file handles can be redirected to a device or pipe. ═══ 9.3.3. File Pointer ═══ Every open file has a file pointer that specifies the next byte to be read or the location to receive the next byte that is written. When a file is first opened, the system places the file pointer at the beginning of the file. As each byte is read or written, OS/2 advances the pointer. An application can also move the pointer by using DosSetFilePtr. When the pointer reaches the end of the file and the application attempts to read from the file, no bytes are read and no error occurs. Thus, reading 0 bytes without an error means the program has reached the end of the file. When an application writes to a disk file, the data being written is usually collected in an internal buffer. OS/2 writes to the disk only when the amount of data equals (or is a multiple of) the sector size of the disk. If there is data in the internal buffer when the file is closed, the system automatically writes the data to the disk before closing the file. An application can also flush the buffer (that is, write the contents of the buffer to the disk) by using DosResetBuffer. ═══ 9.3.4. Copying Files ═══ DosCopy is used to copy files and subdirectories. Metacharacters (global file name characters) are not allowed, so only individual files or entire subdirectories can be copied with this function. The source and target can be on different drives. When the source specified is a subdirectory and an I/O error occurs, the file being copied from the source directory to the target directory at the time of the error will be deleted from the target directory and DosCopy will be terminated. When a file is being copied and an I/O error occurs,  if the source file name is that of a file to be replaced, the file is deleted from the target path.  if the source file name is that of a file to be appended, the target file is resized to its original size. DosCopy will copy the attributes of the source file, such as date of creation, and time of creation to the target file. Additionally, DosCopy will copy the extended attributes of the source file when creating a new subdirectory or a new file, or when replacing an existing file. ═══ 9.3.5. Moving Files ═══ DosMove is used to move a file from one subdirectory to another on the same drive. In the process of moving the file, its name can be changed. Metacharacters (global file name characters) are not permitted. ═══ 9.3.6. Deleting Files ═══ Calling DosDelete removes the directory entry associated with a file name. Metacharacters (global file name characters) are not permitted. Files whose read-only attribute is set cannot be deleted. ═══ 9.3.7. Changing File Sizes ═══ DosSetFileSize is used to extend or truncate the size of a file that is open for Read/Write or Write-Only access. When a file is extended, for example to reserve disk space, the value of the additional bytes allocated by the system is undefined. ═══ 9.3.8. Locking and Unlocking File Regions ═══ Because OS/2 permits more than one application to open a file and write to it, it is important that the applications not write over each other's work. An application can protect against this problem by temporarily locking a region in a file. DosSetFileLocks provides a simple mechanism that temporarily prevents access by other processes to regions within a file. DosSetFileLocks specifies a range of bytes in the file that is locked by an application and that can be accessed only by the application locking the region. The application uses the same function to free the locked region. Locking and unlocking regions in a file enables sharing processes to coordinate their efforts. A process can lock a region in a file so the process can read and update the file region. A sharing process that attempts to lock the region before the other process finishes its update and unlocks the region receives an error code. When a lock is unsuccessful because a lock is already in place, ERROR_LOCK_VIOLATION is returned. A lock that extends beyond end-of-file is not considered an error. However, a locked region cannot overlap another locked region, nor can it encompass a locked region. Any such conflicting locks must be cleared before a region can be locked. When a file handle is duplicated, the duplicate handle has access to any locked regions currently being accessed by the original handle. Although a child process created with DosExecPgm can inherit the file handles of its parent process, it does not inherit access to locked regions in effect for a file handle unless the file handle is duplicated and passed to it. Note: File locks are intended to be in effect for only short periods of time. When a file with locks is closed, the locks are released in no defined order. ═══ 9.3.9. Searching for Files ═══ An application can use DosFindFirst, DosFindNext, and DosFindClose to search the current directory for all file names that match a given pattern. The pattern must be an OS/2 file name and can include metacharacters (global file name characters). The wildcard characters are the question mark (?) and the asterisk (*). The question mark matches any single character; the asterisk matches any combination of characters. For example, the pattern "A*" matches the names "ABC", "A23", and "ABCD", but the pattern "A?C" matches only the name "ABC". ═══ 9.3.10. Determining the Maximum Path Length ═══ An application that recognizes long file names might be run on systems with or without installable file systems. Additionally, the maximum length of a file name might vary from one installable file system to another. So that an application can properly handle this variable condition (and, for example, allocate large enough buffers to hold file names), the application should call DosQuerySysInfo to determine the maximum path length supported by the file system. Make this call before calling file system functions that require full path names. ═══ 9.3.11. Specifying an Extended LIBPATH ═══ The LIBPATH, which is set in CONFIG.SYS, specifies a search path which OS/2 uses when searching for dynamic link libraries (DLLs). The LIBPATH is set during system startup and cannot be changed dynamically. OS/2 provides the capability of specifying extensions to the LIBPATH. An Extended LIBPATH is a path which is searched in conjunction with the system LIBPATH, but which can be changed dynamically, either by the user from the command line, or by an application. There are two Extended LIBPATHs: BeginLIBPATH, which is searched before the system LIBPATH, and EndLIBPATH, which is searched after the system LIBPATH. Extended LIBPATHs are ASCIIZ strings which are formatted in the same manner as the system LIBPATH, that is, they contains a list of subdirectory paths separated by semicolons. The maximum size for each Extended LIBPATH is 1024 characters. Applications can use DosSetExtLIBPATH to set the Extended LIBPATHs. Likewise, they can use DosQueryExtLIBPATH to query the value of either of the Extended LIBPATHs. A flag parameter for the function specifies whether the BeginLIBPATH or the EndLIBPATH is being set or queried. The flag allows two values: BEGIN_LIBPATH (1) which will set or query BeginLIBPATH, or END_LIBPATH (2) which will set or query EndLIBPATH. You can call DosSetExtLIBPATH as often as you want. The new Extended LIBPATH that you pass in will replace the current Extended LIBPATH. You can specify the current Extended LIBPATH as part of the argument by using the % symbol before and after the Extended LIBPATH variable name, that is, as %BeginLIBPATH% or %EndLIBPATH%. For example, suppose you set BeginLIBPATH to "D:\MYDLLS", then later you want to add "D:\TOMSDLLS" before the existing BeginLIBPATH, and "D:\JOESDLLS" after the existing BeginLIBPATH (that is, you want to change BeginLIBPATH to "D:\TOMSDLLS;D:\MYDLLS;D:\JOESDLLS"). You could accomplish this by using "D:\TOMSDLLS;%BeginLIBPATH%;D:\JOESDLLS" as the argument for DosSetExtLIBPATH. The string argument can be up to 1024 characters long. However, if the resulting Extended LIBPATH is longer than 1024 characters, the function will return an error. ═══ 9.3.12. Devices ═══ OS/2 enables you to access peripheral devices using file system commands, and treat those devices as file streams of data. These devices include: Character devices COM, clock, console (keyboard and screen), screen, keyboard, printer, null, pointer, and mouse devices. Standard I/O devices Character devices automatically installed by OS/2 and recognized by the file system as the standard input, standard output, and standard error devices. Pseudocharacter devices An application can attach a device name to a file system and use the file system as a pseudocharacter device (also called a single-file device). Attaching a device name to a file system allows an application to open the device associated with the file system as if it were a character device (for example, a serial port) and read from and write to the device by using DosRead and DosWrite. In addition, an application can use DosSetFilePtr and DosSetFileLocks with a pseudocharacter device. Also, pseudocharacter devices can be redirected. A file system that can be attached to a pseudocharacter device is typically associated with a single disk file or with a special storage device, such as a tape drive. The file system establishes a connection with the device and transfers requests and data between OS/2 and the device. The user perceives this file as a device name for a nonexistent device. This file is seen as a character device because the current drive and directory have no effect on the name. A pseudocharacter device name is an ASCII string with the name of an OS/2 file in the form: \DEV\DEV_NAME The "\dev\" is a required part of the name, but there is no actual subdirectory named "\dev\". This just lets OS/2 know that it is a pseudocharacter device name. Logical file devices The hard disk or floppy disk drives, or the partitions on the hard disk or floppy disk drives. ═══ 9.4. Using File Commands ═══ Files are accessed through the file system using file handles. File handles are returned by DosOpen when the file is opened and are used for all subsequent accesses to the file. Files can be be created, opened, read from, written to, closed, copied, moved, deleted, renamed, and locked. Files can be searched for based on a metacharacter pattern template. Each open file has a file pointer that indicates the current reading or writing location within the file. The file pointer moves automatically with each read or write operation on the file and can be moved manually by the application. The standard file handles-standard input, standard output, and standard error-provide redirectable input and output to applications. The standard file handles can be used to read input from the keyboard and write output to the display. Alternatively, they can be redirected to read input from and write output to a file. To an application reading and writing the standard file handles, there is no difference between the two. Note: In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred. ═══ 9.4.1. Creating Files ═══ DosOpen is used to create files, which are then read from or written to. To create a file, specify FILE_CREATE as the sixth argument in the call to the function. DosOpen then creates the file, if it does not already exist. If the file already exists, the function returns the error value FILE_EXISTED. The following code fragment shows how to use DosOpen to create the file NEWFILE.TXT: Uses Crt,Dos,Os2Def,Os2Base; Var Hf : HFILE; UlAction : ULONG; Ulrc : APIRET; Begin ulrc := DosOpen('NEWFILE.TXT', (* Name of file to create and open *) hf, (* Address of file handle *) ulAction, (* Action taken *) 0, (* Size of new file *) FILE_NORMAL, (* File attributes *) FILE_CREATE, (* Creates the file *) OPEN_ACCESS_WRITEONLY or OPEN_SHARE_DENYNONE, nil); (* No extended attributes *) End. In this example, DosOpen creates the file and opens it for writing only. Note that the sharing method chosen permits other processes to open the file for any access. The new file is empty (contains no data). When you use DosOpen to create (or replace) a file, you must specify the attributes the new file is to have. In the preceding example, this value is FILE_NORMAL, so the file is created as a normal file. Other possible file attributes include read-only and hidden, which correspond to FILE_READONLY and FILE_HIDDEN, respectively. The possible file attributes are: File Attribute Defined Constant Normal file FILE_NORMAL Read-only file FILE_READONLY Hidden file FILE_HIDDEN System file FILE_SYSTEM Subdirectory FILE_DIRECTORY Archived file FILE_ARCHIVED. The file attribute affects how other processes access the file. For example, if the file is read-only, no process can open the file for writing. There is one exception-the process that creates the read-only file can write to it immediately after creating it. After closing the file, however, the process cannot reopen it for writing. If you are creating a new file object (a new file or a replacement for an existing one), you must specify the size of the new file in bytes. For example, if you specify 256, the file size is 256 bytes. However, these 256 bytes are undefined. If the file being opened already exists, the file size parameter is ignored. It is up to the application to write valid data to the file. No matter what size you specify, the file pointer is set to point to the beginning of the file so a subsequent call to DosWrite starts writing data at the beginning of the file. Extended attributes can be defined by an application when a file object is created. An application can define an extended attribute for a file when the file is created during a DosOpen call. Applications can also control access to specific regions within a file by calling DosSetFileLocks. ═══ 9.4.2. Opening Files ═══ Before performing input or output operations on a file, you must open the file and obtain a file handle. You obtain a file handle by using DosOpen. This function opens the specified file and returns a file handle for it. DosOpen can also be used to create new files. DosOpen establishes a connection betwee n a file object and an application. This connection is in the form of a 32-bit identifier called a file handle, which is used to refer to the file object and any information associated with it. DosOpen returns a handle that is used in other file system calls to gain access to the object. The file object can be a new file, an existing file, or a replacement for an existing file. It can also be a character device, a block device, or the client end of a named pipe. The type of object is determined by the file name you pass to DosOpen. Note: If the object is a named pipe, it must be in a listening state for DosOpen to be successful. The following code fragment shows the use of DosOpen to:  Open the existing file SAMPLE.TXT for reading  Put the file handle into the hf variable Uses Crt,Dos,Os2Def,Os2Base; Var Hf : HFILE; UlAction : ULONG; Ulrc : APIRET; Begin ulrc := DosOpen('SAMPLE.TXT', (* Name of file to open *) hf, (* Address of file handle *) ulAction, (* Action taken *) 0, (* Size of file *) FILE_NORMAL, (* File attribute *) FILE_OPEN, (* Open the file *) OPEN_ACCESS_READONLY or OPEN_SHARE_DENYNONE, nil); (* Extended attribute buffer *) End. If DosOpen successfully opens the file, it copies the file handle to the hf variable and copies a value to the ulAction variable indicating what action was taken (for example, FILE_EXISTED for "existing file opened"). A file size is not needed to open an existing file, so the fourth argument is 0. The fifth argument, FILE_NORMAL, specifies the normal file attribute. The sixth argument, FILE_OPEN, directs DosOpen to open the file if it exists or return an error if it does not exist. The seventh argument directs DosOpen to open the file for reading only and enables other applications to open the file even while the current application has it open. The final argument is a pointer to a structure containing information on extended attributes. If the file has no extended attributes, this argument must be nil. DosOpen returns 0 if it successfully op ens the file. Applications use the file handle in subsequent functions to read data from the file or to check the status and other file characteristics. If DosOpen fails to open the file, it returns an error value. When you open a file you must specify whether you want to read from the file, write to it, or both read and write. Also, since more than one application might attempt to open the same file, you must also specify whether you want to allow other processes to have access to the file while you have it open. A file that is shared can be shared for reading only, writing only, or reading and writing. A file that is not shared cannot be opened by another application (or more than once by the first application) until it has been closed by the first application. An application defines its file access rights (that is, I/O it needs to perform on a file) by setting the appropriate file access mode field in the file open-mode parameter. An application accesses a file as:  Read-only  Write-only  Read/write An application defines what I/O operations other processes can perform on a file by setting the appropriate sharing mode field in the OpenMode parameter. Other processes are granted:  Deny read/write access  Deny write access  Deny read access  Deny neither read or write access (deny none) File sharing requires cooperation between the sharing processes. For example, if process A calls DosOpen to open a file with Deny Write sharing and process B calls DosOpen to open the same file with Read/Write access, the DosOpen call made by process B fails. You indicate whether or not you want to permit another application to access your file by combining an OPEN_ACCESS_ value and an OPEN_SHARE_ value from the following list: File Access and Sharing Rights ┌──────────────────────────────┬──────────────────────────────┐ │Value │Meaning │ ├──────────────────────────────┼──────────────────────────────┤ │OPEN_ACCESS_READONLY │Open a file for reading. │ ├──────────────────────────────┼──────────────────────────────┤ │OPEN_ACCESS_WRITEONLY │Open a file for writing. │ ├──────────────────────────────┼──────────────────────────────┤ │OPEN_ACCESS_READWRITE │Open a file for reading and │ │ │writing. │ ├──────────────────────────────┼──────────────────────────────┤ │OPEN_SHARE_DENYREADWRITE │Open a file for exclusive use,│ │ │denying other processes read │ │ │and write access. │ ├──────────────────────────────┼──────────────────────────────┤ │OPEN_SHARE_DENYWRITE │Deny other processes write │ │ │access to a file. │ ├──────────────────────────────┼──────────────────────────────┤ │OPEN_SHARE_DENYREAD │Deny other processes read │ │ │access to a file. │ ├──────────────────────────────┼──────────────────────────────┤ │OPEN_SHARE_DENYNONE │Open a file with no sharing │ │ │restrictions, granting read │ │ │and write access to all │ │ │processes. │ └──────────────────────────────┴──────────────────────────────┘ In general, you can combine any access method (read, write, or read and write) with any sharing method (deny reading, deny writing, deny reading and writing, or grant any access). Some combinations have to be handled carefully, however, such as opening a file for writing without denying other processes access to it. Note: For named pipes, the access and sharing modes must be consistent with those specified by DosCreateNPipe. Other characteristics of the file handle that can be set: File Handle Characteristics ┌───────────────┬──────────────────────────────┐ │Flag │Purpose │ ├───────────────┼──────────────────────────────┤ │Inheritance │Handle is inherited by a child│ │ │process created with │ │ │DosExecPgm, or is private to │ │ │the calling process. │ ├───────────────┼──────────────────────────────┤ │Write-Through │Actual I/O for synchronous │ │ │writes is not guaranteed as │ │ │complete or is guaranteed as │ │ │complete before the write │ │ │returns. │ ├───────────────┼──────────────────────────────┤ │Fail-Errors │Media errors are reported by │ │ │the system critical error │ │ │handler, or by the │ │ │application. │ ├───────────────┼──────────────────────────────┤ │DASD │The file name parameter is the│ │ │name of a file or device │ │ │opened in the normal way, or a│ │ │drive specification for a │ │ │fixed disk or diskette drive. │ │ │The DASD flag can be set for │ │ │direct access to an entire │ │ │disk or diskette volume, │ │ │independent of the file │ │ │system. When the DASD flag is │ │ │set, the handle returned by │ │ │DosOpen represents the logical│ │ │volume as a single file. To │ │ │block other processes from │ │ │accessing the logical volume, │ │ │a DosDevIOCtl Category 8, │ │ │Function 0 call should be made│ │ │using the handle returned by │ │ │DosOpen. The DASD flag should │ │ │be set only by systems │ │ │programs, not by applications.│ ├───────────────┼──────────────────────────────┤ │Cache │The file system caches or does│ │ │not cache data for I/O │ │ │operations on the file. This │ │ │flag is advisory only. │ └───────────────┴──────────────────────────────┘ See the DosOpen material in the Control Program Programming Reference for details of these characteristics. After an object has been opened, its file handle state flags can be queried by calling DosQueryFHState and reset by calling DosSetFHState. See Determining and Setting the State of a File or Device Handle for information on determining the state of a file handle. When a child process inherits a file handle, it also inherits the sharing and access rights of the file. You cannot use metacharacters (global file name characters; * and ?) in file names you supply to DosOpen. ═══ 9.4.3. Reading from Files ═══ Once you open a file or have a file handle, you can read from and write to the file by using DosRead and DosWrite. DosRead is called with a handle (obtained with DosOpen) for a file, pipe, or device. DosRead copies a specified number of bytes (up to the end of the file) from the file object to the buffer you specify. OS/2 returns, in a parameter, the number of bytes actually read (which might not be the same as the number of bytes requested because the end of the file might have been reached). To read from a file, you must open it for reading or for reading and writing. The following code fragment shows how to open the file named SAMPLE.TXT and read the first 512 bytes from it: Uses Crt,Dos,Os2Def,Os2Base; Var Hf : HFILE; UlAction : ULONG; AbBuffer : Array[0..BUF_SIZE] of BYTE; CbRead : ULONG; Ulrc : APIRET; Begin ulrc := DosOpen('SAMPLE.TXT', hf, ulAction, 0, FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_READONLY or OPEN_SHARE_DENYNONE, nil); If (NOT ulrc) Then Begin DosRead(hf, abBuffer, sizeof(abBuffer), cbRead); DosClose(hf); End; End. If the file does not have 512 bytes, DosRead reads to the end of the file and puts the number of bytes read in the cbRead variable. If the file pointer is already positioned at the end of the file when DosRead is called, the function puts a 0 in the cbRead variable. ═══ 9.4.4. Writing to Files ═══ Once you open a file or have a file handle, you can read from and write to the file by using DosRead and DosWrite. DosWrite copies bytes from a buffer you specify to a file, device, or pipe. Calling DosWrite with a handle for a file, pipe, or device transfers the number of bytes specified from a buffer location to the object. The system returns, in a parameter, the number of bytes actually written (which in the case of a disk file might not be the same as the number requested because of insufficient disk space). To write to a file, you must first open it for writing or for reading and writing. The following code fragment shows how to open the file SAMPLE.TXT again and write 512 bytes to it: Uses Crt,Dos,Os2Def,Os2Base; Var Hf : HFILE; UlAction : ULONG; AbBuffer : Array[0..BUF_SIZE] of BYTE; CbWritten : ULONG; Ulrc : APIRET; Begin ulrc := DosOpen('SAMPLE.TXT', hf, ulAction, 0, FILE_NORMAL, FILE_CREATE, OPEN_ACCESS_WRITEONLY or OPEN_SHARE_DENYWRITE, nil); If (NOT ulrc) Then Begin DosWrite(hf, abBuffer, sizeof(abBuffer), cbWritten); DosClose(hf); End; End. DosWrite writes the contents of the buffer to the file. If it fails to write 512 bytes (for example, if the disk is full), the function puts the number of bytes written in the cbWritten variable. The data is read and written exactly as given; the function does not format the data-that is, they do not convert binary data to decimal strings, or vice versa. The Write-Through Flag If an application requires data to be written in a specific order, setting the Write-Through flag to 1 guarantees that actual I/O for a synchronous write is completed before the DosWrite returns. If this flag has been set with DosOpen for buffered I/O, and multiple synchronous writes are performed, the system cannot guarantee the actual order in which the data is written. For details on changing the state of the Write-Through flag, see Determining and Setting the State of a File or Device Handle. ═══ 9.4.5. Moving the File Pointer ═══ Every disk file has a corresponding file pointer that marks the current location in the file. The current location is the byte in the file that will be read from or written to on the next call to DosRead or DosWrite. Usually, the file pointer is at the beginning of the file when you first open or create the file, and it advances as you read or write to the file. You can, however, change the position of the file pointer at any time by using DosSetFilePtr. DosSetFilePtr moves the file pointer a specified offset from a given position. You can move the pointer from the beginning of the file, from the end, or from the current position. The following code fragment shows how to move the pointer 200 bytes from the end of the file: Uses Crt,Dos,Os2Def,Os2Base; Var AbBuf Array[0..BUF_SIZE] of BYTE; Hf : HFILE; UlRead : ULONG; ulWritten : ULONG; ulAction : ULONG; ulNewPtr : ULONG; Begin DosOpen(ЫSAMPLE.TXT', hf, ulAction, 0, FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_READONLY or OPEN_SHARE_DENYNONE, nil); DosSetFilePtr(hf, -200, FILE_END, ulNewPtr); DosRead(hf, abBuf, sizeof(abBuf), ulRead); DosWrite(HF_STDOUT, abBuf, ulRead, ulWritten); End. In this example, DosSetFilePtr moves the file pointer to the 200th byte from the end of the file. If the file is not that long, the function moves the pointer to the first byte in the file and returns the actual position (relative to the end of the file) in the ulNewPtr variable. You can move the file pointer for disk files only. You cannot use DosSetFilePtr on devices, despite using other file functions (DosOpen, DosRead) to access a device. If a file is read-only, write operations to the file will not be performed. Moving the pointer from the end of the file can be used to determine the size of the file. ═══ 9.4.6. Closing Files ═══ You can close a file by using DosClose. Since each application has a limited number of file handles that can be open at one time, it is a good practice to close a file after using it. To do so, supply the file handle in DosClose, as shown in the following code fragment: Uses Crt,Dos,Os2Def,Os2Base; Var Hf : HFILE; UlAction : ULONG; AbBuffer Array[0..BUF_SIZE] of BYTE; UlRead : ULONG; Ulrc : APIRET; Begin ulrc := DosOpen('SAMPLE.TXT', hf, ulAction, 0, FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_READONLY or OPEN_SHARE_DENYNONE, nil); If (NOT ulrc) Then Begin DosRead(hf, abBuffer, sizeof(abBuffer), ulRead); DosClose(hf); End; End. If you open a file for writing, DosClose directs the system to flush the file buffer-that is, to write any existing data in OS/2's intermediate file buffer to the disk or device. The system keeps these intermediate file buffers to make file input and output more efficient. For example, it saves data from previous calls to DosWrite until a certain number of bytes are in the buffer then writes the contents of the buffer to the disk. DosClose also closes the handle to the file (or pipe, or device). If one or more additional handles to a file have been created with DosDupHandle , the internal buffers for the file are not written to disk, and its directory entry is not updated, until DosClose has been called for all the handles. ═══ 9.4.7. Creating Duplicate File or Device Handles ═══ DosDupHandle enables a process to create a duplicate handle for an open file, pipe, or device. The value for the old-file-handle parameter is the handle of an open file, a pipe, or a device. The valid values for the new-file-handle parameter include FFFFH, 0000H (standard input), 0001H (standard output), and 0002H (standard error). Any value other than FFFFH is assumed to be the value of the new file handle. A value of FFFFH causes the system to allocate a new file handle and send it to this location. If the value specified for the new-file-handle parameter is that of a currently open file, the file handle is closed before it is redefined. An agreed upon value for a duplicate file handle can be established between a parent process and a child process. Avoid choosing arbitrary values for the new file handle. The duplicate handle acquires the characteristics of the original. If you move the read/write pointer of the original file handle, for example by calling DosRead, DosWrite, or DosSetFilePtr , the pointer of the duplicate handle is also moved. If the original handle has access to regions in a file that have been locked by DosSetFileLocks , the duplicate also has access. If inheritance was indicated when a file was opened with DosOpen, a parent process can create a duplicate handle for the file and pass it to a child process by means of shared memory. This permits the child to close the duplicate handle without affecting the file handle of the parent. Because a parent process controls the meanings for standard I/O done by any child process it creates, the parent can use DosDupHandle to redefine unnamed pipe handles as standard I/O handles to communicate with a child. The steps involved are:  The parent process creates two pipes and duplicates the read handle of one pipe as 0000 and the write handle of the other pipe as 0001.  When the child process performs standard I/O, instead of reading from the keyboard and writing to the display, it reads from and writes to the pipes created by its parent.  As the owner of the pipe, the parent process uses its read and write handles to write to the pipe defined to the child as standard input and read from the pipe defined to the child as standard output. ═══ 9.4.8. Determining and Setting the State of a File or Device Handle ═══ After a file has been opened, the file handle state flags set with a DosOpen can be queried and reset by calling DosQueryFHState and DosSetFHState . The handle returned by DosSetFHState is used for subsequent input and output to the file. The following code fragment calls DosSetFHState to set the File Write-Through flag for an opened file. Writes to the file may go through the file system buffer, but the sectors are to be written before any synchronous write call returns. DosQueryFHState is called first to obtain the file handle state bits. Assume that the appropriate file handle has been placed into FileHandle already. Uses Crt,Dos,Os2Def,Os2Base; Var HfFileHandle : HFILE; (* File handle *) UlFileHandleState : ULONG; (* File handle state *) Ulrc : APIRET; (* Return code *) Begin ulrc := DosQueryFHState(hfFileHandle, FileHandleState); If (ulrc <> 0) Then Begin Write('DosQueryFHState error: return code = ', ulrc); Halt; End; ulFileHandleState := UlFileHandleState OR OPEN_FLAGS_WRITE_THROUGH; ulrc := DosSetFHState(hfFileHandle, ulFileHandleState); If (ulrc <> 0) Then Begin Write('DosSetFHState error: return code = ', ulrc); Halt; End; End. Here are two scenarios involving the use of this function.  An application requires that data be written in a specific order. To guarantee the order of the data written, it must perform separate synchronous write operations. The application can call DosSetFHState to set the Write-Through flag for the file. This action does not affect any previous asynchronous writes, whose data can remain in the buffers.  An application cannot handle a certain critical error situation. DosSetFHState can be called to reset critica l error handling as OS/2's responsibility. The I/O function that caused the critical error must be called again so the error can recur, causing control to be passed to OS/2. In the case where asynchronous I/O is being done, the precise time the results of this function will be available to the application is unpredictable. ═══ 9.4.9. Determining the Handle Type ═══ DosQueryHType enables an application to determine whether a handle is to a file, a pipe, or a device. This function can be used when a file-oriented application needs to modify its behavior, depending on the source of its input. For example, CMD.EXE suppresses writing prompts when its input is from a disk file. The following code fragment determines whether a given file handle refers to a file or a device. Assume that the desired file handle has been placed into FileHandle already. Uses Crt,Dos,Os2Def,Os2Base; Var HfFileHandle : HFILE; (* File handle *) UlHandType : ULONG; (* Handle type (returned) *) UlFlagWord : ULONG; (* Device driver attribute (returned) *) Ulrc : APIRET; (* Return code *) Begin ulrc := DosQueryHType(hfFileHandle, ulHandType, ulFlagWord); If (ulrc <> 0) Then Begin Write('DosQueryHType error: return code = ', ulrc); return; End; End. In the preceding example, DosQueryHType returns a value that characterizes the type of file handle, and the associated device driver attribute word, if the handle type indicates that the file handle is associated with a local character device. ═══ 9.4.10. Searching for Files ═══ You can locate files with names that match a given pattern by using metacharacters in DosFindFirst and DosFindNext . DosFindFirst searches the current directory and locates the first file name that matches the given pattern. DosFindNext locates the next matching file name and continues to find additional matches on each subsequent call until all matching names are found. The functions copy the file statistics on each file located to a data structure that you supply. The file information returned by a search includes file dates and times, length of data in the file, file size, file attributes, and file name. To find all files that match the file specification, call DosFindNext repeatedly until the message ERROR_NO_MORE_FILES is returned. Then call DosFindClose to close the directory handle. The following code fragment shows how to find all file names that have the extension ".C": Uses Crt,Dos,Os2Def,Os2Base; Var HdHdir : HDIR; UlFilenames : ULONG; ffbFindBuf : FILEFINDBUF3; Ulrc : APIRET; Begin ulFilenames := 1; hdHdir := HDIR_SYSTEM; ulrc := DosFindFirst('*.C', hdHdir, (* Directory handle *) FILE_NORMAL, (* File attribute to look for *) ffbFindBuf, (* Result buffer *) sizeof(ffbFindBuf), (* Size of result buffer *) ulFilenames, (* Number of matching names to look for *) FIL_STANDARD); (* Standard level of information *) If (NOT ulrc) Then Begin Repeat . . (* Use file name in findbuf.achName *) . ulrc := DosFindNext(hdHdir, ffbFindBuf, sizeof(ffbFindBuf), ulFilenames); Until ulrc = 0; End; DosFindClose(hdHdir); End. In this example, DosFindNext continues to retrieve matching file names until it returns an error value (it returns ERROR_NO_MORE_FILES when it cannot find any more matching files). To keep track of which files have already been found, both functions use the directory handle, hdir. This directory handle must be set to HDIR_SYSTEM or HDIR_CREATE before the call to DosFindFirst. HDIR_SYSTEM (00000001H) tells OS/2 to use the system handle for standard output, which is always available to the requesting process. HDIR_CREATE (FFFFFFFFH) tells OS/2 to allocate a new, unused handle. The directory handle returned by DosFindFirst must be used in subsequent calls to DosFindNext ; it identifies for DosFindNext the name of the file being sought and the current position in the directory. An attribute parameter for DosFindFirst allows hidden and system files, as well as normal files, to be included in searches. After locating the files you need, use DosFindClose to close the directory handle. This ensures that when you search for the same files again, you will start at the beginning of the directory. After DosFindClose is called, a subsequent DosFindNext fails. ═══ 9.4.11. Searching Paths for Files ═══ DosSearchPath searches directory paths for t he name of a file object. The file specification can include metacharacters (global file name characters). The path string used in the search consists of directory paths separated by semicolons. The caller can supply the path string, or it can supply the name of an environment variable whose value is the path string to be searched. The caller can request that the current working directory be searched before the path string is searched. If the caller specifies an environment variable, DosSearchPath uses DosScanEnv to find the path string. DosScanEnv searches the environment segme nt for an environment variable name; for example, DPATH. The result pointer points to the string that is the value of the environment variable. The call to DosScanEnv can be made direct ly by the application, or it can be invoked by DosSearchPath. If the file is found, its full path name is returned, with metacharacters left in place. The results might not be meaningful if a buffer overflow occurs. As an example, assume that a string such as the following exists in the environment: DPATH=C:\SYSDIR;C:\INIT The following code fragment illustrates a method for searching directory paths to find a file. Uses Crt,Dos,Os2Def,Os2Base; Var PszPathRef : PSZ; AchResultBuffer : Array[0..ResultBufLen] of UCHAR; PszFile : PSZ; Begin PszPathRef := ''; PszFile := 'OS2.INI'; DosScanEnv('DPATH', pszPathRef); DosSearchPath(0, (* Path source bit=0 *) pszPathRef, pszFile, achResultBuffer, ResultBufLen); Writeln('Result 1: ', achResultBuffer); DosSearchPath(2, (* Path source bit=1 *) 'DPATH', pszFile, achResultBuffer, ResultBufLen); Writeln('Result 2: ', achResultBuffer); Halt; End. ═══ 9.5. Specifying Extended LIBPATHs ═══ An Extended LIBPATH is a path which is searched in conjunction with the system LIBPATH, but which can be changed dynamically, either by the user from the command line, or by an application. There are two Extended LIBPATHs: BeginLIBPATH, which is searched before the system LIBPATH, and EndLIBPATH, which is searched after the system LIBPATH. Applications can use DosSetExtLIBPATH to set the Extended LIBPATHs. Likewise, they can use DosQueryExtLIBPATH to query the value of either of the Extended LIBPATHs. A flag parameter for the function specifies whether the BeginLIBPATH or the EndLIBPATH is being set or queried. The flag allows two values: BEGIN_LIBPATH (1) which will set or query BeginLIBPATH, or END_LIBPATH (2) which will set or query EndLIBPATH. Extended LIBPATHs are ASCIIZ strings which are formatted in the same manner as the system LIBPATH, that is, they contains a list of subdirectory paths separated by semicolons. The string argument can be up to 1024 characters and will return an error if longer. The following example updates the path to be searched before the system LIBPATH, then queries and displays the value. Uses Crt,Dos,Os2Def,Os2Base; Var UchBeginLIBPATH : Array[0..1023] of UCHAR; (* Begin LIBPATH value returned *) Ulrc : APIRET; (* Return code *) Begin Ulrc := NO_ERROR; StrCopy(UchBeginLIBPATH,''); ulrc := DosSetExtLIBPATH('C:\TOOL_X\VERS_20\DLL', BEGIN_LIBPATH); (* Add to beginning LIBPATH *) If (ulrc <> NO_ERROR) Then Begin Writeln('DosSetExtLIBPATH error: return code = ', ulrc); Exit; End; ulrc := DosQueryExtLIBPATH(uchBeginLIBPATH, BEGIN_LIBPATH); (* Query the BeginLIBPATH *) If (ulrc <> NO_ERROR) Then Begin Writeln('DosQueryExtLIBPATH error: return code = ', ulrc); Exit; End; Writeln(' BeginLIBPATH := ', uchBeginLIBPATH); End. ═══ 9.6. Standard File Handles ═══ Every application, when it first starts, has three input and output file handles available to use. These file handles, called the standard input, standard output, and standard error files, enable the application to read input from the keyboard and display output on the screen without opening or preparing the keyboard or screen. ═══ 9.6.1. Standard Input, Output, and Error File Handles ═══ As OS/2 starts an application, it automatically opens the three standard files and makes the file handles-numbered 0, 1, and 2-available to the application. Applications can read from and write to the standard files as soon as they start. Standard Input File handle 0 is the standard input file. This handle can be used to read characters from the keyboard with DosRead. The function reads the specified number of characters unless the user types a turnaround character-that is, a character that marks the end of a line (the default turnaround character is a carriage-return/linefeed character pair). As DosRead reads the characters, it copies them to the buffer you have supplied, as shown in the code fragment below. In this example, DosRead copies the number of characters read from standard input to to variable cbRead. DosRead also copies the turnaround character, or characters, to the buffer If the function reads fewer than 80 characters, the turnaround character is the last one in the buffer. Standard Output File handle 1 is the standard output file. This handle can be used to write characters on the screen with DosWrite. The function writes the characters in the given buffer to the current line. If you want to start a new line, you must place the current turnaround character in the buffer. The code fragment below:  Displays a prompt  Reads a string  Displays the string Uses Crt,Dos,Os2Def,Os2Base; Var UlWritten : ULONG; ulRead : ULONG; AbBuffer : Array[0..BUF_SIZE] of BYTE; Begin @error@ static UCHAR ucEnterName[] := 'Enter a name: '; DosWrite(HF_STDOUT, ucEnterName, sizeof(ucEnterName), ulWritten); DosRead(HF_STDIN, abBuffer, sizeof(abBuffer), ulRead); DosWrite(HF_STDOUT, abBuffer, ulRead, ulWritten); End. Standard Error File handle 2 is the standard error file. This handle, like the standard output handle, enables applications to write characters on the screen. Most applications use the standard error file to display error messages, enabling the application to redirect standard output to a file without also redirecting error messages to the file. ═══ 9.6.2. Redirecting Standard File Handles ═══ The standard input, standard output, and standard error files are usually the keyboard and screen, but not always. For example, if you redirect standard output by using the greater-than (>) redirection symbol on the application's command line, all data written to the standard output file goes to the given file. The following command line redirects standard output to the file SAMPLE.TXT and redirects error messages to the file SAMPLE.ERR: type startup.cmd >sample.txt 2>sample.err When a standard file is redirected, its handle is still available but corresponds to the given disk file instead of to the keyboard or screen. You can still use DosRead and DosWrite to read from and write to the files. You can use DosDupHandle to redirect a standard file from inside your application. If you duplicate the standard input file handle, your application reads from the specified file rather than from the keyboard. Duplicating the standard output file handle causes output to be directed to a file or device instead of to the standard output device. The following code fragment shows how to use the standard input handle to read from a file: Uses Crt,Dos,Os2Def,Os2Base; Var AbBuffer Array[0..BUF_SIZE] of BYTE; Hf : HFILE; hfNew : HFILE; UlRead : ULONG; UlWritten : ULONG; UlAction : ULONG; Ulrc : APIRET; Begin ulrc := DosOpen('SAMPLE.C', hf, ulAction, 0, FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_READONLY OR OPEN_SHARE_DENYNONE, nil); If (NOT ulrc) Then Begin hfNew := 0; (* Duplicates standard input *) DosDupHandle(hf, hfNew); DosRead(HF_STDIN, abBuffer, sizeof(abBuffer), ulRead); DosWrite(HF_STDOUT, abBuffer, ulRead, ulWritten); End; End. ═══ 10. File Names ═══ File names are the identifiers used by the file system to uniquely identify files on a disk. All file systems have specific rules for constructing names of file objects. Different file systems can have different rules for naming file objects. The OS/2 FAT file system supports the DOS naming conventions. The OS/2 High Performance File System (HPFS) supports a superset of the DOS naming conventions, allowing for long file names and characters illegal under DOS. Although different file systems can have different rules for naming file objects, all OS/2 file systems require that full path names consist of directory and file names separated by backslashes (\). The OS/2 operating system views path names as ASCII strings and does not restrict file systems to the DOS file name format. Compatibility with existing DOS applications requires that all installable file systems support a superset of the 8.3 file name format used in the FAT file system. The following topics are related to the information in this chapter:  File Systems  File Management  Extended Attributes  Device I/O ═══ 10.1. File-Naming Conventions ═══ File name conventions are the rules used to form file names in a given file system. Although each installable file system (IFS) can have specific rules about how individual components in a directory or file name are formed, all file systems follow the same general conventions for combining components. For example, although the FAT file system requires that file and directory names have the 8.3 file name format, and HPFS supports names of up to 255 characters long, both file systems use the backslash (\) character to separate directory names and the file name when forming a path. When creating names for directories and files, or when processing names supplied by the user, an application must follow these general rules:  Process a path as a nil-terminated string. An application can determine maximum length for a path by using DosQuerySysInfo.  Use any character in the current code page for a name, but do not use a path separator, a character in the range 0 through 31, or any character explicitly prohibited by the file system. The following characters are reserved by the operating system. Do not use them in directory or file names. < > : " / \ | Although a name can contain characters in the extended character set (128 - 255), an application must be able to switch code pages if necessary to access the corresponding file.  Compare names without regard to case. Names such as "ABC", "Abc", and "abc" are considered to be the same.  Use the backslash (\) or the forward slash (/) to separate components in a path. No other character is accepted as a path separator.  Use the dot (.) as a directory component in a path to represent the current directory.  Use two dots (..) as a directory component in a path to represent the parent of the current directory.  Use a period (.) to separate components in a directory name or file name. Unless explicitly defined by a file system, no restrictions are placed on the number of components in a name. ═══ 10.1.1. File Names in the FAT File System ═══ Valid file names in the OS/2 FAT file system have the following form: [drive:][directory\]filename[extension] The drive parameter must name an existing drive and can be any letter from A through Z. The drive letter must be followed by a colon (:). The directory parameter specifies the directory that contains the file's directory entry. The directory name must be followed by a backslash (\) to separate it from the file name. If the specified directory is not the current directory, directory must include the names of all the directories in the path, separated by backslashes. The root directory is specified by using a backslash at the beginning of the name. For example, if the directory ABC is in the directory SAMPLE, and SAMPLE is in the root directory, the directory specification is: \SAMPLE\ABC. A directory name can also have an extension, which is any combination of up to three letters, digits, or special characters, preceded by a period (.). The filename and extension parameters specify the file. FAT File-Naming Rules For file objects managed by the FAT file system, the following rules apply:  File names are limited to 8 characters before and three characters after a single dot. This is referred to as the 8.3 file name format. The 8 characters before the dot are blank-filled. Embedded blanks are significant, trailing blanks and blanks immediately preceding the dot are not significant. Trailing blanks are truncated. For example, "FILE.A" is really "FILE .A ". "FILE.A" and "FILE .A " are treated as the same file by the operating system and refer to the same file. Also, "FILE.TXT " and "FILE.TXT" are treated as the same file. Blanks elsewhere in the name are significant-"F I L E.TXT" is not the same as "FILE.TXT".  Names are not case sensitive. This means that "FILE.TXT" and "file.txt" refer to the same file. Lowercase and uppercase characters are folded together for name comparison purposes.  Names returned by file system functions are in uppercase. This means that if "file.txt" is created, DosFindFirst returns "FILE.TXT".  Directory and file names can be any combination of up to eight letters, digits, or the following special characters: $ % ' - _ @ { } ~ ` ! # ( ) File extensions can be any combination of up to three letters, digits, or special characters, preceded by a period.  Invalid characters for directory names, file names, and volume labels are: - the range 0 - 1Fh - and the characters: < > | + = : ; , . " / \ [ ] ═══ 10.1.2. File Names in the High Performance File System ═══ In HPFS, file names can be up to 255 characters long (one must be a terminating nil, "\0"). Directory names can also be 255 characters long, but the length of the complete path, including drive, directories, and file name, cannot exceed 260 characters. Certain characters that are illegal in the FAT file system are legal in HPFS file names: + = ; , [ ] Also, blank spaces can be used anywhere in an HPFS file name or directory name, but blank spaces and periods at the end of a file name are ignored. Additionally, the period (.) is a valid file name character and can be used as many times as desired. There is no requirement that HPFS file names have extensions; however, many applications still create and use them. An HPFS file name can be all uppercase, all lowercase, or mixed case. The case is preserved for directory listings but is ignored in file searches and all other system operations. Therefore, in a given directory, there cannot be more than one file with the same name when the only difference is case. File-Naming Rules for Installable File Systems For file objects managed by OS/2 installable file systems, the following rules apply:  Each element of a full path name residing on a disk managed by an installable file system can consist of up to 255 characters. File names can be up to 255 characters long (one of the characters must be a terminating nil, "\0"). Directory names can also be 255 characters long, but the length of the complete path, including drive, directories, and file name, cannot exceed 260 characters. For example, in the path name "c:\XXX...XXX\YYY", "XXX...XXX" can include up to 255 characters. This is referred to as long file names.  Names are not case sensitive.  File name case as specified at create time is preserved. This means that if the file "file.TXT" is created, DosFindFirst returns "file.TXT". File name case may be modified using DosMove.  Blanks immediately preceding a dot are significant. This means that "FILE.TXT" and "FILE .TXT" refer to different files.  Trailing blanks are truncated. This means that "FILE.TXT " is the same as "FILE.TXT".  Blanks elsewhere in the name are significant. This means that "F I L E.TXT" is not the same as "FILE.TXT".  For compatibility reasons, trailing dots on component names are discarded. For Example, "\FILE.TXT...TEXT...\A..B...\C." becomes "\FILE.TXT...TEXT\A..B\C". This processing includes semaphore, queue, pipe, module, shared memory names, and device names.  The set of legal characters is expanded to include + = ; , [ ] as well as all characters legal for the FAT file system.  If an installable file system uses a component separator within a file name, it must be a dot (.). There are no restrictions on the number of components which can be allowed within a file name, for example "My.Programming.Reference.Part.One". ═══ 10.1.3. Long File Names ═══ Programs that recognize long file names must indicate this by including the NEWFILES statement in their module definition file. This statement directs the linker to set a bit in the executable file header. It indicates that the module supports long file names. This bit is meaningless in a DOS Session and on versions of the OS/2 operating system prior to Version 1.2. Programs written for OS/2 Version 1.2 (and all later versions) installable file systems should set this bit. Bound programs that have this bit set can see files with long file names in OS/2 mode, but only files with 8.3 file name format in DOS Sessions. This bit has meaning when attached to program modules, not when attached to DLLs. Whether the program recognizes long file names format is entirely dependent on the value of its NEWFILES bit and the effect of the bit extends into any calls to DLLs. In order to be compatible with all OS/2 file systems, dynamic link libraries must not create internal temporary files or directories that do not comply with 8.3 file naming conventions. In addition, dynamic link libraries cannot return long file names to an application. (The caller might be running on a file system that only supports 8.3 file names and use the returned name to create a file.) OS/2 applications which do not recognize long file names can run with some restrictions. For these programs, long names (including device names) are filtered according to the following rules:  Any name not representable in the 8.3 file name format is not returned from DosFindFirst or DosFindNext. This is because the application's buffers are unlikely to be large enough to handle longer names.  Any long file name passed to the file system functions listed below are rejected in exactly the same way as under previous versions of the OS/2 operating system. It is not acceptable to create and manipulate a name that you cannot find. DosOpen DosDelete DosMove DosQueryPathInfo DosSetPathInfo DosCreateDir DosDeleteDir DosFindFirst DosFindNext DosQueryFSAttach DosFSAttach DosCopy DosSearchPath  Long file names can be passed to DosSetCurrentDir and DosQueryCurrrentDir so that all programs can use all directories.  Long names used with non-file system functions (for example, DosCreateSem) are not filtered. For files located on file devices managed by the OS/2 FAT file system, long file names are handled differently in OS/2 mode than in DOS mode. In OS/2 mode, the long file name is considered an error. In DOS mode, the name is truncated and is not an error. The DOS mode treatment of file name formats provides compatibility with the PC-DOS environment for applications originally written for PC-DOS. However, if you are writing a family application to run under both the OS/2 operating system and the PC-DOS environment, your application must allow for this difference in operating environments. Because long file names can be input to applications through program command lines, dialog boxes, or function calls, applications must provide their users with rules for how to enter file names. File Names in User Input provides some general guidelines in this matter, that are applicable to both long file names and 8.3 file names. ═══ 10.1.4. Moving Files with Long Names ═══ The Workplace Shell supports copying files with long file names to media that is managed by a non-installable file system (IFS) and for returning these files to IFS media with the long name intact. When a file with a long name is copied to media that does not support long file names, the Workplace Shell stores the file's long name in the .LONGNAME extended attribute. When the file is copied back to a disk that does support long file names, the Workplace Shell restores the long name from the extended attribute. If the new media does not support extended attributes, files that have long names cannot be moved to the media without having their names modified or truncated. Note: The behavior described above only applies to the Workplace Shell The command processors, CMD.EXE and COMMAND.COM, do not automatically save the long file name; they require the user to enter a new file name that is legal on the new media. The DosCopy command also does not save the long file name automatically; the programmer must provide the target file name to DosCopy and the target file name must be a legal file name for the target media. If you choose to store and restore the file's long name, you must do it yourself in the manner described above. ═══ 10.2. Metacharacters in File Names ═══ Metacharacters are characters that can be used to represent placeholders in a file name. The asterisk (*) and the question mark (?) are the two metacharacters. The asterisk matches one or more characters, including blanks, but does not match the period. The question mark matches exactly one character, unless that character is a period. To match a period, the original name must contain a period. Metacharacters are illegal in all but the last component of a path. Metacharacters are also referred to as global file name characters, or as wildcard characters. An application that allows more than one file name on its command line, can accept metacharacters to provide users with a shortcut for entering a long list of names. For instance, metacharacters can be used to reference a set of files with a common base name; to reference all files with an extension of EXE, the user would enter: *.exe Although a name that contains metacharacters is not a complete file name, an application can use functions, such as DosFindFirst and DosEditName, to expand the name (replace the metacharacters) and create one or more valid file names. Metacharacters have two sets of semantics:  As search metacharacters, which are used to select the files that are returned to the user when the user searches the disk for a file.  As edit metacharacters, which are used to construct a new file name, given a source name and a target name specification. Both asterisks and question marks, therefore, have two sets of rules, one for searching for file names and one for editing file names. Search metacharacters are used in commands that search for files or groups of files, like DIR: dir *.exe An application can expand a name with metacharacters to a list of file names by using DosFindFirst and DosFindNext. These functions take a file name template (a name with metacharacters) and return the names of files on the disk that match the pattern in the template. Edit metacharacters are used in commands that can change the names of files; for example, in a global copy command: copy *.txt *.old An application can create a new file name from an existing name by using the DosEditName function. This function takes a template (a name with metacharacters) and expands it, using characters from an existing name. An asterisk in the template directs the function to copy all characters in the existing name until it locates a character that matches the character following the asterisk. A question mark directs the function to copy one character, unless that character is a period. The period in the template directs the function to look for and move to the next period in the existing name, skipping any characters between the current position and the period. ═══ 10.2.1. Searching for Files Using Metacharacters ═══ An asterisk (*) matches 0 or more characters, any character, including blank. It does not cross nil or \, which means it only matches a file name, not an entire path. A question mark (?) matches 1 character, unless what it would match is a period (.) or the terminating nil, in which case it matches 0 characters. It also does not cross the backslash character (\). Any character, other than asterisks and question marks, matches itself, including a period. Searching is case-insensitive. For example, "FILE.TXT" references the same file named "file.txt". For compatibility reasons, any file name that does not have a dot in it gets an implicit one automatically appended to the end during searching operations. This means that searching for "FILE." would return "FILE". Some file system functions accept file object name specifications using metacharacters. ═══ 10.2.2. Editing File Names Using Metacharacters ═══ Metacharacters in a source name simply match files and behave just like any other search metacharacter. Metacharacters in a target name are copy-edit commands and work as follows:  A question mark (?) copies one character unless the character it would copy is a period (.), in which case it copies 0 characters. It also copies 0 characters if it is at the end of the source string.  An asterisk (*) copies characters from the source name to the target name until it finds a source character that matches the character following it in the target.  A period (.) in the target name causes the source pointer to match the corresponding "." in the target. They count from the left. Editing is case-insensitive. If a case conflict between the source and editing string arises, the case in the editing string is used, thus: copy file.txt *E.tmp results in file.txt being copied as filE.tmp. DosEditName provides applications with the ability to transform a file object name into another name, using an editing string that contains global characters. ═══ 10.2.3. Transforming File Names Using Metacharacters ═══ File system functions that an application uses to copy, rename or move file objects do not support the use of global characters. For example, a user can perform a global copy of all files with the extension .EXE by entering the following on the command line: copy *.exe An application, however, cannot perform a similar global copy operation by making a single call to DosCopy or DosMove. These functions operate on a single, specific file object. DosEditName, however, provides applications with the ability to transform an element of a full path name into another name, using an editing string that contains global characters. For example, for an application to copy all files with an extension of .SRC to files with an extension of .SAM, the application would: 1. Search for all files with the .SRC extension by using DosFindFirst and DosFindNext, 2. Transform the file names by using DosEditName with an editing string of "*.SAM", 3. Copy the files with the new extension with DosCopy. ═══ 10.3. File Names in User Input ═══ Users often supply file names as part of an application's command line or in response to a prompt from the application. Traditionally, users have been able to supply more than one file name by separating the names with certain characters, such as a blank space. In some file systems, however, traditional separators are valid file name characters. This means additional conventions are required to ensure that an application processes all characters in a name. When an application processes arguments (including file names) from its command line, the operating system treats the double quotation mark (") and the caret (^) as quotation characters. All characters between the opening and closing double quotation marks are processed as a single argument. The caret is used to quote characters that would otherwise have some special property. The character immediately following the caret is treated as a normal character; any special characteristics that the character has are to be ignored. For example, the greater-than symbol (>) normally causes a program's output to be redirected to a file or device. Typing "^>" causes the ">" to be included in the command line passed to the application. In both cases, the operating system discards the quotation characters and does not treat them as part of the final argument. When a Presentation Manager*(PM) application processes two or more file names from a dialog box or other prompt, it expects the user to enter each file name on a new line. Therefore, a PM application would use a multiple-line entry field to prompt for multiple file names. This often makes the use of quotation characters unnecessary. When an application is started, the operating system constructs a command line for the application. If the command line includes file names, the operating system places a space character between names and marks the end of the list with two nil characters. Applications that start other applications by using DosExecPgm can also pass arguments by using this convention or by using quotation characters. In practice, most applications receive a command line as a single, nil-terminated string. Therefore, applications that use DosExecPgm should prepare command lines as a single string, and enclose any file names in quotation marks. ═══ 10.4. Device Names ═══ Naming conventions for character devices are similar to those for naming files. The OS/2 operating system has reserved certain names for character devices supported by the base device drivers. These device names are listed below: CLOCK$ Clock COM1-COM4 First through fourth serial ports CON Console keyboard and screen KBD$ Keyboard LPT1 First parallel printer LPT2 Second parallel printer LPT3 Third parallel printer MOUSE$ Mouse NUL Nonexistent (dummy) device POINTER$ Pointer draw device (mouse screen support) PRN The default printer, usually LPT1 SCREEN$ Screen These names can be used with DosOpen to open the corresponding devices. Reserved device names take precedence over file names; DosOpen checks for a device name before checking for a file name. Do not use a file name which is the same as a reserved device name; the file will never be opened, because the command will open the device instead. COM1 through COM4 are reserved device names only when the ASYNC (RS-232C) device driver is loaded. The same is true for POINTER$ and MOUSE$, which are reserved only when a mouse device driver is loaded. An application can call DosQueryFHState to verify that a file or device has been opened. See Determining and Setting the State of a File or Device Handle for more information on getting the state of a file handle. ═══ 11. File Systems ═══ An application views a file as a logical sequence of data; OS/2 file systems manage the physical locations of that data on the storage device for the application. The file systems also manage file I/O operations and control the format of the stored information. Applications use the OS/2 file system functions to open, read, write, and close disk files. File system functions also enable an application to use and maintain the disk that contains the files-the volumes, the directories, and the files on the disks of the computer. Applications also use OS/2 file system functions to perform I/O operations to pipes and to peripheral devices connected to the computer, like the printer. The following topics are related to the information in this chapter:  File names  File management  Extended attributes  Device I/O ═══ 11.1. About File Systems ═══ A file system is the combination of software and hardware that supports storing information on a storage device. In the OS/2 operating system, the file system specifies how data is organized on the mass storage devices of the computer, such as hard disks and floppy disks. Each drive is assigned a unique letter to distinguish it from other drives. A single hard disk can also be divided into two or more logical drives. A logical drive represents a portion of the hard disk and, like a physical drive, is assigned a unique letter to distinguish it from other physical and logical drives. The file system organizes disks into volumes, directories, and files. A volume is the largest file system unit. It represents all the available storage on the logical drive. An optional volume name identifies the volume. Volumes are subdivided into directories, which contain files and other subdirectories. Each volume has a root directory, which contains file and directory entries. All other subdirectories trace their ancestry back to the root directory. Each directory entry identifies the name, location, and size of a specific file or subdirectory on the disk. A file is one or more bytes of data stored on the disk. Subdirectories provide an additional level of organization and, like the root directory, can contain files and directory entries. The file system also enables users and applications to access certain non-disk devices as if they were files. An example of such a device would be the printer, which can be accessed through the file system by using the printer's logical name, PRN, as a file name. ═══ 11.1.1. Types of File Systems ═══ The OS/2 operating system has two file systems: the file allocation table (FAT) file system and the high performance file system (HPFS). These file systems define how information is organized on the storage devices. A user can choose to install either or both file systems. An application must be able to work with any file system. The OS/2 operating system provides a common set of file system functions that are not dependent on a particular file system. Both of these file systems support:  Existing logical file and directory structure  Existing naming conventions  Multiple logical volumes (partitions)  Multiple and different storage devices  Redirection or connection to remote file systems  Extended attributes  Metacharacter file-name processing. Additionally, HPFS supports:  Long file names  An extendable application interface. The High Performance File System is an example of a class of file systems called installable file systems. Installable file systems are installed by the user (by changing CONFIG.SYS) and are loaded by the operating system during system initialization. The OS/2 operating system permits users to have multiple file systems active at the same time; for example a FAT file system for one hard disk and HPFS for another. ═══ 11.1.1.1. FAT File System ═══ The OS/2 FAT file system is based on the DOS FAT file system. This file system, also used in previous releases of the OS/2 operating system and in PC-DOS, controls storage of data files for hard and floppy disks. The FAT file system is hierarchical, supporting multiple directories on the disk. Each directory can contain one or more files or subdirectories. The FAT file system uses the 8.3 file name convention. Under this convention, the file name consists of a file name of up to eight characters, a separating period (.), and a file name extension of up to three characters. ═══ 11.1.1.2. Installable File Systems ═══ An installable file system is a file system in which software is installed when the operating system is started. The OS/2 operating system supports installable file systems and permits users to have multiple file systems active at the same time. Users install a file system by specifying the file system components in the CONFIG.SYS file. The file system software consists of device drivers and dynamic link libraries. The device drivers access storage devices; the dynamic link libraries control the format of information on a device and manage the flow of data to and from the device. The user must use the DEVICE= command to specify the device driver and the IFS= command to specify the dynamic link library. Installable file system drivers are loaded during system initialization when an IFS= statement is encountered in the CONFIG.SYS file. The operating system loads the device driver and dynamic link library and initializes a specific device for use with a file system. These file systems can support file and directory structures different from the FAT file system. An example of an installable file system might be a file system designed specifically for use on a network server. Another example of an installable file system is the High Performance File System (HPFS), which is included with the OS/2 operating system. ═══ 11.1.1.3. High Performance File System ═══ The High Performance File System (HPFS) is an installable file system. It is a hierarchical system and supports multiple directories. In many cases, accessing files under HPFS is faster than accessing similar files under the FAT file system. During installation of the OS/2 operating system, users can install the HPFS on the hard disk they use to start their computer. Features of HPFS include:  Caching of directories, data, and file system data structures  Multi-threaded I/O operations  Write-behind logic  Optional write-through  Strategic allocation of directory structures  Highly contiguous file allocation  Enhanced recoverability  Extended attribute support  Long file name support  Starting the OS/2 operating system from an HPFS disk File names under HPFS can contain 255 characters (one must be the terminating nil, "\0") and can contain characters that are not valid for the FAT file system-for example, spaces. Each element of a path name residing on an HPFS disk can also have up to 255 characters. The total path including drive, directories, and file name cannot exceed 260 characters (259 with the terminating nil). For more information on long file name support by installable file systems see Long File Names. HPFS provides extremely fast access to very large disk volumes. HPFS uses a memory cache divided into blocks of 2KB. Data that is read from and written to the disk is transferred through this cache. Frequently-used data will often be found in the cache, thereby saving the time that a disk-read operation would require. When a user request specifies data that is not present in the cache, HPFS selects the least-recently used discardable block and then fills the block with the requested data. When a write-data request is received, it usually is not necessary that the data be immediately written to the disk. HPFS will copy such data into the block cache without actually performing the disk-write operation. When the data is in the cache, it is written to disk as a background activity (referred to as lazy writing) which enables the typical user-write operation to occur much faster than in file systems where all write operations are synchronous. The High Performance File System consists of:  The High Performance File System driver, HPFS.IFS  The High Performance File System lazy-write utility, CACHE.EXE  The High Performance File System lazy-write startup program, STARTLW.DLL  The High Performance File System utilities, UHPFS.DLL The user determines the amount of lazy-write support by setting the following parameters on the command line that calls CACHE.EXE:  MaxAge: When the data in a cache block exceeds the specified time the block is queued for writing to the disk. This reduces the amount of data lost due to inopportune system shutdowns.  DiskIdle and BufferIdle When no user I/O request (non-lazy-write) has been made for DiskIdle number of milliseconds, all cache blocks (in oldest-first order) that have not been touched for BufferIdle number of milliseconds are queued for writing to disk. This enables HPFS to write out user data during times of relative disk inactivity and to reduce the need for rewriting heavily used cached blocks. STARTLW.DLL contains the code that starts the lazy-write thread. ═══ 11.1.1.4. Local and Remote File Systems ═══ Installable file systems work with a variety of storage devices. A file system on a local device such as a disk drive or virtual drive is called a local file system. A file system on a remote device, such as a disk drive on another computer, is called a remote file system. An application can establish a connection to a local or a remote file system by using DosFSAttach. For a local file system, the operating system uses a block device driver, which accesses disk hardware, to handle input and output to the device. The operating system automatically connects most (if not all) local file systems when it starts. However, an application can attach and detach additional file systems as needed. For a remote file system, the operating system uses a device driver that typically accesses a communications or network device. Usually, the actual storage device is located on another computer, and the two computers communicate requests and data through a network connection. An application can associate a remote file system with a drive letter by using DosFSAttach. Once the connection is made, the application can access directories and files on the remote device simply by using the assigned drive letter, treating the remote device as if it were on the same computer. ═══ 11.1.2. Recognizing DOS and OS/2 File Objects ═══ The OS/2 FAT file system recognizes file objects created by the DOS FAT file system. This means that applications running under the OS/2 operating system (these include both OS/2 applications and DOS applications running in a DOS Session) can access file objects created by applications running under DOS. Because the OS/2 FAT file system supports the same directory structure as the DOS FAT file system, applications running under DOS can access files and directories created by the OS/2 FAT file system. However, the High Performance File System (HPFS) does not support the same directory structure as the DOS FAT file system. Therefore, the DOS FAT file system will not recognize file objects created by HPFS. This means that if you start the computer with DOS, applications running under DOS cannot access files and directories on HPFS disks. DOS applications running in a DOS Session under the OS/2 operating system can recognize files and directories on both FAT and HPFS disks. A request from a DOS Session to read a file on a FAT disk is handled by the OS/2 FAT file system. Similarly, a request from a DOS Session to read a file on an HPFS disk is handled by the OS/2 High Performance File System. ═══ 11.1.3. Storage Devices and File Systems ═══ OS/2 file systems store information on mass storage devices. These devices are usually hard disks or floppy diskettes, but can be other media, such as CD-ROM. Each drive (or device) is assigned a unique letter to distinguish it from other drives. On most personal computers, drive A is the first floppy disk drive, drive B is the second floppy disk drive, drive C is the first hard disk drive, and drive D is the second hard disk drive. A single hard disk can be divided into two or more partitions, each of which are then viewed as a separate logical drive. A logical drive, like a physical drive, is assigned a unique letter to distinguish it from other physical and logical drives. FDISK is the OS/2 utility used to partition physical storage devices. A personal computer running the OS/2 operating system can have up to 26 logical disk drives. Each logical storage device can be managed by a different file system. The file system attached to a storage device manages that device. A user attaches a file system to a storage device by:  Loading the file system driver during system initialization (by including an IFS= statement in CONFIG.SYS).  Formatting the storage device by using the format options for the file system. During installation of the OS/2 operating system, users have the option of formatting hard disks with the FAT file system or with the (HPFS). If the user chooses to use the HPFS, an IFS= statement is added to the CONFIG.SYS file so that HPFS is loaded automatically during each system startup. During formatting, the file system driver is associated with the logical storage device or drive letter of the hard disk. When an application calls a file system function, the operating system directs the request to:  The installable file system managing the storage device, or  The FAT file system, if no installable file system is loaded and attached to the storage device. The file system used to format the storage media manages that media each time the system is started, as long as the file system is loaded during system start-up. The operating system directs file system requests for a storage media to the file system that formatted the media. If no file system recognizes the format of the media, the OS/2 FAT file system attempts to manage that media. This might occur when the file system used to format the storage media is not loaded during system startup (the IFS= statement was removed from the CONFIG.SYS file after OS/2 installation). If the OS/2 FAT file system cannot recognize the media format (the media might have a different directory structure), the user receives an error when attempting to access the media. For example, assume a system is configured with diskette drive A and hard disk drives C and D. During OS/2 installation, the user elects to format drive C using HPFS. Drive C is, then, managed by HPFS. Drive D was formatted with the FAT file system, so it is managed by the OS/2 FAT file system, as is diskette drive A (removable media cannot be formatted using HPFS). When an application calls DosOpen to open a file on drive C, the operating system directs the request to HPFS. When an application calls DosOpen to open a file on drive A, the operating system directs the request to the OS/2 FAT file system. If HPFS is not loaded during system startup, the FAT file system will receive file system requests made for drive C. However, because HPFS supports a different directory structure than the FAT file system does, the OS/2 FAT file system cannot recognize file objects on the disk. The user will receive an error when attempting to gain access to the disk. Users can determine which file system was used to format a storage device by using the CHKDSK utility. CHKDSK displays a message indicating which file system manages the specified drive. Because DOS does not use extended attributes, a user must use CHKDSK in an OS/2 session rather than in a Dos Session to examine a FAT partition. ═══ 11.1.4. File System Utilities ═══ Utilities for each file system are in a single dynamic link library. The utilities that the operating system calls are based on the file system that recognizes the volume on which the utility is to be run. The dynamic link library for each file system has the following utilities: FORMAT Disk formatter CHKDSK File system validation and repair RECOVER File recovery SYS System installation ═══ 11.1.5. OS/2 Boot Manager ═══ The OS/2 Boot Manager enables different operating systems to co-reside on the same computer. The user selects the operating system to boot when the computer is turned on. For example, DOS, AIX, and the OS/2 operating system can co-reside on the same machine. There can also be a previous version of the OS/2 operating system on the machine co-existing with the current version of the operating system. Each operating system has its own partition and each partition is managed by the appropriate file system for the operating system that owns it. A DOS partition has a FAT file system. An OS/2 partition can have either a FAT file system or HPFS. An AIX partition will use the AIX file system to manage its partition. Note: FAT partitions that follow HPFS partitions on the same physical disk cannot be accessed when using DOS because DOS stops at the first partition it does not recognize. ═══ 11.2. Using File Systems ═══ In order to take advantage of the capabilities of OS/2 file systems, application developers must be able to manage the file systems. Most of the file system functions work with either the FAT file system or the High Performance File System (HPFS). Note: In the example code fragments that follow, error checking was left out to conserve space. Applications should always check the return code that the functions return. Control Program functions return an APIRET value. A return code of 0 indicates success. If a non-zero value is returned, an error occurred. ═══ 11.2.1. Attaching and Detaching File Systems ═══ A file system driver that uses a block device driver for I/O operations to a local or remote (virtual disk) device is called a local file system. A file system driver that accesses a remote system without a block device driver is called a remote file system. An application, typically a network application, can call DosFSAttach to:  Attach a drive to a remote file system  Detach a drive from a remote file system  Attach a pseudocharacter device name to a local or remote file system  Detach a pseudocharacter device name from a local or remote file system. DosFSAttach establishes or breaks the connection between a drive or device and a file system. If an attachment is successful, all requests to that drive or name are routed to the specified file system. If a detachment is successful, the operating system will no longer recognize the drive or name in a file system call. DosFSAttach does not support:  Redirection of drive letters representing local drives  Attachment to drives or devices that are not in the system's name space. (DosFSCtl can be used to attach to drives or devices not in the system's name space.) A name space is a set of names that are known to the file system. For example, CON (console) and PRN (printer) are always in the OS/2 file system's name space. The following code fragment attaches a drive to a remote file system driver (FSD). Assume that the FSD does not require any user-supplied data arguments. Uses Crt,Dos,Os2Def,Os2Base; Var UcDeviceName : Array[0..7] of UCHAR; (* Device name or drive letter string *) UcFSDName : Array[0..39] of UCHAR; (* FSD name *) PDataBuffer : PVOID; (* Attach argument data *) UlDataBufferLen : ULONG; (* Buffer length *) UlOpFlag : ULONG; (* Attach or detach *) Ulrc : APIRET; (* Return code *) Begin StrCopy(ucDeviceName,'Y:'); (* Drive letter with which to attach the *) (* file system driver *) StrCopy(ucFSDName,'\lan03\src'); pDataBuffer := 0; (* Assume that no user-supplied data *) (* arguments are required *) ulDataBufferLen := 0; (* No data buffer supplied *) ulOpFlag := 0; (* Indicate Attach request *) ulrc := DosFSAttach(ucDeviceName, ucFSDName, pDataBuffer, ulDataBufferLen, ulOpFlag); If (ulrc <> 0) Then Begin Write('DosFSAttach error: return code = ', ulrc); Halt; End; End. ═══ 11.2.2. Obtaining Information about an Attached File System ═══ To obtain information about block devices, and all character and pseudocharacter devices, including the type of device and the name of the file system driver the device is attached to, use DosQueryFSAttach. The information can be used to determine if the operating system recognizes that a particular file system is attached to a storage device. This is important to an application that must guarantee such a state. An application of this type must handle the situation where the file system driver that formatted a certain disk was not loaded during system startup. (The user might have omitted the IFS= statement in the CONFIG.SYS. file). In such a situation, the data on the disk could be destroyed because the wrong file system was attached to the disk by default. The following code fragment returns information about an attached file system. Uses Crt,Dos,Os2Def,Os2Base; Var UcDeviceName : Array[0..7] of UCHAR; (* Device name or drive letter *) (* string *) UlOrdinal : ULONG; (* Ordinal of entry in name list *) UlFSAInfoLevel : ULONG; (* Type of attached FSD data *) (* required *) FsqDataBuffer : FSQBUFFER2; (* Returned data buffer *) UlDataBufferLen : ULONG; (* Buffer length *) Ulrc : APIRET; (* Return code *) Begin StrCopy(ucDeviceName, 'Y:'); (* Logical drive of the attached file system *) ulFSAInfoLevel := 1; ulDataBufferLen := sizeof(FSQBUFFER2); (* Data buffer length *) ulrc := DosQueryFSAttach(ucDeviceName, ulOrdinal, ulFSAInfoLevel, fsqDataBuffer, ulDataBufferLen); If (ulrc <> 0) Then Begin Write('DosQueryFSAttach error: return code = ', ulrc); Halt; End; End. In this example, information was requested about the drive whose name was specified within the DeviceName variable. After the DosQueryFSAttach call, the DataBuffer structure contained a set of information describing the specified attached file system, and the DataBufferLen variable contained the size of information within the structure. ═══ 11.2.3. Obtaining Information about a File System ═══ An application can retrieve information about the file system on a given drive by using DosQueryFSInfo. The file system information includes information on the amount of free storage space on the disk. The storage space is given in number of allocation units (clusters) on the disk. Each cluster has an associated number of sectors; each sector contains a given number of bytes. A typical disk has 512 bytes for each sector and 4 sectors for each cluster. DosSetFSInfo enables an application to change the volume identifier for the disk in the given drive. The following code fragment obtains information about the file system that is associated with a particular logical drive. Uses Crt,Dos,Os2Def,Os2Base; Var UlDriveNumber : ULONG; (* Drive number *) UlFSInfoLevel : ULONG; (* File system data required *) UcFSInfoBuf : Array[0..39] of UCHAR; (* File system info buffer *) UlFSInfoBufSize : ULONG; (* File system info buffer size *) Ulrc : APIRET; (* Return code *) Begin ulDriveNumber := 3; (* SpecIfy drive C *) ulFSInfoLevel := FSIL_ALLOC; (* Indicate that file system allocation *) (* information is requested *) ulFSInfoBufSize := 40; (* Size of return data buffer *) ulrc := DosQueryFSInfo(ulDriveNumber, ulFSInfoLevel, ucFSInfoBuf, ulFSInfoBufSize); If (ulrc <> 0) Then Begin Write('DosQueryFSInfo error: return code = ', ulrc); Halt; End; End. In this example, the data buffer FSInfoBuf is used to receive information about space allocation within the specified file system. ═══ 11.2.4. Obtaining Information about a File ═══ An application can retrieve and set information about a specific file by using DosQueryFSInfo and DosSetFileInfo. File information consists of the dates and times that the file was created, last accessed, and last written to (only the time and date the file was last written to are given for FAT partitions); the size (in bytes) of the file; the number of sectors (or clusters) the file occupies; and the file attributes. The following code fragment obtains file information for a specified file. The example obtains the Level 1 information set for the file. The Level 1 information set for a file includes the dates and times of creation, last access, and last writing. It also includes information about the size of the file and the file's standard attributes. Assume that the handle of the desired file has been placed into FileHandle already. Uses Crt,Dos,Os2Def,Os2Base; Var HfFileHandle : HFILE; (* File handle *) UlFileInfoLevel : ULONG; (* Level of file info required *) FsFileInfoBuf : FILESTATUS3; (* File info buffer *) UlFileInfoBufSize : ULONG; (* File data buffer size *) Ulrc : APIRET; (* Return code *) Begin ulFileInfoLevel := 1; (* Indicate that Level 1 information is desired *) fsFileInfoBufSize := sizeof(FILESTATUS3); (* Size of the buffer that will *) (* receive the Level 1 information *) ulrc := DosQueryFileInfo(hfFileHandle, ulFileInfoLevel, fsFileInfoBuf, ulFileInfoBufSize); If (ulrc <> 0) Then Begin Write('DosQueryFileInfo error: return code = ', ulrc); Halt; End; End. In this example, Level 1 file information is placed into the FileInfoBuf buffer. ═══ 11.2.5. Communicating with a File System ═══ An extended standard interface between an application and a file system driver is provided by DosFSCtl. This function is similar to DosDevIOCtl, which provides a standard interface between an application and a device driver. An application sends a request to the file system driver by specifying a particular function code. Data is exchanged through data areas and parameter lists. DosFSCtl can be used to establish open connections to file system drivers that are not attached to a name in the operating system's name space. (A name space is a set of names that are known to the file system. For example, CON and PRN are always in the OS/2 file system's name space.) The following code fragment demonstrates how a process can communicate with a file system driver (FSD). Assume that the calling process has placed an appropriate file handle into FileHandle. Assume that the specified file system recognizes a function code of hex 8100, and that the function code accepts an ASCII string as input, requires no specific command parameter list, and returns a string of ASCII characters to the caller. Uses Crt,Dos,Os2Def,Os2Base; Var UcDataArea : Array[0..99] of UCHAR; (* Data area *) UlDataLengthMax : ULONG; (* Max. length of Data area *) UlDataLengthInOut : ULONG; (* Data area length, in and out *) PParmList : PVOID; (* Parameter list *) UlParmLengthMax : ULONG; (* Max. length of Parameter list *) UlParmLengthInOut : ULONG; (* Parameter list length, in and out *) UlFunctionCode : ULONG; (* Function code *) PszRouteName : PSZ; (* Path or FSD name *) HfFileHandle : HFILE; (* File handle *) UlRouteMethod : ULONG; (* Method for routing *) Ulrc : APIRET; (* Return code *) Begin ulFunctionCode := $8100; (* Indicate the function to request *) (* of the file system *) StrCopy(ucDataArea, 'PARM1: 98'); (* ASCII string to pass to file system *) ulDataLengthMax := 100; (* Tell the file system the maximum *) (* amount of data it can return *) ulDataLengthInOut := strlen(ucDataArea); (* On input, this is the number of *) (* bytes sent to the file system *) pParmList := 0; (* In this example, assume that no *) ulParmLengthMax := 0; (* specIfic command parameter list *) ulParmLengthInOut := 0; (* is required by the file system *) (* for this function code *) ulRouteMethod := 1; (* Indicate that the file handle *) pszRouteName := 0; (* directs routing (this implies *) (* that the RouteName variable is *) (* unused in this example) *) ulrc := DosFSCtl(ucDataArea, ulDataLengthMax, ulDataLengthInOut, pParmList, ulParmLengthMax, ulParmLengthInOut, ulFunctionCode, pszRouteName, hfFileHandle, ulRouteMethod); If (ulrc <> 0) Then Begin Write('DosFSCtl error: return code = ', ulrc); halt; End; End. In this example, the the DataArea buffer is used to store the ASCII string sent by the file system in response to the function request, and the DataLengthInOut variable is used to store the number of bytes placed in the buffer by the file system. ═══ 11.2.6. Preparing File Systems for System Shutdown ═══ At any time during normal system operation, data destined for a disk might be in a cache. If this information is not written to disk before the system powered-off, the disk can become corrupted. To prevent this, applications call DosShutdown to ensure that the operating system writes the data in the cache to the disk and prevents any further data from being cached. The user can then safely power-off the system. Note: This call prepares all file systems and device drivers for system shutdown. Therefore, it must be called only when system shutdown is about to occur. The user and applications will no longer have access to their storage devices. The following code fragment locks out changes to all file systems, and writes system buffers to the disk in preparation for turning off power to the system. Uses Crt,Dos,Os2Def,Os2Base; Var UlReserved : ULONG; (* Reserved, must be zero *) Ulrc : APIRET; (* Return code *) Begin ulReserved := 0; (* Reserved, must be set to zero *) ulrc := DosShutdown(ulReserved); If (ulrc <> 0) Then Begin Write('DosShutdown error: return code = ', ulrc); Halt; End; End. ═══ 11.2.7. Writing Cache Buffers ═══ DosResetBuffer is used to write to disk (flush) the file system's cache buffers for a specific file handle. When called with a value of hex FFFF for the file handle, DosResetBuffer writes all files belonging to the requesting process to disk (this usage should be administered with care, so the user is not burdened with insertion and removal of a large number of removable media volumes). When DosResetBuffer is called for single file handle, the directory entry for the file is updated as if the file had been closed. However, the file remains open. DosResetBuffer can also be called with the name of a named pipe. The process that calls DosResetBuffer is blocked at one end of the pipe until all data it has written has been successfully read by the process at the other end of the pipe. This enables communicating processes to synchronize their dialogs. The following code fragment opens a file, writes some data to the file's buffer, then writes the file's system buffer to the disk. Uses Crt,Dos,Os2Def,Os2Base; Var HfFileHandle : HFILE; (* Handle for file being manipulated *) UlAction : ULONG; (* Action taken (returned by DosOpen) *) UlWrote : ULONG; (* Number of bytes written by DosWrite *) Ulrc : APIRET; (* Return code *) UchFileName : Array[0..19] of UCHAR; (* Name of file *) UchFileData : Array[0..99] of UCHAR; (* Data to write to file *) Begin HfFileHandle := 0; UlAction := 0; UlWrote := 0; Ulrc := NO_ERROR; StrCopy(UchFileName,'MYDATA.DAT'); StrCopy(uchFileData,' '); (* Open the file MYDATA.DAT. If the file already exists, replace it. If the file doesn't exist, create it. *) ulrc := DosOpen(uchFileName, (* Path and file name *) hfFileHandle, (* File handle *) ulAction, (* Action taken (returned) *) 100, (* File primary allocation *) FILE_ARCHIVED or FILE_NORMAL, (* File attributes *) OPEN_ACTION_CREATE_IF_NEW or OPEN_ACTION_REPLACE_IF_EXISTS, (* Open function type *) OPEN_SHARE_DENYREADWRITE or OPEN_ACCESS_READWRITE, (* Open mode of the file *) 0); (* No extended attributes *) If (ulrc <> NO_ERROR) Then Begin Writeln('DosOpen error: return code = ', ulrc); Exit; End; StrCopy (uchFileData, 'Data....'); (* This data will be written to file *) ulrc := DosWrite (hfFileHandle, (* File handle *) uchFileData, (* String to be written *) sizeof (uchFileData), (* Size of string to be written *) ulWrote); (* Bytes written (returned) *) If (ulrc <> NO_ERROR) Then Begin Writeln('DosWrite error: return code = ', ulrc); DosClose(hfFileHandle); (* close the file *) Exit; End; ulrc := DosResetBuffer(hfFileHandle); If (ulrc <> NO_ERROR) Then Begin Writeln('DosResetBuffer error: return code = ', ulrc); DosClose(hfFileHandle); (* close the file *) Exit; End; DosClose(hfFileHandle); (* close the file *) End.