或る日、デスクトップからWindowsネットワーク上の他のユーザーやLinuxなどのリモートホストのファイルやフォルダに、突然アクセスする事が出来なくなりました。

と言うか、Explorerのネットワークから自分以外のコンピュータが表示されなくなってしまいました。他のWindows7やWindows8.1で試したところ、正常にアクセス出来たのですが。

ヤフー(Yahoo!)で色々と調べたところ、共有サービスに必要なクライアントサービスを、お馬鹿にも削除してしまったからの様です。

解説には、クライアントサービスが動作出来ない状態として、

『Microsoftネットワーク用クライアント』が表示されていない⇒インストールされていない
『Microsoftネットワーク用クライアント』は表示されているが無効になっている

とありましたので、インストールする事に。


◆Microsoftネットワーク用クライアントをインストールする

1.Windows7の「スタート」画面で、「コントロールパネル」をクリックします。

スタート画面

2.「コントロールパネル」の画面が表示されますので、

コントロールパネル画面

「ネットワークとインターネット」カテゴリの「ネットワークの状態とタスクの表示」項目をクリックします。

3.「ネットワークと共有センター」の画面が表示されますので、

ネットワークと共有センター画面

左側の「アダプターの設定の変更」項目をクリックします。

4.「ネットワーク接続」の画面が表示されますので、

ネットワーク接続画面

設定を行う(ここではローカルエリア接続の)「接続アイコン」を右クリック後、「プロパティ」をクリックします。

5.「ローカルエリア接続のプロパティ」の画面が表示されますので、

ネットワーク接続画面

「Microsoftネットワーク用クライアント」項目が無いのを確認して、「インストール」ボタンをクリックします。

注意)表示されているが無効になっているについて

「Microsoftネットワーク用ファイルとプリンター共有」項目が存在しても、「□」の中にチェックが無ければ、共有サービスが動作出来ない状態となっています。

6.「ネットワーク機能の種類の選択」画面が表示されますので、

ネットワーク接続画面

「クライアント」を選択後、「追加」ボタンをクリックします。

注意)ネットワーク機能の種類の概要

①クライアントは、接続しているネットワーク上のコンピュータやファイルへのアクセスを提供します。
②サービスは、  ファイルとプリンターの共有などの追加機能を提供します。
③プロトコルは、 コンピュータ間の通信に使用される言語です。

7.「ネットワーク クライアントの選択」画面が表示されますので、

ネットワーク接続画面

「Client for Microsoft Networks」を選択後、「OK」ボタンをクリックします。

★以上で、コンピュータからMicrosoftネットワーク上のリソースにアクセスする為の問題の一つは、解決出来た筈です。

粗々、お釈迦のLIFEBOOK(Windows7)に替えて、新たに使い始めたPC(勿論、Windows7)にもFTPソフトが欲しいなぁという事で、2月18日時点で「窓の杜」での最新版の「FFFTP Ver5.8」をダウンロード&インストール<好きな所へ解凍するだけ>。

但し、Windows7 対応は32ビット版で、64ビット版は Windows10 以降だそうで残念。と言うのも、ハードさえ持てば後20年位は不自由なく使えるでしょ? と思っているので。

私的には、日本で最も有名で利用者の多い「FFFTP」も、Windows7では粗最終版かな? という事で、改めて、FFFTPの暗号化を含めた設定をメモしてみる事に。

さて、ヤフー(YAHOO!)で検索してみると、一部で「デフォルトのマスターパスワード」が悪さをしているらしいので、面倒でもマスターパスワードは設定した方が宜しいかと。(パスワード忘れたら、其れは其れで悲惨ですが)


◆デフォルトのマスターパスワードの更新

1.インストール終了後FFFTPを起動すると、次の画面が立ち上がります。

FFFTP初回起動画面

どうも、赤線で示した「デフォルトのマスターパスワードが使われます.」の様に、パスワードが設定されているのかも・・・。
最前部(アクティブ)ウインドウの「閉じる」ボタンか「×」アイコンをクリックし、アクティブウインドウを閉じます。

2.FFFTPの「メインウインドウ」画面となりますので、

FFFTPメインウインドウ

メニューバーの「接続」から「設定」を選択し、更に「マスターパスワードの変更」をクリックします。

3.マスターパスワードの入力画面が表示されますので、

マスターパスワード入力画面

お好きなマスターパスワードを1度入力し、「OK」ボタンをクリックします。再度(パスワードを)入力する画面が表示されますので、同一パスワードを入力して下さい(同じ事を2回繰り返します)。

注意)マスターパスワードを設定すると、FFFTPを起動する度にパスワードの入力が必要になります。パスワードを絶対にお忘れなく!


◆FFFTPの設定

1.デフォルトのマスターパスワード更新後、

1.1)メニューバーの「接続」から「ホストの設定」をクリックします。

FFFTPメインウインドウ

または

1.2)一旦FFFTPを終了し、再度FFFTPを起動します。

2.「ホスト一覧」のダイアログボックスが表示されますので、

ホスト一覧画面

「新規ホスト」ボタンをクリックします。

3.「ホストの設定」ダイアログが表示されますので、

3.1)「ホストの設定」画面で、「基本」タブを選択し、

ホストの設定画面

①ホストの設定名     :お好きな(識別子的)名前
②ホスト名(アドレス)  :FTPサーバー(s???.coreserver.jp、s???.xrea.comなど)
③ユーザー名       :アカウント名(FTPユーザー名)
④パスワード/パスフレーズ:パスワード
⑤ローカルの初期フォルダ :PCの任意のディレクトリ
⑥ホストの初期フォルダ  :/public_html(転送先ディレクトリ)

を「CORESERVER.JP NEW SIGNUP」のメールに記載されている情報を下に入力します。

3.2)「ホストの設定」画面で、「拡張」タブを選択

ホストの設定の拡張タブ

①PASVモードを使うにチェックを入れます。
②ポート番号の「21」を確認

3.3)「ホストの設定」画面で、「暗号化」タブを選択

ホストの設定の暗号化タブ

①暗号化なしで接続を許可:チェックを外す
②FTPS(Explicit)で接続 :チェックを入れる
③FTPS(Implicit)で接続 :チェックを外す

上記、3.1)~3.3)を入力&確認して、「OK」ボタンをクリックします。


◆FFFTPでの接続

「ホスト一覧」画面で登録したホスト名を選択し、「接続」ボタンをクリックします。

「暗号化の状態の保存」画面が表示されますので、

暗号化の状態の保存画面

「はい」ボタンをクリックします。


★FFFTP初期設定後の感

初めて「FTPS(Explicit)」で接続した場合でも、証明書の確認(警告?)画面が表示されなくなりました。

・FFFTP作成者が目を瞑るように・・・・・した?

・CNの一致でバリュードメインがケチらなくなった?<sxxx.coreserver.jp で *.value-domain.com は頂けないよね!>

前回は AsyncWaitHandle.WaitOne メソッドで非同期デリゲート処理の終了待ちをしましたので、今回は、IsCompleted プロパティで終了待ちを試してみる事に。(UIサービスを提供するスレッド向き?)

★元ネタ-マイクロソフトドキュメントで、ほぼそのままのソース。


<注意点>

・非同期呼び出しの完了を IsCompleted でポーリングすると、スレッドプールで非同期処理を実行しながら、呼出し元スレッドを実行継続できます。

・何れの終了待ちをするにしても、EndInvokeを常に呼び出して、非同期呼出しの完了とします。 ← 重要!

・エラー処理は手抜きです。


お試し環境
  Windows7 64bit Edition
  Visual Basic 2008 AnyCPU対象


/*-------------------------------- お試し結果 ------------------------------*/

Button3 thread 10 does some work.
Wait Test begins.
.....
.....
.....
.....
.....
.....
.....
.....
The call executed on thread 7, with return value "Wait time was 2000.".

/*----------------------------------------------------------------------------*/


/*----------------------------  お試しソース -------------------------------*/

Public Class Form1

    Private Delegate Function dlgtAsyncWaitChk(ByVal tm As Integer, ByRef id As Integer) As String

    Private Function waitTest(ByVal term As Integer, ByRef threadId As Integer) As String
        Debug.Print("Wait Test begins.")
        System.Threading.Thread.Sleep(term)
        threadId = System.Threading.Thread.CurrentThread.ManagedThreadId
        Return String.Format("Wait time was {0}.", term.ToString())
    End Function

    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
        Dim threadId As Integer
        Dim awc As dlgtAsyncWaitChk
        Dim result As IAsyncResult
        Dim retValue As String

        awc = New dlgtAsyncWaitChk(AddressOf waitTest)
        result = awc.BeginInvoke(2000, threadId, Nothing, Nothing)

        Debug.Print("Button3 thread {0} does some work.", System.Threading.Thread.CurrentThread.ManagedThreadId)

        While Not result.IsCompleted
            System.Threading.Thread.Sleep(250)
            Debug.Print(".....")
        End While

        retValue = awc.EndInvoke(threadId, result)
        Debug.Print("The call executed on thread {0}, with return value ""{1}"".", threadId, retValue)
    End Sub

End Class

/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*============================================================================*/

前回は EndInvoke メソッドで非同期デリゲート処理の終了待ちをしましたので、今回は、AsyncWaitHandle プロパティの WaitOne メソッドで終了待ちを試してみる事に。(当然、UIスレッドには不向き)。

★元ネタ-マイクロソフトドキュメントで、ほぼそのままのソース。


<注意点>

・WaitHandle は非同期呼び出しが完了すると通知されるので、WaitOne 呼び出しでこれを待機します。

・待機ハンドルの使用終了と同時にシステムリソースを解放する場合、WaitHandle.Close メソッドを呼び出します。

・エラー処理は手抜きです。


お試し環境
  Windows7 64bit Edition
  Visual Basic 2008 AnyCPU対象


/*-------------------------------- お試し結果 ------------------------------*/

Button2 thread 10 does some work.
Wait Test begins.
The call executed on thread 7, with return value "Wait time was 2000.".

/*----------------------------------------------------------------------------*/


/*----------------------------  お試しソース -------------------------------*/

Public Class Form1

    Private Delegate Function dlgtAsyncWaitChk(ByVal tm As Integer, ByRef id As Integer) As String

    Private Function waitTest(ByVal term As Integer, ByRef threadId As Integer) As String
        Debug.Print("Wait Test begins.")
        System.Threading.Thread.Sleep(term)
        threadId = System.Threading.Thread.CurrentThread.ManagedThreadId
        Return String.Format("Wait time was {0}.", term.ToString())
    End Function

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Dim threadId As Integer
        Dim awc As dlgtAsyncWaitChk
        Dim result As IAsyncResult
        Dim retValue As String

        awc = New dlgtAsyncWaitChk(AddressOf waitTest)
        result = awc.BeginInvoke(2000, threadId, Nothing, Nothing)

        Debug.Print("Button2 thread {0} does some work.", System.Threading.Thread.CurrentThread.ManagedThreadId)

        result.AsyncWaitHandle.WaitOne()

        retValue = awc.EndInvoke(threadId, result)

        result.AsyncWaitHandle.Close() ' ガベージコレクションの効率化
        Debug.Print("The call executed on thread {0}, with return value ""{1}"".", threadId, retValue)
    End Sub

End Class

/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*============================================================================*/

5/16(月)、コアサーバー(coreserver.jp)で最新高速サーバーへの移転メンテナンスが行われた結果、Movable Type 5で、ログインが出来なくなりました。

ログイン不可の症状は、たった一行表示のみ。

『Got an error: Unescaped left brace in regex is illegal here in regex; marked by <- HERE in m/{{ <- HERE support}}/?/』

仕方が無いので検索すると、

★本家Perl?のWebページ「perldiag - さまざまな Perl 診断メッセージ」(https://perldoc.jp/docs/perl/5.28.0/perldiag.pod)に解説が有りました。

Unescaped left brace in regex is illegal here in regex; marked by <-- HERE in m/%s/

(F) 正規表現中で リテラルな "{" 文字 (U+007B LEFT CURLY BRACKET) にマッチングしたいときに 覚えておくべき単純な規則は、何らかの方法で それぞれのリテラルな実体をエスケープすることです。 一般的に一番簡単なのは、"\{" のように逆スラッシュを前置するか、 かっこでかこむ ("[{]") ことです。. パターン区切り文字も中かっこの場合、マッチングする右中かっこ ("}") も、パーサーの混乱を避けるためにエスケープするべきです; 例えば:

 qr{abc\{def\}ghi}

リテラルな "{" 文字にエスケープを強制することにより、 将来のリリースで様々な方法で Perl 言語を拡張できるようになります。 既存のコードを不必要に壊すことを避けるために、 拡張が "{" をリテラルとして使うことと競合しそうにない文脈では 制限は強制されません。


★CORESERVERの運営元 バリューサーバー【サーバーシステムの増強と移行について】のページには、対処法のQ&Aが有りました(https://www.value-server.com/info/brandnew2022.html)。

Q:Movable Typeで「Got an error: Unescaped left brace in regex is illegal here in regex; marked by <-- HERE in m/{{ <-- HERE support}}/?/」が発生しました。

A:こちらのファイルを下記のように修正の上、正常に動作するかご確認下さい。 /virtual/アカウント名/public_html/ドメイン名/lib/MT/App/CMS.pm

4828行目
$css =~ s#{{support}}/?#$app->support_directory_url#ie;

$css =~ s#\{\{support}}/?#$app->support_directory_url#ie;

4830行目
$css =~ s#{{theme_static}}/?#$theme->static_file_url#ie;

$css =~ s#\{\{theme_static}}/?#$theme->static_file_url#ie;


ただ、何行目かは、それぞれお使いのMovable Typeのバージョンによって異なる様です。
<使用中のMTOS5は、4976行目と 4978行目>


★CORESERVER補足情報

・修正ファイルの場所
/public_html/設置したMTのディレクトリ名/lib/MT/App/CMS.pm

非同期デリゲート処理の呼び出し完了待ちを検索していたら、マイクロソフトが言うには、

・呼び出すメソッドと同じシグネチャを持つデリゲートを定義すれば、.NETでは全てのメソッドを非同期的に呼び出すことが出来る。

・デリゲート定義では、共通言語ランタイム(CLR)により適切なシグネチャが使用された BeginInvoke および EndInvoke メソッドが自動的に定義される。

・BeginInvoke メソッドは、非同期的に実行するメソッドと同じパラメーターと、2つの省略可能な追加パラメーターを持っている。
 追加の1つ目は、非同期呼び出しが完了したときに呼び出されるメソッドを参照する AsyncCallback デリゲート
 追加の2つ目は、コールバックメソッドに情報を渡すユーザー定義オブジェクト。

・BeginInvoke は IAsyncResult を返すので、これを使用して非同期呼び出しの進捗状況を監視出来る。

・EndInvoke メソッドは、非同期処理の呼び出し結果を取得し、BeginInvoke の後であればいつでも呼び出すことが出来る。

・EndInvoke のパラメーターには、非同期実行するメソッドの <Out>ByRef と ByRef、BeginInvoke の戻り値 IAsyncResult が含まれる。

らしいので、追加パラメーター2つを省略して(=Nothing)、EndInvokeメソッドで非同期処理の終了待ちを試してみる事に(当然、UIスレッドには不向き)。

★元ネタ-マイクロソフトドキュメントで、ほぼそのままのソース。


<注意点>

・非同期呼び出しが完了していない場合、 EndInvoke は非同期呼び出しが完了するまで呼び出し元スレッドをブロック。

・エラー処理は手抜きです。


お試し環境
  Windows7 64bit Edition
  Visual Basic 2008 AnyCPU対象


/*-------------------------------- お試し結果 ------------------------------*/

Wait Test begins.
Button1 thread 10 does some work.
The call executed on thread 7, with return value "Wait time was 2000.".

/*----------------------------------------------------------------------------*/


/*----------------------------  お試しソース -------------------------------*/

Public Class Form1

    Private Delegate Function dlgtAsyncWaitChk(ByVal tm As Integer, ByRef id As Integer) As String

    Private Function waitTest(ByVal term As Integer, ByRef threadId As Integer) As String
        Debug.Print("Wait Test begins.")
        System.Threading.Thread.Sleep(term)
        threadId = System.Threading.Thread.CurrentThread.ManagedThreadId
        Return String.Format("Wait time was {0}.", term.ToString())
    End Function

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim threadId As Integer
        Dim awc As dlgtAsyncWaitChk
        Dim result As IAsyncResult
        Dim retValue As String

        awc = New dlgtAsyncWaitChk(AddressOf waitTest)
        result = awc.BeginInvoke(2000, threadId, Nothing, Nothing)

        Debug.Print("Button1 thread {0} does some work.", System.Threading.Thread.CurrentThread.ManagedThreadId)

        retValue = awc.EndInvoke(threadId, result)
        Debug.Print("The call executed on thread {0}, with return value ""{1}"".", threadId, retValue)
    End Sub

End Class

/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*============================================================================*/

フリーズしてる訳でもないんだから、プログレスバーでも出しておけば良いんじゃないの?と書いておきながら、非同期処理でUIにプログレスバーを表示してみる事に。

矢張り曲者は、別スレッドからのUIコントロール更新でしょうか。(デリゲートメソッドもコールバックメソッドも、スレッドプールで処理を実行)

【デリゲートメソッドの終了待ち】

・BeginInvoke呼出しメソッドの終了をEndInvokeで受けるのですが、UI(メイン)スレッド中に EndInvoke を呼び出すと非同期(デリゲート)メソッドが完了するまで、呼出し元スレッドがブロックされてしまいます。*1

・UIサービスを提供するスレッドからは、EndInvoke を呼び出さない方が宜しいかと。⇒ コールバックメソッドの利用

*1:IDEのデバッグ実行では、飛んだまま戻ってこず


<注意点>

・仮引数の範囲のチェック(0%~100%のプログレスバー設定値)や何処でチェックするかなどは、多分に好みの問題だと思うので・・・。

・エラー処理は手抜きです。


お試し環境
  Windows7 64bit Edition
  Visual Basic 2008 AnyCPU対象


/*-------------------------------- お試し結果 ------------------------------*/

・ボタン1を押すと、

プログレスバー表示その1

プログレスバー表示その2

・ボタン2を押すと、

プログレスバー表示その3

プログレスバー表示その4

/*----------------------------------------------------------------------------*/


/*----------------------------  お試しソース -------------------------------*/

Public Class Form4

    Private Delegate Sub dlgtWork1Prgbar1(ByVal data As Integer)

    Private Sub Set1Prgbar1Value(ByVal percent As Integer)
        If InvokeRequired = True Then
            Invoke(New dlgtWork1Prgbar1(AddressOf Set1Prgbar1Value), percent)
        Else
            ProgressBar1.Value = percent
            If percent >= 100 Then
                Label1.ForeColor = System.Drawing.Color.Red
                Label1.Text = "完了"
                Button1.Enabled = True
            End If
        End If
    End Sub

    Private Sub work1PrgBar1(ByVal no As Integer)
        For i As Integer = no To 10
            System.Threading.Thread.Sleep(500) ' 時間の掛かる処理の代替
            Set1Prgbar1Value(i * 10)
        Next
    End Sub

    Private Sub bar1Callback1(ByVal ar As IAsyncResult)
        Dim arWk1Prgbar1 As dlgtWork1Prgbar1

        arWk1Prgbar1 = CType(ar.AsyncState, dlgtWork1Prgbar1)
        arWk1Prgbar1.EndInvoke(ar)
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim wk1PrgBar1 As dlgtWork1Prgbar1
        Dim ar As IAsyncResult

        Button1.Enabled = False
        Label1.ForeColor = System.Drawing.Color.FromKnownColor(KnownColor.ControlText)
        Label1.Text = "実行中・・・"

        Set1Prgbar1Value(0)
        wk1PrgBar1 = New dlgtWork1Prgbar1(AddressOf work1PrgBar1)
        ar = wk1PrgBar1.BeginInvoke(1, New AsyncCallback(AddressOf bar1Callback1), wk1PrgBar1)
    End Sub

/*--------------------------  お試しソース 改 -----------------------------*/

    Structure prgrsData
        Dim percent As Integer
        Dim leftTm As TimeSpan
    End Structure

    Private Delegate Sub dlgtWork2Prgbar1(ByVal data As prgrsData)

    Private Sub Set2Prgbar1Value(ByVal prgDat As prgrsData)
        If InvokeRequired = True Then
            Invoke(New dlgtWork2Prgbar1(AddressOf Set2Prgbar1Value), prgDat)
        Else
            ProgressBar1.Value = prgDat.percent
            Label1.Text = String.Format("あと {0:0.0} 秒・・・", prgDat.leftTm.TotalSeconds)
            If prgDat.percent >= 100 Then
                Label1.ForeColor = System.Drawing.Color.Red
                Label1.Text = "完了"
                Button2.Enabled = True
            End If
        End If
    End Sub

    Private Sub work2PrgBar1(ByVal prgDat As prgrsData)
        Const Cntr As Integer = 10
        Const MSec As Integer = 500
        Dim pData As prgrsData

        For i As Integer = prgDat.percent To Cntr
            System.Threading.Thread.Sleep(MSec) ' 時間の掛かる処理の代替
            pData.percent = i * Cntr
            pData.leftTm = TimeSpan.FromMilliseconds((Cntr - i) * MSec)
            Set2Prgbar1Value(pData)
        Next
    End Sub

    Private Sub bar1Callback2(ByVal ar As IAsyncResult)
        Dim arWk2Prgbar1 As dlgtWork2Prgbar1

        arWk2Prgbar1 = CType(ar.AsyncState, dlgtWork2Prgbar1)
        arWk2Prgbar1.EndInvoke(ar)
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Dim wk2Prgbar1 As dlgtWork2Prgbar1
        Dim ar As IAsyncResult
        Dim progDat As prgrsData

        Button2.Enabled = False
        Label1.ForeColor = System.Drawing.Color.FromKnownColor(KnownColor.ControlText)
        Label1.Text = "実行中・・・"

        progDat.percent = 0
        progDat.leftTm = TimeSpan.FromMilliseconds(5000)
        Set2Prgbar1Value(progDat)
        progDat.percent = 1
        wk2Prgbar1 = New dlgtWork2Prgbar1(AddressOf work2PrgBar1)
        ar = wk2Prgbar1.BeginInvoke(progDat, New AsyncCallback(AddressOf bar1Callback2), wk2Prgbar1)
    End Sub

End Class

/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*============================================================================*/

前回の非同期処理では、別スレッドからUIコントロールの Label1.Text 更新が出来ませんでしたので、別スレッドからUIコントロール更新の仕方を浚ってみる事に。

別スレッドで実行しているメソッドがUIを書き換える場合、処理をUIスレッドに移行する必要が有りますが、それにはWindowsフォームが備えているInvokeメソッドを呼び出すと良い様です。

尚、別スレッドで実行されると分かっているなら、直接Invokeメソッドを呼び出した方が簡単なのですが、後の改修などを考慮してInvokeRequiredプロパティを使用するのが定石?なんだそうです。


<注意点>

・InvokeRequiredプロパティは、UIスレッドと異なるスレッドで実行していると"真"を返します。

・分かり易い?様に敢えて、別スレッドからUIを更新する為のデリゲートを定義していますが、(関数呼び出しとは違い)デリゲートの名称は区別されず、同一のシグネチャが重要なので、「Invoke(New dlgtShowMsg(AddressOf SetLabel1Text), ・・・)」でも構いません。


お試し環境
  Windows7 64bit Edition
  Visual Basic 2008 AnyCPU対象


/*-------------------------------- お試し結果 ------------------------------*/

・メッセージボックスの表示は、前回と同じなので省略

・ボタン3を押すと、約2秒後にラベルのテキストを表示

ラベルの更新テキストを表示

/*----------------------------------------------------------------------------*/


/*----------------------------  お試しソース -------------------------------*/

Public Class Form3

    Delegate Sub dlgtShowMsg(ByVal msg As String)

    Private Sub ShowMessage(ByVal msg As String) ' BeginInvokeから呼ばれるので別スレッド
        System.Threading.Thread.Sleep(2000)
        Invoke(New dlgtSetLabel1(AddressOf SetLabel1Text), "Asynchronous Method" & " and " & msg) ' 別スレッドからUIを更新するInvoke呼出し
        MsgBox(msg, MsgBoxStyle.Information, "Asynchronous Method")
    End Sub

    Private Sub showCallback(ByVal ar As IAsyncResult) ' コールバックメソッド
        Dim arShowMsg As dlgtShowMsg

        arShowMsg = CType(ar.AsyncState, dlgtShowMsg)
        arShowMsg.EndInvoke(ar)
    End Sub

    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
        Dim showMsg As dlgtShowMsg
        Dim ar As IAsyncResult

        showMsg = New dlgtShowMsg(AddressOf ShowMessage)
        ar = showMsg.BeginInvoke("See actions", New AsyncCallback(AddressOf showCallback), showMsg)

        MsgBox("See actions", MsgBoxStyle.Information, "Asynchronous Test")
    End Sub

    Delegate Sub dlgtSetLabel1(ByVal msg As String) ' ①別スレッドからUIを更新するデリゲートの定義

    Private Sub SetLabel1Text(ByVal msg As String) ' ②別スレッドからUIを更新するメソッドの作成
        If InvokeRequired = True Then ' ③Windowsフォームは別スレッドからのUI操作は保障されていない
            Invoke(New dlgtSetLabel1(AddressOf SetLabel1Text), "Required " & msg)
        Else
            Label1.Text = msg
        End If
    End Sub

End Class

/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*============================================================================*/

前回、デリゲートの機能の一つを浚いましたので、今回はDelegateの二つ目の機能である「非同期処理」について浚ってみる事に。

色々と調べてみると、VBコンパイラはDelegateを宣言すると、自動的にBeginInvokeメソッドとEndInvokeメソッドを生成してくれます。

UIがフリーズしないようにする為の非同期処理は、この二つのメソッドにより可能となる様です。

【非同期呼出しの勘所】

・呼び出し方:BeginInvoke(デリゲートの仮引数 *1、コールバックデリゲート、Object) As IAsyncResult

・BeginInvoke メソッドを使用する事により、デリゲート経由で呼び出すメソッドを別のスレッドで非同期に実行。

・コールバックメソッドの中で EndInvoke メソッドを呼び出し、メモリリークの可能性を回避。

・デリゲート経由でメソッドを呼び出すと、CLRが管理しているスレッドプールに実行を依頼。その後、制御が直ぐに戻ってくるので呼び出したメソッドとは関係無く、(終了を待たずに)プログラムを先に進める事が可能。

・コールバックメソッドはスレッドプール側で実行されるので、UIを直接更新する事は不可。

*1:引数の数はデリゲート宣言の仮引数に基づくので、お試しソースでは一つ。


<注意点>

・メソッドのシグネチャは同一である事が重要。コールバックメソッドも対応するデリゲートの引数の数と型、戻り値の型に合わせる事。

・BeginInvoke のデリゲート呼出しは非同期で行われる(別スレッド)ので、メソッド呼出し後、直ぐに呼び出し元スレッドに制御が戻る。今回の場合、メッセージボックスのタイトルは「Asynchronous Test」「Asynchronous Method」の順で表示される。

・Windowsフォームは、別スレッドからのUI操作は保障されていないので、②の Label1.Text のコメントを外して実行すると、「有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール 'Label1' がアクセスされました。」と例外が発生する。


お試し環境
  Windows7 64bit Edition
  Visual Basic 2008 AnyCPU対象


/*-------------------------------- お試し結果 ------------------------------*/

・ボタン2を押すと、直ぐに表示

Asynchronous Test のタイトルで表示

・その約2秒後に表示

Asynchronous Method のタイトルで表示

/*----------------------------------------------------------------------------*/


/*----------------------------  お試しソース -------------------------------*/

Public Class Form3

    Delegate Sub dlgtDispRpt(ByVal msg As String) ' ①非同期 デリゲートの定義

    Private Sub DisplayRpt(ByVal msg As String) ' ②非同期 デリゲートで呼び出すメソッドの作成
        System.Threading.Thread.Sleep(2000)
        '        Label1.Text = "Asynchronous Method" & " and " & msg
        MsgBox(msg, MsgBoxStyle.Information, "Asynchronous Method")
    End Sub

    Private Sub dispCallback(ByVal ar As IAsyncResult) ' コールバックメソッド
        Dim arDispRpt As dlgtDispRpt

        arDispRpt = CType(ar.AsyncState, dlgtDispRpt)
        arDispRpt.EndInvoke(ar)
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Dim dispRpt As dlgtDispRpt ' ③非同期 デリゲート型の変数を宣言
        Dim ar As IAsyncResult

        dispRpt = New dlgtDispRpt(AddressOf DisplayRpt) ' ④非同期 デリゲート型変数にメソッドを登録
        ar = dispRpt.BeginInvoke("See actions", New AsyncCallback(AddressOf dispCallback), dispRpt) ' ⑤非同期 デリゲートの呼び出し

        MsgBox("See actions", MsgBoxStyle.Information, "Asynchronous Test")
    End Sub

End Class

/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*============================================================================*/

UIは元よりUXは尚更、快適な操作環境を云々、と叫ばれてから早十余年。タッチ操作が主流?となった今では、当然の如き言われ様。

でもね、フリーズしてる訳でもないんだし、プログレスバーでも出しておけば良いんじゃないの?と思ってしまうのはさて置き。

色々検索してみると、即応性=非同期 ⇒ コールバック ⇔ デリゲート が良いらしい(単なるマイクロソフトの推奨?)って事で、

以前、C言語でデリゲートとイベントの概要記事「VC++フォームだからこそ、コマンドプロンプトでEXE?」を書きましたので、それを眺めつつ先ずは、Delegateの使い方を浚ってみる事に。

マイクロソフトのドキュメントによると、

デリゲートは、メソッドを参照するオブジェクトです。デリゲートは他のプログラミング言語で使用される関数ポインターに似ているため、"タイプセーフ関数ポインター"と説明されることがあります。しかしながら、関数ポインターとは異なり、Visual Basicのデリゲートは、System.Delegateクラスに基づく参照型です。

と、何とも要領の得ない、教えたくない様な説明。きっと独自では使って欲しくないのでしょうね。

マイクロソフトのドキュメントによれば、Delegateの構文は、

[<attrlist>][accessmodifier][Shadows] Delegate [Sub | Function] name [(Of typeparamlist)][([parameterlist])][As type]

なので、敢えて大雑把に言ってしまえば以下の様な形式かと。

Delegate Sub デリゲート名称(仮引数リスト...)

または

Delegate Function デリゲート名称(仮引数リスト...) As 戻り値の型

★さて、デリゲートとは?と問われれば一種の型です。利用するためのポイントは以下の5つで、

①デリゲートの宣言(メソッドのシグネチャを定義)
②デリゲートで呼び出すメソッドの作成
③デリゲート型の変数を宣言
④デリゲート型変数にメソッドを登録
⑤デリゲートの呼出し

となるようです。


<注意点>

・デリゲートの機能の一つは、メソッドのアドレスを格納する変数の宣言と呼び出すメソッドを登録出来る型。

・メソッドのシグネチャは同一である事が重要。引数の数と型、戻り値の型が同じ。

・通常のデリゲートの呼出しは同期して行われる(同一スレッド)ので、メソッドの処理が完了してから呼び出し元スレッドに制御が戻る。今回の場合、メッセージボックスのタイトルは「Delegate Method」「Delegate Test」の順で表示される。


お試し環境
  Windows7 64bit Edition
  Visual Basic 2008 AnyCPU対象


/*-------------------------------- お試し結果 ------------------------------*/

・ボタン1を押すと、約2秒後に表示

Delegate Method のタイトルで表示

・上記画面のOKボタンを押すと、直ぐに表示

Delegate Test のタイトルで表示

・ボタン1を押すと、約2秒後にラベルのテキストを表示

ラベルの更新テキストを表示

/*----------------------------------------------------------------------------*/


/*----------------------------  お試しソース -------------------------------*/

Public Class Form3

    Delegate Sub dlgtDispMsg(ByVal msg As String) ' ①デリゲートの定義

    Private Sub DisplayMsg(ByVal msg As String) ' ②デリゲートで呼び出すメソッドの作成
        System.Threading.Thread.Sleep(2000)
        Label1.Text = "Delegate Method" & " and " & msg
        MsgBox(msg, MsgBoxStyle.Information, "Delegate Method")
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim dispMsg As dlgtDispMsg ' ③デリゲート型の変数を宣言

        dispMsg = New dlgtDispMsg(AddressOf DisplayMsg) ' ④デリゲート型変数にメソッドを登録
        dispMsg("See actions") ' ⑤デリゲートの呼び出し

        MsgBox("See actions", MsgBoxStyle.Information, "Delegate Test")
    End Sub

End Class

/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*============================================================================*/

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

タグクラウド

ウェブページ

お気に入りリンク

NOP法人 アジアチャイルドサポート 最も大切なボランティアは、自分自身が一生懸命に生きること