【C#】アプリを多重起動しないようにする

Mutex を取得して新規に作成できれば新規の起動、取れなかったら 2つめの起動という感じに判断できます。

string key = "application_name";
using (var mutex = new Mutex(true, key, out bool createdNew))
{
    if (!createdNew)
    {
        return; // 多重起動になる
    }
    else
    {
        // 多重起動ではない → ここにメインの処理を記述する
        mutex.ReleaseMutex();
    }
}

定型的な処理なので Utility 化したいと思います。

// MultiStartupUtil.cs

using System;
using System.Threading;

public static class MultiStartupUtil
{
    public static void SingleStartupContext(string key, Action action)
    {
        using (var mutex = new Mutex(true, key, out bool createdNew))
        {
            if (!createdNew)
            {
                return;
            }
            action?.Invoke();
            mutex.ReleaseMutex();
        }
    }
}

使い方は以下の通りです。

// Program.cs

using System;
using System.Windows.Forms;

internal static class Program
{
    [STAThread]
    static void Main()
    {
        // 多重起動防止用のメソッドにキーとメイン処理を渡す
        MultiStartupUtil.SingleStartupContext("application_name", _Main);
    }

    // メイン処理をこっちに引っ越す
    private static void _Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        using (FormTaskTray formTaskTray = new FormTaskTray())
        {
            Application.Run();
        }
    }
}

【C#】NuGetパッケージを再インストールする

環境

  • Windows10
  • VisualStudio 2019

状況

以下でパッケージのキャッシュをクリアしたらエラーが出て「発行」できなくなった。

ツール > オプション > NuGet パッケージ マネージャー > すべての NuGet キャッシュをクリア

次に、Webプロジェクトを発行すると以下エラーが出て完了しない。

warning MSB3106
アセンブリの厳密な名前 "${DLLのパス}"
は、見つからなかったパスであるか、形式が正しくない完全アセンブリ名です。

error MSB3030:
ファイル "${DLLのパス}"
は見つからなかったためコピーできません。

手順(1)

以下手順でパッケージを復元する。

  • VisualStudio を閉じる
  • 隠しファイルを表示 > .vs ファイルを削除
  • VisualStudio を立ち上げてパッケージマネージャーコンソールを表示
// 以下を入力する
update-package -reinstall

PC のディスクの容量が少なくなったからキャッシュ削除したら動かなくなって焦りました。再インストールは IDE の NuGet パッケージの管理 からは実行できないのでパッケージマネージャーコンソールからコマンドを入力して実行する必要があります。

手順(2)

以下フォルダにパッケージが見つからないといわれる場合はこれかもしれません。

%userprofile%\.nuget\packages\${ライブラリ名}\${バージョン}
  • 新しいソシューションとプロジェクトを .NET Frameowrk 4.7.2 とか 4.8 で作成
  • NuGet パッケージマネージャーを平出 ${ライブラリ名}\${バージョン} を選択しインストール

これでキャッシュフォルダにパッケージが配置されます。既存のプロジェクトで既に別のバージョンをダウンロード済みだとうまくいかないかもしれません。

プロジェクトは .NET Core や .NET だとではなく .NET Framework を選択したほうがよさそう。

VisualStudio2022がメモリを使い過ぎている場合の対処法

なんか VisualStudio 2019 で開いてたソリューションを 2022 で開くと 3倍くらいメモリを消費してる場合があるため、PCのメモリが 16GB だと Teams + Outlook + Chorome なんか開いてると、メモリを使いきってそのまま全部クラッシュしたり、OS が変な感じになってしまう事があったので回避するための設定の紹介です。

結構前から以下で紹介されている内容のため、原文が読めるならこの記事よりソース見たほうが早いかもしれません。

stackoverflow.com

確認環境

  • Visual Studio 2022 (17.4.4)
  • Windows11

設定項目

画面上部の ツール > オプション から

  • テキストエディター > C# > 詳細 > 'pull' 診断を有効にする > チェックボックスのチェックを外す

  • デバッグ > シンボル > 指定したモジュールのみを読み込むを選択する

  • プロジェクトおよびソリューション > ソリューション読み込み時にドキュメントを再度開くのチェックを外す

  • テキストエディター > C# > 詳細 > 別のプロセスでコード分析を実行するのチェックを外す

最後のが一番効果あると思います。VS2019 で開くと 400MB だったメモリ使用量がこの設定で 600MB 程度になりました。

またアプデで改善することもあるかと思うのでここで設定した内容は忘れないようにしましょう。

【C#】appsettings.jsonをコンソールで扱う

コンソールアプリでも App.congi に代わる新しい定義ファイルの形式である appsettings.json を使用する場合の設定と実装方法の紹介です。

ASP.NET Core および ASP.NET 5~6 であれば、IServiceCollection.Configure にセクション名を渡せば勝手に内容をオブジェクトにマッピングしてくれる機能が存在しますが、コンソールアプリで始めてしまうとそこらへんが存在しないので自分で読み書きする形になります。

したがって、定義ファイルにアクセスしやすいようヘルパークラスの実装例を紹介したいと思います。

確認環境

この記事は以下環境で確認しています。

  • VisualStudio 2022
  • .NET 6
  • コンソールプロジェクトが作成済み

パッケージの導入

「NuGet パッケージの管理」の「参照」から探してもいいですが、微妙にわかりにくかったので「パッケージマネージャー コンソール」からインストールします。

ツール > NuGet パッケージ マネージャー > パッケージ マネージャー コンソール

NuGetのページ: https://www.nuget.org/packages/Microsoft.Extensions.Configuration.Json/

以下コマンドを入力します。

PM>
Install-Package Microsoft.Extensions.Configuration.Json -Version 5.0.0

プロジェクトに間接的に必要なDLLが以下の通り追加されます。

// 結構大量に追加されます
Microsoft.Extensions.Configuration.Abstractions.dll
Microsoft.Extensions.Configuration.dll
Microsoft.Extensions.Configuration.FileExtensions.dll
Microsoft.Extensions.Configuration.Json.dll
Microsoft.Extensions.FileProviders.Abstractions.dll
Microsoft.Extensions.FileProviders.Physical.dll
Microsoft.Extensions.FileSystemGlobbing.dll
Microsoft.Extensions.Primitives.dll

定義ファイルの追加・編集

ソリューションエクスプローラー > 対象プロジェクトのコンテキストメニュー > 追加 > 新しい項目

「JSON ファイル」などの JSON 形式のファイルを選択し「appsetting.json」という名前で追加します。(実際は名前は何でも良いです)追加したらファイルのプロパティでビルドアクションを「コンテンツ」に変更し、出力先ディレクトリにコピーを「新しい場合上書きする」に変更します。

JSON ファイルの内容は以下の通りに編集します。

{
    "MySettings": {
        "Key1": "Key1",
        "Key2": "Key2",
        "InnerSettings": {
            "Key3": "Key3",
            "Key4": "Key4"
        }
    }
}

値の取得

以下のように書けばアクセスできるようになります。

using System.IO;
using Microsoft.Extensions.Configuration;

internal class AppMain
{
    private static void Main(string[] args)
    {
        // 定義ファイルの読み書き用のオブジェクトを初期化する
        IConfigurationRoot configuration = 
            new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json", true, true).Build();

        IConfigurationSection section = config.GetSection("MySettings");
        string key1 = section["Key1"];
        string key2 = section["Key2"];
        
        // 取得できない場合null、例外は発生しない
        string strN = section["KeyXX"];

        // 入れ子の場合「A:B」という風にコロンを挟む
        IConfigurationSection innerSection = config.GetSection("MySettings:InnerSettings");
        string key3 = innerSection["Key3"];
        string key4 = innerSection["Key4"];
    }
}

アクセス用のヘルパークラスの作成

上記のようなコードを毎回書くのも面倒なのでヘルパークラスを作成して手間を軽減したいと思います。

AppSettingJsonクラス

以前の XML 形式の「アプリケーション 構成ファイル」と似たような形でアクセスできるといいので以下のような「MySettings」セクションの下に key-value 形式で値が並んでいる定義ファイルを想定してヘルパークラスを作成したいと思います。

// appsetting.json
{
    "MySettings": {
        "key1": "stringstring",
        "key2": 10,
        "key3": 123.5698
    }
}

実装は以下の通りです。

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Runtime.CompilerServices;
using Microsoft.Extensions.Configuration;

/// <summary>
/// appsettings.json のセクションを簡単に操作できるようにするためのヘルパークラス
/// </summary>
public class AppSettingsSection
{
    #region 見本...
    //
    // 見本:
    // 以下の通り記述されている appSettings.json 内のセクションへのアクセスを簡単にする
    //
    // appsetting.json
    // {
    //     "Setting": { ★ここ
    //         "key1": "stringstring",
    //         "key2": 10,
    //         "key3": 123.5698
    //     }
    // }
    //
    #endregion

    // 対象のセクション
    IConfigurationSection _section;

    //
    // Constructors
    // - - - - - - - - - - - - - - - - - - - -

    public AppSettingsSection(IConfigurationSection section)
    {
        _section = section ?? throw new ArgumentNullException(nameof(section));
    }

    //
    // Static Methods
    // - - - - - - - - - - - - - - - - - - - -

    public static AppSettingsSection CreateInstance(string filePath, string sectionName)
    {
        if (File.Exists(filePath))
            throw new FileNotFoundException($"File not found.", filePath);
        if (string.IsNullOrWhiteSpace(sectionName))
            throw new ArgumentException($"Require {sectionName}", nameof(sectionName));

        var config = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile(filePath, true, true)
                    .Build();

        return new AppSettingsSection(GetSection(config, sectionName));
    }

    public static AppSettingsSection CreateInstance(IConfiguration config, string sectionName)
    {
        if (config == null)
            throw new ArgumentNullException(nameof(config));
        if (string.IsNullOrWhiteSpace(sectionName))
            throw new ArgumentException($"Requre {nameof(sectionName)}", nameof(sectionName));

        return new AppSettingsSection(GetSection(config, sectionName));
    }

    private static IConfigurationSection GetSection(IConfiguration config, string sectionName)
    {
        IConfigurationSection section =
            config.GetSection(sectionName) ??
            throw new KeyNotFoundException($"{sectionName} section is not found.");

        return section;
    }

    //
    // Public Methods
    // - - - - - - - - - - - - - - - - - - - -

    /// <summary>
    /// appSetting セクション内の指定したキーに対応する値を取得します。
    /// </summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string GetString(string key) => GetValue(key);

    // 以下基本型として値を取得するメソッド
    public bool GetBool(string key) => Convert.ToBoolean(GetValue(key));
    public byte GetByte(string key) => Convert.ToByte(GetValue(key));
    public sbyte GetSByte(string key) => Convert.ToSByte(GetValue(key));
    public char GetChar(string key) => Convert.ToChar(GetValue(key));
    public decimal GetDecimal(string key) => Convert.ToDecimal(GetValue(key));
    public double GetDouble(string key) => Convert.ToDouble(GetValue(key));
    public float GetFloat(string key) => Convert.ToSingle(GetValue(key));
    public int GetInt(string key) => Convert.ToInt32(GetValue(key));
    public uint GetUInt(string key) => Convert.ToUInt32(GetValue(key));
    public long GetLong(string key) => Convert.ToInt64(GetValue(key));
    public ulong GetULong(string key) => Convert.ToUInt64(GetValue(key));
    public short GetShort(string key) => Convert.ToInt16(GetValue(key));
    public ushort GetUShort(string key) => Convert.ToUInt16(GetValue(key));
    // 時刻で取得
    public DateTime GetDateTime(string key, string format)
        => DateTime.ParseExact(GetString(key), format, CultureInfo.InvariantCulture);
    // 時間で取得
    public TimeSpan GetTimeSpan(string key, Func<double, TimeSpan> conv)
        => conv(GetDouble(key));

    //
    // Not-Public Methods
    // - - - - - - - - - - - - - - - - - - - -

    // 対象のセクションの値を取得する
    private string GetValue(string key)
    {
        return _section[key] ?? throw new KeyNotFoundException($"Key not found. key={key}");
    }
}

関連記事

takap-tech.com

takap-tech.com

OpenCVをCMakeとVsiaulStudiio2019でビルドする

ダウンロードした OpenCV の bin フォルダに DLL 入ってなくね?Lib はどこ…?

え、自分でビルドするんだよ、え、マジで?

まぁ、C++er なら普通のことですが…

…という事でまぁこういった事はプロジェクト初期には割と毎回発生する、というか、Windows民は大抵のライブラリは自分でビルドしてLibとDLLを作成することになるので手順をまとめてみました。

実行環境

今回の実験環境は以下の通りです。

  • Windows10
  • VisualStudio2019
  • OpenCV 4.5.2
  • CMake 3.20.3が導入済みで環境変数にパスが通っている事

CMakeあたりの手順なほかのプロジェクトにも汎用で使えるので、OpenCVじゃなくて大体イケます。

OpenCVの準備

まず、OpenCV のサイトからパッケージをダウンロードしてきます。

https://opencv.org/

Library > Releases を選択して OpenCV 4.5.2 の Windows をクリックしてパッケージをダウンロードします。

f:id:Takachan:20210603214536p:plain

通常、SourceForge にリダイレクトされダウンロードが自動で始まるので終わるまで待ちます。

自己解凍形式の実行形式ファイル「opencv-4.5.2-vc14_vc15.exe」がダウンロードされているのでこれを実行します。

解答場所は適当ですが今回は「C:\temp」を指定します。なんか解凍がかなり遅いので7zipとかで展開しても良いです。(exeですがzipのように解凍できます)

CMakeでVisualStudio用のソリューションを作成する

CMake の GUI を使って VisualStudio 用のソリューションを作成します。

CMakeのフォルダに cmake-gui.exe というツールが入っているので起動します。

ツールを起動したら以下のように入力し「Configure」ボタンを押します。

f:id:Takachan:20210603214552p:plain

「Where is the source code」に「C:/temp/opencv/sources」を指定します。これは「CMakeLists.txt」がある場所です。

次に「Where to build the binaries」に「C:/temp/opencv/build-self」を指定します。慣例で「build」とする事が多いですが OpenCV の場合既に存在するため新しい別のフォルダを用意します。

f:id:Takachan:20210603214559p:plain

ボタンを押すと以下のような次に生成するプロジェクトの種類を選ぶダイアログが表示されるので次のように入力します。

f:id:Takachan:20210603214608p:plain

「Specify the generator for this project」は「Visual Studio 16 2019」を選択します。

次に64bit版にしたいので「Optional platform for generator」に「x64」を指定します。空白で何も指定しないと「Win32」で32bit版となります。

で「Finish」を押すとツールが動き始めますが、少し時間がかかるので終わるまで待ちます。

完了すると以下のように表示が変わりますがオプションを変更したい場合チェックボックスの ON/OFF で指定できますが今回はそのまま「Generate」をクリックします。

f:id:Takachan:20210603214616p:plain

VisualStudio2019からビルドする

上記の手順で「C:\temp\opencv\build-self」に「OpenCV.sln」が作成されているのでこれを開くと VS2019 でソリューションが開くのでそのまま Debug/Release でビルドします。

割と時間がかかるの opencv_test とか opencv_perf はビルドしないでも良いです。

ビルドが完了すると

「bin」の中にDLL、「lib」の中にLibファイルがそれぞれ生成されるのでパスを通したり環境に配置したりすればOKです。

デバッグビルドには末尾にdがついているものが生成されていると思います。

ソリューションからコードがプロジェクトごとに整理されて閲覧できるので何かあったときは参照することになります。(API呼び出しエラーとか起きると割と見たりします)

CMakeのGUIツールについて

CMake の GUI ツールは CMakeList.txt があるものには大抵の場合適用できるので Win だとビルドがすごく簡単。

参考サイト

第8回 初めてのOpenCV開発 ― CMakeを使ったOpenCVのカスタマイズ【OpenCV 3.1.0】

生成されるファイル

以下自分用メモ。

opencv_coreXXX.dllが無い…無い…みたいなゾンビにならないように。

XXXはバージョンが入る。ソリューションと同じ階層に生成される。

binフォルダ内

DLLとかEXEが入っている

Debug

opencv_calib3d452d.dll
opencv_core452d.dll
opencv_dnn452d.dll
opencv_features2d452d.dll
opencv_flann452d.dll
opencv_gapi452d.dll
opencv_highgui452d.dll
opencv_imgcodecs452d.dll
opencv_imgproc452d.dll
opencv_ml452d.dll
opencv_objdetect452d.dll
opencv_photo452d.dll
opencv_stitching452d.dll
opencv_video452d.dll
opencv_videoio_ffmpeg452_64.dll
opencv_videoio452d.dll
opencv_annotationd.exe
opencv_interactive-calibrationd.exe
opencv_model_diagnosticsd.exe
opencv_version_win32d.exe
opencv_versiond.exe
opencv_visualisationd.exe

Release

opencv_calib3d452.dll
opencv_core452.dll
opencv_dnn452.dll
opencv_flann452.dll
opencv_gapi452.dll
opencv_highgui452.dll
opencv_imgcodecs452.dll
opencv_imgproc452.dll
opencv_ml452.dll
opencv_objdetect452.dll
opencv_photo452.dll
opencv_stitching452.dll
opencv_video452.dll
opencv_videoio452.dll
opencv_videoio_ffmpeg452_64.dll
opencv_features2d452.dll
opencv_annotation.exe
opencv_interactive-calibration.exe
opencv_model_diagnostics.exe
opencv_version.exe
opencv_version_win32.exe
opencv_visualisation.exe

libフォルダ内

ソリューションと同じ階層にある。LibファイルとExpファイルが入っている。

Debug

opencv_calib3d452d.lib
opencv_core452d.lib
opencv_dnn452d.lib
opencv_features2d452d.lib
opencv_flann452d.lib
opencv_gapi452d.lib
opencv_highgui452d.lib
opencv_imgcodecs452d.lib
opencv_imgproc452d.lib
opencv_ml452d.lib
opencv_objdetect452d.lib
opencv_photo452d.lib
opencv_stitching452d.lib
opencv_ts452d.lib
opencv_video452d.lib
opencv_videoio452d.lib

Release

opencv_calib3d452.lib
opencv_core452.lib
opencv_dnn452.lib
opencv_features2d452.lib
opencv_flann452.lib
opencv_gapi452.lib
opencv_highgui452.lib
opencv_imgcodecs452.lib
opencv_imgproc452.lib
opencv_ml452.lib
opencv_objdetect452.lib
opencv_photo452.lib
opencv_stitching452.lib
opencv_ts452.lib
opencv_video452.lib
opencv_videoio452.lib

Windows10から游ゴシックを削除する

初めに

Windowsから「游ゴシック」を削除するための「Meryo UIも大嫌い」などのツールで游ゴシックに関係するフォントの設定を変更しても「エクスプローラーのコンテキストメニュー」、「UWPの設定UI」などは游ゴシックが引き続き表示されます。

正直なところ高DPI環境以外では游ゴシックは表示がよくないので残りの設定も古いWindows同様にMeiryo系のフォントに戻す方法です。

やり方自体はいかに紹介されています。

qiita.com

Windowsアップデートが入ると設定が戻ってしまうことがあるため、ここではこの方法ををワンクリックでできるようにしたいと思います。

やり方

レジストリファイルの作成

上記サイトの通り以下の内容で「游ゴシック削除.reg」というレジストリファイルを作成します。

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontSubstitutes]
"Yu Gothic"="Meiryo UI"
"Yu Gothic Bold"="Meiryo UI Bold"
"Yu Gothic Light"="Meiryo UI"
"Yu Gothic Medium"="Meiryo UI"
"Yu Gothic Regular"="Meiryo UI"
"Yu Gothic UI"="Meiryo UI"
"Yu Gothic UI Light"="Meiryo UI"
"Yu Gothic UI Regular"="Meiryo UI"
"Yu Gothic UI Semibold"="Meiryo UI Bold"
"Yu Gothic UI Semilight"="Meiryo UI"
"游ゴシック"="Meiryo UI"
"游ゴシック Bold"="Meiryo UI Bold"
"游ゴシック Light"="Meiryo UI"
"游ゴシック Medium"="Meiryo UI"
"游ゴシック Regular"="Meiryo UI"

入力したらUTF-16、BOMありで保存します。

バッチファイルの作成

以下のように「游ゴシック削除.bat」というバッチファイルを作成し先ほどの「游ゴシック削除.reg」と同じ階層にSJISで保存します。

rem -x-x- 00. 制御対象のサービスの名前 -x-x-
set SERVICENAME=Windows Font Cache Service

rem -x-x- 01. 游ゴシックの代替フォントを定義する -x-x-
call 游ゴシック削除.reg

rem -x-x- 02. Windows Font Cache Serviceを無効化する -x-x-
net stop "%SERVICENAME%"

rem -x-x- 03. 游ゴシックフォントファイルを削除する -x-x-
cd %windir%\Fonts
takeown /F YuGoth*.ttc /A
icacls YuGoth*.ttc /grant Administrators:F
ren YuGoth*.ttc YuGoth*.ttc.1

rem -x-x- 04. フォントキャッシュファイルを削除する -x-x-
del /S /Q %windir%\ServiceProfiles\LocalService\AppData\Local\*FontCache*.dat
del %windir%\system32\FNTCACHE.DAT

実行する

上記を「游ゴシック削除.bat」を「管理者として実行」してWindowsを再起動します。

これで、再起動後にはすべてMeiryo UIに置き換えられていると思います。

なんか設定が戻ったなと思ったらまた管理者で実行すると設定が戻せます。

最後に

なんかこの設定をしてもOSの文字表示は汚いままです。一部設定が聞いてない箇所もあるというかこの設定をしてもなんかだんだんこの設定が効果のない範囲が大きくなってる気がする…

最初は「スタートメニュー」とか「設定」の大半に効果があったのに今だと游ゴシックで表示されているところが増えているような…?

Choromeのコンテキストメニューと比べて表示が汚すぎる…

はてなブックマークのホッテントリから特定の記事を非表示にする

Adblock plusのフィルター機能を使ってはてなブログのホットエントリーを非表示にする方法です。

確認環境

  • Adblock plus
  • GoogleChome

Adblock plusがある環境ならどれでも行けると思います。しらんけど。

Fanboy's Social Blocking Listが入ってるとはてぶの機能が壊れるのでOFFっておきます。

ブロック方法

AdBlock Plusの[オプション] > 詳細設定 > マイフィルターリストに以下値を設定する

ドメインを指定してブロック

まずはドメインごとブロックする方法です。

b.hatena.ne.jp#?#li:-abp-has(> .entrylist-contents > .entrylist-contents-main > .entrylist-contents-detail > p > a > span:-abp-contains(任意のドメイン))

e.g.

「日経クロステック」のドメインをすべて非表示 b.hatena.ne.jp#?#li:-abp-has(> .entrylist-contents > .entrylist-contents-main > .entrylist-contents-detail > p > a > span:-abp-contains(tech.nikkeibp.co.jp))

タイトルを指定してブロック

次にタイトルをブロックする方法です。

b.hatena.ne.jp#?#li:-abp-has(> .entrylist-contents > .entrylist-contents-main > .entrylist-contents-title > a[title*="任意の単語"])

e.g.

タイトルに「ライブドア」を含む記事を非表示 b.hatena.ne.jp#?#li:-abp-has(> .entrylist-contents > .entrylist-contents-main > .entrylist-contents-title > a[title*="ライブドア"])

複数のキーワードを同時指定(orのような指定)はできない(と思っている)

最後に

ソーシャル要素があるサービスなのにブロック機能がないサービスは最低だと思いました。

【C#】Consoleの出力先を変更する

Consoleの出力先をファイルにしたりカスタムクラス用いて任意の出力先を設定する方法です。

確認環境

  • Windows11
  • VisualStudio2022
  • .NET 7.0

.NET 7.0 で確認しましたがどの環境でも同じです。

Consoleの出力先の変更方法

以下のメソッドで出力先を変更できます。

// 出力先を変更するメソッド
Console.SetOut(TextWriter newOut);

Consoleの出力先をファイルに変更する

TextWriter を継承したクラスに StreamWriter があるので以下のようにするとファイルに書き出すように変更できます。

// Consoleの内容をファイルに出力するように変更する
StreamWriter newOut = new StreamWriter(@"d:\sample.txt", true, Encoding.UTF8);
Console.SetOut(newOut);

Console.WriteLine("Test"); // d:\sample.txtにTestが書きこまれる

変更した後に出力先を元に戻す

いちど SetOut で出力先を変更すると元に戻せなくなるので後でコンソール出力を復元したい場合事前に Console.Out でインスタンスを保持しておきます。

// 事前にインスタンスを保持しておく
var defaultOut = Console.Out;

// 出力先を変更する
var foroed = new ConsoleAndTraceWriter(Console.Out);
Console.SetOut(foroed);

// コンソール出力するように戻す
Console.SetOut(defaultOut);

カスタム出力先を指定する

SetOut には TextWriter を継承しているクラスが設定できるため以下のように TextWriter を継承したクラスを自作すれば任意の出力先に内容を転送できます。

以下例では Trace に内容を転送してます。

// TraceWriter.cs

using System.Diagnostics;
using System.IO;
using System.Text;

// Consoleへの出力をTraceへ転送するためのクラス
public class TraceWriter : TextWriter
{
    // TextWriterからの要請で実装
    public override Encoding Encoding => Encoding.UTF8;

    // 既定の初期値でオブジェクトを初期化するコンストラクタ
    public TraceWriter() { }

    // Overrideしていないメンバーが呼ばれた時に呼び出される
    public override void Write(string value) => Trace.Write(value);

    // Console.WriteLine(string)が呼ばれた時に呼び出される
    public override void WriteLine(string value) => Trace.WriteLine(value);
}

Consoleとトレースに同時に出力する

// TraceConsoleWriter.cs

// TraceとConsoleに同時に出力するためのクラス
public class TraceConsoleWriter : TextWriter
{
    public readonly TextWriter Console;

    public override Encoding Encoding => Encoding.UTF8;

    public TraceConsoleWriter(TextWriter consoleOut) => Console = consoleOut;

    public override void Write(string value)
    {
        Console.Write(value);
        Trace.Write(value);
    }

    public override void WriteLine(string value)
    {
        Console.WriteLine(value);
        Trace.WriteLine(value);
    }
}

使い方は以下の通りです。

var newOut = new TraceConsoleWriter(Console.Out);
Console.SetOut(newOut);

// ConsoleとTraceに同時に出力される
Console.WriteLine("hogehoge1");
Console.WriteLine("hogehoge2");
Console.WriteLine("hogehoge3");

Excelで増殖したセルのスタイル(書式)を一括で削除する

Excelでファイルサイズが異様に大きくなっている時に、セルのスタイルが異常に増殖している事があります。

原因は不明ですが、どうやら"シートを複製する"、"セルをコピペする"操作で増殖するみたいです。

以下画像の赤枠の書式欄ですが、見えている部分の様子が既に少々おかしいです。

f:id:Takachan:20180705002821j:plain

展開するとスタイル定義が大量に存在しています。

f:id:Takachan:20180705003244p:plain

多分1万個くらいあると思います…

f:id:Takachan:20180705003302p:plain

以下のように、1つ1つの定義は手動で削除できるのですが、定義の量が多いと手作業は現実的ではありません。

f:id:Takachan:20180705003349p:plain

Excel File Cleanerで余計な書式を削除する

この状況を解消するために「Excel File Cleaner」を使用します。

これは、先述のスタイルを全て消去して初期状態にリセットするためのツールになります。

注意:

スタイルを使用しているファイルだとファイル内の書式がすべて消えて見た目崩壊する可能性があるのでご注意ください。

先ずは公式サイトからソフトをダウンロードしてインストールします。

archive.codeplex.com

で、インストールした後起動した状態がこちら。

f:id:Takachan:20180705003432p:plain

「Open ans Clean」ボタンを選択してスタイルを削除したいExcelを選択します。

ファイル選択すると即座に処理が開始され、ログが画面に流れ始めます。

f:id:Takachan:20180705003544p:plain

オリジナルのファイルは対象ファイルと同じ階層に「***_BACKUP.xlsx」として保存されているので万が一、内容がおかしくなった場合も安心です。

で、実行後は以下のように既存のスタイルが消滅します。

f:id:Takachan:20180705003623p:plain

この方法の良いところですが、外部ツールを使用するため、対象のExcelファイルを開かなくても(開けない状態{たとえばメモリ不足とか})でも使用できる点だと思います。また、VBSを自作すると説明が難しい場合があるのでエンジニア以外の型でも簡単に使用できます。

他の方法で消す

上記の他にもいろいろ方法があるためリンクで紹介します。

Excel内のVBSを自作して消す方法

qiita.com

MS公式の書式削除ツールを使用する

説明ページ https://support.microsoft.com/ja-jp/help/244435/how-to-reset-the-last-cell-in-excel

ツールダウンロードページ https://archive.codeplex.com/?p=xsformatcleaner

VisualStudioのエラーリンクの検索エンジンをGoogleに変更する

VisualStudioでコードを書いている時にコンパイルエラーが発生した際に、エラーリンク(以下画像の赤枠)をクリックすると、Bingでエラーが検索されます。

このBing検索、全く役に立たないので検索エンジンをGooleに変更します。

f:id:Takachan:20180705001556p:plain

前提条件

以下、唯一の条件です。

  • Google Chromeを使っていること。

Chrometanaを使ってBing検索をリダイレクトする

Choromeの拡張機能を利用してBing検索を一律Google検索へ飛ばすようにします。

先ずはChrometanaをインストールします。

f:id:Takachan:20180705001918p:plain

インストールが完了した直後にオプション画面が表示されるので赤枠内のチェックボックスを選択しておきます。(←最重要

f:id:Takachan:20180705002045p:plain

導入前後の比較

前後の比較画像です。

導入前

f:id:Takachan:20180705002202p:plain

導入後

f:id:Takachan:20180705002219p:plain

Googleに検索がリダイレクトされていることが確認できました。

メッセージごとリダイレクトして検索しているだけなので、適当にキーワードを削って検索しなしましょう。

f:id:Takachan:20180705002234p:plain

最後に

MSは一刻も早くBingの運営から手を引くか、設定から検索エンジンを変更できるようにしてほしいですね。

環境変数をコマンドを使ってバックアップする

Windowsで、コマンドを使って環境変数をファイルにバックアップする方法です。

環境変数自身は、OSのレジストリに保存されているため、レジストリ操作のための「reg」コマンドを使用します。

regコマンドでレジストリを保存する時のパラメータは以下の通りです。

reg save $保存対象のレジストリキー $保存先のファイルパス

次に、ユーザー環境変数が「HKEY_CURRENT_USER\Environment」、システム環境変数が「HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment」にそれぞれ記録されているのでコマンドは以下の通り記述します。

rem ユーザー環境変数の保存
reg save HKEY_CURRENT_USER\Environment d:\env_user.hiv

rem システム環境変数の保存
reg save "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" d:\env_system.hiv

上記コマンドは管理者権限のプロンプトで実行してください。

出力されるファイルはバイナリ形式でregコマンドでしか読めません。従って復元するときはreg restoreで値を復元します。

rem ユーザー環境変数の保存
reg restore HKEY_CURRENT_USER\Environment d:\env_user.hiv

rem システム環境変数の保存
reg restore  "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" d:\env_system.hiv

Google翻訳から韓国語を消す

Google翻訳にはお世話になっていますが、言語の選択欄に「韓国語」って表示ありますよね?使わないし邪魔なので消したいと思います。

以下前提条件です。

  • Chromeを使っている
  • Adblock Plus の Extension を入れていること

Adblockの要素のブロックを利用する

Google翻訳のWebサイトを開いた状態で、Chorome のアドレスバーの右側に Adblock plus のアイコンをクリックして「要素をブロック」を選択します。

f:id:Takachan:20180412230532p:plain

その状態でブロックしたい要素の上にカーソルを持っていきクリックします。対象の要素はカーソルが上に乗ると薄黄色で強調されます。

f:id:Takachan:20180412230947p:plain

Block element の子ウインドウが表示されるので、表示されている内容から「2段目を消去して」「OKを押下」します

f:id:Takachan:20180412231013p:plain

2段目は消します。(これを消さないと、言語が選べなくなります。)

f:id:Takachan:20180412231041p:plain

要素名は「translate.google.com###sugg-item-ko」です。

translate.google.com##button[data-language-code='ko'](2021/07/20最新版に対応)

「ko」の部分を他の言語指定のキーワードに変更すれば他の言語も消すことができます。追加後に、画面に即座に設定が反映され、無事両方から表示が消えました。

f:id:Takachan:20180412231052p:plain

同時に、右側の翻訳先の言語からも消えています。

f:id:Takachan:20180412231109p:plain

以上です。