- /clr (Common Language Runtime Compilation)
- Syntax
- Arguments
- Remarks
- Metadata and Unnamed Classes
- To set this compiler option in the Visual Studio development environment
- Знакомство с Microsoft .NET Framework
- Введение
- Компонент Common Language Runtime
- Исполняемые файлы и метаданные
- Microsoft Intermediate Language
- Just-In-Time Compiler
- Сборки
- Global Assembly Cache
/clr (Common Language Runtime Compilation)
Enables applications and components to use features from the common language runtime (CLR) and enables C++/CLI compilation.
Syntax
Arguments
options
One or more of the following comma-separated arguments.
With no options, /clr creates metadata for the component. The metadata can be consumed by other CLR applications, and enables the component to consume types and data in the metadata of other CLR components. For more information, see Mixed (Native and Managed) Assemblies.
netcore
Available starting in Visual Studio 2019 version 16.4, /clr:netcore creates metadata and code for the component using the latest cross-platform .NET framework, also known as .NET Core. The metadata can be consumed by other .NET Core applications. And, the option enables the component to consume types and data in the metadata of other .NET Core components.
nostdlib
Instructs the compiler to ignore the default \clr directory. The compiler produces errors if you include multiple versions of a DLL, such as System.dll. This option lets you specify the specific framework to use during compilation.
pure
/clr:pure is deprecated. The option is removed in Visual Studio 2017 and later. We recommend that you port code that must be pure MSIL to C#.
safe
/clr:safe is deprecated. The option is removed in Visual Studio 2017 and later. We recommend that you port code that must be safe MSIL to C#.
noAssembly
/clr:noAssembly is deprecated. Use /LN (Create MSIL Module) instead.
Tells the compiler not to insert an assembly manifest into the output file. By default, the noAssembly option isn’t in effect.
A managed program that doesn’t have assembly metadata in the manifest is known as a module. The noAssembly option can be used only to produce a module. If you compile by using /c and /clr:noAssembly , then specify the /NOASSEMBLY option in the linker phase to create a module.
Before Visual Studio 2005, /clr:noAssembly required /LD . /LD is now implied when you specify /clr:noAssembly .
initialAppDomain
initialAppDomain is obsolete. Enables a C++/CLI application to run on version 1 of the CLR. An application that’s compiled by using initialAppDomain shouldn’t be used by an application that uses ASP.NET because it’s not supported in version 1 of the CLR.
Remarks
Managed code is code that can be inspected and managed by the CLR. Managed code can access managed objects. For more information, see /clr Restrictions.
For information about how to develop applications that define and consume managed types in C++, see Component Extensions for Runtime Platforms.
An application compiled by using /clr may or may not contain managed data.
To enable debugging on a managed application, see /ASSEMBLYDEBUG (Add DebuggableAttribute).
Only CLR types are instantiated on the garbage-collected heap. For more information, see Classes and Structs. To compile a function to native code, use the unmanaged pragma. For more information, see managed , unmanaged .
By default, /clr isn’t in effect. When /clr is in effect, /MD is also in effect. For more information, see /MD , /MT , /LD (Use Run-Time Library). /MD ensures that the dynamically linked, multithreaded versions of the runtime routines are selected from the standard header files. Multithreading is required for managed programming because the CLR garbage collector runs finalizers in an auxiliary thread.
If you compile by using /c , you can specify the CLR type of the resulting output file by using the /CLRIMAGETYPE linker option.
/clr implies /EHa , and no other /EH options are supported for /clr . For more information, see /EH (Exception Handling Model).
For information about how to determine the CLR image type of a file, see /CLRHEADER .
All modules passed to a given invocation of the linker must be compiled by using the same run-time library compiler option ( /MD or /LD ).
Use the /ASSEMBLYRESOURCE linker option to embed a resource in an assembly. /DELAYSIGN , /KEYCONTAINER , and /KEYFILE linker options also let you customize how an assembly is created.
When /clr is used, the _MANAGED symbol is defined to be 1. For more information, see Predefined macros.
The global variables in a native object file are initialized first (during DllMain if the executable is a DLL), and then the global variables in the managed section are initialized (before any managed code is run). #pragma init_seg only affects the order of initialization in the managed and unmanaged categories.
Metadata and Unnamed Classes
Unnamed classes appear in metadata under names such as $UnnamedClass$ $ $ , where is a sequential count of the unnamed classes in the compilation. For example, the following code sample generates an unnamed class in metadata.
Use ildasm.exe to view metadata.
To set this compiler option in the Visual Studio development environment
Open the project’s Property Pages dialog box. For details, see Set C++ compiler and build properties in Visual Studio.
Set the Configuration dropdown to All configurations, and set the Platform dropdown to All Platforms.
Select the Configuration Properties > C/C++ > General page.
Modify the Common Language Runtime Support property. Choose OK to save your changes.
In the Visual Studio IDE, the /clr compiler option can be individually set on the Configuration Properties > C/C++ > General page of the Property Pages dialog. However, we recommend you use a CLR template to create your project. It sets all of the properties required for successful creation of a CLR component. Another way to set these properties is to use the Common Language Runtime Support property on the Configuration Properties > Advanced page of the Property Pages dialog. This property sets all the other CLR-related tool options at once.
Знакомство с Microsoft .NET Framework
Microsoft .NET Framework является ключевым компонентом Microsoft .NET и служит платформой для создания, внедрения и выполнения Web-сервисов и приложений. Она предоставляет основанную на стандартах высокопроизводительную многоязычную среду, в которой выполняются различные типы приложений и сервисов.
Введение
В этой и последующих частях статьи мы ознакомимся с Microsoft .NET Framework, с ее основными компонентами — Common Language Runtime (CLR), библиотекой классов и ASP.NET, рассмотрим назначение каждого из них, архитектуру, составные части, а также приведем примеры их использования.
Основные компоненты Microsoft .NET Framework показаны на диаграмме (рис. 1).
Компонент Common Language Runtime, подробно рассматриваемый в данной статье, располагается над сервисами операционной системы, которая в настоящее время является операционной системой Windows, но в дальнейшем таковой может быть практически любая программная платформа. Основное назначение CLR — выполнение приложений, соблюдение всех программных зависимостей, управление памятью, обеспечение безопасности, интеграция с языками программирования и т.п. Среда выполнения обеспечивает множество сервисов, облегчающих создание и внедрение приложений, и существенно улучшает надежность последних.
Разработчики не взаимодействуют с Common Language Runtime напрямую — все сервисы предоставляются унифицированной библиотекой классов, которая располагается над CLR. Эта библиотека содержит более 1000 классов для решения различных программных задач — от взаимодействия с сервисами операционной системы до работы с данными и XML-документами.
Частью указанной библиотеки классов является программная модель для создания Web-приложений, называемая ASP.NET. Она модель содержит классы, компоненты и сервисы, облегчающие создание Web-сервисов и приложений. Отметим, что помимо Web-сервисов и Web-приложений с помощью Microsoft .NET Framework можно создавать и другие типы приложений — консольные приложения, приложения на основе Windows Forms и Windows-сервисы. Но поскольку основной задачей Microsoft .NET Framework является поддержка создания Web-приложений, именно компонент ASP.NET играет наиболее значительную роль в архитектуре Microsoft .NET Framework.
На самом нижнем уровне нашей диаграммы (см. рис. 1) располагаются сервисы операционной системы. Сервисы платформы Windows обеспечивают поддержку таких технологий, как COM+, а также управление транзакциями, очередями сообщений и т.п.
После того как мы рассмотрели основные компоненты Microsoft .NET Framework, перейдем к более подробному изучению важнейшего из них — Common Language Runtime.
Компонент Common Language Runtime
Common Language Runtime обеспечивает среду выполнения .NET-приложений. Среди предоставляемых этой средой функций следует отметить обработку исключительных ситуаций, обеспечение безопасности, средства отладки поддержки версий. Все эти функции доступны из любого языка программирования, соответствующего спецификации Common Language Specification. Microsoft предоставляет три языка программирования, способных использовать CLR, — Visual Basic .NET, Visual C# .NET и Visual C++ With Managed Extensions. Кроме того, ряд третьих фирм работает над .NET-версиями таких языков программирования, как Perl, Python и COBOL.
компилируемый компилятором код для CLR называется управляемым кодом (managed code). Управляемый код пользуется преимуществами среды выполнения и помимо собственно кода содержит метаданные, которые создаются в процессе компиляции и содержат информацию о типах, членах и ссылках, используемых в коде. Метаданные используются средой выполнения:
- для обнаружения классов;
- загрузки классов;
- генерации кода для конкретной платформы;
- обеспечения безопасности.
Среда выполнения также следит за временем жизни объектов. В COM/COM+ с этой целью использовались специальные счетчики (reference counter); в CLR тоже используются счетчики, а удаление объектов из памяти происходит с помощью процесса, называемого сборкой мусора (garbage collection).
Common Language Runtime также задает общую систему типов, используемую всеми языками программирования. Это означает, например, что все языки программирования будут оперировать целочисленными данными или данными с плавающей точкой единого формата и единой длины, а представления строк тоже будут едиными для всех языков программирования. За счет единой системы типов достигается более простая интеграция компонентов и кода, написанных на разных языках программирования. В отличие от COM-технологии, также основанной на наборе стандартных типов, но представляемых в бинарном виде, CLR позволяет выполнять интеграцию кода (который может быть написан на различных языках программирования) в режиме дизайна, а не в режиме выполнения.
После компиляции управляемый код содержит метаданные, описывающие сам компонент, а также компоненты, использовавшиеся для создания кода. Среда выполнения проверяет, доступны ли все необходимые ресурсы. Использование метаданных позволяет отказаться от необходимости хранить информацию о компонентах в реестре. Следовательно, при переносе компонента на другой компьютер нам больше не требуется регистрировать этот компонент (за исключением глобальной сборки — global assembly, которую мы рассмотрим ниже), а удаление компонента сводится к простому удалению содержащей его сборки.
Как видно из приведенного выше функционального описания Common Language Runtime, среда выполнения обеспечивает ряд преимуществ, облегчающих создание, выполнение и внедрение .NET-приложений.
Исполняемые файлы и метаданные
А теперь более подробно остановимся на концепции метаданных. Начнем с того, что создадим примитивную программу на VB.NET. Эта консольная программа выводит строку “Running under .NET” на стандартное устройство вывода и завершает свое выполнение. Код этой программы таков:
Директива Imports указывает на то, что мы используем классы, реализованные в пространстве имен System. Одним из таких классов является класс Console. Метод WriteLine этого класса мы используем для вывода строки на экран. Откомпилируем эту программу (рис. 2) с помощью пакетного компилятора VB.NET:
В результате компиляции мы получаем исполняемый файл CONS.EXE, который является файлом в формате СOFF/PE с дополнительными секциями, содержащими информацию, необходимую для Common Language Runtime. Мы можем убедиться в этом, выполнив команду DUMPBIN:
На листинге 1 приведен фрагмент дампа исполняемого файла (показаны только основные элементы).
Дамп исполняемого файла для .NET начинается с обычных заголовков для MS-DOS и COFF-заголовка, стандартных в отношении всех Windows-программ. Далее мы увидим, что наша программа — это 32-битная Windows-программа (PE32). В секции данных #1 располагаются CLR-заголовок и данные. Отметим, что эта секция имеет атрибуты Code и Execute Read, указывающие загрузчику на то, что в секции содержится код, который будет выполняться средой выполнения.
В заголовке CLR Header мы видим импортируемую функцию _CorExeMain, реализованную в mscoree.dll — основном модуле среды выполнения. Для реализации возможности выполнения .NET-файлов под управлением операционных систем Windows 98, Windows Me и Windows 2000 фирма Microsoft изменила код загрузчика: теперь он распознает CLR-элементы в исполняемых файлах и передает управление CLR через точку входа — _CorExeMain. Функция Main() нашей программы вызывается самой средой выполнения.
После того как мы рассмотрели содержимое исполняемого файла, обратимся к той его секции, где размещаются код и метаданные.
Метаданные содержат описания типов, информацию о версии, ссылки на различные сборки и другую информацию, используемую средой выполнения. В целом метаданные представляют собой высокоуровневый вариант библиотеки типов, известной нам из технологии COM/COM+. Метаданные используются самой средой выполнения, загрузчиком классов, JIT-компилятором, а также различными утилитами. Одна из таких утилит — ILDASM, входящая в состав Microsoft .NET Framework. Данная утилита может использоваться не только для просмотра метаданных, но и для изучения кода на промежуточном языке (IL) в том или ином файле (в файле с расширением .EXE или .DLL, рис. 3).
С помощью ILDASM можно сохранить в текстовом файле дамп нашего исполняемого файла. В результате мы получим более подробное описание заголовка, уже рассмотренного выше, а также дополнительную информацию, используемую CLR. На листинге 2 показан фрагмент дампа, содержащий секции, о которых мы расскажем далее.
Как видно из приведенного фрагмента дампа, в нем содержится вся необходимая информация о нашей программе. Первая инструкция на языке IL (.assembly) содержит указание на внешнюю сборку — mscorlib. Следующая инструкция также содержит ссылку на внешнюю сборку. Это будет набор классов для поддержки программ на Visual Basic, реализованный в сборке Microsoft, — Microsoft.VisualBasic. Далее идет инструкция, описывающая нашу программу (в виде сборки) и собственно код. Отметим, что нашей программе присвоен уникальный глобальный идентификатор (GUID).
Обратите внимание на то, что наш класс Cons является наследником класса System.Object — базового класса библиотеки классов .NET Framework. Как видно из кода нашей программы, мы не указывали этого наследования напрямую — этот факт автоматически добавлен компилятором. Класс имеет два метода. Метод Main() является статическим методом, который реализован в коде нашей программы. Второй метод — конструктор ctor(), автоматически добавленный компилятором. Конструктор позволяет другим приложениям создавать экземпляры нашего приложения.
Microsoft Intermediate Language
В вышеприведенном дампе намеренно пропущена важная часть — код на языке IL. Когда мы компилируем наш код, результатом этого процесса становится не код на языке, понятном конкретной платформе, а код на промежуточном языке, называемом Microsoft Intermediate Language (MSIL), который представляет собой набор инструкций, не зависящих от конкретного процессора. Ниже приведен IL-код нашего метода Main():
Если опустить пролог, наш код занимает 8 ячеек стека и состоит из 3 инструкций. Первая инструкция — ldstr — загружает строку в стек. Инструкция call вызывает указанный метод из сборки и передает ей параметр. Выполнение завершается инструкцией ret. Несмотря на то что язык IL является достаточно простым, писать программы на нем не имеет особого смысла, однако общее понимание инструкций может помочь вам разобраться в логике работы программ, а также ядра .NET Framework. (Дополнительную информацию можно получить из спецификации Common Language Infrastructure, Partition 3. CIL Instruction Set, доступной на Web-сайте фирмы Microsoft.)
Just-In-Time Compiler
Программа на языке IL остается неизменной до тех пор, пока мы не вызовем на выполнение тот файл, в котором она хранится. В момент, когда управление передается среде исполнения, IL-код преобразуется в код для конкретной платформы. Эту задачу выполняет компилятор, называемый Just-In-Time Compiler (JIT). Теоретически только указанный компилятор является компонентом .NET, зависящим от конкретной платформы. Однако на самом деле от платформы также зависят и значительная часть библиотеки классов, и ряд других компонентов, например следующие компоненты платформы .NET:
- mscorlib
- System
- System.Design
- System.Drawing
- System.Windows.Forms
Тем не менее существует реальная возможность наличия .NET для не-Windows-платформ, и, возможно, уже в ближайшее время мы увидим реализации .NET для Linux.
Следует также обратить внимание на то, что из .NET-программ можно напрямую вызывать функции Windows API. В этом случае наши программы будут привязаны к конкретной платформе и перенести их без изменения кода будет невозможно.
Интересно, что JIT-компилятор не выполняет компиляцию всего IL-кода при первом обращении к программе. Вместо этого каждый метод компилируется при первом обращении к нему, и, таким образом, неиспользуемый код не компилируется. Откомпилированный код хранится в памяти, а последующие обращения к программе выполняют уже откомпилированный код. Microsoft предоставляет специальный компилятор CLR Native Image Generator (NGEN), который выполняет компиляцию всего IL-кода и сохраняет результат на диске (рис. 4).
На рис. 5 представлены пояснения к циклу приложений под управлением .NET.
Сборки
При рассказе о Common Language Runtime мы неоднократно использовали термин «сборка» (assembly). Сборка представляет собой коллекцию из одного или более файлов. Часто эти файлы содержат код, но в состав сборки могут также входить и графические изображения, и ресурсы, и другие бинарные данные, ассоциированные с кодом. Такие сборки называются статическими сборками, поскольку они хранятся на диске. Динамические сборки создаются во время выполнения программы и на диске обычно не сохраняются.
Сборки являются минимальной единицей внедрения, контроля версий, повторного использования и системы безопасности. Каждая сборка содержит специальные метаданные, называемые манифестом. В манифесте содержится информация о классах, типах и ссылках на другие сборки. Сборки могут содержать более одного класса — так, библиотека классов .NET состоит из десятка сборок, каждая из которых содержит несколько десятков классов.
На рис. 6 показаны сборки, состоящие из одного и нескольких файлов.
Для просмотра манифеста сборки можно использовать утилиту ILDASM, о которой было сказано выше. На рис. 7, например, показан манифест для нашей тестовой программы.
Как видно из рисунка, манифест содержит следующую информацию о сборке:
- имя сборки;
- версию;
- файлы в данной сборке;
- сборки, используемые данной сборкой.
Помимо стандартных полей в манифесте могут присутствовать дополнительные поля, задаваемые программистами.
Использование сборок и номеров версии может решить проблему совместимости между различными версиями DLL, известную как DLL Hell. Теперь .NET-программы ищут сборки в локальном каталоге, что позволяет одновременно использовать несколько версий одной и той же сборки без всяких проблем. (Дополнительную информацию можно получить, прочитав спецификацию Common Language Infrastructure, Partition 2. Metadata Definition and Semantics, доступную на Web-сайте фирмы Microsoft.)
Global Assembly Cache
Хотя компоненты .NET не требуют регистрации, необходимо некоторое хранилище для сборок, используемых более чем одним приложением. Common Language Runtime поддерживает два таких хранилища — Download Cache и Global Assembly Cache (GAC). Сборка, используемая более чем одним приложением, помещается в GAC. Если сборка не обнаружена в локальном каталоге или в GAC, то Common Language Runtime пытается прочитать файл конфигурации. В этом файле можно указать местонахождение сборки (code base), и тогда среда выполнения загрузит сборку и сохранит ее в Download Cache. Для просмотра содержимого GAC можно использовать утилиту GACUtil.
Утилита GACUtil также может использоваться для просмотра содержимого Download Cache, для установки и удаления сборок. Отметим, что GAC представляет собой специализированный каталог, располагаемый в C:\ WINNT\assembly (рис. 8).
В следующей части статьи мы рассмотрим последний компонент Common Language Runtime — Common Type System. (Дополнительная информация о Common Language Runtime имеется в спецификации Common Language Infrastructure, Partition 1. Concepts and Architecture на Web-сайте фирмы Microsoft.)