win32系统下关于串口通讯API详解(microsoft )
2011-09-26 15:25
417 查看
SerialCommunicationsinWin32 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
AllenDenver MicrosoftWindowsDeveloperSupport December11,1995 Allenseldomeatsbreakfast,butifhehadtopickafavorite,Win32serialcommunicationswouldbethetopchoice. AbstractSerialcommunicationsinMicrosoft?Win32?issignificantlydifferentfromserialcommunicationsin16-bitMicrosoftWindows?Thosefamiliarwith16-bitserialcommunicationsfunctionswillhavetorelearnmanypartsofthesystemtoprogramserialcommunicationsproperly.Thisarticlewillhelptoaccomplishthis.Thoseunfamiliarwithserialcommunicationswillfindthisarticleahelpfulfoundationfordevelopmentefforts. ThisarticleassumesthereaderisfamiliarwiththefundamentalsofmultiplethreadingandsynchronizationinWin32.Inaddition,abasicfamiliarityoftheWin32heapfunctionsisusefultofullycomprehendthememorymanagement methodsusedbythesample,MTTTY,includedwiththisarticle.Formoreinformationregardingthesefunctions,consultthePlatformSDKdocumentation,theMicrosoftWin32SDKKnowledgeBase,ortheMicrosoftDeveloperNetworkLibrary.Applicationprogramming interfaces(APIs)thatcontroluserinterfacefeaturesofwindowsanddialogboxes,thoughnotdiscussedhere,areusefultoknowinordertofullycomprehendthesampleprovidedwiththisarticle.ReadersunfamiliarwithgeneralWindowsprogrammingpractices shouldlearnsomeofthefundamentalsofgeneralWindowsprogrammingbeforetakingonserialcommunications.Inotherwords,getyourfeetwetbeforedivinginheadfirst. IntroductionThefocusofthisarticleisonapplicationprogramminginterfaces(APIs)andmethodsthatarecompatiblewithMicrosoft?WindowsNT?andWindows95;therefore,APIssupportedonbothplatformsaretheonlyonesdiscussed.Windows95supportstheWin32?TelephonyAPI(TAPI)andWindowsNT3.xdoesnot;therefore,thisdiscussionwillnotincludeTAPI.TAPIdoesdeservemention,however,inthatitverynicelyimplementsmodeminterfacingandcallcontrolling.Aproductionapplication thatworkswithmodemsandmakestelephonecallsshouldimplementthesefeaturesusingtheTAPIinterface.ThiswillallowseamlessintegrationwiththeotherTAPI-enabledapplicationsthatausermayhave.Furthermore,thisarticledoesnotdiscusssomeof theconfigurationfunctionsinWin32,suchasGetCommProperties. Thearticleisbrokenintothefollowingsections:Openingaport,readingandwriting(nonoverlappedandoverlapped),serialstatus(eventsanderrors),andserialsettings(DCB,flowcontrol,andcommunicationstime-outs). Thesampleincludedwiththisarticle,MTTTY:MultithreadedTTY,implementsmanyofthefeaturesdiscussedhere.Itusesthreethreadsinitsimplementation:auserinterfacethreadthatdoesmemorymanagement,awriterthreadthatcontrols allwriting,andareader/statusthreadthatreadsdataandhandlesstatuschangesontheport.Thesampleemploysafewdifferentdataheapsformemorymanagement.Italsomakesextensiveuseofsynchronizationmethodstofacilitatecommunicationbetween threads. OpeningaPortTheCreateFilefunctionopensacommunicationsport.TherearetwowaystocallCreateFiletoopenthecommunicationsport:overlappedandnonoverlapped.Thefollowingistheproperwaytoopenacommunicationsresourceforoverlappedoperation: HANDLEhComm; hComm=CreateFile(gszPort, GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); if(hComm==INVALID_HANDLE_VALUE) //erroropeningport;abort RemovaloftheFILE_FLAG_OVERLAPPEDflagfromthecallto CreateFilespecifiesnonoverlappedoperation.Thenextsectiondiscussesoverlappedandnonoverlappedoperations. ThePlatformSDKdocumentationstatesthatwhenopeningacommunicationsport,thecalltoCreateFilehasthefollowingrequirements: fdwShareModemustbezero.Communicationsportscannotbesharedinthesamemannerthatfilesareshared.ApplicationsusingTAPIcanusetheTAPIfunctionstofacilitatesharingresourcesbetweenapplications.ForWin32applications notusingTAPI,handleinheritanceorduplicationisnecessarytosharethecommunicationsport.Handleduplicationisbeyondthescopeofthisarticle;pleaserefertothePlatformSDKdocumentationformoreinformation. [align=justify]fdwCreatemustspecifytheOPEN_EXISTINGflag.[/align] [align=justify]hTemplateFileparametermustbeNULL.[/align] OnethingtonoteaboutportnamesisthattraditionallytheyhavebeenCOM1,COM2,COM3,orCOM4.TheWin32APIdoesnotprovideanymechanismfordeterminingwhatportsexistonasystem.WindowsNTandWindows95keeptrackofinstalled portsdifferentlyfromoneanother,soanyonemethodwouldnotbeportableacrossallWin32platforms.Somesystemsevenhavemoreportsthanthetraditionalmaximumoffour.Hardwarevendorsandserial-device-driverwritersarefreetonametheportsanything theylike.Forthisreason,itisbestthatusershavetheabilitytospecifytheportnametheywanttouse.Ifaportdoesnotexist,anerrorwilloccur(ERROR_FILE_NOT_FOUND)afterattemptingtoopentheport,andtheusershouldbenotifiedthattheport isn’tavailable. ReadingandWritingReadingfromandwritingtocommunicationsportsinWin32isverysimilartofileinput/output(I/O)inWin32.Infact,thefunctionsthataccomplishfileI/OarethesamefunctionsusedforserialI/O.I/OinWin32canbedoneeitheroftwoways:overlappedornonoverlapped.ThePlatformSDKdocumentationusestheterms asynchronousandsynchronoustoconnotethesetypesofI/Ooperations.Thisarticle,however,usesthetermsoverlappedandnonoverlapped. NonoverlappedI/OisfamiliartomostdevelopersbecausethisisthetraditionalformofI/O,whereanoperationisrequestedandisassumedtobecompletewhenthefunctionreturns.InthecaseofoverlappedI/O,thesystem mayreturntothecallerimmediatelyevenwhenanoperationisnotfinishedandwillsignalthecallerwhentheoperationcompletes.TheprogrammayusethetimebetweentheI/Orequestanditscompletiontoperformsome“background?work. ReadingandwritinginWin32issignificantlydifferentfromreadingandwritingserialcommunicationsportsin16-bitWindows.16-bitWindowsonlyhastheReadCommandWriteCommfunctions.Win32readingand writingcaninvolvemanymorefunctionsandchoices.Theseissuesarediscussedbelow. NonoverlappedI/ONonoverlappedI/Oisverystraightforward,thoughithaslimitations.Anoperationtakesplacewhilethecallingthreadisblocked.Oncetheoperationiscomplete,thefunctionreturnsandthethreadcancontinueitswork.ThistypeofI/OisusefulformultithreadedapplicationsbecausewhileonethreadisblockedonanI/Ooperation,otherthreadscanstillperformwork.Itistheresponsibilityoftheapplicationtoserializeaccesstotheportcorrectly.Ifonethreadisblockedwaiting foritsI/Ooperationtocomplete,allotherthreadsthatsubsequentlycallacommunicationsAPIwillbeblockeduntiltheoriginaloperationcompletes.Forinstance,ifonethreadwerewaitingforaReadFilefunctiontoreturn,anyotherthread thatissuedaWriteFilefunctionwouldbeblocked. Oneofthemanyfactorstoconsiderwhenchoosingbetweennonoverlappedandoverlappedoperationsisportability.Overlappedoperationisnotagoodchoicebecausemostoperatingsystemsdonotsupportit.Mostoperatingsystemssupportsome formofmultithreading,however,somultithreadednonoverlappedI/Omaybethebestchoiceforportabilityreasons. OverlappedI/OOverlappedI/OisnotasstraightforwardasnonoverlappedI/O,butallowsmoreflexibilityandefficiency.AportopenforoverlappedoperationsallowsmultiplethreadstodoI/Ooperationsatthesametimeandperformotherworkwhiletheoperationsarepending.Furthermore,thebehaviorofoverlappedoperationsallowsasinglethreadtoissuemanydifferentrequestsanddoworkinthebackgroundwhiletheoperationsarepending. Inbothsingle-threadedandmultithreadedapplications,somesynchronizationmusttakeplacebetweenissuingrequestsandprocessingtheresults.Onethreadwillhavetobeblockeduntiltheresultofanoperationisavailable.Theadvantage isthatoverlappedI/Oallowsathreadtodosomeworkbetweenthetimeoftherequestanditscompletion.Ifnoworkcanbedone,thentheonlycaseforoverlappedI/Oisthatitallowsforbetteruserresponsiveness. OverlappedI/OisthetypeofoperationthattheMTTTYsampleuses.Itcreatesathreadthatisresponsibleforreadingtheport’sdataandreadingtheport’sstatus.Italsoperformsperiodicbackgroundwork.Theprogramcreatesanother threadexclusivelyforwritingdataouttheport. NoteApplicationssometimesabusemultithreadingsystemsbycreatingtoomanythreads.Althoughusingmultiplethreadscanresolvemanydifficultproblems,creatingexcessivethreadsisnotthemostefficientuseofthem inanapplication.ThreadsarelessastrainonthesystemthanprocessesbutstillrequiresystemresourcessuchasCPUtimeandmemory.Anapplicationthatcreatesexcessivethreadsmayadverselyaffecttheperformanceoftheentiresystem.Abetteruse ofthreadsistocreateadifferentrequestqueueforeachtypeofjobandtohaveaworkerthreadissueanI/Orequestforeachentryintherequestqueue.ThismethodisusedbytheMTTTYsampleprovidedwiththisarticle. AnoverlappedI/Ooperationhastwoparts:thecreationoftheoperationandthedetectionofitscompletion.CreatingtheoperationentailssettingupanOVERLAPPEDstructure,creatingamanual-reseteventforsynchronization, andcallingtheappropriatefunction(ReadFileorWriteFile).TheI/Ooperationmayormaynotbecompletedimmediately.Itisanerrorforanapplicationtoassumethatarequestforanoverlappedoperationalwaysyieldsan overlappedoperation.Ifanoperationiscompletedimmediately,anapplicationneedstobereadytocontinueprocessingnormally.Thesecondpartofanoverlappedoperationistodetectitscompletion.Detectingcompletionoftheoperationinvolveswaiting fortheeventhandle,checkingtheoverlappedresult,andthenhandlingthedata.Thereasonthatthereismoreworkinvolvedwithanoverlappedoperationisthattherearemorepointsoffailure.Ifanonoverlappedoperationfails,thefunctionjustreturns anerror-returnresult.Ifanoverlappedoperationfails,itcanfailinthecreationoftheoperationorwhiletheoperationispending.Youmayalsohaveatime-outoftheoperationoratime-outwaitingforthesignalthattheoperationiscomplete. ReadingTheReadFilefunctionissuesareadoperation.ReadFileExalsoissuesareadoperation,butsinceitisnotavailableonWindows95,itisnotdiscussedinthisarticle.Hereisacodesnippetthatdetailshowtoissueareadrequest.Noticethatthefunctioncallsafunctiontoprocess thedataiftheReadFilereturnsTRUE.Thisisthesamefunctioncallediftheoperationbecomesoverlapped.NotethefWaitingOnReadflagthatisdefinedbythecode;itindicateswhetherornotareadoperationisoverlapped.Itisusedto preventthecreationofanewreadoperationifoneisoutstanding. DWORDdwRead; BOOLfWaitingOnRead=FALSE; OVERLAPPEDosReader={0}; //Createtheoverlappedevent.Mustbeclosedbeforeexiting //toavoidahandleleak. osReader.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); if(osReader.hEvent==NULL) //Errorcreatingoverlappedevent;abort. if(!fWaitingOnRead){ //Issuereadoperation. if(!ReadFile(hComm,lpBuf,READ_BUF_SIZE,&dwRead,&osReader)){ if(GetLastError()!=ERROR_IO_PENDING)//readnotdelayed? //Errorincommunications;reportit. else fWaitingOnRead=TRUE; } else{ //readcompletedimmediately HandleASuccessfulRead(lpBuf,dwRead); } } Thesecondpartoftheoverlappedoperationisthedetectionofitscompletion.TheeventhandleintheOVERLAPPEDstructureispassedtotheWaitForSingleObjectfunction,whichwillwaituntiltheobjectis signaled.Oncetheeventissignaled,theoperationiscomplete.Thisdoesnotmeanthatitwascompletedsuccessfully,justthatitwascompleted.TheGetOverlappedResultfunctionreportstheresultoftheoperation.Ifanerroroccurred,GetOverlappedResult returnsFALSEandGetLastErrorreturnstheerrorcode.Iftheoperationwascompletedsuccessfully,GetOverlappedResultwillreturnTRUE. NoteGetOverlappedResultcandetectcompletionoftheoperation,aswellasreturntheoperation’sfailurestatus.GetOverlappedResultreturnsFALSEandGetLastErrorreturnsERROR_IO_INCOMPLETE whentheoperationisnotcompleted.Inaddition,GetOverlappedResultcanbemadetoblockuntiltheoperationcompletes.ThiseffectivelyturnstheoverlappedoperationintoanonoverlappedoperationandisaccomplishedbypassingTRUEas thebWaitparameter. Hereisacodesnippetthatshowsonewaytodetectthecompletionofanoverlappedreadoperation.Notethatthecodecallsthesamefunctiontoprocessthedatathatwascalledwhentheoperationcompletedimmediately.Alsonotetheuse ofthefWaitingOnReadflag.Hereitcontrolsentryintothedetectioncode,sinceitshouldbecalledonlywhenanoperationisoutstanding. #defineREAD_TIMEOUT500//milliseconds DWORDdwRes; if(fWaitingOnRead){ dwRes=WaitForSingleObject(osReader.hEvent,READ_TIMEOUT); switch(dwRes) { //Readcompleted. caseWAIT_OBJECT_0: if(!GetOverlappedResult(hComm,&osReader,&dwRead,FALSE)) //Errorincommunications;reportit. else //Readcompletedsuccessfully. HandleASuccessfulRead(lpBuf,dwRead); //Resetflagsothatanotheropertioncanbeissued. fWaitingOnRead=FALSE; break; caseWAIT_TIMEOUT: //Operationisn'tcompleteyet.fWaitingOnReadflagisn't //changedsinceI'llloopbackaround,andIdon'twant //toissueanotherreaduntilthefirstonefinishes. // //Thisisagoodtimetodosomebackgroundwork. break; default: //ErrorintheWaitForSingleObject;abort. //ThisindicatesaproblemwiththeOVERLAPPEDstructure's //eventhandle. break; } } WritingTransmittingdataoutthecommunicationsportisverysimilartoreadinginthatitusesalotofthesameAPIs.Thecodesnippetbelowdemonstrateshowtoissueandwaitforawriteoperationtobecompleted.BOOLWriteABuffer(char*lpBuf,DWORDdwToWrite) { OVERLAPPEDosWrite={0}; DWORDdwWritten; DWORDdwRes; BOOLfRes; //Createthiswriteoperation'sOVERLAPPEDstructure'shEvent. osWrite.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); if(osWrite.hEvent==NULL) //errorcreatingoverlappedeventhandle returnFALSE; //Issuewrite. if(!WriteFile(hComm,lpBuf,dwToWrite,&dwWritten,&osWrite)){ if(GetLastError()!=ERROR_IO_PENDING){ //WriteFilefailed,butisn'tdelayed.Reporterrorandabort. fRes=FALSE; } else //Writeispending. dwRes=WaitForSingleObject(osWrite.hEvent,INFINITE); switch(dwRes) { //OVERLAPPEDstructure'seventhasbeensignaled. caseWAIT_OBJECT_0: if(!GetOverlappedResult(hComm,&osWrite,&dwWritten,FALSE)) fRes=FALSE; else //Writeoperationcompletedsuccessfully. fRes=TRUE; break; default: //AnerrorhasoccurredinWaitForSingleObject. //Thisusuallyindicatesaproblemwiththe //OVERLAPPEDstructure'seventhandle. fRes=FALSE; break; } } } else //WriteFilecompletedimmediately. fRes=TRUE; CloseHandle(osWrite.hEvent); returnfRes; } NoticethatthecodeaboveusestheWaitForSingleObject functionwithatime-outvalueofINFINITE.ThiscausestheWaitForSingleObjectfunctiontowaitforeveruntiltheoperationiscompleted;thismaymakethethreadorprogramappeartobe“hung?when,infact,thewriteoperationis simplytakingalongtimetocompleteorflowcontrolhasblockedthetransmission.Statuschecking,discussedlater,candetectthiscondition,butdoesn’tcausetheWaitForSingleObjecttoreturn.Threethingscanalleviatethiscondition: [align=justify]Placethecodeinaseparatethread.Thisallowsotherthreadstoexecuteanyfunctionstheydesirewhileourwriterthreadwaitsforthewritetobecompleted.ThisiswhattheMTTTYsampledoes.[/align] [align=justify]UseCOMMTIMEOUTStocausethewritetobecompletedafteratime-outperiodhaspassed.Thisisdiscussedmorefullyinthe“CommunicationsTime-outs?sectionlaterinthisarticle.ThisisalsowhattheMTTTYsampleallows.[/align] ChangetheWaitForSingleObjectcalltoincludearealtime-outvalue.Thiscausesmoreproblemsbecauseiftheprogramissuesanotheroperationwhileanolderoperationisstillpending,newOVERLAPPEDstructures andoverlappedeventsneedtobeallocated.Thistypeofrecordkeepingisdifficult,particularlywhencomparedtousinga“jobqueue?designfortheoperations.The“jobqueue?methodisusedintheMTTTYsample. Note:Thetime-outvaluesinsynchronizationfunctionsarenotcommunicationstime-outs.Synchronizationtime-outscauseWaitForSingleObjectorWaitForMultipleObjectstoreturnWAIT_TIMEOUT. Thisisnotthesameasareadorwriteoperationtimingout.Communicationstime-outsaredescribedlaterinthisarticle. BecausetheWaitForSingleObjectfunctionintheabovecodesnippetusesanINFINITEtime-out,itisequivalenttousingGetOverlappedResultwithTRUEforthefWaitparameter.Hereisequivalentcode inamuchsimplifiedform: BOOLWriteABuffer(char*lpBuf,DWORDdwToWrite) { OVERLAPPEDosWrite={0}; DWORDdwWritten; BOOLfRes; //CreatethiswritesOVERLAPPEDstructurehEvent. osWrite.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); if(osWrite.hEvent==NULL) //Errorcreatingoverlappedeventhandle. returnFALSE; //Issuewrite. if(!WriteFile(hComm,lpBuf,dwToWrite,&dwWritten,&osWrite)){ if(GetLastError()!=ERROR_IO_PENDING){ //WriteFilefailed,butitisn'tdelayed.Reporterrorandabort. fRes=FALSE; } else{ //Writeispending. if(!GetOverlappedResult(hComm,&osWrite,&dwWritten,TRUE)) fRes=FALSE; else //Writeoperationcompletedsuccessfully. fRes=TRUE; } } else //WriteFilecompletedimmediately. fRes=TRUE; CloseHandle(osWrite.hEvent); returnfRes; } GetOverlappedResultisnotalwaysthebestwaytowaitforanoverlappedoperationtobecompleted.Forexample,ifanapplicationneedstowaitonanothereventhandle,thefirstcodesnippetservesasabettermodelthan thesecond.ThecalltoWaitForSingleObjectiseasytochangeto WaitForMultipleObjectstoincludetheadditionalhandlesonwhichtowait.ThisiswhattheMTTTYsampleapplicationdoes. AcommonmistakeinoverlappedI/OistoreuseanOVERLAPPEDstructurebeforethepreviousoverlappedoperationiscompleted.Ifanewoverlappedoperationisissuedbeforeapreviousoperationiscompleted,anewOVERLAPPED structuremustbeallocatedforit.Anewmanual-reseteventforthehEventmemberoftheOVERLAPPEDstructuremustalsobecreated.Onceanoverlappedoperationiscomplete,theOVERLAPPEDstructureandits eventarefreeforreuse. TheonlymemberoftheOVERLAPPEDstructurethatneedsmodifyingforserialcommunicationsisthehEventmember.TheothermembersoftheOVERLAPPEDstructureshouldbeinitializedtozero andleftalone.ModifyingtheothermembersoftheOVERLAPPEDstructureisnotnecessaryforserialcommunicationsdevices.ThedocumentationforReadFileandWriteFilestatethattheOffsetandOffsetHigh membersoftheOVERLAPPEDstructuremustbeupdatedbytheapplication,orelseresultsareunpredictable.ThisguidelineshouldbeappliedtoOVERLAPPEDstructuresusedforothertypesofresources,suchasfiles. SerialStatusTherearetwomethodstoretrievethestatusofacommunicationsport.Thefirstistosetaneventmaskthatcausesnotificationoftheapplicationwhenthedesiredeventsoccur.TheSetCommMaskfunctionsetsthiseventmask,andtheWaitCommEventfunctionwaitsforthedesiredeventstooccur.Thesefunctionsaresimilartothe16-bitfunctionsSetCommEventMaskandEnableCommNotification,exceptthattheWin32functionsdonotpost WM_COMMNOTIFYmessages.Infact,theWM_COMMNOTIFYmessageisnotevenpartoftheWin32API.Thesecondmethodforretrievingthestatusofthecommunicationsportistoperiodicallycallafewdifferentstatusfunctions.Pollingis,ofcourse,neitherefficient norrecommended. CommunicationsEventsCommunicationseventscanoccuratanytimeinthecourseofusingacommunicationsport.Thetwostepsinvolvedinreceivingnotificationofcommunicationseventsareasfollows:[align=justify]SetCommMasksetsthedesiredeventsthatcauseanotification.[/align] [align=justify]WaitCommEventissuesastatuscheck.Thestatuscheckcanbeanoverlappedornonoverlappedoperation,justasthereadandwriteoperationscanbe.[/align] Note:Thewordeventinthiscontextreferstocommunicationseventsonly.Itdoesnotrefertoaneventobjectusedforsynchronization. HereisanexampleoftheSetCommMaskfunction: DWORDdwStoredFlags; dwStoredFlags=EV_BREAK|EV_CTS|EV_DSR|EV_ERR|EV_RING|\ EV_RLSD|EV_RXCHAR|EV_RXFLAG|EV_TXEMPTY; if(!SetCommMask(hComm,dwStoredFlags)) //errorsettingcommunicationsmask AdescriptionofeachtypeofeventisinTable1. Table1.CommunicationsEventFlags
structure.Thefunctionblocksthecallingthreaduntiltheoccurrenceofoneoftheevents.Ifaneventneveroccurs,thethreadmayblockindefinitely. HereisacodesnippetthatshowshowtowaitforanEV_RINGeventwhentheportisopenfornonoverlappedoperation: DWORDdwCommEvent; if(!SetCommMask(hComm,EV_RING)) //Errorsettingcommunicationsmask returnFALSE; if(!WaitCommEvent(hComm,&dwCommEvent,NULL)) //Anerroroccurredwaitingfortheevent. returnFALSE; else //Eventhasoccurred. returnTRUE; NoteTheMicrosoftWin32SDKKnowledgeBasedocumentsaproblemwithWindows95andtheEV_RINGflag.TheabovecodeneverreturnsinWindows95becausetheEV_RINGeventisnotdetectedbythesystem;WindowsNTproperly reportstheEV_RINGevent.PleaseseetheWin32SDKKnowledgeBaseformoreinformationonthisbug. Asnoted,thecodeabovecanbeblockedforeverifaneventneveroccurs.Abettersolutionwouldbetoopentheportforoverlappedoperationandwaitforastatuseventinthefollowingmanner: #defineSTATUS_CHECK_TIMEOUT500//Milliseconds DWORDdwRes; DWORDdwCommEvent; DWORDdwStoredFlags; BOOLfWaitingOnStat=FALSE; OVERLAPPEDosStatus={0}; dwStoredFlags=EV_BREAK|EV_CTS|EV_DSR|EV_ERR|EV_RING|\ EV_RLSD|EV_RXCHAR|EV_RXFLAG|EV_TXEMPTY; if(!SetCommMask(comHandle,dwStoredFlags)) //errorsettingcommunicationsmask;abort return0; osStatus.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); if(osStatus.hEvent==NULL) //errorcreatingevent;abort return0; for(;;){ //Issueastatuseventcheckifonehasn'tbeenissuedalready. if(!fWaitingOnStat){ if(!WaitCommEvent(hComm,&dwCommEvent,&osStatus)){ if(GetLastError()==ERROR_IO_PENDING) bWaitingOnStatusHandle=TRUE; else //errorinWaitCommEvent;abort break; } else //WaitCommEventreturnedimmediately. //Dealwithstatuseventasappropriate. ReportStatusEvent(dwCommEvent); } //Checkonoverlappedoperation. if(fWaitingOnStat){ //Waitalittlewhileforaneventtooccur. dwRes=WaitForSingleObject(osStatus.hEvent,STATUS_CHECK_TIMEOUT); switch(dwRes) { //Eventoccurred. caseWAIT_OBJECT_0: if(!GetOverlappedResult(hComm,&osStatus,&dwOvRes,FALSE)) //Anerroroccurredintheoverlappedoperation; //callGetLastErrortofindoutwhatitwas //andabortifitisfatal. else //Statuseventisstoredintheeventflag //specifiedintheoriginalWaitCommEventcall. //Dealwiththestatuseventasappropriate. ReportStatusEvent(dwCommEvent); //SetfWaitingOnStatflagtoindicatethatanew //WaitCommEventistobeissued. fWaitingOnStat=FALSE; break; caseWAIT_TIMEOUT: //Operationisn'tcompleteyet.fWaitingOnStatusHandleflag //isn'tchangedsinceI'llloopbackaroundandIdon'twant //toissueanotherWaitCommEventuntilthefirstonefinishes. // //Thisisagoodtimetodosomebackgroundwork. DoBackgroundWork(); break; default: //ErrorintheWaitForSingleObject;abort //ThisindicatesaproblemwiththeOVERLAPPEDstructure's //eventhandle. CloseHandle(osStatus.hEvent); return0; } } } CloseHandle(osStatus.hEvent); Thecodeaboveverycloselyresemblesthecodeforoverlappedreading.Infact,theMTTTYsampleimplementsitsreadingandstatuscheckinginthesamethreadusingWaitForMultipleObjectstowaitforeitherthereadevent orthestatuseventtobecomesignaled. TherearetwointerestingsideeffectsofSetCommMaskand WaitCommEvent.First,ifthecommunicationsportisopenfornonoverlappedoperation,WaitCommEventwillbeblockeduntilaneventoccurs.IfanotherthreadcallsSetCommMasktosetaneweventmask,thatthread willbeblockedonthecalltoSetCommMask.ThereasonisthattheoriginalcalltoWaitCommEventinthefirstthreadisstillexecuting.ThecalltoSetCommMaskblocksthethreaduntiltheWaitCommEvent functionreturnsinthefirstthread.ThissideeffectisuniversalforportsopenfornonoverlappedI/O.Ifathreadisblockedonanycommunicationsfunctionandanotherthreadcallsacommunicationsfunction,thesecondthreadisblockeduntilthe communicationsfunctionreturnsinthefirstthread.Thesecondinterestingnoteaboutthesefunctionsistheiruseonaportopenforoverlappedoperation.IfSetCommMasksetsaneweventmask,anypendingWaitCommEventwill completesuccessfully,andtheeventmaskproducedbytheoperationisNULL. CaveatUsingtheEV_RXCHARflagwillnotifythethreadthatabytearrivedattheport.Thisevent,usedincombinationwiththeReadFilefunction,enablesaprogramtoreaddataonlyafteritisinthereceivebuffer,asopposedtoissuingareadthatwaitsforthedatatoarrive.Thisisparticularlyusefulwhenaportisopenfornonoverlappedoperationbecausetheprogramdoesnotneedtopollforincomingdata;theprogramisnotifiedoftheincomingdatabythe occurrenceoftheEV_RXCHARevent.Initialattemptstocodethissolutionoftenproducethefollowingpseudocode,includingoneoversightcoveredlaterinthissection: DWORDdwCommEvent; DWORDdwRead; charchRead; if(!SetCommMask(hComm,EV_RXCHAR)) //Errorsettingcommunicationseventmask. for(;;){ if(WaitCommEvent(hComm,&dwCommEvent,NULL)){ if(ReadFile(hComm,&chRead,1,&dwRead,NULL)) //Abytehasbeenread;processit. else //AnerroroccurredintheReadFilecall. break; } else //ErrorinWaitCommEvent. break; } TheabovecodewaitsforanEV_RXCHAReventtooccur.Whenthishappens,thecodecallsReadFiletoreadtheonebytereceived.Theloopstartsagain,andthecodewaitsforanotherEV_RXCHARevent.Thiscodeworksfinewhen oneortwobytesarriveinquicksuccession.ThebytereceptioncausestheEV_RXCHAReventtooccur.Thecodereadsthebyte.IfnootherbytearrivesbeforethecodecallsWaitCommEventagain,thenallisfine;thenextbytetoarrivewill causetheWaitCommEventfunctiontoindicatetheoccurrenceoftheEV_RXCHARevent.IfanothersinglebytearrivesbeforethecodehasachancetoreachtheWaitCommEventfunction,thenallisfine,too.Thefirstbyteisread asbefore;thearrivalofthesecondbytecausestheEV_RXCHARflagtobesetinternally.WhenthecodereturnstotheWaitCommEventfunction,itindicatestheoccurrenceoftheEV_RXCHAReventandthesecondbyteisreadfromtheportintheReadFile call. Theproblemwiththeabovecodeoccurswhenthreeormorebytesarriveinquicksuccession.ThefirstbytecausestheEV_RXCHAReventtooccur.ThesecondbytecausestheEV_RXCHARflagtobesetinternally.ThenexttimethecodecallsWaitCommEvent, itindicatestheEV_RXCHARevent.Now,athirdbytearrivesatthecommunicationsport.ThisthirdbytecausesthesystemtoattempttosettheEV_RXCHARflaginternally.Becausethishasalreadyoccurredwhenthesecondbytearrived,thearrivalofthethird bytegoesunnoticed.Thecodeeventuallywillreadthefirstbytewithoutaproblem.Afterthis,thecodewillcallWaitCommEvent,anditindicatestheoccurrenceoftheEV_RXCHARevent(fromthearrivalofthesecondbyte).Thesecondbyte isread,andthecodereturnstotheWaitCommEventfunction.Thethirdbytewaitsinthesystem’sinternalreceivebuffer.Thecodeandthesystemarenowoutofsync.Whenafourthbytefinallyarrives,theEV_RXCHAReventoccurs,andthe codereadsasinglebyte.Itreadsthethirdbyte.Thiswillcontinueindefinitely. Thesolutiontothisproblemseemsaseasyasincreasingthenumberofbytesrequestedinthereadoperation.Insteadofrequestingasinglebyte,thecodecouldrequesttwo,ten,orsomeothernumberofbytes.Theproblemwiththisidea isthatitstillfailswhentwoormoreextrabytesabovethesizeofthereadrequestarriveattheportinquicksuccession.So,iftwobytesareread,thenfourbytesarrivinginquicksuccessionwouldcausetheproblem.Tenbytesrequestedwouldstill failiftwelvebytesarrivedinquicksuccession. Therealsolutiontothisproblemistoreadfromtheportuntilnobytesareremaining.Thefollowingpseudocodesolvestheproblembyreadinginaloopuntilzerocharactersareread.AnotherpossiblemethodwouldbetocallClearCommError todeterminethenumberofbytesinthebufferandreadthemallinonereadoperation.Thismethodrequiresmoresophisticatedbuffermanagement,butitreducesthenumberofreadswhenalotofdataarrivesatonce. DWORDdwCommEvent; DWORDdwRead; charchRead; if(!SetCommMask(hComm,EV_RXCHAR)) //Errorsettingcommunicationseventmask for(;;){ if(WaitCommEvent(hComm,&dwCommEvent,NULL)){ do{ if(ReadFile(hComm,&chRead,1,&dwRead,NULL)) //Abytehasbeenread;processit. else //AnerroroccurredintheReadFilecall. break; }while(dwRead); } else //ErrorinWaitCommEvent break; } Theabovecodedoesnotworkcorrectlywithoutsettingthepropertime-outs.Communicationstime-outs,discussedlater,affectthebehavioroftheReadFileoperationinordertocauseittoreturnwithoutwaitingforbytes toarrive.Discussionofthistopicoccurslaterinthe“CommunicationsTime-outs?sectionofthisarticle. TheabovecaveatregardingEV_RXCHARalsoappliestoEV_RXFLAG.Ifflagcharactersarriveinquicksuccession,EV_RXFLAGeventsmaynotoccurforallofthem.Onceagain,thebestsolutionistoreadallbytesuntilnoneremain. Theabovecaveatalsoappliestoothereventsnotrelatedtocharacterreception.Ifothereventsoccurinquicksuccessionsomeofthenotificationswillbelost.Forinstance,iftheCTSlinevoltagestartshigh,thengoeslow,high,and lowagain,anEV_CTSeventoccurs.ThereisnoguaranteeofhowmanyEV_CTSeventswillactuallybedetectedwithWaitCommEventifthechangesintheCTSlinehappenquickly.Forthisreason,WaitCommEventcannotbeusedto keeptrackofthestateoftheline.Linestatusiscoveredinthe“ModemStatus?sectionlaterinthisarticle. ErrorHandlingandCommunicationsStatusOneofthecommunicationseventflagsspecifiedinthecalltoSetCommMaskispossiblyEV_ERR.TheoccurrenceoftheEV_ERReventindicatesthatanerrorconditionexistsinthecommunicationsport.OthererrorscanoccurintheportthatdonotcausetheEV_ERReventtooccur.Ineithercase,errorsassociated withthecommunicationsportcauseallI/Ooperationstobesuspendeduntilremovaloftheerrorcondition.ClearCommErroristhefunctiontocalltodetecterrorsandcleartheerrorcondition. ClearCommErroralsoprovidescommunicationsstatusindicatingwhytransmissionhasstopped;italsoindicatesthenumberofbyteswaitinginthetransmitandreceivebuffers.Thereasonwhytransmissionmaystopisbecause oferrorsortoflowcontrol.Thediscussionofflowcontroloccurslaterinthisarticle. HereissomecodethatdemonstrateshowtocallClearCommError: COMSTATcomStat; DWORDdwErrors; BOOLfOOP,fOVERRUN,fPTO,fRXOVER,fRXPARITY,fTXFULL; BOOLfBREAK,fDNS,fFRAME,fIOE,fMODE; //Getandclearcurrenterrorsontheport. if(!ClearCommError(hComm,&dwErrors,&comStat)) //ReporterrorinClearCommError. return; //Geterrorflags. fDNS=dwErrors&CE_DNS; fIOE=dwErrors&CE_IOE; fOOP=dwErrors&CE_OOP; fPTO=dwErrors&CE_PTO; fMODE=dwErrors&CE_MODE; fBREAK=dwErrors&CE_BREAK; fFRAME=dwErrors&CE_FRAME; fRXOVER=dwErrors&CE_RXOVER; fTXFULL=dwErrors&CE_TXFULL; fOVERRUN=dwErrors&CE_OVERRUN; fRXPARITY=dwErrors&CE_RXPARITY; //COMSTATstructurecontainsinformationregarding //communicationsstatus. if(comStat.fCtsHold) //TxwaitingforCTSsignal if(comStat.fDsrHold) //TxwaitingforDSRsignal if(comStat.fRlsdHold) //TxwaitingforRLSDsignal if(comStat.fXoffHold) //Txwaiting,XOFFcharrec'd if(comStat.fXoffSent) //Txwaiting,XOFFcharsent if(comStat.fEof) //EOFcharacterreceived if(comStat.fTxim) //CharacterwaitingforTx;charqueuedwithTransmitCommChar if(comStat.cbInQue) //comStat.cbInQuebyteshavebeenreceived,butnotread if(comStat.cbOutQue) //comStat.cbOutQuebytesareawaitingtransfer ModemStatus(a.k.a.LineStatus)ThecalltoSetCommMaskmayincludetheflagsEV_CTS,EV_DSR,EV_RING,andEV_RLSD.Theseflagsindicatechangesinthevoltageonthelinesoftheserialport.Thereisnoindicationoftheactualstatusoftheselines,justthatachangeoccurred.TheGetCommModemStatusfunctionretrievestheactualstateofthesestatuslinesbyreturningabitmaskindicatinga0forlowornovoltageand1forhighvoltageforeachofthelines. PleasenotethatthetermRLSD(ReceiveLineSignalDetect)iscommonlyreferredtoastheCD(CarrierDetect)line. NoteTheEV_RINGflagdoesnotworkinWindows95asmentionedearlier.TheGetCommModemStatusfunction,however,doesdetectthestateoftheRINGline. Changesintheselinesmayalsocauseaflow-controlevent.The ClearCommErrorfunctionreportswhethertransmissionissuspendedbecauseofflowcontrol.Ifnecessary,athreadmaycallClearCommErrortodetectwhethertheeventisthecauseofaflow-controlaction.Flowcontroliscovered inthe“FlowControl?sectionlaterinthisarticle. HereissomecodethatdemonstrateshowtocallGetCommModemStatus: DWORDdwModemStatus; BOOLfCTS,fDSR,fRING,fRLSD; if(!GetCommModemStatus(hComm,&dwModemStatus)) //ErrorinGetCommModemStatus; return; fCTS=MS_CTS_ON&dwModemStatus; fDSR=MS_DSR_ON&dwModemStatus; fRING=MS_RING_ON&dwModemStatus; fRLSD=MS_RLSD_ON&dwModemStatus; //Dosomethingwiththeflags. ExtendedFunctionsThedriverwillautomaticallychangethestateofcontrollinesasnecessary.Generallyspeaking,changingstatuslinesisunderthecontrolofadriver.IfadeviceusescommunicationsportcontrollinesinamannerdifferentfromRS-232standards,thestandardserialcommunicationsdriverwillnotworktocontrolthedevice.Ifthestandardserialcommunicationsdriverwillnotcontrolthedevice,acustomdevicedriverisnecessary. Thereareoccasionswhenstandardcontrollinesareunderthecontroloftheapplicationinsteadoftheserialcommunicationsdriver.Forinstance,anapplicationmaywishtoimplementitsownflowcontrol.Theapplicationwould beresponsibleforchangingthestatusoftheRTSandDTRlines.EscapeCommFunctiondirectsacommunicationsdrivertoperformsuchextendedoperations.EscapeCommFunctioncanmakethedriverperformsomeotherfunction,such assettingorclearingaBREAKcondition.Formoreinformationonthisfunction,consultthePlatformSDKdocumentation,theMicrosoftWin32SDKKnowledgeBase,ortheMicrosoftDeveloperNetwork(MSDN)Library. SerialSettingsDCBSettingsThemostcrucialaspectofprogrammingserialcommunicationsapplicationsisthesettingsintheDevice-ControlBlock(DCB)structure.ThemostcommonerrorsinserialcommunicationsprogrammingoccurininitializingtheDCBstructureimproperly.Whentheserialcommunicationsfunctionsdonotbehaveasexpected,acloseexaminationoftheDCBstructureusuallyrevealstheproblem. TherearethreewaystoinitializeaDCBstructure.ThefirstmethodistousethefunctionGetCommState.ThisfunctionreturnsthecurrentDCBinuseforthecommunicationsport.ThefollowingcodeshowshowtousetheGetCommState function: DCBdcb={0}; if(!GetCommState(hComm,&dcb)) //ErrorgettingcurrentDCBsettings else //DCBisreadyforuse. ThesecondmethodtoinitializeaDCBistouseafunctioncalled BuildCommDCB.Thisfunctionfillsinthebaud,paritytype,numberofstopbits,andnumberofdatabitsmembersoftheDCB.Thefunctionalsosetstheflow-controlmemberstodefaultvalues.Consultthedocumentationofthe BuildCommDCBfunctionfordetailsonwhichdefaultvaluesitusesforflow-controlmembers.OthermembersoftheDCBareunaffectedbythisfunction.Itistheprogram'sdutytomakesuretheothermembersoftheDCBdonotcauseerrors.The simplestthingtodointhisregardistoinitializetheDCBstructurewithzerosandthensetthesizemembertothesize,inbytes,ofthestructure.IfthezeroinitializationoftheDCBstructuredoesnotoccur,thentheremaybenonzerovaluesinthe reservedmembers;thisproducesanerrorwhentryingtousetheDCBlater.Thefollowingfunctionshowshowtoproperlyusethismethod: DCBdcb; FillMemory(&dcb,sizeof(dcb),0); dcb.DCBlength=sizeof(dcb); if(!BuildCommDCB("9600,n,8,1",&dcb)){ //Couldn'tbuildtheDCB.Usuallyaproblem //withthecommunicationsspecificationstring. returnFALSE; } else //DCBisreadyforuse. ThethirdmethodtoinitializeaDCBstructureistodoitmanually.TheprogramallocatestheDCBstructureandsetseachmemberwithanyvaluedesired.ThismethoddoesnotdealwellwithchangestotheDCBinfutureimplementationsof Win32andisnotrecommended. AnapplicationusuallyneedstosetsomeoftheDCBmembersdifferentlythanthedefaultsormayneedtomodifysettingsinthemiddleofexecution.OnceproperinitializationoftheDCBoccurs,modificationofindividualmembersispossible. ThechangestotheDCBstructuredonothaveanyeffectonthebehavioroftheportuntilexecutionoftheSetCommStatefunction.HereisasectionofcodethatretrievesthecurrentDCB,changesthebaud,andthenattemptstosettheconfiguration: DCBdcb; FillMemory(&dcb,sizeof(dcb),0); if(!GetCommState(hComm,&dcb))//getcurrentDCB //ErrorinGetCommState returnFALSE; //UpdateDCBrate. dcb.BaudRate=CBR_9600; //Setnewstate. if(!SetCommState(hComm,&dcb)) //ErrorinSetCommState.Possiblyaproblemwiththecommunications //porthandleoraproblemwiththeDCBstructureitself. HereisanexplanationofeachofthemembersoftheDCBandhowtheyaffectotherpartsoftheserialcommunicationsfunctions. NoteMostofthisinformationisfromthePlatformSDKdocumentation.Becausedocumentationistheofficialwordinwhatthemembersactuallyareandwhattheymean,thistablemaynotbecompletelyaccurateifchanges occurintheoperatingsystem. Table2.TheDCBStructureMembers
FlowControlFlowcontrolinserialcommunicationsprovidesamechanismforsuspendingcommunicationswhileoneofthedevicesisbusyorforsomereasoncannotdoanycommunication.Therearetraditionallytwotypesofflowcontrol:hardwareandsoftware.Acommonproblemwithserialcommunicationsiswriteoperationsthatactuallydonotwritethedatatothedevice.Often,theproblemliesinflowcontrolbeingusedwhentheprogramdidnotspecifyit.AcloseexaminationoftheDCBstructure revealsthatoneormoreofthefollowingmembersmaybeTRUE:fOutxCtsFlow,fOutxDsrFlow,orfOutX.AnothermechanismtorevealthatflowcontrolisenabledistocallClearCommErrorandexaminetheCOMSTATstructure.Itwill revealwhentransmissionissuspendedbecauseofflowcontrol. Beforediscussingthetypesofflowcontrol,agoodunderstandingofsometermsisinorder.Serialcommunicationstakesplacebetweentwodevices.Traditionally,thereisaPCandamodemorprinter.ThePCislabeledtheDataTerminalEquipment (DTE).TheDTEissometimescalledthehost.Themodem,printer,orotherperipheralequipmentisidentifiedastheDataCommunicationsEquipment(DCE).TheDCEissometimesreferredtoasthedevice. HardwareflowcontrolHardwareflowcontrolusesvoltagesignalsoncontrollinesoftheserialcabletocontrolwhethersendingorreceivingisenabled.TheDTEandtheDCEmustagreeonthetypesofflowcontrolusedforacommunicationssession.SettingtheDCBstructuretoenableflowcontroljustconfigurestheDTE.TheDCEalsoneedsconfigurationtomakecertaintheDTEandDCEusethesametypeofflowcontrol.ThereisnomechanismprovidedbyWin32tosettheflowcontroloftheDCE.DIPswitchesonthe device,orcommandssenttoittypicallyestablishitsconfiguration.Thefollowingtabledescribesthecontrollines,thedirectionoftheflowcontrol,andtheline'seffectontheDTEandDCE. Table3.HardwareFlow-controlLines
inputbuffersizemaycausetheerrortooccurlessfrequently,butitdoesnotcompletelysolvetheproblem.Inputflowcontrolisnecessarytocompletelyalleviatethisproblem.Whenthedriverdetectsthattheinputbufferisnearlyfull,itwilllower theinputflow-controllines.ThisshouldcausetheDCEtostoptransmitting,whichgivestheDTEenoughtimetoreadthedatafromtheinputbuffer.Whentheinputbufferhasmoreroomavailable,thevoltageonflow-controllinesissethigh,andtheDCE resumessendingdata. AsimilarerrorisCE_OVERRUN.Thiserroroccurswhennewdataarrivesbeforethecommunicationshardwareandserialcommunicationsdrivercompletelyreceivesolddata.Thiscanoccurwhenthetransmissionspeedistoohighforthetypeof communicationshardwareorCPU.Thiscanalsooccurwhentheoperatingsystemisnotfreetoservicethecommunicationshardware.Theonlywaytoalleviatethisproblemistoapplysomecombinationofdecreasingthetransmissionspeed,replacingthecommunications hardware,andincreasingtheCPUspeed.Sometimesthird-partyhardwaredriversthatarenotveryefficientwithCPUresourcescausethiserror.FlowcontrolcannotcompletelysolvetheproblemsthatcausetheCE_OVERRUNerror,althoughitmayhelptoreduce thefrequencyoftheerror. SoftwareflowcontrolSoftwareflowcontrolusesdatainthecommunicationsstreamtocontrolthetransmissionandreceptionofdata.Becausesoftwareflowcontrolusestwospecialcharacters,XOFFandXON,binarytransferscannotusesoftwareflowcontrol;theXONorXOFFcharactermayappearinthebinarydataandwouldinterferewithdatatransfer.Softwareflowcontrolbefitstext-basedcommunicationsordatabeingtransferredthatdoesnotcontaintheXONandXOFFcharacters. Inordertoenablesoftwareflowcontrol,thefOutXand fInXmembersoftheDCBmustbesettoTRUE.ThefOutXmembercontrolsoutputflowcontrol.ThefInXmembercontrolsinputflowcontrol. OnethingtonoteisthattheDCBallowstheprogramtodynamicallyassignthevaluesthesystemrecognizesasflow-controlcharacters.TheXoffCharmemberoftheDCBdictatestheXOFFcharacterforbothinputandoutputflow control.TheXonCharmemberoftheDCBsimilarlydictatestheXONcharacter. Forinputflowcontrol,theXoffLimmemberoftheDCBspecifiestheminimumamountoffreespaceallowedintheinputbufferbeforetheXOFFcharacterissent.Iftheamountoffreespaceintheinputbufferdropsbelowthis amount,thentheXOFFcharacterissent.Forinputflowcontrol,theXonLimmemberoftheDCBspecifiestheminimumnumberofbytesallowedintheinputbufferbeforetheXONcharacterissent.Iftheamountofdataintheinputbufferdrops belowthisvalue,thentheXONcharacterissent. Table4liststhebehavioroftheDTEwhenusingXOFF/XONflowcontrol. Table4.Softwareflow-controlbehavior
fTXContinueOnXoffmemberoftheDCBtakeseffect.ThefTXContinueOnXoffmembercontrolswhethertransmissionissuspendedaftertheXOFFcharacterisautomaticallysentbythesystem.IffTXContinueOnXoffis TRUE,thentransmissioncontinuesaftertheXOFFissentwhenthereceivebufferisfull.IffTXContinueOnXoffisFALSE,thentransmissionissuspendeduntilthesystemautomaticallysendstheXONcharacter.DCEdevicesusingsoftwareflow controlwillsuspendtheirsendingaftertheXOFFcharacterisreceived.SomeequipmentwillresumesendingwhentheXONcharacterissentbytheDTE.Ontheotherhand,someDCEdeviceswillresumesendingafteranycharacterarrives.The fTXContinueOnXoffmembershouldbesettoFALSEwhencommunicatingwithaDCEdevicethatresumessendingafteranycharacterarrives.IftheDTEcontinuedtransmissionafteritautomaticallysenttheXOFF,theresumptionoftransmissionwould causetheDCEtocontinuesending,defeatingtheXOFF. ThereisnomechanismavailableintheWin32APItocausetheDTEtobehavethesamewayasthesedevices.TheDCBstructurecontainsnomembersforspecifyingsuspendedtransmissiontoresumewhenanycharacterisreceived.TheXON characteristheonlycharacterthatcausestransmissiontoresume. OneotherinterestingnoteaboutsoftwareflowcontrolisthatreceptionofXONandXOFFcharacterscausespendingreadoperationstocompletewithzerobytesread.TheXONandXOFFcharacterscannotbereadbytheapplication,sincethey arenotplacedintheinputbuffer. Alotofprogramsonthemarket,includingtheTerminalprogramthatcomeswithWindows,givetheuserthreechoicesforflowcontrol:Hardware,Software,orNone.TheWindowsoperatingsystemitselfdoesnotlimitanapplicationinthis way.ThesettingsoftheDCBallowforSoftwareandHardwareflowcontrolsimultaneously.Infact,itispossibletoseparatelyconfigureeachmemberoftheDCBthataffectsflowcontrol,whichallowsforseveraldifferentflow-controlconfigurations. Thelimitsplacedonflow-controlchoicesarethereforease-of-usereasonstoreduceconfusionforendusers.Thelimitsplacedonflow-controlchoicesmayalsobebecausedevicesusedforcommunicationsmaynotsupportalltypesofflowcontrol. CommunicationsTime-outsAnothermajortopicaffectingthebehaviorofreadandwriteoperationsistime-outs.Time-outsaffectreadandwriteoperationsinthefollowingway.Ifanoperationtakeslongerthanthecomputedtime-outperiod,theoperationiscompleted.ThereisnoerrorcodethatisreturnedbyReadFile,WriteFile,GetOverlappedResult,orWaitForSingleObject.Allindicatorsusedtomonitortheoperationindicatethatitcompletedsuccessfully. Theonlywaytotellthattheoperationtimedoutisthatthenumberofbytesactuallytransferredarefewerthanthenumberofbytesrequested.So,ifReadFilereturnsTRUE,butfewerbyteswerereadthanwererequested,theoperationtimed out.Ifanoverlappedwriteoperationtimesout,theoverlappedeventhandleissignaledandWaitForSingleObjectreturnsWAIT_OBJECT_O.GetOverlappedResultreturnsTRUE,butdwBytesTransferredcontainsthenumberofbytesthat weretransferredbeforethetime-out.Thefollowingcodedemonstrateshowtohandlethisinanoverlappedwriteoperation: BOOLWriteABuffer(char*lpBuf,DWORDdwToWrite) { OVERLAPPEDosWrite={0}; DWORDdwWritten; DWORDdwRes; BOOLfRes; //Createthiswriteoperation'sOVERLAPPEDstructurehEvent. osWrite.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); if(osWrite.hEvent==NULL) //Errorcreatingoverlappedeventhandle. returnFALSE; //Issuewrite if(!WriteFile(hComm,lpBuf,dwToWrite,&dwWritten,&osWrite)){ if(GetLastError()!=ERROR_IO_PENDING){ //WriteFilefailed,butitisn'tdelayed.Reporterror. fRes=FALSE; } else //Writeispending. dwRes=WaitForSingleObject(osWrite.hEvent,INFINITE); switch(dwRes) { //Overlappedeventhasbeensignaled. caseWAIT_OBJECT_0: if(!GetOverlappedResult(hComm,&osWrite,&dwWritten,FALSE)) fRes=FALSE; else{ if(dwWritten!=dwToWrite){ //Thewriteoperationtimedout.Inowneedto //decideifIwanttoabortorretry.IfIretry, //Ineedtosendonlythebytesthatweren'tsent. //IfIwanttoabort,IwouldjustsetfResto //FALSEandreturn. fRes=FALSE; } else //Writeoperationcompletedsuccessfully. fRes=TRUE; } break; default: //AnerrorhasoccurredinWaitForSingleObject.Thisusually //indicatesaproblemwiththeoverlappedeventhandle. fRes=FALSE; break; } } } else{ //WriteFilecompletedimmediately. if(dwWritten!=dwToWrite){ //Thewriteoperationtimedout.Inowneedto //decideifIwanttoabortorretry.IfIretry, //Ineedtosendonlythebytesthatweren'tsent. //IfIwanttoabort,thenIwouldjustsetfResto //FALSEandreturn. fRes=FALSE; } else fRes=TRUE; } CloseHandle(osWrite.hEvent); returnfRes; } TheSetCommTimeoutsfunctionspecifiesthecommunicationstime-outsforaport.Toretrievethecurrenttime-outsforaport,aprogramcallstheGetCommTimeoutsfunction.Anapplicationsshouldretrievethe communicationstime-outsbeforemodifyingthem.Thisallowstheapplicationtosettime-outsbacktotheiroriginalsettingswhenitfinisheswiththeport.Followingisanexampleofsettingnewtime-outsusingSetCommTimeouts: COMMTIMEOUTStimeouts; timeouts.ReadIntervalTimeout=20; timeouts.ReadTotalTimeoutMultiplier=10; timeouts.ReadTotalTimeoutConstant=100; timeouts.WriteTotalTimeoutMultiplier=10; timeouts.WriteTotalTimeoutConstant=100; if(!SetCommTimeouts(hComm,&timeouts)) //Errorsettingtime-outs. NoteOnceagain,communicationstime-outsarenotthesameastime-outvaluessuppliedinsynchronizationfunctions.WaitForSingleObject,forinstance,usesatime-outvaluetowaitforanobjecttobecome signaled;thisisnotthesameasacommunicationstime-out. SettingthemembersoftheCOMMTIMEOUTSstructuretoallzeroscausesnotime-outstooccur.Nonoverlappedoperationswillblockuntilalltherequestedbytesaretransferred.TheReadFilefunctionisblocked untilalltherequestedcharactersarriveattheport.TheWriteFilefunctionisblockeduntilallrequestedcharactersaresentout.Ontheotherhand,anoverlappedoperationwillnotfinishuntilallthecharactersaretransferredorthe operationisaborted.Thefollowingconditionsoccuruntiltheoperationiscompleted: [align=justify]WaitForSingleObjectalwaysreturnsWAIT_TIMEOUTifasynchronizationtime-outissupplied.WaitForSingleObjectwillblockforeverifanINFINITEsynchronizationtime-outisused.[/align] GetOverlappedResultalwaysreturnsFALSEand GetLastErrorreturnsERROR_IO_INCOMPLETEifcalleddirectlyafterthecalltoGetOverlappedResult. SettingthemembersoftheCOMMTIMEOUTSstructureinthefollowingmannercausesreadoperationstocompleteimmediatelywithoutwaitingforanynewdatatoarrive: COMMTIMEOUTStimeouts; timeouts.ReadIntervalTimeout=MAXDWORD; timeouts.ReadTotalTimeoutMultiplier=0; timeouts.ReadTotalTimeoutConstant=0; timeouts.WriteTotalTimeoutMultiplier=0; timeouts.WriteTotalTimeoutConstant=0; if(!SetCommTimeouts(hComm,&timeouts)) //Errorsettingtime-outs. Thesesettingsarenecessarywhenusedwithanevent-basedreaddescribedinthe“Caveat?sectionearlier.InorderforReadFiletoreturn0bytesread,theReadIntervalTimeoutmemberoftheCOMMTIMEOUTS structureissettoMAXDWORD,andtheReadTimeoutMultiplierandReadTimeoutConstantarebothsettozero. Anapplicationmustalwaysspecificallysetcommunicationstime-outswhenitusesacommunicationsport.Thebehaviorofreadandwriteoperationsisaffectedbycommunicationstime-outs.Whenaportisinitiallyopen,itusesdefault time-outssuppliedbythedriverortime-outsleftoverfromapreviouscommunicationsapplication.Ifanapplicationassumesthattime-outsaresetacertainway,whilethetime-outsareactuallydifferent,thenreadandwriteoperationsmaynevercomplete ormaycompletetoooften. ConclusionThisarticleservesasadiscussionofsomeofthecommonpitfallsandquestionsthatarisewhendevelopingaserialcommunicationsapplication.TheMultithreadedTTYsamplethatcomeswiththisarticleisdesignedusingmanyofthetechniquesdiscussedhere.Downloaditandtryitout.LearninghowitworkswillprovideathoroughunderstandingoftheWin32serialcommunicationsfunctions. BibliographyBrain,Marshall.Win32SystemServices:TheHeartofWindowsNT.EnglewoodCliffs,NJ:PrenticeHall,1994.Campbell,Joe.CProgrammer’sGuidetoSerialCommunications.2ded.Indianapolis,IN:HowardW.Sams&Company,1994. Mirho,Charles,andAndyTerrice.“CreateCommunicationsProgramsforWindows95withtheWin32CommAPI.?MicrosoftSystemsJournal12(December1994).(MSDNLibrary,BooksandPeriodicals) |
相关文章推荐
- Win32系统中关于COM串口操作的API
- SDN 网络系统之 Mininet 与 API 详解
- 调用win32 API,实现全局系统热键小结
- 系统理解Win32 API和MFC(上)
- 关于(WTL),怎么在OnCreate中调用DwmExtendFrameIntoClientArea()这个API(vista系统的毛玻璃效果)
- 关于Linux系统网卡流量Tx和Rx的意思详解
- Win32 API 函数大全使用详解十 鼠标输入函数
- SDN 网络系统之 Mininet 与 API 详解
- 关于财易酒店管理系统折扣问题详解
- 关于Microsoft Unified Communications Managed API 3.0开发的相关技术问题(UserEndpoint 、ApplicationEndpoint的区别是什么?)
- 系统理解Win32 API和MFC
- 系统理解Win32 API和MFC(下)
- Win32 API 实现系统托盘程序
- Microsoft Win32 to Microsoft .NET Framework API Map
- 探索Win32系统之窗口类(Window Classes in Win32)(异域の蜗牛注:关于win32编程中的窗口类的探讨)
- windows程序设计 win32 API 获取系统内存信息 GlobalMemoryStatus 和 GlobalMemoryStatusEx
- 关于Win32 API的RedrawWindow函数和InvalidateRect函数
- 关于linux系统中命令的使用详解
- Unity 关于安卓和各平台读写本地json文件,WWW读取本地文件,Unity各路径API(persistentDataPath,StreamingAssets文件夹),目前较完整的详解
- 系统理解Win32 API和MFC(上)