Metro.cs #1 でぐらばくセッションの後半として喋ったやつです。
実装と言いつつ概要レベルですが。
時間が短かったのもあって雰囲気くらいしか分からなかったのではないかという予感がしているので、詳しいことはソースコードを見つつテストコードをデバッグ実行しながら確かめてみてください。。
KanColleViewer プラグインの作り方などをダラダラと書きます。
(何か思いついたら適宜更新するかも)
※ KanColleViewer 4.2 現在のもの
とりあえず簡単に。
ちゃんとしたのはそのうち気が向いたら書くかも。
でも既存開発者はわかってる人たちだからこれでも十分な気が……
新規開発も追々。
むしろちゃんと書くべきはこっち。。
KanColleViewer 4.0
KanColleViewer 4.0 がリリースされましたが、プラグインシステムが刷新されたため、3.x のプラグインは利用できなくなりました。
Release version 4.0 · Grabacr07/KanColleViewer
プラグイン システムの刷新 (version 3.8.2 またはそれ以前に向けて作られたプラグインは使用できなくなります)
というわけで、3.x プラグインをどう移行すればいいかを 雑に 簡単に説明します。
実際のコードは、本体に付属しているプラグインの実装などを参考にすると良いでしょう。
仕様的な
- Analyzer を使う際、Analyzer の依存アセンブリも Analyzer として参照追加する必要がある
- 通常の参照追加じゃダメ
- NuGet を用いた Analyzer のインストールは install.ps1 で行われる
- RTM 版テンプレートの install.ps1 では、nupkg 解凍直下の analyzers ディレクトリにある dll を Analyzer として登録する
- ので、上手いことそこにアセンブリが配置されるよう nuspec を書く必要がある
- だがどうやら lib 配下以外に配置すると pack 時に怒られるので、
-NoPackageAnalysis
オプションが必要 - この仕様では、Analyzer の依存アセンブリは同梱配布しかできないのではないか疑惑 (PowerShell頑張ればなんとかなるんだろうか)
とりあえずどうすればいいのか
- csproj を使って nuget pack する前提の nuspec を書くパターン
- bin\$configuration$\ 直下の dll を全部 analyzers ディレクトリに突っ込むようにする(Analyzer 開発用アセンブリは除外)
- Analyzer が依存してるアセンブリの「ローカルにコピー」を True にして含まれるようにする
- プロジェクト新規作成時点で指定されている packages.config の中身は開発時しか使わないので developmentDependency=”true” にして利用時の依存を除外
こんな感じ。
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>$id$</id>
<title>$title$</title>
<version>$version$</version>
<authors>$author$</authors>
<owners>$author$</owners>
<licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl>
<projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>$description$</description>
<language>ja-JP</language>
<tags>analyzers</tags>
<frameworkAssemblies>
</frameworkAssemblies>
<dependencies>
</dependencies>
</metadata>
<files>
<file src="bin\$configuration$\*.dll" target="analyzers\" exclude="**\Microsoft.CodeAnalysis.*;**\System.Collections.Immutable.*;**\System.Reflection.Metadata.*;**\System.Composition.*" />
<file src="tools\*.ps1" target="tools\" />
</files>
</package>
で、こう
nuget pack Hoge.csproj -NoPackageAnalysis -Properties Configuration=Release
参照 : Damir’s Corner – Quick Guide to Unit Testing Diagnostic Analyzers
Visual Studio 2015 RC にて、.NET Compiler Platform SDK Templates for RC の「Analyzer with Code Fix」テンプレートで Analyzer 用のプロジェクトを作成すると、自動的にテストプロジェクトも生成されます。
んでもってこれを使ってテストコードを書くわけですが、どうやらここでテストコードに与えるソース文字列がコンパイル通らないコードだとテストに失敗することがあるようです。
で、アセンブリ参照が足りない場合なんかも同様にコケるわけですが、テストプロジェクトにアセンブリ参照を追加しても残念ながら参照してくれません。
ではどうすれば良いかというと、テストプロジェクトの Helpers/DiagnosticVerifier.Helper.cs
内の CreateProject
メソッドにアセンブリ参照を追加している箇所があるので、そこに追加します。
public abstract partial class DiagnosticVerifier
{
//中略
private static readonly MetadataReference KanColleViewerCompositionReference
= MetadataReference.CreateFromAssembly(typeof(Grabacr07.KanColleViewer.Composition.IPlugin).Assembly);
private static readonly MetadataReference SystemComponentModelCompositionReference
= MetadataReference.CreateFromAssembly(typeof(System.ComponentModel.Composition.ExportAttribute).Assembly);
//中略
private static Project CreateProject(string[] sources, string language = LanguageNames.CSharp)
{
string fileNamePrefix = DefaultFilePathPrefix;
string fileExt = language == LanguageNames.CSharp ? CSharpDefaultFileExt : VisualBasicDefaultExt;
var projectId = ProjectId.CreateNewId(debugName: TestProjectName);
var solution = new AdhocWorkspace()
.CurrentSolution
.AddProject(projectId, TestProjectName, TestProjectName, language)
.AddMetadataReference(projectId, CorlibReference)
.AddMetadataReference(projectId, SystemCoreReference)
.AddMetadataReference(projectId, CSharpSymbolsReference)
.AddMetadataReference(projectId, CodeAnalysisReference)
.AddMetadataReference(projectId, KanColleViewerCompositionReference)
.AddMetadataReference(projectId, SystemComponentModelCompositionReference)
;
int count = 0;
foreach (var source in sources)
{
var newFileName = fileNamePrefix + count + “.” + fileExt;
var documentId = DocumentId.CreateNewId(projectId, debugName: newFileName);
solution = solution.AddDocument(documentId, newFileName, SourceText.From(source));
count++;
}
return solution.GetProject(projectId);
}
//以下略
[/csharp]
これでアセンブリが参照されるようになり、テストが通るようになります。
これは RC 版でのテンプレートの問題なので、もしかしたら将来変更されるかもしれませんね。
さて巷ではJVNVU#98282440: 「提督業も忙しい!」(KanColleViewer) がオープンプロキシとして動作する問題が話題ですが、FiddlerCore は何も考えないで実装すると恐らくこうしてしまうような仕様です。クソですね。。
そもそも何が問題なのか
とりあえず適当に FiddlerCore を使ったアプリケーションを立ち上げてみましょう。
[csharp]
FiddlerApplication.Startup(55555, FiddlerCoreStartupFlags.Default);
[/csharp]
するとあら不思議、0.0.0.0:55555でListningされてしまいます。
[raw]
C:\Windows\system32>netstat -a -b -n -o -p TCP
アクティブな接続
プロトコル ローカル アドレス 外部アドレス 状態 PID
…(中略)…
TCP 0.0.0.0:55555 0.0.0.0:0 LISTENING 6688
[FiddlerTest.vshost.exe]
[/raw]
これではアプリが立ち上がってる間は外部からHTTPプロキシとして利用できてしまいます。
知らず知らずのうちに他所様への攻撃の踏み台にされて、ある日突然警察がやってくるなんてこともあるかもしれません。怖いですね。
で、どうすればいいかというと、127.0.0.1:55555でLisningするようにできればローカルプロセスしかアクセスできなくなるので、そう変更したいですね?
これはごく簡単で、AllowRemoteClients
をfalse
にしてやればOKです。
[csharp]
FiddlerApplication.Startup(55555
, FiddlerCoreStartupFlags.RegisterAsSystemProxy
| FiddlerCoreStartupFlags.ChainToUpstreamGateway
| FiddlerCoreStartupFlags.MonitorAllConnections
| FiddlerCoreStartupFlags.CaptureLocalhostTraffic);
[/csharp]
Flagなので指定しなければOK。
[raw]
C:\Windows\system32>netstat -a -b -n -o -p TCP
アクティブな接続
プロトコル ローカル アドレス 外部アドレス 状態 PID
…(中略)…
TCP 127.0.0.1:55555 0.0.0.0:0 LISTENING 7128
[FiddlerTest.vshost.exe]
[/raw]
つまりFiddlerCoreStartupFlags.Default
がイカンわけです。
でも FiddlerCore は Fiddler.exe のUI以外の部分を抜き出したものなわけですから、Fiddler.exe 自体がそういうもんだという可能性もあります。
参照: FiddlerCore – Fiddler Proxy Engine for your .NET Applications
ここで Fiddler.exe および FiddlerCore のデフォルト値を見てみましょう。
めとべや東京 #7 で話したやつです。
IShellLibrary APIを叩くしか無さそうな雰囲気だったので、WindowsAPICodePackを使うという手抜き。
下記はピクチャのパス。
DbContextでの話。
普通はDbContextから取得したデータを更新すれば良い。
[csharp]
using (var context = new Entities())
{
var hoge= context.Hoge.Find(“id的な”);
hoge.Fuga = “New!”;
context.SaveChanges();
}
[/csharp]
これはDbContextに変更追跡機能があるため可能な事。
DbContextのライフサイクル外から受け取ったEntityオブジェクトなんかはただのPOCOであり、どう変更されたかなんてDbContextは知らない。
DataSetなんかは、DataTable自体が変更追跡機能持っててシリアライズもできたのだけど…
自己追跡エンティティ(STE)なんかもあるけど、Not Recommendedとされているし、EF6やVS2013ではもう使えない。
EF4から導入されたばかりなのに短い寿命でしたね…
んで、WCFなんかで外部から受け取ったデータは当然変更追跡の対象外なため、Attachとかしないとダメなことは想像付く。
でもAttachしただけではUnchangedな状態なので、変更してくれない。
ではどうすれば良いかというと、どうやらModified状態に変更してしまえば勝手に更新してくれるようだ。
[csharp]
public void Update(Hoge hoge){
using (var context = new Entities())
{
context.Hoge.Attach(hoge);
context.Entry(hoge).State = EntityState.Modified;
context.SaveChanges();
}
}
[/csharp]
削除はAttachしてからRemoveすればOK。
[csharp]
public void Remove(Hoge hoge){
using (var context = new Entities())
{
context.Hoge.Attach(hoge);
context.Hoge.Remove(hoge);
context.SaveChanges();
}
}
[/csharp]
ちなみにナビゲーションプロパティでIncludeされてる関連オブジェクトがある場合で、外部キーの値を変更したのに、関連オブジェクトは変更前のままとかいう場合、Attachすると死ぬ。
[csharp]
//どっかで読み込んだ
context.Hoge
.AsNoTracking()
.Include(x => x.Piyo)
;
[/csharp]
[csharp]
//どっかで外部参照のキー値を更新した
hoge.PiyoId = “new id”; //※hoge.Piyoは変更前のまま
[/csharp]
[csharp]
//そのままAttachすると死ぬ
context.Hoge.Attach(hoge);
[/csharp]
この場合、どうやらナビゲーションプロパティをnullにしてしまえばAttachできる。
関連オブジェクトを再取得してきてちゃんと値を合わせてやってもうまくいくんじゃないかな?(試してない
[csharp]
hoge.Piyo = null;
//これなら大丈夫。取得時にIncludeしてないのと同じ状態になる。
context.Hoge.Attach(hoge);
[/csharp]
現象
Cドライブ直下に”testdir “というディレクトリがある場合
という具合に検索すると見つからないといわれる。
ディレクトリ名末尾が全角スペースの場合も同様。
原因
と検索しても、実際に検索されるのは”c:\testdir”となるため。
空白をTrimしているっぽい。
回避策
検索するパスの末尾に”\”をつければ検索可能。
参考
全角スペースがあるフォルダあるとSystem.IO.Directory.GetFilesがエラーになる: DOBON.NETプログラミング掲示板過去ログ