Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Knowing the "End of File" has been rerached.

Status
Not open for further replies.

THOMASNG

Technical User
May 3, 2002
254
US
I've been trying to use VB6 and MSComm to transfer files between two computers. However, if (on the receiving PC), I
set:

MSComm1.InputLen = 0
MSComm1.EOFEnable = True

The first blank line is (mis)interpeted as an EOF, and I
simply just get the title line of the file, not the entire
file.
I've been careful to empty the receive buffer.

Any ideas?
 
is it possible to retrieve LOF(1) and use it instead?
"Length Of File"

(I am not too familiar with MSComm... Never had a reason to use it.. So Far) Sometimes... the BASIC things in life are the best...
cheers.gif

or at least the most fun ;-)
-Josh Stribling
 
Are you sure that it is the EOF character that raised the event and not some other comEvent? Also, if it is not a plain text file, but a binary file of some kind, the danger exists that somewhere along the line an EOF character will be found.

Anyway, the MSCOMM control is not really designed for file transfers. If you really want to transfer files between PC's over the comm ports you might want to use the PDQ Comm control, which has a file transfer protocol, or write your own protocol for transfering files with the MSCOMM control. In both cases you can have programatically control over the validity of the received bytes.
Rum am Morgen vorkommt Kummer und Sorgen... Cheers !

Mr. Rum
 
I realize that MSComm control is not intended for file transfer. However, I've found that by preceding the file
with a non-alphanumeric (but still visible) character, i.e.
"*", and emding the file with a different non-alphanumeric
(but still visible character), such as "!" - then asking the
receiving computer to check for BOTH, works fine.
 
Aren't you afraid that half way through the file an "!" will be found and the receiving computer stops too early?
Rum am Morgen vorkommt Kummer und Sorgen... Cheers !

Mr. Rum
 
The symbol could be anything visible. I simply used
"!" and "*" because I KNOW neither of them will be in the file.
 
Well, you are well on the way to writing your own protocol then...
 
It almost works perfectly on the receiving computer (it's always worked on the transmitting computer, which I tested using HYPERTERMINAL.)

However, there's still one annoying bug. I have to (for
some reason) place a MsgBox "" right after I receive the first part of the transmission. After I click "OK" on the
its display, the rest of the file transfer proceeds is
planned. If I try commenting out that line, my receiving
software stops working, but when I restore the line it
resumes working.
My hypothesis is that I'm setting/restting some necessary
variable when I "click" OK.
 
I do not know exactly what you mean by "the first part of the transmission". are you transmitting one file in multiple sessions ?

Could you post the code you are using to receive the bits and bytes here, maybe it will be clearer then...
Rum am Morgen vorkommt Kummer und Sorgen... Cheers !

Mr. Rum
 
Is there a really good reason why you're not using a more appropriate control?

If it's a straightforward textfile, what's wrong with winsock & udp?
________________________________________________________________
If you want to get the best response to a question, please check out FAQ222-2244 first

'People who live in windowed environments shouldn't cast pointers.'
 
NOTE 1 : This code is still incomplete. For instance,
CONTAINER.TXT, has not been dealt with yet.
NOTE 2 : The code uses VB6.
NOTE 3 : Com1 is currently being used because we only have
a suitable RF modem that uses a serial port. We
keep bitching, but the bosses don't respond.

******************

Trying to eliminate the line, MsgBox "+".

******************

The code on the receiving computer is :("global_str_message " is a global string variable define in its own module)

' ******************************
' Saved as InDataReceiver.
'
' Uses the InData Systems RF modem, on the
' MICROSLATE's Com1 serial port, to receive
' the updated, revised versions of OWNER.TXT
' and CONTAINER.TXT.
' ******************************
' General | Declarations

Private fso As Object
Private ts As Object

Private FileName As String
Private IOMode As Integer
Private CreateFile As Boolean
Private Format As Integer

Private DeleteROMFiles As Boolean

' ****************************************
' Defining framing variables.

Private str_start, str_end As String

' ****************************************
' ****************************************

Private Sub Form_Load()

' *******************************
' Com1 will be represented by "MSComm1".
' InData Systems has factory preset their modem to:
' 9,600 bps, 8-N-1, Xon/Xoff handshaking.

MSComm1.CommPort = 1
MSComm1.Settings = "9600, N, 8, 1"
MSComm1.Handshaking = comXOnXoff

MSComm1.RThreshold = 1
' Required activate the OnComm event.

MSComm1.InputLen = 0
' Asked to read the entire contents of the receive buffer.

MSComm1.EOFEnable = True

MSComm1.PortOpen = True

' ***********************
' If Com1 can't be opened...

If Err Then
MsgBox "Com1 is not available."
End If

' ***********************
' Initializing the buffer string variable to empty.

global_str_message = ""

' ********************************
' Doing most of the preparations required to create/
' write to any textfile.
' Copied from Scott Warner's text, Chapter 10.

IOMode = 8
' IoMode = 1 Open file for reading ONLY.
' IOMode = 8 Open file for writing ONLY

CreateFile = True
' CreateFile = True : Create a file if one doesn't already exist.
' CreateFile = False : DON'T create a file if one doesn't already exist.

Format = 0
' Format = 0 : ASCII (plain) text
' Format = -1 : UNICODE text
' Format = -2 : System's default text format

' ******************************************
' ******************************************
' For OWNER.TXT:

FileName = "C:\owner.txt"

Set fso = CreateObject("Scripting.FileSystemObject")

Set ts = _
fso_OpenTextFile(FileName, IOMode, CreateFile, Format)

' Immediately closing it.
' This ensures that there's ALWAYS a text file of the
' specified name for DeleteFile to act on.

ts.Close

' ****************
fso.DeleteFile FileName

' ****************
' Releasing any memory associated with the file.
Set fso = Nothing

' MsgBox "Pre-existing OWNER.TXT deleted."

' ******************************************
' ******************************************
' Similarly, for CONTAINER.TXT:

FileName = "C:\container.txt"

Set fso = CreateObject("Scripting.FileSystemObject")

Set ts = _
fso_OpenTextFile(FileName, IOMode, CreateFile, Format)

' Immediately closing it.
' This ensures that there's ALWAYS a text file of the
' specified name for DeleteFile to act on.

ts.Close

' ****************
fso.DeleteFile FileName

' ****************
' Releasing any memory associated with the file.
Set fso = Nothing

End Sub


' *************************************

Private Sub MSComm1_OnComm()

Dim long_stringlength As Long
Dim str_length, str_textfile, str_owner, str_container As String
Dim int_start_text_file As Integer
Dim str_EOF As String

str_start = "!"
str_end = "*"

' **********************
' Reading the entire transmitted file contents,
' including those stored in the receive buffer.

global_str_message = MSComm1.Input

MsgBox "+"

Do While (MSComm1.InBufferCount > 0)
global_str_message = global_str_message & MSComm1.Input
Loop

If (InStr(global_str_message, str_start) = 0) Then
Exit Sub
End If

If (InStr(global_str_message, str_end) = 0) Then
Exit Sub
End If

If (InStr(global_str_message, "OWNER") > O) Then
int_textfile = 1
MsgBox "1. The transmitted file is : " & _
global_str_message
End If

MsgBox "2. The transmitted file is : " & _
global_str_message

' *********************
' At this point, "global_str_message" must contain all of
' OWNER.TXT, with the framing characters added,
' OR all of CONTAINER.TXT, with the framing characters
' added.

If (int_textfile = 1) Then
' If a standard OWNER.TXT file, do the following block
' of statements...

str_owner = global_str_message

' ***********************
' Opening the file.

FileName = "C:\owner.txt"
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = _
fso_OpenTextFile(FileName, IOMode, CreateFile, Format)

' Writing the contents to the new OWNER.TXT file.

ts.Write (str_owner)

' ********************
' Closing the OWNER.TXT file.

ts.Close

' ****************
' Releasing any memory associated with the file.
Set fso = Nothing

' ********************
' Resetting the buffer string variable to empty.

global_str_message = ""

Else
' Else, it must be a standard CONTAINER.TXT file...

Exit Sub 'Temporary.

End If

End Sub

' *******************************
Private Sub cmd_Transmit_Click()

Dim str_output As String

str_output = "AAAAAAAAAAAAAA"

MSComm1.Output = str_output

End Sub
 
Also note, the file size can vary from tiny to very, very large. As a result, the file size may exceed the size
of the receive buffer.
 
To answer some of your questions:
1) Why aren't we using WINSOCK?
Basically, we want a wireless connection that has
a much greater range. While we do have room for
a second PCMIIA card, our network administrator
doubts one with a greater range is available on
the market. So we have to use one that connects to
a serial port (thus MsComm).
2) As I thought I mentioned earlier, the size of the file
being transferred will vary greatly. So making the
receive buffer "large enough to contain the entire
file" is not a realistic option.

I hope this helps.
 
First of all, I do not see any specific event handlers in the OnComm method. This method is not only called when the receivebuffer threshold has been reached. It is also called other times, like a change in the CTS line or something like that.

I once had the same problem as you now have, but it was financial information that I had to transmit, so it was crucial that all bytes arrived, without any corruption. I also only had the MSCOMM control at hand, so I wrote a protocol of my own (which, until so far, seems to work fine):


'Sender:


Private Function SendFileByModem(ByVal vsFile As String, ByVal vsCmd As String, Optional ByVal vbKillFile As Boolean = False) As Boolean
'Send a file to a remote system.
'
'vsFile : Full path and filename of source file.
'vsCmd : Command to be send to remote system, befor starting upload.
'vbKillFile: Specifies whether or not to kill the original file.
'
'Since we do not have a protocol available, made one up:
'* A header prefixes the contents of the file:
' - 1st byte represents size of this header (including this byte and checkbyte).
' - 2nd byte represents size of filename.
' - Then follow X characters which specify the name of the file being transfered.
' - Then follow X characters (1st byte - 1(2nd byte) - number of characters of the name - CheckByte) which represent the size of the file.
' - Then follows the checkbyte.

'* Then follow X transfers of 255 characters each (or less), including the prefix byte (which represent the number of characters to follow) and including checkbyte (last character).

'* When a block has been transfered, a footer follows:

' - 1st byte is size of footer (always 18).
' - Following bytes are ASCII characters "#XFER COMPLETED#".
' - Again a checkbyte.

'* The checkbyte is the exlusive or of all bytes of the block, including the length byte.

'* After EVERY block that has been received, the receiver has to answer with "XFEROK" & vbCr.

Dim lbOk As Boolean
Dim lnFileNr As Integer, lnBlockSize As Integer
Dim lnFileSize As Long, lnFilePointer As Long
Dim lsFile As String, lsData As String
Dim lobjMdm As MSComm

Set lobjMdm = frmMain.Modem

lbOk = True

ResetModemIdleTimer
lobjMdm.OutBufferCount = 0
lobjMdm.Output = vsCmd 'Tell recevier that we want to send a file.

If Synchronize() <> 0 Then 'Wait until receiver is ready.
lbOk = False
Else
lnFileSize = FileLen(vsFile)
lnFilePointer = 1
If lnFileSize > 0 Then 'Only if there is actually something to send.
'Send header:
lsData = Chr(1 + Len(CStr(lnFileSize)) + 1 + Len(RemovePath(vsFile)) + 1) 'Size of header.
lsData = lsData & Chr(Len(RemovePath(vsFile))) 'Length of filename.
lsData = lsData & RemovePath(vsFile) 'Filename.
lsData = lsData & CStr(lnFileSize) 'Filesize.
lsData = lsData & GetCheckByte(lsData) 'Add checkbyte.
lobjMdm.Output = lsData
If WaitReceiver() Then
'Send data:
lnFileNr = FreeFile
Open Trim$(vsFile) For Binary As #lnFileNr
While lnFilePointer <= lnFileSize
ResetModemIdleTimer
If lnFilePointer + 252 <= lnFileSize Then lnBlockSize = 253 Else lnBlockSize = lnFileSize - lnFilePointer + 1
lsData = Space(lnBlockSize) 'Number of bytes to send.
Get #lnFileNr, lnFilePointer, lsData
lnFilePointer = lnFilePointer + lnBlockSize
frmMain.MainModemStatus = &quot;Sending &quot; & RemovePath(vsFile) & &quot; (&quot; & Format(CSng(CSng(lnFilePointer - 1) / CSng(lnFileSize)) * 100, &quot;0.00&quot;) & &quot; %)&quot;
lsData = Chr(Len(lsData) + 2) & lsData 'Length of this block and filedata.
lsData = lsData & GetCheckByte(lsData) 'Checkbyte.
lobjMdm.Output = lsData
If Not WaitReceiver() Then
lnFilePointer = lnFileSize 'If not ok, then force halt.
lbOk = False
End If
Wend
Close #lnFileNr
If lbOk Then 'Ok, file has been send correctly.
'Send footer:
lsData = Chr(18) 'Length is always 18.
lsData = lsData & &quot;#XFER COMPLETED#&quot;
lsData = lsData & GetCheckByte(lsData)
lobjMdm.Output = lsData
If Not WaitReceiver() Then lbOk = False
End If
End If
End If
End If

If lbOk And vbKillFile Then Kill vsFile

SendFileByModem = lbOk
End Function


Private Function GetCheckByte(ByVal lsData As String) As String

Dim lnPos As Integer, lnByte As Integer

For lnPos = 1 To Len(lsData)
lnByte = lnByte Xor Asc(Mid(lsData, lnPos, 1))
Next lnPos

GetCheckByte = Chr(lnByte)
End Function


//
//The next method is a bit sloppy, but this is because it was an ancient system and it had to stay compatible....

Function Synchronize() As Integer

Dim n As Integer
Dim n2 As Single, e As Single
Dim strModem As String

Synchronize = 0

'Wait for the receiver to become ready...
n2 = Timer
e = n2 + 300
If e >= 86400 Then
e = e - 86400
End If
Do
If Not frmMain.Modem.CDHolding Then Synchronize = -1 'No carrier
If frmMain.Modem.InBufferCount = 7 Then 'Could be it...
strModem = UCase(StripControl(frmMain.Modem.Input))
If strModem = &quot;RCV14&quot; Then Exit Function 'Yes it is.
End If
If frmMain.Modem.InBufferCount = 8 Then 'Could be it...
strModem = frmMain.Modem.Input
strModem = UCase(StripControl(Mid$(strModem, 3)))
If strModem = &quot;RCV14&quot; Then Exit Function 'yes it is.
End If
DoEvents
Loop While e > Timer And Synchronize = 0

Synchronize = IIf((Synchronize = 0), -5, Synchronize) 'timeout or other problem ?
End Function


//
//Receiver (also a bit sloppy since this is really ancient (vb3):

Private Sub Modem_OnComm()

Dim lnFileNr As Integer

Select Case Modem.CommEvent
Case 3 'change in CTS
If DebugFlag Then Display Bonvenster, 0, &quot;CTS &quot; & Modem.CTSHolding
Case 4 'change in DSR
If DebugFlag Then Display Bonvenster, 0, &quot;DSR &quot; & Modem.DSRHolding
Case 5 'change in CD
If DebugFlag Then Display Bonvenster, 0, &quot;CD &quot; & Modem.CDHolding
If Not Modem.CDHolding Then 'No carrier
ModemIdle.Enabled = False 'stop modem idle timer.
Hangup 0 'autoanswer on
lnFileNr = FreeFile
Open Trim$(SysOptions.AppPath) & Trim$(SysOptions.AppName) & &quot;.ERR&quot; For Append As #lnFileNr
Print #lnFileNr, &quot;****** Connection broken: &quot; & Date$ & &quot; / &quot; & Time$
Close #lnFileNr
Else
ModemBusy = True
End If
Case 6 'ringing
If SysOptions.DatacomStatus = 2 Then 'auto answer allowed
Modem.InBufferCount = 0
Display Statusvenster, DatacomStatusregel, &quot;Modem: &quot; & TABCHAR & &quot;Answering&quot;
Modem.Output = &quot;ATA&quot; & Chr(13) 'beantwoord de oproep
lnFileNr = FreeFile
Open Trim$(SysOptions.AppPath) & Trim$(SysOptions.AppName) & &quot;.ERR&quot; For Append As #lnFileNr
Print #lnFileNr, &quot;****** Start datacom: &quot; & Date$ & &quot; / &quot; & Time$
Close #lnFileNr
gblnDatacom = True
ModemIdle.Interval = 60000
ModemIdle.Enabled = True 'start modem idle timer.
DoModemComm
End If
End Select
End Sub


Private Sub DoModemComm()
Dim lsRx As String, lsCmd As String

Do
DoEvents
If Modem.InBufferCount > 0 Then
'Reset idle timer:
ResetModemIdleTimer
'Add to data buffer:
lsRx = lsRx & Modem.Input
End If
If Len(lsRx) > 0 Then
If InStr(lsRx, Chr(13)) > 0 Then 'A command has been received.
lsCmd = StripControl(Trim$(Left$(lsRx, InStr(lsRx, Chr(13)) - 1)))
lsRx = Mid$(lsRx, InStr(lsRx, Chr(13)) + 1)
If DebugFlag = True And Len(lsCmd) > 0 Then Display BonVenster, 0, Left$(lsCmd, 20)
Debug.Print &quot;<<Modem<< &quot; & lsCmd
If Len(lsCmd) > 0 Then DoModemCmd lsCmd 'this is where a huge select case switches os to the next method
lsCmd = &quot;&quot;
End If
End If
Loop While gblnDataCom
End Sub


Private Sub DoReceiveImport()
Dim lnFileNr As Integer


Modem.Output = Chr$(13) & &quot;RCV14&quot; & Chr$(13) 'We're ready to receive data now.

While Modem.OutBufferCount > 0
DoEvents
Wend

GetFileByModem Trim$(SysOptions.AppPath) & &quot;\import&quot;, &quot;&quot;
End Sub



Private Function GetFileByModem(ByVal vsDestPath As String, ByVal vsFileName As String) As Integer
Dim lnBlockSize As Integer, lnCheckSum As Integer, lnPos As Integer, lnFileNr As Integer
Dim lnFileSize As Long, lnReceived As Long
Dim lsData As String, lsOrgFile As String, lsDestFile As String, lsTemp As String

GetFileByModem = False
If Right(vsDestPath, 1) <> &quot;\&quot; Then vsDestPath = vsDestPath & &quot;\&quot;

Modem.OutBufferCount = 0


ResetModemIdleTimer

'First the header:
lnBlockSize = -1
lsData = &quot;&quot;
While (lnBlockSize <> Len(lsData)) And (ModemIdle.Enabled = True)
If Modem.InBufferCount > 0 Then
ResetModemIdleTimer
lsData = lsData & Modem.Input
If lnBlockSize = -1 Then lnBlockSize = Asc(Left(lsData, 1))
DoEvents
End If
Wend
'Process header:
If Not IsCheckSumOk(lsData) Then Exit Function
lsOrgFile = Mid(lsData, 3, Asc(Mid(lsData, 2, 1))) 'Original filename.
lnFileSize = Val(Mid(lsData, 3 + Len(lsOrgFile), Len(lsData) - 3 - Len(lsOrgFile))) 'Size in bytes of the file.
lsDestFile = vsDestPath & CStr(IIf(vsFileName <> &quot;&quot;, vsFileName, lsOrgFile)) 'Destination.
Modem.Output = &quot;XFEROK&quot; & Chr(13) 'Ready to receive next part.
'Get file data and footer:
lnFileNr = FreeFile
lnReceived = 0
If CheckFileExists(lsDestFile) Then Kill lsDestFile
Open lsDestFile For Binary As #lnFileNr
Do While True
lnBlockSize = -1
lsData = &quot;&quot;
While (lnBlockSize <> Len(lsData)) And (ModemIdle.Enabled = True)
If Modem.InBufferCount > 0 Then
ResetModemIdleTimer
lsData = lsData & Modem.Input
If lnBlockSize = -1 Then lnBlockSize = Asc(Left(lsData, 1))
DoEvents
End If
Wend
'Process block:
If Not IsCheckSumOk(lsData) Then
Close #lnFileNr
lnFileNr = -1 'Force halt.
On Error Resume Next
Kill lsDestFile
On Error GoTo 0
Exit Do
End If
If lnBlockSize = 18 Then 'This might be the footer.
Select Case Mid(lsData, 2, 16)
Case &quot;#XFER COMPLETED#&quot;: 'Done.
Close #lnFileNr
lnFileNr = -1 'Force halt.
Exit Do
Case Else: 'Ordinary data block.
lnReceived = lnReceived + Len(lsData) - 2
lsTemp = CStr(Mid(lsData, 2, 16))
Put #lnFileNr, , lsTemp
End Select
Else
lnReceived = lnReceived + Len(lsData) - 2
lsTemp = CStr(Mid(lsData, 2, Len(lsData) - 2))
Put #lnFileNr, , lsTemp
End If
Display StatusVenster, DatacomStatusRegel, &quot;Modem:&quot; & TabChar & &quot;<< &quot; & lsOrgFile & &quot; (&quot; & Format(CSng((CSng(lnReceived) / CSng(lnFileSize))) * 100, &quot;0.00&quot;) & &quot;%)&quot;
Modem.Output = &quot;XFEROK&quot; & Chr(13) 'Ready for next block.
Loop
Modem.Output = &quot;XFEROK&quot; & Chr(13) 'Done.

GetFileByModem = ModemIdle.Enabled
End Function



Private Function IsCheckSumOk(ByVal vsData As String) As Integer
Dim lnPos As Integer, lnCheck As Integer, lnFileNr As Integer

For lnPos = 1 To Len(vsData) - 1
lnCheck = lnCheck Xor Asc(Mid(vsData, lnPos, 1))
Next lnPos

If Not (lnCheck = Asc(Right(vsData, 1))) Then
lnFileNr = FreeFile
Open Trim$(SysOptions.AppPath) & Trim$(SysOptions.AppName) & &quot;.ERR&quot; For Append As lnFileNr
Print #lnFileNr, &quot;!!!!!! Checksum error: &quot; & Date$ & &quot;/ &quot; & Time$
Close #lnFileNr
IsCheckSumOk = False
Else
IsCheckSumOk = True
End If
End Function




As you can see, we did more than just sending files and also we used a modem. But stripping those parts should lead to a fairly stable way of transmitting data.

Rum am Morgen vorkommt Kummer und Sorgen... Cheers !

Mr. Rum
 
Problem solved. What learned was:
1) Prior to sending anything through the Comm port,
the two MSComm's (transmitting PC, receiving PC)
exchange a pair of very short messages to verify
that there is something on the other end. These
test signals can inadvertently trigger any
MSComm_OnComm event handler.
2) To properly transfer a text file via MSComm, you
need to both PRECEDE IT and FOLLOW IT, by a
character (which must be different from each other).
3) The receive buffer is so tiny that it is almost
always saturated by a text file (even a tiny one).
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top