VB.NETで楽天RSSからリアルタイム情報を取得する<その8>
ずっと連載が止まっていました。すみません。<その7>までは順調に書いていたのですが、GWを挟んだというのもありますし内容がややこしくなるので「どうやって解説しようか」と悩んでいたら遅くなりました。実は今日、職場の後輩から「入門シリーズ、滞ってますよね」と突っ込まれました(苦笑)。彼は自動売買プログラム「MATS」開発のために私のブログでAdvice()編が進むのを心待ちにしているそうですが、肝心なところで記事が止まったのでヤキモキしていたらしいです。すんませ~~ん。もしかしたら彼以外にも「早く書いてくれよ」と思っていた方がいらっしゃるかも知れません。それに応え、今日は久しぶりに続きを書きたいと思います。もしこの記事を最初に読まれて流れの分からない方は<その1>からご覧頂く事をお勧めします。左のカテゴリから「「VB.NETで自動売買」入門 」をクリックして頂いたら楽です。一番下から順番に見る事が出来ます。さてさて、前回はclient.StartAdvise("現在値", 1, True, 60000)をソースに追加すると書きました。ですが、このメソッドを呼び出しても変更通知イベントは発生しません。何故か分からないのですが、Request()メソッドを呼び出したDDEクライアントはAdvice()メソッドで使い回し出来ないようなのです。テストしてみて改めて分かりました。すみません。なので前回「取得_Click」メソッドからコピーしてこなかったclient.Dispose()で一旦インスタンスを破棄し、その後再び新たなインスタンスを生成しないといけません。そこで、ただコピーしても寂しいので、ついでと言ってはナンですが、新たな豆知識を書きます(笑)。それは「Usingブロック」です。あるインスタンスを生成し、いらなくなった時にそのインスタンスを破棄しないと無意味にメモリを圧迫する事になります。いらなくなる時に今回のようにDispose()を呼び出せば良いですが、もし例外が発生してその部分が処理されなかったりしたら、インスタンスがゴミとして残り、悪影響を及ぼす危険があります。例外処理をご存知の方は「Finallyブロック」内に記述するという方法を思い付くかも知れませんが、それより確実なのが「Usingブロック」です。DDEクライアントをインスタンス化する時に「Using」と記述し、使用する部分の最後に「End Using」と記述して囲めば終わりです。これで、処理が進みUsingブロックの外に制御が移った時に、自動的にDDEクライアントは破棄されるのです。是非そう記述して下さい。念のためにソースを載せます。この下に前回ご紹介した文を追加します。その前に、DDEクライアントを改めてインスタンス化し、接続しないといけないのでご注意下さい。追加するのは3文です。Dim adviceClient As DdeClient = New DdeClient("RSS", topic)adviceClient.Connect()adviceClient.StartAdvise("現在値", 1, True, 60000)あれ?今まで「client」と書いていたDDEクライアントの変数名を変えています。同じ名前だとカブるので、名前は何でも良いのですが今回は「adviceClient」としました。これで、現在値に変更があれば変更通知イベントが発生します。前回ご紹介したadviceClient.Adviseイベントです。ですが、このイベントが発生したからと言って今のままでは何もしません。「adviceClient.Adviseイベントが発生したら、このメソッドを呼んでね」という宣言をしておく必要があるのです。ではまず、肝心の「このメソッド」を作っておく必要があります。メソッド名は何でも良いですが、今回は現在値(現在の株価)が変わった時に動くメソッドなので「PriceChange()メソッド」にしました。以下のように書いて下さい。監視_Click()メソッドの最後の「End Sub」の下に記述して下さいね。Private Sub PriceChange(ByVal sender As Object, ByVal e As DdeAdviseEventArgs)End Sub細かくは解説しませんが、引数の「sender」はこのメソッドを呼んだ人。つまりイベントを発生させた人が入ってきます。つまりDDEクライアントのインスタンスです。「e」は、このメソッドが呼び出されるきっかけとなったイベントから引き渡された色々な値が入ったオブジェクトです。まだ中身は記述してません。まずはこのまま放置して下さい。ではイベントが発生したらこのメソッドを呼び出すようにソースに追加します。こちらもお作法だと思ってこのまま記述して下さい。AddHandler adviceClient.Advise, AddressOf PriceChangeこの行はPriceChange()メソッドにイベントハンドラを追加するという作業ですが、平たく言うと「adviceClientのAdviseイベントが発生したら、PriceChange()メソッドを呼んでね」です。何となく分かりますよね?ちなみにこの行を追加する場所ですが、adviceClient.StartAdvise("現在値", 1, True, 60000)の上にして下さい!「何で?」と思われるかも知れません。別に一番下で良いのではないかと。理由は、adviceClient.StartAdvise()によって更新通知を開始したら、次にAddhandlerが実行されるまでのほんのわずかな時間(数ミリ秒とかの単位)にAdviseイベントが発生する可能性があります。すると、まだPriceChange()メソッドとの紐付けが行われていないので、その通知は無視されてしまいます。出来れば避けたいですよね。という訳で、先に紐付けてから更新通知を開始するようにします。こうです。これで、現在値に変更があるたび(値動きするたび)にPriceChange()メソッドが呼び出されますので、このメソッド内に、画面の現在値を更新する記述を書けば終わりじゃないですか!取得_Click()メソッドから'現在値のバイト配列を取得Dim praceByte As Byte() = client.Request("現在値", 1, 60000)'バイト配列を文字列に変換して画面に表示Dim priceText As String = Encoding.Default.GetString(praceByte).TrimDim dotIndex As Integer = priceText.IndexOf(".")If dotIndex > -1 Then '小数点があれば小数点以下を破棄 priceText = priceText.Substring(0, dotIndex)End If現在値.Text = priceTextをゴッソリコピーしましょう(本当は同じソースが複数箇所にあるのは望ましくないと以前書きましたが、今はそうします)。すると、一箇所コンパイルエラーが出てます。「client」です。このメソッド内にはclientもadviceClientもありません。 ※実はadviceClientは取得出来ますが今は触れませんここではこのメソッドの引数である「e」から現在値を取得します。e(DdeAdviseEventArgsというオブジェクト)は「Data」というプロパティを持っています。ここには、変更通知のあった値そのものがバイト配列で入っているのです。つまり現在値を表すバイト配列。「client.Request("現在値", 1, 60000)」と同じです。なので、代わりに「e.Data」と書いてやれば終わりです!!ソースはこうなりました。ここまでくればそれなりに動作すると思うでしょう。では動かしてみましょう!デバッグ開始ボタン(再生ボタンっぽいやつ)を押します。そして前回のように楽天を・・・と思いましたが、今日は私が印象に残っているオックスHLDGSを検索したいので(笑)「2350」「OJ」と入力します。そして取得ではなく監視ボタンをクリックです!!すると・・・上手く・・・行きません(ぉぃ)。こんなダイアログが表示され、現在値を画面に移送する所で例外が発生しているのが分かります。どうしようもないので停止ボタン(■)をクリックしてプログラムを停止します。興味のある方は勉強されたら良いと思いますが、これはマルチスレッドが原因で起こっています。更新通知のイベントはメイン処理とは別のスレッドが非同期で発生しているのですが、スレッドをまたいでオブジェクトを操作するにはややこしい制御が必要になります。画面の現在値というテキストボックスはメインスレッド上に存在するオブジェクトですがPriceChange()メソッドを実行しているのは別スレッドです。このスレッドが直接画面のオブジェクトを操作すると怒られるのです。最良の解決方法はThreadやBackGroundWorker、Invokeなどというキーワードで勉強されれば良いかと思いますが、今は一番簡単な方法で解決します。DDEクライアントのコンストラクタ(インスタンス化するメソッド)には、別のシグニチャ(引数の組み合わせ)が存在します。今までDim adviceClient As DdeClient = New DdeClient("RSS", topic)と書いてましたが、ヘルプを見ると、引数が3つのものがあります。第三引数は、同期を取るオブジェクトです。詳しくは書きませんが、第三引数で渡すオブジェクト毎にDDEインスタンスのメソッドを同期を取って動作させてくれます。そうする事によって、現在値.Textを直接更新出来るようになるんです!分からない方は理由は考えず、次のように書き換えて下さい(^-^;Dim adviceClient As DdeClient = New DdeClient("RSS", topic, Me)「Me」とは自分自身のオブジェクトの事を指します。ここでは「Form1」の事です。もう一度実行させると、正しく動作する事が分かります。実行するのがザラ場中だったら尚良いんですが。画面上の株価がリアルタイムで更新されるのを見る事が出来ますからね!!・・・今日はここまでにします。一応、更新通知イベントを受けて現在値を更新する処理が出来ました。ですが、監視を停止する処理が無いのでプログラムを止めるしかないですし、例外処理も一切書いていないので、実運用するにはプログラムが異常終了しないようにちゃんと実装する必要があるでしょう。それらを解説するかどうかは決めてませんが、Advice()形式の肝である複数銘柄、複数項目の監視をどうやって実現するかについてまだ書いていませんのでこれについては是非書きたいと思っています。次回にご期待下さい。