[英]Assembly loaded using Assembly.LoadFrom() on remote machine causes SecurityException
[英]Reflection not working on assembly that is loaded using Assembly.LoadFrom
我有一个包含一些反射代码的库,该代码检查Asp.Net的主程序集,任何引用的程序集并进行出色的处理。 我试图获取相同的代码以在控制台应用程序中执行,同时仍然反映在Asp.Net的程序集上,并且看到了奇怪的结果。 我已经完成所有工作并执行了代码,但是当我在调试器中逐步执行该反射代码时,当我知道反射代码应该返回true时,反射代码将返回false。这使我发疯,我不知道为什么从控制台应用程序运行时,反射表现出不同的行为。
这是一些反射代码的完美示例,该反射代码获取Asp.Net应用程序中所有类型的区域注册( type.IsSubclassOf(typeof(System.Web.Mvc.AreaRegistration))
)。 在Asp.Net应用程序的应用程序域中执行时,对于几种类型返回true,但是在控制台应用程序中执行时,对于那些相同类型返回false,但是仍然反映那些相同的Asp.Net类型。
我也尝试过使用Assembly.ReflectionOnlyLoadFrom
方法,但是即使编写了所有代码以手动解析引用的程序集之后,下面显示的反射代码在应返回true的类型上也返回false。
我怎样才能使这项工作?
public static Assembly EntryAssembly { get; set; } // this is set during runtime if within the Asp.Net domain and set manually when called from the console application.
public CodeGenerator(string entryAssemblyPath = null)
{
if (entryAssemblyPath == null) // running under the Asp.Net domain
EntryAssembly = GetWebEntryAssembly(); // get the Asp.Net main assembly
else
{
// manually load the assembly into the domain via a file path
// e:\inetpub\wwwroot\myAspNetMVCApp\bin\myApp.dll
EntryAssembly = Assembly.LoadFrom(entryAssemblyPath);
}
var areas = GetAreaRegistrations(); // returns zero results under console app domain
... code ...
}
private static List<Type> GetAreaRegistrations()
{
return EntryAssembly.GetTypes().Where(type => type.IsSubclassOf(typeof(System.Web.Mvc.AreaRegistration)) && type.IsPublic).ToList();
}
这与LoadFrom
加载程序集的程序集上下文有关。 在Load
上下文中解析“常规”程序集时,将不使用在LoadFrom
加载的依赖项。
相同的应用ReflectionOnly
重载,该重载加载到ReflectionOnly上下文中。
有关详细信息,请参见https://stackoverflow.com/a/2493855/292411 ,并避免Assembly.LoadFrom; 而是使用Assembly.Load解决与您类似的LoadFrom
问题。
当我遇到这个问题时,我切换到使用Load
并要求“插件”程序集与可执行文件位于同一路径中。 我不知道如果程序集位于不同的路径下是否有使工作正常的技巧。
好的,经过大量调试后,我可以正常工作了! 事实证明,即使Nuget和属性窗口要求5.1,我的库项目仍针对Asp.Net MVC 4.0进行编译。 Nuget / MS再次失败。 我的库所反映的Asp.Net MVC应用程序正在使用MVC 5.1,因此,当Assembly.LoadFrom
和AssemblyResolve
事件运行时,它将两个版本的System.Web.Mvc.dll
加载到LoadFrom
上下文(4.0和5.1)中,并且当预期结果应该为true时,这导致IsSubclassOf()
方法返回false。
我在调试时在上面的注释中提到的非常奇怪的错误: The type 'System.Web.Mvc.AreaRegistration' exists in both 'System.Web.Mvc.dll' and 'System.Web.Mvc.dll'
,但只有在事实之后。
我最终跟踪此问题的方法是,写出所有需要AssemblyResolve
进行解析的程序集,并注意到System.Web.Mvc.dll
不在列表中。 我启动了程序集绑定日志查看器 ,很清楚地看到System.Web.Mvc.dll
被加载了两次。
回想起来,应该只跳过所有自定义日志记录,而仅使用Assembly Binding Log Viewer来验证每个程序集中只有一个正在加载并且它是您期望的正确版本。
弄清楚如何正确使用AssemblyResolve
是一场噩梦,所以这是我未完成的但后代有效的代码。
public class CodeGenerator
{
public static string BaseDirectory { get; set; }
public static string BinDirectory { get; set; }
static CodeGenerator()
{
BinDirectory = "bin";
// setting this in a static constructor is best practice
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}
public CodeGenerator(string entryAssemblyPath = null, string baseDirectory = null, string binDirectory = null)
{
if (string.IsNullOrWhiteSpace(baseDirectory))
BaseDirectory = AppDomain.CurrentDomain.BaseDirectory;
else
BaseDirectory = baseDirectory;
if (string.IsNullOrWhiteSpace(binDirectory) == false)
BinDirectory = binDirectory;
if (entryAssemblyPath == null) // running under the Asp.Net domain
EntryAssembly = GetWebEntryAssembly(); // get the Asp.Net main assembly
else
{
// manually load the assembly into the domain via a file path
// e:\inetpub\wwwroot\myAspNetMVCApp\bin\myApp.dll
EntryAssembly = Assembly.LoadFrom(entryAssemblyPath);
}
var areas = GetAreaRegistrations(); // reflect away!
... code ...
}
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
try
{
if (args == null || string.IsNullOrWhiteSpace(args.Name))
{
Logger.WriteLine("cannot determine assembly name!", Logger.LogType.Debug);
return null;
}
AssemblyName assemblyNameToLookFor = new AssemblyName(args.Name);
Logger.WriteLine("FullName is {0}", Logger.LogType.Debug, assemblyNameToLookFor.FullName);
// don't load the same assembly twice!
var domainAssemblies = AppDomain.CurrentDomain.GetAssemblies();
var skipLoading = false;
foreach (var dAssembly in domainAssemblies)
{
if (dAssembly.FullName.Equals(assemblyNameToLookFor.FullName))
{
skipLoading = true;
Logger.WriteLine("skipping {0} because its already loaded into the domain", Logger.LogType.Error, assemblyNameToLookFor.FullName);
break;
}
}
if (skipLoading == false)
{
var requestedFilePath = Path.Combine(Path.Combine(BaseDirectory, BinDirectory), assemblyNameToLookFor.Name + ".dll");
Logger.WriteLine("looking for {0}...", Logger.LogType.Warning, requestedFilePath);
if (File.Exists(requestedFilePath))
{
try
{
Assembly assembly = Assembly.LoadFrom(requestedFilePath);
if (assembly != null)
Logger.WriteLine("loaded {0} successfully!", Logger.LogType.Success, requestedFilePath);
// todo: write an else to handle load failure and search various probe paths in a loop
return assembly;
}
catch (FileNotFoundException)
{
Logger.WriteLine("failed to load {0}", Logger.LogType.Error, requestedFilePath);
}
}
else
{
try
{
// ugh, hard-coding, but I need to get on with the real programming for now
var refedAssembliesPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.1");
requestedFilePath = Path.Combine(refedAssembliesPath, assemblyNameToLookFor.Name + ".dll");
Logger.WriteLine("looking for {0}...", Logger.LogType.Warning, requestedFilePath);
Assembly assembly = Assembly.LoadFrom(requestedFilePath);
if (assembly != null)
Logger.WriteLine("loaded {0} successfully!", Logger.LogType.Success, requestedFilePath);
// todo: write an else to handle load failure and search various probe paths in a loop
return assembly;
}
catch (FileNotFoundException)
{
Logger.WriteLine("failed to load {0}", Logger.LogType.Error, requestedFilePath);
}
}
}
}
catch (Exception e)
{
Logger.WriteLine("exception {0}", Logger.LogType.Error, e.Message);
}
return null;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.