新しもの好きプログラマの耳より情報ブログ

仕事でもあるプログラミングについて役に立ちそうな情報を発信していこうというブログです。役に立たなそうな情報はfacebookで。

VRもくもく会を今週もやりました&その様子の写真

VRもくもく会を今週も開催しました。これは、「VRでのイベントというのも今後は選択肢に入ってくるのではないか、とにかくやってみて経験値を溜めてみよう」ということでやっています。2~3週間おきの定期開催ということで、もう1年以上続けています。

私は、なかなか理解する機会が無かったWindows App SDKについて、WPFへの組み込み方や組み込むメリットなどを調べてみました。アプリケーションのライフサイクルなどは欲しかったところを埋めてくれそうですが、情報が少なく・・・WPFやGenericHostの既存の仕組みとどう組み合わせればメリットが得られるのか、まだ試行錯誤が必要そうです。

mishizakiさんは、ちょっとした作業としてpptxファイルを編集して保存したのに、なぜか変更が反映されない・・・という厄介な問題と戦っていたら時間が終わってしまったようです。QRコードのURL変更だと差分が認識されないっぽい、とのこと?こういう問題はハマってしまうとなかなか抜け出せませんね。

会場の様子を貼って・・・と思ったのですが、今回は終盤にトラブって私一人になってしまいました。なので普段あんまりやらない遠景とか、屋上のくつろぎ空間を撮ってみました。

Entity Framework CoreのマイグレーションがWindows SDKバージョン指定プロジェクトでエラーになる問題の解決方法

概要

Entity Framework Coreを使う場合に、Windows向けのプロジェクトでマイグレーションがエラーになるという問題にぶつかったので、その解決方法です。

解決方法

プロジェクトのターゲットに、次のようにWindowsSDKのバージョン指定が入っている場合に発生します。

<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>

解決方法としては、いったん次のようにWindowsSDK指定を外して実行した後、また元に戻せばOKです。

<TargetFramework>net8.0</TargetFramework>

理由ははっきりしないのですが、エラー内容が次のようになっているため、指定されたバージョンのWindowsSDKのDLLを探しに行き、該当のものが無いためにエラーになっているように見えます。

Error:
An assembly specified in the application dependencies manifest (ProjectName.deps.json) was not found:
package: 'runtimepack.Microsoft.Windows.SDK.NET.Ref', version: '10.0.19041.31'
path: 'Microsoft.Windows.SDK.NET.dll'

同じような問題に .NET 6で引っかかっているスレッドも見かけたので、EF Coreの仕様なのかもしれません。

ごく普通のWindowsSDK指定のプロジェクトで使っていきなりエラーになるというのも困ったものですが・・・知っていれば回避することは簡単なので、面倒ですが運用で何とかするしかなさそうです。

proxmox(VMWare)のWindowsでのUSBリダイレクトは、最新版のvert-viewerでは動かないので注意(2024/10/19時点)

概要

proxmox(VMWare)では、2024/10/19現在、SPICEを使うことでWindowsVMでのUSBリダイレクトも可能となっています。しかし、実際に手順通りに最新版をダウンロードして動かすと上手く行きません。解決方法が分かったので、まとめます。

最初に結論まとめ

解決方法1

virt-viewer(SPICE)について、最新版のv11を使わず、1つ前のv10を使いましょう。それだけで解決します。

virt-manager download pageのVer.10.0-1.0 https://releases.pagure.org/virt-viewer/virt-viewer-x64-10.0-1.0.msiを使うことで、上手く行きました。

解決方法2

もし最新版のv11を使いたい場合は、libusbだけを最新版に差し替えても解決します。

vert-viewerインストール先のlibusbを、Ver.1.0.27へ差し替える事で、上手く行きました。

説明

proxmox上にWindowsVMを作成して、Windowsからリモート接続して使う場合に、リモート接続元のPCに接続したUSBデバイスVM上で使用できると便利です。(proxmox自体については他の情報を参照ください。知っている前提で書きます)

こうした機能をUSBリダイレクト、あるいはUSBパススルーなどと呼びます。SPICEという方式で接続する事でこれを実現できます。Web上にはWindows非サポートといった情報もありますが、現時点ではWindowsでも使えるようになっていました。

しかし、ドキュメントの通りに構築しても、virt-viewer(SPICE)が異常終了してしまい、接続できません。最新版のvirt-viewerで報告のある問題で、未修正。旧バージョンを使えば解決する。という話のようです。正常動作が確認できたバージョンの組み合わせとセットアップ手順をまとめます。proxmox Ver.8.2.7で動作確認しました。

リモート接続先(VM)側

VM側では、次の手順が必要です。順序はおそらく自由です。

  1. 管理画面のVM設定から、ハードウェア->ディスプレイのVGAをSPICEに変更
  2. spice-guest-toolsをインストール
  3. https://www.spice-space.org/download.htmlからusbredirect-x64-0.14.0.msiをDLしてインストール
  4. ハードウェア->追加で、USBデバイスの「spice」を追加

リモート接続元(物理PC)側

USBメモリを接続する物理PC側では、下記の手順が必要です。順序はおそらく自由です。こちらは2つの解決方法があります。用途に合わせてどちらかを選択すれば良いです。順に説明します。

解決方法1, 1つ古いバージョンのvirt-viewerを使う場合

  1. UsbDkをインストール https://www.spice-space.org/download.htmlからVer.1.0.22 UsbDk_1.0.22_x64.msiをDLして使う
  2. virt-viewerをインストール virt-manager download pageからVer.10.0-1.0 https://releases.pagure.org/virt-viewer/virt-viewer-x64-10.0-1.0.msiをDLして使う
    • 2024/9/30現在、最新版ではないので、「Previous releases」からDLする必要があります

virt-viewer旧バージョンを使うことの影響

変更履歴を見ると、バグ修正とホットキー関連の変更がメインのようです。そのため影響は少なそうだと思いますが、この内容の影響が大きい場合は、少し手順が厄介になりますが、次のようにlibusbだけを差し替える手があります。

解決方法2, libusbだけを差し替える場合

  1. UsbDkをインストール https://www.spice-space.org/download.htmlからVer.1.0.22 UsbDk_1.0.22_x64.msiをDLして使う
  2. virt-viewerをインストール virt-manager download pageから最新版11.0-1.0 https://releases.pagure.org/virt-viewer/virt-viewer-x64-11.0-1.0.msiをDLして使う
  3. libusbのDLLだけを最新版へ差し替え
    1. VC++ランタイム(x64)が入っていない場合は、インストールする
    2. libusb 最新版Ver.1.0.27 https://github.com/libusb/libusb/releases/tag/v1.0.27のページを開く
    3. 7zファイル(libusb-1.0.27.7z)をDLして解凍し、VS2022-x64版のDLLを見つける(libusb-1.0.27\VS2022\MS64\dll\libusb-1.0.dll)
    4. virt-viewerのインストール先(%ProgramFiles%\VirtViewer v11.0-256\bin)を開き、そのファイルを上書きする ※バージョンによってフォルダ名が変わりそうなので注意

まとめ

ドキュメントには使えると書かれているのに、実際に最新版を入れると使えない、という罠がありました。思わず挫折してしまいそうになりますが、解決方法もありました。諦めずにproxmoxを使っていきましょう!

VRもくもく会を今週もやりました&その様子の写真

VRもくもく会を今週も開催しました。これは、「VRでのイベントというのも今後は選択肢に入ってくるのではないか、とにかくやってみて経験値を溜めてみよう」ということでやっています。2~3週間おきの定期開催ということで、もう1年以上続けています。

私は、「Build 2024で言ってた、あれ、あれ!」が思い出せずに、動画などの情報を漁っていました。しかし途中でふと気付いて、ふわっとした質問をChatGPTとPerplexityに投げてみたらどっちも正しい回答をくれて、Perplexityは関連のMSブログまで教えてくれました。最初から聞けば良かった・・・使い慣れていきたいですね。

mishizakiさんは、Copilot Studioを試してみようとチャレンジしたそうです。が、個人用アカウントでちょっと試すといった使い方ができず、組織アカウントで評価版を使わないといけないようで・・・そのあたりで苦戦していたら、「作るボタンを押してみた」までで時間切れになってしまったようです。

実はイベントに申し込んでくれた人は他にもいたのですが、都合が合わなかったのか、上手くVRChatが起動できなかったのか、会えませんでした。残念。

会場の写真を貼っておきます。

Entity Framework Coreで、同じプロジェクトに複数のDBContextを入れる場合のマイグレーションの使い分け方

概要

Entity Framework CoreのCode Firstでは、DBContextを継承した型でDBのテーブルを定義すると、コマンドでDBのマイグレーションをしたりそのためのSQL文を出力できます。しかし、1つのプロジェクトで複数のDBを扱いたい場合は、複数のDBContext派生型を作って同じコマンドを打っても、マイグレーションが上手く行きません。その場合のマイグレーションのやり方です。

解決方法

次のように、通常のマイグレーションのコマンドに、2つのオプションを追加します。

dotnet ef migrations add <このマイグレーションの名前> --context <DBContext派生型のクラス名> --output-dir <このマイグレーションの出力先フォルダパス>

contextオプションに、どのDBContext派生型のマイグレーションを追加するかを指定します。ここはクラス名をそのまま指定します。

そのままだと別々のDBContext派生型のマイグレーション出力が混ざって分かりづらくなるため、output-dirオプションに、出力先のフォルダを指定します。DBContext派生型ごとにフォルダを分けると良いと思います。

値を入れた例としてはこんな感じです。

//前提
class UserDBContext : DBContext { ... }
class RoleDBContext : DBContext { ... }
dotnet ef migrations add Ver_2_0 --context UserDBContext --output-dir Migrations/UserDB
dotnet ef migrations add Ver_2_0 --context RoleDBContext --output-dir Migrations/RoleDB

この後のupdateコマンドなども同様に、contextオプションを指定します。

dotnet ef database update --context <DBContext派生型のクラス名>

値を入れた例だとこうなります。

dotnet ef database update --context UserDBContext
dotnet ef database update --context RoleDBContext

EF Coreはサンプル通りに動かすだけならサンプルのコマンドをコピペするだけでも動きますが、ちゃんと使っていくと理解するべきところが増えてきます。1つずつ調べていきましょう。

Taskを返すeventをawaitで待ってはいけない(気付きづらいうっかりミスの紹介)

概要

C#のeventは割と基本的な機能なので、気軽に使っていると思います。しかしawaitと組み合わせたら、意外なところで罠にはまったので、気をつけましょうという事で紹介です。

最初に結論まとめ

Taskを返すタイプのeventをawaitで待った場合、eventに登録されたデリゲート全ての完了を待たず、1つでも完了したら処理が進んでしまいます。これはおそらく、意図と違う動きになっていると思います。気をつけましょう。

良く考えてみれば当たり前の動きですが、見た目には次のような感じで普通に見えるので、コードをぱっと見ただけだとなかなか気付きませんでした。

event Func<Task> action1Async;

await action1Async();

詳しく紹介

Taskを返すタイプのeventとawaitを組み合わせた場合、一見、eventに登録されたデリゲート全ての完了を待機しているように見えます。しかし、実際には1つしか待機していない。という話です。

C#のeventは、Observerパターン的なものなので、複数のObserverを登録できます。複数の登録がある場合は、Invoke()を呼び出した時に、登録されたメソッドが同期で順番に呼ばれ、全ての処理が終わったら制御を返します。こんな感じです。

event Action action1;
action1 += () => Console.Write("1");
action1 += () => Console.Write("2");

//この状態でInvokeを呼び出すと
action1();
Console.Write("3");
//必ず"1"と"2"が出力されてから制御を返すので、コンソール出力は
//>123
//になる

※eventは本来はイベント登録とInvoke()を行える場所が異なりますが、本記事ではコードをシンプルにするため同じ場所から呼び出すコードを書きます

そのため、「Invoke()が制御を返したら、全ての登録済みデリゲートの処理は終わっている」と期待して使うと思います。

しかし、その感覚のままでawaitと組み合わせると、書き方によってはデリゲートの処理が終わる前に制御が返ってきてしまいます。こんな感じの場合です。

event Func<Task> action1Async;

async Task Func1()
{
    await Task.Delay(3000);
    Console.Write("1");
}
async Task Func2()
{
    Console.Write("2");
    await Task.CompletedTask;
}

action1Async += Func1;
action1Async += Func2;

//ここで、action1Asyncの完了をawaitしようとすると・・・
await action1Async();
Console.Write("3");
//"1"が出力されるより前に制御が返ってくるため、コンソール出力がこうなる
//>23

完了をawaitで待機しているはずなのに、なぜ完了前に制御が返ってきてしまうのでしょうか?

実は、よく考えてみると当然の動きだったりします。eventとawaitという便利な構文によって見えづらくなっているだけで、このコードはデリゲート1つ分の完了しか待機していません。ポイントは、eventをInvoke()した場合、全てのデリゲートの実行が終わった後に、「戻り値を1つだけ返す」という動作です。

このケースではFunc1とFunc2がasyncなので、「全てのデリゲートの実行が終わった」時点では、Taskを作成しただけであって処理は完了していません。返ってきたTask全ての完了を待つ必要があります。しかし、eventのInvokeは戻り値を1つしか返しません。つまりawait action1Async();の部分を分解すると、次のようなコードになっていることになります。

Task result = Func1();
result = Func2();
await result;

Func1が返したTaskは待機せずに放置されています。当然、そちらで例外が発生したとしてもcatchできません。

このように考えていくと当たり前なのですが、 await action1Async(); という記法があまりにも正しそうに見えるので、うっかり見逃してしまいがちです。気をつけましょう・・・。

VRもくもく会を今週もやりました&その様子の写真

VRもくもく会を今週も開催しました。これは、「VRでのイベントというのも今後は選択肢に入ってくるのではないか、とにかくやってみて経験値を溜めてみよう」ということでやっています。2~3週間おきの定期開催ということで、もう1年以上続けています。

今日は、明日にイベントがあるので、.NET Aspire Dayの動画はけっきょくどんな話をしてたのか?といった情報交換をしていました。人によって視点が違うので、会話できて良かったと思います。

会場の写真を貼っておきます。