-2

我想在我的应用程序运行时修改 Assembly.Location 属性。我正在从资源中加载 .NET 程序集,并且加载的程序集(从内存中加载)使该字段为空,并且它分解了应用程序功能。

我无法控制他们的源代码。我知道如何在我自己的代码中解决这个错误,但事实并非如此。当他们使用 Assembly.Location 并获得一个空字符串时,问题就开始了。

http://msdn.microsoft.com/en-us/library/system.reflection.assembly.location.aspx

它是只读的。有什么低级的方法吗?有任何想法吗?

我的应用程序是嵌入在资源中的那些应用程序的加载器,因此它不依赖于它们。尝试从内存中加载任何程序集并检查这些已加载程序集的 Assembly.Location 字段,它将为空白。它们没有位置,因为它们是从内存中加载的,但我仍然想通过 .NET 内部修改或任何其他方法来更改它。

压缩的程序集无法解压缩到磁盘。如果你知道如何实现它,我不介意重大问题的危险。

4

4 回答 4

8

将所需的程序集资源从其容器程序集中复制到容器程序集的目录或子目录中。然后将复制的程序集加载到您的应用程序中。这比尝试更改私有字段(或函数调用的结果)要容易得多。

Assembly 类公开了一个公共接口——它们应该使用它的方式。尝试修改类的内部工作可能会给您带来重大问题,假设它甚至是可能的。一些未来的版本,甚至只是定期更新,可能会改变类的内部工作并破坏你的代码。您也无法预测该类的其他部分依赖于该字段。改变其价值可能会产生意想不到的后果。您提议从其定义的行为更改类,这可能会导致其他程序集或程序员进一步感到困惑和沮丧。这就是为什么这个字段被实现为只读的,这就是为什么 .NET 没有提供简单的方法来修改只读值。


更新

这是 Location 属性的源代码:

[System.Security.SecurityCritical]  // auto-generated
[ResourceExposure(ResourceScope.Machine)] 
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity] 
private static extern void GetLocation(RuntimeAssembly assembly,
    StringHandleOnStack retString); 

public override String Location 
{
    [System.Security.SecuritySafeCritical]  // auto-generated
    [ResourceExposure(ResourceScope.Machine)]
    [ResourceConsumption(ResourceScope.Machine)] 
    get {
        String location = null; 

        GetLocation(GetNativeHandle(), 
                    JitHelpers.GetStringHandleOnStack(ref location));

        if (location != null)
            new FileIOPermission( FileIOPermissionAccess.PathDiscovery, location ).Demand();

        return location; 
    }
} 

请注意,此代码实际上位于名为 RuntimeAssembly 的未记录类中,该类定义为internal在 Assembly 类中。您可以在此处查看完整的源代码:http:
//www.dotnetframework.org/default.aspx/4@0/4@0/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/clr/src/BCL/System/Reflection /Assembly@cs/1305376/Assembly@cs

如您所见,这里没有要修改的支持字段。无法根据需要覆盖 Location 属性(无需重写 Windows 操作系统)。

并且...以防万一您渴望重写 GetLocation 函数,您可能会对这个 Q/A 感兴趣:
什么是 [DllImport("QCall")]?
(不用说,在这一点上,你是靠自己的。)

于 2013-06-24T14:54:05.333 回答
4

尽管@Cyborgx37 是正确的——您不能更改现有程序集的位置,但还有其他方法可以解决此问题。

与其尝试设置已加载程序集的位置,不如使用类库Mono.Cecil来使用此属性重写程序集。

假设它不是核心 .NET 框架程序集之一,Assembly.Location用您自己可以注入的静态属性替换所有访问非常容易。

在编写自己的加密程序集加载器时,我不得不做很多这样的事情,并且非常成功。

编辑

如果你真的下定决心要这样做,这两篇文章应该让你开始了解如何在运行时重写现有方法。但是有多个警告:这根本不适用于 NGEN 或 GAC,并且您必须解决异步/等待/可枚举/异常处理的多个难题。使用风险自负。

MSIL 注入:在运行时重写非动态方法

CLR 注入:运行时方法替换器

我强烈反对它,因为这些解决方案太脆弱而无法在生产代码中使用,作者提到了这一点:

我们需要记住,我们正在以非预期的方式直接操作 CLR 内存。此代码可能不适用于较新版本的 .NET 框架。这是在 Vista x86 上使用 .NET 3.5 测试的,可能无法在您的机器上运行。

微软不支持任何这种骇人听闻的东西。如果可能,您应该寻找另一种方法来实现您的目标。

于 2013-06-27T14:11:46.330 回答
4

简单地说,你不能修改属性。在这种情况下,它会通知您从磁盘加载程序集的位置。相反,您需要移动程序集并确保您的应用程序从新位置加载它。

于 2013-06-24T14:03:24.403 回答
1

答案稍长;

这会有点困难(我相信你已经在怀疑了),它很难的原因是Location getter 返回的值没有存储在支持字段中,例如

  private readonly string location;

相反,它是使用程序集的本机句柄来获取的(或者实际上因为该类是抽象的,RuntimeAssembly是您从内存中加载内容时获得的子类),然后使用外部dll调用来读取位置。

它看起来像这样(简化);

[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern void GetLocation(RuntimeAssembly assembly, StringHandleOnStack retString);

public override String Location {
  get {
    String location = null;
    GetLocation(GetNativeHandle(), JitHelpers.GetStringHandleOnStack(ref location));

    return location;
  }
}

更改要求 this 为非空的代码比向其中注入值更容易。

--L劳利特--

于 2013-07-03T00:13:07.763 回答