/*++ Copyright (c) 1993 Microsoft Corporation Module Name: vwdos.c Abstract: ntVdm netWare (Vw) IPX/SPX Functions Vw: The peoples' network Contains handlers for DOS IPX/SPX calls (netware functions). The IPX APIs use WinSock to perform the actual operations Contents: VwIPXCancelEvent VwIPXCloseSocket VwIPXDisconnectFromTarget VwIPXGenerateChecksum VwIPXGetInformation VwIPXGetInternetworkAddress VwIPXGetIntervalMarker VwIPXGetLocalTarget VwIPXGetLocalTargetAsync VwIPXGetMaxPacketSize VwIPXInitialize VwIPXListenForPacket VwIPXOpenSocket VwIPXRelinquishControl VwIPXScheduleAESEvent VwIPXScheduleIPXEvent VwIPXSendPacket VwIPXSendWithChecksum VwIPXSPXDeinit VwIPXVerifyChecksum VwSPXAbortConnection VwSPXEstablishConnection VwSPXGetConnectionStatus VwSPXInitialize VwSPXListenForConnection VwSPXListenForSequencedPacket VwSPXSendSequencedPacket VwSPXTerminateConnection Author: Richard L Firth (rfirth) 30-Sep-1993 Environment: User-mode Win32 Revision History: 30-Sep-1993 rfirth Created --*/ #include "vw.h" #pragma hdrstop // // functions // VOID VwIPXCancelEvent( VOID ) /*++ Routine Description: Cancels event described by an ECB This call is Synchronous Arguments: Inputs BX 06h ES:SI ECB Outputs AL Completion code: 00h Success F9h Can't cancel ECB FFh ECB not in use Return Value: None. --*/ { LPECB pEcb; WORD status; CHECK_INTERRUPTS("VwIPXCancelEvent"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_IPXCancelEvent, IPXDBG_LEVEL_INFO, "VwIPXCancelEvent(%04x:%04x)\n", getES(), getSI() )); IPX_GET_IPX_ECB(pEcb); status = _VwIPXCancelEvent( pEcb ); IPX_SET_STATUS(status); } VOID VwIPXCloseSocket( VOID ) /*++ Routine Description: Closes a socket and cancels any outstanding events on the socket. Closing an unopened socket does not return an error ESRs in cancelled ECBs are not called This call is Synchronous Arguments: Inputs BX 01h DX Socket Number Outputs Nothing Return Value: None. --*/ { WORD socketNumber; CHECK_INTERRUPTS("VwIPXCloseSocket"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_IPXCloseSocket, IPXDBG_LEVEL_INFO, "VwIPXCloseSocket(%#x)\n", B2LW(IPX_SOCKET_PARM()) )); IPX_GET_SOCKET(socketNumber); _VwIPXCloseSocket( socketNumber ); } VOID VwIPXDisconnectFromTarget( VOID ) /*++ Routine Description: Performs no action for NTVDM IPX This call is Synchronous Arguments: Inputs BX 0Bh ES:SI Request buffer: Destination Network DB 4 DUP (?) Destination Node DB 6 DUP (?) Destination Socket DB 2 DUP (?) Outputs Nothing Return Value: None. --*/ { CHECK_INTERRUPTS("VwIPXDisconnectFromTarget"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_IPXDisconnectFromTarget, IPXDBG_LEVEL_INFO, "VwIPXDisconnectFromTarget\n" )); } VOID VwIPXGenerateChecksum( VOID ) /*++ Routine Description: Generates checksum for a transmit ECB This call is Synchronous Arguments: Inputs BX 21h ES:SI ECB address Outputs No registers ECB checksum field is updated Return Value: None. --*/ { CHECK_INTERRUPTS("VwIPXGenerateChecksum"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_IPXGenerateChecksum, IPXDBG_LEVEL_INFO, "VwIPXGenerateChecksum\n" )); } VOID VwIPXGetInformation( VOID ) /*++ Routine Description: Returns a bit-map of supported functions This call is Synchronous Arguments: Inputs BX 1Fh DX 0000h Outputs DX Bit map: 0001h Set if IPX is IPXODI.COM, not dedicated IPX 0002h Set if checksum functions (20h, 21h, 22h) supported Return Value: None. --*/ { CHECK_INTERRUPTS("VwIPXGetInformation"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_IPXGetInformation, IPXDBG_LEVEL_INFO, "VwIPXGetInformation\n" )); IPX_SET_INFORMATION(IPX_ODI); } VOID VwIPXGetInternetworkAddress( VOID ) /*++ Routine Description: Returns a buffer containing the net number and node number for this station. This function cannot return an error (!) Assumes: 1. GetInternetAddress has been successfully called in the DLL initialization phase This call is Synchronous Arguments: Inputs BX 09h Outputs ES:SI Buffer Network Address DB 4 DUP (?) Node Address DB 6 DUP (?) Return Value: None. --*/ { LPINTERNET_ADDRESS pAddr; CHECK_INTERRUPTS("VwIPXGetInternetworkAddress"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_IPXGetInternetworkAddress, IPXDBG_LEVEL_INFO, "VwIPXGetInternetworkAddress(%04x:%04x)\n", getES(), getSI() )); pAddr = (LPINTERNET_ADDRESS)IPX_BUFFER_PARM(sizeof(*pAddr)); if (pAddr) { _VwIPXGetInternetworkAddress( pAddr ); } } VOID VwIPXGetIntervalMarker( VOID ) /*++ Routine Description: Just returns the tick count maintained by Asynchronous Event Scheduler This call is Synchronous Arguments: Inputs BX 08h Outputs AX Interval marker Return Value: None. --*/ { CHECK_INTERRUPTS("VwIPXGetIntervalMarker"); setAX( _VwIPXGetIntervalMarker() ); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_IPXGetIntervalMarker, IPXDBG_LEVEL_INFO, "VwIPXGetIntervalMarker: Returning %04x\n", getAX() )); } VOID VwIPXGetLocalTarget( VOID ) /*++ Routine Description: Given a target address of the form (network address {4}, node address {6}), returns the node address of the target if on the same network, or the node address of the router which knows how to get to the next hop in reaching the eventual target This call is Synchronous Arguments: Inputs BX 02h ES:SI Request buffer Destination Network DB 4 DUP (?) Destination Node DB 6 DUP (?) Destination Socket DB 2 DUP (?) ES:DI Response buffer Local Target DB 6 DUP (?) Outputs AL Completion code 00h Success FAh No path to destination node found AH Number of hops to destination CX Transport time Return Value: None. --*/ { LPBYTE pImmediateAddress; LPBYTE pNetworkAddress; WORD transportTime; WORD status; CHECK_INTERRUPTS("VwIPXGetLocalTarget"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_IPXGetLocalTarget, IPXDBG_LEVEL_INFO, "VwIPXGetLocalTarget(target buf @ %04x:%04x, local buf @ %04x:%04x)\n", getES(), getSI(), getES(), getDI() )); pImmediateAddress = POINTER_FROM_WORDS(getES(), getDI(), 6); pNetworkAddress = POINTER_FROM_WORDS(getES(), getSI(), 12); if (pImmediateAddress && pNetworkAddress) { status = _VwIPXGetLocalTarget( pNetworkAddress, pImmediateAddress, &transportTime ); } else { status = IPX_BAD_REQUEST; } setCX( transportTime ); setAH(1); IPX_SET_STATUS(status); } VOID VwIPXGetLocalTargetAsync( VOID ) /*++ Routine Description: description-of-function. This call is Asynchronous Arguments: None. Return Value: None. --*/ { CHECK_INTERRUPTS("VwIPXGetLocalTargetAsync"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_ANY, IPXDBG_LEVEL_INFO, "VwIPXGetLocalTargetAsync\n" )); } VOID VwIPXGetMaxPacketSize( VOID ) /*++ Routine Description: Returns the maximum packet size the underlying network can handle Assumes: 1. A successfull call to GetMaxPacketSize has been made during DLL initialization 2. Maximum packet size is constant This call is Synchronous Arguments: Inputs BX 1Ah Outputs AX Maximum packet size CX IPX retry count Return Value: None. --*/ { WORD maxPacketSize; WORD retryCount; CHECK_INTERRUPTS("VwIPXGetMaxPacketSize"); maxPacketSize = _VwIPXGetMaxPacketSize( &retryCount ); setAX(maxPacketSize); // // The DOS Assembly and C manuals differ slightly here: DOS says // we return the IPX retry count in CX. There is no corresponding parameter // in the C interface? // setCX(retryCount); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_IPXGetMaxPacketSize, IPXDBG_LEVEL_INFO, "VwIPXGetMaxPacketSize: PacketSize=%d, RetryCount=%d\n", getAX(), getCX() )); } VOID VwIPXInitialize( VOID ) /*++ Routine Description: description-of-function. Arguments: None. Return Value: None. --*/ { CHECK_INTERRUPTS("VwIPXInitialize"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_ANY, IPXDBG_LEVEL_INFO, "VwIPXInitialize\n" )); } VOID VwIPXListenForPacket( VOID ) /*++ Routine Description: Queue a listen request against a socket. All listen requests will be completed asynchronously, unless cancelled by app This call is Asynchronous Arguments: Inputs BX 04h ES:SI ECB address Outputs AL Completion code FFh Socket doesn't exist Return Value: None. --*/ { LPECB pEcb; WORD status; CHECK_INTERRUPTS("VwIPXListenForPacket"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_IPXListenForPacket, IPXDBG_LEVEL_INFO, "VwIPXListenForPacket(%04x:%04x)\n", getES(), getSI() )); IPX_GET_IPX_ECB(pEcb); status = _VwIPXListenForPacket( pEcb, ECB_PARM_ADDRESS() ); IPX_SET_STATUS(status); } VOID VwIPXOpenSocket( VOID ) /*++ Routine Description: Opens a socket for use by IPX or SPX. Puts the socket into non-blocking mode. The socket will be bound to IPX This call is Synchronous Arguments: Inputs AL Socket Longevity flag This parameter is actually in BP - AX has been sequestered by the VDD dispatcher BX 00h DX Requested Socket Number CX DOS PDB. This parameter is not part of the IPX API. Added because we need to remember which DOS executable created the socket: we need to clean-up short-lived sockets when the executable terminates Outputs AL Completion code: 00h Success FFh Socket already open FEh Socket table full DX Assigned socket number Return Value: None. --*/ { BYTE socketLife; WORD socketNumber; WORD status; CHECK_INTERRUPTS("VwIPXOpenSocket"); IPX_GET_SOCKET_LIFE(socketLife); IPX_GET_SOCKET(socketNumber); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_IPXOpenSocket, IPXDBG_LEVEL_INFO, "VwIPXOpenSocket(Life=%02x, Socket=%04x, Owner=%04x)\n", socketLife, B2LW(socketNumber), IPX_SOCKET_OWNER_PARM() )); status = _VwIPXOpenSocket( &socketNumber, socketLife, IPX_SOCKET_OWNER_PARM() ); if ( status == IPX_SUCCESS ) IPX_SET_SOCKET(socketNumber); IPX_SET_STATUS(status); } VOID VwIPXRelinquishControl( VOID ) /*++ Routine Description: Just sleep for a nominal amount. Netware seems to be dependent on the default setting of the PC clock, so one timer tick (1/18 second) would seem to be a good value This call is Synchronous Arguments: Inputs BX 0Ah Outputs Nothing Return Value: None. --*/ { CHECK_INTERRUPTS("VwIPXRelinquishControl"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_IPXRelinquishControl, IPXDBG_LEVEL_INFO, "VwIPXRelinquishControl\n" )); _VwIPXRelinquishControl(); } VOID VwIPXScheduleAESEvent( VOID ) /*++ Routine Description: Schedules a an event to occur in some number of ticks. When the tick count reaches 0, the ECB InUse field is cleared and any ESR called This call is Asynchronous Arguments: Inputs BX 07h AX Delay time - number of 1/18 second ticks ES:SI ECB address Outputs Nothing Return Value: None. --*/ { LPXECB pXecb = AES_ECB_PARM(); WORD ticks = IPX_TICKS_PARM(); if (pXecb == NULL) { return; } CHECK_INTERRUPTS("VwIPXScheduleAESEvent"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_IPXScheduleAESEvent, IPXDBG_LEVEL_INFO, "VwIPXScheduleAESEvent(%04x:%04x, %04x)\n", getES(), getSI(), ticks )); ScheduleEvent(pXecb, ticks); } VOID VwIPXScheduleIPXEvent( VOID ) /*++ Routine Description: Schedules a an event to occur in some number of ticks. When the tick count reaches 0, the ECB InUse field is cleared and any ESR called This call is Asynchronous Arguments: Inputs BX 05h AX Delay time - number of 1/18 second ticks ES:SI ECB address Outputs Nothing Return Value: None. --*/ { LPECB pEcb; WORD ticks = IPX_TICKS_PARM(); CHECK_INTERRUPTS("VwIPXScheduleIPXEvent"); IPX_GET_IPX_ECB(pEcb); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_IPXScheduleIPXEvent, IPXDBG_LEVEL_INFO, "VwIPXScheduleIPXEvent(%04x:%04x, %04x)\n", getES(), getSI(), ticks )); _VwIPXScheduleIPXEvent( ticks, pEcb, ECB_PARM_ADDRESS() ); } VOID VwIPXSendPacket( VOID ) /*++ Routine Description: Sends a packet to the target machine/router. This call can be made on a socket that is not open The app must have filled in the following IPX_ECB fields: EsrAddress Socket ImmediateAddress FragmentCount fragment descriptor fields and the following IPX_PACKET fields: PacketType Destination.Net Destination.Node Destination.Socket This call is Asynchronous Arguments: Inputs BX 03h CX DOS PDB. This parameter is not part of the IPX API. Added because we need to remember which DOS executable owns the socket IF WE MUST CREATE A TEMPORTARY SOCKET: we need to clean-up short-lived sockets when the executable terminates ES:SI ECB Address Outputs Nothing Return Value: None. --*/ { LPECB pEcb; WORD owner; CHECK_INTERRUPTS("VwIPXSendPacket"); IPX_GET_IPX_ECB(pEcb); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_IPXSendPacket, IPXDBG_LEVEL_INFO, "VwIPXSendPacket(%04x:%04x), owner = %04x\n", getES(), getSI(), IPX_SOCKET_OWNER_PARM() )); _VwIPXSendPacket(pEcb, ECB_PARM_ADDRESS(), IPX_SOCKET_OWNER_PARM() ); } VOID VwIPXSendWithChecksum( VOID ) /*++ Routine Description: description-of-function. This call is Asynchronous Arguments: None. Return Value: None. --*/ { CHECK_INTERRUPTS("VwIPXSendWithChecksum"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_IPXSendWithChecksum, IPXDBG_LEVEL_INFO, "VwIPXSendWithChecksum\n" )); } VOID VwIPXSPXDeinit( VOID ) /*++ Routine Description: description-of-function. This call is Synchronous Arguments: None. Return Value: None. --*/ { CHECK_INTERRUPTS("VwIPXSPXDeinit"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_ANY, IPXDBG_LEVEL_INFO, "VwIPXSPXDeinit\n" )); } VOID VwIPXVerifyChecksum( VOID ) /*++ Routine Description: description-of-function. This call is Synchronous Arguments: None. Return Value: None. --*/ { CHECK_INTERRUPTS("VwIPXVerifyChecksum"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_IPXVerifyChecksum, IPXDBG_LEVEL_INFO, "VwIPXVerifyChecksum\n" )); } VOID VwSPXAbortConnection( VOID ) /*++ Routine Description: Aborts this end of a connection This call is Asynchronous Arguments: Inputs BX 14h DX Connection ID Outputs Nothing Return Value: None. --*/ { WORD connectionId = SPX_CONNECTION_PARM(); CHECK_INTERRUPTS("VwSPXAbortConnection"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_SPXAbortConnection, IPXDBG_LEVEL_INFO, "VwSPXAbortConnection(%04x)\n", connectionId )); _VwSPXAbortConnection(connectionId); } VOID VwSPXEstablishConnection( VOID ) /*++ Routine Description: Creates a connection with a remote SPX socket. The remote end can be on this machine (i.e. same app in DOS world) This call is Asynchronous Arguments: Inputs BX 11h AL Retry count AH WatchDog flag ES:SI ECB Address Outputs AL Completion code: 00h Attempting to talk to remote EFh Local connection table full FDh Fragment count not 1; buffer size not 42 FFh Send socket not open DX Connection ID Return Value: None. --*/ { WORD status; BYTE retryCount = SPX_RETRY_COUNT_PARM(); BYTE watchDogFlag = SPX_WATCHDOG_FLAG_PARM(); WORD connectionId = 0; LPECB pEcb; CHECK_INTERRUPTS("VwSPXEstablishConnection"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_SPXEstablishConnection, IPXDBG_LEVEL_INFO, "VwSPXEstablishConnection(%02x, %02x, %04x:%04x)\n", retryCount, watchDogFlag, ECB_PARM_SEGMENT(), ECB_PARM_OFFSET() )); IPX_GET_IPX_ECB( pEcb ); IPXDUMPECB((pEcb, getES(), getSI(), ECB_TYPE_SPX, TRUE, TRUE, FALSE)); status = _VwSPXEstablishConnection( retryCount, watchDogFlag, &connectionId, pEcb, ECB_PARM_ADDRESS() ); SPX_SET_CONNECTION_ID( connectionId ); SPX_SET_STATUS( status ); } VOID VwSPXGetConnectionStatus( VOID ) /*++ Routine Description: Returns buffer crammed full of useful statistics or something (hu hu huh) This call is Synchronous Arguments: Inputs BX 15h DX Connection ID ES:SI Buffer address Outputs AL Completion code: 00h Connection is active EEh No such connection on output, buffer in ES:SI contains: BYTE ConnectionStatus BYTE WatchDogActive WORD LocalConnectionID WORD RemoteConnectionID WORD SequenceNumber WORD LocalAckNumber WORD LocalAllocationNumber WORD RemoteAckNumber WORD RemoteAllocationNumber WORD LocalSocket BYTE ImmediateAddress[6] BYTE RemoteNetwork[4] WORD RetransmissionCount WORD RetransmittedPackets WORD SuppressedPackets Return Value: None. --*/ { WORD status; WORD connectionId = SPX_CONNECTION_PARM(); LPSPX_CONNECTION_STATS pStats = (LPSPX_CONNECTION_STATS)SPX_BUFFER_PARM(sizeof(*pStats)); CHECK_INTERRUPTS("VwSPXGetConnectionStatus"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_SPXGetConnectionStatus, IPXDBG_LEVEL_INFO, "VwSPXGetConnectionStatus: connectionId=%04x\n", connectionId )); status = _VwSPXGetConnectionStatus( connectionId, pStats ); SPX_SET_STATUS(status); } VOID VwSPXInitialize( VOID ) /*++ Routine Description: Informs the app that SPX is present on this station This call is Synchronous Arguments: Inputs BX 10h AL 00h Outputs AL Installation flag: 00h Not installed FFh Installed BH SPX Major revision number BL SPX Minor revision number CX Maximum SPX connections supported normally from SHELL.CFG DX Available SPX connections Return Value: None. --*/ { WORD status; BYTE majorRevisionNumber; BYTE minorRevisionNumber; WORD maxConnections; WORD availableConnections; CHECK_INTERRUPTS("VwSPXInitialize"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_SPXInitialize, IPXDBG_LEVEL_INFO, "VwSPXInitialize\n" )); status = _VwSPXInitialize( &majorRevisionNumber, &minorRevisionNumber, &maxConnections, &availableConnections ); setBH( majorRevisionNumber ); setBL( minorRevisionNumber ); setCX( maxConnections ); setDX( availableConnections ); SPX_SET_STATUS(status); } VOID VwSPXListenForConnection( VOID ) /*++ Routine Description: Listens for an incoming connection request This call is Asynchronous Arguments: Inputs BX 12h AL Retry count AH SPX WatchDog flag ES:SI ECB Address Outputs Nothing Return Value: None. --*/ { BYTE retryCount = SPX_RETRY_COUNT_PARM(); BYTE watchDogFlag = SPX_WATCHDOG_FLAG_PARM(); LPECB pEcb; CHECK_INTERRUPTS("VwSPXListenForConnection"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_SPXListenForConnection, IPXDBG_LEVEL_INFO, "VwSPXListenForConnection(%02x, %02x, %04x:%04x)\n", retryCount, watchDogFlag, ECB_PARM_SEGMENT(), ECB_PARM_OFFSET() )); IPX_GET_IPX_ECB( pEcb ); IPXDUMPECB((pEcb, getES(), getSI(), ECB_TYPE_SPX, TRUE, FALSE, FALSE)); _VwSPXListenForConnection( retryCount, watchDogFlag, pEcb, ECB_PARM_ADDRESS() ); } VOID VwSPXListenForSequencedPacket( VOID ) /*++ Routine Description: Attempts to receive an SPX packet. This call is made against the top-level socket (the socket in SPX-speak, not the connection). We can receive a packet from any connection assigned to this socket. In this function, we just queue the ECB (since there is no return status, we expect that the app has supplied an ESR) and let AES handle it This call is Asynchronous Arguments: Inputs BX 17h ES:SI ECB Address Outputs Nothing Return Value: None. --*/ { LPECB pEcb; CHECK_INTERRUPTS("VwSPXListenForSequencedPacket"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_SPXListenForSequencedPacket, IPXDBG_LEVEL_INFO, "VwSPXListenForSequencedPacket(%04x:%04x)\n", ECB_PARM_SEGMENT(), ECB_PARM_OFFSET() )); IPX_GET_IPX_ECB( pEcb ); IPXDUMPECB((pEcb, getES(), getSI(), ECB_TYPE_SPX, TRUE, FALSE, FALSE)); _VwSPXListenForSequencedPacket( pEcb, ECB_PARM_ADDRESS()); } VOID VwSPXSendSequencedPacket( VOID ) /*++ Routine Description: Sends a packet on an SPX connection This call is Asynchronous Arguments: Inputs BX 16h DX Connection ID ES:SI ECB address Outputs Nothing Return Value: None. --*/ { WORD connectionId = SPX_CONNECTION_PARM(); LPECB pEcb; CHECK_INTERRUPTS("VwSPXSendSequencedPacket""VwSPXSendSequencedPacket"); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_SPXSendSequencedPacket, IPXDBG_LEVEL_INFO, "VwSPXSendSequencedPacket(%04x, %04x:%04x)\n", connectionId, getES(), getSI() )); IPX_GET_IPX_ECB( pEcb ); IPXDUMPECB((pEcb, getES(), getSI(), ECB_TYPE_SPX, TRUE, TRUE, FALSE)); _VwSPXSendSequencedPacket( connectionId, pEcb, ECB_PARM_ADDRESS() ); } VOID VwSPXTerminateConnection( VOID ) /*++ Routine Description: Terminates a connection This call is Asynchronous Arguments: Inputs BX 13h DX Connection ID ES:SI ECB Address Outputs Nothing Return Value: None. --*/ { WORD connectionId = SPX_CONNECTION_PARM(); LPECB pEcb; CHECK_INTERRUPTS("VwSPXTerminateConnection"); IPX_GET_IPX_ECB( pEcb ); IPXDBGPRINT((__FILE__, __LINE__, FUNCTION_SPXTerminateConnection, IPXDBG_LEVEL_INFO, "VwSPXTerminateConnection(%04x, %04x:%04x)\n", connectionId, ECB_PARM_SEGMENT(), ECB_PARM_OFFSET() )); _VwSPXTerminateConnection(connectionId, pEcb, ECB_PARM_ADDRESS()); }