Fusion API を使用して GAC に登録されているアセンブリの一覧を取得する

Last Update:
このエントリーをはてなブックマークに追加

こんにちは、Japan Developer Support Core チームの松井です。 今回は、.NET Framework の Fusion API を使用して、グローバル アセンブリ キャッシュ (GAC) に登録されているアセンブリの一覧を取得する方法について説明します。

GAC の概要

GAC とは、.NET Framework において共有アセンブリを格納するためのキャッシュの場所です。GAC に登録されたアセンブリは、複数の .NET Framework アプリケーションから共有して使用することができます。GAC に登録されているアセンブリの例としては、.NET Framework 自体が提供するフレームワーク ライブラリや、サードパーティ製の共有ライブラリなどがあります。もちろんアプリケーション固有のアセンブリを GAC に登録することも可能です。

GAC に登録されているアセンブリの確認方法

GAC にインストールされているアセンブリを確認する一般的な方法としては、Visual Studio や .NET Framework SDK に付属する gacutil.exe を使用する方法があります。しかし、gacutil.exe の再頒布は許諾されていないため、運用環境で利用する場合は開発者ツールをインストールする必要があります。また、従来は .NET Framework に付属していたアセンブリ キャッシュ ビューアーのシェル拡張機能 (shfusion.dll) によりエクスプローラーから GAC の内容を参照することも可能でしたが、これは Windows 8 および Windows Server 2012 以降の OS バージョンでは廃止されており、.NET Framework 4 以降にも対応していません。

Fusion API を使用して GAC のアセンブリを一覧取得する方法

あまり知られていませんが、.NET Framework はアセンブリのインストール、アンインストール、検索などの操作をプログラムから行う方法として Fusion API を公開しています。Fusion API は COM ベースのインターフェイスで提供されており、C++ や C# などの言語から利用できる他、PowerShell からも呼び出すことも可能です。列挙体やインターフェイスなどの型を定義したヘッダー ファイルは .NET Framework SDK に付属しており、C:\Program Files (x86)\Windows Kits\NETFXSDK\<version>\Include\um\fusion.h に格納されています。以下では PowerShell スクリプトから Fusion API を呼び出して、GAC に登録されているアセンブリの一覧を取得するサンプルコードを示します。

注意事項

サンプルスクリプトは機能や使用方法を紹介する目的で提供しているものとなります。実際に運用環境で使用する場合はスクリプトの内容を理解した上で十分な動作確認とテストを行い、お客様の責任のもとでご利用ください。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# List assemblies in the Global Assembly Cache (GAC) like "gacutil.exe /l" by calling the following Fusion APIs.
# https://learn.microsoft.com/en-us/dotnet/framework/unmanaged-api/fusion/createassemblyenum-function
# https://learn.microsoft.com/en-us/dotnet/framework/unmanaged-api/fusion/iassemblyenum-interface
# https://learn.microsoft.com/en-us/dotnet/framework/unmanaged-api/fusion/iassemblyname-interface
# https://learn.microsoft.com/en-us/dotnet/framework/unmanaged-api/fusion/asm-display-flags-enumeration
$source = @"
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;

public class FusionAPi
{
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("21b8916c-f28e-11d2-a473-00c04f8ef448")]
internal interface IAssemblyEnum
{
[PreserveSig]
int GetNextAssembly(IntPtr pvReserved, out IAssemblyName ppName, int flags);
int Reset();
int Clone(out IAssemblyEnum ppEnum);
}

[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("CD193BC0-B4BC-11d2-9833-00C04FC31D2E")]
internal interface IAssemblyName
{
int SetProperty(int PropertyId, IntPtr pvProperty, int cbProperty);
int GetProperty(int PropertyId, IntPtr pvProperty, ref int pcbProperty);
int Finalize();
[PreserveSig]
int GetDisplayName(StringBuilder pDisplayName, ref int pccDisplayName, int displayFlags);
int Reserved(ref Guid guid, Object obj1, Object obj2, String string1, Int64 llFlags, IntPtr pvReserved, int cbReserved, out IntPtr ppv);
[PreserveSig]
int GetName(ref int pccBuffer, StringBuilder pwzName);
int GetVersion(out int versionHi, out int versionLow);
int IsEqual(IAssemblyName pAsmName, int cmpFlags);
int Clone(out IAssemblyName pAsmName);
}

[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Unicode)]
private delegate int CreateAssemblyEnumDelegate(out IAssemblyEnum ppEnum, IntPtr pUnkReserved, IAssemblyName pName, int flags, IntPtr pvReserved);

[DllImport("kernel32.dll", SetLastError=true)]
private static extern IntPtr LoadLibrary(string lpFileName);

[DllImport("kernel32.dll", SetLastError=true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procName);

[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool FreeLibrary(IntPtr hModule);

public static List<string> GetGacAssemblies()
{
RegistryKey regKeyNetFx = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\.NETFramework", false);
string installRoot = Path.Combine(regKeyNetFx.GetValue("InstallRoot").ToString(), "v4.0.30319");
string fusionDllPath = Path.Combine(installRoot, "fusion.dll");
IntPtr hModule = LoadLibrary(fusionDllPath);
if (hModule == IntPtr.Zero)
{
throw new Win32Exception(fusionDllPath);
}

List<string> result = new List<string>();
try
{
if (hModule != IntPtr.Zero)
{
const string procName = "CreateAssemblyEnum";
IntPtr pfnCreateAssemblyEnum = GetProcAddress(hModule, procName);
if (pfnCreateAssemblyEnum == IntPtr.Zero)
{
throw new Win32Exception(procName);
}

var d = (CreateAssemblyEnumDelegate)Marshal.GetDelegateForFunctionPointer(pfnCreateAssemblyEnum, typeof(CreateAssemblyEnumDelegate));

IAssemblyEnum assemblyEnum = null;
int hr = d(out assemblyEnum, IntPtr.Zero, null, 2 /* AssemblyCacheFalgs.Gac */, IntPtr.Zero);
if (hr < 0)
{
Marshal.ThrowExceptionForHR(hr);
}

IAssemblyName assemblyName = null;
while (assemblyEnum.GetNextAssembly(IntPtr.Zero, out assemblyName, 0) >= 0 && assemblyName != null)
{
// ASM_DISPLAYF_VERSION | ASM_DISPLAYF_CULTURE | ASM_DISPLAYF_PUBLIC_KEY_TOKEN | ASM_DISPLAYF_PROCESSORARCHITECTURE | ASM_DISPLAYF_RETARGET
const int displayFlag = 0xA7;
int bufferSize = 0;
StringBuilder buffer = null;
assemblyName.GetDisplayName(buffer, ref bufferSize, displayFlag);
buffer = new StringBuilder(bufferSize);
assemblyName.GetDisplayName(buffer, ref bufferSize, displayFlag);
result.Add(buffer.ToString());
}
}
}
finally
{
FreeLibrary(hModule);
}
return result;
}
}
"@
Add-Type -Language CSharp -TypeDefinition $source
[FusionApi]::GetGacAssemblies()


本ブログの内容は弊社の公式見解として保証されるものではなく、開発・運用時の参考情報としてご活用いただくことを目的としています。もし公式な見解が必要な場合は、弊社ドキュメント (https://learn.microsoft.comhttps://support.microsoft.com) をご参照いただくか、もしくは私共サポートまでお問い合わせください。