C#, ソフトウェア開発

Taskの配列を作成し、Task.WhenAllを呼ぶことで、
複数のTaskを同時に実行し、全てのTaskの処理が終了するまで待機する。

C#, ソフトウェア開発

FormBorderStyle.Noneのフォームは、枠がなくなると同時に、周囲の影も消えてしまう。

下記コードを適用することで、Aeroの影を復活することができる。

    public partial class Form1 : Form
    {
        #region Win32API

        [StructLayout(LayoutKind.Sequential)]
        private struct MARGINS
        {
            public int leftWidth;
            public int rightWidth;
            public int topHeight;
            public int bottomHeight;
        }

        [DllImport("dwmapi.dll")]
        private static extern int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins);

        private enum DWMWINDOWATTRIBUTE : uint
        {
            NCRenderingEnabled = 1,
            NCRenderingPolicy,
            TransitionsForceDisabled,
            AllowNCPaint,
            CaptionButtonBounds,
            NonClientRtlLayout,
            ForceIconicRepresentation,
            Flip3DPolicy,
            ExtendedFrameBounds,
            HasIconicBitmap,
            DisallowPeek,
            ExcludedFromPeek,
            Cloak,
            Cloaked,
            FreezeRepresentation
        }

        [DllImport("dwmapi.dll", PreserveSig = true)]
        private static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE attr, ref int attrValue, int attrSize);

        [DllImport("dwmapi.dll")]
        private static extern int DwmIsCompositionEnabled(out bool enabled);

        const int WM_NCPAINT = 0x85;


        #endregion


        public Form1()
        {
            InitializeComponent();

            this.FormBorderStyle = FormBorderStyle.None;
        }

        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_NCPAINT:

                    // デザイン時は処理しない
                    if (!DesignMode)
                    {
                        // Vista以降のOSのみ
                        if (Environment.OSVersion.Version.Major >= 6)
                        {
                            // Aeroが使用可能か確認
                            bool bolAeroEnabled = false;
                            DwmIsCompositionEnabled(out bolAeroEnabled);

                            if (bolAeroEnabled == true)
                            {
                                int intAttrValue = 2;
                                int intAttrSize = sizeof(int);
                                DwmSetWindowAttribute(this.Handle, DWMWINDOWATTRIBUTE.NCRenderingPolicy, ref intAttrValue, intAttrSize);

                                MARGINS objMargins = new MARGINS()
                                {
                                    leftWidth = 1,
                                    rightWidth = 1,
                                    topHeight = 1,
                                    bottomHeight = 1
                                };

                                DwmExtendFrameIntoClientArea(this.Handle, ref objMargins);
                            }
                        }
                    }

                    break;
            }
            base.WndProc(ref m);
        }
    }

 

 

余談:WPFの場合は、GlowWindowを使うと綺麗にできる。

C#, ソフトウェア開発

MouseDown・MouseMove・MouseDown等のマウスイベントは、各コントロールで発生する為、
親フォームで一括して受信するには、明示的にイベントハンドラを設定する必要がある。

        public Form1()
        {
            InitializeComponent();

            // 親フォームのマウスイベントハンドラの割り当て
            SetEventHandler(this);
        }

        /// <summary>
        /// 子コントロールにMouseイベントハンドラを設定(再帰)
        /// </summary>
        private void SetEventHandler(Control objControl)
        {
            // イベントの設定
            // (親フォームにはすでにデザイナでマウスのイベントハンドラが割り当ててあるので除外)
            if(objControl != this)
            {
                objControl.MouseDown += (sender, e) => this.OnMouseDown(e);
                objControl.MouseMove += (sender, e) => this.OnMouseMove(e);
                objControl.MouseUp += (sender, e) => this.OnMouseUp(e);
            }

            // さらに子コントロールを検出する
            if(objControl.Controls.Count > 0)
            {
                foreach(Control objChildControl in objControl.Controls)
                {
                    SetEventHandler(objChildControl);
                }
            }
        }

        #region FormのMouseイベント受信部

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            // senderは常にFormだが、eの座標は各コントロールを基準とした座標が入る為、
            // スクリーン座標からクライアント座標を計算すること
            Point pntScreen = Control.MousePosition;
            Point pntForm = this.PointToClient(pntScreen);

            this.Text = "X=" + pntForm.X + " Y=" + pntForm.Y;
        }

        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            Point pntScreen = Control.MousePosition;
            Point pntForm = this.PointToClient(pntScreen);

            this.Text = "X=" + pntForm.X + " Y=" + pntForm.Y;
        }

        private void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            Point pntScreen = Control.MousePosition;
            Point pntForm = this.PointToClient(pntScreen);

            this.Text = "X=" + pntForm.X + " Y=" + pntForm.Y;
        }

        #endregion

 

キーボードのイベントも各コントロールで発生するが、
こちらは親フォームでイベントを発生させるプロパティが用意されている。

            // 子・孫問わず、すべてのコントロールで発生したキーイベントが親フォームで取得できる
            this.KeyPreview = true;

 

C#, ソフトウェア開発

VisualStudioのデザインモードを判定する方法として、DesignModeプロパティがあるが、
継承コントロールやユーザコントロールの場合、正しく値が取得できない。

EndInit後に全ての親コントロールのDesignModeを確認することで、
デザインモードの判定を行う。

internal class uctlControl : PictureBox, ISupportInitialize // ISupportInitializeを継承し、EndInitイベントを受信する
{
        /// <summary>
        /// Init後処理
        /// </summary>
        public void EndInit()
        {
            // 自身のデザインモードを取得
            bool bolDesignMode = base.DesignMode;

            // 親コントロールを再帰取得
            Control objParent = this.Parent;
            while (objParent != null)
            {
                ISite objSite = objParent.Site;
                if (objSite != null)
                    bolDesignMode |= objSite.DesignMode;

                objParent = objParent.Parent;
            }

            // デザインモードの場合は処理を抜ける
            if (bolDesignMode == true)
                return;

            // デザインモードではないときに行う初期化処理
        }
}

 

ソフトウェア, ソフトウェア開発

Visual Studio 2015には、Dotfuscator and Analytics CE(以下Dotfuscator)というソースコード難読化ツールが付属している。

通常はGUI画面を呼び出し、手作業で登録・処理を行う必要があるが、
ユーザー登録を行うことでコマンドラインからの実行ができるようになる。

64BitOSの場合、Dotfuscatorは下記ディレクトリにインストールされている。
C:Program Files (x86)Microsoft Visual Studio 14.0PreEmptive SolutionsDotfuscator and Analytics Community Edition

(2017/03/08 追記)
Visual Studio 2017では、Dotfuscatorは下記ディレクトリとなる。
C:Program Files (x86)Microsoft Visual Studio2017CommunityCommon7IDEExtensionsPreEmptiveSolutionsDotfuscatorCE

dotfuscator.exeがVisualStudioから呼び出されたGUI画面、dotfuscatorCLI.exeがコマンドライン版。

dotfuscatorCLI.exeのヘルプは以下の通り。

C:Program Files (x86)Microsoft Visual Studio 14.0PreEmptive SolutionsDotfuscator and Analytics Community Edition>dotfuscatorCLI.exe /?

使用法: dotfuscator [オプション] [構成ファイル]

オプション
/g : GUI モードで実行します。
/i : 調査のみ実行します。
/p=<プロパティ リスト> : /p=<プロパティ リスト>
(例: /p=projectdir=c: emp,projectname=MyApp)
/q : メッセージを表示しません。
/v : 詳細な情報を表示します。
/c:<カルチャ> : ユーザー インターフェイスの言語を設定します (適切な言語リソースが必要です)。引数は、小文字の言語コードです。:
(例: /c:de, /c:ja, /c:zh-CHS)
/? : このメッセージを表示します。
[構成ファイル] : ランタイム オプションを含む構成ファイルです。

拡張オプション
/in:[+|-]<ファイル>[,[+|-]<ファイル>] : 入力アセンブリを指定します。公開 (+) または秘密 (-) アセンブリとして入力を難読化するには、プレフィックスを使用します。また、パッケージの種類を指定するには、ファイル名の後に : を使用します。

使用可能なパッケージの種類

Appx
ClickOnce
Silverlight
Directory

/out: <ディレクトリ> : 出力ディレクトリを指定します。
/honor:[on*|off] : すべての入力アセンブリで見つかった難読化属性の使用指令を有効/無効にします。
/strip:[on*|off] : すべての入力アセンブリからの難読化属性の除去を有効/無効にします。
/smart:[on*|off] : 出力プログラムが正しく動作するために必要であると見なされるプログラム要素を自動的に処理します。
/soreport:[all*|warn|none] : スマート難読化レポートの詳細を指定します。
/makeconfig:<ファイル> : コマンド ライン オプションを構成ファイルに保存します。
/disable : すべての変換 (“-rena:off -prune:off -encr:off -cont:off -link:off" の省略形です) およびインストルメンテーションを無効にします。

拡張オプション (Professional Edition のみ)
/debu:[on|off*] : 難読化されたアセンブリ用にデバッグ シンボルを出力します。

名前の変更の拡張オプション
/rename:[on*:off] : 名前の変更を有効/無効にします。
/mapout:<ファイル> : 出力割り当てファイルを指定します。
/clobbermap:[on|off*] : 割り当て上書きモードを指定します。
/keep:[namespace|hierarchy|none*] : 型の名前の変更規則を指定します。

名前の変更の拡張オプション (Professional Edition のみ)
/naming:[loweralpha*|upperalpha|numeric|unprintable] : 識別子の名前の変更規則を指定します。
/pref:[on|off*] : 名前を変更した型にプレフィックスを付けます。
/mapin:<ファイル> : 入力割り当てファイルを指定します。
/enhancedOI:[on|off*] : Enhanced Overload Induction(TM) を使用します。
/refsrename:[on*|off] : 入力割り当てファイルにのみ定義されている、参照されるメタデータの名前を変更します。

制御フローの拡張オプション (Professional Edition のみ)
/controlflow:[high*|medium|low|off] : 制御フロー レベルを設定します。

文字列の暗号化の拡張オプション (Professional Edition のみ)
/encrypt:[on*|off] : 文字列の暗号化を有効/無効にします。

不要コードの除去の拡張オプション (Professional Edition のみ)
/prune:[on*|off] : 不要コードの除去を有効/無効にします。

リンクの拡張オプション (Professional Edition のみ)
/link:[[+]<名前>[,[+]<名前>],]out=<名前> : 名前を付けて出力アセンブリとしてリンクする複数のアセンブリを指定します。プライマリ アセンブリは '+’ で指定します。すべての入力をリンクし、最初のアセンブリをプライマリとする場合は、リストを省略できます。

ウォーターマークの拡張オプション (Professional Edition のみ)
/premark:[on|off*|only] : ウォーターマークを有効/無効にします。"Only" オプションは、他のすべての変換を無効にします。
/watermark:<文字列> : ウォーターマークに使用する文字列を指定します。二重引用符は省略可能です。既定では、すべての入力アセンブリにウォーターマークが設定されます。
/passphrase:<パスフレーズ> : 文字列の暗号化に使うパスフレーズを指定します (オプション)。
/charmap:<名前> : 使用する文字セットを指定します。

オプション キーは '-' または '/’ です。拡張オプションは先頭 4 文字によって識別されます。
拡張オプションは構成ファイルのオプションよりも優先されます (ただし入力ファイルをマージする場合は除きます)。

●VisualStudioのビルド後に、自動的にDotfuscatorを実行する

1.プロジェクトのビルドイベントのビルド後のコマンドラインにDotfuscatorのコマンドを記載する

記載例

if $(ConfigurationName) == Release (
	del /S /Q "$(TargetDir)Dotfuscated*.*"
	md "$(TargetDir)Dotfuscated"
	"C:Program Files (x86)Microsoft Visual Studio 14.0PreEmptive SolutionsDotfuscator and Analytics Community EditiondotfuscatorCLI.exe" /in:"$(TargetDir)*.dll" /out:"$(TargetDir)Dotfuscated"
)

記載例解説
if $(ConfigurationName) == Releaseを指定することにより、リリースビルド時のみ実行する設定となる。
ビルドしたディレクトリにあるすべてのDLLを、ビルドしたディレクトリ配下のDotfuscatedディレクトリに、難読化後に出力する。

ソフトウェア, ソフトウェア開発

ILMergeを用いることで、EXE内にDLLを格納する等、アセンブリファイルをまとめることができる。

 

1.ILMergeのダウンロード・インストール

下記サイトよりILMergeをダウンロードし、インストールを行う。

Download ILMerge from Official Microsoft Download Center

ここでは「C:Program Files (x86)MicrosoftILMerge」にインストールしたものとし、解説をする。

 

2.ILMergeの実行

ILMergeを実行すると、下記のヘルプが表示される。

C:Program Files (x86)MicrosoftILMerge>ILMerge.exe
Usage: ilmerge [/lib:directory]* [/log[:filename]] [/keyfile:filename [/delaysign]] [/internalize[:filename]] [/t[arget]:(library|exe|winexe)] [/closed] [/ndebug] [/ver:version] [/copyattrs [/allowMultiple] [/keepFirst]] [/xmldocs] [/attr:filename] [/targetplatform:<version>[,<platformdir>] | /v1 | /v1.1 | /v2 | /v4] [/useFullPublicKeyForReferences] [/wildcards] [/zeroPeKind] [/allowDup:type]* [/union] [/align:n] /out:filename <primary assembly> [<other assemblies>…]

基本的な使い方は、「/out:」にマージしたファイルの出力先パスを、「primary assembly」にメインとなるファイル、「other assemblies」にマージしたいファイルを指定する。

publicな修飾子をinternalに変更したいときは、「/internalize」を指定する。
しかし、これによりマージ後のアプリケーションが正常に動作しなくなる(参照エラー)恐れもあるため、テストは十分に行うこと。

pdbファイルを生成しないようにするには「/ndebug」を指定する。

.NetFrameworkを用いたアセンブリの場合は、「/targetplatform:」に.Net Frameworkのバージョン または プラットフォームディレクトリを指定する。
.NetFramework4.5.2の場合は下記のようになる。
/targetplatform:"v4,C:Program Files (x86)Reference AssembliesMicrosoftFramework.NETFrameworkv4.5.2″

「other assemblies」に、全てのファイルを列挙するのではなく、ワイルドカードを用いて指定することもできる。
その場合は「/wildcards」を指定する。

使用例

ILMerge.exe /internalize /ndebug /targetplatform:"v4,C:Program Files (x86)Reference AssembliesMicrosoftFramework.NETFrameworkv4.5.2" /wildcards /out:MargeProject.exe ReleaseProject1.exe Release*.dll

 

出力先を異なるディレクトリにする場合は、事前にディレクトリを用意しておくこと。
ディレクトリの自動生成はされず、エラーとなる。

 

3.VisualStudioのビルド後に、自動的にILMergeを実行する

3.1.各プロジェクトのビルド出力先を同一の場所にする

プロジェクトの初期値では、プロジェクトのフォルダ内にBinフォルダが作られ、ビルドされたアセンブリファイルが置かれる。
これを、各プロジェクトが同一のフォルダにアセンブリファイルを出力するように変更する。
注意:指定対象は全てのプロジェクト

 

3.2.メインプロジェクトのビルドイベントのビルド後のコマンドラインにILMergeのコマンドを記載する

注意:指定対象はメインのプロジェクトのみ

記載例

if $(ConfigurationName) == Release (
	del /S /Q "$(TargetDir)Marge*.*"
	md "$(TargetDir)Marge"
	"C:Program Files (x86)MicrosoftILMergeILMerge.exe" /internalize /ndebug /targetplatform:"v4,C:Program Files (x86)Reference AssembliesMicrosoftFramework.NETFrameworkv4.5.2" /wildcards /out:"$(TargetDir)Marge$(TargetName)$(TargetExt)" "$(TargetDir)$(TargetName)$(TargetExt)" "$(TargetDir)*.dll"
)

記載例解説
if $(ConfigurationName) == Releaseを指定することにより、リリースビルド時のみ実行する設定となる。
ILMergeは、3.1.にて指定したディレクトリ内のMargeディレクトリに、プロジェクトのEXEと同名の、DLLを全てマージしたEXEを生成する。
パスは全てダブルクォーテーションで囲う。

 

3.3.プロジェクトをビルドする

C#, VB.NET, ソフトウェア開発

返り値のないメソッドの場合

        private async void btnStart_Click(object sender, EventArgs e)
        {
            Console.WriteLine("Start:" + DateTime.Now.ToString("HH:mm:ss.ff"));

            await Count();

            Console.WriteLine("Finish:" + DateTime.Now.ToString("HH:mm:ss.ff"));
        }

        private async Task Count()
        {
            int intValue = 1;

            await Task.Run(() =>
            {
                for(int intCount = 0; intCount < 15; intCount++)
                {
                    intValue *= 2;
                    Console.WriteLine(intValue);

                    System.Threading.Thread.Sleep(100);
                }
            });
        }

 

別スレッドからキャンセル処理を行う場合は下記のようになる。

        // トークン
        private CancellationTokenSource objToken = null;

        private async void btnStart_Click(object sender, EventArgs e)
        {
            Console.WriteLine("Start:" + DateTime.Now.ToString("HH:mm:ss.ff"));

            await Count();

            Console.WriteLine("Finish:" + DateTime.Now.ToString("HH:mm:ss.ff"));
        }

        private async Task Count()
        {
            int intValue = 1;

            objToken = new CancellationTokenSource();

            await Task.Run(() =>
            {
                for(int intCount = 0; intCount < 15; intCount++)
                {
                    // キャンセルフラグが立っていたら処理を抜ける
                    if(objToken.IsCancellationRequested == true)
                    {
                        return;
                    }

                    intValue *= 2;
                    Console.WriteLine(intValue);

                    System.Threading.Thread.Sleep(100);
                }
            }, objToken.Token);
        }

        /// <summary>
        /// キャンセル処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCancel_Click(object sender, EventArgs e)
        {
            if(objToken != null)
            {
                objToken.Cancel();
            }
        }

また、IsCancellationRequested ではなく ThrowIfCancellationRequested を用いることで、
例外として処理することもできる。

        // トークン
        private CancellationTokenSource objToken = null;

        private async void btnStart_Click(object sender, EventArgs e)
        {
            Console.WriteLine("Start:" + DateTime.Now.ToString("HH:mm:ss.ff"));

            try
            {
                await Count();
                Console.WriteLine("Finish:" + DateTime.Now.ToString("HH:mm:ss.ff"));
            }
            catch (OperationCanceledException exCanceled)
            {
                // キャンセル時
                Console.WriteLine("Canceled:" + DateTime.Now.ToString("HH:mm:ss.ff"));
            }
        }

        private async Task Count()
        {
            try
            {
                int intValue = 1;

                objToken = new CancellationTokenSource();

                await Task.Run(() =>
                {
                    try
                    {
                        for (int intCount = 0; intCount < 15; intCount++)
                        {
                            // キャンセルフラグが立っていたらOperationCanceledExceptionを発生させる
                            objToken.Token.ThrowIfCancellationRequested();

                            intValue *= 2;
                            Console.WriteLine(intValue);

                            System.Threading.Thread.Sleep(100);
                        }
                    }
                    catch (OperationCanceledException exCanceled)
                    {
                        // キャンセル時
                        throw (exCanceled);
                    }
                }, objToken.Token);
            }
            catch (Exception ex)
            {
                throw (ex);
            }
        }

        /// <summary>
        /// キャンセル処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCancel_Click(object sender, EventArgs e)
        {
            if (objToken != null)
            {
                objToken.Cancel();
            }
        }

 

 

返り値のあるメソッドの場合

        private async void btnStart_Click(object sender, EventArgs e)
        {
            Console.WriteLine("Start:" + DateTime.Now.ToString("HH:mm:ss.ff"));

            int intResult = await Count();

            Console.WriteLine("Total:" + intResult);

            Console.WriteLine("Finish:" + DateTime.Now.ToString("HH:mm:ss.ff"));
        }

        private async Task<int> Count()
        {
            int intValue = 1;
            
            int intResult = await Task.Run(() =>
            {
                int intSum = 0;

                for(int intCount = 0; intCount < 15; intCount++)
                {
                    intValue *= 2;
                    Console.WriteLine(intValue);

                    intSum += intValue;

                    System.Threading.Thread.Sleep(100);
                }

                return intSum;
            });

            return intResult;
        }

 

また、VB.NETの場合は下記のようになる(返り値のないメソッド)

    Private Async Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
        Debug.Print("Start:" + DateTime.Now.ToString("HH:mm:ss.ff"))

        Await Count()

        Debug.Print("Finish:" + DateTime.Now.ToString("HH:mm:ss.ff"))
    End Sub

    Private Async Function Count() As Task
        Dim intValue As Integer = 1

        Await Task.Run(Sub()
                           For intCount = 0 To 14
                               intValue *= 2
                               Debug.Print(intValue)

                               System.Threading.Thread.Sleep(100)
                           Next
                       End Sub)
    End Function

 

C#, ソフトウェア開発

        // サンプルデータ
        private List<string> lstData = new List<string> { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K" };

        /// <summary>
        /// 通常処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            Console.WriteLine("Start:" + DateTime.Now.ToString("HH:mm:ss.ff"));
            Console.WriteLine("For文");
            for (int intCount = 0; intCount < lstData.Count; intCount++)
            {
                Console.WriteLine(lstData[intCount]);
                System.Threading.Thread.Sleep(300);
            }

            Console.WriteLine("ForEach文");
            foreach (string strData in lstData)
            {
                Console.WriteLine(strData);
                System.Threading.Thread.Sleep(300);
            }
            Console.WriteLine("Finish:" + DateTime.Now.ToString("HH:mm:ss.ff"));
        }
Start:12:00:32.75
For文
A
B
C
D
E
F
G
H
I
J
K
ForEach文
A
B
C
D
E
F
G
H
I
J
K
Finish:12:00:39.36

 

これにParallelを適用すると、下記のようになる。

        // サンプルデータ
        private List<string> lstData = new List<string> { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K" };

        /// <summary>
        /// パラレル処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnParallelStart_Click(object sender, EventArgs e)
        {
            Console.WriteLine("Start:" + DateTime.Now.ToString("HH:mm:ss.ff"));
            Console.WriteLine("Parallel.For文");
            Parallel.For(0, lstData.Count, intCount =>
            {
                Console.WriteLine(lstData[intCount]);
                System.Threading.Thread.Sleep(300);
            });

            Console.WriteLine("Parallel.ForEach文");
            Parallel.ForEach(lstData, strData =>
            {
                Console.WriteLine(strData);
                System.Threading.Thread.Sleep(300);
            });
            Console.WriteLine("Finish:" + DateTime.Now.ToString("HH:mm:ss.ff"));
        }
Start:12:01:43.02
Parallel.For文
A
C
D
E
F
G
H
I
J
B
K
Parallel.ForEach文
A
E
D
F
B
G
H
C
I
J
K
Finish:12:01:44.24

並列に処理を行うため、全体の処理速度が向上している反面、処理の順番がバラバラになっていることに注意されたい。

 

並列処理内部でBreakを行いたい場合は下記のようになる。

        /// <summary>
        /// パラレル処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnParallelStart_Click(object sender, EventArgs e)
        {
            Console.WriteLine("Start:" + DateTime.Now.ToString("HH:mm:ss.ff"));
            Console.WriteLine("Parallel.For文");
            Parallel.For(0, lstData.Count, (intCount, objLoopState) =>
            {
                Console.WriteLine(lstData[intCount]);

                if (lstData[intCount] == "G")
                {
                    objLoopState.Break();
                }

                System.Threading.Thread.Sleep(300);
            });

            Console.WriteLine("Parallel.ForEach文");
            Parallel.ForEach(lstData, (strData, objLoopState) =>
            {
                Console.WriteLine(strData);

                if (strData == "G")
                {
                    objLoopState.Break();
                }

                System.Threading.Thread.Sleep(300);
            });
            Console.WriteLine("Finish:" + DateTime.Now.ToString("HH:mm:ss.ff"));
        }
Start:12:07:37.11
Parallel.For文
A
C
B
D
E
F
G
Parallel.ForEach文
A
F
D
E
G
B
C
Finish:12:07:37.73

Gが処理されたタイミングでBreakをかけているが、すでに並列処理が走ってしまっている分に関しては処理されてしまうので注意。

 

また、外部のスレッドから処理を中断する場合は下記のようになる。

        // トークン
        private CancellationTokenSource objToken = null;

        /// <summary>
        /// パラレル処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void btnParallelStart_Click(object sender, EventArgs e)
        {
            try
            {
                await Task.Run(() =>
                {
                    // ParallelOptionsの構築
                    objToken = new CancellationTokenSource();
                    ParallelOptions options = new ParallelOptions();
                    options.CancellationToken = objToken.Token;

                    Console.WriteLine("Start:" + DateTime.Now.ToString("HH:mm:ss.ff"));
                    Console.WriteLine("Parallel.For文");
                    Parallel.For(0, lstData.Count, options, intCount =>
                    {
                        Console.WriteLine(lstData[intCount]);
                        System.Threading.Thread.Sleep(300);
                    });

                    Console.WriteLine("Parallel.ForEach文");
                    Parallel.ForEach(lstData, options, strData =>
                    {
                        Console.WriteLine(strData);
                        System.Threading.Thread.Sleep(300);
                    });
                    Console.WriteLine("Finish:" + DateTime.Now.ToString("HH:mm:ss.ff"));
                });
            }
            catch (OperationCanceledException ex)
            {
                // キャンセルされた際に入る例外処理
                Console.WriteLine("Cancel:" + DateTime.Now.ToString("HH:mm:ss.ff"));
            }
        }

        /// <summary>
        /// パラレル処理のキャンセル
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnParallelBreak_Click(object sender, EventArgs e)
        {
            if (objToken != null)
            {
                objToken.Cancel();
            }
        }

 

C#, ソフトウェア開発

System.Windows.Forms.WebBrowserは、デフォルト状態ではIE7相当。

確認サイト:User Agent String.Com

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Shown(object sender, EventArgs e)
        {
            webBrowser1.Navigate("http://www.useragentstring.com/");
        }
    }
}

 

IE11に変更する場合は、レジストリ「HKEY_CURRENT_USERSOFTWAREMicrosoftInternet ExplorerMainFeatureControlFEATURE_BROWSER_EMULATION」に

プロセス名と対応するデータを登録する。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private const string strRegPath = @"SoftwareMicrosoftInternet ExplorerMainFeatureControlFEATURE_BROWSER_EMULATION";
        private Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(strRegPath, true);
        private string strProcessName = System.Diagnostics.Process.GetCurrentProcess().ProcessName + ".exe";

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Shown(object sender, EventArgs e)
        {
            regKey.SetValue(strProcessName, 11001, Microsoft.Win32.RegistryValueKind.DWord);

            webBrowser1.Navigate("http://www.useragentstring.com/");
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            regKey.DeleteValue(strProcessName);
            regKey.Close();
        }
    }
}

 

レジストリに登録するデータを変更すると、他のレンダリングバージョンに変更することも可能。

MSDN Internet Feature Controls (B..C) (Internet Explorer)

 

また、レジストリを使用せず、NavigateメソッドのadditionalHeadersに、直接UserAgentを指定する方法もある。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private const string strUserAgent = @"User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko";

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Shown(object sender, EventArgs e)
        {
            webBrowser1.Navigate("http://www.useragentstring.com/", "_self", null, strUserAgent);
        }
    }
}