/
clsComm.cls
1052 lines (902 loc) · 34.4 KB
/
clsComm.cls
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
Persistable = 0 'NotPersistable
DataBindingBehavior = 0 'vbNone
DataSourceBehavior = 0 'vbNone
MTSTransactionMode = 0 'NotAnMTSObject
END
Attribute VB_Name = "clsComm"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
'22Feb Added CommBuffer
' clsDCB - Device Communication Block utility class
' Part of the Desaware API Class Library
' Copyright (c) 1996 by Desaware Inc.
' All Rights Reserved
Option Explicit
' Storage for the debug ID.
Private mlngDebugID As Long
Private Type COMMTIMEOUTS
ReadIntervalTimeout As Long
ReadTotalTimeoutMultiplier As Long
ReadTotalTimeoutConstant As Long
WriteTotalTimeoutMultiplier As Long
WriteTotalTimeoutConstant As Long
End Type
Private Type OVERLAPPED
Internal As Long
InternalHigh As Long
Offset As Long
OffsetHigh As Long
hEvent As Long
End Type
Private Type ADDINFO 'additional info
' Index As Long
hName As String
' ErrMsg As String
State As Long '0 = closed, 1=Opem (Only polled when state is 1)
Hidx As Long 'Comm Array Index(So we know the Socket when data rcvd)
Idx As Long 'Sockets index (So we know the Socket when data rcvd)
AutoBaudRate As Boolean 'Turn on NMEA baudRate detection
End Type
' Private members
Private timeouts As COMMTIMEOUTS
Private handle As Long ' Comm handle
Private DevName$ ' Com1, com2 or other compatible comm device
' Public members
Public DCB As clsDCB
'JNA Private members
Public PartSentence As String
'Will need to be public to access with a Forwarder module
Private Info As ADDINFO
'JNA Public members
'Public StatusBlock As COMMSTATUS
'Public Name As String
' Current state indicators
' Holds output data that arrives while an output transfer is in progress
Private PendingOutput$
Private CurrentEventMask& ' Non zero if events are being watched for
' Buffers for overlapped input and output
' Must take this approach due to VB's ability to move strings
Private CurrentInputBuffer&
Private CurrentOutputBuffer&
Private TriggeredEvents& ' Variable to load with event results
' Three overlapped structures,
' 0 = read, 1 = write, 2 = waitevent
Private overlaps(2) As OVERLAPPED
' Indicates background operation is in progress
Private inprogress(2) As Boolean
' Amount of data transferred on write
Private closeinprogress As Boolean
Private DataWritten&
Private DataRead&
Private EventResults& 'New
' This object must have two functions
' CommInput(dev As clsComm, info As String)
' CommEvent(dev As clsComm, event as long)
Private CallbackObject As Object
' Declarations
Private Declare Function apiSetCommTimeouts Lib "kernel32" Alias "SetCommTimeouts" (ByVal hFile As Long, lpCommTimeouts As COMMTIMEOUTS) As Long
Private Declare Function apiGetCommTimeouts Lib "kernel32" Alias "GetCommTimeouts" (ByVal hFile As Long, lpCommTimeouts As COMMTIMEOUTS) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function SetupComm Lib "kernel32" (ByVal hFile As Long, ByVal dwInQueue As Long, ByVal dwOutQueue As Long) As Long
Private Declare Function GetCommModemStatus Lib "kernel32" (ByVal hFile As Long, ByRef lpModemStat As Long) As Long
Private Declare Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long
Private Declare Function GlobalFree Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Function lstrcpyFromBuffer Lib "kernel32" Alias "lstrcpynA" (ByVal lpString1 As String, ByVal Buffer As Long, ByVal iMaxLength As Long) As Long
Private Declare Function lstrcpyToBuffer Lib "kernel32" Alias "lstrcpynA" (ByVal Buffer As Long, ByVal lpString2 As String, ByVal iMaxLength As Long) As Long
Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenA" (ByVal lpString As String) As Long
Private Declare Function CreateEvent Lib "kernel32" Alias "CreateEventA" (ByVal lpEventAttributes As Long, ByVal bManualReset As Long, ByVal bInitialState As Long, ByVal lpName As String) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, ByVal lpBuffer As Long, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, lpOverlapped As OVERLAPPED) As Long
Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, ByVal lpBuffer As Long, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, lpOverlapped As OVERLAPPED) As Long
'new
Private Declare Function GetLastError Lib "kernel32" () As Long
Private Declare Function SetCommMask Lib "kernel32" (ByVal hFile As Long, ByVal dwEvtMask As Long) As Long
Private Declare Function ClearCommError Lib "kernel32" (ByVal hFile As Long, lpErrors As Long, ByVal l As Long) As Long
Private Declare Function WaitCommEvent Lib "kernel32" (ByVal hFile As Long, lpEvtMask As Long, lpOverlapped As OVERLAPPED) As Long
'Not used added back by jna to handle embedded nulls
'Private Declare Sub CopyMemory Lib "Kernel32" Alias "RtlMoveMemory" (dst As Any, src As Any, ByVal dwBytes As Long)
'My mod - byval
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal dst As Long, ByVal src As Long, ByVal dwBytes As Long)
'Added for strin manipulation routines
Private Declare Function lstrlenA Lib "kernel32" Alias "lstrlen" (ByVal lpString As String) As Long
Private Declare Function lstrlenW Lib "kernel32" Alias "lstrlen" (ByVal lpString As String) As Long
'added to get system messages when failed
Private Declare Function FormatMessage Lib "kernel32" Alias "FormatMessageA" (ByVal dwFlags As Long, lpSource As Any, ByVal dwMessageId As Long, ByVal dwLanguageId As Long, ByVal lpBuffer As String, ByVal nSize As Long, Arguments As Long) As Long
Private Const GENERIC_READ = &H80000000
Private Const GENERIC_WRITE = &H40000000
Private Const OPEN_EXISTING = 3
Private Const FILE_FLAG_OVERLAPPED = &H40000000
Private Const INVALID_HANDLE_VALUE = -1
Private Const GMEM_FIXED = &H0
Private Const ClassBufferSizes% = 1024
Private Const ERROR_IO_PENDING = 997 ' dderror
Private Const ERROR_OPERATION_ABORTED = 995
Private Const WAIT_TIMEOUT = &H102&
' GetCommModemStatus flags
Private Const MS_CTS_ON = &H10&
Private Const MS_DSR_ON = &H20&
Private Const MS_RING_ON = &H40&
Private Const MS_RLSD_ON = &H80&
' Error values
Private Const CLASS_NAME$ = "clsComm"
Private Const ERR_NOCOMMACCESS = 31010
Private Const ERR_UNINITIALIZED = 31011
Private Const ERR_MODEMSTATUS = 31012
Private Const ERR_READFAIL = 31013
'new
Private Const ERR_EVENTFAIL = 31014
'New
Private Const EV_RXCHAR = &H1
Private Const EV_RXFLAG = &H2
Private Const EV_TXEMPTY = &H4
Private Const EV_CTS = &H8
Private Const EV_DSR = &H10
Private Const EV_RLSD = &H20
Private Const EV_BREAK = &H40
Private Const EV_ERR = &H80
Private Const EV_RING = &H100
Private Const EV_PERR = &H200
Private Const EV_RX80FULL = &H400
Private Const EV_EVENT1 = &H800
Private Const EV_EVENT2 = &H1000
'New
Private Const CE_RXOVER = &H1
Private Const CE_OVERRUN = &H2
Private Const CE_RXPARITY = &H4
Private Const CE_FRAME = &H8
Private Const CE_BREAK = &H10
Private Const CE_TXFULL = &H100
' An empty string with a single null character
Private EmptyString As String * 1
Property Get DebugID() As Long
DebugID = mlngDebugID
End Property
Private Sub Class_Initialize()
Dim olnum%
'debugserial function is defined in ModRouter but not used in AisDecoder
mlngDebugID = DebugSerial
' Add a string entry to the global collection.
gcolDebug.Add "clsComm; DebugID=" _
& mlngDebugID, CStr(mlngDebugID)
Set DCB = New clsDCB
CurrentInputBuffer = GlobalAlloc(GMEM_FIXED, ClassBufferSizes + 1)
CurrentOutputBuffer = GlobalAlloc(GMEM_FIXED, ClassBufferSizes + 1)
CurrentEventMask = EV_ERR 'new
EmptyString = Chr$(0)
' Create event objects for the background transfer
For olnum = 0 To 2
overlaps(olnum).hEvent = CreateEvent(0, True, False, vbNullString)
Next olnum
End Sub
Private Sub Class_Terminate()
Dim olnum
' Close existing comm device
'Set DCB = Nothing ' Be sure DCB is free (Must close before CloseComm)
Call CloseComm
' Dump the event objects
'Debug.Print "Terminate " & inprogress(0) & inprogress(1) & inprogress(2)
For olnum = 0 To 2
Call CloseHandle(overlaps(olnum).hEvent)
inprogress(olnum) = False
Next olnum
Set DCB = Nothing ' Be sure DCB is free
Call GlobalFree(CurrentInputBuffer)
Call GlobalFree(CurrentOutputBuffer)
' Remove the string entry, so you know the object
' isn't around any more.
gcolDebug.Remove CStr(mlngDebugID)
End Sub
' Useful error routines
Private Sub DeviceNotOpenedError()
Call CloseComm
'Debug.Print "DeviceNotOpenedError"
Exit Sub
err.Raise vbObjectError + ERR_UNINITIALIZED, CLASS_NAME, "Communication Device is not open"
End Sub
Private Sub ModemStatusError()
err.Raise vbObjectError + ERR_MODEMSTATUS, CLASS_NAME, "GetCommModemStatus Failed"
End Sub
'-----------------------------------------------
' Timeout property access follows
'-----------------------------------------------
Public Property Get ReadIntervalTimeout() As Long
ReadIntervalTimeout = timeouts.ReadIntervalTimeout
End Property
Public Property Let ReadIntervalTimeout(vNewValue As Long)
timeouts.ReadIntervalTimeout = vNewValue
End Property
Public Property Get ReadTotalTimeoutMultiplier() As Long
ReadTotalTimeoutMultiplier = timeouts.ReadTotalTimeoutMultiplier
End Property
Public Property Let ReadTotalTimeoutMultiplier(vNewValue As Long)
timeouts.ReadTotalTimeoutMultiplier = vNewValue
End Property
Public Property Get ReadTotalTimeoutConstant() As Long
ReadTotalTimeoutConstant = timeouts.ReadTotalTimeoutConstant
End Property
Public Property Let ReadTotalTimeoutConstant(vNewValue As Long)
timeouts.ReadTotalTimeoutConstant = ReadTotalTimeoutConstant
End Property
Public Property Get WriteTotalTimeoutMultiplier() As Long
WriteTotalTimeoutMultiplier = timeouts.WriteTotalTimeoutMultiplier
End Property
Public Property Let WriteTotalTimeoutMultiplier(vNewValue As Long)
timeouts.WriteTotalTimeoutMultiplier = WriteTotalTimeoutMultiplier
End Property
Public Property Get WriteTotalTimeoutConstant() As Long
WriteTotalTimeoutConstant = timeouts.WriteTotalTimeoutConstant
End Property
Public Property Let WriteTotalTimeoutConstant(vNewValue As Long)
timeouts.WriteTotalTimeoutConstant = WriteTotalTimeoutConstant
End Property
' The device handle is read only
Public Property Get hCommDev() As Long
hCommDev = handle
End Property
' This property is read only
Public Property Get DeviceName() As String
DeviceName = DevName
End Property
'JNA Properties
Public Property Get State() As Long
State = Info.State
End Property
Public Property Let State(vNewValue As Long)
Info.State = vNewValue
End Property
Public Property Get hIndex() As Long
hIndex = Info.Hidx
End Property
Public Property Let hIndex(vNewValue As Long)
Info.Hidx = vNewValue
End Property
Public Property Get sIndex() As Long
sIndex = Info.Idx
End Property
Public Property Let sIndex(vNewValue As Long)
Info.Idx = vNewValue
End Property
Public Property Get Name() As String
Name = Info.hName
End Property
Public Property Let Name(vNewValue As String)
Info.hName = vNewValue
End Property
Public Property Get AutoBaudRate() As Boolean
AutoBaudRate = Info.AutoBaudRate
End Property
Public Property Let AutoBaudRate(vNewValue As Boolean)
Info.AutoBaudRate = vNewValue
End Property
Public Property Get PendingOutputLen() As Double
PendingOutputLen = Len(PendingOutput)
End Property
#If False Then
'complie error
Public Property Get errmsg() As String
errmsg = StatusBlock.Name
End Property
Public Property Let errmsg(vNewValue As String)
StatusBlock.errmsg = vNewValue
End Property
#End If
Public Property Get Closing() As Boolean
Closing = closeinprogress
End Property
Public Property Let Closing(vNewValue As Boolean)
Closing = vNewValue
End Property
Public Sub GetCommTimeouts()
' Is there any real need to report errors here?
If handle = 0 Then Exit Sub
Call apiGetCommTimeouts(handle, timeouts)
End Sub
Public Function SetCommTimeouts() As Long
If handle = 0 Then Exit Function ' Returns false
SetCommTimeouts = apiSetCommTimeouts(handle, timeouts) <> 0
End Function
' The main function for opening a comm device
' Receives device name (com?) and optionally the size of the internal input and output queues
Public Function OpenComm(CommDeviceName As String, Notify As Object, Optional cbInQueue, Optional cbOutQueue) As Long
Dim Idx As Long
' Close an existing port when reopening
WriteLog "Opening " & Mid$(CommDeviceName, 5), LogForm
On Error GoTo OpenComm_error
If handle <> 0 Then CloseComm
DevName = CommDeviceName
Set CallbackObject = Notify
handle = CreateFile(DevName, GENERIC_READ Or GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0)
If handle = INVALID_HANDLE_VALUE Then
'Exit Function
'MsgBox GetLastSystemError
err.Raise ERR_NOCOMMACCESS, CLASS_NAME, "Unable to open communications device"
End If
' If the input and output queue size is specified, set it now
If Not (IsMissing(cbInQueue) Or IsMissing(cbOutQueue)) Then
Call SetupComm(handle, cbInQueue, cbOutQueue)
Else
Call SetupComm(handle, 8192, 1024) 'old 4096
End If
' Ok, we've got the comm port. Initialize the timeouts
GetCommTimeouts
' Set some default timeouts
timeouts.ReadIntervalTimeout = 1
timeouts.ReadTotalTimeoutMultiplier = 0 'old 1
timeouts.ReadTotalTimeoutConstant = 10 'old 1
timeouts.WriteTotalTimeoutMultiplier = 1
timeouts.WriteTotalTimeoutConstant = 1
SetCommTimeouts
' Initialize the DCB to the current device parameters
Call DCB.GetCommState(Me)
Call SetCommMask(handle, CurrentEventMask) 'new
'Now set in StartInput
' StartInput '(Poll calls StartInput)
Exit Function
OpenComm_error:
'The error number is passed back to Commcfg & reported by it
OpenComm = err.Number 'return error
'Idx = CurrentSocket changed to sindex
Idx = sIndex
sockets(Idx).State = -1
sockets(Idx).errmsg = err.Description
If sockets(Idx).Hidx > 0 Then 'Its possible for the handler to have closed the socket
Set Comms(sockets(Idx).Hidx) = Nothing 'Remove the Comm Socket if closed
sockets(Idx).Hidx = -1
End If
'calling another function clears the err. messages
WriteLog "Open failed with error " & err.Number & " " & err.Description, LogForm
End Function 'will clear err.
' Close the comm port (Previous settings are retained)
Public Function CloseComm() As Long
' frmRouter.PollTimer.Enabled = False
Me.State = 0
' Me.sIndex = 0
'Clear the Pending buffer
PendingOutput = ""
'If Already closed, just exit
'Debug.Print "CloseComm Handle=" & handle
If handle > 0 Then
'Debug.Print "CloseComm " & inprogress(0) & inprogress(1) & inprogress(2)
Set CallbackObject = Nothing
Call CloseHandle(handle)
handle = 0
'Must set the socket state to 0 so that the reconnect timer '
'tries to reconnect, which it will only do if closed
'V33 SocketState should only be changed on return from this call
'There is a possibility the socket has been closed and this
'CloseComm has been called by the poll_timer
On Error Resume Next
sockets(Me.sIndex).State = 0 'v19
On Error GoTo 0
End If
' frmRouter.PollTimer.Enabled = True
WriteLog Mid$(DevName, 5) & " closed", LogForm
End Function
Public Function DisableComm()
Call CloseComm
End Function
#If False Then
Public Function EnableComm_notused()
Dim ret As Long
Dim Hidx As Long
On Error GoTo EnableComm_error
Call DisplayComms("EnableComm Start")
If handle <= 0 Then
ret = OpenComm("\\.\" & Name, frmRouter)
If ret <> 0 Then
err.Raise ret, "EnableComm", sockets(CurrentSocket).errmsg
End If
End If
Me.State = 1
Call DisplayComms("EnableComm Finish")
Exit Function
EnableComm_error:
'Stop
'This will clear the error On Error GoTo 0
Select Case err.Number
Case Is = 31010
err.Description = err.Description & vbCrLf & "Unable to open " & Name
' Set Comms(hIndex) = Nothing
Case Is = 380
err.Description = err.Description & vbCrLf & "Invalid Baud Rate " & DCB.BaudRate
' Set Comms(hIndex) = Nothing
Case Else
End Select
MsgBox err.Number & " " & err.Description, , "Enable Comm Error"
'Dont unload the form so that user has to cancel or enter a valid port
Stop
End Function
#End If
' This is another entry to retreive the comm state
' Note how it handles the problem of DCB needing the
' clsComm object parameter
Public Function GetCommState() As Long
If handle = 0 Then DeviceNotOpenedError
GetCommState = DCB.GetCommState(Me)
End Function
' This is another entry to retreive the comm state
' Note how it handles the problem of DCB needing the
' clsComm object parameter
Public Function SetCommState() As Long
If handle = 0 Then DeviceNotOpenedError
SetCommState = DCB.SetCommState(Me)
End Function
' Here are some easy functions to determine the current
' modem status
Public Property Get CTS_ON()
Dim modemstatus&
Dim Res&
If handle = 0 Then DeviceNotOpenedError
Res = GetCommModemStatus(handle, modemstatus)
If Res = 0 Then ModemStatusError
CTS_ON = (modemstatus And MS_CTS_ON) <> 0
End Property
Public Property Get DSR_ON()
Dim modemstatus&
Dim Res&
If handle = 0 Then DeviceNotOpenedError
Res = GetCommModemStatus(handle, modemstatus)
If Res = 0 Then ModemStatusError
DSR_ON = (modemstatus And MS_DSR_ON) <> 0
End Property
Public Property Get RING_ON()
Dim modemstatus&
Dim Res&
If handle = 0 Then DeviceNotOpenedError
Res = GetCommModemStatus(handle, modemstatus)
If Res = 0 Then ModemStatusError
RING_ON = (modemstatus And MS_RING_ON) <> 0
End Property
Public Property Get RLSD_ON()
Dim modemstatus&
Dim Res&
If handle = 0 Then DeviceNotOpenedError
Res = GetCommModemStatus(handle, modemstatus)
If Res = 0 Then ModemStatusError
RLSD_ON = (modemstatus And MS_RLSD_ON) <> 0
End Property
'To deal with the Serial Data Loss a better way would be to set a larger
'Output queue for serial comms and buffer the data in this queue
'rather than moving the data to comm output and then having to have
'a big stream buffer (PendingOutput) here
'There qould not be a need to poll for output
'as this would be governed by the Deque, which would alter its
'rate of output dependant on how quickly commoutput cleared the data
'If there was any poutput pending, it would not deque any more data
'If any data had to be discarded it could be in complete sentences
'by not queueing them in the first place.
'If commoutput returns false (if unsuccesfull) to Deque then the item would
'not be removed from the queue
Public Function CommOutput(outputdata As String) As Long
Dim bytestosend&
Dim Res&
Dim kb As String
Dim b() As Byte
Dim lpsz As Long
Dim ret As Long
Static lostcount As Long
State = 1 'Must be to be here
If handle = 0 Then DeviceNotOpenedError
'Output data can be blant because WriteComplete calls CommOutput
'with a null string to get CommOutput set to true
'Debug.Print Left$(outputdata, 3) & " " & Len(outputdata)
If outputdata <> "" Then
If PendingOutputLen + Len(outputdata) < MAX_COMM_OUTPUT_BUFFER_SIZE Then
PendingOutput = PendingOutput & outputdata
State = 1
Else
lostcount = lostcount + 1
'frmRouter.StatusBar.Panels(1).Text = lostcount
' State = 21
End If
End If
If inprogress(1) Then ' Write operation is in progress
CommOutput = True
Exit Function
End If
'Debug.Print Len(PendingOutput)
'With a reserve of global memory of 20 it the output gets truncated
'so ive allowed 40 (I think is still can at 40)
DoEvents
If Len(PendingOutput) < ClassBufferSizes - 100 Then
bytestosend = Len(PendingOutput)
Call lstrcpyToBuffer(CurrentOutputBuffer&, PendingOutput$ & vbNull, bytestosend + 1)
PendingOutput = ""
Else
bytestosend = ClassBufferSizes - 1 - 100
Call lstrcpyToBuffer(CurrentOutputBuffer&, Left$(PendingOutput$, bytestosend) & vbNull, bytestosend + 1)
PendingOutput = Mid$(PendingOutput, bytestosend + 1)
End If
If bytestosend > 0 Then
Res = WriteFile(handle, CurrentOutputBuffer, bytestosend, DataWritten, overlaps(1))
' Debug.Print bytestosend
If Res <> 0 Then
ProcessWriteComplete
CommOutput = True
Else
If err.LastDllError = ERROR_IO_PENDING Then
inprogress(1) = True
CommOutput = True
End If
End If
End If
End Function
' Restart the next output operation if necessary
Public Sub ProcessWriteComplete()
inprogress(1) = False
Call CommOutput("")
End Sub
' Called periodically
Public Sub PollWrite()
Dim Res&
If Not inprogress(1) Then Exit Sub
' Check the event
Res = WaitForSingleObject(overlaps(1).hEvent, 0)
' If not yet signaled, just exit
If Res = WAIT_TIMEOUT Then Exit Sub
' Data was written - Try writing any pending data
ProcessWriteComplete
End Sub
' This function enables or disables data transfer
Private Sub StartInput()
Dim Res&
Dim errors&
' Read already in progress
State = 1
If inprogress(0) Then
Exit Sub
End If
If handle = 0 Then
'Exit Sub
DeviceNotOpenedError
End If
Res = ReadFile(handle, CurrentInputBuffer, ClassBufferSizes, DataRead, overlaps(0))
If Res <> 0 Then
ProcessReadComplete
Else
Select Case err.LastDllError
Case Is = ERROR_IO_PENDING
inprogress(0) = True
'Debug.Print "pended read"
Case Is = ERROR_OPERATION_ABORTED
'This is normally caused by closing the socket but a
'read operation has become pending (from the polling)
' Debug.Print "read aborted"
'Clear the error and continue
Call ClearCommError(handle, errors, 0)
'Read will never be completed
inprogress(0) = False
Case Is > 0
'v19 MsgBox Err.LastDllError & vbCrLf & GetLastSystemError
'we appear to come here when weve stopped the input for a time
'We come here when we pull out a USB device
' Call CloseComm
'Exit Sub
' Err.Raise vbObjectError + ERR_READFAIL, CLASS_NAME, "Failure on Comm device read operation"
Call CloseComm
'Call OpenComm
'Set Comms(Me.Index) = Nothing
End Select
End If
End Sub
Public Sub PollRead()
Dim Res&
If Not inprogress(0) Then
StartInput
Exit Sub
End If
' Check the event
Res = WaitForSingleObject(overlaps(0).hEvent, 0)
' If not yet signaled, just exit
If Res = WAIT_TIMEOUT Then Exit Sub
' Data was written - Try writing any pending data
ProcessReadComplete
End Sub
Public Sub ProcessReadComplete()
Dim resstring$
Dim copied&
If inprogress(0) Then ' Was overlapped
DataRead = overlaps(0).InternalHigh
inprogress(0) = False
End If
If DataRead <> 0 Then
'Debug.Print "Read " & "(" & Me.Name & ") " & DataRead & " bytes"
'Debug.Print "Read " & inprogress(0) & inprogress(1) & inprogress(2)
'Create Buffer (resstring) of reqired length full of nulls
resstring$ = String$(DataRead + 1, 0)
'The return value is the address of a temp buffer
'which is no loner valid with vb
' copied = lstrcpyFromBuffer(resstring, CurrentInputBuffer, DataRead + 1)
Call lstrcpyFromBuffer(resstring, CurrentInputBuffer, DataRead + 1)
Call CommBuffer(resstring)
End If
End Sub
'This is event driven when data is received
'The Data MUST be buffered in this socket before being
'sent to be displayed or forwarded
Private Sub CommBuffer(commdata As String)
Dim cpos% '
Dim Is8BitAscii As Boolean 'NMEA should only be 7 bit ascii
If Left$(commdata$, 1) = Chr$(10) Then
'Debug.Print "LF commdata"
End If
If Left$(PartSentence, 1) = Chr$(10) Then
'Debug.Print "LF PartSentence"
End If
'Add on any previously received partial sentence
commdata = PartSentence & commdata
PartSentence = ""
If commdata <> "" Then
'Debug.Print thiscomm.DeviceName
'commdata$ always has a NULL appended by CommRead
'MsgBox thiscomm.ChrCtrl(commdata$), , "CommInput (commdata$)"
' txtTerm.SelStart = Len(txtTerm.Text)
' Substitute the CR with a CRLF pair, dump the LF
Do Until Len(commdata$) = 0
cpos% = InStr(commdata$, Chr$(13))
If cpos% > 0 Then 'Complete sentence
' txtTerm.SelText = Left$(commdata$, cpos% - 1) & vbCrLf
'chrctrl added to try and see if any non ascii characters are being output
'If Left$(commdata$, 1) = Chr$(10) Then
' Call CommRcv(thiscomm.ChrCtrl(Left$(commdata$, cpos% - 1)) & vbCrLf, thiscomm.Index)
'End If
If Not (CallbackObject Is Nothing) Then
Call CallbackObject.CommRcv(Left$(commdata$, cpos% - 1) & vbCrLf, hIndex)
End If
'Call back Call CommRcv(Left$(commdata$, cpos% - 1) & vbCrLf, Index)
commdata$ = Mid$(commdata$, cpos% + 1)
cpos% = InStr(commdata$, Chr$(10))
If cpos% > 0 Then
commdata$ = Mid$(commdata$, cpos% + 1)
End If
Else 'No CR
cpos% = InStr(commdata$, Chr$(10))
'We probably ought to replace a LF Null with CRLF
If cpos% > 0 Then 'But has LF, Keep LF + NULL
commdata$ = Mid$(commdata$, cpos% + 1)
Else 'No CR or LF but will have last NULL
'Output the whole buffer when a terminating Null
' txtTerm.SelText = commdata$ 'includes last NULL
'except the Null
'save the partial sentence for this socket
PartSentence = PartSentence _
& Left$(commdata$, Len(commdata$) - 1)
' Call CommRcv(Left$(commdata$, Len(commdata$) - 1), thiscomm.Index)
'Code to AutoBaudRate
If AutoBaudRate = True Then
'Check to see if bit(7) is 1 (chrno >= 128) - NMEA is only 7 bit
For cpos = 1 To Len(PartSentence)
If Asc(Mid$(PartSentence, cpos, 1)) >= 128 Then
Is8BitAscii = True
End If
Next cpos
If Is8BitAscii Then
Call RetryBaudRate
End If
End If
commdata$ = ""
End If
End If
Loop
' If Len(txtTerm.Text) > 4096 Then
' txtTerm.Text = Right$(txtTerm.Text, 2048)
' End If
End If
End Sub
#If False Then
Private Sub StartEventWatch() 'new
Dim Res&
If inprogress(2) Then Exit Sub
If handle = 0 Then DeviceNotOpenedError
EventResults = 0
Res = WaitCommEvent(handle, EventResults, overlaps(2))
If Res <> 0 Then
ProcessEventComplete
Else
If GetLastError() = ERROR_IO_PENDING Then
inprogress(2) = True
#If DEBUGMODE Then
' Debug.Print "pended event"
#End If
Else
err.Raise vbObjectError + ERR_EVENTFAIL, CLASS_NAME, "Failure on Comm device event test operation"
End If
End If
End Sub
Private Sub ProcessEventComplete() 'new
Dim errors&
If inprogress(2) Then
inprogress(2) = False
End If
If EventResults <> 0 Then
#If DEBUGMODE Then
' Debug.Print "Event value " & Hex$(EventResults)
#End If
If Not (CallbackObject Is Nothing) Then
Call ClearCommError(handle, errors, 0)
' If (errors And CE_RXOVER) <> 0 Then Call CallbackObject.CommEvent(Me, "Receive Queue Full Error")
' If (errors And CE_OVERRUN) <> 0 Then Call CallbackObject.CommEvent(Me, "Receive Overrun Error")
' If (errors And CE_RXPARITY) <> 0 Then Call CallbackObject.CommEvent(Me, "Receive Parity Error")
' If (errors And CE_FRAME) <> 0 Then Call CallbackObject.CommEvent(Me, "Frame Error")
' If (errors And CE_BREAK) <> 0 Then Call CallbackObject.CommEvent(Me, "Break Detected")
' If (errors And CE_TXFULL) <> 0 Then Call CallbackObject.CommEvent(Me, "Transmit Queue Full")
End If
End If
End Sub
Private Sub PollEvent() 'new
Dim Res&
If Not inprogress(2) Then
StartEventWatch
Exit Sub
End If
Res = WaitForSingleObject(overlaps(2).hEvent, 0)
If Res = WAIT_TIMEOUT Then Exit Sub
ProcessEventComplete
End Sub
#End If
Public Sub PollClose()
Call CloseComm
End Sub
' Test results on all background processes
Public Function Poll() As Boolean
If Not Closing Then
If handle > 0 Then
'frmRouter.StatusBar.Panels(2).Text = PendingOutputLen
PollWrite
PollRead
Poll = True
Else
'stop
End If
' PollEvent 'new
Else
CloseComm
End If
'frmRouter.StatusBar.Panels(3).Text = State
End Function
Public Function ChrCtrl(ByRef Line As String) As String
Dim kb As String
Dim Outbuf As String
Dim i As Long
Dim Chrno As Long
Dim b() As Byte
b = StrConv(Line, vbFromUnicode)
' If Not b Is Nothing Then
For i = 0 To UBound(b)
If b(i) >= 32 And b(i) <= 127 Then
Outbuf = Outbuf & Chr$(b(i))
Else
Outbuf = Outbuf & "<" & b(i) & ">"
End If
Next i
' Else
' DpyBuf = "<empty>"
' End If
ChrCtrl = Outbuf
End Function
Function BSTRtoLPWSTR(sBSTR As String, b() As Byte, lpwsz As Long) As Long
' Input: a nonempty BSTR string
' Input: **undimensioned** byte array b()
' Output: Fills byte array b() with Unicode char string from sBSTR
' Output: Fills lpwsz with a pointer to b() array
' Returns byte count, not including terminating 2-byte Unicode null character
' Original BSTR is not affected
Dim cBytes As Long
cBytes = LenB(sBSTR)
' ReDim array, with space for terminating null
ReDim b(1 To cBytes + 2) As Byte
' Point to BSTR char array
lpwsz = StrPtr(sBSTR)
' Copy the array
CopyMemory b(1), ByVal lpwsz, cBytes + 2
' Point lpsz to new array
lpwsz = VarPtr(b(1))
' Return byte count
BSTRtoLPWSTR = cBytes
End Function
'The function to convert a BSTR to an LPSTR is similar, but requires a translation from Unicode to ANSI first:
Function BSTRtoLPSTR(sBSTR As String, b() As Byte, lpsz As Long) As Long
' Input: a nonempty BSTR string
' Input: **undimensioned** byte array b()
' Output: Fills byte array b() with ANSI char string
' Output: Fills lpsz with a pointer to b() array
' Returns byte count, not including terminating null
' Original BSTR is not affected
Dim cBytes As Long
Dim sABSTR As String
cBytes = LenB(sBSTR)
' ReDim array, with space for terminating null
ReDim b(1 To cBytes + 2) As Byte
' Convert to ANSI
sABSTR = StrConv(sBSTR, vbFromUnicode)
' Point to BSTR char array
lpsz = StrPtr(sABSTR)
' Copy the array
CopyMemory b(1), ByVal lpsz, cBytes + 2
' Point lpsz to new array
lpsz = VarPtr(b(1))
' Return byte count
BSTRtoLPSTR = cBytes
End Function
Function LPWSTRtoBSTR(ByVal lpwsz As Long) As String
' Input: a valid LPWSTR pointer lpwsz
' Return: a sBSTR with the same character array
Dim cChars As Long
' Get number of characters in lpwsz
cChars = lstrlenW(lpwsz)
' Initialize string
LPWSTRtoBSTR = String$(cChars, 0)
' Copy string
CopyMemory ByVal StrPtr(LPWSTRtoBSTR), ByVal lpwsz, cChars * 2
End Function
Function LPSTRtoBSTR(ByVal lpsz As Long) As String
' Input: a valid LPSTR pointer lpsz
' Output: a sBSTR with the same character array
Dim cChars As Long
' Get number of characters in lpsz
cChars = lstrlenA(lpsz)
' Initialize string
LPSTRtoBSTR = String$(cChars, 0)
' Copy string
CopyMemory ByVal StrPtr(LPSTRtoBSTR), ByVal lpsz, cChars
' Convert to Unicode
LPSTRtoBSTR = Trim(StrConv(LPSTRtoBSTR, vbUnicode))
End Function
Public Function GetLastSystemError() As String
Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000
Const FORMAT_MESSAGE_IGNORE_INSERTS = &H200
Dim sError As String * 500 '\\ Preinitilise a string buffer to put any error message into
Dim lErrNum As Long
Dim lErrMsg As Long
Dim lCount As Long
lErrNum = err.LastDllError
lErrMsg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, ByVal 0&, lErrNum, 0, sError, Len(sError), 0)