2009-04-24 38 views
1

我的目標是當用戶註銷或關閉電腦時,讓此程序發送註銷命令。爲什麼在Form_QueryUnload的上下文中,我的winsock控件在vb6中不能正確運行?

該程序被連接到經由使用一個Winsock對象TCP套接字服務器應用程序。撥打singleSock.SendData "quit" & vbCrLf只是一種註銷方式。我將開始使用Wireshark捕獲數據,但我想知道我是否試圖做一些根本性錯誤。奇怪的是,如果我將Cancel設置爲True,並且允許一個運行時執行logout命令的計時器,然後再調用另一個unload,它可以工作,但是在測試此配置時(不同代碼),這會阻止用戶第一次登出。他們必須發起註銷,它什麼都不做,然後他們再次註銷,並且我的程序在那個時候沒有了。另外奇怪的是,在Vista中,註銷之後會短暫顯示一個屏幕,說我的程序阻止了註銷。我的大部分部署都在XP上,它有兩個註銷問題。

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer) 

    If UnloadMode = vbFormControlMenu Then 
     Me.WindowState = vbMinimized 
     Cancel = True 
    Else 

     If SHUTDOWN_FLAG = True Then 
     Cancel = False 
     Else 
     Cancel = True 
     SHUTDOWN_FLAG = True 
     End If 
     tmrSocket.Enabled = False 
     SHUTDOWN_FLAG = True 
     Sleep (1000) 
     singleSock.SendData "quit" & vbCrLf 

     Call pUnSubClass 
     'If singleSock.state <> sckConnected Then 
     ' singleSock.Close 
     ' tmrSocket.Enabled = False 
     ' LogThis "tmrSocket turned off" 
     'End If 
     DoEvents 
    End If 

End Sub 

回答

1

您並不等待Winsock控件實際發送「退出」消息。 SendData方法是異步的:它可以在數據實際上通過網絡發送之前返回。數據在本地緩存在您的機器上,稍後由網絡驅動程序發送。

在你的情況,你要發送的「跳槽」的消息,然後關閉套接字幾乎隨即。因爲SendData是異步的,所以在「退出」消息實際發送到服務器之前,調用可能會返回,因此代碼可能會在有機會發送消息之前關閉套接字。

當你第一次取消形式的卸載,並讓計時器發送「退出」消息,因爲你給插座足夠的額外時間將消息發送到服務器關閉套接字之前它的工作原理。不過,我不會指望這總是有效的;這是一個巧合,額外的步驟給套接字足夠的時間來發送消息,並且不能保證總是以這種方式工作。

您可以通過在發送「退出」消息並關閉套接字之前等待套接字引發SendCompleted事件來解決此問題。以下是一個基本的例子。請注意,QueryUnload代碼要簡單得多。

Private m_bSendCompleted As Boolean 
Private m_bSocketError As Boolean 

Private Sub singleSock_Error(ByVal Number As Integer, Description As String, ByVal Scode As Long, ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean) 
    'Set error flag so we know if a SendData call failed because of an error' 
    'A more robust event handler could also store the error information so that' 
    'it can be properly logged elsewhere' 
    m_bSocketError = True 
End Sub 

Private Sub singleSock_SendCompleted() 
    'Set send completed flag so we know when all our data has been sent to the server' 
    m_bSendCompleted = True 
End Sub 

'Helper routine. Use this to send data to the server' 
'when you need to make sure that the client sends all the data.' 
'It will wait until all the data is sent, or until an error' 
'occurs (timeout, connection reset, etc.).' 
Private Sub SendMessageAndWait(ByVal sMessage As String) 

    m_bSendCompleted = False 
    singleSock.SendData sMessage 

    singleSock.SendData sMessage 

    Do Until m_bSendCompleted or m_bSocketError 
     DoEvents 
    Loop 

    If m_bSocketError Then 
     Err.Raise vbObjectError+1024,,"Socket error. Message may not have been sent." 
    End If 

End Sub 

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer) 

    'This is (almost) all the code needed to properly send the quit message' 
    'and ensure that it is sent before the socket is closed. The only thing' 
    'missing is some error-handling (because SendMessageAndWait could raise an error).' 

    If UnloadMode = vbFormControlMenu Then 
     Me.WindowState = vbMinimized 
     Cancel = True 
    Else 
     SendMessageAndWait "quit" & vbCrLf 
     singleSock.Close 
    End If 

End Sub 

您可以通過將邏輯來發送消息,並等待它在一個單獨的類被送到使代碼更乾淨。這將私有變量和事件處理程序保留在一個地方,而不是讓它們遺留在主代碼中。當您有多個套接字時,它還可以更輕鬆地重新使用代碼。我稱SynchronousMessageSender爲缺乏更好的名字。該實施例還具有更完整的錯誤處理:

SynchronousMessageSender.cls

Private WithEvents m_Socket As Winsock 
Private m_bAttached As Boolean 

Private m_bSendCompleted As Boolean 
Private m_bSocketError As Boolean 

Private Type SocketError 
    Number As Integer 
    Description As String 
    Source As String 
    HelpFile As String 
    HelpContext As Long 
End Type 

Private m_LastSocketError As SocketError 

'Call this method first to attach the SynchronousMessageSender to a socket' 
Public Sub AttachSocket(ByVal socket As Winsock) 

    If m_bAttached Then 
     Err.Raise 5,,"A socket is already associated with this SynchronousMessageSender instance." 
    End If 

    If socket Is Nothing Then 
     Err.Raise 5,,"Argument error. 'socket' cannot be Nothing." 
    End If 

    Set m_Socket = socket 

End Sub 

Private Sub socket_SendCompleted() 
    m_bSendCompleted = True 
End Sub 

Private Sub socket_Error(ByVal Number As Integer, Description As String, ByVal Scode As Long, ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean) 

    m_bSocketError = True 

    'Store error information for later use' 
    'Another option would be to create an Error event for this class' 
    'and re-raise it here.' 

    With m_lastSocketError 
     .Number = Number 
     .Description = Description 
     .Source = Source 
     .HelpFile = HelpFile 
     .HelpContext = HelpContext 
    End With 

End Sub 

'Sends the text in sMessage and does not return' 
'until the data is sent or a socket error occurs.' 
'If a socket error occurs, this routine will re-raise' 
'the error back to the caller.' 

Public Sub SendMessage(ByVal sMessage As String) 

    If Not m_bAttached Then 
     Err.Raise 5,,"No socket is associated with this SynchronousMessageSender. Call Attach method first." 
    End If 

    m_bSendCompleted = False 
    m_bSocketError = False 

    m_socket.SendData sMessage & vbCrLf 

    'Wait until the message is sent or an error occurs' 
    Do Until m_bSendCompleted Or m_bSocketError 
     DoEvents 
    Loop 

    If m_bSocketError Then 
     RaiseLastSocketError 
    End If 

End Sub 

Private Sub RaiseLastSocketError() 

    Err.Raise m_lastSocketError.Number, _ 
       m_lastSocketError.Source, _ 
       m_lastSocketError.Description, _ 
       m_lastSocketError.HelpFile, _ 
       m_lastSocketError.HelpContext 

End Sub 

實施例使用

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer) 

    Dim sender As New SynchronousMessageSender 

    'Ignore errors since the application is closing...' 
    On Error Resume Next 

    If UnloadMode = vbFormControlMenu Then 
     Me.WindowState = vbMinimized 
     Cancel = True 
    Else 

     Set sender = New SynchronousMessageSender 
     sender.AttachSocket singleSock 
     sender.SendMessage "quit" 
     singleSock.Close 

    End If 

End Sub 

通過使用一個單獨的類,現在所有的必要的代碼可以被放置在Form_QueryUnload,保持事情更整潔。

1

豈不就是更容易只是去無quit命令。在你的服務器代碼中,假設關閉一個套接字與收到一個退出操作相同。

另外,你要注意的一件事是客戶端軟件的突然關閉。例如,丟失電源或網絡連接的機器或進入睡眠或休眠模式的機器。

在這些情況下,您應該定期檢查來自服務器的所有客戶端的連接,並關閉任何不響應某種ping命令的連接。

0

當實際事件名爲_SendComplete()時,爲什麼sub命名爲Private Sub singleSock_SendCompleted()?

相關問題