VSTOによるOfficeアプリの拡張

業務で久しぶりにVSTOを使用しました。

VSTOは、EXCELやWordなどのOfficeアプリケーションをカスタマイズし、機能を拡張するためのフレームワークです。これは、EXCELマクロ(VBA)の代替として使用され、以下の点で優れています。

1. C#で実装:VSTOはレガシーなVBではなく、C#でアプリケーションを拡張できます。
2. .NETのフル機能:VSTOは.NETフレームワークの全機能を利用できます。
3. GUI:WindowsFormsやWPFを使用して、Officeアプリケーションのユーザーインターフェースをカスタマイズできます。
4. コードの隠蔽:VSTOを使用することで、コードを隠蔽してアプリケーションを保護できます。

お客様がEXCELのVBAを使用して、工場内の設備の出来高や計測結果の集計などをされている場合がよくあります。
このようなVBAマクロをVSTOに置き換えることは、ユーザにとって新たな価値が生まれる可能性があります。

今後もVSTOを使用して効率的な開発を進めていきます。

AIチャット機能を実装

今回は弊社で開発しているアプリに実装したAIチャット機能についてご紹介します。
この機能は、アプリをさらにユーザフレンドリーにし、質問応答をスムーズに行えるようにするために導入されました。

AIチャット機能の実現方法

「Azure OpenAI」と「Azure AI Search」を組み合わせて、AIチャット機能を実現しました。以下に、具体的な手順を説明します。

  1. Azure AI Searchで関連する文章を取得
    「Azure AI Search」は、特定のキーワードやトピックに関連する文章を検索できる強力なサービスです。このサービスを活用して、ユーザの質問に適切な回答を提供するための情報を収集しました。
  2. Azure OpenAIで質問の回答を行う
    「Azure OpenAI」は、自然言語処理技術を駆使して、ユーザの質問に対して適切な回答を生成します。「Azure AI Search」で取得した文章をもとに、ユーザの質問に適切な回答を生成するために「Azure OpenAI」を活用しています。

驚くべき効果

実際にこのAIチャット機能を試してみると、以下のような驚くべき効果がありました。

  1. 文章量が少なくても適切な回答が生成される
    「Azure AI Search」で取得する文章量が少なくても、ユーザの質問に対して適切な回答が生成されることに驚きました。これにより、ユーザが短い質問をしても満足のいく回答を得ることができます。
  2. ユーザフレンドリーなアプリに貢献
    AIチャット機能は、ユーザがアプリを使いやすく感じる要素の一つです。この機能によってさらにユーザフレンドリーになり、ユーザの満足度を向上させることができました。

AIチャット機能は、技術アピールにもなりつつ、ユーザフレンドリーなアプリを作成するための非常に有効な手段であると感じています。今後もAI技術を駆使して、さらなるユーザビリティ向上を行っていきます。

C#でダブルバッファを使って画面のちらつきを防ぐ方法

今回は、C#でダブルバッファを使って画面のちらつきを防ぐ方法についてです。

画面がちらつくという現象は、画面の描画が速く行われるときに起こります。例えばウィンドウをリサイズしたりすると、画面の一部が更新されます。
このとき画面の更新が一瞬で行われると、画面がちらついて見えることがあります。これは、画面の更新がユーザーの目に追いつかないために起こります。

画面のちらつきを防ぐには、ダブルバッファという技術を使います。
ダブルバッファとは、画面に表示するデータを2つのバッファに分けて管理する方法です。
一つのバッファは、画面に表示されるバッファで、もう一つのバッファは、画面に表示する準備をするバッファです。
画面の更新が必要になったときには、準備されたバッファを画面に表示するバッファと入れ替えます。
このようにすると、画面の更新が一瞬で行われるのではなく、スムーズに行われるので、画面のちらつきが防げます。

C#のWindowsFormsでダブルバッファを使うには、DoubleBufferedプロパティを使用します。
DoubleBufferedプロパティは、コントロールのダブルバッファを有効にするかどうかを表すプロパティです。
DoubleBufferedプロパティをtrueにすると、コントロールのダブルバッファが有効になります。
ただし、単に自身のコントロールのDoubleBufferedをtrueにするだけでは、子コントロールのちらつきは防げません。
子コントロールも含めて全てのコントロールのDoubleBufferedをtrueにする必要があります。
しかし、子コントロールのDoubleBufferedは、通常は外部から設定できません。
そこで、リフレクションを使って、外部から子コントロールのDoubleBufferedを設定できるようにし、子コントロールのDoubleBufferedプロパティを再帰的にtrueに設定します。

このようにして、C#でダブルバッファを使って画面のちらつきを防ぐことができます。

Stopwatchクラスの拡張メソッド

C#のStopwatchクラスを使用する際によくあるパターンとして、あるフラグによってスタートまたはリセットしたり、リスタートまたはリセットしたりする場合があります。
せっかくなので拡張メソッドを作成してみました。
実装はしょうもない内容ですが、これによりコードがスッキリします。

public static void StartOrReset(this Stopwatch swbool start) {
    if (start) {
        sw.Start();
    } else {
        sw.Reset();
    }
}
public static void RestartOrReset(this Stopwatch swbool restart) {
    if (restart) {
        sw.Restart();
    } else {
        sw.Reset();
    }
}

お手軽リレー接点出力

最近の案件でリレー接点出力を1点だけ制御する必要があったため、「DSD TECH」の「SH-UR01A」というUSB接続のリレー接点出力を使用しました。
仮想COM通信でコマンドを送信するだけで制御が出来るので、とても簡単です。

普段はコンテックなどのI/Oデバイスを使用しますが、1,2点程度の場合は「SH-UR01A」のほうがお手軽そうです。

DataGridViewの列幅オートサイズモードによって描画速度が遅い場合の対策

DataGridViewの列幅オートサイズモード(AutoSizeColumnsMode)がAllCellsなどのようにセルの内容によってオートサイズするモードの場合、セルのValueの設定回数が多いと描画が遅くなります。

これを改善するために、列幅オートサイズモードを一旦Noneに変更する拡張メソッドを作成しました。
これにより劇的に速度改善しました。

/// <summary>処理実行中に列幅のオートサイズを停止します。セルのValueの設定回数が多い場合、オートサイズ処理が遅いため、一旦オートサイズを停止することで高速化を行います。</summary>
public static void SuspendColumnAutoSize(this DataGridView dgv, System.Action action) {
    //注意:AutoSizeRowsModeは対象外。Noneに変更しただけで遅いため
 
    //finallyで行う処理
    System.Action finallyAction = () => { };
    //AutoSizeColumnsModeがNoneなどでない場合はNoneに変更する
    if (dgv.AutoSizeColumnsMode.EqualsAny(DataGridViewAutoSizeColumnsMode.None, DataGridViewAutoSizeColumnsMode.Fill).Not()) {
        var mode = dgv.AutoSizeColumnsMode;
        finallyAction += () => dgv.AutoSizeColumnsMode = mode;
        dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
    }
    //列のAutoSizeColumnsModeがNoneなどでない場合はNoneに変更する
    foreach (var col in dgv.Columns.ToList()) {
        if (col.AutoSizeMode.EqualsAny(DataGridViewAutoSizeColumnMode.None, DataGridViewAutoSizeColumnMode.NotSet, DataGridViewAutoSizeColumnMode.Fill).Not()) {
            var mode = col.AutoSizeMode;
            finallyAction += () => col.AutoSizeMode = mode;
            col.AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
        }
    }
    //処理実行
    try {
        action();
    } finally {
        finallyAction();
    }
}

UAC(ユーザアカウント制御)などの有効/無効を取得

UAC(ユーザアカウント制御)や、「UACによる管理者昇格時の同意プロンプト表示」の有効/無効によって処理を変えたい場合があったので取得方法を調べました。

結果、レジストリを調べれば良いことが分かりました。簡単ですね。

以下のプロパティを実装してみました。

/// <summary>UAC(ユーザアカウント制御)が有効か</summary>
public static bool? IsUacEnabled {
    get {
        try {
            var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System");
            if (key == null) return null;
            var value = key.GetValue("EnableLUA");
            if (value == null) return null;
            return value.Equals(1);
        } catch {
            return null;
        }
    }
}
 
/// <summary>UACによる管理者昇格時の同意プロンプト表示が有効か</summary>
public static bool? IsAdminConsentPromptEnabled {
    get {
        try {
            var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System");
            if (key == null) return null;
            var value = key.GetValue("ConsentPromptBehaviorAdmin");
            if (value == null) return null;
            return value.Equals(0).Not();
        } catch {
            return null;
        }
    }
}

DisposeActionクラス

C#で後処理を行いたい場合にfinallyブロックに記載することが良くあります。
途中でreturnで抜けても、例外発生しても必ずfinallyブロックが実行されるので安心です。

たとえば以下のように記載します。


try {
    //ここで処理Aを実行
    counter++;
    //ここで処理Bを実行
} finally {
    counter--;
}

処理Aのあとにcounterを+1し、処理Bを実行後にcounterを-1するコードです。
これだと処理Aでreturnや例外発生するとcounterが-1されて正しく動作しないです。
このコードだと、処理Aだけtryブロックの外側で実行するだけで良いですが、そうもいかない場合も多々あったり。

というわけで、DisposeActionクラスを作ってみました。
いたってシンプルなクラスで、コンストラクタで与えたラムダ式をDisposeメソッド内で実行するだけです。
以下のように使用します。


//ここで処理Aを実行
counter++;
using var _ = new DisposeAction(() => counter--);
//ここで処理Bを実行

先程の問題が解決し、行数が減り、コードも見やすくなったのではなかろうかと思います。

DisposeActionのインスタンスは不要なので、本当は以下のように書けると良いのですが。現状のC#では出来ないです。


using new DisposeAction(() => counter--);

社内ライブラリをGit+VisualStudio2022に移行

弊社で開発しているソースコードのバージョン管理はVisualSourceSafeを惰性で使い続けいました。
数年前からは新規プロジェクトはGitを使用していますが、古いソフトはVisualSourceSafeのままです。

社内ライブラリもVisualSourceSafeだったのですが、VisualStudio2022がVisualSourceSafeに対応していないため、この週末にGit+VisualStudio2022に移行しました。
予想していたより問題も起こらず、すんなりと移行完了。単体テストも全てクリア。

こんなに簡単なら、もっと早くに移行しておけばよかった。

ほかのプロジェクトも徐々にVisualSourceSafeからGit+VisualStudio2022に移行を進めていきます。
VisualStudio2022でないと「GitHub Copilot」も使用できないですし。

「GitHub Copilot」で効率的なコーディング

「GitHub Copilot」とは、コーディング中にAIがコード例を提示してくれる、今後のソフトウェア開発を大きく変える革新的なツールです。
弊社でも最近このツールを使用しております。
VisualStudio標準のAI補完機能「InteliCode」よりも柔軟にコード例を提示してくれます。

自分が書いたコードを学習し、適切なコード例を提案してくれます。
開発者は、Copilotが提供するコード例を参考にしながら、より迅速かつ正確なコードを作成することができます。
これにより、開発の生産性が向上し、バグのリスクを低減することができます。

Copilotはまだ新しいツールであり、正しくないコードを提案することもあります。
しかし、Copilotの精度は徐々に向上していくことが期待できますので、今後が楽しみです。