装载程序时初始化库

27

我有一个 .net 库的 dll 文件,它像一个函数库一样。其中有许多静态类型和静态方法。

我需要运行一些初始化代码,使库准备好可用。

当程序集被加载时是否有一种方法可以确保自动运行特定的方法?类似于 AppDomain.AssemblyLoad,但是从程序集本身调用。我想也许有一些 AssemblyAttribute 可以使用?

目前我将这个初始化代码放在了一个静态构造函数中,但由于这是一个具有许多入口点的库,所以无法保证一定会使用此特定类型。

谢谢!

4个回答

20

是的,有一种方法可以实现。可以使用由Einar Egilsson开发的优秀小工具InjectModuleInitializer

在构建后执行此可执行文件以创建一个小的.cctor函数(模块初始化函数),它调用您的静态无参函数。如果编译器能够让我们创建.cctor(),那会很好,幸运的是我们很少需要这个功能。

然而,这并不是完整的DllMain替代品。CLR仅在调用程序集中的任何方法之前调用此.cctor函数,而不是在程序集加载时调用。因此,如果您需要在程序集加载时执行某些操作,则需要直接调用某个方法或者使用我在https://dev59.com/fWsy5IYBdhLWcg3w9Srk#9745422中详细介绍的技巧。


14
以下解决方案仅适用于您控制主要执行程序集的情况,即不适用于专门用于分发的独立库。
我曾经遇到类似的问题,并通过创建一个面向程序集的属性'InitializeOnLoad'来解决它,其中包含一个类型参数。然后,在主要可执行文件中,我添加了一个微不足道的AppDomain.AssemblyLoaded处理程序,该处理程序扫描新加载的程序集以查找上述属性,并在其上调用System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor()。
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class InitializeOnLoadAttribute : Attribute
{
    Type type;

    public InitializeOnLoadAttribute(Type type) { this.type = type; }

    public Type Type { get { return type; } }
}

// somewhere very early in main exe initialization
AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(AssemblyInitializer);

static void AssemblyInitializer(object sender, AssemblyLoadEventArgs args)
{
    // force static constructors in types specified by InitializeOnLoad
    foreach (InitializeOnLoadAttribute attr in args.LoadedAssembly.GetCustomAttributes(typeof(InitializeOnLoadAttribute), false))
        System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(attr.Type.TypeHandle);
}

另外,如果您担心在挂钩AssemblyLoad事件之前已经加载程序集,可以通过运行AppDomain.GetAssemblies()并为它们调用“初始化器”来解决该问题。


7
为什么需要在使用数据之前将所有数据加载,而不是在需要它的第一个类型被使用时才加载?
我认为没有办法在程序集内部强制运行方法以进行装配。您可以在每个类型中放置静态构造函数,但老实说,我认为最好有一个单独的类型来表示该数据并提供对其的访问,并仅在该类型上放置静态构造函数。(如果您有可独立使用的单独数据部分,则可以为它们创建单独的类型。)

1
我没有提到加载任何数据。我只是想确保库使用特定的DateTime转换器而不是默认的转换器。(请参阅https://dev59.com/eEbRa4cB1Zd3GeqPy0gP)。 - sixtowns
2
我只想在程序集加载时运行此代码一次: TypeDescriptor.AddAttributes(typeof(DateTime),new TypeConverterAttribute(typeof(DateTimeConverterEx))); - sixtowns
我认为这可能是情况。问题标题有误导性,因为它说“数据” - 我已经更改了它。干杯。 - sixtowns
6
实际上,有些人可能希望在程序集加载时运行代码,例如如果涉及到昂贵的初始化过程,最好在一个地方解决。当然,可以通过手动查找程序集中的某些初始化例程并执行来解决这个问题,但是是否框架提供了内置方法来实现这一点是值得探讨的。 - EFraim
2
@JonSkeet “为什么需要在使用数据之前加载所有数据?” 假设有一个带有某种类型 Vector3 的第三方库。还有一个操作注册表的库,您可以为任何类型注册操作(例如,添加或转换)。现在有一个实现和使用向量操作的几何库。几何库想要在操作注册表中注册向量操作。此初始化应在程序集加载时执行。这是可能的-只需将静态构造函数添加到 <Module> 类即可。 - Ark-kun
显示剩余3条评论

2

只需要在 <Module> 类中添加一个静态构造函数即可实现。但是我不知道如何在没有 IL 修改的情况下完成此操作。


5
Fody. 只需运行 Install-Package ModuleInit.Fody。 - some_engineer

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