.Net:当程序集被加载时运行代码

45

在不进行加载代码中特定操作的情况下,在程序集被加载时运行某些代码是否可能?我需要的是像类型上的静态构造函数一样的东西。

例如:

程序集 A 不知道程序集 B,但是 B 知道 A。如果已经加载了程序集 B,则在运行时(显式或引用)要执行一个代码片段(静态方法或属性),该代码片段调用程序集 A 中的一个方法以便程序集 A 可以了解有关 B 的某些信息。

这个问题的根本原因是当序列化 A 中包含未在编译时作为接口使用的 B 类型的类型时遇到了未知的类型。

6个回答

47

2
@RichardCollette,它仍在进行黑客攻击,但Fody只是将黑客攻击自动化到构建过程中,使其易于完成。 - Scott Chamberlain

7
你可以在 .Net 中使用静态构造函数,但不幸的是它们并不能实现你想要的功能。静态构造函数只有在类型被使用之前才会执行。详情请参见 http://msdn.microsoft.com/en-us/library/k9x6w0hc(VS.80).aspx
你可以通过订阅 AppDomain 的 AssemblyLoad 事件来实现一些功能。详情请参见 http://msdn.microsoft.com/en-us/library/system.appdomain.assemblyload.aspx
在事件处理程序中,你可以对新加载的程序集进行反射,并执行任何你想要的代码。

不幸的是,由于只有30个程序集中的一个需要进行此注册,因此AssemblyLoad可能有点过度杀伤力。 - Robert Wagner

7
有3种方法可以初始化.NET程序集:
  1. 在你的程序集中编写一个静态函数Init()或Main()以进行初始化,并通过反射从加载该程序集的C#代码中调用此函数。
  2. 编写托管C++程序集,将您的代码放入DllMain()中。请注意,您的代码将在加载器锁定期间执行,其中禁止进行多项操作(例如加载其他DLL等)。但是,您可以启动一个新线程来执行任何初始化操作。(关于LoaderLock: https://msdn.microsoft.com/en-us/library/ms173266.aspx) (关于DllMain: C# to C++/CLI to C DLL System.IO.FileNotFoundException)
  3. 编译纯C#程序集并修改已编译的DLL以添加模块初始化器代码,如这里所述: http://einaregilsson.com/module-initializers-in-csharp/ 这种方法的缺点是,在将程序集加载到进程时不会立即调用初始化函数,但是它会在首次访问程序集中的任何其他内容之前被调用。

可以说不满足“在加载代码中没有做任何特定操作”的条件? - user585968
唯一不修改加载代码的方法是选择2.)。 - Elmue
是的,我相当喜欢你的第二个选项。 - user585968

6

(编辑 - 适用于C#;有关C ++方法,请参见此答案)

基本上,不行:你不能这样做。这将是一个巨大的攻击面,是不允许的。您可能希望在一些B类型上放置静态构造函数以确保执行初始化代码,但仅限于此...


1
似乎很容易在其中隐藏代码,而没有任何知情的代码调用 - 很难追踪等。更不用说应该由哪个线程运行它的问题了...也许我无法恰当地表达它,但对我来说听起来不明智。但这是无意义的,因为你无论如何都做不到 ;-p - Marc Gravell
你知道有没有关于这个的任何信息(模块初始化器不在Load时运行,而是在静态构造函数之前),所以如果代码可以在Load本身期间运行,那就很有趣。如果你想检查是否能够回答这个非常接近的问题- https://dev59.com/tmAf5IYBdhLWcg3wXhyz#24719526?noredirect=1#comment38423553_24719526 - Alexei Levenkov

3

您可能需要重新审视您的序列化方法以减轻此问题。如果您使用 ISerializableSerializableAttribute 属性进行序列化,您可以使序列化图在必要时加载程序集 B,而无需程序集 A 明确知道程序集 B 的存在。


我该如何使用这种方法识别并加载程序集B? - Robert Wagner
如果您使用BinaryFormatter进行序列化,那么在反序列化期间,序列化图中实现ISerializable接口的任何类型都将调用其构造函数,您可以在其中调用所需的任何代码。因为在序列化时,序列化会查询该接口... - Jeff Yates
不管 A 是否只通过接口引用 B 的类型,它们都会被正确地序列化。 - Jeff Yates
我们正在使用WCF的XML序列化。 - Robert Wagner
如果您采用此方法,那么您可能会彻底放弃。有一个 SoapFormatter 将 ISerializable 类型持久化到 SOAP XML 中,也有 XmlFormatter 在线上可用(尽管我不知道它们的成功率如何)。 - Jeff Yates

0
使用混合程序集,您可以在程序集加载时运行DllMain。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接