-
JavaScript の中でなんで jQuery なのかと思われるでしょうが、 jQuery を使うことで JavaScript では記述が長い部分をコンパクトにできたり、 ブラウザ毎で動作の違いを吸収してくれます。 また、jQuery で記述されたプラグインを使うことで、ページに新しい機能が追加できます。
「JavaScript 何に使う」の記事の中で以下の様なソースがあります。 (ソースを少し変更しましたが)<html> <head> <meta charset="utf-8"> <title>test 1-2</title> </head> <body> <p id="msg1">テストメッセージ</p> <p id="msg2">テストメッセージ</p> <script> var element = document.getElementById('msg1'); element.innerText = 'こんにちは1'; var element = document.getElementById('msg2'); element.innerText = 'こんにちは2'; </script> </body> </html>■jQueyを使った簡単な例
document.getElementById の記述が少し冗長だと思いますので、 この部分に jQuery の記述を適用してみます。
$('#msg1') の部分が document.getElementById('msg1') の替わりで、 .text('こんにちは1') の部分が .innerText = 'こんにちは1' の替わりになります。
結構シンプルな記述になりました。 ここで $(function(){ ... }); の中で処理の記述がありますが、この関数の中身の実行は全ページの読込後に行われます。 $(document).ready(function(){ ... }); としても同じです。<html> <head> <meta charset="utf-8"> <title>test jQuery</title> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> </head> <body> <p id="msg1">テストメッセージ</p> <p id="msg2">テストメッセージ</p> <script> $(function(){ $('#msg1').text('こんにちは1'); $('#msg2').text('こんにちは2'); }); </script> </body> </html>
$('#msg1') に注目して下さい。 msg1 は p タグの id として宣言されていますが、 id 属性のタグを参照するものしてとして #(シャープ) を使います。 この$('...') と記述するものをセレクタといいます。
セレクタとは全HTMLの中の、どの要素なのかを示すオブジェクトです。 このセレクタですが、 p タグの様なHTMLのタグそのものを示したり、 上のソースの様にID属性のタグや、Class属性のタグを示すことができます。
セレクタの方法はいっぱいありますので、下の方で説明したいと思います。
■jQueyのダウンロード
ところで jQuery の読込ですが、上の例では公式サイトに存在するスクリプトを読込んでいます。 このスクリプトファイルを自分のサイトにダウンロードしてそれを読込ことでもできます。 ダウンロードは以下の公式サイトからできます。
■jQueryのダウンロードサイト
公式サイトには jquery-3.2.1.min.js と min無しの jquery-3.2.1.js の2種類がありますが、 min無しの方はスクリプト内の空白を全て削除してあるため、ファイルサイズが小さいので ロード時間的には有利です。<html> <head> <meta charset="utf-8"> <title>test jQuery</title> <script src="スクリプトを設置したアドレス/jquery-3.2.1.min.js"></script> </head> ...
■jQueyのセレクタ
セレクタの方法は HTMLの要素 , id名 , class名 等があり、またそれらを複合的に組み合わせたものがあります。
■HTMLの要素
最初にHTMLの要素のセレクタの例を以下に示します。 $("button") でボタン要素を指し示します。
さらに .(ドット) で繋ぐことでそのセレクタの示すプロパティ、メソッド等にアクセスできます。 $("button").click(function(){ ... }) はボタンのクリックイベントの関数宣言を行います。
この関数の中の $("p") は p タグの要素全てを示しますので、ボタンをクリックした時に全ての p タグの表示が「こんにちは」に変わります。<html> <head> <meta charset="utf-8"> <title>test jQuery</title> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> </head> <body> <p id="msg1">テストメッセージ</p> <p id="msg2">テストメッセージ</p> <button>変更</button> <script> $(function () { //HTML要素<button>のクリックイベントの関数を宣言 $("button").click(function(){ //HTML要素<p>の全てを変更 $("p").text("こんにちは"); }) }); </script> </body>上の例を以下に実際のHTMLで示します。
⇒HTMLの要素の例
■id名
HTML要素に設定された id名 でセレクタを限定します。 セレクタの書式としては $("#id名") の様に id名 の前に #(シャープ) を付けます。 HTML要素の中に id名 が複数存在しても最初の要素が対象となります。 (jQuery の大元の getElementById 関数は最初の要素しか取得しないからです。)
2個のボタンはそれぞれ id名 が btn1 , btn2 と設定されているので、 それぞれのクリック時のイベント関数を宣言しています。 それぞれの関数の中では p タグの msg1 , msg2 に対応してテキストを変更しています。<html> <head> <meta charset="utf-8"> <title>test jQuery</title> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> </head> <body> <p id="msg1">テストメッセージ</p> <p id="msg2">テストメッセージ</p> <button id="btn1">変更1</button> <button id="btn2">変更2</button> <script> $(function () { //<button id="btn1">のクリックイベントの関数を宣言 $("#btn1").click(function(){ //<p id="msg1">のTEXTを変更 $("#msg1").text("11111"); }) //<button id="btn2">のクリックイベントの関数を宣言 $("#btn2").click(function(){ //<p id="msg2">のTEXTを変更 $("#msg2").text("22222"); }) }); </script> </body>上の例を以下に実際のHTMLで示します。
⇒id名の例
■class名
HTML要素に設定された class名 でセレクタを限定します。 セレクタの書式としては $(".class名") の様に class名 の前に .(ドット) を付けます。 HTML要素の中に class名 は複数存在する可能性があるので、それら全てが対象となります。
class名 が btn と設定されたボタンのクリック時のイベント関数を宣言しています。 関数の中では msg > と設定された全ての p タグに対してテキストを変更しています。 (以下の例では3個の p タグのテキストを変更しています。)<html> <head> <meta charset="utf-8"> <title>test jQuery</title> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> </head> <body> <p class="msg">テストメッセージ1</p> <p class="msg">テストメッセージ2</p> <p class="msg">テストメッセージ3</p> <button class="btn">変更</button> <script> $(function () { //<button class="btn">のクリックイベントの関数を宣言 $(".btn").click(function(){ //<p class="msg">の全てのTEXTを変更 $(".msg").text("ああああああああ"); }) }); </script> </body>上の例を以下に実際のHTMLで示します。
⇒class名の例
以上、セレクタの基本的な使い方でしたが、次回はこれらの複合的な使い方を説明したいと思います。
PR -
前回「その3」では「メイン処理スレッドの実際の中身を組み込んでいきたいと思います」と記しましたが、実際のプログラム例を示します。
今回の例ではメイン処理スレッド内で、時刻が毎分00秒になった場合、サブ処理のスレッドを起動します。
毎分00秒の判定方法は、メインループ内で毎回、現在時刻の秒を取得し、初期状態から"00"になった時にスレッド開始のトリガとしています。
スレッド開始時の秒を前回値として退避しておきます。 この前回値が設定済みで、現在時刻の秒が"00"ではない場合に、再度前回値を初期状態に戻しています。
この一連の処理はループ最後の待ち時間が1秒より十分小さくないと成り立ちません。
(尚、このトリガ時刻判定はもっといい方法があるかもしれませんが。)
サブ処理のスレッドの中身は、単に1秒毎に現在時刻をイベントログの書き出す処理を3回行っています。 3回の処理を終わるとこのスレッドは処理を終え、消滅します。
実際のソースは以下の通りです。■サービスPG・メイン処理内で別のスレッドを呼出す
Public Class TestService Protected Overrides Sub OnStart(ByVal args() As String) ' サービスを開始するコードをここに追加します。このメソッドによって、 ' サービスが正しく実行されるようになります Me.EventLog.WriteEntry("サービスを開始します。") 'メイン処理スレッドの生成を行う Me.mThreadMain = New Threading.Thread(New Threading.ThreadStart(AddressOf Me.MainProc)) Me.mThreadMain.Start() End Sub Protected Overrides Sub OnStop() ' サービスを停止するのに必要な終了処理を実行するコードをここに追加します。 Me.EventLog.WriteEntry("サービスを停止します。") 'メインスレッド停止依頼フラグON Me.mblnThreadStop = True 'メインスレッドの停止を待つ Me.mThreadMain.Join() End Sub ' スレッドクラス Private mThreadMain As Threading.Thread ' スレッド停止フラグ Private mblnThreadStop As Boolean = False ' メイン処理スレッド Private Sub MainProc() Me.EventLog.WriteEntry("MainProc...開始。") Try '前回秒の初期状態 Dim strLastSS As String = "" 'メインスレッドのループ While mblnThreadStop = False '現在の秒の取得 Dim strSS As String = Now.ToString("ss") If strLastSS = "" Then '前回秒の初期状態の場合 If strSS = "00" Then '時刻が毎分00秒の場合、サブスレッド開始 Dim t As New Threading.Thread(New Threading.ThreadStart(AddressOf SubProc)) t.Start() '現在の秒の退避 strLastSS = strSS End If Else If strSS <> "00" Then '前回秒の初期化 strLastSS = "" End If End If ' 約0.1秒待ち時間を入れる(このプロセスがCPUを独占しない様に) Threading.Thread.Sleep(100) End While Catch ex As Exception ' エラー処理があればここに書く Me.EventLog.WriteEntry("MainProc...エラー" & ex.Message) End Try Me.EventLog.WriteEntry("MainProc...停止。") End Sub 'サブ処理スレッド Private Sub SubProc() Me.EventLog.WriteEntry("SubProc...開始。") Try 'ループを3回で処理終了 For i As Integer = 1 To 3 '時刻の時間.分.秒.ミリ秒の表示 Me.EventLog.WriteEntry("SubProc..." & Now.ToString("HH:mm:ss.fff")) ' 約1秒待ち時間を入れる Threading.Thread.Sleep(1000) Next Catch ex As Exception ' エラー処理があればここに書く Me.EventLog.WriteEntry("SubProc...エラー" & ex.Message) End Try Me.EventLog.WriteEntry("SubProc...停止。") End Sub End Classこのサービスを実際に開始し、ログの結果を「その3」同様 Windowsの powershell を起動し Get-EventLog コマンド でログの一覧を見てみます。
「Windows + R」 で「ファイル名を指定して実行」を開き、 powershell を起動します。
powershell 起動後、以下のコマンドをキー入力してログ一覧を行います。- Get-EventLog Application -Newest 26
ログを見ますと、ほぼ毎分00秒でサブスレッドが起動され、1秒毎に3回時刻が登録されています。この例ではサービスを起動後、約4分後にサービスを停止させています。
サービスの処理では外部からのリクエストにより何かの処理を行い結果を返すというのが一般的な処理ではないかと思われます。 今回の例ではタイマをリクエストのトリガとして使っています。タイマもサービスプログラムから見れば外部と考えられなくもありませんが。
外部からのリクエストとしては、通信による方法だとか、原始的な方法ではファイルによるものだとかいろいろ考えられます。 今後は外部からのリクエストに応答するサービスを例に挙げていきたいと思います。
さて、今回の例でメイン処理のなかで、サブスレッドを起動していますが、 外部から複数の起動が掛かることを将来的に考えるのであれば、 サブスレッドを複数起動しそれぞれのスレッドのログが判別できる様にしてみます。
サブスレッドの処理をクラスでラップし、クラスの中に内部のスレッドの起動メソッドを設けてみます。■サービスPG・メイン処理内で別のスレッドを呼出す(別スレッドをクラス化)
Public Class TestService Protected Overrides Sub OnStart(ByVal args() As String) ' サービスを開始するコードをここに追加します。このメソッドによって、 ' サービスが正しく実行されるようになります Me.EventLog.WriteEntry("サービスを開始します。") 'メイン処理スレッドの生成を行う Me.mThreadMain = New Threading.Thread(New Threading.ThreadStart(AddressOf Me.MainProc)) Me.mThreadMain.Start() End Sub Protected Overrides Sub OnStop() ' サービスを停止するのに必要な終了処理を実行するコードをここに追加します。 Me.EventLog.WriteEntry("サービスを停止します。") 'メインスレッド停止依頼フラグON Me.mblnThreadStop = True 'メインスレッドの停止を待つ Me.mThreadMain.Join() End Sub ' スレッドクラス Private mThreadMain As Threading.Thread ' スレッド停止フラグ Private mblnThreadStop As Boolean = False ' メイン処理スレッド Private Sub MainProc() Me.EventLog.WriteEntry("MainProc...開始。") Try '前回秒の初期状態 Dim strLastSS As String = "" 'メインスレッドのループ While mblnThreadStop = False '現在の秒の取得 Dim strSS As String = Now.ToString("ss") If strLastSS = "" Then '前回秒の初期状態の場合 If strSS = "00" Then '時刻が毎分00秒の場合、3個のサブスレッド開始 For i As Integer = 1 To 3 'スレッド名前 Dim strName As String = "Test" & i.ToString 'スレッドクラス生成 Dim clsSubTread As New SubThread(strName, Me.EventLog) 'クラス内のスレッド開始 clsSubTread.Start() Next '現在の秒の退避 strLastSS = strSS End If Else If strSS <> "00" Then '前回秒の初期化 strLastSS = "" End If End If ' 約0.1秒待ち時間を入れる(このプロセスがCPUを独占しない様に) Threading.Thread.Sleep(100) End While Catch ex As Exception ' エラー処理があればここに書く Me.EventLog.WriteEntry("MainProc...エラー" & ex.Message) End Try Me.EventLog.WriteEntry("MainProc...停止。") End Sub '===== 'サブスレッド用ラップクラス '===== Class SubThread '名前 Private mstrName As String 'イベントログ Private mEventLog As System.Diagnostics.EventLog 'コンストラクタ Sub New(strName As String, EventLog As System.Diagnostics.EventLog) Me.mstrName = strName Me.mEventLog = EventLog End Sub '内部スレッドの開始 Sub Start() Dim t As New Threading.Thread(New Threading.ThreadStart(AddressOf SubProc)) t.Start() End Sub 'サブ処理スレッド Private Sub SubProc() Me.mEventLog.WriteEntry("SubProc[" & Me.mstrName & "]...開始。") Try 'ループを3回で処理終了 For i As Integer = 1 To 3 '時刻の時間.分.秒.ミリ秒の表示 Me.mEventLog.WriteEntry("SubProc[" & Me.mstrName & "]..." & Now.ToString("HH:mm:ss.fff")) ' 約1秒待ち時間を入れる Threading.Thread.Sleep(1000) Next Catch ex As Exception ' エラー処理があればここに書く Me.mEventLog.WriteEntry("SubProc[" & Me.mstrName & "]...エラー" & ex.Message) End Try Me.mEventLog.WriteEntry("SubProc[" & Me.mstrName & "]...停止。") End Sub End Class End Classログを見ますと、ほぼ毎分00秒で3個のサブスレッドが起動され、1秒毎に3回時刻が登録されています。
3個のスレッドは実行が順番通りではありません。これはスレッドを同時に起動したとしても、スレッドの優先がどれになるかは分からない様です。
サブスレッドをクラス化しましたが、各スレッドで少し違った処理をさせたい場合にはクラス化してその中で場合分けした方が組みやすいと思います。関連する記事
⇒サービスプログラムの作り方について・その1
⇒サービスプログラムの作り方について・その2
⇒サービスプログラムの作り方について・その3(メイン処理をスレッド化)
おすすめ書籍
-
JavaScript とはブラウザで実行されるクライアントプログラムのことですが、 HTMLで記述されたWebページを動的に変更したりするために使います。
主なjavaScriptの機能としては以下の様なものがあります。■HTML要素の操作(HTMLの要素の属性値、CSSプロパティを変更、HTML要素そのものの生成)
以下のソースを見て下さい。 これは p タグで囲って テストメッセージ を表示するだけの HTML ソースです。 これをブラウザ表示させると テストメッセージ が表示されます。
<html> <head> <meta charset="utf-8"> <title>test 1</title> </head> <body> <p>テストメッセージ</p> </body> </html>
この p タグの中身を変更するには JavaScript のソースを以下の様に記述します。
このソースをブラウザで表示させるといきなり こんにちは が表示されます。
body タグに記述された script タグの中の JavaScript のソースが順次実行されるため、 このソースを読込むと同時に JavaScript のソースが実行され こんにちは のみが表示されます。<html> <head> <meta charset="utf-8"> <title>test 1</title> </head> <body> <p>テストメッセージ</p> <script> var elements = document.getElementsByTagName('p'); elements[0].innerText = 'こんにちは'; </script> </body> </html>上のソースでいきなり document オブジェクトが出てきましたが、 これはブラウザーに読み込まれたウェブページ全体を示すオブジェクトです。 このオブジェクトの getElementsByTagName メソッドを使ってタグのオブジェクトの一覧を取得します。
getElementsByTagName に 'p' を指定し、p タグの一覧を取得します。 結果はオブジェクト配列となりますので、上の例では配列の最初のプロパティ innerText へ こんにちは を設定します。
ここで テストメッセージ を1行追加して以下の様にし、 ブラウザに表示すると、当然なのですが最初の1行目しか こんにちは となりません。 そこで、 JavaScript 部分を以下の様にすれば全てのp タグの中身のテキストを こんにちは に変更できます。<html> <head> <meta charset="utf-8"> <title>test 1</title> </head> <body> <p>テストメッセージ</p> <p>テストメッセージ</p> <script> var elements = document.getElementsByTagName('p'); for (var i = 0; i < elements.length; i++) { elements[i].innerText = 'こんにちは'; } </script> </body> </html>
getElementsByTagName 複数のタグの一覧を取得しますが、特定のオブジェクト(エレメント)を取得したい場合があります。 その場合には getElementById で指定した ID名 のオブジェクトが取得できます。
以下のソースでは各 p タグに ID を別な名前で割り当てています。 JavaScript では2行目の ID を指定して getElementById を呼出します。 返されたオブジェクトの innerText へ こんにちは を設定します。<html> <head> <meta charset="utf-8"> <title>test 1-2</title> </head> <body> <p id="msg1">テストメッセージ</p> <p id="msg2">テストメッセージ</p> <script> var element = document.getElementById('msg2'); element.innerText = 'こんにちは'; </script> </body> </html>■HTML要素などによる動作の指定
上の例では、ソースをブラウザで表示したとたんにメッセージが変化していましたが、 これでは面白くないので、ボタンを2個設置して、ボタンを押下した時にそれぞれのメッセージが変化する様に ソースを変更してみます。
<html> <head> <meta charset="utf-8"> <title>test 1-3</title> </head> <body> <p id="msg1">テストメッセージ</p> <p id="msg2">テストメッセージ</p> <button onclick="changeMsg('msg1');">msg1</button> <button onclick="changeMsg('msg2');">msg2</button> <script> //メッセージ変更関数 function changeMsg(strId) { var element = document.getElementById(strId); element.innerText = 'こんにちは'; } </script> </body> </html>
上のソースでは各ボタン要素のマウスクリックイベントが発生した時に、関数 changeMsg を呼出す様にしています。
実際に以下のボタンをクリックして下さい。 テストメッセージ が こんにちは に変化します。テストメッセージ
テストメッセージ
onclick の様なイベントは他にもいろんなものがありますが、以下に一部を示します。 必要に応じて各イベントの処理を行います。- onkeydown :キーが押した時のイベント
- onkeyup :キーを放した時のイベント
- ondblclick :マウスの左ボタンをダブルクリックした時のイベント
- onmouseover:マウスのカーソルを要素の上に合わせた時のイベント
- onmouseout :マウスのカーソルを要素の上から外した時のイベント
いろんな動作に関してのイベント処理の例は別の機会に追々紹介したいと思います。
■Webサーバとの通信によるデータのやり取り(Ajaxを利用)
Webページ上でページを移動しないで、サーバからデータを取得し一部分を動的に変更したい場合があります。 この時に用いる方法がAjax(Asynchronous JavaScript XML)と言われるものです。 JavaScript でサーバ側から渡されるXMLデータ(JSONデータもできます)をページ上に反映させます。 手順としては以下の通りです。
- クライアント側からサーバ側にデータ取得等に必要な引数等を送信する
- サーバ側は引数データを受け取って返信すべきデータを整形しクライアント側に送信する
- クライアント側はサーバ側から受信したデータをページ上に表示する
Ajax の処理には XMLHttpRequest というオブジェクトを使います。 XMLHttpRequest のメソッド、プロパティは以下の通りです。
メソッド 説明 open(method, url
[, async [, user[, pass]]])新しく作成されたリクエストを初期化する
method : HTTP リクエストメソッド("GET", "POST", ...)
url : リクエストを送信する URL を表す
async : 操作が非同期的に行われるかどうかを示します
user : 認証プロセスで使用するユーザー名
pass : 認証プロセスで使用するパスワードsend([body]) リクエストをサーバーに送信します
body : POST リクエストの場合に使用しますabort() メソッドは、すでに送信された要求を中止します プロパティ 説明 onreadystatechange EventHandler で、これは readyState 属性が変化する度に呼び出されます readyState リクエストの状態を返します
(0):UNSENT XMLHttpRequest クライアントは作成済みだが、まだ open() メソッドは呼ばれていない
(1):OPENED open() が呼び出し済み
(2):HEADERS_RECEIVED send() は呼び出し済みでレスポンスヘッダーを受け取り済み
(3):LOADING レスポンスボディを受け取り中。ResponseType が "text" か空文字の場合、responseText はロードするごとに部分テキストを持つ
(4):DONE 取得操作が完了している(データ転送が完全に成功したか失敗したかどちらか)responseText リクエストに対するレスポンスがテキスト形式で入った DOMString を返すか、 リクエストが失敗した場合や、まだ送信されていない場合は null を返す responseXML リクエストに対するレスポンスが入った Document を返すか、 またはリクエストが成功しなかった場合、まだ送信されていない場合、 XML または HTML として解釈できなかった場合は null を返す status リクエストに対するレスポンスのステータスを返す statusText HTTP サーバーから返ってきたレスポンス文字列が入った DOMString を返す
以下に Ajax 処理の簡単な例を示します。 このソースで行われるのは、ボタンを押下した時にAjax処理関数を呼出します。 Ajax の手順は以下の通りです。- XMLHttpRequest オブジェクトの生成
- サーバへのリクエストの設定を行う(ここでは GET でこのソースが存在するディレクトリのテキストファイルが対象)
- イベントハンドラの登録(データ受信が完了でステータスが正常の場合、受信データを表示)
- リクエストをサーバーに送信
イベントハンドラの登録では、ここで処理が動作するわけでは無く、単に関数の登録のみを行っています。 XMLHttpRequest オブジェクトの readyState プロパティの値が変化した時に起動される関数を定義しています。 結果的にこの関数が走るのは send された後になります。
<html> <head> <meta charset="utf-8"> <title>test ajax</title> </head> <body> <h1>test ajax</h1> <p id="msg1">テストメッセージ</p> <button onclick="getAjax();">Ajax</button> <script> //Ajax関数 function getAjax() { //XMLHttpRequestの生成 var xhr = new XMLHttpRequest(); //サーバへのリクエストの設定を行う xhr.open('GET', 'test_ajax.txt'); //イベントハンドラの登録 xhr.onreadystatechange = function () { if (xhr.readyState == 4) { //データ受信完了 if (xhr.status == 200) { //ステータスが正常の場合、返信データを表示 var elem = document.getElementById("msg1"); elem.innerText = xhr.responseText; } else { alert("status = " + xhr.status); } } }; //リクエストをサーバーに送信 xhr.send(); // xhr.abort(); } </script> </body> </html>
実際の Ajax 処理では上記の様に XMLHttpRequest そのものでプログラムするわけでは無く、 JQuery と呼ばれるライブラリを利用し、もっと簡略化された記述で処理を行います。 この辺りは別の機会に紹介したいと思います。
-
名前付きパイプは、パイプサーバーと複数のパイプクライアントとの間でのプロセス間通信ができます。 名前付きパイプは、メッセージ単位で通信するモードと、バイトストリームとして読み書きするモードのいずれかが選べます。
どちらのモードでも、名前付きパイプを生成し、接続した後は、サーバー側・クライアント側とも通常のファイル入出力を使うことで通信が行われます。
名前付きパイプの例として以下の2通りの方法を示します。■1回の通信毎に名前付きパイプを生成する方法
サーバー側では、名前付きパイプ処理を行うスレッドを立ち上げて、そのスレッドの中で通信処理を行います。
フォームに通信開始ボタンと、受信メッセージ等の表示用にリッチテキストボックスを設置します。
スレッド処理では NamedPipeServerStream によりサーバー用名前付きパイプクラスを生成します。
その後 WaitForConnection メソッドで接続を待ち StreamReader によりパイプからの受信文字列を取得します。 取得文字列をリッチテキストボックスにデリゲートで表示し、すぐに StreamReader とサーバー用名前付きパイプクラスを閉じます。
サーバー側プログラム
Imports System.Threading Imports System.IO Imports System.IO.Pipes Public Class frmNameServer '別スレッドからメッセージを処理するためデリゲートを利用 Delegate Sub SetRichTextBox1Delegate(ByVal Value As String) Private RichTextBox1Delegate As New SetRichTextBox1Delegate(AddressOf _ AppendTextRichTextBox1) 'リッチテキストボックスにメッセージを表示する Private Sub AppendTextRichTextBox1(ByVal message As String) '文字列を最後尾に追加 RichTextBox1.AppendText(message) 'カレット位置を末尾に移動 RichTextBox1.SelectionStart = RichTextBox1.TextLength 'カレット位置までスクロール RichTextBox1.ScrollToCaret() End Sub '別スレッドからボタンの許可設定処理するためデリゲートを利用 Delegate Sub SetButton1EnabledDelegate(ByVal bln As Boolean) Private SetButton1Delegate As New SetButton1EnabledDelegate(AddressOf _ SetButton1Enabled) '[Buttin1]のEnabled設定 Private Sub SetButton1Enabled(ByVal blnEnabled As Boolean) Me.Button1.Enabled = blnEnabled End Sub '[Start NamePipe]ボタンクリックイベント Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles Button1.Click '名前付きパイプによる通信スレッド開始 Dim server As New Thread(AddressOf ServerThread) server.Start() AppendTextRichTextBox1("名前付きパイプ通信待機中..." & vbNewLine) '[Start NamePipe]ボタン不可設定 Me.Button1.Enabled = False End Sub '名前付きパイプによる通信スレッド Private Sub ServerThread() 'サーバー用のパイプを双方向でクライアント数は2個で生成 Dim pipeServer As New NamedPipeServerStream("PIPE_TEST", PipeDirection.InOut, 2) Try 'パイプスレッド取得 Dim threadId As Integer = Thread.CurrentThread.ManagedThreadId Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ生成完了(スレッドID:" & threadId & ")" & vbNewLine}) 'パイプの接続を待つ pipeServer.WaitForConnection() Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ接続完了" & vbNewLine}) 'ストリーム読込を生成 Dim pipeStreamReader As New StreamReader(pipeServer) Try 'パイプからの文字列受信(入力) Dim strRead As String = pipeStreamReader.ReadLine() '受信文字列の表示 Me.Invoke(RichTextBox1Delegate, _ New Object() {"受信文字列:" & strRead & vbNewLine}) Catch ex As IOException MsgBox(ex.Message) Finally pipeStreamReader.Close() End Try Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ通信の終了" & vbNewLine}) '[Button1]の許可設定 Me.Invoke(SetButton1Delegate, New Object() {True}) Catch ex As Exception MsgBox(ex.Message) Finally pipeServer.Close() End Try End Sub End Class
クライアント側プログラムは送信ボタンとメッセージ入力用にテキストボックスを設置します。
送信ボタンを押下した時に NamedPipeClientStream によりクライアント用名前付きパイプを生成します。
そのクラスの Connect メソッドで接続後ストリームライタで1回のみ文字列送信を行います。
送信後、ストリームライタとクライアント用名前付きパイプを閉じます。
クライアント側プログラム
Imports System.IO Imports System.IO.Pipes Imports System.Security.Principal Public Class frmNameClient '[Start Named Pipe]ボタンクリック時イベント Private Sub ButtonSend_Click(sender As Object, e As EventArgs) _ Handles ButtonSend.Click Dim pipeStream As NamedPipeClientStream = Nothing Dim pipeSw As StreamWriter = Nothing Try '[Send Message]ボタンを不可設定 Me.ButtonSend.Enabled = False '名前付きパイプ・クライアント生成 pipeStream = New NamedPipeClientStream(".", "PIPE_TEST", _ PipeDirection.InOut, _ PipeOptions.None, _ TokenImpersonationLevel.Impersonation) '名前付きパイプ接続 pipeStream.Connect(1000) 'ストリームライタ pipeSw = New StreamWriter(pipeStream) pipeSw.AutoFlush = True '文字列送信 pipeSw.WriteLine(Me.TextBox1.Text.Trim) Catch ex As Exception MsgBox(ex.Message) Finally 'ストリームライタを閉じる If Not pipeSw Is Nothing Then pipeSw.Close() End If '名前付きパイプを閉じる If Not pipeStream Is Nothing Then pipeStream.Close() End If '[Send Message]ボタンを許可設定 Me.ButtonSend.Enabled = True End Try End Sub End Classプログラムの実行は以下の様になります。
■名前付きパイプを生成後、複数回の通信を行う方法
1回の通信毎に名前付きパイプを生成するのでは効率が悪いですし、連続での通信ができませんので サーバー側のスレッド部分を複数回の通信が出来る様に変更します。
変更した部分は、スレッド内でストリーム読込を生成をした後で、ループを形成しパイプからの文字列受信を繰り返す様にしています。 受信した文字列がNULLの場合にストリームライタとクライアント用名前付きパイプを閉じます。
サーバー側プログラム
Imports System.Threading Imports System.IO Imports System.IO.Pipes Public Class frmNameServer '別スレッドからメッセージを処理するためデリゲートを利用 Delegate Sub SetRichTextBox1Delegate(ByVal Value As String) Private RichTextBox1Delegate As New SetRichTextBox1Delegate(AddressOf _ AppendTextRichTextBox1) 'リッチテキストボックスにメッセージを表示する Private Sub AppendTextRichTextBox1(ByVal message As String) RichTextBox1.AppendText(message) RichTextBox1.SelectionStart = RichTextBox1.TextLength End Sub '別スレッドからボタンの許可設定処理するためデリゲートを利用 Delegate Sub SetButton1EnabledDelegate(ByVal bln As Boolean) Private SetButton1Delegate As New SetButton1EnabledDelegate(AddressOf _ SetButton1Enabled) '[Buttin1]のEnabled設定 Private Sub SetButton1Enabled(ByVal blnEnabled As Boolean) Me.Button1.Enabled = blnEnabled End Sub '[Start NamePipe]ボタンクリックイベント Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click '名前付きパイプによる通信スレッド開始 Dim server As New Thread(AddressOf ServerThread) server.Start() AppendTextRichTextBox1("名前付きパイプ通信待機中・・・" & vbNewLine) '[Start NamePipe]ボタン不可設定 Me.Button1.Enabled = False End Sub '名前付きパイプによる通信スレッド Private Sub ServerThread() 'サーバー用のパイプを双方向でクライアント数は2個で生成 Dim pipeServer As New NamedPipeServerStream("PIPE_TEST", PipeDirection.InOut, 2) Try 'パイプスレッド取得 Dim threadId As Integer = Thread.CurrentThread.ManagedThreadId Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ生成完了(スレッドID :" & threadId & ")" & vbNewLine}) 'パイプの接続を待つ pipeServer.WaitForConnection() Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ接続完了" & vbNewLine}) 'ストリーム読込を生成 Dim pipeStreamReader As New StreamReader(pipeServer) Try While True 'パイプからの文字列受信(入力) Dim strRead As String = pipeStreamReader.ReadLine() 'ストリームの末尾に到達した場合はNullなので、通信を終了する 'If strRead Is Nothing Then If strRead = "" Then Exit While End If '受信文字列の表示 Me.Invoke(RichTextBox1Delegate, _ New Object() {"受信文字列:" & strRead & vbNewLine}) End While Catch ex As IOException MsgBox(ex.Message) Finally pipeStreamReader.Close() End Try Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ通信の終了" & vbNewLine}) '[Button1]の許可設定 Me.Invoke(SetButton1Delegate, New Object() {True}) Catch ex As Exception MsgBox(ex.Message) Finally pipeServer.Close() End Try End Sub End Class
クライアント側プログラムではパイプ開始ボタンとメッセージ送信ボタンを設置します。
パイプ開始ボタン押下で名前付きパイプ・クライアント生成と接続、及びストリームライタ生成を行います。
この生成されるクラスは、メッセージ送信ボタン押下時にも使用されるため、関数の外で静的変数として宣言します。
メッセージ送信ボタン押下時にはストリームライタでメッセージを送信します。 メッセージが空白の場合はストリームライタ、名前付きパイプ・クライアントを閉じます。
クライアント側プログラム
Imports System.IO.Pipes Imports System.IO Imports System.Security.Principal Public Class frmNameClient '名前付きパイプ・クライアント Private pipeStream As NamedPipeClientStream = Nothing 'パイプ用ストリームライタ Private pipeSw As StreamWriter = Nothing 'フォームロード時イベント Private Sub frmNameClient_Load(sender As Object, e As EventArgs) Handles Me.Load '[Start Named Pipe]ボタンを許可設定 Me.Button1.Enabled = True '[Send Message]ボタンを不可設定 Me.Button2.Enabled = False End Sub '[Start Named Pipe]ボタンクリック時イベント Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Try '名前付きパイプ・クライアント生成 pipeStream = New NamedPipeClientStream(".", _ "PIPE_TEST", _ PipeDirection.InOut, _ PipeOptions.None, _ TokenImpersonationLevel.Impersonation) '名前付きパイプ接続 pipeStream.Connect(1000) 'ストリームライタ生成 pipeSw = New StreamWriter(pipeStream) pipeSw.AutoFlush = True '[Start Named Pipe]ボタンを不可設定 Me.Button1.Enabled = False '[Send Message]ボタンを許可設定 Me.Button2.Enabled = True Catch ex As Exception MsgBox(ex.Message) End Try End Sub '[Send Message]ボタンクリック時イベント Private Sub Button2_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button2.Click Try '送信文字列取得 Dim strTX As String = Me.TextBox1.Text.Trim pipeSw.WriteLine(strTX) If strTX = "" Then 'ストリームライタを閉じる pipeSw.Close() '名前付きパイプを閉じる pipeStream.Close() '[Start Named Pipe]ボタンを許可設定 Me.Button1.Enabled = True '[Send Message]ボタンを不可設定 Me.Button2.Enabled = False End If Catch ex As Exception MsgBox(ex.Message) End Try End Sub 'フォームクローズ時イベント Private Sub frmNameClient_FormClosed(sender As Object, _ e As FormClosedEventArgs) Handles Me.FormClosed 'ストリームライタを閉じる If Not pipeSw Is Nothing Then pipeSw.Close() End If '名前付きパイプを閉じる If Not pipeStream Is Nothing Then pipeStream.Close() End If End Sub End Classクライアント側でフォームを閉じることでパイプの接続が閉じるため、サーバー側に通信の終了が通知できます。
プログラムの実行は以下の様になります。関連する記事
⇒Remoting の IPC を使ったプロセス間通信について
⇒Remoting の IPC を使ったプロセス間通信についてその2(HTTPチャネル)
⇒名前付きパイプを使ったプロセス間通信についてその2(複数クライアントとの通信)
⇒名前付きパイプを使ったプロセス間通信についてその3(クライアントとの双方向通信)
-
関数はJavaScriptで繰り返し行う処理などをまとめたものを言います。 他の言語を使っている方には特に説明は必要無いかとは思いますが、 JavaScript特有の記述もあります。 関数の宣言方法の違いによって以下の2種類がありますので、それぞれについて見ていきます。
■一般的な関数の宣言(functionを使った宣言)
function キーワードを使った関数宣言の構文は以下の様になっています。 function キーワードの後に「関数名」を書いて関数の引数が在れば「引数リスト」(複数あれば「,」カンマで区切ります)を記述します。 関数内部には行いたい処理を記述し、最後に関数として戻したい値があれば return 文で戻してやります。
function 関数名( [ 引数リスト ] ) { // 関数内の処理 ... [return [戻り値];] }基本的な使い方は以下のソースを見て下さい。
func1 という名前で関数を宣言しています。1から引数に与えられた数値までの合計を計算する簡単な関数です。 本来ならば、引数の値チェック(数値以外の文字列等が渡されたらどうするか)や、合計値の桁あふれなどにもチェックが必要かもしれません。
取敢えずこの関数を実行するために、関数の戻り値を変数に代入しています。 尚、戻り値を直接別の関数に渡すことも可能です。(以下では console.log への引数の値として渡されます)//関数の宣言(引数までの合計を計算) function func1( intPara ) { var sum = 0; for (var i = 1; i <= intPara; i++) { sum = sum + i; } return sum; } //変数の宣言 var val; //関数の実行 val = func1(10); // 変数への代入 console.log(val); // 55 と表示される console.log(func1(100)); // 5050 と表示される
「引数」が文字列の場合を想定した関数の例を以下に示します。//関数の宣言(引数に文字列指定) function func2( strPara1, strPara2 ) { var strRet = "" + strPara1 + "..." + strPara2; return strRet; } //関数の実行 console.log(func2("10", "aaaa")); // 10...aaaa と表示される関数の戻り値としてオブジェクトを指定
関数の戻り値としてオブジェクトを指定できます。以下にその例を示します。
関数としては2個の引数を持ち、それぞれをオブジェクトのプロパティの値として設定しています。 関数の実行後、戻り値をそのままコンソールに表示すれば結果はオブジェクトが戻されることが分かります。
ここで注意が必要なのですが、関数の引数としては数値でも文字列でも指定できますので オブジェクトのプロパティ値もそれぞれ設定したままが戻されます。//関数の宣言(戻り値がオブジェクトの場合) function func3( strPara1, strPara2 ) { //オブジェクトの宣言 var obj = { Para1: strPara1, Para2: strPara2, } //オブジェクトを戻す return obj; } //関数の実行(変数の宣言も同時に行う) var val = func3(100, "aaaa"); console.log(val); // Object { Para1: 100, Para2: "aaaa" } と表示される console.log(val.Para1); // 100 と数値として表示される console.log(val.Para2); // aaaa と文字列として表示される val = func3("100", "aaaa"); console.log(val); // Object { Para1: "100", Para2: "aaaa" } と表示される console.log(val.Para1); // 100 と文字列として表示される console.log(val.Para2); // aaaa と文字列として表示される上の例でもありますが、引数の型等をどの様に定義するかは関数を作る前によく考えておく必要があります。
関数の引数としてオブジェクトを指定
引数にArrayやユーザ定義のオブジェクトを指定できますが、 関数内でそのオブジェクトのプロパティを変更すると、関数の呼出元にも反映されるため注意が必要です。 以下にその例を示します。
//関数の宣言(引数がオブジェクトの場合) function func4( objPara ) { //プロパティを変更 objPara.prop1 = "000"; } //オブジェクトの宣言 var obj = { prop1: "1234" , prop2: "abc" }; console.log(obj); // Object { prop1: "1234", prop2: "abc" } と表示される //関数の呼出 func4( obj ); console.log(obj); // Object { prop1: "000", prop2: "abc" } と表示される //----- //関数の宣言(引数がArrayの場合) function func5( arr ) { //配列の内容を変更 arr[1] = "111"; } //Arrayの宣言 var arr = new Array("aaa", "bbbb", "123"); console.log(arr); // Array(3) [ "aaa", "bbbb", "123" ] と表示される //関数の呼出 func5( arr ); console.log(arr); // Array(3) [ "aaa", "111", "123" ] と表示される
引数にオブジェクト、及びArrayの場合を例にしましたが、関数内でのプロパティの変更が 関数外にも影響していることが分かります。
■関数式(関数リテラル)による宣言
今までは function キーワードを使った構文としての関数宣言でしたが、 関数式として宣言することができます。
//関数式による関数の宣言 var test = function (strPara) { //引数の前後に括弧([])を付加して戻す return "[" + strPara + "]"; }; //ここで文の最後であるセミコロン「;」を記述(無くてもOKですが) //関数の実行 var str = test("12345"); console.log(str); // [12345] と表示される //ちなみに「test」を表示する console.log(test); // function test()... と表示される
上の例からもわかりますが var test と変数 test を宣言しているようですが、 test の実体は関数として存在しますので、これを関数の様に呼出すことができます。
ただし関数式は名前をつけることもでき、関数内で自身を参照することができます。 以下の例では、1から引数値までの総和を求める処理を行っています。 sum と言う名前を関数に付けて自分自身の中で再帰的に呼び出しを行い、 引数値から順次1小さい値を加算することを再帰的に行っています。//[1~N]までの総和を求める関数を関数式で宣言 var summation = function sum(intPara) { if (intPara <= 1) { //1になったらそこで再帰呼出し終了 return 1; } else { //再帰呼出しで順次1小さい値を加算してゆく return intPara + sum(intPara - 1); } }; //関数の実行 console.log(summation(1)); // 1 と表示される console.log(summation(2)); // 3 と表示される console.log(summation(3)); // 6 と表示される console.log(summation(10)); // 55 と表示される console.log(summation(100));// 5050 と表示される
関数式で宣言した関数オブジェクトを引数にした関数を宣言し、 それぞれの関数式での関数を呼び分ける方法を以下に記します。//関数オブジェクトを引数にした関数宣言 function math(fn, val) { //関数オブジェクトを実行 return fn(val); } //関数式による引数を2乗する関数宣言 var sqr = function(val) { return val * val; }; //関数式による引数を +1 する関数宣言 var inc = function(val) { return val + 1; }; //関数式による引数を -1 する関数宣言 var dec = function(val) { return val - 1; }; //関数式による引数を2倍する関数宣言 var mul2 = function(val) { return val * 2; }; //関数オブジェクトを変えて関数をコールする console.log(math(sqr, 10)); // 100 と表示される console.log(math(inc, 10)); // 11 と表示される console.log(math(dec, 10)); // 9 と表示される console.log(math(mul2, 10)); // 20 と表示される