windows-nt/Source/XPSP1/NT/shell/osshell/accesory/terminal/mdmutil.c
2020-09-26 16:20:57 +08:00

899 lines
25 KiB
C

/*===========================================================================*/
/* Copyright (c) 1987 - 1988, Future Soft Engineering, Inc. */
/* Houston, Texas */
/*===========================================================================*/
#define NOGDICAPMASKS TRUE
#define NOICONS TRUE
#define NOKEYSTATES TRUE
#define NOSYSCOMMANDS TRUE
#define NOATOM TRUE
#define NOCLIPBOARD TRUE
#define NODRAWTEXT TRUE
#define NOMINMAX TRUE
#define NOOPENFILE TRUE
#define NOSCROLL TRUE
#define NOHELP TRUE
#define NOPROFILER TRUE
#define NODEFERWINDOWPOS TRUE
#define NOPEN TRUE
#define NO_TASK_DEFINES TRUE
#define NOLSTRING TRUE
#define USECOMM
#include <stdarg.h>
#include <windows.h>
#include <port1632.h>
#include "dcrc.h"
#include "dynacomm.h"
#include "task.h"
#include "connect.h"
/*---------------------------------------------------------------------------*/
/* mdmConnect() - [mbb] */
/*---------------------------------------------------------------------------*/
/* NOTE: PATCH until WIN COMM DRV is fixed!!! */
#define DEB_MSR_OFFSET 35 /* mbbx 1.10: carrier... */
#define DEB_MSR_RLSD 0x80
BOOL mdmConnect() /* mbbx 2.00: network... */
{
BOOL bRc,bCarrier = FALSE;
// -sdj unreferenced local var: LPBYTE lpMSR;
DWORD dwModemStatus;
if(trmParams.fCarrier)
{
switch(trmParams.comDevRef)
{
case ITMWINCOM:
DEBOUT("mdmConnect: %s\n","Calling getmodemstatus to see rlsd!");
bRc = GetCommModemStatus(sPort,&dwModemStatus);
DEBOUT("mdmConnect: rc of getmodemstatus = %lx\n",bRc);
DEBOUT("mdmConnect: dw of getmodemstatus = %lx\n",dwModemStatus);
if (!bRc)
{
DEBOUT("mdmconnect: %s\n","getmodemstatus failed, setting bCar=TRUE");
bCarrier = TRUE;
}
else
{
bCarrier = (dwModemStatus & MS_RLSD_ON) ? TRUE : FALSE;
DEBOUT("mdmconnect: bCarrier is set as: %lx\n",bCarrier);
}
break;
default:
bCarrier = TRUE;
break;
}
if(mdmOnLine != bCarrier)
{
if(!(mdmOnLine = bCarrier))
{
}
return(TRUE);
}
}
return(FALSE);
}
/*---------------------------------------------------------------------------*/
/* modemReset() - Send XON character to the serial port. [mbb] */
/*---------------------------------------------------------------------------*/
VOID modemReset() /* mbbx 2.00: network... */
{
switch(trmParams.comDevRef)
{
case ITMWINCOM:
switch(trmParams.flowControl)
{
case ITMXONFLOW:
DEBOUT("modemReset: Esccom(SETXON) on comport=%lx\n",sPort);
EscapeCommFunction(sPort, SETXON);
break;
case ITMHARDFLOW:
DEBOUT("modemReset: Esccom(SETRTS) on comport=%lx\n",sPort);
EscapeCommFunction(sPort, SETRTS);
break;
}
break;
}
sPortErr = FALSE;
}
/*---------------------------------------------------------------------------*/
/* modemBreak() - Send BREAK signal to the serial port. [mbb] */
/*---------------------------------------------------------------------------*/
/* NOTE: units for modemSendBreak are approx. 1/9 secs ( = 7 ticks) */
/*---------------------------------------------------------------------------*/
/* VT100 standards for BREAK signals are as follows: */
/* */
/* short break: 0.233 sec = 2 units */
/* long break: 3.500 sec = 30 units */
/* */
VOID modemSendBreak(INT nCount)
{
DCB dcb; /* slc nova 051 */
switch(trmParams.comDevRef)
{
case ITMWINCOM:
if(nCount > 2) /* slc nova 051 moved... */
{
if(GetCommState(sPort, (DCB FAR *)&dcb) == 0) /* slc nova 051 */
{
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
EscapeCommFunction(sPort,CLRRTS);
EscapeCommFunction(sPort,CLRDTR);
if(!SetCommState(sPort,(DCB FAR *)&dcb))
{
}
}
}
DEBOUT("modemSendBrk:EscapeCommFunction(sPort, SETBREAK), DelayStart..for port=%lx\n",sPort);
EscapeCommFunction(sPort, SETBREAK);
delay(nCount*7, NULL);
DEBOUT("modemSendBrk:DelayOver...EscapeCommFunction(sPort, CLRBREAK)for port=%lx\n",sPort);
EscapeCommFunction(sPort, CLRBREAK);
if(nCount > 2) /* slc nova 051 moved... */
{
if(GetCommState(sPort, (DCB FAR *)&dcb) == 0) /* slc nova 051 */
{
dcb.fRtsControl = RTS_CONTROL_ENABLE;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
EscapeCommFunction(sPort,SETRTS);
EscapeCommFunction(sPort,SETDTR);
DEBOUT("modemSendBreak: set fRtsDtrdisable to false: for port=%lx\n",sPort);
if(!SetCommState(sPort,(DCB FAR *)&dcb))
{
DEBOUT("FAIL: modemSendBreak: set fRtsDtrdisable to false: for port=%lx\n",sPort);
}
}
}
break;
case ITMDLLCONNECT: /* rjs bug2 */
DLL_modemSendBreak(ghCCB, nCount);
break;
}/* switch */
resetSerial(&trmParams, FALSE, FALSE, 0);
}
/*---------------------------------------------------------------------------*/
/* modemBytes() - [mbb] */
/*---------------------------------------------------------------------------*/
VOID NEAR WIN_modemBytes() /* mbbx 2.00: network... */
{
// -sdj unreferenced local var: INT i;
LPBYTE ltmp;
COMSTAT serInfo;
DWORD dwErrors;
DWORD dwBytesRead;
OVERLAPPED overlap;
BOOL bRc;
overlap.hEvent = overlapEvent;
overlap.Internal = 0;
overlap.InternalHigh = 0;
overlap.Offset = 0;
overlap.OffsetHigh = 0;
ResetEvent(overlapEvent);
gotCommEvent = FALSE;
bRc = ReadFile(sPort, serBytes+1, LOCALMODEMBUFSZ-1,
(LPDWORD)&serCount, (LPOVERLAPPED)&overlap);
if(!bRc && ((dwErrors = GetLastError()) != ERROR_IO_PENDING))
{
bRc = ClearCommError(sPort, &dwErrors, &serInfo); /* reset after error */
if(trmParams.flowControl == ITMHARDFLOW)
{
if(serInfo.cbInQue < 100)
{
modemReset();
}
}
if(serInfo.fXoffSent || serInfo.fCtsHold || serInfo.fDsrHold)
{
if(serInfo.cbInQue < 100)
{
modemReset();
}
}
}
else
{
if(!bRc && ((dwErrors = GetLastError()) == ERROR_IO_PENDING))
{
bRc = ClearCommError(sPort, &dwErrors, &serInfo);
}
if(!GetOverlappedResult(sPort, &overlap, &dwBytesRead, FALSE))
return;
if(serCount = dwBytesRead)
{
ltmp = serBytes+1;
if(serCount != LOCALMODEMBUFSZ-1)
{
bRc = ClearCommError(sPort, &dwErrors, &serInfo); /* reset after error */
if(trmParams.flowControl == ITMHARDFLOW) /* rjs bug2 003 */
{
if(serInfo.cbInQue < 100)
{
modemReset();
}
}
if(serInfo.fXoffSent || serInfo.fCtsHold || serInfo.fDsrHold)
{
if(serInfo.cbInQue < 100)
{
modemReset();
}
}
} /* if readfile cameout halfway through */
else
{
gotCommEvent = TRUE; /* we read a full buffer, try again..*/
}
}
} /* readfile succeded lets see the bytes read */
}
INT modemBytes() /* mbbx 2.00: network... */
{
LPCONNECTOR_CONTROL_BLOCK lpCCB; /* slc nova 031 */
BYTE tmp1[TMPNSTR+1];
BYTE tmp2[TMPNSTR+1];
if(serNdx > 0)
return(serCount - (serNdx-1));
switch(trmParams.comDevRef)
{
case ITMWINCOM:
//-sdj comments from checkcommevent():
//-sdj for telnet-quit processing
//-sdj if this is a telnet connection which was opened before and
//-sdj the user hits cntl-c/bye/quit etc
//-sdj the telnet service will stop talking
//-sdj with us, but terminalapp still keeps
//-sdj doing io without knowing that the handle
//-sdj can only be closed now, The way we can
//-sdj detect this is, to check if getlasterror
//-sdj is ERROR_NETNAME_DELETED, if this is the
//-sdj case then we should do exactly same thing
//-sdj which we do when the user tries to go to
//-sdj some other comm port, close this one, and
//-sdj go to the next one.
//-sdj by setting bPortDisconnected to TRUE,
//-sdj further modemBytes()[reads] will stop on
//-sdj this port, and modemBytes will prompt the
//-sdj user to select some other port, and return.
if (bPortDisconnected)
{
LoadString(hInst, STR_PORTDISCONNECT, (LPSTR) tmp1, TMPNSTR);
LoadString(hInst, STR_ERRCAPTION, (LPSTR) tmp2, TMPNSTR);
MessageBox(hItWnd, (LPSTR) tmp1, (LPSTR)tmp2, MB_OK | MB_APPLMODAL);
serCount = 0; //so that return dword is 0, no chars to process
// resetSerial(&trmParams, FALSE,TRUE,0);
if(!trmParams.fResetDevice)
trmParams.newDevRef = trmParams.comDevRef;
exitSerial();
doSettings(IDDBCOMM, dbComm);
break;
}
if(gotCommEvent)
{
WIN_modemBytes();
}
else
serCount = 0;
break;
case ITMDLLCONNECT: /* slc nova 012 bjw nova 002 */
serCount = DLL_ConnectBytes(ghCCB); /* slc nova 031 */
if((lpCCB = (LPCONNECTOR_CONTROL_BLOCK)GlobalLock(ghCCB)) != NULL) /* slc nova 031 */
{
if(serCount == CONNECT_READ_ERROR)
serCount = lpCCB->wReadBufferRead;
lmovmem((LPSTR)lpCCB->lpReadBuffer, (LPSTR)(serBytes + 1), (WORD)serCount);
GlobalUnlock(ghCCB);
}
break;
}
if(serCount > 0)
serNdx = 1; /* indicates chars to process */
return(serCount);
}
/*---------------------------------------------------------------------------*/
/* getMdmChar() - Get a modem character out of local buffer. [mbb] */
/*---------------------------------------------------------------------------*/
/* NOTE: modemBytes() must be called prior to this routine */
BYTE getMdmChar(BOOL bText)
{
BYTE nextChar;
nextChar = serBytes[serNdx++];
if(serNdx > serCount)
serNdx = 0;
if(trmParams.parity != ITMNOPARITY)
nextChar &= 0x7F;
if(bText && (trmParams.language > ICS_NONE) && (termState == NULL)) /* mbbx 1.06A: ics new xlate... */
{
if(nextChar >= 0x80) /* slc swat */
{
if(trmParams.setIBMXANSI)
nextChar = ansiXlateTable[nextChar]; /* IBM extended to ANSI */
}
else
{
if(trmParams.language > ICS_NONE)
nextChar = icsXlateTable[nextChar]; /* ISO char to ANSI */
}
}
return(nextChar);
}
/*---------------------------------------------------------------------------*/
/* getRcvChar() - [mbb] */
/*---------------------------------------------------------------------------*/
BOOL getRcvChar(BYTE *theChar, BYTE charMask)
{
if(modemBytes())
{
*theChar = getMdmChar(FALSE);
if(charMask != 0)
*theChar &= charMask;
return(TRUE);
}
*theChar = 0;
return(FALSE);
}
/*---------------------------------------------------------------------------*/
/* waitRcvChar() - [mbb] */
/*---------------------------------------------------------------------------*/
BOOL waitRcvChar(BYTE *theChar, WORD timeOut, BYTE charMask, BYTE charFirst, ...)
{
va_list ap;
DWORD waitTicks; //-sdj was LONG, getcurrenttime,tickcount returns dword
//-sdj this was causing a sign/unsign warning noise
BYTE charList;
waitTicks = tickCount() + (timeOut * 6);
repeat
{
va_start(ap,charFirst);
updateTimer();
if(doneFlag)
{
xferStopped = TRUE;
va_end(ap);
return(FALSE);
}
if(xferStopped)
break;
gotCommEvent = TRUE;
if(getRcvChar(theChar, charMask))
{
if(charFirst == 0)
{
va_end(ap);
return(TRUE);
}
for(charList = charFirst; charList != 0; charList = va_arg(ap,BYTE))
{
if(charList == *theChar)
{
va_end(ap);
return(TRUE);
}
}
*theChar = 0;
}
//if(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
// mainEventLoop();
//sdj: now that rcv and snd b file are threads the main
//sdj: thread will deal with UI while the xfer is going on, so
//sdj: no need of this hack to peek the msges.
//sdj: but lets put sleep of 10ms so that we dont hog the CPU
//sdj: and give chance for the buffer to fill and reduce number
//sdj: of calls to ReadFile()
Sleep(10);
}
until(tickCount() >= waitTicks);
va_end(ap);
return(FALSE);
}
/*---------------------------------------------------------------------------*/
/* flushRBuf() - [mbb] */
/*---------------------------------------------------------------------------*/
VOID flushRBuff()
{
if(modemBytes())
delay(6, NULL); /* mbbx: wtf??? */
while(modemBytes())
serNdx = 0; /* mbbx: dump serBytes data */
}
/*---------------------------------------------------------------------------*/
/* checkUserAbort() - [mbb] */
/*---------------------------------------------------------------------------*/
BOOL checkUserAbort()
{
BOOL checkUserAbort = FALSE;
return(FALSE);
if(PeekMessage(&msg, hdbXferCtrls, 0, 0, PM_REMOVE))
{
if((msg.hwnd != xferCtlStop) ||
(msg.message < WM_MOUSEFIRST) ||
(msg.message > WM_MOUSELAST))
{
IsDialogMessage(hdbXferCtrls, &msg);
}
}
while(PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE))
{
if((msg.message == WM_KEYDOWN) && (msg.wParam == VK_CANCEL))
checkUserAbort = TRUE;
}
return(checkUserAbort);
}
/*---------------------------------------------------------------------------*/
/* modemWrite() - Send data to comm port (no special processing here) [mbb] */
/*---------------------------------------------------------------------------*/
BOOL NEAR WIN_modemWrite(LPSTR lpData, INT nSize)
{
BOOL modemWrite = TRUE;
BOOL bWriteFile;
INT nRemain;
INT nBytes;
INT nSent;
COMSTAT serInfo;
// -sdj unreferenced local var: BYTE str[80];
DWORD dwErrors;
OVERLAPPED overlap;
BOOL bRc;
overlap.hEvent = overlapEvent;
overlap.Internal = 0;
overlap.InternalHigh = 0;
overlap.Offset = 0;
overlap.OffsetHigh = 0;
for(nRemain = nSize; nRemain > 0; nRemain -= nSent)
{
nBytes = nRemain;
bWriteFile = WriteFile(sPort, (LPVOID) (lpData+(nSize-nRemain)),
nBytes, (LPDWORD) &nSent,
(LPOVERLAPPED)&overlap);
dwErrors = GetLastError();
if ((!bWriteFile) && (dwErrors != ERROR_IO_PENDING))
{
bRc = ClearCommError(sPort, &dwErrors, &serInfo);
if(serInfo.fXoffSent || serInfo.fCtsHold)
{
if(serInfo.fCtsHold)
{
sPortErr = TRUE;
return(FALSE);
}
rxEventLoop(); /* jtf 3.20 */
if ( (xferStopped == TRUE) && (xferFlag != XFRNONE) ) /* jtf 3.33 3.30 */
{
modemWrite = TRUE;
return(modemWrite); /* jtf 3.30 */
}
if(checkUserAbort()) /* mbbx: see if CTRL BREAK hit */
{
switch(trmParams.flowControl) /* mbbx 1.10: CUA... */
{
case ITMXONFLOW:
modemReset();
break;
case ITMHARDFLOW: /* drastic ... */
trmParams.flowControl = ITMNOFLOW;
resetSerial(&trmParams, FALSE, FALSE, 0);
break;
}
modemWrite = FALSE;
break;
}/* if checkUserAbort */
}/* if serInfo.hold */
}/* if writeComm */
else
{
if(!bWriteFile)
if(dwErrors != ERROR_IO_PENDING)
{
bRc = ClearCommError(sPort, &dwErrors, &serInfo);
modemWrite = FALSE;
nSent = 0;
}
else
{
if(WaitForSingleObject(overlapEvent, dwWriteFileTimeout) == 0)
{
GetOverlappedResult(sPort, &overlap, (LPDWORD)&nSent, TRUE);
}
else
{
ResetEvent(overlapEvent);
bRc = ClearCommError(sPort, &dwErrors, &serInfo);
nSent = 0;
}
}
#ifdef SLEEP_FOR_CONTEXT_SWITCH
Sleep((DWORD)5);
#endif
// if(!nSent)
// {
// modemWrite = FALSE;
// break;
// }
}
}/* for */
if(xferBreak) /* mbbx 2.00: xfer ctrls... */
{
setXferCtrlButton(IDSTOP, STR_STOP);
xferBreak = FALSE;
}
return(modemWrite);
}/* WIN_modemWrite */
/* ----------------------------------------------------------------------- */
BOOL modemWrite(LPSTR lpData, INT nSize)
{
LPCONNECTOR_CONTROL_BLOCK lpCCB; /* slc nova 031 */
BOOL bResult = FALSE; /* slc swat */
if (nSize == 0) /* mbbx 2.00.04: check outgoing buffer... */
{
switch(trmParams.comDevRef)
{
case ITMDLLCONNECT: /* slc nova 028 */
if((lpCCB = (LPCONNECTOR_CONTROL_BLOCK)GlobalLock(ghCCB)) != NULL)
{
lpCCB->wWriteBufferUsed = (WORD)nSize; /* slc nova 031 */
GlobalUnlock(ghCCB);
DLL_WriteConnector(ghCCB);
}
break;
case ITMWINCOM:
default:
break;
}
return(TRUE);
}
/* We cannot allow recursive calls to the modemWrite() sub,
cuz we stack overflow! Design issue: caller should check
this return value and re-call with same data!
*/
if(bgOutStandingWrite) /* slc swat */
{
sysBeep();
return(FALSE);
}
else
bgOutStandingWrite = TRUE;
switch(trmParams.comDevRef)
{
case ITMWINCOM:
bResult = WIN_modemWrite(lpData, nSize);
break;
case ITMDLLCONNECT: /* slc nova 012 bjw nova 002 */
if((lpCCB = (LPCONNECTOR_CONTROL_BLOCK)GlobalLock(ghCCB)) != NULL) /* slc nova 031 */
{
if(lpCCB->lpWriteBuffer) /* seh nova 005 */
{
lmovmem((LPSTR)lpData, (LPSTR)lpCCB->lpWriteBuffer, (WORD)nSize); /* seh nova 005 */
lpCCB->wWriteBufferUsed = (WORD)nSize; /* seh nova 005 */
GlobalUnlock(ghCCB); /* slc nova 031 */
bResult = DLL_WriteConnector(ghCCB);
}
}
break;
}/* switch */
bgOutStandingWrite = FALSE; /* slc swat */
return(bResult);
}
/*---------------------------------------------------------------------------*/
/* modemWr() - Send character to the windows serial port driver. [mbb] */
/*---------------------------------------------------------------------------*/
VOID modemWr(BYTE theByte)
{
BYTE saveByte = theByte;
BYTE ISOByte;
if((theByte >= 0x80) && (xferFlag < XFRBSND)) /* mbbx 1.10: VT220 8BIT... */
{
if(trmParams.language > ICS_NONE)
ISOByte = icsXlateTable[theByte];
else
ISOByte = theByte;
if(trmParams.setIBMXANSI)
{
if(ISOByte >= 0x80) /* was not ISO */
theByte = ansiXlateTable[theByte & 0x7F]; /* ANSI to IBM extended */
}
else
theByte = ISOByte;
}
else if((theByte == CR) && (trmParams.emulate == ITMDELTA))
theByte = XOFF;
if(!modemWrite((LPSTR) &theByte, 1))
return;
if(trmParams.localEcho && (xferFlag < XFRTYP))
{
modemInp(saveByte, FALSE);
if((theByte == CR) && (xferFlag == XFRSND))
modemInp(LF, FALSE);
}
if(trmParams.outCRLF && (theByte == CR) && (xferFlag == XFRNONE)) /* mbbx 2.00: heed outCRLF... */
modemWr(LF); /* yikes! it's recursive!!! */
}
/*---------------------------------------------------------------------------*/
/* termStr() - Send PASCAL character string to the modem. [mbb] */
/*---------------------------------------------------------------------------*/
VOID termStr(STRING *tStr, INT nDelay, BOOL crFlag)
{
WORD ndx;
for(ndx = 1; ndx <= *tStr; ndx++)
{
modemWr(tStr[ndx]);
if(nDelay > 0)
delay(nDelay, NULL);
if(!dialing)
idleProcess();
}
if(crFlag)
modemWr(CR);
}
DWORD checkCommEvent(LPVOID lpThreadParameter)
{
DWORD eventMask;
HANDLE hEvent;
OVERLAPPED OverLapped;
DWORD dwGetLastError;
// eventMask = EV_RXCHAR | EV_ERR | EV_BREAK | EV_CTS | EV_DSR;
eventMask = EV_RXCHAR;
SetCommMask(sPort, eventMask);
hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
OverLapped.hEvent = hEvent;
while(TRUE)
{
if(bPortIsGood && (sPort != NULL) && (sPort != (HANDLE)-1) && (!CommThreadExit) )
{
if(WaitForSingleObject(hMutex, 50) == 0)
{
eventMask = EV_RXCHAR;
// SetCommMask(sPort, eventMask);
if(WaitCommEvent(sPort, (LPDWORD)&eventMask, &OverLapped))
{
gotCommEvent = TRUE;
}
else
{
dwGetLastError = GetLastError();
if (dwGetLastError == ERROR_IO_PENDING)
{
DWORD Trash;
GetOverlappedResult(
sPort,
&OverLapped,
&Trash,
TRUE
);
gotCommEvent = TRUE;
}
else {
//-sdj for telnet-quit processing
//-sdj if this is a telnet connection and
//-sdj the user hits cntl-c/bye/quit etc
//-sdj the telnet service will stop talking
//-sdj with us, but terminalapp still keeps
//-sdj doing io without knowing that the handle
//-sdj can only be close now, The way we can
//-sdj detect this is, to check if getlasterror
//-sdj is ERROR_NETNAME_DELETED, if this is the
//-sdj case then we should do exactly same thing
//-sdj which we do when the user tries to go to
//-sdj some other comm port, close this one, and
//-sdj go to the next one.
//-sdj by setting bPortDisconnected to TRUE,
//-sdj further modemBytes()[reads] will stop on
//-sdj this port, and modemBytes will prompt the
//-sdj user to select some other port, and return.
CloseHandle(sPort); // only valid operation in this state
sPort = NULL; // this will prevent checkcommevent to
// attempt unnecessary waits untill sPort
// becomes valid, and bPortDisconnected
// is set back to FALSE by modembytes/resetserial
bPortDisconnected = TRUE;
}
}
if(CommThreadExit) // was ,doneFlag but doneFlag
// does not get set for sometime
// even after the comm port closes
// so exit the thread when you know
// that the port is going to get closed
// This flag is init to false and set
// to true just before calling exitserial
// in termfile.c
{
gbThreadDoneFlag = TRUE;
ReleaseMutex(hMutex);
ResetEvent(hEvent);
ExitThread((DWORD)0);
}
ReleaseMutex(hMutex);
eventMask = EV_RXCHAR;
} // wait on mutex
#ifdef SLEEP_FOR_CONTEXT_SWITCH
Sleep((DWORD)3);
#endif
ResetEvent(hEvent);
} // good sPort
else
{
if(CommThreadExit) // was doneFlag : see above for comments
{
gbThreadDoneFlag = TRUE;
ReleaseMutex(hMutex);
ResetEvent(hEvent);
ExitThread((DWORD)0);
}
Sleep((DWORD)3); // -sdj either the comm port handle is changing
// or it is invalid, so instead of doing
// a tight while-true, sleep each time you
// come here, so that others get a chance
}
}
return 0;
}