CnRS232.pas 66 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001
  1. {******************************************************************************}
  2. { CnPack For Delphi/C++Builder }
  3. { 中国人自己的开放源码第三方开发包 }
  4. { (C)Copyright 2001-2018 CnPack 开发组 }
  5. { ------------------------------------ }
  6. { }
  7. { 本开发包是开源的自由软件,您可以遵照 CnPack 的发布协议来修 }
  8. { 改和重新发布这一程序。 }
  9. { }
  10. { 发布这一开发包的目的是希望它有用,但没有任何担保。甚至没有 }
  11. { 适合特定目的而隐含的担保。更详细的情况请参阅 CnPack 发布协议。 }
  12. { }
  13. { 您应该已经和开发包一起收到一份 CnPack 发布协议的副本。如果 }
  14. { 还没有,可访问我们的网站: }
  15. { }
  16. { 网站地址:http://www.cnpack.org }
  17. { 电子邮件:master@cnpack.org }
  18. { }
  19. {******************************************************************************}
  20. {******************************************************************************}
  21. { }
  22. { 该串口通讯组件修改自 小猪工作室 Small-Pig Team (中国台湾)的 }
  23. { SPCOMM V2.5 串口通讯组件,以下是原单元的原始声明: }
  24. { }
  25. { 硂琌����梆硄癟じン, ㄑ Delphi 2.0 莱ノ祘Αㄏノ. 続�ノㄓ暗�穨北�の }
  26. { 虏虫肚块. �じン㊣� Win32 API ㄓ笷Θ┮惠��, 叫ǎCommunications场�� }
  27. { }
  28. { �じン把σ David Wann. ┮籹�� COMM32.PAS Version 1.0��﹍弧���� }
  29. { This Communications Component is implemented using separate Read and Write }
  30. { threads. Messages from the threads are posted to the Comm control which is }
  31. { an invisible window. To handle data from the comm port, simply }
  32. { attach a handler to 'OnReceiveData'. There is no need to free the memory }
  33. { buffer passed to this handler. If TAPI is used to open the comm port, some }
  34. { changes to this component are needed ('StartComm' currently opens the comm }
  35. { port). The 'OnRequestHangup' event is included to assist this. }
  36. { }
  37. { David Wann }
  38. { Stamina Software }
  39. { 28/02/96 }
  40. { davidwann@hunterlink.net.au }
  41. { }
  42. { }
  43. { 硂�じンЧ��禣, 舧��ī' �э┪暗ヴ�ㄤウノ硚. 埃�虫縒砪芥�じン. }
  44. { This component is totally free(copyleft), you can do anything in any }
  45. { purpose EXCEPT SELL IT ALONE. }
  46. { }
  47. { }
  48. { Author?: �睫��� Small-Pig Team in Taiwan R.O.C. }
  49. { Email : spigteam@vlsi.ice.cycu.edu.tw }
  50. { Date ? : 1997/5/9 }
  51. { }
  52. { Version 1.01 1996/9/4 }
  53. { - Add setting Parity, Databits, StopBits }
  54. { - Add setting Flowcontrol:Dtr-Dsr, Cts-Rts, Xon-Xoff }
  55. { - Add setting Timeout information for read/write }
  56. { }
  57. { Version 1.02 1996/12/24 }
  58. { - Add Sender parameter to TReceiveDataEvent }
  59. { }
  60. { Version 2.0 1997/4/15 }
  61. { - Support separatly DTR/DSR and RTS/CTS hardware flow }
  62. { control setting }
  63. { - Support separatly OutX and InX software flow control }
  64. { setting }
  65. { - Log file(for debug) may used by many comms at the same }
  66. { time }
  67. { - Add DSR sensitivity property }
  68. { - You can set error char. replacement when parity error }
  69. { - Let XonLim/XoffLim and XonChar/XoffChar setting by }
  70. { yourself }
  71. { - You may change flow-control when comm is still opened }
  72. { - Change TComm32 to TComm }
  73. { - Add OnReceiveError event handler }
  74. { - Add OnReceiveError event handler when overrun, framing }
  75. { error, parity error }
  76. { - Fix some bug }
  77. { }
  78. { Version 2.01 1997/4/19 }
  79. { - Support some property for modem }
  80. { - Add OnModemStateChange event hander when RLSD(CD) change }
  81. { state }
  82. { }
  83. { Version 2.02 1997/4/28 }
  84. { - Bug fix: When receive XOFF character, the system }
  85. { FAULT!!!! }
  86. { }
  87. { Version 2.5 1997/5/9 }
  88. { - Add OnSendDataEmpty event handler when all data in buffer }
  89. { are sent(send-buffer become empty) this handler is called. }
  90. { You may call send data here. }
  91. { - Change the ModemState parameters in OnModemStateChange }
  92. { to ModemEvent to indicate what modem event make this call }
  93. { - Add RING signal detect. When RLSD changed state or RING }
  94. { signal was detected, OnModemStateChange handler is called }
  95. { - Change XonLim and XoffLim from 100 to 500 }
  96. { - Remove TWriteThread.WriteData member }
  97. { - PostHangupCall is re-design for debuging function }
  98. { - Add a boolean property SendDataEmpty, True when send }
  99. { buffer is empty }
  100. { }
  101. {******************************************************************************}
  102. unit CnRS232;
  103. {* |<PRE>
  104. ================================================================================
  105. * 软件名称:网络通讯组件包
  106. * 单元名称:CnRS232串口通讯组件单元
  107. * 单元作者:周劲羽 (zjy@cnpack.org)
  108. * 备 注:CnRS232串口通讯组件直接由 小猪工作室 Small-Pig Team (中国台湾)
  109. * spigteam@vlsi.ice.cycu.edu.tw
  110. * 的 SPCOMM V2.5 串口通讯组件修改而来。
  111. * 而 SPCOMM 又是由 David Wann (Stamina Software)
  112. * davidwann@hunterlink.net.au
  113. * 提供的 COMM32.PAS Version 1.0 修改而来。
  114. * CnRS232.pas 单元保留了以上内容的详细说明,请查阅。
  115. * 开发平台:PWin98SE + Delphi 5.0
  116. * 兼容测试:PWin9X/2000/XP + Delphi 5/6
  117. * 本 地 化:该单元中的字符串均符合本地化处理方式
  118. * 单元标识:$Id$
  119. * 修改记录:2012.03.22 V1.2
  120. * 修正一处读入数据成功但长度为0而退出的问题,感谢大雄
  121. * 2008.11.17 V1.1
  122. * 增加 D2009 支持后修正问题,感谢大雄
  123. * 2002.04.08 V1.0
  124. * 创建单元,增加注释
  125. ================================================================================
  126. |</PRE>}
  127. interface
  128. {$I CnPack.inc}
  129. uses
  130. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  131. IniFiles, CnClasses, CnConsts, CnNetConsts;
  132. const
  133. PWM_GOTCOMMDATA = WM_USER + 1;
  134. PWM_RECEIVEERROR = WM_USER + 2;
  135. PWM_REQUESTHANGUP = WM_USER + 3;
  136. PWM_MODEMSTATECHANGE = WM_USER + 4;
  137. PWM_SENDDATAEMPTY = WM_USER + 5;
  138. PWM_COMMWRITE = WM_USER + 1;
  139. ME_CTS = 1;
  140. ME_DSR = 2;
  141. ME_RING = 4;
  142. ME_RLSD = 8;
  143. type
  144. TParity = (paNone, paOdd, paEven, paMark, paSpace);
  145. {* 串口通讯奇偶校验方式
  146. |<PRE>
  147. paNone: - 无校验
  148. paOdd: - 奇校验方式
  149. paEven: - 偶校验方式
  150. paMark: - 传号校验方式
  151. paSpace: - 空号校验方式
  152. |</PRE>}
  153. TStopBits = (_1, _1_5, _2);
  154. {* 串口通讯停止位长度
  155. |<PRE>
  156. _1: - 1位停止位
  157. _1_5: - 1.5位停止位
  158. _2: - 2位停止位
  159. |</PRE>}
  160. TByteSize = (_5, _6, _7, _8);
  161. {* 串口通讯可用数据位数
  162. |<PRE>
  163. _5: - 5位数据
  164. _6: - 6位数据
  165. _7: - 7位数据
  166. _8: - 8位数据
  167. |</PRE>}
  168. TDtrControl = (DtrEnable, DtrDisable, DtrHandshake);
  169. {* 串口通讯中使用DTR(数据终端就绪)信号进行流量控制的方式
  170. |<PRE>
  171. DtrEnable: - 允许DTR线并保持
  172. DtrDisable: - 禁止DTR线并保持
  173. DtrHandshake: - 允许DTR握手
  174. |</PRE>}
  175. TRtsControl = (RtsEnable, RtsDisable, RtsHandshake, RtsTransmissionAvailable);
  176. {* 串口通讯中使用RTS(请求发送)信号进行流量控制的方式
  177. |<PRE>
  178. RtsEnable: - 允许RTS并保持
  179. RtsDisable: - 禁止RTS并保持
  180. RtsHandshake: - 允许RTS握手
  181. RtsTransmissionAvailable: - 使用触发方式
  182. |</PRE>}
  183. ERS232Error = class(Exception);
  184. EInvalidXonXoffChar = class(Exception);
  185. //------------------------------------------------------------------------------
  186. // RS232串口通讯设置类
  187. //------------------------------------------------------------------------------
  188. { TCnRS232Config }
  189. TCnRS232Config = class(TCnPersistent)
  190. {* RS232串口通讯设置持久性类}
  191. private
  192. FXoffChar: Char;
  193. FReplacedChar: Char;
  194. FXonChar: Char;
  195. FOutx_CtsFlow: Boolean;
  196. FOutx_DsrFlow: Boolean;
  197. FParityCheck: Boolean;
  198. FIgnoreNullChar: Boolean;
  199. FInx_XonXoffFlow: Boolean;
  200. FTxContinueOnXoff: Boolean;
  201. FReplaceWhenParityError: Boolean;
  202. FOutx_XonXoffFlow: Boolean;
  203. FDsrSensitivity: Boolean;
  204. FBaudRate: DWord;
  205. FByteSize: TByteSize;
  206. FDtrControl: TDtrControl;
  207. FParity: TParity;
  208. FRtsControl: TRtsControl;
  209. FStopBits: TStopBits;
  210. FXoffLimit: WORD;
  211. FXonLimit: WORD;
  212. procedure SetBaudRate(const Value: DWord);
  213. procedure SetByteSize(const Value: TByteSize);
  214. procedure SetDsrSensitivity(const Value: Boolean);
  215. procedure SetDtrControl(const Value: TDtrControl);
  216. procedure SetIgnoreNullChar(const Value: Boolean);
  217. procedure SetInx_XonXoffFlow(const Value: Boolean);
  218. procedure SetOutx_CtsFlow(const Value: Boolean);
  219. procedure SetOutx_DsrFlow(const Value: Boolean);
  220. procedure SetOutx_XonXoffFlow(const Value: Boolean);
  221. procedure SetParityCheck(const Value: Boolean);
  222. procedure SetReplacedChar(const Value: Char);
  223. procedure SetReplaceWhenParityError(const Value: Boolean);
  224. procedure SetRtsControl(const Value: TRtsControl);
  225. procedure SetStopBits(const Value: TStopBits);
  226. procedure SetTxContinueOnXoff(const Value: Boolean);
  227. procedure SetXoffChar(const Value: Char);
  228. procedure SetXoffLimit(const Value: WORD);
  229. procedure SetXonChar(const Value: Char);
  230. procedure SetXonLimit(const Value: WORD);
  231. public
  232. constructor Create; override;
  233. {* 类构造器,创建类实例}
  234. procedure Assign(Source: TPersistent); override;
  235. {* 在两个对象之间赋值}
  236. procedure GetDCB(var DCB: TDCB);
  237. {* 从当前设置中取DCB结构}
  238. procedure SetDCB(const DCB: TDCB);
  239. {* 根据DCB结构进行设置}
  240. published
  241. property BaudRate: DWord read FBaudRate write SetBaudRate default 9600;
  242. {* 串口通讯波特率}
  243. property ParityCheck: Boolean read FParityCheck write SetParityCheck default False;
  244. {* 设置是否允许奇偶校验}
  245. property Outx_CtsFlow: Boolean read FOutx_CtsFlow write SetOutx_CtsFlow default False;
  246. {* 设置是否使用CTS(清除发送)信号进行输出流量控制}
  247. property Outx_DsrFlow: Boolean read FOutx_DsrFlow write SetOutx_DsrFlow default False;
  248. {* 设置是否使用DSR(数据设备就绪)信号进行输出流量控制}
  249. property DtrControl: TDtrControl read FDtrControl write SetDtrControl default DtrEnable;
  250. {* 使用DTR(数据终端就绪)信号进行流量控制的方式}
  251. property DsrSensitivity: Boolean read FDsrSensitivity write SetDsrSensitivity default False;
  252. {* 指定通信驱动程序对DSR信号的状态是否敏感。
  253. |<BR> 如果为真,当Modem的DSR输入线为低时,驱动程序将忽略接收到的任何字节。}
  254. property TxContinueOnXoff: Boolean read FTxContinueOnXoff write SetTxContinueOnXoff default False;
  255. {* 指定当接收缓冲区已满,已发送“Xoff字符”后发送是否停止。
  256. |<BR> 如果为真,当被填满的接收缓冲区中的字节数未达到“Xoff阈值”并且驱动
  257. 程序发送了“Xoff字符”后停止接收字节时,继续发送;
  258. |<BR> 如果为假,当被排空的缓冲区中的字节数不足“Xon阈值”个字节,
  259. 且驱动程序发送了“Xon字符”后恢复接收时,继续发送。}
  260. property Outx_XonXoffFlow: Boolean read FOutx_XonXoffFlow write SetOutx_XonXoffFlow default False;
  261. {* 指定数据发送时是否使用Xon/Xoff信息流控制
  262. |<BR> 如果为真,当接收到“Xoff字符”时暂停发送,并在接收到“Xon字符”时恢复发送。}
  263. property Inx_XonXoffFlow: Boolean read FInx_XonXoffFlow write SetInx_XonXoffFlow default False;
  264. {* 指定数据接收时是否使用Xon/Xoff信息流控制
  265. |<BR> 如果为真,当接收缓冲区快满,只剩“Xoff阈值”个字符空闲时发送“Xoff字符”;
  266. 当接收缓冲区中只有“Xon阈值”个字符时,发送“Xon字符”。}
  267. property ReplaceWhenParityError: Boolean read FReplaceWhenParityError write SetReplaceWhenParityError default False;
  268. {* 指定出现奇偶校验错时是否用指定字符ReplacedChar代替}
  269. property IgnoreNullChar: Boolean read FIgnoreNullChar write SetIgnoreNullChar default False;
  270. {* 指定是否丢弃接收到的NULL(ASCII 0)字符}
  271. property RtsControl: TRtsControl read FRtsControl write SetRtsControl default RtsEnable;
  272. {* 指定使用RTS(请求发送)信号进行流量控制的方式}
  273. property XonLimit: WORD read FXonLimit write SetXonLimit default 500;
  274. {* 指明在发送“Xon字符”之前,接收缓冲区中允许的最少字符数。}
  275. property XoffLimit: WORD read FXoffLimit write SetXoffLimit default 500;
  276. {* 指明在发送“Xoff字符”之前,接收缓冲区中允许的最多字符数。
  277. |<BR> 接收缓冲区的长度减去该值,即允许的最多字符数。}
  278. property ByteSize: TByteSize read FByteSize write SetByteSize default _8;
  279. {* 可用数据位数}
  280. property Parity: TParity read FParity write FParity default paNone;
  281. {* 奇偶校验方式}
  282. property StopBits: TStopBits read FStopBits write SetStopBits default _1;
  283. {* 停止位数}
  284. property XonChar: Char read FXonChar write SetXonChar default chr($11);
  285. {* 发送和接收的“Xon字符”的ASCII码,表示允许继续传输。
  286. |<BR> 其值不能与XoffChar相同。}
  287. property XoffChar: Char read FXoffChar write SetXoffChar default chr($13);
  288. {* 发送和接收的“Xoff字符”的ASCII码,表示允许暂停传输。
  289. |<BR> 其值不能与XonChar相同。}
  290. property ReplacedChar: Char read FReplacedChar write SetReplacedChar default chr(0);
  291. {* 指定出现奇偶校验错时用来替换的字符的ASCII码,见ReplaceWhenParityError}
  292. end;
  293. //------------------------------------------------------------------------------
  294. // RS232串口通讯设置超时类
  295. //------------------------------------------------------------------------------
  296. { TCnRS232Timeouts }
  297. TCnRS232Timeouts = class(TCnPersistent)
  298. {* RS232串口通讯超时设置持久性类}
  299. private
  300. FReadTotalTimeoutConstant: DWord;
  301. FReadIntervalTimeout: DWord;
  302. FReadTotalTimeoutMultiplier: DWord;
  303. FWriteTotalTimeoutConstant: DWord;
  304. FWriteTotalTimeoutMultiplier: DWord;
  305. procedure SetReadIntervalTimeout(const Value: DWord);
  306. procedure SetReadTotalTimeoutConstant(const Value: DWord);
  307. procedure SetReadTotalTimeoutMultiplier(const Value: DWord);
  308. procedure SetWriteTotalTimeoutConstant(const Value: DWord);
  309. procedure SetWriteTotalTimeoutMultiplier(const Value: DWord);
  310. public
  311. constructor Create; override;
  312. {* 类构造器,创建类实例}
  313. procedure Assign(Source: TPersistent); override;
  314. {* 在两个对象之间赋值}
  315. function GetCommTimeouts: TCommTimeouts;
  316. {* 从当前设置中取TCommTimeouts结构}
  317. procedure SetCommTimeouts(const Value: TCommTimeouts);
  318. {* 根据TCommTimeouts结构进行设置}
  319. published
  320. property ReadIntervalTimeout: DWord read FReadIntervalTimeout write SetReadIntervalTimeout default 10;
  321. {* 指定通信线路上两个字符到达之间的最大时间。
  322. |<BR> 在读取操作期间,从接收到第一个字符时开始计时,若任意两个字符到达之
  323. 间的时间间隔超过这个最大值,则读取操作完成,返回缓冲数据。
  324. |<BR> 如果置0,表示不使用间隔超时。}
  325. property ReadTotalTimeoutMultiplier: DWord read FReadTotalTimeoutMultiplier write SetReadTotalTimeoutMultiplier default 0;
  326. {* 用于设定读总超时时间的系数。
  327. |<BR> 读总超时时间 = (总超时系数 X 接收字符数) + 总超时常量
  328. |<BR> 常量和系数可分别为0。如果均为0,则不使用总超时设定。}
  329. property ReadTotalTimeoutConstant: DWord read FReadTotalTimeoutConstant write SetReadTotalTimeoutConstant default 0;
  330. {* 用于设定读总超时时间的常量值。
  331. |<BR> 读总超时时间 = (总超时系数 X 接收字符数) + 总超时常量
  332. |<BR> 常量和系数可分别为0。如果均为0,则不使用总超时设定。}
  333. property WriteTotalTimeoutMultiplier: DWord read FWriteTotalTimeoutMultiplier write SetWriteTotalTimeoutMultiplier default 0;
  334. {* 用于设定写总超时时间的系数。
  335. |<BR> 写总超时时间 = (总超时系数 X 接收字符数) + 总超时常量
  336. |<BR> 常量和系数可分别为0。如果均为0,则不使用总超时设定。}
  337. property WriteTotalTimeoutConstant: DWord read FWriteTotalTimeoutConstant write SetWriteTotalTimeoutConstant default 0;
  338. {* 用于设定写总超时时间的常量值。
  339. |<BR> 写总超时时间 = (总超时系数 X 接收字符数) + 总超时常量
  340. |<BR> 常量和系数可分别为0。如果均为0,则不使用总超时设定。}
  341. end;
  342. TReceiveDataEvent = procedure(Sender: TObject; Buffer: Pointer;
  343. BufferLength: WORD) of object;
  344. {* 串口通讯中接收到数据事件。
  345. |<PRE>
  346. Buffer: Pointer 指向该数据缓冲区
  347. BufferLength: WORD 数据长度
  348. |</PRE>}
  349. TModemStateChangeEvent = procedure(Sender: TObject; ModemEvent: DWord) of object;
  350. {* 串口通讯中Modem状态变更事件。
  351. |<PRE>
  352. 参数ModemEvent可取以下值:
  353. ME_CTS = 1
  354. ME_DSR = 2
  355. ME_RING = 4
  356. ME_RLSD = 8
  357. |</PRE>}
  358. TReceiveErrorEvent = procedure(Sender: TObject; EventMask: DWord) of object;
  359. {* 串口通讯错误事件。}
  360. TSendDataEmptyEvent = procedure(Sender: TObject) of object;
  361. {* 串口通讯数据缓冲区空事件,该事件表明缓冲区数据已成功发送完成。}
  362. //------------------------------------------------------------------------------
  363. // RS232串口通讯读线程
  364. //------------------------------------------------------------------------------
  365. { TReadThread }
  366. TReadThread = class(TThread)
  367. protected
  368. procedure Execute; override;
  369. public
  370. hCommFile: THandle;
  371. hCloseEvent: THandle;
  372. hComm32Window: THandle;
  373. function SetupCommEvent(lpOverlappedCommEvent: POverlapped;
  374. var lpfdwEvtMask: DWord): Boolean;
  375. function SetupReadEvent(lpOverlappedRead: POverlapped;
  376. lpszInputBuffer: LPSTR; dwSizeofBuffer: DWord;
  377. var lpnNumberOfBytesRead: DWord): Boolean;
  378. function HandleCommEvent(lpOverlappedCommEvent: POverlapped;
  379. var lpfdwEvtMask: DWord; fRetrieveEvent: Boolean): Boolean;
  380. function HandleReadEvent(lpOverlappedRead: POverlapped;
  381. lpszInputBuffer: LPSTR; dwSizeofBuffer: DWord;
  382. var lpnNumberOfBytesRead: DWord): Boolean;
  383. function HandleReadData(lpszInputBuffer: LPCSTR; dwSizeofBuffer: DWord): Boolean;
  384. function ReceiveData(lpNewString: LPSTR; dwSizeofNewString: DWord): Bool;
  385. function ReceiveError(EvtMask: DWord): Bool;
  386. function ModemStateChange(ModemEvent: DWord): Bool;
  387. procedure PostHangupCall;
  388. end;
  389. //------------------------------------------------------------------------------
  390. // RS232串口通讯写线程
  391. //------------------------------------------------------------------------------
  392. { TWriteThread }
  393. TWriteThread = class(TThread)
  394. protected
  395. procedure Execute; override;
  396. function HandleWriteData(lpOverlappedWrite: POverlapped;
  397. pDataToWrite: PAnsiChar; dwNumberOfBytesToWrite: DWord): Boolean;
  398. public
  399. hCommFile: THandle;
  400. hCloseEvent: THandle;
  401. hComm32Window: THandle;
  402. pFSendDataEmpty: ^Boolean;
  403. procedure PostHangupCall;
  404. end;
  405. //------------------------------------------------------------------------------
  406. // RS232串口通讯组件
  407. //------------------------------------------------------------------------------
  408. { TCnRS232 }
  409. TCnRS232 = class(TCnComponent)
  410. {* RS232串口通讯组件。
  411. |<PRE>
  412. * 组件采用单独的读写线程以overlapped方式进行串口通讯。
  413. * 使用时先要通过 StartComm 方法打开串口,通讯完成可使用 StopComm 关闭。
  414. * 当串口接收到数据时,会产生 OnReceiveData 事件,传递数据缓冲区指针和数据长度。
  415. * 向串口写数据使用 WriteCommData 方法完成,该方法调用后,组件会产生一个写线程
  416. 在后台发送数据,发送完毕产生 OnSendDataEmpty 事件。
  417. * 该组件可搭配串口设置对话框组件 TRS232Dialog 使用。
  418. * 注:Timeouts 超时设置中的 ReadIntervalTimeout 决定了接收数据时对数据分块的
  419. 方法,如果不能接收到预期长度的数据,请尝试调整该属性。
  420. |</PRE>}
  421. private
  422. { Private declarations }
  423. ReadThread: TReadThread;
  424. WriteThread: TWriteThread;
  425. hCommFile: THandle;
  426. hCloseEvent: THandle;
  427. FHWnd: THandle;
  428. FSendDataEmpty: Boolean;
  429. FCommName: string;
  430. FCommConfig: TCnRS232Config;
  431. FTimeouts: TCnRS232Timeouts;
  432. FOnRequestHangup: TNotifyEvent;
  433. FOnReceiveData: TReceiveDataEvent;
  434. FOnReceiveError: TReceiveErrorEvent;
  435. FOnSendDataEmpty: TSendDataEmptyEvent;
  436. FOnModemStateChange: TModemStateChangeEvent;
  437. procedure CommWndProc(var Msg: TMessage);
  438. procedure _SetCommState;
  439. procedure _SetCommTimeout;
  440. procedure SetCommConfig(const Value: TCnRS232Config);
  441. procedure SetTimeouts(const Value: TCnRS232Timeouts);
  442. function GetConnected: Boolean;
  443. protected
  444. { Protected declarations }
  445. procedure GetComponentInfo(var AName, Author, Email, Comment: string); override;
  446. procedure ConfigChanged(Sender: TObject);
  447. procedure TimeoutsChanged(Sender: TObject);
  448. procedure CloseReadThread;
  449. procedure CloseWriteThread;
  450. procedure ReceiveData(Buffer: PAnsiChar; BufferLength: WORD); virtual;
  451. procedure ReceiveError(EvtMask: DWord); virtual;
  452. procedure ModemStateChange(ModemEvent: DWord); virtual;
  453. procedure RequestHangup; virtual;
  454. procedure _SendDataEmpty; virtual;
  455. property OnModemStateChange: TModemStateChangeEvent read FOnModemStateChange write FOnModemStateChange;
  456. public
  457. { Public declarations }
  458. property Handle: THandle read hCommFile;
  459. {* 串口设备句柄}
  460. property SendDataEmpty: Boolean read FSendDataEmpty;
  461. {* 当前发送数据缓冲区是否为空,运行期只读属性}
  462. procedure Assign(Source: TPersistent); override;
  463. {* 对象赋值方式}
  464. constructor Create(AOwner: TComponent); override;
  465. destructor Destroy; override;
  466. procedure StartComm;
  467. {* 根据当前设置打开串口设备}
  468. procedure StopComm;
  469. {* 关闭已打开的串口设备}
  470. procedure ReadFromIni(Ini: TCustomIniFile; const Section: string); overload; virtual;
  471. {* 从INI配置对象中读取串口通讯设置,包含了组件的所有通讯参数,可重载
  472. |<BR> 技巧:可以使用TRegIniFile对象来将设置保存到注册表中}
  473. procedure WriteToIni(Ini: TCustomIniFile; const Section: string); overload; virtual;
  474. {* 将当前的串口通讯设置保存到INI配置对象,包含了组件的所有通讯参数,可重载
  475. |<BR> 技巧:可以使用TRegIniFile对象来从注册表中读取}
  476. procedure ReadFromIni(const FileName: string; const Section: string); overload;
  477. {* 从INI文件中读取串口通讯设置,包含了组件的所有通讯参数}
  478. procedure WriteToIni(const FileName: string; const Section: string); overload;
  479. {* 将当前的串口通讯设置保存到INI文件中,包含了组件的所有通讯参数}
  480. function WriteCommData(pDataToWrite: PAnsiChar; dwSizeofDataToWrite: WORD): Boolean;
  481. {* 向串口写数据方法
  482. |<PRE>
  483. pDataToWrite: PAnsiChar - 要发送的数据缓冲区指针
  484. dwSizeofDataToWrite: WORD - 数据块的长度
  485. |</PRE>}
  486. function GetModemState: DWord;
  487. {* 取当前Modem状态}
  488. property Connected: Boolean read GetConnected;
  489. {* 标识当前端口是否已打开 }
  490. published
  491. { Published declarations }
  492. property CommName: string read FCommName write FCommName;
  493. {* 串口端口名,为类似 COM1、COM2 这样的字符串。
  494. |<BR> 如果指定错误的设备名,打开设备时将产生错误。}
  495. property CommConfig: TCnRS232Config read FCommConfig write SetCommConfig;
  496. {* 串口通讯设置}
  497. property Timeouts: TCnRS232Timeouts read FTimeouts write SetTimeouts;
  498. {* 串口通讯超时设置}
  499. property OnReceiveData: TReceiveDataEvent read FOnReceiveData write FOnReceiveData;
  500. {* 接收到数据事件}
  501. property OnReceiveError: TReceiveErrorEvent read FOnReceiveError write FOnReceiveError;
  502. {* 接收数据错误事件}
  503. property OnRequestHangup: TNotifyEvent read FOnRequestHangup write FOnRequestHangup;
  504. {* 接收中断通讯事件}
  505. property OnSendDataEmpty: TSendDataEmptyEvent read FOnSendDataEmpty write FOnSendDataEmpty;
  506. {* 数据发送缓冲区空事件}
  507. end;
  508. implementation
  509. const
  510. INPUT_BUFFER_SIZE = 2048;
  511. { TReadThread }
  512. procedure TReadThread.Execute;
  513. var
  514. szInputBuffer: array[0..INPUT_BUFFER_SIZE - 1] of AnsiChar;
  515. nNumberOfBytesRead: DWord;
  516. HandlesToWaitFor: array[0..2] of THandle;
  517. dwHandleSignaled: DWord;
  518. fdwEvtMask: DWord;
  519. // Needed for overlapped I/O (ReadFile)
  520. overlappedRead: TOverlapped;
  521. // Needed for overlapped Comm Event handling.
  522. overlappedCommEvent: TOverlapped;
  523. label
  524. EndReadThread;
  525. begin
  526. FillChar(overlappedRead, SizeOf(overlappedRead), 0);
  527. FillChar(overlappedCommEvent, SizeOf(overlappedCommEvent), 0);
  528. // Lets put an event in the Read overlapped structure.
  529. overlappedRead.hEvent := CreateEvent(nil, True, True, nil);
  530. if overlappedRead.hEvent = 0 then
  531. begin
  532. PostHangupCall;
  533. goto EndReadThread;
  534. end;
  535. // And an event for the CommEvent overlapped structure.
  536. overlappedCommEvent.hEvent := CreateEvent(nil, True, True, nil);
  537. if overlappedCommEvent.hEvent = 0 then
  538. begin
  539. PostHangupCall;
  540. goto EndReadThread;
  541. end;
  542. // We will be waiting on these objects.
  543. HandlesToWaitFor[0] := hCloseEvent;
  544. HandlesToWaitFor[1] := overlappedCommEvent.hEvent;
  545. HandlesToWaitFor[2] := overlappedRead.hEvent;
  546. // Setup CommEvent handling.
  547. // Set the comm mask so we receive error signals.
  548. if not SetCommMask(hCommFile, EV_ERR or EV_RLSD or EV_RING) then
  549. begin
  550. PostHangupCall;
  551. goto EndReadThread;
  552. end;
  553. // Start waiting for CommEvents (Errors)
  554. if not SetupCommEvent(@overlappedCommEvent, fdwEvtMask) then
  555. goto EndReadThread;
  556. // Start waiting for Read events.
  557. if not SetupReadEvent(@overlappedRead,
  558. PAnsiChar(@szInputBuffer[0]), INPUT_BUFFER_SIZE,
  559. nNumberOfBytesRead) then
  560. goto EndReadThread;
  561. // Keep looping until we break out.
  562. while True do
  563. begin
  564. // Wait until some event occurs (data to read; error; stopping).
  565. dwHandleSignaled := WaitForMultipleObjects(3, @HandlesToWaitFor,
  566. False, INFINITE);
  567. // Which event occured?
  568. case dwHandleSignaled of
  569. WAIT_OBJECT_0: // Signal to end the thread.
  570. begin
  571. // Time to exit.
  572. goto EndReadThread;
  573. end;
  574. WAIT_OBJECT_0 + 1: // CommEvent signaled.
  575. begin
  576. // Handle the CommEvent.
  577. if not HandleCommEvent(@overlappedCommEvent, fdwEvtMask, True) then
  578. goto EndReadThread;
  579. // Start waiting for the next CommEvent.
  580. if not SetupCommEvent(@overlappedCommEvent, fdwEvtMask) then
  581. goto EndReadThread;
  582. {break;??}
  583. end;
  584. WAIT_OBJECT_0 + 2: // Read Event signaled.
  585. begin
  586. // Get the new data!
  587. if not HandleReadEvent(@overlappedRead,
  588. PAnsiChar(@szInputBuffer[0]),
  589. INPUT_BUFFER_SIZE,
  590. nNumberOfBytesRead) then
  591. goto EndReadThread;
  592. // Wait for more new data.
  593. if not SetupReadEvent(@overlappedRead,
  594. PAnsiChar(@szInputBuffer[0]), INPUT_BUFFER_SIZE,
  595. nNumberOfBytesRead) then
  596. goto EndReadThread;
  597. {break;}
  598. end;
  599. WAIT_FAILED: // Wait failed. Shouldn't happen.
  600. begin
  601. PostHangupCall;
  602. goto EndReadThread;
  603. end
  604. else // This case should never occur.
  605. begin
  606. PostHangupCall;
  607. goto EndReadThread;
  608. end
  609. end {case dwHandleSignaled}
  610. end; {while True}
  611. // Time to clean up Read Thread.
  612. EndReadThread:
  613. PurgeComm(hCommFile, PURGE_RXABORT + PURGE_RXCLEAR);
  614. CloseHandle(overlappedRead.hEvent);
  615. CloseHandle(overlappedCommEvent.hEvent)
  616. end; {TReadThread.Execute}
  617. function TReadThread.SetupReadEvent(lpOverlappedRead: POverlapped;
  618. lpszInputBuffer: LPSTR; dwSizeofBuffer: DWord;
  619. var lpnNumberOfBytesRead: DWord): Boolean;
  620. var
  621. dwLastError: DWord;
  622. label
  623. StartSetupReadEvent;
  624. begin
  625. Result := False;
  626. StartSetupReadEvent:
  627. // Make sure the CloseEvent hasn't been signaled yet.
  628. // Check is needed because this function is potentially recursive.
  629. if WAIT_TIMEOUT <> WaitForSingleObject(hCloseEvent, 0) then
  630. Exit;
  631. // Start the overlapped ReadFile.
  632. if ReadFile(hCommFile,
  633. lpszInputBuffer^, dwSizeofBuffer,
  634. lpnNumberOfBytesRead, lpOverlappedRead) then
  635. begin
  636. // This would only happen if there was data waiting to be read.
  637. // Handle the data.
  638. if lpnNumberOfBytesRead > 0 then // If got zero, do not handle and continue
  639. if not HandleReadData(lpszInputBuffer, lpnNumberOfBytesRead) then
  640. Exit;
  641. // Start waiting for more data.
  642. goto StartSetupReadEvent
  643. end;
  644. // ReadFile failed. Expected because of overlapped I/O.
  645. dwLastError := GetLastError;
  646. // LastError was ERROR_IO_PENDING, as expected.
  647. if dwLastError = ERROR_IO_PENDING then
  648. begin
  649. Result := True;
  650. Exit;
  651. end;
  652. // Its possible for this error to occur if the
  653. // service provider has closed the port. Time to end.
  654. if dwLastError = ERROR_INVALID_HANDLE then
  655. Exit;
  656. // Unexpected error come here. No idea what could cause this to happen.
  657. PostHangupCall;
  658. end; {TReadThread.SetupReadEvent}
  659. function TReadThread.HandleReadData(lpszInputBuffer: LPCSTR; dwSizeofBuffer: DWord): Boolean;
  660. var
  661. lpszPostedBytes: LPSTR;
  662. begin
  663. Result := False;
  664. // If we got data and didn't just time out empty...
  665. if dwSizeofBuffer <> 0 then
  666. begin
  667. // Do something with the bytes read.
  668. lpszPostedBytes := PAnsiChar(LocalAlloc(LPTR, dwSizeofBuffer + 1));
  669. if lpszPostedBytes = nil {NULL} then
  670. begin
  671. // Out of memory
  672. PostHangupCall;
  673. Exit;
  674. end;
  675. Move(lpszInputBuffer^, lpszPostedBytes^, dwSizeofBuffer);
  676. lpszPostedBytes[dwSizeofBuffer] := #0;
  677. Result := ReceiveData(lpszPostedBytes, dwSizeofBuffer)
  678. end;
  679. end; {TReadThread.HandleReadData}
  680. function TReadThread.HandleReadEvent(lpOverlappedRead: POverlapped;
  681. lpszInputBuffer: LPSTR; dwSizeofBuffer: DWord;
  682. var lpnNumberOfBytesRead: DWord): Boolean;
  683. var
  684. dwLastError: DWord;
  685. begin
  686. Result := False;
  687. if GetOverlappedResult(hCommFile,
  688. lpOverlappedRead^, lpnNumberOfBytesRead, False) then
  689. begin
  690. Result := HandleReadData(lpszInputBuffer, lpnNumberOfBytesRead);
  691. Exit
  692. end;
  693. // Error in GetOverlappedResult; handle it.
  694. dwLastError := GetLastError;
  695. // Its possible for this error to occur if the
  696. // service provider has closed the port. Time to end.
  697. if dwLastError = ERROR_INVALID_HANDLE then
  698. Exit;
  699. // Unexpected error come here. No idea what could cause this to happen.
  700. PostHangupCall;
  701. end; {TReadThread.HandleReadEvent}
  702. function TReadThread.SetupCommEvent(lpOverlappedCommEvent: POverlapped;
  703. var lpfdwEvtMask: DWord): Boolean;
  704. var
  705. dwLastError: DWord;
  706. label
  707. StartSetupCommEvent;
  708. begin
  709. Result := False;
  710. StartSetupCommEvent:
  711. // Make sure the CloseEvent hasn't been signaled yet.
  712. // Check is needed because this function is potentially recursive.
  713. if WAIT_TIMEOUT <> WaitForSingleObject(hCloseEvent, 0) then
  714. Exit;
  715. // Start waiting for Comm Errors.
  716. if WaitCommEvent(hCommFile, lpfdwEvtMask, lpOverlappedCommEvent) then
  717. begin
  718. // This could happen if there was an error waiting on the
  719. // comm port. Lets try and handle it.
  720. if not HandleCommEvent(nil, lpfdwEvtMask, False) then
  721. begin
  722. {??? GetOverlappedResult does not handle "NIL" as defined by Borland}
  723. Exit
  724. end;
  725. // What could cause infinite recursion at this point?
  726. goto StartSetupCommEvent;
  727. end;
  728. // We expect ERROR_IO_PENDING returned from WaitCommEvent
  729. // because we are waiting with an overlapped structure.
  730. dwLastError := GetLastError;
  731. // LastError was ERROR_IO_PENDING, as expected.
  732. if dwLastError = ERROR_IO_PENDING then
  733. begin
  734. Result := True;
  735. Exit;
  736. end;
  737. // Its possible for this error to occur if the
  738. // service provider has closed the port. Time to end.
  739. if dwLastError = ERROR_INVALID_HANDLE then
  740. Exit;
  741. // Unexpected error. No idea what could cause this to happen.
  742. PostHangupCall;
  743. end; {TReadThread.SetupCommEvent}
  744. function TReadThread.HandleCommEvent(lpOverlappedCommEvent: POverlapped;
  745. var lpfdwEvtMask: DWord; fRetrieveEvent: Boolean): Boolean;
  746. var
  747. dwDummy: DWord;
  748. dwErrors: DWord;
  749. dwLastError: DWord;
  750. dwModemEvent: DWord;
  751. begin
  752. Result := False;
  753. // If this fails, it could be because the file was closed (and I/O is
  754. // finished) or because the overlapped I/O is still in progress. In
  755. // either case (or any others) its a bug and return FALSE.
  756. if fRetrieveEvent then
  757. begin
  758. if not GetOverlappedResult(hCommFile,
  759. lpOverlappedCommEvent^, dwDummy, False) then
  760. begin
  761. dwLastError := GetLastError;
  762. // Its possible for this error to occur if the
  763. // service provider has closed the port. Time to end.
  764. if dwLastError = ERROR_INVALID_HANDLE then
  765. Exit;
  766. PostHangupCall;
  767. Exit;
  768. end
  769. end;
  770. // Was the event an error?
  771. if (lpfdwEvtMask and EV_ERR) <> 0 then
  772. begin
  773. // Which error was it?
  774. if not ClearCommError(hCommFile, dwErrors, nil) then
  775. begin
  776. dwLastError := GetLastError;
  777. // Its possible for this error to occur if the
  778. // service provider has closed the port. Time to end.
  779. if dwLastError = ERROR_INVALID_HANDLE then
  780. Exit;
  781. PostHangupCall;
  782. Exit;
  783. end;
  784. // Its possible that multiple errors occured and were handled
  785. // in the last ClearCommError. Because all errors were signaled
  786. // individually, but cleared all at once, pending comm events
  787. // can yield EV_ERR while dwErrors equals 0. Ignore this event.
  788. if not ReceiveError(dwErrors) then
  789. Exit;
  790. Result := True;
  791. end;
  792. dwModemEvent := 0;
  793. if ((lpfdwEvtMask and EV_RLSD) <> 0) then
  794. dwModemEvent := ME_RLSD;
  795. if ((lpfdwEvtMask and EV_RING) <> 0) then
  796. dwModemEvent := dwModemEvent or ME_RING;
  797. if dwModemEvent <> 0 then
  798. begin
  799. if not ModemStateChange(dwModemEvent) then
  800. begin
  801. Result := False;
  802. Exit;
  803. end;
  804. Result := True;
  805. end;
  806. if ((lpfdwEvtMask and EV_ERR) = 0) and (dwModemEvent = 0) then
  807. begin
  808. // Should not have gotten here.
  809. PostHangupCall;
  810. end
  811. end; {TReadThread.HandleCommEvent}
  812. function TReadThread.ReceiveData(lpNewString: LPSTR; dwSizeofNewString: DWord): Bool;
  813. begin
  814. Result := False;
  815. if not PostMessage(hComm32Window, PWM_GOTCOMMDATA,
  816. WPARAM(dwSizeofNewString), LPARAM(lpNewString)) then
  817. PostHangupCall
  818. else
  819. Result := True;
  820. end;
  821. function TReadThread.ReceiveError(EvtMask: DWord): Bool;
  822. begin
  823. Result := False;
  824. if not PostMessage(hComm32Window, PWM_RECEIVEERROR, 0, LPARAM(EvtMask)) then
  825. PostHangupCall
  826. else
  827. Result := True;
  828. end;
  829. function TReadThread.ModemStateChange(ModemEvent: DWord): Bool;
  830. begin
  831. Result := False;
  832. if not PostMessage(hComm32Window, PWM_MODEMSTATECHANGE, 0, LPARAM(ModemEvent)) then
  833. PostHangupCall
  834. else
  835. Result := True;
  836. end;
  837. procedure TReadThread.PostHangupCall;
  838. begin
  839. PostMessage(hComm32Window, PWM_REQUESTHANGUP, 0, 0)
  840. end;
  841. { TWriteThread }
  842. procedure TWriteThread.Execute;
  843. var
  844. Msg: TMsg;
  845. dwHandleSignaled: DWord;
  846. overlappedWrite: TOverlapped;
  847. CompleteOneWriteRequire: Boolean;
  848. label
  849. EndWriteThread;
  850. begin
  851. // Needed for overlapped I/O.
  852. FillChar(overlappedWrite, SizeOf(overlappedWrite), 0); {0, 0, 0, 0, NULL}
  853. overlappedWrite.hEvent := CreateEvent(nil, True, True, nil);
  854. if overlappedWrite.hEvent = 0 then
  855. begin
  856. PostHangupCall;
  857. goto EndWriteThread;
  858. end;
  859. CompleteOneWriteRequire := True;
  860. // This is the main loop. Loop until we break out.
  861. while True do
  862. begin
  863. if not PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
  864. begin
  865. // If there are no messages pending, wait for a message or
  866. // the CloseEvent.
  867. pFSendDataEmpty^ := True;
  868. if CompleteOneWriteRequire then
  869. begin
  870. if not PostMessage(hComm32Window, PWM_SENDDATAEMPTY, 0, 0) then
  871. begin
  872. PostHangupCall;
  873. goto EndWriteThread;
  874. end
  875. end;
  876. CompleteOneWriteRequire := False;
  877. dwHandleSignaled := MsgWaitForMultipleObjects(1, hCloseEvent, False,
  878. INFINITE, QS_ALLINPUT);
  879. case dwHandleSignaled of
  880. WAIT_OBJECT_0: // CloseEvent signaled!
  881. begin
  882. // Time to exit.
  883. goto EndWriteThread;
  884. end;
  885. WAIT_OBJECT_0 + 1: // New message was received.
  886. begin
  887. // Get the message that woke us up by looping again.
  888. Continue
  889. end;
  890. WAIT_FAILED: // Wait failed. Shouldn't happen.
  891. begin
  892. PostHangupCall;
  893. goto EndWriteThread;
  894. end
  895. else // This case should never occur.
  896. begin
  897. PostHangupCall;
  898. goto EndWriteThread;
  899. end
  900. end
  901. end;
  902. // Make sure the CloseEvent isn't signaled while retrieving messages.
  903. if WAIT_TIMEOUT <> WaitForSingleObject(hCloseEvent, 0) then
  904. goto EndWriteThread;
  905. // Process the message.
  906. // This could happen if a dialog is created on this thread.
  907. // This doesn't occur in this sample, but might if modified.
  908. if Msg.HWND <> 0 {NULL} then
  909. begin
  910. TranslateMessage(Msg);
  911. DispatchMessage(Msg);
  912. Continue;
  913. end;
  914. // Handle the message.
  915. case Msg.message of
  916. PWM_COMMWRITE: // New string to write to Comm port.
  917. begin
  918. // Write the string to the comm port. HandleWriteData
  919. // does not return until the whole string has been written,
  920. // an error occurs or until the CloseEvent is signaled.
  921. if not HandleWriteData(@overlappedWrite,
  922. PAnsiChar(Msg.LPARAM), DWord(Msg.WPARAM)) then
  923. begin
  924. // If it failed, either we got a signal to end or there
  925. // really was a failure.
  926. LocalFree(HLOCAL(Msg.LPARAM));
  927. goto EndWriteThread
  928. end;
  929. CompleteOneWriteRequire := True;
  930. // Data was sent in a LocalAlloc()d buffer. Must free it.
  931. LocalFree(HLOCAL(Msg.LPARAM))
  932. end
  933. end
  934. end; {main loop}
  935. // Thats the end. Now clean up.
  936. EndWriteThread:
  937. PurgeComm(hCommFile, PURGE_TXABORT + PURGE_TXCLEAR);
  938. pFSendDataEmpty^ := True;
  939. CloseHandle(overlappedWrite.hEvent);
  940. end; {TWriteThread.Execute}
  941. function TWriteThread.HandleWriteData(lpOverlappedWrite: POverlapped;
  942. pDataToWrite: PAnsiChar; dwNumberOfBytesToWrite: DWord): Boolean;
  943. var
  944. dwLastError,
  945. dwNumberOfBytesWritten,
  946. dwWhereToStartWriting,
  947. dwHandleSignaled: DWord;
  948. HandlesToWaitFor: array[0..1] of THandle;
  949. begin
  950. Result := False;
  951. dwNumberOfBytesWritten := 0;
  952. dwWhereToStartWriting := 0; // Start at the beginning.
  953. HandlesToWaitFor[0] := hCloseEvent;
  954. HandlesToWaitFor[1] := lpOverlappedWrite^.hEvent;
  955. // Keep looping until all characters have been written.
  956. repeat
  957. // Start the overlapped I/O.
  958. if not WriteFile(hCommFile,
  959. pDataToWrite[dwWhereToStartWriting],
  960. dwNumberOfBytesToWrite, dwNumberOfBytesWritten,
  961. lpOverlappedWrite) then
  962. begin
  963. // WriteFile failed. Expected; lets handle it.
  964. dwLastError := GetLastError;
  965. // Its possible for this error to occur if the
  966. // service provider has closed the port. Time to end.
  967. if dwLastError = ERROR_INVALID_HANDLE then
  968. Exit;
  969. // Unexpected error. No idea what.
  970. if dwLastError <> ERROR_IO_PENDING then
  971. begin
  972. PostHangupCall;
  973. Exit;
  974. end;
  975. // This is the expected ERROR_IO_PENDING case.
  976. // Wait for either overlapped I/O completion,
  977. // or for the CloseEvent to get signaled.
  978. dwHandleSignaled := WaitForMultipleObjects(2, @HandlesToWaitFor,
  979. False, INFINITE);
  980. case dwHandleSignaled of
  981. WAIT_OBJECT_0: // CloseEvent signaled!
  982. begin
  983. // Time to exit.
  984. Exit
  985. end;
  986. WAIT_OBJECT_0 + 1: // Wait finished.
  987. begin
  988. // Time to get the results of the WriteFile
  989. if not GetOverlappedResult(hCommFile,
  990. lpOverlappedWrite^,
  991. dwNumberOfBytesWritten, True) then
  992. begin
  993. dwLastError := GetLastError;
  994. // Its possible for this error to occur if the
  995. // service provider has closed the port.
  996. if dwLastError = ERROR_INVALID_HANDLE then
  997. Exit;
  998. // No idea what could cause another error.
  999. PostHangupCall;
  1000. Exit
  1001. end
  1002. end;
  1003. WAIT_FAILED: // Wait failed. Shouldn't happen.
  1004. begin
  1005. PostHangupCall;
  1006. Exit;
  1007. end
  1008. else // This case should never occur.
  1009. begin
  1010. PostHangupCall;
  1011. Exit;
  1012. end
  1013. end {case}
  1014. end; {WriteFile failure}
  1015. // Some data was written. Make sure it all got written.
  1016. Dec(dwNumberOfBytesToWrite, dwNumberOfBytesWritten);
  1017. Inc(dwWhereToStartWriting, dwNumberOfBytesWritten)
  1018. until (dwNumberOfBytesToWrite <= 0); // Write the whole thing!
  1019. // Wrote the whole string.
  1020. Result := True;
  1021. end; {TWriteThread.HandleWriteData}
  1022. procedure TWriteThread.PostHangupCall;
  1023. begin
  1024. PostMessage(hComm32Window, PWM_REQUESTHANGUP, 0, 0)
  1025. end;
  1026. { TCnRS232Config }
  1027. procedure TCnRS232Config.Assign(Source: TPersistent);
  1028. begin
  1029. if Source is TCnRS232Config then
  1030. begin
  1031. FXoffChar := TCnRS232Config(Source).FXoffChar;
  1032. FReplacedChar := TCnRS232Config(Source).FReplacedChar;
  1033. FXonChar := TCnRS232Config(Source).FXonChar;
  1034. FOutx_CtsFlow := TCnRS232Config(Source).FOutx_CtsFlow;
  1035. FOutx_DsrFlow := TCnRS232Config(Source).FOutx_DsrFlow;
  1036. FParityCheck := TCnRS232Config(Source).FParityCheck;
  1037. FIgnoreNullChar := TCnRS232Config(Source).FIgnoreNullChar;
  1038. FInx_XonXoffFlow := TCnRS232Config(Source).FInx_XonXoffFlow;
  1039. FTxContinueOnXoff := TCnRS232Config(Source).FTxContinueOnXoff;
  1040. FReplaceWhenParityError := TCnRS232Config(Source).FReplaceWhenParityError;
  1041. FOutx_XonXoffFlow := TCnRS232Config(Source).FOutx_XonXoffFlow;
  1042. FDsrSensitivity := TCnRS232Config(Source).FDsrSensitivity;
  1043. FBaudRate := TCnRS232Config(Source).FBaudRate;
  1044. FByteSize := TCnRS232Config(Source).FByteSize;
  1045. FDtrControl := TCnRS232Config(Source).FDtrControl;
  1046. FParity := TCnRS232Config(Source).FParity;
  1047. FRtsControl := TCnRS232Config(Source).FRtsControl;
  1048. FStopBits := TCnRS232Config(Source).FStopBits;
  1049. FXoffLimit := TCnRS232Config(Source).FXoffLimit;
  1050. FXonLimit := TCnRS232Config(Source).FXonLimit;
  1051. Changed;
  1052. end
  1053. else
  1054. inherited;
  1055. end;
  1056. constructor TCnRS232Config.Create;
  1057. begin
  1058. inherited Create;
  1059. FBaudRate := 9600;
  1060. FParityCheck := False;
  1061. FOutx_CtsFlow := False;
  1062. FOutx_DsrFlow := False;
  1063. FDtrControl := DtrEnable;
  1064. FDsrSensitivity := False;
  1065. FTxContinueOnXoff := False;
  1066. FOutx_XonXoffFlow := False;
  1067. FInx_XonXoffFlow := False;
  1068. FReplaceWhenParityError := False;
  1069. FIgnoreNullChar := False;
  1070. FRtsControl := RtsEnable;
  1071. FXonLimit := 500;
  1072. FXoffLimit := 500;
  1073. FByteSize := _8;
  1074. FParity := paNone;
  1075. FStopBits := _1;
  1076. FXonChar := chr($11); // Ctrl-Q
  1077. FXoffChar := chr($13); // Ctrl-S
  1078. FReplacedChar := chr(0);
  1079. end;
  1080. procedure TCnRS232Config.GetDCB(var DCB: TDCB);
  1081. begin
  1082. DCB.DCBlength := SizeOf(TDCB);
  1083. DCB.BaudRate := FBaudRate;
  1084. DCB.Flags := 1;
  1085. if FParityCheck then
  1086. DCB.Flags := DCB.Flags or 2;
  1087. if FOutx_CtsFlow then
  1088. DCB.Flags := DCB.Flags or 4;
  1089. if FOutx_DsrFlow then
  1090. DCB.Flags := DCB.Flags or 8;
  1091. if FDtrControl = DtrEnable then
  1092. DCB.Flags := DCB.Flags or $10
  1093. else if FDtrControl = DtrHandshake then
  1094. DCB.Flags := DCB.Flags or $20;
  1095. if FDsrSensitivity then
  1096. DCB.Flags := DCB.Flags or $40;
  1097. if FTxContinueOnXoff then
  1098. DCB.Flags := DCB.Flags or $80;
  1099. if FOutx_XonXoffFlow then
  1100. DCB.Flags := DCB.Flags or $100;
  1101. if FInx_XonXoffFlow then
  1102. DCB.Flags := DCB.Flags or $200;
  1103. if FReplaceWhenParityError then
  1104. DCB.Flags := DCB.Flags or $400;
  1105. if FIgnoreNullChar then
  1106. DCB.Flags := DCB.Flags or $800;
  1107. if FRtsControl = RtsEnable then
  1108. DCB.Flags := DCB.Flags or $1000
  1109. else if FRtsControl = RtsHandshake then
  1110. DCB.Flags := DCB.Flags or $2000
  1111. else if FRtsControl = RtsTransmissionAvailable then
  1112. DCB.Flags := DCB.Flags or $3000;
  1113. DCB.XonLim := FXonLimit;
  1114. DCB.XoffLim := FXoffLimit;
  1115. DCB.ByteSize := Ord(FByteSize) + 5;
  1116. DCB.Parity := Ord(FParity);
  1117. DCB.StopBits := Ord(FStopBits);
  1118. DCB.XonChar := AnsiChar(FXonChar);
  1119. DCB.XoffChar := AnsiChar(FXoffChar);
  1120. DCB.ErrorChar := AnsiChar(FReplacedChar);
  1121. end;
  1122. procedure TCnRS232Config.SetDCB(const DCB: TDCB);
  1123. begin
  1124. FBaudRate := DCB.BaudRate;
  1125. FParityCheck := DCB.Flags and 2 <> 0;
  1126. FOutx_CtsFlow := DCB.Flags and 4 <> 0;
  1127. FOutx_DsrFlow := DCB.Flags and 8 <> 0;
  1128. if DCB.Flags and $10 <> 0 then
  1129. FDtrControl := DtrEnable
  1130. else if DCB.Flags and $20 <> 0 then
  1131. FDtrControl := DtrHandshake
  1132. else
  1133. FDtrControl := DtrDisable;
  1134. FDsrSensitivity := DCB.Flags and $40 <> 0;
  1135. FTxContinueOnXoff := DCB.Flags and $80 <> 0;
  1136. FOutx_XonXoffFlow := DCB.Flags and $100 <> 0;
  1137. FInx_XonXoffFlow := DCB.Flags and $200 <> 0;
  1138. FReplaceWhenParityError := DCB.Flags and $400 <> 0;
  1139. FIgnoreNullChar := DCB.Flags and $800 <> 0;
  1140. if DCB.Flags and $1000 <> 0 then
  1141. FRtsControl := RtsEnable
  1142. else if DCB.Flags and $2000 <> 0 then
  1143. FRtsControl := RtsHandshake
  1144. else if DCB.Flags and $3000 <> 0 then
  1145. FRtsControl := RtsTransmissionAvailable
  1146. else
  1147. FRtsControl := RtsDisable;
  1148. FXonLimit := DCB.XonLim;
  1149. FXoffLimit := DCB.XoffLim;
  1150. FByteSize := TByteSize(DCB.ByteSize - 5);
  1151. FParity := TParity(DCB.Parity);
  1152. FStopBits := TStopBits(DCB.StopBits);
  1153. FXonChar := Char(DCB.XonChar);
  1154. FXoffChar := Char(DCB.XoffChar);
  1155. FReplacedChar := Char(DCB.ErrorChar);
  1156. end;
  1157. procedure TCnRS232Config.SetBaudRate(const Value: DWord);
  1158. begin
  1159. if FBaudRate <> Value then
  1160. begin
  1161. FBaudRate := Value;
  1162. Changed;
  1163. end;
  1164. end;
  1165. procedure TCnRS232Config.SetByteSize(const Value: TByteSize);
  1166. begin
  1167. if FByteSize <> Value then
  1168. begin
  1169. FByteSize := Value;
  1170. Changed;
  1171. end;
  1172. end;
  1173. procedure TCnRS232Config.SetDsrSensitivity(const Value: Boolean);
  1174. begin
  1175. if FDsrSensitivity <> Value then
  1176. begin
  1177. FDsrSensitivity := Value;
  1178. Changed;
  1179. end;
  1180. end;
  1181. procedure TCnRS232Config.SetDtrControl(const Value: TDtrControl);
  1182. begin
  1183. if FDtrControl <> Value then
  1184. begin
  1185. FDtrControl := Value;
  1186. Changed;
  1187. end;
  1188. end;
  1189. procedure TCnRS232Config.SetIgnoreNullChar(const Value: Boolean);
  1190. begin
  1191. if FIgnoreNullChar <> Value then
  1192. begin
  1193. FIgnoreNullChar := Value;
  1194. Changed;
  1195. end;
  1196. end;
  1197. procedure TCnRS232Config.SetInx_XonXoffFlow(const Value: Boolean);
  1198. begin
  1199. if FInx_XonXoffFlow <> Value then
  1200. begin
  1201. FInx_XonXoffFlow := Value;
  1202. Changed;
  1203. end;
  1204. end;
  1205. procedure TCnRS232Config.SetOutx_CtsFlow(const Value: Boolean);
  1206. begin
  1207. if FOutx_CtsFlow <> Value then
  1208. begin
  1209. FOutx_CtsFlow := Value;
  1210. Changed;
  1211. end;
  1212. end;
  1213. procedure TCnRS232Config.SetOutx_DsrFlow(const Value: Boolean);
  1214. begin
  1215. if FOutx_DsrFlow <> Value then
  1216. begin
  1217. FOutx_DsrFlow := Value;
  1218. Changed;
  1219. end;
  1220. end;
  1221. procedure TCnRS232Config.SetOutx_XonXoffFlow(const Value: Boolean);
  1222. begin
  1223. if FOutx_XonXoffFlow <> Value then
  1224. begin
  1225. FOutx_XonXoffFlow := Value;
  1226. Changed;
  1227. end;
  1228. end;
  1229. procedure TCnRS232Config.SetParityCheck(const Value: Boolean);
  1230. begin
  1231. if FParityCheck <> Value then
  1232. begin
  1233. FParityCheck := Value;
  1234. Changed;
  1235. end;
  1236. end;
  1237. procedure TCnRS232Config.SetReplacedChar(const Value: Char);
  1238. begin
  1239. if FReplacedChar <> Value then
  1240. begin
  1241. FReplacedChar := Value;
  1242. Changed;
  1243. end;
  1244. end;
  1245. procedure TCnRS232Config.SetReplaceWhenParityError(const Value: Boolean);
  1246. begin
  1247. if FReplaceWhenParityError <> Value then
  1248. begin
  1249. FReplaceWhenParityError := Value;
  1250. Changed;
  1251. end;
  1252. end;
  1253. procedure TCnRS232Config.SetRtsControl(const Value: TRtsControl);
  1254. begin
  1255. if FRtsControl <> Value then
  1256. begin
  1257. FRtsControl := Value;
  1258. Changed;
  1259. end;
  1260. end;
  1261. procedure TCnRS232Config.SetStopBits(const Value: TStopBits);
  1262. begin
  1263. if FStopBits <> Value then
  1264. begin
  1265. FStopBits := Value;
  1266. Changed;
  1267. end;
  1268. end;
  1269. procedure TCnRS232Config.SetTxContinueOnXoff(const Value: Boolean);
  1270. begin
  1271. if FTxContinueOnXoff <> Value then
  1272. begin
  1273. FTxContinueOnXoff := Value;
  1274. Changed;
  1275. end;
  1276. end;
  1277. procedure TCnRS232Config.SetXoffChar(const Value: Char);
  1278. begin
  1279. if FXonChar = Value then
  1280. raise ERS232Error.Create(SInvalidXonXoffChar);
  1281. if FXoffChar <> Value then
  1282. begin
  1283. FXoffChar := Value;
  1284. Changed;
  1285. end;
  1286. end;
  1287. procedure TCnRS232Config.SetXoffLimit(const Value: WORD);
  1288. begin
  1289. if FXoffLimit <> Value then
  1290. begin
  1291. FXoffLimit := Value;
  1292. Changed;
  1293. end;
  1294. end;
  1295. procedure TCnRS232Config.SetXonChar(const Value: Char);
  1296. begin
  1297. if FXoffChar = Value then
  1298. raise ERS232Error.Create(SInvalidXonXoffChar);
  1299. if FXonChar <> Value then
  1300. begin
  1301. FXonChar := Value;
  1302. Changed;
  1303. end;
  1304. end;
  1305. procedure TCnRS232Config.SetXonLimit(const Value: WORD);
  1306. begin
  1307. if FXonLimit <> Value then
  1308. begin
  1309. FXonLimit := Value;
  1310. Changed;
  1311. end;
  1312. end;
  1313. { TCnRS232Timeouts }
  1314. procedure TCnRS232Timeouts.Assign(Source: TPersistent);
  1315. begin
  1316. if Source is TCnRS232Timeouts then
  1317. begin
  1318. FReadIntervalTimeout := TCnRS232Timeouts(Source).FReadIntervalTimeout;
  1319. FReadTotalTimeoutMultiplier := TCnRS232Timeouts(Source).FReadTotalTimeoutMultiplier;
  1320. FReadTotalTimeoutConstant := TCnRS232Timeouts(Source).FReadTotalTimeoutConstant;
  1321. FWriteTotalTimeoutMultiplier := TCnRS232Timeouts(Source).FWriteTotalTimeoutMultiplier;
  1322. FWriteTotalTimeoutConstant := TCnRS232Timeouts(Source).FWriteTotalTimeoutConstant;
  1323. Changed;
  1324. end
  1325. else
  1326. inherited;
  1327. end;
  1328. constructor TCnRS232Timeouts.Create;
  1329. begin
  1330. inherited Create;
  1331. FReadIntervalTimeout := 10;
  1332. FReadTotalTimeoutMultiplier := 0;
  1333. FReadTotalTimeoutConstant := 0;
  1334. FWriteTotalTimeoutMultiplier := 0;
  1335. FWriteTotalTimeoutConstant := 0;
  1336. end;
  1337. function TCnRS232Timeouts.GetCommTimeouts: TCommTimeouts;
  1338. begin
  1339. Result.ReadIntervalTimeout := FReadIntervalTimeout;
  1340. Result.ReadTotalTimeoutMultiplier := FReadTotalTimeoutMultiplier;
  1341. Result.ReadTotalTimeoutConstant := FReadTotalTimeoutConstant;
  1342. Result.WriteTotalTimeoutMultiplier := FWriteTotalTimeoutMultiplier;
  1343. Result.WriteTotalTimeoutConstant := FWriteTotalTimeoutConstant;
  1344. end;
  1345. procedure TCnRS232Timeouts.SetCommTimeouts(const Value: TCommTimeouts);
  1346. begin
  1347. FReadIntervalTimeout := Value.ReadIntervalTimeout;
  1348. FReadTotalTimeoutMultiplier := Value.ReadTotalTimeoutMultiplier;
  1349. FReadTotalTimeoutConstant := Value.ReadTotalTimeoutConstant;
  1350. FWriteTotalTimeoutMultiplier := Value.WriteTotalTimeoutMultiplier;
  1351. FWriteTotalTimeoutConstant := Value.WriteTotalTimeoutConstant;
  1352. end;
  1353. procedure TCnRS232Timeouts.SetReadIntervalTimeout(const Value: DWord);
  1354. begin
  1355. if FReadIntervalTimeout <> Value then
  1356. begin
  1357. FReadIntervalTimeout := Value;
  1358. Changed;
  1359. end;
  1360. end;
  1361. procedure TCnRS232Timeouts.SetReadTotalTimeoutConstant(const Value: DWord);
  1362. begin
  1363. if FReadTotalTimeoutConstant <> Value then
  1364. begin
  1365. FReadTotalTimeoutConstant := Value;
  1366. Changed;
  1367. end;
  1368. end;
  1369. procedure TCnRS232Timeouts.SetReadTotalTimeoutMultiplier(const Value: DWord);
  1370. begin
  1371. if FReadTotalTimeoutMultiplier <> Value then
  1372. begin
  1373. FReadTotalTimeoutMultiplier := Value;
  1374. Changed;
  1375. end;
  1376. end;
  1377. procedure TCnRS232Timeouts.SetWriteTotalTimeoutConstant(const Value: DWord);
  1378. begin
  1379. if FWriteTotalTimeoutConstant <> Value then
  1380. begin
  1381. FWriteTotalTimeoutConstant := Value;
  1382. Changed;
  1383. end;
  1384. end;
  1385. procedure TCnRS232Timeouts.SetWriteTotalTimeoutMultiplier(
  1386. const Value: DWord);
  1387. begin
  1388. if FWriteTotalTimeoutMultiplier <> Value then
  1389. begin
  1390. FWriteTotalTimeoutMultiplier := Value;
  1391. Changed;
  1392. end;
  1393. end;
  1394. { TCnRS232 }
  1395. procedure TCnRS232.Assign(Source: TPersistent);
  1396. var
  1397. Save: Boolean;
  1398. begin
  1399. if Source is TCnRS232 then
  1400. begin
  1401. Save := hCommFile <> 0;
  1402. if Save then StopComm;
  1403. FCommName := TCnRS232(Source).FCommName;
  1404. FCommConfig.Assign(TCnRS232(Source).FCommConfig);
  1405. FTimeouts.Assign(TCnRS232(Source).FTimeouts);
  1406. if Save then StartComm;
  1407. end
  1408. else
  1409. inherited;
  1410. end;
  1411. constructor TCnRS232.Create(AOwner: TComponent);
  1412. begin
  1413. inherited Create(AOwner);
  1414. FCommName := 'COM2';
  1415. FCommConfig := TCnRS232Config.Create(ConfigChanged);
  1416. FTimeouts := TCnRS232Timeouts.Create(TimeoutsChanged);
  1417. ReadThread := nil;
  1418. WriteThread := nil;
  1419. hCommFile := 0;
  1420. hCloseEvent := 0;
  1421. FSendDataEmpty := True;
  1422. if not (csDesigning in ComponentState) then
  1423. FHWnd := AllocateHWnd(CommWndProc)
  1424. end;
  1425. destructor TCnRS232.Destroy;
  1426. begin
  1427. StopComm;
  1428. if not (csDesigning in ComponentState) then
  1429. DeallocateHWnd(FHWnd);
  1430. FCommConfig.Free;
  1431. FTimeouts.Free;
  1432. inherited Destroy;
  1433. end;
  1434. procedure TCnRS232.StartComm;
  1435. var
  1436. hNewCommFile: THandle;
  1437. begin
  1438. if (hCommFile <> 0) then
  1439. raise ERS232Error.Create(SSerialPortAlreadyOpened);
  1440. // 解决串口号大于10无法识别的问题
  1441. hNewCommFile := CreateFile(PChar('\\.\' + CommName), GENERIC_READ or GENERIC_WRITE,
  1442. 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED, 0);
  1443. if hNewCommFile = INVALID_HANDLE_VALUE then
  1444. raise ERS232Error.Create(SSerialPortOpenError);
  1445. if GetFileType(hNewCommFile) <> FILE_TYPE_CHAR then
  1446. begin
  1447. CloseHandle(hNewCommFile);
  1448. raise ERS232Error.Create(SNotACommHandle);
  1449. end;
  1450. if not SetupComm(hNewCommFile, 4096, 4096) then
  1451. begin
  1452. CloseHandle(hCommFile);
  1453. raise ERS232Error.Create(SSetupBuffFail);
  1454. end;
  1455. hCommFile := hNewCommFile;
  1456. PurgeComm(hCommFile, PURGE_TXABORT or PURGE_RXABORT or
  1457. PURGE_TXCLEAR or PURGE_RXCLEAR);
  1458. FSendDataEmpty := True;
  1459. _SetCommTimeout;
  1460. _SetCommState;
  1461. hCloseEvent := CreateEvent(nil, True, False, nil);
  1462. if hCloseEvent = 0 then
  1463. begin
  1464. CloseHandle(hCommFile);
  1465. hCommFile := 0;
  1466. raise ERS232Error.Create(SCreateEventFail);
  1467. end;
  1468. try
  1469. ReadThread := TReadThread.Create(True {suspended});
  1470. except
  1471. ReadThread := nil;
  1472. CloseHandle(hCloseEvent);
  1473. CloseHandle(hCommFile);
  1474. hCommFile := 0;
  1475. raise ERS232Error.Create(SCreateReadFail)
  1476. end;
  1477. ReadThread.hCommFile := hCommFile;
  1478. ReadThread.hCloseEvent := hCloseEvent;
  1479. ReadThread.hComm32Window := FHWnd;
  1480. ReadThread.Priority := tpHighest;
  1481. try
  1482. WriteThread := TWriteThread.Create(True {suspended});
  1483. except
  1484. CloseReadThread;
  1485. WriteThread := nil;
  1486. CloseHandle(hCloseEvent);
  1487. CloseHandle(hCommFile);
  1488. hCommFile := 0;
  1489. raise ERS232Error.Create(SCreateWriteFail);
  1490. end;
  1491. WriteThread.hCommFile := hCommFile;
  1492. WriteThread.hCloseEvent := hCloseEvent;
  1493. WriteThread.hComm32Window := FHWnd;
  1494. WriteThread.pFSendDataEmpty := @FSendDataEmpty;
  1495. WriteThread.Priority := tpHigher;
  1496. ReadThread.Resume;
  1497. WriteThread.Resume;
  1498. end;
  1499. procedure TCnRS232.StopComm;
  1500. begin
  1501. if hCommFile = 0 then
  1502. Exit;
  1503. CloseReadThread;
  1504. CloseWriteThread;
  1505. CloseHandle(hCloseEvent);
  1506. CloseHandle(hCommFile);
  1507. hCommFile := 0;
  1508. end;
  1509. function TCnRS232.WriteCommData(pDataToWrite: PAnsiChar; dwSizeofDataToWrite: WORD): Boolean;
  1510. var
  1511. Buffer: Pointer;
  1512. begin
  1513. if (WriteThread <> nil) and (dwSizeofDataToWrite <> 0) then
  1514. begin
  1515. Buffer := Pointer(LocalAlloc(LPTR, dwSizeofDataToWrite + 1));
  1516. Move(pDataToWrite^, Buffer^, dwSizeofDataToWrite);
  1517. if PostThreadMessage(WriteThread.ThreadID, PWM_COMMWRITE,
  1518. WPARAM(dwSizeofDataToWrite), LPARAM(Buffer)) then
  1519. begin
  1520. FSendDataEmpty := False;
  1521. Result := True;
  1522. Exit;
  1523. end;
  1524. end;
  1525. Result := False;
  1526. end;
  1527. function TCnRS232.GetModemState: DWord;
  1528. var
  1529. dwModemState: DWord;
  1530. begin
  1531. if not GetCommModemStatus(hCommFile, dwModemState) then
  1532. Result := 0
  1533. else
  1534. Result := dwModemState;
  1535. end;
  1536. procedure TCnRS232.CloseReadThread;
  1537. begin
  1538. if ReadThread <> nil then
  1539. begin
  1540. SetEvent(hCloseEvent);
  1541. PurgeComm(hCommFile, PURGE_RXABORT + PURGE_RXCLEAR);
  1542. if (WaitForSingleObject(ReadThread.Handle, 10000) = WAIT_TIMEOUT) then
  1543. ReadThread.Terminate;
  1544. ReadThread.Free;
  1545. ReadThread := nil;
  1546. end;
  1547. end;
  1548. procedure TCnRS232.CloseWriteThread;
  1549. begin
  1550. if WriteThread <> nil then
  1551. begin
  1552. SetEvent(hCloseEvent);
  1553. PurgeComm(hCommFile, PURGE_TXABORT + PURGE_TXCLEAR);
  1554. FSendDataEmpty := True;
  1555. if WaitForSingleObject(WriteThread.Handle, 10000) = WAIT_TIMEOUT then
  1556. WriteThread.Terminate;
  1557. WriteThread.Free;
  1558. WriteThread := nil;
  1559. end;
  1560. end;
  1561. procedure TCnRS232.ReceiveData(Buffer: PAnsiChar; BufferLength: WORD);
  1562. begin
  1563. if Assigned(FOnReceiveData) then
  1564. FOnReceiveData(Self, Buffer, BufferLength)
  1565. end;
  1566. procedure TCnRS232.ReceiveError(EvtMask: DWord);
  1567. begin
  1568. if Assigned(FOnReceiveError) then
  1569. FOnReceiveError(Self, EvtMask)
  1570. end;
  1571. procedure TCnRS232.ModemStateChange(ModemEvent: DWord);
  1572. begin
  1573. if Assigned(FOnModemStateChange) then
  1574. FOnModemStateChange(Self, ModemEvent)
  1575. end;
  1576. procedure TCnRS232.RequestHangup;
  1577. begin
  1578. if Assigned(FOnRequestHangup) then
  1579. FOnRequestHangup(Self)
  1580. end;
  1581. procedure TCnRS232._SendDataEmpty;
  1582. begin
  1583. if Assigned(FOnSendDataEmpty) then
  1584. FOnSendDataEmpty(Self)
  1585. end;
  1586. procedure TCnRS232.CommWndProc(var Msg: TMessage);
  1587. begin
  1588. case Msg.Msg of
  1589. PWM_GOTCOMMDATA:
  1590. begin
  1591. ReceiveData(PAnsiChar(Msg.LPARAM), Msg.WPARAM);
  1592. LocalFree(Msg.LPARAM)
  1593. end;
  1594. PWM_RECEIVEERROR: ReceiveError(Msg.LPARAM);
  1595. PWM_MODEMSTATECHANGE: ModemStateChange(Msg.LPARAM);
  1596. PWM_REQUESTHANGUP: RequestHangup;
  1597. PWM_SENDDATAEMPTY: _SendDataEmpty;
  1598. else
  1599. with msg do //默认消息处理,解决win98无法正常退出问题
  1600. //(WM_QUERYENDSESSION和WM_ENDSESSION)
  1601. Result := DefWindowProc(FHWnd, Msg, WParam, LParam);
  1602. end;
  1603. end;
  1604. procedure TCnRS232._SetCommState;
  1605. var
  1606. DCB: TDCB;
  1607. commprop: TCommProp;
  1608. fdwEvtMask: DWord;
  1609. begin
  1610. GetCommState(hCommFile, DCB);
  1611. GetCommProperties(hCommFile, commprop);
  1612. GetCommMask(hCommFile, fdwEvtMask);
  1613. FCommConfig.GetDCB(DCB);
  1614. SetCommState(hCommFile, DCB);
  1615. end;
  1616. procedure TCnRS232._SetCommTimeout;
  1617. var
  1618. CommTimeouts: TCommTimeouts;
  1619. begin
  1620. //GetCommTimeouts(hCommFile, CommTimeouts);
  1621. CommTimeouts := Timeouts.GetCommTimeouts;
  1622. SetCommTimeouts(hCommFile, CommTimeouts);
  1623. end;
  1624. procedure TCnRS232.ConfigChanged(Sender: TObject);
  1625. begin
  1626. _SetCommState;
  1627. end;
  1628. procedure TCnRS232.TimeoutsChanged(Sender: TObject);
  1629. begin
  1630. _SetCommTimeout;
  1631. end;
  1632. function TCnRS232.GetConnected: Boolean;
  1633. begin
  1634. Result := hCommFile <> 0;
  1635. end;
  1636. procedure TCnRS232.SetCommConfig(const Value: TCnRS232Config);
  1637. begin
  1638. FCommConfig.Assign(Value);
  1639. end;
  1640. procedure TCnRS232.SetTimeouts(const Value: TCnRS232Timeouts);
  1641. begin
  1642. FTimeouts.Assign(Value);
  1643. end;
  1644. const
  1645. csCommName = 'CommName';
  1646. csXoffChar = 'XoffChar';
  1647. csReplacedChar = 'ReplacedChar';
  1648. csXonChar = 'XonChar';
  1649. csOutx_CtsFlow = 'Outx_CtsFlow';
  1650. csOutx_DsrFlow = 'Outx_DsrFlow';
  1651. csParityCheck = 'ParityCheck';
  1652. csIgnoreNullChar = 'IgnoreNullChar';
  1653. csInx_XonXoffFlow = 'Inx_XonXoffFlow';
  1654. csTxContinueOnXoff = 'TxContinueOnXoff';
  1655. csReplaceWhenParityError = 'ReplaceWhenParityError';
  1656. csOutx_XonXoffFlow = 'Outx_XonXoffFlow';
  1657. csDsrSensitivity = 'DsrSensitivity';
  1658. csBaudRate = 'BaudRate';
  1659. csByteSize = 'ByteSize';
  1660. csDtrControl = 'DtrControl';
  1661. csParity = 'Parity';
  1662. csRtsControl = 'RtsControl';
  1663. csStopBits = 'StopBits';
  1664. csXoffLimit = 'XoffLimit';
  1665. csXonLimit = 'XonLimit';
  1666. csReadIntervalTimeout = 'ReadIntervalTimeout';
  1667. csReadTotalTimeoutConstant = 'ReadTotalTimeoutConstant';
  1668. csReadTotalTimeoutMultiplier = 'ReadTotalTimeoutMultiplier';
  1669. csWriteTotalTimeoutMultiplier = 'WriteTotalTimeoutMultiplier';
  1670. csWriteTotalTimeoutConstant = 'WriteTotalTimeoutConstant';
  1671. procedure TCnRS232.ReadFromIni(Ini: TCustomIniFile; const Section: string);
  1672. begin
  1673. FCommName := Ini.ReadString(Section, csCommName, FCommName);
  1674. with FCommConfig do
  1675. begin
  1676. FXoffChar := Char(Ini.ReadInteger(Section, csXoffChar, Byte(FXoffChar)));
  1677. FReplacedChar := Char(Ini.ReadInteger(Section, csReplacedChar, Byte(FReplacedChar)));
  1678. FXonChar := Char(Ini.ReadInteger(Section, csXonChar, Byte(FXonChar)));
  1679. FOutx_CtsFlow := Ini.ReadBool(Section, csOutx_CtsFlow, FOutx_CtsFlow);
  1680. FOutx_DsrFlow := Ini.ReadBool(Section, csOutx_DsrFlow, FOutx_DsrFlow);
  1681. FParityCheck := Ini.ReadBool(Section, csParityCheck, FParityCheck);
  1682. FIgnoreNullChar := Ini.ReadBool(Section, csIgnoreNullChar, FIgnoreNullChar);
  1683. FInx_XonXoffFlow := Ini.ReadBool(Section, csInx_XonXoffFlow, FInx_XonXoffFlow);
  1684. FTxContinueOnXoff := Ini.ReadBool(Section, csTxContinueOnXoff, FTxContinueOnXoff);
  1685. FReplaceWhenParityError := Ini.ReadBool(Section, csReplaceWhenParityError, FReplaceWhenParityError);
  1686. FOutx_XonXoffFlow := Ini.ReadBool(Section, csOutx_XonXoffFlow, FOutx_XonXoffFlow);
  1687. FDsrSensitivity := Ini.ReadBool(Section, csDsrSensitivity, FDsrSensitivity);
  1688. FBaudRate := Ini.ReadInteger(Section, csBaudRate, FBaudRate);
  1689. FByteSize := TByteSize(Ini.ReadInteger(Section, csByteSize, Ord(FByteSize)));
  1690. FDtrControl := TDtrControl(Ini.ReadInteger(Section, csDtrControl, Ord(FDtrControl)));
  1691. FParity := TParity(Ini.ReadInteger(Section, csParity, Ord(FParity)));
  1692. FRtsControl := TRtsControl(Ini.ReadInteger(Section, csRtsControl, Ord(FRtsControl)));
  1693. FStopBits := TStopBits(Ini.ReadInteger(Section, csStopBits, Ord(FStopBits)));
  1694. FXoffLimit := Ini.ReadInteger(Section, csXoffLimit, FXoffLimit);
  1695. FXonLimit := Ini.ReadInteger(Section, csXonLimit, FXonLimit);
  1696. end;
  1697. with FTimeouts do
  1698. begin
  1699. FReadTotalTimeoutConstant := Ini.ReadInteger(Section, csReadTotalTimeoutConstant, FReadTotalTimeoutConstant);
  1700. FReadIntervalTimeout := Ini.ReadInteger(Section, csReadIntervalTimeout, FReadIntervalTimeout);
  1701. FReadTotalTimeoutMultiplier := Ini.ReadInteger(Section, csReadTotalTimeoutMultiplier, FReadTotalTimeoutMultiplier);
  1702. FWriteTotalTimeoutConstant := Ini.ReadInteger(Section, csWriteTotalTimeoutConstant, FWriteTotalTimeoutConstant);
  1703. FWriteTotalTimeoutMultiplier := Ini.ReadInteger(Section, csWriteTotalTimeoutMultiplier, FWriteTotalTimeoutMultiplier);
  1704. end;
  1705. end;
  1706. procedure TCnRS232.WriteToIni(Ini: TCustomIniFile; const Section: string);
  1707. begin
  1708. Ini.WriteString(Section, csCommName, FCommName);
  1709. with FCommConfig do
  1710. begin
  1711. Ini.WriteInteger(Section, csXoffChar, Byte(FXoffChar));
  1712. Ini.WriteInteger(Section, csReplacedChar, Byte(FReplacedChar));
  1713. Ini.WriteInteger(Section, csXonChar, Byte(FXonChar));
  1714. Ini.WriteBool(Section, csOutx_CtsFlow, FOutx_CtsFlow);
  1715. Ini.WriteBool(Section, csOutx_DsrFlow, FOutx_DsrFlow);
  1716. Ini.WriteBool(Section, csParityCheck, FParityCheck);
  1717. Ini.WriteBool(Section, csIgnoreNullChar, FIgnoreNullChar);
  1718. Ini.WriteBool(Section, csInx_XonXoffFlow, FInx_XonXoffFlow);
  1719. Ini.WriteBool(Section, csTxContinueOnXoff, FTxContinueOnXoff);
  1720. Ini.WriteBool(Section, csReplaceWhenParityError, FReplaceWhenParityError);
  1721. Ini.WriteBool(Section, csOutx_XonXoffFlow, FOutx_XonXoffFlow);
  1722. Ini.WriteBool(Section, csDsrSensitivity, FDsrSensitivity);
  1723. Ini.WriteInteger(Section, csBaudRate, FBaudRate);
  1724. Ini.WriteInteger(Section, csByteSize, Ord(FByteSize));
  1725. Ini.WriteInteger(Section, csDtrControl, Ord(FDtrControl));
  1726. Ini.WriteInteger(Section, csParity, Ord(FParity));
  1727. Ini.WriteInteger(Section, csRtsControl, Ord(FRtsControl));
  1728. Ini.WriteInteger(Section, csStopBits, Ord(FStopBits));
  1729. Ini.WriteInteger(Section, csXoffLimit, FXoffLimit);
  1730. Ini.WriteInteger(Section, csXonLimit, FXonLimit);
  1731. end;
  1732. with FTimeouts do
  1733. begin
  1734. Ini.WriteInteger(Section, csReadTotalTimeoutConstant, FReadTotalTimeoutConstant);
  1735. Ini.WriteInteger(Section, csReadTotalTimeoutMultiplier, FReadTotalTimeoutMultiplier);
  1736. Ini.WriteInteger(Section, csReadIntervalTimeout, FReadIntervalTimeout);
  1737. Ini.WriteInteger(Section, csWriteTotalTimeoutMultiplier, FWriteTotalTimeoutMultiplier);
  1738. Ini.WriteInteger(Section, csWriteTotalTimeoutConstant, FWriteTotalTimeoutConstant);
  1739. end;
  1740. end;
  1741. procedure TCnRS232.ReadFromIni(const FileName, Section: string);
  1742. var
  1743. Ini: TIniFile;
  1744. begin
  1745. Ini := TIniFile.Create(FileName);
  1746. try
  1747. ReadFromIni(Ini, Section);
  1748. finally
  1749. Ini.Free;
  1750. end;
  1751. end;
  1752. procedure TCnRS232.WriteToIni(const FileName, Section: string);
  1753. var
  1754. Ini: TIniFile;
  1755. begin
  1756. Ini := TIniFile.Create(FileName);
  1757. try
  1758. WriteToIni(Ini, Section);
  1759. finally
  1760. Ini.Free;
  1761. end;
  1762. end;
  1763. procedure TCnRS232.GetComponentInfo(var AName, Author, Email, Comment: string);
  1764. begin
  1765. AName := SCnRS232Name;
  1766. Author := SCnPack_Zjy;
  1767. Email := SCnPack_ZjyEmail;
  1768. Comment := SCnRS232Comment;
  1769. end;
  1770. end.