20210605のC#に関する記事は14件です。

System.Text.Jsonのシリアライズで躓いた点について...。[C#]

class JSON { internal List<string> Word { get; set; } internal string Test { get; set; } internal string Test2 { get; set; } internal int[] Test3 { get; set; } } class Program { static void Main() { JSON testString = new JSON() { Word = new List<string> { "Apple", "Banana", "Pine", }, Test = "Hello", Test2 = "HEllo222", Test3 = new int[] { 1, 2, 3, }, }; //シリアライズ string jsonString = JsonSerializer.Serialize(testString, new JsonSerializerOptions { WriteIndented = true}); //JSON文字列をテキストファイルに保存 File.WriteAllText(@"C:\Users\user\test.txt", jsonString); } 上の図のようにデータクラス(JSON.cs)を用意して、Program.csでJSON文字列にしていこう思ったのですが、上手くいきませんでした。 原因はデータクラス(JSON.cs)のプロパティのアクセス修飾子がinternalだったからです。 下の図のようにデータクラス(JSON.cs)のアクセス修飾子をpublicにすれば上手くいきます。 class JSON { public List<string> Word { get; set; } public string Test { get; set; } public string Test2 { get; set; } public int[] Test3 { get; set; } } しかし何故internalではいけないのか分かりませんでした。 分かり次第、追記します。(2020/6/5更新)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

イベントハンドラ名のプレフィックス「on」の解釈

はじめに 「プログラムは英語の自然言語に近くなるように」というテーマで命名を検討するに当たって、イベントハンドラに付与するonプレフィックスについての質問を受けました。 なんとなくはわかるもののうまく言語化できず、良い機会なので調べてみました。 その調査結果として、どのように捉えれば上記テーマと齟齬が生じないのか、一つの解釈を提示してみます。 ※あくまで一つの解釈です。 前置詞onの意味 まずは前置詞としてのonの意味を掘り下げます。 用途として多いのは「上に」に類するものですが、本来は「接触している」状況を表していたのだそうです。 そこから「上側に接している」という意味で使用されたり、「時間に接している」ということでon Mondayやon scheduleという用法が用いられたりするようになったようです。 更にそこから転じて、「on + 動作を表す名詞/動名詞」で「〜するとすぐに」という意味を持つようにもなりました。 Oxford Advanced Learner's Dictionaryの例文が分かりやすかったです。 immediately after something On arriving home I discovered they had gone. Please report to reception on arrival. There was a letter waiting for him on his return. イベントハンドラのonプレフィックスはこの用法を利用していると考えられます。 また、英語の掲示板では、onがタイミングを示す意味で使われることから、whenのタイプ数を減らすために代わりにonが用いられたのではないか、という意見もありました。 このどちらかで整理できそうな気がしてきました。 利用パターン別解釈 event構文での解釈 まずはC#のevent構文での命名を考えます。 event構文利用箇所の命名 public class Enemy { public event EventHandler OnDead; } public class Player { public void StartBattle(Enemy enemy) { enemy.OnDead += () => this.FinishBattle(); ... } } これのenemy.OnDead += () => this.FinishBattle()の部分を英文にすると下記のようになります。 ()は省略されている単語を補った部分です。 onをそのまま使うパターン On enemy's (being) dead, finish battle. onをwhenに置き換えるパターン When enemy (is) dead, finish battle. 翻訳サイトで日本語に翻訳してみましょう。 On enemy's being dead, finish battle. →敵が死んだら戦闘終了。 Finish battle on enemy's being dead. →敵が死亡した時点で戦闘を終了する。 When enemy is dead, finish battle. →敵が死亡したら戦闘終了。 きれいな文章になりましたね。 命令文として扱ってほしいところが主語省略版の平叙文として訳されてしまってはいますが。 イベントハンドラメソッドでの解釈 では、イベントハンドラメソッドにonプレフィックスを付与するのはどのように解釈できるでしょうか。 イベントハンドラメソッドの命名 public class Authentication { public static event EventHandler OnSucceeded; } public class MainView { public MainView() { Authentication.OnSucceeded += this.OnAuthenticationSucceeded(); } private void OnAuthenticationSucceeded() { this.OpenWindow(); } } これのOnAuthenticationSucceeded() => this.OpenWindow()の部分を英文にすると下記のようになります。 ()は省略されている単語を補った部分です。 onをそのまま使うパターン On authentication's (having been) succeeded, open window. onをwhenに置き換えるパターン When authentication (has been) succeeded, open window. 翻訳してみましょう。 On authentication's having been succeeded, open window. →認証が成功したら、ウィンドウを開きます。 When authentication has been succeeded, open window. →認証に成功したらウィンドウを開きます。 こちらもきれいな文章にできました。 イベントハンドラのような扱いのメソッドでの解釈 多用は避けたいですが、依存の方向が「処理を行うクラス→タイミング決めるクラス」ではなく逆の場合があります。 こういう場合はevent構文は使用できないので、外から直接メソッドを呼び出すことになります。 イベントハンドラのような扱いのメソッドの命名 public class CharcterSelectionView { private OnSelectedCharacterIndexChanged(int index) { var character = this.characters[index]; character.OnSelected(); } } public class Character { public void OnSelected() { this.SayHello(); } } これのcharacter.OnSelected()の部分を英文にすると下記のようになります。 ()は省略されている単語を補った部分です。 このパターンの場合はonをそのまま使うよりwhenに置き換えた方が理解しやすい文章になります。 onをそのまま使うパターン Character, (do process for) on (your being) selected. onをwhenに置き換えるパターン Character, (do process for) when (you are) selected. 翻訳してみましょう。Characterのままだと不自然な文章なため上手く訳してくれなかったので、Johnにしてみました。 John, do process for on your being selected. →ジョン、あなたの選択に関するプロセスを行います。 John, do process for when you are selected. →ジョン、あなたが選ばれたときの手続きをしてください。 話の繋がる文章になりました。 「do process forの省略だなんて無理矢理だ」と感じるかもしれません。 しかしこの部分は「do things for」等複数の表現が可能な大した意味を持たない部分なので省略し、大事な部分のみ残して簡潔に意図が伝わるようにした、という見解は可能なのではないかと考えます。 しかし、イベント駆動にできそうな場合は、そちらを軸に設計を行うのが適切な場面が多いように思います。 その他の解釈 OnClick or OnClicked 言語やフレームワークにより両方の命名パターンが存在し、これまでそれぞれどういう思想なのか自信を持って説明できずにいました。 しかし今回調査を行なって、下記のような解釈で良いのではないかと整理できました。 「on + 動作を表す名詞/動名詞」で「〜するとすぐに」となる ここから以下のパターンが作れます。 on click = on + 動作を表す名詞 on (being) clicked = on + 動作を表す動名詞 「click」は動詞の原型ではなく、名詞だったのですね。 「on + イベント名」という解釈 「on + 動作を表す名詞/動名詞」で「〜するとすぐに」を意味するので、「on + イベント名」で「〇〇イベント後すぐに」を表すと考えることも可能です。 OnButtonClick = on button's click event 翻訳してみましょう。 On button's click event, show message box. →ボタンクリックイベントでメッセージボックスを表示します。 省略部分の補完量が少ないので、まずはこちらの考え方で慣らすというのはアリかもしれません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

async/await 非同期メソッドの待ちかた調査

async/await 非同期メソッドの待ちかた async/awaitを使うと数珠繋ぎでasync/awaitが連鎖しがち。 そんな時に非同期処理を同期として呼び出したい場合に以下の2パターンが多い様子。。 task.GetAwaiter().GetResult() task.Wait() 例外で差分があるようなので比較した。 Wait() ソース namespace Sandbox { using System; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { try { Console.WriteLine($"{DateTime.Now} start"); Task.Run(HeavyFunctionAsync).Wait(); Console.WriteLine($"{DateTime.Now} end"); } catch (Exception e) { Console.WriteLine(e.ToString()); } } public static async Task HeavyFunctionAsync() { for (var i = 0; i < 5; i++) { await Task.Delay(1000); Console.WriteLine($"{DateTime.Now} N={i}"); } throw new InvalidOperationException("exception"); return; } } } 結果 AggregrateExceptinoとして捕捉。InnerException見る必要があるのがメンドクサイ。 2021/06/05 21:30:30 start 2021/06/05 21:30:32 N=0 2021/06/05 21:30:33 N=1 2021/06/05 21:30:34 N=2 2021/06/05 21:30:35 N=3 2021/06/05 21:30:36 N=4 System.AggregateException: One or more errors occurred. (exception) ---> System.InvalidOperationException: exception at Sandbox.Program.HeavyFunctionAsync() in D:\work\Repos\Sandbox\Sandbox\Program.cs:line 30 --- End of inner exception stack trace --- at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.Wait() at Sandbox.Program.Main(String[] args) in D:\work\Repos\Sandbox\Sandbox\Program.cs:line 13 GetAwaiter().GetResult() ソース。 namespace Sandbox { using System; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { try { Console.WriteLine($"{DateTime.Now} start"); Task.Run(HeavyFunctionAsync).GetAwaiter().GetResult(); Console.WriteLine($"{DateTime.Now} end"); } catch (Exception e) { Console.WriteLine(e.ToString()); } } public static async Task HeavyFunctionAsync() { for (var i = 0; i < 5; i++) { await Task.Delay(1000); Console.WriteLine($"{DateTime.Now} N={i}"); } throw new InvalidOperationException("exception"); return; } } } 結果 InvalidOperationExceptionとして捕捉。 2021/06/05 21:27:48 start 2021/06/05 21:27:49 N=0 2021/06/05 21:27:50 N=1 2021/06/05 21:27:51 N=2 2021/06/05 21:27:52 N=3 2021/06/05 21:27:53 N=4 System.InvalidOperationException: exception at Sandbox.Program.HeavyFunctionAsync() in D:\work\Repos\Sandbox\Sandbox\Program.cs:line 30 at Sandbox.Program.Main(String[] args) in D:\work\Repos\Sandbox\Sandbox\Program.cs:line 13 結論 Wait()は良くある別タスク内で発生した例外同様AggreateExceptinoでまとめてChatchされる。 一方でGetAwaiter().GetResult()は同期メソッドで例外が発生したかの如く、発生時の状態で補足される。 個人的にはGetAwaiter().GetResult()の方が好きだが別意見の人もいそう
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DataGridなどのItemsControlでSelectedItemsやIsSelectedをBindingする

はじめに WPFでDataGridなどのItemControlを使用していると複数を選択した際のSelectedItemsを取得したいということがあります。 この際に、コードビハインドを使用すればあまり苦労せずに使用できますが、ViewModelとBindingしようとすると、難しくなってしまいます。 ググるとこれでもかというほどたくさんの実装方法が出ていますが、自分の記録用としてもここに記載しようと思います。 ここではDataGridを例に取り上げてみます。 IsSelectedのバインディング まずはシンプルなものからIsSelectedのバインディング方法ですが、これはDataGridの場合、DataGrid.RowStyleを以下のコードのように設定するとできます <DataGrid ItemsSource="{Binding SampleList}"> <DataGrid.RowStyle> <Style TargetType="DataGridRow"> <Setter Property="IsSelected" Value="{Binding IsSelected}"/> </Style> </DataGrid.RowStyle> </DataGrid> SelectedItemsのバインディング DataGridにはSelectedItemsというDependency Propertyがないので、この場合はビヘイビアを作成してバインドできるようにします ビヘイビアのコードは以下の通りです DataGridで使用するだけでなく、ItemsControlを継承したコントロールで使用できるようにSelectorのビヘイビアとして作成しています。 そのためにOnSelectionChangedでvar selector = AssociatedObjectとしています。varはdynamicとしてもかまいません using Microsoft.Xaml.Behaviors; using System.Collections; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; namespace WPF.Behaviors { public class SelectedItemsBehavior : Behavior<Selector> { public IList SelectedItems { get { return (IList)GetValue(SelectedItemsProperty); } set { SetValue(SelectedItemsProperty, value); } } // Using a DependencyProperty as the backing store for SelectedItemsProxy. This enables animation, styling, binding, etc... public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register("SelectedItems", typeof(IList), typeof(SelectedItemsBehavior), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); protected override void OnAttached() { base.OnAttached(); this.AssociatedObject.SelectionChanged += OnSelectionChanged; } protected override void OnDetaching() { base.OnDetaching(); this.AssociatedObject.SelectionChanged -= OnSelectionChanged; } private void OnSelectionChanged(object sender, SelectionChangedEventArgs e) { if (sender == null) return; var selector = AssociatedObject; if(e.AddedItems != null && e.AddedItems.Count > 0 && this.SelectedItems != null) { foreach(var item in e.AddedItems) { this.SelectedItems.Add(item); } } if(e.RemovedItems != null && e.RemovedItems.Count > 0 && this.SelectedItems != null) { foreach(var item in e.RemovedItems) { this.SelectedItems.Remove(item); } } } } } Dependency PropertyとしてSelectedItemsを作成し、OnSelectionChangedでAddedItems、RemovedItemsで項目を追加削除しています あとは、Viewで以下のように使用します <DataGrid ItemsSource="{Binding SampleList}"> <i:Interaction.Behaviors> <behavior:SelectedItemsBehavior SelectedItems="{Binding SelectedItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/> </i:Interaction.Behaviors> </DataGrid> これでSelectedItemsとのバインディングが可能となりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

NLogの出力先をfluentd にしてmongodb経由で中身を見る

NLogのtargetにはいろいろあってasp で作ったWEB Serviceやらファイルやらが使えるけど、 fluentd にすると様々なログ収集が便利になる. 例えばS3に出力するなら https://docs.fluentd.org/output/s3 ただし内容の確認は less /var/log/foo.log みたいなやり方だとLogLevelによるフィルタリングが面倒だったりするので内容を mongodbに集めて見やすくしたい。 ここでは docker 使って fluentd,mongodbを実行してみる ディレクトリ構成 fluentd │ docker-compose.yml ├─fluentd │ │ Dockerfile │ │ │ └─conf │ fluentd.conf docker-compose.yml version: '3' services: fluentd: build: ./fluentd ports: - "24224:24224" - "24224:24224/udp" volumes: - ./fluentd/conf:/fluentd/etc environment: FLUENTD_CONF: fluentd.conf mongo: image: mongo volumes: - ./mongo:/data/db ports: - 27017:27017 fluentd と mongodb を起動するようにする。 それぞれ port はデフォルトのまま。 fluentd/Dockerfile FROM fluent/fluentd:latest # below RUN includes plugin as examples elasticsearch is not required # you may customize including plugins as you wish RUN apk add --update --virtual .build-deps \ sudo build-base ruby-dev \ && sudo gem install \ fluent-plugin-mongo \ && sudo gem sources --clear-all \ && apk del .build-deps \ && rm -rf /var/cache/apk/* \ /home/fluent/.gem/ruby/2.5.0/cache/*.gem fluentd イメージに mongo プラグインを追加する fluentd/conf/fluentd.conf <source> type forward port 24224 bind 0.0.0.0 </source> <match consoleapp2.*> @type mongo host mongo port 27017 database fluentd collection consoleapp2 # for capped collection capped capped_size 1024m # key name of timestamp time_key time # flush flush_interval 10s </match> fluentd に送信されたログを mongodbに送るような設定。 match に指定しているのは nlog.conf に指定したタグ。 C# アプリでの設定 Nlog.Target.Fluentd.NetStandard パッケージマネージャーでNlog..Fluentd.NetStandard をインストール install-package Nlog.Target.Fluentd.NetStandard nlog.config <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true" internalLogLevel="Trace" internalLogFile="${basedir}\logs\internal-nlog.log"> <extensions> </extensions> <variable name="logDirectory" value="${basedir}\logs" /> <targets> <target xsi:type="Fluentd" name="fluentd" host="localhost" port="24224" tag="consoleapp2.log" layout="[${level:uppercase=true}] [${threadid}] ${callsite:includeNamespace=false}#${callsite-linenumber} ${message} ${exception:format=tostring}"/> </targets> <rules> <logger name="*" minlevel="Trace" writeTo="fluentd,console" /> </rules> </nlog> level とか layout は要件に合わせて適宜設定する docker 起動 いつものように docker compose up -d アプリケーション 実行 .\consoleapp2.exe dotnet コマンドの場合 dotnet run データの確認 A5SQL:MK2 とか MongoDB Compass とか使って localhost:27017 に接続する。 対象のコレクション(RDBで言うテーブル) は fluentd.conf に記述した database、collection となる こんな感じ consoleapp2._id consoleapp2.level consoleapp2.message consoleapp2.logger_name consoleapp2.sequence_id consoleapp2.time 60BB4E09CCC3320012542A29 Trace [TRACE] [1] TraceInterceptorAsync.InterceptSynchronous#38 Service DoSomething method start ConsoleApp2.Services.Service 3 2021/06/05 10:11:21 60BB4E09CCC3320012542A2A Trace [TRACE] [1] TransactionInterceptorAsync.InterceptSynchronous#41 Service DoSomething method start ConsoleApp2.Services.Service 4 2021/06/05 10:11:21 60BB4E09CCC3320012542A2B Trace [TRACE] [1] TransactionInterceptorAsync.InterceptSynchronous#45 Service DoSomething method end ConsoleApp2.Services.Service 5 2021/06/05 10:11:21 60BB4E09CCC3320012542A2C Trace [TRACE] [1] TraceInterceptorAsync.InterceptSynchronous#40 Service DoSomething method end ConsoleApp2.Services.Service 6 2021/06/05 10:11:21 60BB4E09CCC3320012542A2D Trace [TRACE] [1] TraceInterceptorAsync.InternalInterceptAsynchronous#66 Service DoSomethingAsync method start ConsoleApp2.Services.Service 7 2021/06/05 10:11:21 60BB4E09CCC3320012542A2E Trace [TRACE] [1] TransactionInterceptorAsync.InternalInterceptAsynchronous#73 Service DoSomethingAsync method start ConsoleApp2.Services.Service 8 2021/06/05 10:11:21 60BB4E09CCC3320012542A2F Trace [TRACE] [1] Service.DoSomethingAsync#31 DoSomethingAsync start ConsoleApp2.Services.Service 9 2021/06/05 10:11:21 60BB4E09CCC3320012542A30 Trace [TRACE] [4] Service.<DoSomethingAsync>b__7_0#33 DoSomethingAsync invoking ConsoleApp2.Services.Service 10 2021/06/05 10:11:21 60BB4E09CCC3320012542A31 Trace [TRACE] [4] TransactionInterceptorAsync.MoveNext#76 Service DoSomethingAsync method end ConsoleApp2.Services.Service 11 2021/06/05 10:11:21 60BB4E09CCC3320012542A32 Trace [TRACE] [4] TraceInterceptorAsync.MoveNext#68 Service DoSomethingAsync method end ConsoleApp2.Services.Service 12 2021/06/05 10:11:21 60BB4E09CCC3320012542A33 Trace [TRACE] [4] Service.Dispose#22 Service disposed ConsoleApp2.Services.Service 13 2021/06/05 10:11:21 その他 オンプレミスやらクラウドやらに各サービスを載せたいならそれなりにする。 参考 この記事自体の参考は 他各サービス等 https://www.fluentd.org/ https://github.com/NLog/NLog https://www.mongodb.com/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

無効になったアドインを有効化してからExcelを起動する

はじめに 業務アプリケーションで、ExcelやWordのアドインをVSTOで開発しています。 ExcelやWordは他の.NETアプリケーションからコマンド起動するのですが、何らかの原因でアドインが無効になってしまう事があり、業務に支障が出ていました。 無効になったアドインを有効化してからExcelやWordを起動する方法をまとめます。 アドインが読み込まれない原因 アドインが読み込まれない場合、以下のいずれかに登録されてしまっている事が原因です。 ExcelやWordの[ファイル]-[オプション]-[アドイン]で確認することができます。 アクティブでないアプリケーションアドイン 無効なアプリケーションアドイン アクティブでないアプリケーションアドイン 以下が原因で、アクティブでないアプリケーションアドインに登録される事があります。 アドインの読み込み時にエラーがスローされた アドインの動作に時間がかかった アドインの動作時にエラーがスローされた Microsoftのドキュメントでは、ソフトに無効化された VSTO アドインという表現が使われています。 https://docs.microsoft.com/ja-jp/visualstudio/vsto/how-to-re-enable-a-vsto-add-in-that-has-been-disabled#soft-disabled-vsto-add-ins 無効なアプリケーションアドイン 以下が原因で、無効なアプリケーションアドインに登録される事があります。 ExcelやWordがクラッシュする、よほどのエラーケースという事ですね。 アドインが原因でExcel/Wordが予期せず終了した Microsoftのドキュメントでは、ハードに無効化された VSTO アドインという表現が使われています。 https://docs.microsoft.com/ja-jp/visualstudio/vsto/how-to-re-enable-a-vsto-add-in-that-has-been-disabled#hard-disabled-vsto-add-ins アドインが管理されているレジストリ それぞれ、以下のレジストリエントリで管理されています。 アクティブでないアプリケーションアドイン アクティブでないアプリケーションアドインに登録されているかどうかは、以下のレジストリを調べることで判断できます。 「Excel」の部分は「Word」「Outlook」でも同様です。 HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\Excel\Addins\<アドイン名> LoadBehavior の値が "2" (アクティブなアプリケーションアドインは"3") LoadBehavior の値が "3"以外の場合、"3"に更新する、という処理で有効化できます。 参考:LoadBehavior の値 https://docs.microsoft.com/ja-jp/visualstudio/vsto/registry-entries-for-vsto-add-ins#loadbehavior-values 無効なアプリケーションアドイン 無効なアプリケーションアドインに登録されているかどうかは、以下のレジストリを調べることで判断できます。 「Excel」の部分は「Word」「Outlook」でも同様です。 HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\<バージョン>\Excel\Resiliency\DisabledItems HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\<バージョン>\Excel\Resiliency\CrashingAddinList ※ <バージョン> には以下の数字が入ります Outlook 2013 : 15.0 Microsoft 365 Apps / Outlook 2019 / Outlook 2016 : 16.0 これらのレジストリの値の名前はランダムで、値にはアドイン名がバイナリ値で格納されています。 アドイン名の判断が難しい場合は、CrashingAddinList や DisabledItems キーごと削除するでも良いかもしれません。 アドインを有効化するコード 上記のレジストリをチェックし、アドインを有効化するコードです。参考になれば幸いです。 using Microsoft.Win32; private void checkAddin() { var app = "Excel"; var addin = "hogeAddin"; // // 「アクティブでないアプリケーションアドイン」のチェック // try { var regKey = $@"SOFTWARE\Microsoft\Office\{app}\Addins\{addin}"; using (var key = Registry.CurrentUser.OpenSubKey(regKey, true)) { if (key == null) { MessageBox.Show("アドインがインストールされていません。"); return; } if (!int.Equals(key.GetValue("LoadBehavior", -1), 3)) { // アクティブになっていないので「3」を設定する key.SetValue("LoadBehavior", 3, RegistryValueKind.DWord); } } } catch (Exception e) { // エラーは無視 Console.Error.WriteLine("レジストリ[LoadBehavior]チェック:Exception\n" + e.ToString()); } // // 「無効なアプリケーションアドイン」のチェック // try { using (var key = Registry.CurrentUser.OpenSubKey($@"SOFTWARE\Microsoft\Office\{getOfficeVer()}\{app}\Resiliency\DisabledItems", true)) { if (key != null) { key.GetValueNames() .ToList() .ForEach(val => { var data = (byte[])key.GetValue(val, null); if (data != null) { // キーで読みだした値から「0」を除去して結合。ASCIIから文字列を取得し、アドイン名が含まれている場合は該当のレジストリを消去。 if (System.Text.Encoding.UTF8.GetString(data.ToList().Where(v => v != 0).ToArray()).Contains($"{addin}")) { key.DeleteValue(val); } } }); } } } catch (Exception e) { // エラーは無視 Console.Error.WriteLine("レジストリ[DisabledItems]チェック:Exception\n" + e.ToString()); } try { using (var key = Registry.CurrentUser.OpenSubKey($@"SOFTWARE\Microsoft\Office\{getOfficeVer()}\{app}\Resiliency\CrashingAddinList", true)) { if (key != null) { key.GetValueNames() .ToList() .ForEach(val => { var data = (byte[])key.GetValue(val, null); if (data != null) { // キーで読みだした値から「0」を除去して結合。ASCIIから文字列を取得し、アドイン名が含まれている場合は該当のレジストリを消去。 if (System.Text.Encoding.UTF8.GetString(data.ToList().Where(v => v != 0).ToArray()).Contains($"{addin}")) { key.DeleteValue(val); } } }); } } } catch (Exception e) { // エラーは無視 Console.Error.WriteLine("レジストリ[CrashingAddinList]チェック:Exception\n" + e.ToString()); } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VSTOのClickOnceアプリではCheckForUpdateによる更新ができない

本記事の内容は2021年5月時点の情報に基づいています。 はじめに Visual Studio Tools for Office (VSTO) でExcelのアドインを開発し、ClickOnceで配布しています。 Excelの起動時、ClickOnceがアドインの更新をチェックし、更新があれば自動的に更新してから読み込まれます。 これが、外出時など、インターネット環境のない状態でExcelを起動した場合、ClickOnceの更新確認で時間がかかりExcelの起動に時間がかかる、という事象がありました。 今回は、VSTOアドインのコードからの更新でハマった事をメモします。 Windows Formsアプリケーションの場合 Windows FormsアプリケーションのClickOnceの場合、「アプリケーションの更新プログラムを確認する」をOFFにしておき、ApplicationDeployment.CheckForUpdateメソッドやUpdateメソッドを呼ぶことでコード上から更新確認と更新処理を行なう事が可能です。 https://docs.microsoft.com/ja-jp/visualstudio/deployment/how-to-check-for-application-updates-programmatically-using-the-clickonce-deployment-api Windowsアプリケーションの発行-更新画面 VSTOの場合 同じ事がVSTOでもできるかと思い、「更新の確認をしない」にして同じように実装してみました。 ところが、CheckForUpdateメソッドの呼び出しで以下のエラーが発生しました。 例外発生: System.Deployment.Application.TrustNotGrantedException: ユーザーは必要なアクセス許可をアプリケーションに与えることを拒否しました。 場所 System.Deployment.Application.ApplicationTrust.RequestTrust(SubscriptionState subState, Boolean isShellVisible, Boolean isUpdate, ActivationContext actCtx, TrustManagerContext tmc) 場所 System.Deployment.Application.DeploymentManager.DetermineTrustCore(Boolean blocking, TrustParams tp) 場所 System.Deployment.Application.DeploymentManager.DetermineTrust(TrustParams trustParams) 場所 System.Deployment.Application.ApplicationDeployment.CheckForDetailedUpdate(Boolean persistUpdateCheckResult) 場所 System.Deployment.Application.ApplicationDeployment.CheckForUpdate() 場所 System.Deployment.Application.ApplicationTrust.RequestTrust(SubscriptionState subState, Boolean isShellVisible, Boolean isUpdate, ActivationContext actCtx, TrustManagerContext tmc) 場所 System.Deployment.Application.DeploymentManager.DetermineTrustCore(Boolean blocking, TrustParams tp) 場所 System.Deployment.Application.DeploymentManager.DetermineTrust(TrustParams trustParams) 場所 System.Deployment.Application.ApplicationDeployment.CheckForDetailedUpdate(Boolean persistUpdateCheckResult) 場所 System.Deployment.Application.ApplicationDeployment.CheckForUpdate() 調査結果 MSに問い合わせたところ、以下のような回答でした。 ClickOnceがアプリケーションのインストールを試みた際に、通常は信頼プロンプトを表示し、ユーザーがインストールを明示的に許可することによって当該アプリケーションに「信頼」が付与されます。 VSTO アドインの更新時も同様の確認が行われますが、CheckForUpdate メソッドによる更新時は信頼プロンプトが表示されないため、信頼された発行元ストアに証明書がインストールされていない場合はアプリケーションに信頼を付与することができず、例外の発生に至ります。 解決策としては、ClickOnceマニフェストに署名した証明書の公開鍵を、クライアントにインストールしておく、という回答でしたが、既に数百台と展開している業務アプリだったため、各クライアントにインストールして回るのは無理でした。 信頼された発行元ストアに証明書がインストールされていない場合、アプリケーションのインストールを試みた際に信頼プロンプトを表示し、ユーザーがインストールを明示的に許可することによって当該アプリケーションに信頼が付与されます。 信頼された発行元ストアに証明書がインストールされている場合は、信頼プロンプトは表示されずそのままアプリケーションのインストールが進みます。 VSTO アドインの更新時も同様の確認が行われますが、CheckForUpdate メソッドによる更新時は信頼プロンプトが表示されないため、信頼された発行元ストアに証明書がインストールされていない場合はアプリケーションに信頼を付与することができず、例外の発生に至ります。 また、VSTO以外との比較について 標準の ClickOnce アプリケーションでは信頼プロンプトでインストールを許可して信頼済みとマークした際にレジストリに情報を保持していますが、VSTO アドインのインストール時には同レジストリ情報が保持されていないように見受けられます。 結論として、 大変恐れ入りますが、VSTO では ApplicationDeployment.CheckForUpdate メソッドの動作が想定されておらず、ご利用いただくことができない状況でした。 ドキュメントに記載されておらず大変恐れ入りますが、今後変更される見通しはなく、このメソッドを利用してアプリケーション内から更新を行うことはできません。 という事でした。。 別案 別案として、VSTOInstaller.exeを呼ぶことで非同期に更新する事ができました。 但し、非同期のため新しいバージョンでの起動は次回起動時となります。更新があったらメッセージを表示し、ユーザーにExcelの再起動を促す必要があります。 VSTOInstaller.exe /install [URL] /silent コード例 const string defaultInstallerPath = @"C:\Program Files\Common Files\Microsoft Shared\VSTO\10.0\VSTOInstaller.exe"; string installerPath = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VSTO Runtime Setup\v4", "InstallerPath", defaultInstallerPath).ToString(); string deploymentUrl = "<https://***.***.jp/hoge.vsto";> string arg = $"/install {deploymentUrl} /silent"; Process.Start(installerPath, arg); 結論 VSTOのClickOnceアプリはCheckForUpdateによる更新ができない。 代案として、VSTOInstaller.exeで非同期更新することができ、次回起動時に最新版が起動される。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MSIXでパッケージ化する場合はレジストリアクセスに気を付ける

はじめに ClickOnceに変わる新しいアプリケーション配布の仕組みとして、MSIXがあります。 既存の.NET Frameworkで作成したアプリケーションでも、Windows アプリケーション パッケージ プロジェクトをソリューションに追加するだけで、容易にMSIXでパッケージ化することができます。 (具体的なやり方はDocsを参照) https://docs.microsoft.com/ja-jp/windows/msix/desktop/desktop-to-uwp-packaging-dot-net 今回は、MSIXでパッケージングした結果、レジストリアクセスにハマってしまった事例について紹介します。 MSIXでパッケージ化したらレジストリが他のアプリと共有できなくなった 既存のアプリでレジストリに書き込みを行ない、他のアプリで同じキーを読み取るケースを想定して下さい。 例えば、以下の2つのアプリがあるとします。 アプリA・・・今回MSIXでパッケージ化したアプリ アプリB・・・その他のアプリ 以下の流れで同じレジストリキーの値に対して読み書きを行ないます。 レジストリキーHKCU\SOFTWARE\HOGEのVAL値に「0」が設定されているとする アプリAでVAL値を読み込む アプリBでVAL値を読み込む アプリAでVAL値を「1」に書き替える アプリAでVAL値を読み込む アプリBでVAL値を読み込む 単体のEXEとして動作させた場合、  2.・・・「0」  3.・・・「0」  5.・・・「1」  6.・・・「1」 という結果になります。当然の結果です。 ところが、この アプリA をMSIXでパッケージングして配布・動作させると、  2.・・・「0」  3.・・・「0」  5.・・・「1」  6.・・・ 「0」 という結果になります。 MSIXでパッケージ化されたアプリからのレジストリ アクセスは仮想化される MSに問い合わせた結果、 MSIXでパッケージ化されたアプリからのレジストリ アクセスは仮想化されており、システムや他のアプリケーションから分離された【ユーザーごと・アプリごとのプライベートな場所】にリダイレクトされる。 そのため、パッケージ化されたアプリケーションから直接他のアプリケーションで読み書きされるレジストリに書き込むことはできない。 ということでした。以下に詳細な説明があります。 https://docs.microsoft.com/ja-jp/windows/msix/desktop/desktop-to-uwp-behind-the-scenes#registry 即ち、MSIXでパッケージ化したアプリからHKLM\SoftwareやHKCU以下への書き込みを行なうと、インストール後初回の書き込み時に【ユーザーごと・アプリごとのプライベートな場所】にコピーされ、それ以降はその場所に対する読み書きとなる、という事です。 上記の例で言うと、 4.アプリAで上記のレジストリキーの値を「1」に書き替える の時点で、書き込みを行なったレジストリキーの値がアプリA専用のプライベートな場所にコピーされ、そちらに対する書き込み・読み込みとなっていたため、他のアプリBから読み取った値と異なってしまった、という事です。 (MSの回答では、HKCU全体ではなく、書き込みを行なった「値」単位でコピーされるとのこと) なお、値の読み込みに関しては、システムレジストリの値と、【ユーザーごと・アプリごとのプライベートな場所】の値がマージされて読み取られるそうです。 回避策 .NETのコードでレジストリの書き込みを行なうと上記の現象になりますが、reg.exeを起動して別プロセスから値を書き込むと、システムレジストリに対する書き込みとなる事がわかりました。 元々、C#のRegistryKey.SetValueメソッドで書き込みを行なっていましたが、以下のようにプロセスを起動する事で、【ユーザーごと・アプリごとのプライベートな場所】が作られることなく、レジストリへの書き込みが行なえました。 var argument = @"ADD ""HKEY_CURRENT_USER\SOFTWARE\HOGE"" /v ""VAL"" /t REG_DWORD /d ""1"" /f"; Process.Start("reg.exe", argument); なお、読み込みに関しては、書き込みを全てreg.exeで行なう前提であれば、RegistryKey.GetValueメソッドで行なっても問題ありません。 (【ユーザーごと・アプリごとのプライベートな場所】ができていない、という前提) 注意点 既にMSIXで配布し、レジストリへの書き込みを行なってしまい、【ユーザーごと・アプリごとのプライベートな場所】が作成されてしまったものについては、MSIXアプリをアンインストールするしかこれを削除する方法はありません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pictureBoxに画像を埋め込む

1.新規プロジェクトを立ち上げ、Form1.csにpictureBoxを配置して下さい pictureBoxのサイズと配置場所はとりあえずどこでもいいです。 2.埋め込みたい画像をプロジェクト内に入れる作業を行います。 2-1.ソリューションエクスプローラー→プロジェクト名を右クリック→追加→新しいフォルダーを選択→「Recources」というフォルダー名にしてエンターを押す 2-2.ソリューション内のPropertiesをクリックし下記の画面を表示させてください 2-3. 上記の画面に切り替わったら左のメニュー(画像ではアプリケーションとなっている部分)のリソース*をクリックします。 2-4. 水色のメニュー「文字列」横にある▼をクリックしイメージを選択してください。 2-5.文字列一覧からイメージ一覧が表示される画面へ切り替わります。 (画像を挿入していないので一覧には何も表示されません) 2-6.枠内に取り込みたい画像をドラッグ&ドロップで挿入します。 2-7.今回は「?ブロック」の画像を挿入したので一覧画面に表示されていることが分かります(作成したResourcesフォルダー内に元画像データが保存されているのが分かります) 3.次にClass内に以下のコードを記述します。 Form1.cs namespace maptiptest { public partial class Form1 : Form { // 変数定義 public Image img; private void Form1_Paint(object sencdr, PaintEventArgs e) { Graphics g = e.Graphics; // Properties内のResourcesからtestという画像を探す img = Properties.Resources.test; // 変数名, 設置する座標X, 設置する座標Y, 画像の幅Width, 画像の高さHeight); g.DrawImage(img, 10, 10, 48, 48); } } } 4.記事の初めに設置したPictureBoxのプロパティ→イベントをもう一度開き、Paint項目に関数名(Form1_Pain)を記述してください。 5.以上のことをやり終え開始ボタンをクリックすると画像が表示されます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【C#/ASP.NET】ファイルアップロードを扱うAPIを作り、Swaggerで動作確認する

概要 ASP.NET Web APIでmultipart/form-dataでファイルアップロードを扱う方法の実装を示す。 Swaggerを用いてファイルアップロードAPIの動作確認を行う手順を示す。 実現したいこと (1)Swaggerから画像データをmultipart/form-dataで送信する。 (2)サーバサイドでmultipart/form-dataを解析し、画像情報をローカルに格納する。 動作確認環境 Visual Studio 2019 ASP.NET Webアプリケーション(.NET Framework4.6.2) Nugetパッケージ ・Swashbuckle V5.6.0(swagger) Internet Explore ver21H1 目次 ファイルアップロードのWeb Apiを実装する * 1.ソリューションを作成する * 2.Swaggerを設定する * 3.WebApiを実装する * 4.Swaggerで動作確認する。 ファイルアップロードを扱うWeb Apiを実装する 1.ソリューションを作成する (1)ASP.NET Webアプリケーションを選択する。 ここでは[MultiPartServer]って名称で作っています。名前は任意です。 (2).NET Frameworkのバージョンを選択する。   今回は.NET Framework4.6.2を選択した。 (3)Web APIを選ぶ。 2.Swaggerを設定する (1)Nugetパッケージをインストールする。 (2)プロジェクトのビルド設定を変更して、XMLファイルを出力するように変更する。 [ビルド]→[XMLドキュメントファイル]にチェックを入れる。 (3)「ソリューションエクスプローラ」で「App_Start直下」の[SwaggerConfig.cs]を開く。 (3)自動生成コードを下記のように修正する。 SwaggerConfig using System.Web.Http; using WebActivatorEx; using MultiPartServer; using Swashbuckle.Application; using System.IO; [assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")] namespace MultiPartServer { public class SwaggerConfig { public static void Register() { var thisAssembly = typeof(SwaggerConfig).Assembly; GlobalConfiguration.Configuration .EnableSwagger(c => { c.SingleApiVersion("v1", "MultiPartServer"); //XMLファイルを読込む c.IncludeXmlComments(GetXmlCommentsPath()); }) .EnableSwaggerUi(c => { }); } private static string GetXmlCommentsPath() { #warning パスはXMLドキュメントファイルの設定に合わせて修正すること return Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "bin", "MultiPartServer.xml"); } } } (4)Swagger UI画面用にフィルタ設定を適用する。 下記ソースを追加する。 UploadFileOperationFilter.cs using System.Web.Http.Description; using Swashbuckle.Swagger; namespace MultiPartServer.Controllers { /// <summary> /// Swagger UI用ファイルアップロードフィルタ /// </summary> public class UploadFileOperationFilter : IOperationFilter { public void Apply( Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription ) { operation.consumes.Add("multipart/form-data"); operation.parameters = new[] { new Parameter() { name = "File", @in = "formData", required = true, type = "file", description = "UploadFile" }, }; } } } 3.WebApiを実装する (1)「ソリューションエクスプローラ」で「Controllers」を選択する。 (2)「追加」→「Web API コントローラクラス」を選択してファイルを追加する。 (3)追加したファイルを下記のように修正する。 using System; using System.Net; using System.Net.Http; using System.Web.Http; using System.IO; using System.Threading.Tasks; using System.Web; using Swashbuckle.Swagger.Annotations; namespace MultiPartServer.Controllers { /// <summary> /// ファイルアップロードのイベントハンドラ /// </summary> public class UploadController : ApiController { /// <summary> /// Postメソッド /// </summary> /// <returns>Httpステータスコード</returns> [SwaggerOperationFilter(typeof(UploadFileOperationFilter))] public async Task<IHttpActionResult> PostFile() { // multipart/form-data以外、サポート外のメディア種類(Http Status 415)を返す if(!Request.Content.IsMimeMultipartContent()) { return StatusCode(HttpStatusCode.UnsupportedMediaType); } // multipart/form-dataを保存する場所を指定する var root = HttpContext.Current.Server.MapPath("~/App_Data"); var provider = new MultipartFormDataStreamProvider(root); try { //データを読み取る await Request.Content.ReadAsMultipartAsync(provider); foreach(var file in provider.FileData) { //ファイルに格納する var fileInfo = new FileInfo(file.LocalFileName); } } catch(Exception e) { return InternalServerError(e); } return Ok(); } } } ポイントはMultipartFormDataStreamProviderクラス。 アップロードされたmultipart/form-dataをファイルストリームとして読み込むことができる。 4.Swaggerで動作確認する。 (1)デバック実行する。 (2)URL欄に[/swagger]を追記して、Enterを押す。 ※por番号はご自身の環境のものが表示されます。 (3)下記のようなSwagger UI画面が表示されることを確認する。 (4)UploadのPostメソッドのタブを開く。 (5)送信したいファイルを選択し、データを送信する。  (5-a)送信したいファイルを[Value]の項目で選択する。  今回は下記のような単純なテキストデータを送った。 Test.txt ABCD  (5-b)[Try it out]を押下する。 (6)Http応答を確認する。 ステータコードが200なら、問題なし。 (7)送信した画像情報が指定したフォルダに格納されていることを確認する。 (7-a)Visual Studioの「ソリューションエクスプローラ」で「App_Data」を選択する。 右クリックして、[エクスプローラでファルダを開く]を選択する。  (7-b)開かれたエクスプローラにファイルが格納されていること。  下記のように送信情報が変な名前のファイルが作成されている。   (7-c)テキストエディタで格納されたファイルを開く。 送信内容と一致してれば、問題なし。 サンプル実装のリンク Gitに実装をアップしています。 リンク 参考 No リンク 説明 1 ASP.NET Web Apiガイダンス 「ASP.NET Web API での HTML フォームデータの送信: ファイルのアップロードとマルチパート MIME」を参考にした。 2 MSDNコードギャラリー 参考 No1のソースコードのダウンロードリンクが切れており、Githubからガイダンスのソースを探したもの。 3 しばやんさんの雑記 記事「ASP.NET Web API でアップロードされたファイルを扱う方法を知らなかったので調べた」を参考にした。 4 @volpe28vさんの記事 Qiitaの記事「ASP .Net MVC 4 WebAPI のPOSTでマルチパートのファイルを送受信する」 5 TonyTonyKunさんの記事 「ファイルをアップロードする API の Swagger ドキュメントを書く」を参考にした。 6 How to upload multipart/form-data in web api 2 英語でmultipart/form-dataでファイルアップロードを扱う方法が記載されている。 7 MultipartFormDataStreamProviderクラス MSDNのクラス仕様書
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【C#/ASP.NET Web API】ファイルアップロードを扱うAPIを作り、Swaggerで動作確認する

概要 ASP.NET Web APIでmultipart/form-dataでファイルアップロードを扱う方法の実装を示す。 Swaggerを用いてファイルアップロードAPIの動作確認を行う手順を示す。 実現したいこと (1)Swaggerから画像データをmultipart/form-dataで送信する。 (2)サーバサイドでmultipart/form-dataを解析し、画像情報をローカルに格納する。 動作確認環境 Visual Studio 2019 ASP.NET Webアプリケーション(.NET Framework4.6.2) Nugetパッケージ ・Swashbuckle V5.6.0(swagger) Internet Explore ver21H1 目次 ファイルアップロードのWeb Apiを実装する * 1.ソリューションを作成する * 2.Swaggerを設定する * 3.WebApiを実装する * 4.Swaggerで動作確認する。 ファイルアップロードを扱うWeb Apiを実装する 1.ソリューションを作成する (1)ASP.NET Webアプリケーションを選択する。 ここでは[MultiPartServer]って名称で作っています。名前は任意です。 (2).NET Frameworkのバージョンを選択する。   今回は.NET Framework4.6.2を選択した。 (3)Web APIを選ぶ。 2.Swaggerを設定する (1)Nugetパッケージをインストールする。 (2)プロジェクトのビルド設定を変更して、XMLファイルを出力するように変更する。 [ビルド]→[XMLドキュメントファイル]にチェックを入れる。 (3)「ソリューションエクスプローラ」で「App_Start直下」の[SwaggerConfig.cs]を開く。 (3)自動生成コードを下記のように修正する。 SwaggerConfig using System.Web.Http; using WebActivatorEx; using MultiPartServer; using Swashbuckle.Application; using System.IO; [assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")] namespace MultiPartServer { public class SwaggerConfig { public static void Register() { var thisAssembly = typeof(SwaggerConfig).Assembly; GlobalConfiguration.Configuration .EnableSwagger(c => { c.SingleApiVersion("v1", "MultiPartServer"); //XMLファイルを読込む c.IncludeXmlComments(GetXmlCommentsPath()); }) .EnableSwaggerUi(c => { }); } private static string GetXmlCommentsPath() { #warning パスはXMLドキュメントファイルの設定に合わせて修正すること return Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "bin", "MultiPartServer.xml"); } } } (4)Swagger UI画面用にフィルタ設定を適用する。 下記ソースを追加する。 UploadFileOperationFilter.cs using System.Web.Http.Description; using Swashbuckle.Swagger; namespace MultiPartServer.Controllers { /// <summary> /// Swagger UI用ファイルアップロードフィルタ /// </summary> public class UploadFileOperationFilter : IOperationFilter { public void Apply( Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription ) { operation.consumes.Add("multipart/form-data"); operation.parameters = new[] { new Parameter() { name = "File", @in = "formData", required = true, type = "file", description = "UploadFile" }, }; } } } 3.WebApiを実装する (1)「ソリューションエクスプローラ」で「Controllers」を選択する。 (2)「追加」→「Web API コントローラクラス」を選択してファイルを追加する。 (3)追加したファイルを下記のように修正する。 using System; using System.Net; using System.Net.Http; using System.Web.Http; using System.IO; using System.Threading.Tasks; using System.Web; using Swashbuckle.Swagger.Annotations; namespace MultiPartServer.Controllers { /// <summary> /// ファイルアップロードのイベントハンドラ /// </summary> public class UploadController : ApiController { /// <summary> /// Postメソッド /// </summary> /// <returns>Httpステータスコード</returns> [SwaggerOperationFilter(typeof(UploadFileOperationFilter))] public async Task<IHttpActionResult> PostFile() { // multipart/form-data以外、サポート外のメディア種類(Http Status 415)を返す if(!Request.Content.IsMimeMultipartContent()) { return StatusCode(HttpStatusCode.UnsupportedMediaType); } // multipart/form-dataを保存する場所を指定する var root = HttpContext.Current.Server.MapPath("~/App_Data"); var provider = new MultipartFormDataStreamProvider(root); try { //データを読み取る await Request.Content.ReadAsMultipartAsync(provider); foreach(var file in provider.FileData) { //ファイルに格納する var fileInfo = new FileInfo(file.LocalFileName); } } catch(Exception e) { return InternalServerError(e); } return Ok(); } } } ポイントはMultipartFormDataStreamProviderクラス。 アップロードされたmultipart/form-dataをファイルストリームとして読み込むことができる。 4.Swaggerで動作確認する。 (1)デバック実行する。 (2)URL欄に[/swagger]を追記して、Enterを押す。 ※por番号はご自身の環境のものが表示されます。 (3)下記のようなSwagger UI画面が表示されることを確認する。 (4)UploadのPostメソッドのタブを開く。 (5)送信したいファイルを選択し、データを送信する。  (5-a)送信したいファイルを[Value]の項目で選択する。  今回は下記のような単純なテキストデータを送った。 Test.txt ABCD  (5-b)[Try it out]を押下する。 (6)Http応答を確認する。 ステータコードが200なら、問題なし。 (7)送信した画像情報が指定したフォルダに格納されていることを確認する。 (7-a)Visual Studioの「ソリューションエクスプローラ」で「App_Data」を選択する。 右クリックして、[エクスプローラでファルダを開く]を選択する。  (7-b)開かれたエクスプローラにファイルが格納されていること。  下記のように送信情報が変な名前のファイルが作成されている。   (7-c)テキストエディタで格納されたファイルを開く。 送信内容と一致してれば、問題なし。 サンプル実装のリンク Gitに実装をアップしています。 リンク 参考 No リンク 説明 1 ASP.NET Web Apiガイダンス 「ASP.NET Web API での HTML フォームデータの送信: ファイルのアップロードとマルチパート MIME」を参考にした。 2 MSDNコードギャラリー 参考 No1のソースコードのダウンロードリンクが切れており、Githubからガイダンスのソースを探したもの。 3 しばやんさんの雑記 記事「ASP.NET Web API でアップロードされたファイルを扱う方法を知らなかったので調べた」を参考にした。 4 @volpe28vさんの記事 Qiitaの記事「ASP .Net MVC 4 WebAPI のPOSTでマルチパートのファイルを送受信する」 5 TonyTonyKunさんの記事 「ファイルをアップロードする API の Swagger ドキュメントを書く」を参考にした。 6 How to upload multipart/form-data in web api 2 英語でmultipart/form-dataでファイルアップロードを扱う方法が記載されている。 7 MultipartFormDataStreamProviderクラス MSDNのクラス仕様書
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C#のExpressionTreeでref/outパラメータを使う

C#のExpressionTreeでrefやoutの引数を使うときはType.MakeByRefType()を使う。 ActionやFuncは使えないのでdelegateを定義する必要があることにも注意。 /// <summary> /// ExpressionTree経由でTargetMethodを呼び出すだけのサンプル /// </summary> public class Test { /// <summary> /// ExpressionTree経由でTargetMethodを呼び出して結果を出力する /// </summary> public void InvokeTargetMethodWithExpressionTree() { // 呼び出すメソッド情報を取得 MethodInfo methodInfo = typeof(Test).GetMethod(nameof(TargetMethod)); // out引数を定義(MakeByRefTypeを使う) ParameterExpression valueArg = Expression.Parameter(typeof(int).MakeByRefType()); // ExpressionTreeでdelegateを作成 TestDelegate compiled = Expression.Lambda<TestDelegate>( Expression.Call( Expression.Constant(this), methodInfo, valueArg ), valueArg ).Compile(); // 呼び出して結果を出力 compiled.Invoke(out int intValue); Console.WriteLine($"intValue={intValue}"); } /// <summary> /// ExpressionTreeで呼び出されるメソッド /// </summary> public void TargetMethod(out int value) { value = 100; } private delegate void TestDelegate(out int value); }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C#のExpressionTreeでin/ref/outパラメータを使う

C#のExpressionTreeでrefやoutの引数を使うときはType.MakeByRefType()を使う。 ActionやFuncは使えないのでdelegateを定義する必要があることにも注意。 /// <summary> /// ExpressionTree経由でTargetMethodを呼び出すだけのサンプル /// </summary> public class Test { /// <summary> /// ExpressionTree経由でTargetMethodを呼び出して結果を出力する /// </summary> public void InvokeTargetMethodWithExpressionTree() { // 呼び出すメソッド情報を取得 MethodInfo methodInfo = typeof(Test).GetMethod(nameof(TargetMethod)); // out引数を定義(MakeByRefTypeを使う) ParameterExpression valueArg = Expression.Parameter(typeof(int).MakeByRefType()); // ExpressionTreeでdelegateを作成 TestDelegate compiled = Expression.Lambda<TestDelegate>( Expression.Call( Expression.Constant(this), methodInfo, valueArg ), valueArg ).Compile(); // 呼び出して結果を出力 compiled.Invoke(out int intValue); Console.WriteLine($"intValue={intValue}"); } /// <summary> /// ExpressionTreeで呼び出されるメソッド /// </summary> public void TargetMethod(out int value) { value = 100; } private delegate void TestDelegate(out int value); }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity(C#)】ネットワーク接続の状態に応じて処理を行う

はじめに OculusQuestでアプリがオフラインかオンラインかに応じた処理が必要になったので調べました。 Application.internetReachability 結論、Application.internetReachabilityを使えばOKです。 状態に応じてEnumを返します。 Enum 状態 NotReachable インターネットに接続していない ReachableViaCarrierDataNetwork キャリアネットワークで接続している ReachableViaLocalAreaNetwork Wi-Fiでネットワークに接続している 【参考リンク】:Unityで作成したアプリにおいて、ネットワーク接続を確認する場合に使う「Application.internetReachability」! バージョン情報 UniRx 7.1.0 Unity 2019.4.8f1 コード ネットーワークの状態に応じてテキストを変更する簡易サンプルです。 ReactivePropertyで状態が変わった時だけ処理するようにしています。 using UniRx; using UnityEngine; using UnityEngine.UI; /// <summary> /// インターネット接続をチェックするサンプル /// </summary> public class NetworkConditionChecker : MonoBehaviour { [SerializeField] private Text networkConditionText; private readonly ReactiveProperty<NetworkReachability> _networkReachReactiveProperty = new ReactiveProperty<NetworkReachability>(); private void Start() { _networkReachReactiveProperty .SkipLatestValueOnSubscribe() .Subscribe(condition => { if (condition == NetworkReachability.NotReachable) { networkConditionText.text = "接続無し"; } else { networkConditionText.text = "接続有り"; } }) .AddTo(this); } private void Update() { _networkReachReactiveProperty.Value = Application.internetReachability; } } デモ 画面下の接続の状態に応じてテキストが変更されました。成功です。 エディター上でも実機上でも変わりなく動作してくれてありがたいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む