.NET Frameworkのライブラリのpublicな名前を探求する

この記事は完全に投稿を忘れていたC# Advent Calendar 2016 - Qiitaの14日目です。

目的

問1「.NET Frameworkの標準ライブラリで一番長いクラス名やメソッド名は?」
問2「publicな”HWND”を含む名前を持つ名前はどう表記するのが適切?」

問1についてはAPI名最長選手権 - Qiitaという記事が2013年にあります。その後の更新などでもっと長い名前がないか確認してみたいものです。

問2については、もう少し実用的。Win32にはウィンドウハンドルを表すHWND型が頻繁に登場します。.NETで扱うときはWindowHandleやHandleといった名前が適切ですが、相互運用の関係上、HWNDをそのまま使いたい状況もしばしばあります。 その時の名前は、一単語とみなしてHwndとするか、Wnd(Windowの略)を意識してHWndの方が正しい気もします。こういうときは、標準ライブラリの命名に従って置くのが間違いありません。

ということで、標準ライブラリのpublicな名前を列挙するソフトを作成してみます。

とりあえず型名の列挙

型名を列挙するのはAssemblyのGetTypesを使うだけです。今回はとりあえず50文字以上を抽出してみます。

この程度なら、VisualStudioなどふよう、LinqPadとかを使えば簡単ですね!

結果。
50文字以上のクラス名の列挙結果

一つしか列挙されません。ここではAppDomain.CurrentDomain.GetAssemblies();読み込み済みのアセンブリ一覧を取得しているので全部のライブラリが読み込まれているわけではありません。

標準ライブラリのアセンブリを列挙するには

では、標準ライブラリのアセンブリを列挙するにはどうすれば良いのか。

まず、コンピューター上で共有されるライブラリはGAC(Global Assembly Cache)に保存されています。
グローバル アセンブリ キャッシュ
に書いてあるとおり、

このフォルダは以前はshfusion.dllというシェル拡張によりアセンブリを列挙する特殊な表示がされていましたが、現在ではこの機能は廃止されて普通にディレクトリの中身をエクスプローラから見ることができます。 この中身のDLLを片っ端から列挙すればOKです。

別関数にくくりだし、条件を外部から与えるようにしています。その際、PublicでないものとIsImportがtrueの物(COM由来)は見てもしょうがないので弾きます。 ここまで来ると流石に時間がかかるため、Parallel.ForEachでお手軽並列処理をしています。便利。

さて、私の環境で実行してみると、一番長いクラス名は名前空間含まずに75文字。

標準ライブラリで長いのは54文字「System.Windows.AttachedPropertyBrowsableWhenAttributePresentAttribute」53文字「System.Windows.Forms.ListViewVirtualItemsSelectionRangeChangedEventHandler」あたりですね。
うーむ、長い。

では続いて、メンバーの検索に踏み込みます。
修正点が雑ですが・・・

以下は実行結果。なお、実際には複数回列挙されますが、多分検索のやり方が雑で別バージョンのアセンブリを二回読み込んでるからです。

107 : System.ServiceModel.MessageSecurityVersion.get_WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10
107 : System.ServiceModel.MessageSecurityVersion.get_WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10
103 : System.ServiceModel.MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10
103 : System.ServiceModel.MessageSecurityVersion.WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10

get_から始まるものはコンパイラが自動生成するプロパティに対応したメソッドなので対象外とすると、最長はやはりWSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10でした。ぱちぱち!

ちなみに、IsImportの条件を消してCOM由来のものも含めてもこれが最長です。

問2:HWNDの表記は?

A : ほとんどHwndと表記される。

以下のようなプログラムで探しました。普通の命名規則だと_は入らないので、_が入れば特殊な用途やプロパティのgetter/setterだろうと考え省いてます。

それなりの数の検索結果がヒットします。HwndをHbitmapやHfont、Hrgnなどに変更しても同様に発見されます。

しかし、HWndで見つからないかというとそうでもありません。以下の2件のみ見つかります。

Microsoft.VisualStudio.Platform.WindowManagement.DTE.WindowBase.HWnd
System.Windows.Forms.Message.HWnd

VS系はまだいいのですが、System.Windows.Forms.Message.HWndはもろに標準ライブラリです。(これが見つけたのがそもそもこの記事を書く動機なんですが)。
その他に、HBITMAPとHICONは、基本的にHbitmap、Hfontなのですが一部に例外があります。

HBitmap

System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap
System.Windows.Forms.ControlPaint.CreateHBitmapTransparencyMask
System.Windows.Forms.ControlPaint.CreateHBitmap16Bit
System.Windows.Forms.ControlPaint.CreateHBitmapColorMask

HIcon

System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon

他には、HRESULTとHGLOBALに関してはHresultやHglobalという表記は存在しません。すべてHResult、HGlobalと表記されます。

ネイティブでは?

これで探せるのは.NET Frameworkの名前だけですが、ネイティブの方にもいくつかキャメルケースで書かれた名前が登場する関数が存在します。
まず、OleのGetHGlobalFromStream等の関数です。ではネイティブはすべて大文字が2連続なのかというとそうではありません。
NtGdiHfontCreateNtUserCallHwndなどの関数では小文字になっています。(undocumentedのシステムコールですが・・・)

また、.NET FrameworkのCreateBitmapSourceFromHBitmap関数の元ネタはIWICImagingFactory::CreateBitmapFromHBITMAPだと思われます。この関数ではHBITMAPと大文字表記。

結論

おそらく、キャメルケースで書き表すときの規則はしっかり決まっていないのではないかと思います。
まぁ、相互運用は.NET Framworkの仕事外でしょうから、あまり考え込んでもしょうがないことです。
よって、HResultとHGlobal以外はHwnd、Hiconのように小文字で表記するがよいでしょう。

おまけ;Fusion API

当初、アセンブリの列挙にFusionAPIを使おうと考えていました。FusionAPIとは、GAC上のアセンブリを列挙し、適切なバージョンのDLLを取得するためのAPI群です。こいつを使うことである程度詳しいアセンブリのリストを取得することができます。
FusionAPI自体はネイティブのDLLで提供されています。マニアックなAPIのくせに何故かドキュメント化されている上に翻訳も存在

Fusion (Unmanaged API Reference) fusion (アンマネージ API リファレンス)

しかし、すべてのライブラリのpublicな名前を列挙する、という目的を達成するにはFusionAPIは特に利用価値がなく、せいぜいGACの場所が変更されていることを考慮してGetCachePathを使う程度です。それもソフト化するならまだしも、LINQPad上で動かすなら個人で確認してもらえばいいだけの話。

FusionAPIのサンプルを書いて機能を紹介する、ということも考えたのですが、ILSpyあたりにバッチリなサンプルがあります。

icsharpcode/ILSpy: .NET Decompiler

そもそもドキュメント化されているので、存在さえ知っていれば使うことには苦労しないはず。そんなことよりHWndの方が面白い結果だなーということで今回はこんな感じの記事になりました。