perl5.26となる進化?を遂げたCORESERVERサーバー。mtos5.2を新規インストールした儘ではなく、せめてスタイルキャッチャーぐらい使って、見た目も変化させてみたいかなと。

★ですが、スタイルを適用しようとクリックすると、又もや例のエラーが。

Movable Type

エラーが発生しました。
failed loading package StyleCatcher::CMS for routine StyleCatcher::CMS::view: Unescaped left brace in regex is illegal here in regex; marked by <-- HERE in m/{{ <-- HERE static}}/

正規表現の中で、左括弧「{{」がエスケープされてないと言うエラーです。


◆次の3ファイルを下記のように修正の上、正常に動作するか、確認して下さい。

①plugins\StyleCatcher\lib\StyleCatcher\CMS.pm
②plugins\StyleCatcher\lib\StyleCatcher\Library\Default.pm
③plugins\StyleCatcher\lib\StyleCatcher\Library\Local.pm

・CMS.pm ファイルの65行目、66行目、68行目
65: $lib->{url} =~ s/{{static}}/$static_webpath/i;
66: $lib->{url} =~ s/{{support}}/$support_url/i;
68: =~ s/{{theme_static}}/MT::Theme::static_file_url_from_id($lib->{key})/ie;
↓↓↓
65: $lib->{url} =~ s/\{\{static}}/$static_webpath/i;
66: $lib->{url} =~ s/\{\{support}}/$support_url/i;
68: =~ s/\{\{theme_static}}/MT::Theme::static_file_url_from_id($lib->{key})/ie;


・Default.pm ファイルの22行目、23行目、25行目
22: $url =~ s/{{static}}/$static_webpath/i;
23: $url =~ s/{{support}}/$support_url/i;
25: =~ s/{{theme_static}}/MT::Theme::static_file_url_from_id($self->key)/ie;
↓↓↓
22: $url =~ s/\{\{static}}/$static_webpath/i;
23: $url =~ s/\{\{support}}/$support_url/i;
25: =~ s/\{\{theme_static}}/MT::Theme::static_file_url_from_id($self->key)/ie;


・Local.pm ファイルの27行目、28行目、30行目、38行目、39行目、41行目
27: $path =~ s/{{static}}/$static_path/i;
28: $path =~ s/{{support}}/$support_path/i;
30: =~ s/{{theme_static}}/MT::Theme::static_file_path_from_id($self->key)/ie;
38: $url =~ s/{{static}}/$static_webpath/i;
39: $url =~ s/{{support}}/$support_url/i;
41: =~ s/{{theme_static}}/MT::Theme::static_file_url_from_id($self->key)/ie;
↓↓↓
27: $path =~ s/\{\{static}}/$static_path/i;
28: $path =~ s/\{\{support}}/$support_path/i;
30: =~ s/\{\{theme_static}}/MT::Theme::static_file_path_from_id($self->key)/ie;
38: $url =~ s/\{\{static}}/$static_webpath/i;
39: $url =~ s/\{\{support}}/$support_url/i;
41: =~ s/\{\{theme_static}}/MT::Theme::static_file_url_from_id($self->key)/ie;

粗々2年前、CORESERVERサーバーにシステムの増強などを施す移行(マイグレーションだそうな)が行われました。新仕様のCORESERVERサーバーでは何と、perlのバージョンが5.26となってしまいました。

★難という悲劇、難という弱者切り捨て。じゃなかった、セキュリティーの強化なんだそうです。

其のお陰で、真面に動いていた筈のmtos5.2の動作にも降り懸かる不幸が・・・。

取り敢えず気を取り直して、バージョンアップされたCORESERVERサーバーに、perl5.26を利用してmtos5.2を新規インストールしてみる事に。


◆コアサーバーに設置したmtos5.2にアクセス

perl526のCORESERVERサーバーに設置したmtos52にアクセスすると、意外にも? インストール画面は正常に表示されましたが、「Movable Type システム・チェック」や「Movable Type にサインイン」をクリックするとエラーが容赦無く。。。

★Movable Type システム・チェックのエラーは無視

・Movable Type システム・チェックをクリックすると、以下の様なエラーが

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.
Please contact the server administrator at info@coreserver.jp to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.

・コアサーバーの難点は、エラーログを見られない事。(探し方が足りなかったのかな~)<他のレンタルサーバーも見る事が出来ないのでしょうか?>

・ヤフー(Yahoo!)で検索しても原因が分からなかったので、「Movable Type システム・チェック」のエラーはインストールに直接関係が無い事もあり、無視する事に。

★Movable Type にサインインのエラーについては

・Movable Type にサインインをクリックすると、以下の様なエラーが

Movable Type

エラーが発生しました。
\C no longer supported in regex; marked by <-- HERE in m/(\ <-- HERE C)/

・ヤフー(Yahoo!)を検索すると、以下の該当記事1件がヒットしました。

小粋空間さんの
https://www.koikikukan.com/archives/2022/06/30-235555.php
旧バージョンのMTでPerl5.26(Perl5.24以降)に対応する方法

extlib/URI/Escape.pmの sub escape_char の部分を書き換えるのだそうです。

sub escape_char {
return join '', @URI::Escape::escapes{$_[0] =~ /(\C)/g};
}

↓↓↓以下の様に書き換え↓↓↓

sub escape_char {
# Old versions of utf8::is_utf8() didn't properly handle magical vars (e.g. $1).
# The following forces a fetch to occur beforehand.
my $dummy = substr($_[0], 0, 0);

if (utf8::is_utf8($_[0])) {
my $s = shift;
utf8::encode($s);
unshift(@_, $s);
}

return join '', @URI::Escape::escapes{split //, $_[0]};
}

なのですが、『動作を保証するものではありませんので、個人の責任で行ってください。』と言う但し書きが。

★自己責任の決め手は、参照情報として記されていた以下の記事。

お茶の間さんの
http://www.webkoza.com/rooms/2019/02/fedora29mt522.html
Fedora29にMT5.2.2を導入

Escape.pmについてのエラー

このファイルは extlib/URI/ にコピーされたperl標準モジュールのようなので、cpanmでインストールされている最新のEscape.pmをこの場所にコピーして、webkoza_mt.servise再起動したところ、statusでこのモジュールのエラーは無くなった!

perl標準モジュール? 最新モジュールのコピーでエラーが無くなった?? との事なので、

関数 escape_char の内容を書き換える事に!!

CORESERVERのMovable Typeで、左括弧({)が正規表現の中でエスケープされてないエラーが発生
の記事の修正もお忘れなく。


◆「Movable Type にサインイン」をクリックしてmtos52の新規インストール処理を完了

Movable Type のシステムチェック画面で、

システムチェック画面

「必要なPerlモジュールは揃っています。」と表示されるのに、インストール画面の「Movable Type システム・チェック」は、何が気に入らなかったのでしょうね?

誰か、コアサーバーの「error log」が吐かれているのが何処か、知ってるなら教えて!!

★蛇足:データベースが MySQL 5.7 → MariaDB 10.6 へと変更されています

サーバー側はMariaDBとなりましたが、Movable Type 側で選べるのはMySQLのままです(当然ですよね、mtos5.2ですから)。

気にせずMySQLを選んで、インストールを終わらせましょう。

或る日、デスクトップから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

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

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

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

タグクラウド

ウェブページ

お気に入りリンク

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