目的
自分自身のCutomAttributeなんかを取得するのは対して苦労はしないんですけど、全くの赤の他人にそこにあるexeファイルからCustomAttributeを引っこ抜かせようという試みです
何の役に立つかって?
……………
実装
いきなりです
一応方針としては、細かいところはブラックボックス化してメインの使い勝手は良いように、的な感じで…
細かいブラックボックス
static class Extensions {
static public string CustomAttributeValue<T>(this PEReader peReader) {
if (!peReader.HasMetadata) return null;
MetadataReader metaDataReader = peReader.GetMetadataReader();
CustomAttribute attributeHandle = metaDataReader.CustomAttributes
.Select(h => metaDataReader.GetCustomAttribute(h))
.FirstOrDefault(ca => IsAttribute<T>(metaDataReader, ca));
if (attributeHandle.Equals(default(CustomAttribute))) return null;
BlobReader reader = metaDataReader.GetBlobReader(attributeHandle.Value);
if (reader.ReadInt16() != 1) return null;
return reader.ReadSerializedString();
static bool IsAttribute<T>(MetadataReader metaDataReader, CustomAttribute customAttribute) {
EntityHandle ctorHandle = customAttribute.Constructor;
EntityHandle typeHandle = default;
string typeName = default;
switch (ctorHandle.Kind) {
case HandleKind.MemberReference:
typeHandle = metaDataReader.GetMemberReference((MemberReferenceHandle)ctorHandle).Parent;
typeName = metaDataReader.GetString(metaDataReader.GetTypeReference((TypeReferenceHandle)typeHandle).Name);
break;
case HandleKind.MethodDefinition:
typeHandle = metaDataReader.GetMethodDefinition((MethodDefinitionHandle)ctorHandle).GetDeclaringType();
typeName = metaDataReader.GetString(metaDataReader.GetTypeDefinition((TypeDefinitionHandle)typeHandle).Name);
break;
}
return typeName == typeof(T).Name;
}
}
}
Extensionです
なので、本体の方はこれを使ってあげれば良いですね
使い勝手の良い方
string assemblyFilePath = @"MyApp.exe";
using (FileStream stream = File.OpenRead(assemblyFilePath))
using (PEReader peReader = new PEReader(stream)) {
Console.WriteLine(peReader.CustomAttributeValue<GuidAttribute>());
}
Console.ReadKey(true);
こんな感じ
PEReaderをこっちに持たせたのは、CustomAttributeを複数回取得したいかも知れないから
つまりー
複数取得のケース
string assemblyFilePath = @"MyApp.exe";
using (FileStream stream = File.OpenRead(assemblyFilePath))
using (PEReader peReader = new PEReader(stream)) {
Console.WriteLine(peReader.CustomAttributeValue<GuidAttribute>());
Console.WriteLine(peReader.CustomAttributeValue<AssemblyFileVersionAttribute>());
Console.WriteLine(peReader.CustomAttributeValue<AssemblyDescriptionAttribute>());
}
Console.ReadKey(true);
まぁ、若干効率は良い筈
結語
CustomAttributeと云ってもなんでも良いって訳じゃなくてー
- 文字列として表現されているモノしかダメよ とか
- PEだからと云ってNativeな奴はCustomAttribute持ってないから抜けない とか
- エラーチェックがだいぶ甘い とか
- 上手くCustomAttribute取れないケースは状況によらずnullが返る とか
- こんなGenericの使い方はどうなん? とか
色々ありますけど、取敢えず(5年越し位に)目的は達成できたのでヨシとします