Excel VBAでメッセージ受信を行うWinsockサーバ「clsWinsockServer」
目次
- 概要
- 事前準備
- 使い方
- ソースコード
- サンプル
- 参考
概要
Excel VBAでメッセージ受信を行うWinsockサーバー「clsWinsockServer」を作成しました。
clsWinsockServerを利用すると、別途作成したWinsockクライアント「clsWinsockClient」が送信したメッセージを受信できます。
事前準備
Microsoft Winsock Control 6.0への参照設定
本クライアントを使用するには、以下の手順で「Microsoft Winsock Control 6.0」への参照設定を追加する必要があります。
- Visual Basic Editorの「ツール」メニューから「参照設定」ダイアログを開きます。
- 参照設定ダイアログ上の「参照」ボタンを押し、「ファイルの参照」ダイアログを開きます。
- ファイルの参照ダイアログで「c:\Windows\system32\MSWINSCK.OCX」を選択し、「開く」ボタンを押します。
- 参照可能なライブラリファイルに「Microsoft Winsock Control 6.0」が追加されたことを確認し、「OK」ボタンを押します。
Microsoft Scripting Runtimeへの参照設定。
本クライアントを使用するには、以下の手順で「Microsoft Scripting Runtime」への参照設定を追加する必要があります。
- Visual Basic Editorの「ツール」メニューから「参照設定」ダイアログを開きます。
- 参照設定ダイアログ上のリストボックスで「Microsoft Scripting Runtime」を探し、チェックをつけます。
- 「OK」ボタンを押します。
使い方
clsWinsockServerをクラスモジュールとしてプロジェクトに追加し、標準モジュール等から以下のように利用します。
'// 1. Winsockサーバーを生成する。 Set server = New clsWinsockServer '// 2. 接続の待受を開始する。 If server.StartListen(1500) = 9 Then '// 待受の開始に失敗: Call MsgBox("待受を開始に失敗しました。 ") Exit Sub End If '// 3. 接続のタイムアウトを確認する。 Call server.CheckTimeout '// 4. Winsockサーバーが受信したメッセージを取得する。 If server.GetMessage(wkDatetime, wkRemoteHostIp, wkMessage) = 0 Then '// 受信したメッセージを表示する。 Call MsgBox(wkDatetime & " " & wkRemoteHostIp & " " & wkMessage) End If '// 5.接続の待受を停止する。 If server.StopListen = 9 Then '// 待受の停止に失敗: Call MsgBox("待受の停止に失敗しました。 ") Exit Sub End If
1. Winsockサーバーを生成する。
まず最初に、Newを使ってclsWinsockServerのインスタンスを生成します。
2. 接続の待受を開始する。
クライアントからの接続の待受を開始するには、clsWinsockServerインスタンスのStartListenメソッドを呼び出します。
StartListenメソッドの引数は待受ポート番号です。上記の例の場合、ポート番号「1500」で待受を開始します。
StartListenメソッドの戻り値は待受開始の成功・失敗を表します。成功した場合には「0」が、失敗した場合には「9」が返却されます。
3. 接続のタイムアウトを確認する。
接続のタイムアウトを確認するため、定期的にclsWinsockServerインスタンスのCheckTimeoutメソッドを呼び出す必要があります。
CheckTimeoutメソッドの呼び出し時、タイムアウト時間を過ぎた接続をクローズします。
4. Winsockサーバーが受信したメッセージを取得する。
Winsockサーバが受信したメッセージを標準モジュール等が取得するには、clsWinsockServerインスタンスのGetMessageメソッドを呼び出します。
GetMessageメソッドの引数はすべて出力値です。1番目の引数はメッセージの受信時刻を、2番目の引数は接続元のIPアドレスを、3番目の引数は受信メッセージを返します。
5. 接続の待受を停止する。
接続の待受を停止するには、clsWinsockServerインスタンスのStopListenメソッドを呼び出します。
StopListenメソッドの戻り値は待受開始の成功・失敗を表します。成功した場合には「0」が、失敗した場合には「9」が返却されます。
ソースコード
以下、clsWinsockServerのソースコードです。
clsWinsockServerクラスモジュール
Option Explicit Private WithEvents mdl_winsock_listen As Winsock '// 待受用ソケット Private WithEvents mdl_winsock_data As Winsock '// データ送受信用ソケット Private mdl_timeout_data As Date '// データ送受信用ソケットのタイムアウト時刻 Private mdl_message_queue As Collection '// メッセージキュー '////////////////////////////////////////////////////// ' クラス初期化時の処理 '////////////////////////////////////////////////////// Private Sub Class_Initialize() On Error GoTo ERR_PROC '// 待受用ソケットを生成する。 Set mdl_winsock_listen = New Winsock '// データ送受信用ソケットを生成する。 Set mdl_winsock_data = New Winsock '// メッセージキューを生成する。 Set mdl_message_queue = New Collection Exit Sub ERR_PROC: Call MsgBox(Err.Description) End Sub '////////////////////////////////////////////////////// ' StartListen ' 概要 : 接続の待受を開始する。 ' 引数 : IN :(Intger) i_local_port 待受ポート番号 ' 戻り値: 正常時: 0、エラー時: 9 '////////////////////////////////////////////////////// Public Function StartListen(i_local_port As Integer) As Integer On Error GoTo ERR_PROC StartListen = 9 '// 待受ポート番号を設定する。 mdl_winsock_listen.LocalPort = i_local_port '// 接続の待受を開始する。 Call mdl_winsock_listen.Listen StartListen = 0 Exit Function ERR_PROC: Call MsgBox(Err.Description) '// 待受用ソケットをクローズする。 Call mdl_winsock_listen.Close '// OSに制御を移し、クローズ処理を実行させる。 DoEvents End Function '////////////////////////////////////////////////////// ' StopListen ' 概要 : 接続の待受を停止する。 ' 引数 : なし ' 戻り値: 正常時: 0、エラー時: 9 '////////////////////////////////////////////////////// Public Function StopListen() As Integer On Error GoTo ERR_PROC StopListen = 9 '// 待受用ソケットをクローズする。 Call mdl_winsock_listen.Close '// OSに制御を移し、クローズ処理を実行させる。 DoEvents StopListen = 0 Exit Function ERR_PROC: Call MsgBox(Err.Description) End Function '////////////////////////////////////////////////////// ' CheckTimeout ' 概要 : データ送受信ソケットのタイムアウトを確認します。 ' 引数 : なし ' 戻り値: 正常時: 0、エラー時: 9 '////////////////////////////////////////////////////// Public Function CheckTimeout() As Integer Dim i As Integer On Error GoTo ERR_PROC CheckTimeout = 9 '// データ送受信ソケットが接続中で、かつ、ソケット接続タイムアウト時刻を過ぎたことを確認する。 If mdl_winsock_data.State <> sckClosed And mdl_timeout_data <= Now() Then '// データ送受信ソケットをクローズする。 Debug.Print "一定時間が経過したため、データ通信ソケットをクローズします。" mdl_winsock_data.Close '// OSに制御を移し、クローズ処理を実行させる。 DoEvents End If CheckTimeout = 0 Exit Function ERR_PROC: Call MsgBox(Err.Description) '// 待受用ソケットをクローズする。 Call mdl_winsock_listen.Close '// OSに制御を移し、クローズ処理を実行させる。 DoEvents End Function '////////////////////////////////////////////////////// ' GetMessage ' 概要 : 受信メッセージを取得する。 ' 引数 : OUT:(Date) o_datetime 受信時刻 ' OUT:(String) o_remote_host_ip 接続元ホストのIPアドレス ' OUT:(String) o_message メッセージ ' 戻り値: メッセージあり: 0、メッセージなし: 1、エラー時: 9 '////////////////////////////////////////////////////// Public Function GetMessage(o_datetime As Date, o_remote_host_ip As String, o_message As String) As Integer Dim wkMessageMap As Dictionary '// メッセージを格納するマップ On Error GoTo ERR_PROC GetMessage = 9 '// キューにメッセージが格納されていることを確認する。 If mdl_message_queue.Count = 0 Then '// メッセージが格納されていない: GetMessage = 1 Exit Function End If '// キューから先頭のメッセージを格納したマップを取得する。 Set wkMessageMap = mdl_message_queue(1) '// マップから受信時刻を取得する。 o_datetime = wkMessageMap("Datetime") '// マップから接続元ホストのIPアドレスを取得する。 o_remote_host_ip = wkMessageMap("RemoteHostIP") '// マップからメッセージを取得する。 o_message = wkMessageMap("Message") '// キューから先頭のメッセージを削除する。 Call mdl_message_queue.Remove(1) GetMessage = 0 Exit Function ERR_PROC: Call MsgBox(Err.Description) End Function '////////////////////////////////////////////////////// ' 待受ソケットへの接続要求時の処理 '////////////////////////////////////////////////////// Private Sub mdl_winsock_listen_ConnectionRequest(ByVal requestID As Long) On Error GoTo ERR_PROC '// データ受信用ソケットがクローズされていることを確認する。 If mdl_winsock_data.State = sckClosed Then '// データ送受信用ソケットで接続を受け入れる。 Call mdl_winsock_data.Accept(requestID) '// タイムアウト時刻を設定する。 mdl_timeout_data = DateAdd("s", 10, Now()) End If Exit Sub ERR_PROC: Call MsgBox(Err.Description) End Sub '////////////////////////////////////////////////////// ' 接続元からデータを受信した時の処理 '////////////////////////////////////////////////////// Private Sub mdl_winsock_data_DataArrival(ByVal bytesTotal As Long) Dim wkDatetime As Date '// 受信時刻 Dim wkRemoteHostIp As String '// 接続先ホストのIPアドレス Dim wkMessage As String '// メッセージ Dim wkMessageMap As Dictionary '// メッセージを格納するマップ On Error GoTo ERR_PROC '// 受信時刻を取得する。 wkDatetime = Now '// 接続先ホストのIPアドレスを取得する。 wkRemoteHostIp = mdl_winsock_data.RemoteHostIP '// 接続元からのメッセージを取得する。 Debug.Print "接続元からメッセージを受信しました。" Call mdl_winsock_data.GetData(wkMessage) '// 接続元にOKメッセージを送信する。 Debug.Print "接続元にOKメッセージを送信します。" Call mdl_winsock_data.SendData("OK") '// OSに制御を移し、送信処理を実行させる。 DoEvents '// マップを作成する。 Set wkMessageMap = New Dictionary '// マップに受信時刻を格納する。 wkMessageMap("Datetime") = wkDatetime '// マップに接続先ホストのIPアドレスを格納する。 wkMessageMap("RemoteHostIP") = wkRemoteHostIp '// マップにメッセージを格納する。 wkMessageMap("Message") = wkMessage '// キューにメッセージを追加する。 Call mdl_message_queue.Add(wkMessageMap) Exit Sub ERR_PROC: Call MsgBox(Err.Description) End Sub '////////////////////////////////////////////////////// ' 接続先からソケットをクローズされた時の処理 '////////////////////////////////////////////////////// Private Sub mdl_winsock_data_Close() On Error GoTo ERR_PROC '// ソケットをクローズする。 Debug.Print "接続元にクローズされたため、ソケットをクローズします。" mdl_winsock_data.Close '// OSに制御を移し、クローズ処理を実行させる。 DoEvents Exit Sub ERR_PROC: Call MsgBox(Err.Description) End Sub '////////////////////////////////////////////////////// ' ソケット通信中にエラーが発生した時の処理 '////////////////////////////////////////////////////// Private Sub mdl_winsock_data_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) On Error GoTo ERR_PROC '// ソケットをクローズする。 Debug.Print "ソケット通信中にエラーが発生したため、ソケットをクローズします。" Call mdl_winsock_data.Close '// OSに制御を移し、クローズ処理を実行させる。 DoEvents Exit Sub ERR_PROC: Call MsgBox(Err.Description) End Sub
サンプル
clsWinsockServerを使用するサンプルを作成しました。このサンプルはエクセルシートと標準モジュールで構成されています。
エクセルシート
待受ポート番号を入力して待受開始ボタンを押すと、接続の待受を開始します。クライアントからのメッセージが受信した場合、シート上に受信時刻、接続元IPアドレス、メッセージが出力されます。
また、待受停止ボタンを押すと、接続の待受が停止されます。
なお、接続開始ボタンには標準モジュールのStartListen関数を、接続停止ボタンにはStopListen関数を関連付けています。
標準モジュール
Private server As New clsWinsockServer '////////////////////////////////////////////////////// ' 接続の待受を開始する。 '////////////////////////////////////////////////////// Public Sub StartListen() Dim wkSheet As Worksheet '// シート(Winsockサーバープログラム) Dim wkLocalPort As Integer '// 待受ポート番号 On Error GoTo ERR_PROC '// Winsockサーバープログラムシートを取得する。 Set wkSheet = ThisWorkbook.Worksheets("Winsockサーバープログラム") '// 待受ポート番号を取得する。 wkLocalPort = wkSheet.Cells(4, 4) '// Winsockサーバーを生成する。 Set server = New clsWinsockServer '// 接続の待受を開始する。 If server.StartListen(wkLocalPort) = 9 Then '// 待受の開始に失敗: Call MsgBox("待受を開始に失敗しました。 ") Exit Sub End If '// Winsockサーバーを定期的にチェックする。 Call IntervalCheck Call MsgBox("待受を開始しました。") Exit Sub ERR_PROC: Call MsgBox(Err.Description) End Sub '////////////////////////////////////////////////////// ' 接続の待受を開始する。 '////////////////////////////////////////////////////// Public Sub StopListen() On Error GoTo ERR_PROC '// 接続の待受を停止する。 If server.StopListen = 9 Then '// 待受の停止に失敗: Call MsgBox("待受を停止に失敗しました。 ") Exit Sub End If Call MsgBox("待受を停止しました。") Exit Sub ERR_PROC: Call MsgBox(Err.Description) End Sub '////////////////////////////////////////////////////// ' Winsockサーバーを定期的(5秒毎)にチェックする。 ' ・データ送受信ソケットのタイムアウトを確認 ' ・受信メッセージをシートに出力 '////////////////////////////////////////////////////// Public Sub IntervalCheck() Dim wkSheet As Worksheet '// シート(Winsockサーバープログラム) Dim wkDatetime As Date '// 受信時刻 Dim wkRemoteHostIp As String '// 接続先ホストのIPアドレス Dim wkMessage As String '// メッセージ On Error GoTo ERR_PROC '////////////////////////////////////////////////////// '// データ送受信ソケットのタイムアウトを確認 '////////////////////////////////////////////////////// '// データ送受信ソケットのタイムアウトを確認 Call server.CheckTimeout '////////////////////////////////////////////////////// '// Winsockサーバーが受信したメッセージをシートに出力する。 '////////////////////////////////////////////////////// '// Winsockサーバープログラムシートを取得する。 Set wkSheet = ThisWorkbook.Worksheets("Winsockサーバープログラム") '// 出力先の行番号を決定する。 wkRow = 9 Do While wkSheet.Cells(wkRow, 3) <> "" wkRow = wkRow + 1 Loop '// Winsockサーバーが受信したメッセージをシートに出力する。 Do While server.GetMessage(wkDatetime, wkRemoteHostIp, wkMessage) = 0 wkSheet.Cells(wkRow, 3) = wkDatetime wkSheet.Cells(wkRow, 4) = wkRemoteHostIp wkSheet.Cells(wkRow, 5) = wkMessage wkRow = wkRow + 1 Loop '////////////////////////////////////////////////////// '// 定期チェック用のタイマーをセットする。 '////////////////////////////////////////////////////// '// 5秒後にWinsockサーバーをチェックする。 Call Application.OnTime(earliesttime:=Now + TimeValue("00:00:05"), procedure:="IntervalCheck") Exit Sub ERR_PROC: Call MsgBox(Err.Description) End Sub