在调用程序集中的任何方法之前,CLR 最早调用的入口点是什么?

发布于 2024-09-12 04:19:40 字数 414 浏览 8 评论 0原文

在过去的几年里,我偶尔想知道 .NET 世界中有什么与(臭名昭著的)DLL_PROCESS_ATTACH 等效的东西。我拥有的任何文档都说,稍微简化一下,类的最早入口点是静态构造函数(cctor),但你不能影响 被调用时,也不能定义一个保证在任何其他 cctor 或字段初始值设定项之前调用的 cctor,hack,它可能不会如果从未使用过该类,甚至根本不会被调用。

因此,如果您想保证在调用程序集的任何方法之前初始化某些内容,并且您不想向程序集中的每个类添加一个 cctor,那么您可以采取什么方法呢?或者 .NET 中是否有一个我多年来一直怀念的简单的托管解决方案?

In the past years I've occasionally been wondering what equivalent of the (in)famous DLL_PROCESS_ATTACH was available in the .NET world. Any documentation I have says, slightly simplified, that the earliest entry point to a class is the static constructor (cctor), but you cannot influence when it is called, nor can you define one cctor that's guaranteed to be called prior to any other cctor or field initializer, hack, it may not even be called at all if the class is never used.

So, if you want to guarantee something's initialized before any method of your assembly is called and you don't want to have to add a cctor to every class in your assembly, what approach can you take? Or is there an easy, managed solution in .NET that I have missed all these years?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

阿楠 2024-09-19 04:19:40

我通常不会回答自己的问题,但同时我确实找到了一个以前没有出现过的答案,所以我开始了。

经过一番研究,我发生在 Microsoft 的这篇文章,其中解释了DllMain 中混合托管和非托管代码的问题以及解决方案,该解决方案随 CLI 的第二个版本而来,模块初始值设定项。引用:

此初始化程序在
本机 DllMain(换句话说,
装载机锁之外)但在任何之前
运行托管代码或托管数据
从该模块访问。这
模块 .cctor 的语义是
与 .cctors 类非常相似
并在 ECMA C# 中定义
通用语言基础设施
标准。

虽然我无法在当前 ECMA 规范中找到术语模块初始化器,但它在逻辑上遵循类型初始化器和全局code> 特殊类(参见第 22.26 节 MethodDef,子点 40)。此功能是在 .NET 1.1 之后(即从 2.0 开始)实现的。另请参阅此半官方说明< /a>.

这个问题与 C# 无关,而是因为它是 .NET 的通用语言:C# 不知道全局方法,并且您无法创建 ,更不用说它的 cctor 。然而,Einar Egilsson 认识到了这一明显的缺陷并创建了 InjectModuleInitializer.exe,它允许您作为 Visual Studio 的后/编译步骤来执行此操作。在 C++.NET 中,使用此方法很简单,建议代替 DllMain 进行实践。另请参阅Ben Voigt 的回答 (不是公认的答案)和这个 so yoyoyoyosef 的回答

简而言之,模块初始值设定项是在加载模块之后(不一定是在加载程序集时!)、调用任何类或实例方法之前调用的第一个方法。它不接受参数,不返回值,但可以在其主体中包含任何托管代码。

I'd normally not answer my own question, but meanwhile I did find an answer that hasn't come up here before, so here I go.

After some research, I happened on this post by Microsoft, which explains the problems of mixing managed and unmanaged code inside DllMain and the solution, which came about with the 2nd version of the CLI, module initializers. Quote:

This initializer runs just after the
native DllMain (in other words,
outside of loader lock) but before any
managed code is run or managed data is
accessed from that module. The
semantics of the module .cctor are
very similar to those of class .cctors
and are defined in the ECMA C# and
Common Language Infrastructure
Standards.

While I wasn't able to find the term module initializer inside the current ECMA specification, it follows logically from type initializer and the global <Module> special class (see section 22.26 on MethodDef, sub-point 40). This feature was implemented after .NET 1.1 (i.e., from 2.0 onwards). See also this semi-official description.

This question wasn't about C#, but because it is the lingua franca of .NET: C# doesn't know global methods, and you can't create a <Module>, let alone its cctor. However, Einar Egilsson has recognized this apparent deficiency and created InjectModuleInitializer.exe that allows you to do this as a post/compile step from Visual Studio. In C++.NET, using this method is trivial and recommended practice in place of DllMain. See also this SO answer by Ben Voigt (not the accepted answer) and this SO answer by yoyoyoyosef.

In short, the module initializer is the first method that is called after loading the module (not necessarily when loading assembly!) and before calling any class or instance method. It takes no parameters, returns no value, but can contain any managed code in its body.

撩动你心 2024-09-19 04:19:40

实际上,首先调用 cctor 并不完全正确。如果您有由静态方法初始化的静态字段,则将调用该静态方法。

看看这段代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CallSequence
{
    internal class Test
    {
        internal Test()
        {
            Console.WriteLine("non-static constructor");
        }

        static Test()
        {
            Console.WriteLine("static constructor");
        }

        static int myField = InitMyField();

        static int InitMyField()
        {
            Console.WriteLine("static method : (InitMyField)");
            return 0;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Test t = new Test();
        }
    }
}

编辑:还可以考虑使用工厂模式 这将帮助您在返回创建的对象之前完成所有需要的初始化。

Actually it is not exactly true that cctor is called first. If you have static field initialized by static method that static method will be called.

Have a look at this code :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CallSequence
{
    internal class Test
    {
        internal Test()
        {
            Console.WriteLine("non-static constructor");
        }

        static Test()
        {
            Console.WriteLine("static constructor");
        }

        static int myField = InitMyField();

        static int InitMyField()
        {
            Console.WriteLine("static method : (InitMyField)");
            return 0;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Test t = new Test();
        }
    }
}

EDIT : Also consider using Factory pattern which will help you do all the needed initialization before returning created object.

超可爱的懒熊 2024-09-19 04:19:40

这是设计使然:它最大限度地减少静态构造函数之间的耦合。您知道您的 cctor 将在您的类中的任何内容初始化之前以及您的类使用的任何类的 cctor 之后被调用。但与同一应用程序中不相关的类相比,无法保证它何时运行。

如果您想确保某些代码在入口点之前运行,请考虑为主应用程序编写一个包装器。一种简单的方法是将其放入单独的可执行文件中。

一种更独立的方法可能是:

  1. 以正确的顺序运行所需的任何启动代码。不要引用程序集中不应初始化的任何类型。
  2. 创建您自己的应用程序域
  3. 在第二个应用程序域中运行真正的入口点

This is by design: it minimises coupling between static constructors. You know that your cctor will be invoked before anything in your class initializes, and after the cctors of any classes used by your class. But there's no guarantee on when it will run compared to unrelated classes in the same application.

If you want to make sure some code of yours runs before the entry point, consider writing a wrapper for the main application. A straightforward way would be to put it in a separate executable.

A more self-contained way to do this might be to:

  1. Run whatever startup code is needed, in the right order. Don't reference any types in assemblies that shouldn't get initialized.
  2. Create your own app domain
  3. Run the real entry point within this second app domain
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文