Future Convergence PRJ.

主にプログラミング関連で調べたことのメモ(趣味プログラムなので動作は保証しません)

スクフェス 自動ランキング取得(VB.NET)~パケット取得ver.

スクフェスをPCでプレイする2~キーボードマッピングの追加 - Future Convergence PRJ.まで設定されていることを前提として、イベントの順位情報を自動で取得するプログラムのメモ。

[処理の流れ]
① パケット監視開始(非同期処理)
② BlueStacksからキーボード入力
③ パケットからデータ取得できればパケット監視終了、一定時間待ってもデータが得られなければパケット監視中断
④ 取得したデータ(③で中断した場合は空)からどの画面かを判別して次のキーボード入力内容決定
⑤ ランキングデータがすべて取得できたら終了、まだなら①に戻る


あとはこれをガシガシコーディングするだけなんだけど、デリゲートでの非同期処理は中断できないなど想定外の事態(無知なだけ)に遭遇してだいぶ時間がかかった。できあがったソースは以下の通り。長いし汚いけど、とりあえず動くからOK。使ってる技術は以下の記事を参照。
VB.NETでパケットキャプチャ2~HTTPリクエストボディ・レスポンスボディの取得 - Future Convergence PRJ.
VB.NETでJson形式のデータの取り扱い~Json.NETを使ってみる - Future Convergence PRJ.


SchoolIdolChaserMain.vb

Imports System.Text
Imports System.IO
Imports SchoolIdolChaser.SchoolIdleChaserConstant
Imports Newtonsoft.Json

Module SchoolIdolChaserMain

    'ランキングデータ
    Public Structure RankingData
        Public TotalCnt As Integer
        Public Ranking As Integer
        Public Score As Integer
        Public Sub New(ByVal x_TotalCnt As Integer, ByVal x_Ranking As Integer, ByVal x_Score As Integer)
            TotalCnt = x_TotalCnt
            Ranking = x_Ranking
            Score = x_Score
        End Sub
    End Structure

    'ネットワーク出力データ(パケットキャプチャ出力)
    Public Structure OutputData
        Public RequestMethod As String
        Public RequestBody As String
        Public ResponseStatus As String
        Public ResponseBody As String
        Public Sub New(ByVal x_RequestMethod As String, ByVal x_RequestBody As String, ByVal x_ResponseStatus As String, ByVal x_ResponseBody As String)
            RequestMethod = x_RequestMethod
            RequestBody = x_RequestBody
            ResponseStatus = x_ResponseStatus
            ResponseBody = x_ResponseBody
        End Sub
    End Structure

    '画面状態
    Public Structure DspStatus
        Public Reliability As Integer
        Public Status As Integer
        Public Sub New(ByVal x_Reliability As Integer, ByVal x_Status As Integer)
            Reliability = x_Reliability
            Status = x_Status
        End Sub
    End Structure

    '文字コード
    Private m_Enc As Encoding = Encoding.UTF8
    Private m_EncShiftJis As Encoding = Encoding.GetEncoding("shift-jis")

    '取得するランキング
    Private m_TargetList As Byte() = New Byte() {I_RANK_A, I_RANK_B, I_RANK_C, I_RANK_D, I_RANK_E, I_RANK_F, I_RANK_G, I_RANK_S}
    '画面状態が分からない時の入力(繰り返し)
    Private m_UnknownList As Byte() = New Byte() {I_NO, I_CLOSE, I_NO, I_OK, I_INVITE, I_HOME}

    '別スレッドのパケットキャプチャが終了したかどうかの判定フラグ
    Private m_IsThreadEnd As Boolean = False
    '別スレッドのパケットキャプチャからの戻り値として使用
    Private m_Output As OutputData = Nothing
    

    Sub Main()

        Using wsLog As New System.IO.StreamWriter(LOG_FILE, True, m_EncShiftJis)
            For tryCount As Integer = 1 To 3
                Try
                    '自プロセスをアクティブにする
                    Dim myPs As Process = Process.GetCurrentProcess
                    Interaction.AppActivate(myPs.Id)
                    Threading.Thread.Sleep(1000)

                    wsLog.WriteLine("{0} 処理スタート", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))
                    Execute(wsLog)
                    Exit For
                Catch ex As Exception
                    wsLog.WriteLine("{0} 予期せぬ例外が発生[{1}回目]", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), tryCount)
                    wsLog.WriteLine(ex)
                    Threading.Thread.Sleep(180000)
                End Try
            Next
            wsLog.WriteLine("{0} 処理終了", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))
        End Using

    End Sub

    ''' <summary>
    ''' このプログラムのメイン処理
    ''' キー入力した後のパケットキャプチャから状態を判別して次のキー入力を行う。
    ''' ランキングデータがすべて取得するか、キー入力数が最大になった場合に処理終了。
    ''' </summary>
    ''' <param name="wsLog"></param>
    ''' <remarks></remarks>
    Sub Execute(ByRef wsLog As System.IO.StreamWriter)

        Dim rankingDataList As New Dictionary(Of Byte, RankingData)
        Dim dsp As DspStatus = New DspStatus(RELIABILITY_HIGH - 1, S_HOME)
        Dim unknownCount As Integer = 0
        Dim totalCount As Integer = 1

        Dim input As Byte = I_HOME
        Dim output As OutputData = New OutputData("", "", "", "")

        Dim isEnd As Boolean = False

        While True

            wsLog.WriteLine("{0} インプット[{1}/{2}回目]", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), totalCount, MAX_COUNT)

            m_IsThreadEnd = False
            m_Output = New OutputData("", "", "", "")
            output = New OutputData("", "", "", "")

            'ネットワークからデータを取得するスレッドを開始
            Dim t As New Threading.Thread(New Threading.ThreadStart(AddressOf GetOutputData))
            t.IsBackground = True
            t.Start()

            'Blue Stacksのプロセスをアクティブにする
            Dim ps() As Process = Process.GetProcessesByName("HD-Frontend")
            If ps.Length > 0 Then
                ActiveWindow(ps(0).MainWindowHandle)
                Interaction.AppActivate(ps(0).Id)
            End If

            Threading.Thread.Sleep(1000)
            'キー入力
            InputAction(input)

            Console.Write("待機中")
            For timerCount As Integer = 0 To 4
                If m_IsThreadEnd Then
                    'Exit For
                End If
                Threading.Thread.Sleep(1000)
                Console.Write(".")
            Next

            If m_IsThreadEnd Then
                Console.WriteLine()
                Console.WriteLine("値取得完了")
                output = m_Output
            Else
                Console.WriteLine()
                Console.WriteLine("値取得中断")
                t.Abort()
            End If

            dsp = GetDspStatus(dsp, input, output)
            wsLog.WriteLine("{0} requestMethod={1}", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), output.RequestMethod)

            If dsp.Reliability > RELIABILITY_LOW Then
                '画面状態が判明している時

                If dsp.Status = S_HOME Then
                    'ホーム画面の時
                    wsLog.WriteLine("{0} ホーム画面", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))
                    wsLog.Flush()
                    isEnd = True
                    For i As Integer = 0 To m_TargetList.Length - 1
                        isEnd = isEnd And rankingDataList.ContainsKey(m_TargetList(i))
                        If Not isEnd Then
                            Exit For
                        End If
                    Next
                    If isEnd Then
                        Exit While
                    Else
                        input = I_EVE
                    End If

                ElseIf dsp.Status = S_EVE Then
                    'イベント画面の時
                    wsLog.WriteLine("{0} イベント画面", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))
                    wsLog.Flush()
                    input = I_RANKING

                ElseIf dsp.Status = S_RANKING Then
                    'ランキング画面の時
                    wsLog.WriteLine("{0} ランキング画面", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))
                    wsLog.Flush()
                    If input = I_RANKING Then
                        rankingDataList(I_RANK_S) = GetRankingData(output)
                    Else
                        rankingDataList(input) = GetRankingData(output)
                    End If

                    input = I_HOME
                    For i As Integer = 0 To m_TargetList.Length - 1
                        If Not rankingDataList.ContainsKey(m_TargetList(i)) Then
                            InputAction(I_RANKING_SELECT)
                            Threading.Thread.Sleep(2000)
                            input = m_TargetList(i)
                            Exit For
                        End If
                    Next

                ElseIf dsp.Status = S_INVITE Then
                    '勧誘画面の時
                    wsLog.WriteLine("{0} 勧誘画面", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))
                    wsLog.Flush()
                    input = I_HOME

                Else
                    wsLog.WriteLine("{0} 画面不明1", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))
                    wsLog.Flush()
                    input = I_UNKNOWN

                End If

            Else
                '画面状態が不明な時
                wsLog.WriteLine("{0} 画面不明2", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))
                wsLog.Flush()
                input = I_UNKNOWN

            End If

            If input = I_UNKNOWN Then
                input = m_UnknownList(unknownCount)
                unknownCount = unknownCount + 1
                If unknownCount >= m_UnknownList.Length Then
                    unknownCount = 0
                End If
            End If
            wsLog.WriteLine("{0} 次のインプット=[{1}]", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), input)

            If totalCount >= MAX_COUNT Then
                Exit While
            End If
            totalCount = totalCount + 1

        End While

        'ランキングデータを出力する
        Using wsRank As New System.IO.StreamWriter(RANKING_FILE, True, m_EncShiftJis)
            If isEnd Then
                wsRank.WriteLine("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17}", _
                                 DateTime.Now.ToString("yyyy/MM/dd HH:mm"), rankingDataList(I_RANK_S).TotalCnt, _
                                 rankingDataList(I_RANK_S).Score, rankingDataList(I_RANK_A).Score, rankingDataList(I_RANK_B).Score, _
                                 rankingDataList(I_RANK_C).Score, rankingDataList(I_RANK_D).Score, rankingDataList(I_RANK_E).Score, _
                                 rankingDataList(I_RANK_F).Score, rankingDataList(I_RANK_G).Score, _
                                 rankingDataList(I_RANK_S).Ranking, rankingDataList(I_RANK_A).Ranking, rankingDataList(I_RANK_B).Ranking, _
                                 rankingDataList(I_RANK_C).Ranking, rankingDataList(I_RANK_D).Ranking, rankingDataList(I_RANK_E).Ranking, _
                                 rankingDataList(I_RANK_F).Ranking, rankingDataList(I_RANK_G).Ranking)
            Else
                wsRank.WriteLine("{0},,,,,,,,,,,,,,,,,", DateTime.Now.ToString("yyyy/MM/dd HH:mm"))
            End If
            wsRank.Flush()
        End Using

    End Sub

    ''' <summary>
    ''' 画面の状態を返す
    ''' </summary>
    ''' <param name="currentStatus"></param>
    ''' <param name="input"></param>
    ''' <param name="output"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetDspStatus(ByVal currentStatus As DspStatus, ByVal input As Byte, ByVal output As OutputData) As DspStatus

        Dim updatedStatus As DspStatus = New DspStatus(RELIABILITY_LOW, S_UNKNOWN)

        If output.RequestMethod.Contains(O_HOME) Then
            'ホーム画面確定の時
            updatedStatus.Reliability = RELIABILITY_HIGH
            updatedStatus.Status = S_HOME

        ElseIf output.RequestMethod.Contains(O_RANKING) Then
            'ランキング画面確定の時
            updatedStatus.Reliability = RELIABILITY_HIGH
            updatedStatus.Status = S_RANKING

        ElseIf output.RequestMethod.Contains(O_INVITE) Then
            '勧誘画面確定の時
            updatedStatus.Reliability = RELIABILITY_HIGH
            updatedStatus.Status = S_INVITE

        Else
            '通信から画面特定不可の時
            If currentStatus.Reliability > RELIABILITY_LOW Then
                If currentStatus.Status = S_HOME Then
                    'ホーム画面で
                    If input = I_HOME Then
                        'ホームボタンを押した時
                        updatedStatus.Reliability = currentStatus.Reliability - 1
                        updatedStatus.Status = S_HOME

                    ElseIf input = I_EVE Then
                        'イベントボタンを押した時
                        updatedStatus.Reliability = currentStatus.Reliability - 1
                        updatedStatus.Status = S_EVE
                    Else
                        'それ以外
                        updatedStatus.Reliability = RELIABILITY_LOW
                        updatedStatus.Status = S_UNKNOWN
                    End If

                ElseIf currentStatus.Status = S_EVE Then
                    'イベント画面で
                    If input = I_HOME Then
                        'ホームボタンを押した時
                        updatedStatus.Reliability = currentStatus.Reliability - 1
                        updatedStatus.Status = S_HOME

                    Else
                        'それ以外
                        updatedStatus.Reliability = RELIABILITY_LOW
                        updatedStatus.Status = S_UNKNOWN

                    End If

                ElseIf currentStatus.Status = S_RANKING Then
                    'ランキング画面で
                    If input = I_HOME Then
                        'ホームボタンを押した時
                        updatedStatus.Reliability = currentStatus.Reliability - 1
                        updatedStatus.Status = S_HOME

                    ElseIf input = I_BACK Then
                        '戻るを押した時
                        updatedStatus.Reliability = currentStatus.Reliability - 1
                        updatedStatus.Status = S_EVE

                    Else
                        'それ以外
                        updatedStatus.Reliability = RELIABILITY_LOW
                        updatedStatus.Status = S_UNKNOWN

                    End If

                ElseIf currentStatus.Status = S_INVITE Then
                    '勧誘画面で
                    If input = I_HOME Then
                        'ホームボタンを押した時
                        updatedStatus.Reliability = currentStatus.Reliability - 1
                        updatedStatus.Status = S_HOME

                    Else
                        'それ以外
                        updatedStatus.Reliability = RELIABILITY_LOW
                        updatedStatus.Status = S_UNKNOWN

                    End If

                Else
                    updatedStatus.Reliability = RELIABILITY_LOW
                    updatedStatus.Status = S_UNKNOWN

                End If

            End If

        End If

        Return updatedStatus

    End Function

    ''' <summary>
    ''' Json形式のデータからランキング情報を取得する
    ''' </summary>
    ''' <param name="output"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetRankingData(ByVal output As OutputData) As RankingData

        Dim ranking As RankingData = New RankingData(-1, -1, -1)

        'Json文字列をJson形式データに復元する
        Dim jsonObj As Object = JsonConvert.DeserializeObject(output.ResponseBody)

        If jsonObj(JSON_STATUS_CODE) = "200" Then
            If jsonObj(JSON_RESPONSE_DATA) IsNot Nothing Then
                ranking.TotalCnt = jsonObj(JSON_RESPONSE_DATA)(JSON_TOTAL_CNT)
                For Each item In jsonObj(JSON_RESPONSE_DATA)(JSON_ITEMS)
                    ranking.Ranking = item(JSON_RANK)
                    ranking.Score = item(JSON_SCORE)
                Next
            End If
        End If

        Return ranking

    End Function

    ''' <summary>
    ''' バイトコードで指定したキーを押す
    ''' </summary>
    ''' <param name="input"></param>
    ''' <remarks></remarks>
    Private Sub InputAction(ByVal input As Byte)
        win32api.keybd_event(input, 0, 0, 0)
        win32api.keybd_event(input, 0, 2, 0)
    End Sub

    ''' <summary>
    ''' スクフェスサーバとのパケットデータを取得する
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub GetOutputData()

        Dim output As OutputData = New OutputData("", "", "", "")

        Dim isBuffered As Boolean = False
        Dim contentBuff As Byte() = Nothing
        Dim contentCounter As Integer = 0
        Dim contentSize As Integer = 0
        Dim isGzip As Boolean = False
        Dim isReqest As Boolean = False

        Dim socket As New Net.Sockets.Socket(Net.Sockets.AddressFamily.InterNetwork, Net.Sockets.SocketType.Raw, Net.Sockets.ProtocolType.IP)
        socket.Bind(New Net.IPEndPoint(Net.IPAddress.Parse(MY_IP_ADDRESS), 0))
        socket.SetSocketOption(Net.Sockets.SocketOptionLevel.IP, Net.Sockets.SocketOptionName.AcceptConnection, True)
        socket.IOControl(Net.Sockets.IOControlCode.ReceiveAll, New Byte() {1, 0, 0, 0}, New Byte() {0, 0, 0, 0})

        While True

            Dim buff As Byte() = New Byte(4095) {}
            socket.Receive(buff)

            'IPパケットのサイズを取得
            Dim packetSize As Integer = buff(2) * 256 + buff(3)
            'プロトコル番号を取得
            Dim protocolNum As Integer = buff(9)
            '送信元IPを取得
            Dim srcIPAddress As String = String.Format("{0}.{1}.{2}.{3}", buff(12), buff(13), buff(14), buff(15))
            '送信先IPを取得
            Dim dstIPAddress As String = String.Format("{0}.{1}.{2}.{3}", buff(16), buff(17), buff(18), buff(19))

            'スクフェスサーバとのTCP通信のみ処理
            If (srcIPAddress = TARGET_IP_ADDRESS Or dstIPAddress = TARGET_IP_ADDRESS) And protocolNum = 6 Then

                'TCPの場合ヘッダ長を取得
                Dim tcpHeaderLength As Integer = Convert.ToInt32(Convert.ToString(buff(32), 2).PadLeft(8, "0").Substring(0, 4), 2) * 4
                'パケットの中身を文字列として取得
                Dim packetStr As String = m_Enc.GetString(buff, IP_HEADER_LENGTH + tcpHeaderLength, packetSize)

                If isBuffered Then
                    'バッファリング待機中の場合

                    For i As Integer = IP_HEADER_LENGTH + tcpHeaderLength To packetSize - 1
                        contentBuff(contentCounter) = buff(i)
                        contentCounter = contentCounter + 1

                        'ContentSizeを超えるデータ量だった場合は中断(パケット取り違えの場合)
                        If contentCounter >= contentBuff.Length Then
                            Exit For
                        End If
                    Next

                    'もしパケットにContent-Lengthが含まれていたらバッファリングは中断する
                    If packetStr.Contains("Content-Length:") Then
                        isBuffered = False
                    End If

                End If

                If Not isBuffered Then
                    'バッファリング待機なしの場合

                    'コンテンツバッファカウンターのクリア
                    contentCounter = 0
                    'Content-Lengthのクリア
                    contentSize = 0
                    'リクエストかレスポンスかの判定
                    isReqest = False


                    'リクエストヘッダー-メソッドの取得
                    Dim rGet As New RegularExpressions.Regex("(GET.+?\r\n)", RegularExpressions.RegexOptions.IgnoreCase Or RegularExpressions.RegexOptions.Singleline)
                    Dim mcGet As RegularExpressions.MatchCollection = rGet.Matches(packetStr)
                    For Each m As RegularExpressions.Match In mcGet
                        '正規表現に一致したグループの文字列を表示 
                        output.RequestMethod = m.Groups(1).Value.Replace(Chr(13), "").Replace(Chr(10), "")
                        isReqest = True
                        Exit For
                    Next

                    Dim rPost As New RegularExpressions.Regex("(POST.+?\r\n)", RegularExpressions.RegexOptions.IgnoreCase Or RegularExpressions.RegexOptions.Singleline)
                    Dim mcPost As RegularExpressions.MatchCollection = rPost.Matches(packetStr)
                    For Each m As RegularExpressions.Match In mcPost
                        '正規表現に一致したグループの文字列を表示 
                        output.RequestMethod = m.Groups(1).Value.Replace(Chr(13), "").Replace(Chr(10), "")
                        isReqest = True
                        Exit For
                    Next

                    'レスポンスヘッダ-ステータスの取得
                    Dim rStatus As New RegularExpressions.Regex("(HTTP/1.1 200 OK)", RegularExpressions.RegexOptions.IgnoreCase Or RegularExpressions.RegexOptions.Singleline)
                    Dim mcStatus As RegularExpressions.MatchCollection = rStatus.Matches(packetStr)
                    For Each m As RegularExpressions.Match In mcStatus
                        '正規表現に一致したグループの文字列を表示 
                        output.ResponseStatus = m.Groups(1).Value.Replace(Chr(13), "").Replace(Chr(10), "")
                        isReqest = False
                        Exit For
                    Next


                    'httpヘッダのContent-Lengthを取得
                    If packetStr.Contains("Content-Length:") Then
                        Dim rContentLength As New RegularExpressions.Regex("Content-Length: ([0-9]+)", RegularExpressions.RegexOptions.IgnoreCase Or RegularExpressions.RegexOptions.Singleline)
                        Dim mcrContentLength As System.Text.RegularExpressions.MatchCollection = rContentLength.Matches(packetStr)

                        For Each m As System.Text.RegularExpressions.Match In mcrContentLength
                            '正規表現に一致したグループの文字列を表示 
                            contentSize = m.Groups(1).Value
                            Exit For
                        Next
                    End If

                    If contentSize > 0 Then

                        'gzipかどうかの判別
                        isGzip = packetStr.Contains("Content-Encoding: gzip")

                        'Contentがスタートする場所を取得
                        Dim contentStart As Integer = packetSize
                        For i As Integer = 4 To packetSize - 1
                            If buff(i - 4) = 13 AndAlso buff(i - 3) = 10 AndAlso buff(i - 2) = 13 AndAlso buff(i - 1) = 10 Then
                                contentStart = i
                                Exit For
                            End If
                        Next

                        'Contentの取得
                        contentBuff = New Byte(contentSize - 1) {}
                        For i As Integer = contentStart To packetSize - 1
                            contentBuff(contentCounter) = buff(i)
                            contentCounter = contentCounter + 1
                        Next

                    End If

                End If

                If contentSize > 0 AndAlso contentBuff IsNot Nothing Then

                    If contentCounter >= contentSize - 1 Then
                        'コンテンツが全部取得できた
                        isBuffered = False

                        If isGzip Then
                            'GZIPファイルの書き出し
                            Using fs As New System.IO.FileStream(GZIP_TEMP_FILE, System.IO.FileMode.Create, System.IO.FileAccess.Write)
                                fs.Write(contentBuff, 0, contentBuff.Length)
                            End Using

                            'GZIPファイルの読み込み&解凍書き出し
                            Dim gzipFileStrm As New System.IO.FileStream(GZIP_TEMP_FILE, System.IO.FileMode.Open, System.IO.FileAccess.Read)
                            Using gzipStrm As New System.IO.Compression.GZipStream(gzipFileStrm, System.IO.Compression.CompressionMode.Decompress)
                                Using outFileStrm As New System.IO.FileStream(CONTENT_TEMP_FILE, System.IO.FileMode.Create, System.IO.FileAccess.Write)

                                    Dim gzipBuff(1024) As Byte
                                    While True
                                        '書庫から展開されたデータを読み込む
                                        Dim readSize As Integer = gzipStrm.Read(gzipBuff, 0, gzipBuff.Length)
                                        '最後まで読み込んだ時は、ループを抜ける
                                        If readSize = 0 Then
                                            Exit While
                                        End If '展開先のファイルに書き込む
                                        outFileStrm.Write(gzipBuff, 0, readSize)
                                    End While
                                End Using
                            End Using

                            '全読み込み
                            Using sr As New System.IO.StreamReader(CONTENT_TEMP_FILE, m_Enc)
                                If isReqest Then
                                    output.RequestBody = sr.ReadToEnd
                                Else
                                    output.ResponseBody = sr.ReadToEnd
                                End If

                            End Using

                            File.Delete(CONTENT_TEMP_FILE)
                            File.Delete(GZIP_TEMP_FILE)

                        Else
                            If isReqest Then
                                output.RequestBody = m_Enc.GetString(contentBuff, 0, contentSize)
                            Else
                                output.ResponseBody = m_Enc.GetString(contentBuff, 0, contentSize)
                            End If
                        End If

                    Else
                        'コンテンツが全部取得できていないので次のパケットを待つ
                        isBuffered = True
                    End If

                End If

                If isBuffered = False And output.RequestMethod <> "" And output.ResponseStatus <> "" Then
                    m_Output = output
                    m_IsThreadEnd = True
                    Exit While
                End If

            End If

        End While

    End Sub

    Private Sub ActiveWindow(ByVal hWnd As IntPtr)
        SwitchToThisWindow(hWnd, True)
    End Sub

    <Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _
    Private Sub SwitchToThisWindow(ByVal hWnd As IntPtr, ByVal fAltTab As Boolean)
    End Sub

End Module

Class win32api
    Public Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Integer, ByVal dwExtraInfo As Integer)
End Class


定数は以下のクラスで定義。スクフェスサーバのIPアドレスURIは伏せてあるので、動かしたいなら調べて設定する必要がある。

SchoolIdleChaserConstant.vb

Public Class SchoolIdleChaserConstant

    '#
    '# ユーザ設定にかかわる定数
    '#

    '自分のIPアドレス
    Public Const MY_IP_ADDRESS As String = "192.168.3.XXX"
    'スクフェスサーバのIPアドレス
    Public Const TARGET_IP_ADDRESS As String = "ここにスクフェスのサーバのIPアドレスを設定する"

    'ランキングファイルパス
    Public Const RANKING_FILE As String = "C:\test\ranking.txt"
    'ログファイルパス
    Public Const LOG_FILE As String = "C:\test\log.txt"
    'GZIPの一時ファイルパス
    Public Const GZIP_TEMP_FILE As String = "C:\test\gzip_temp.gz"
    'GZIP解凍の一時ファイルパス
    Public Const CONTENT_TEMP_FILE As String = "C:\test\content_temp.txt"


    '#
    '# システム固有の定数
    '#

    'キー入力の最大回数(超えた場合処理を終了)
    Public Const MAX_COUNT As Integer = 200

    'IPヘッダ長
    Public Const IP_HEADER_LENGTH As Integer = 20

    '画面状態の信頼度
    Public Const RELIABILITY_HIGH As Integer = 5
    Public Const RELIABILITY_LOW As Integer = 1

    'キーマッピング
    Public Const I_UNKNOWN As Byte = 0
    Public Const I_HOME As Byte = AscW("H")
    Public Const I_EVE As Byte = AscW("V")
    Public Const I_RANKING As Byte = AscW("R")
    Public Const I_RANKING_SELECT As Byte = AscW("J")
    Public Const I_RANK_S As Byte = AscW("S")
    Public Const I_RANK_A As Byte = AscW("A")
    Public Const I_RANK_B As Byte = AscW("B")
    Public Const I_RANK_C As Byte = AscW("C")
    Public Const I_RANK_D As Byte = AscW("D")
    Public Const I_RANK_E As Byte = AscW("E")
    Public Const I_RANK_F As Byte = AscW("F")
    Public Const I_RANK_G As Byte = AscW("G")
    Public Const I_BACK As Byte = AscW("Q")
    Public Const I_INVITE As Byte = AscW("I")
    Public Const I_OK As Byte = AscW("O")
    Public Const I_CLOSE As Byte = AscW("X")
    Public Const I_YES As Byte = AscW("Y")
    Public Const I_NO As Byte = AscW("N")

    '画面判別
    Public Const O_HOME As String = "ここにホーム画面のURIを設定する"
    Public Const O_RANKING As String = "ここにイベントのランキング画面のURIを設定する"
    Public Const O_INVITE As String = "ここに勧誘画面のURIを設定する"

    '画面状態
    Public Const S_UNKNOWN As Integer = 0
    Public Const S_HOME As Integer = 1
    Public Const S_EVE As Integer = 2
    Public Const S_RANKING As Integer = 3
    Public Const S_INVITE As Integer = 4

    'JSONパラメータ
    Public Const JSON_STATUS_CODE As String = "status_code"
    Public Const JSON_RESPONSE_DATA As String = "response_data"
    Public Const JSON_TOTAL_CNT As String = "total_cnt"
    Public Const JSON_ITEMS As String = "items"
    Public Const JSON_RANK As String = "rank"
    Public Const JSON_SCORE As String = "score"

End Class