如何在C#中枚举GAC中的所有可用程序集?
实际上我遇到了一个愚蠢的代码问题 - 名为Telerik.Web.UI.dll的程序集在项目中被引用和使用 - 这个特定的DLL在BIN文件夹中不存在。应用程序在服务器上工作正常 - 但在我的机器上,显然编译器会出错。 Ex-Developer坚持认为它不在服务器上的GAC中,而是“它就是这样”。现在我需要检查这个DLL是否在GAC中注册。
帮助将不胜感激。
美好的一天;
答案 0 :(得分:10)
如果您对服务器的访问权限有限,则可能会有效:
// List of all the different types of GAC folders for both 32bit and 64bit
// environments.
List<string> gacFolders = new List<string>() {
"GAC", "GAC_32", "GAC_64", "GAC_MSIL",
"NativeImages_v2.0.50727_32",
"NativeImages_v2.0.50727_64",
"NativeImages_v4.0.30319_32",
"NativeImages_v4.0.30319_64"
};
foreach (string folder in gacFolders)
{
string path = Path.Combine(
Environment.ExpandEnvironmentVariables(@"%systemroot%\assembly"),
folder);
if(Directory.Exists(path))
{
Response.Write("<hr/>" + folder + "<hr/>");
string[] assemblyFolders = Directory.GetDirectories(path);
foreach (string assemblyFolder in assemblyFolders)
{
Response.Write(assemblyFolder + "<br/>");
}
}
}
它基本上枚举了原始GAC文件夹。它适用于DiscountASP共享主机,因此可能适用于您的托管环境。
可以通过更深入地枚举每个程序集的文件夹来删除版本号和公钥令牌来进行修饰。
答案 1 :(得分:4)
查看此codeproject GAC API Interface文章。这使用未记录的fusion.dll来累积GAC。但作者声称
已知此代码可与.NET一起使用 1.1和2.0。请注意,DLL fusion.dll在1.1和更高版本中有所不同 2.0。因此,如果您安装了两个框架,请确保指向代码 到正确的DLL。否则最多 API调用将失败。默认 实现使用的是 WINDOWS目录。
答案 2 :(得分:4)
你真的不需要在C#中写一些东西来找出这个特定的dll是否在gac中。您可以在命令行中使用带有/ l选项的gacutil.exe列出GAC的内容。
答案 3 :(得分:3)
如果它是普通的ASP.NET站点,并且程序集不在站点的bin文件夹或服务器的GAC中(并且web.config中没有任何可疑内容),也许该站点是一个子站点某种类型,其中一个网站的更高版本包含bin文件夹中的引用(或其web.config中的某些内容,因为子站点/文件夹继承了其父项的web.configs)?
现在,如果你有一个正确加载文件的环境(听起来像你这样做),你可以问.NET .dll来自哪里,然后显示它,例如:
Assembly a = Assembly.Load("Microsoft.VisualStudio.Shell, Version=2.0.0.0, "
+ "PublicKeyToken=b03f5f7f11d50a3a, Culture=Neutral");
Console.WriteLine(a.Location);
将加载程序集并显示其磁盘位置,我的输出是:
C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.Shell\2.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.Shell.dll
如果它位于“C:\ Windows \ assembly \”下,您就知道它在GAC中。
你也可以在没有Assembly.Load调用的情况下执行此操作,如果你已经引用了程序集,你可以枚举加载到当前app.domain中的程序集,只需打印出来(或渲染到文字控件,或者Response.Write等等) ...)他们的.Location属性是什么。
修改强> 代码看起来像这样:
foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
{
Console.WriteLine(a.GetName().FullName);
Console.WriteLine(a.Location);
Console.WriteLine();
}
修改强> 在完全信任环境中,以下代码(控制台应用程序)将枚举GAC并写出每个程序集,应该很容易将其修改为ASP.NET应用程序,但我不确定它是否可以在环境中工作这不是完全信任(只是在这里猜测,你可能会很幸运):
static void ProcessFile(string file)
{
try
{
Assembly a = Assembly.LoadFile(file);
Console.WriteLine(a.GetName().FullName);
}
catch { /* do nothing */ }
}
static void ProcessFolder(string folder)
{
foreach (string file in Directory.GetFiles(folder))
{
ProcessFile(file);
}
foreach (string subFolder in Directory.GetDirectories(folder))
{
ProcessFolder(subFolder);
}
}
static void Main(string[] args)
{
ProcessFolder(@"C:\Windows\Assembly");
}
答案 4 :(得分:3)
枚举目录中的dll文件的简单方法
public static string[] GetGlobalAssemblyCacheFiles(string path)
{
List<string> files = new List<string>();
DirectoryInfo di = new DirectoryInfo(path);
foreach (FileInfo fi in di.GetFiles("*.dll"))
{
files.Add(fi.FullName);
}
foreach (DirectoryInfo diChild in di.GetDirectories())
{
var files2 = GetGlobalAssemblyCacheFiles(diChild.FullName);
files.AddRange(files2);
}
return files.ToArray();
}
您可以获取所有文件
string gacPath = Environment.GetFolderPath(System.Environment.SpecialFolder.Windows) + "\\assembly";
var files = GetGlobalAssemblyCacheFiles(gacPath);
如果需要加载每个程序集,则会获得不同运行时版本的异常。因此,您可以使用Assembly.ReflectionOnlyLoadFrom加载程序集,仅在反射中加载程序集。然后,程序集不会加载到您的AppDomain中,也不会抛出异常。
答案 5 :(得分:2)
图我可以投入我的基于linq的解决方案(如果您只是寻找单个文件并且不想枚举每个文件,则很方便)
查找文件的功能:
public static IEnumerable<string> FindFiles (string path, string filter = "", int depth = int.MaxValue) {
DirectoryInfo di = new DirectoryInfo(path);
IEnumerable<string> results = (! string.IsNullOrEmpty(filter) ? di.GetFiles(filter) : di.GetFiles()).Select(f => f.FullName);
if (depth > 0) {
results = results.Concat(di.GetDirectories().SelectMany(d => FindFiles(d.FullName, filter, depth - 1)));
}
return results;
}
要调用(我没有使用Environment.SpecialFolder.Windows,而是使用Environment.SpecialFolder.System代替)
string path = Environment.GetFolderPath(Environment.SpecialFolder.System) + "\\..\\assembly";
foreach (string s in FindFiles(path, "*.dll")) {
Console.WriteLine(s);
}
此外,作为FYI,此方法枚举整个GAC,您可能会在tmp /和temp /中找到重复的DLL,Tmp用于安装,Temp用于卸载。
答案 6 :(得分:1)
检查fusion.dll is used in ILSpy如何枚举所有GAC程序集。
Fusion COM Wrappers
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace GacWithFusion
{
// .NET Fusion COM interfaces
[ComImport, Guid("CD193BC0-B4BC-11D2-9833-00C04FC31D2E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IAssemblyName
{
[PreserveSig]
int SetProperty(uint PropertyId, IntPtr pvProperty, uint cbProperty);
[PreserveSig]
int GetProperty(uint PropertyId, IntPtr pvProperty, ref uint pcbProperty);
[PreserveSig]
int Finalize();
[PreserveSig]
int GetDisplayName([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder szDisplayName,
ref uint pccDisplayName,
uint dwDisplayFlags);
[PreserveSig]
int BindToObject(object refIID,
object pAsmBindSink,
IApplicationContext pApplicationContext,
[MarshalAs(UnmanagedType.LPWStr)] string szCodeBase,
long llFlags,
int pvReserved,
uint cbReserved,
out int ppv);
[PreserveSig]
int GetName(ref uint lpcwBuffer,
[Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwzName);
[PreserveSig]
int GetVersion(out uint pdwVersionHi, out uint pdwVersionLow);
[PreserveSig]
int IsEqual(IAssemblyName pName,
uint dwCmpFlags);
[PreserveSig]
int Clone(out IAssemblyName pName);
}
[ComImport(), Guid("7C23FF90-33AF-11D3-95DA-00A024A85B51"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IApplicationContext
{
void SetContextNameObject(IAssemblyName pName);
void GetContextNameObject(out IAssemblyName ppName);
void Set([MarshalAs(UnmanagedType.LPWStr)] string szName,
int pvValue,
uint cbValue,
uint dwFlags);
void Get([MarshalAs(UnmanagedType.LPWStr)] string szName,
out int pvValue,
ref uint pcbValue,
uint dwFlags);
void GetDynamicDirectory(out int wzDynamicDir,
ref uint pdwSize);
}
[ComImport(), Guid("21B8916C-F28E-11D2-A473-00C04F8EF448"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IAssemblyEnum
{
[PreserveSig]
int GetNextAssembly(out IApplicationContext ppAppCtx,
out IAssemblyName ppName,
uint dwFlags);
[PreserveSig]
int Reset();
[PreserveSig]
int Clone(out IAssemblyEnum ppEnum);
}
internal static class Fusion
{
// dwFlags: 1 = Enumerate native image (NGEN) assemblies
// 2 = Enumerate GAC assemblies
// 4 = Enumerate Downloaded assemblies
//
[DllImport("fusion.dll", CharSet = CharSet.Auto)]
internal static extern int CreateAssemblyEnum(out IAssemblyEnum ppEnum,
IApplicationContext pAppCtx,
IAssemblyName pName,
uint dwFlags,
int pvReserved);
}
}
<强> 主 强>
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace GacWithFusion
{
public class Program
{
static void Main(string[] args)
{
foreach (var assemblyName in GetGacAssemblyFullNames())
{
Console.WriteLine(assemblyName.FullName);
}
}
public static IEnumerable<AssemblyName> GetGacAssemblyFullNames()
{
IApplicationContext applicationContext;
IAssemblyEnum assemblyEnum;
IAssemblyName assemblyName;
Fusion.CreateAssemblyEnum(out assemblyEnum, null, null, 2, 0);
while (assemblyEnum.GetNextAssembly(out applicationContext, out assemblyName, 0) == 0)
{
uint nChars = 0;
assemblyName.GetDisplayName(null, ref nChars, 0);
StringBuilder name = new StringBuilder((int)nChars);
assemblyName.GetDisplayName(name, ref nChars, 0);
AssemblyName a = null;
try
{
a = new AssemblyName(name.ToString());
}
catch (Exception)
{
}
if (a != null)
{
yield return a;
}
}
}
}
}
答案 7 :(得分:-3)