Поделиться через


Практическое руководство. Переход на /clr

В этой теме рассматриваются проблемы, возникающие при компиляции машинного кода с параметром /clr (дополнительные сведения см. в описании /clr (компиляция CLR))./clr позволяет модулям Visual C++ вызывать и быть вызванными из сборок .NET, при этом сохраняя совместимость с неуправляемыми модулями.Дополнительные сведения о преимуществах компиляции с использованием параметра /clr см. в разделах Смешанные (собственные и управляемые) сборки и Взаимодействие исходного кода и платформы.NET.

Известные проблемы, возникающие при компиляции проектов библиотек с использованием параметра /clr

В Visual Studio имеется несколько известных проблем, возникающих при компиляции проектов библиотек с использованием параметра /clr:

  • Код может запрашивать типы во время выполнения с помощью CRuntimeClass::FromName.Однако если тип является библиотекой DLL MSIL (скомпилированной с использованием параметра /clr), вызов CRuntimeClass::FromName может привести к ошибке, если он выполняется перед запуском статических конструкторов в управляемой DLL (этой проблемы не возникает, если вызов FromName производится после выполнения кода в управляемой DLL).Для решения этой проблемы можно принудительно создать управляемый статический конструктор, определив функцию в управляемой DLL, экспортировав ее и вызвав из приложения машинного кода MFC.Примеры.

    // Extension DLL Header file:
    __declspec( dllexport ) void EnsureManagedInitialization () {
       // managed code that won't be optimized away
       System::GC::KeepAlive(System::Int32::MaxValue);
    }
    

Компиляция в Visual C++

Перед применением параметра /clr к любому модулю проекта необходимо предварительно скомпилировать и скомпоновать собственный проект с помощью Visual Studio 2010.

Описанная ниже процедура, выполненная в приведенной очередности, обеспечивает простейший способ компиляции с использованием параметра /clr.По завершении каждого этапа необходимо компилировать и запускать проект.

ms173265.collapse_all(ru-ru,VS.110).gifВерсии ранее Visual C++ 2003

При обновлении с версий ранее Visual C++ 2003 до Visual Studio 2010 могут возникнуть ошибки компилятора, связанные с расширенным соответствием стандарту C++ в Visual C++ 2003.

ms173265.collapse_all(ru-ru,VS.110).gifОбновление с версии Visual C++ 2003

Проекты, построенные ранее с помощью Visual C++ 2003, также должны быть сначала скомпилированы без использования параметра /clr, так как в Visual Studio теперь соблюдается более строгое соответствие стандартам ANSI/ISO и введен ряд изменений.Изменение, которое может потребовать наибольшего внимания, это Средства безопасности в CRT.Код, использующий библиотеку CRT, скорее всего, будет вызывать предупреждения об устаревании.Эти предупреждения можно отключить, однако более предпочтительным является переход на Улучшенные версии Безопасность- функций CRT, поскольку они обеспечивают более высокий уровень безопасности и могут помочь выявить уязвимости в коде.

ms173265.collapse_all(ru-ru,VS.110).gifОбновление с управляемых расширений для C++

Проекты, построенные с помощью Visual C++ .NET или Visual C++ 2003, в которых использовались управляемые расширения для C++, могут потребовать внесения по крайней мере одного изменения в параметры проекта, поскольку эти расширения не являются устаревшими.Поэтому код, написанный с использованием управляемых расширений для C++, не может быть скомпилирован с параметром /clr.Взамен рекомендуется использовать /clr:oldSyntax.

Преобразование кода на языке C в C++

Хотя возможна компиляция Visual Studio в C-файлы, необходимо преобразовать их в C++ для компиляции /clr.Реальное имя файла менять не обязательно, можно воспользоваться параметром /Tp (см. раздел Параметры /Tc, /Tp, /TC, /TP (определение типа исходного файла)). Обратите внимание, что хотя для использования параметра /clr требуются файлы исходного кода на языке C++, для применения объектно-ориентированных парадигм нет необходимости в повторной оптимизации кода.

При компиляции кода на языке C в виде файлов C++ скорее всего потребуются изменения.В C++ действуют очень строгие правила безопасности типов, поэтому преобразования типов должны осуществляться путем явного приведения.Например, функция malloc возвращает указатель типа void, однако ее можно присвоить указателю на любой тип в языке C путем приведения:

int* a = malloc(sizeof(int));   // C code
int* b = (int*)malloc(sizeof(int));   // C++ equivalent

Указатели на функции в C++ также строго типобезопасны, поэтому следующий код на языке C потребует изменения.В C++ оптимальным способом является создание typedef, определяющего тип указателя на функцию, с последующим использованием этого типа для приведения указателей на функции:

NewFunc1 = GetProcAddress( hLib, "Func1" );   // C code
typedef int(*MYPROC)(int);   // C++ equivalent
NewFunc2 = (MYPROC)GetProcAddress( hLib, "Func2" );

В C++ перед ссылкой на функцию или ее вызовом необходимо также создание прототипа функции или ее полное определение.

Имена идентификаторов, совпадающие с ключевыми словами в C++ (например, virtual, new, delete, bool, true, false и т. д.), должны быть изменены.Как правило, это можно осуществить путем обычной операции поиска и замены.

Наконец, вызовы COM в языке C требуют явного использования v-таблицы и указателя this, в то время как в C++ это требование отсутствует.

COMObj1->lpVtbl->Method(COMObj, args);  // C code
COMObj2->Method(args);  // C++ equivalent

Изменение параметров проекта

После компиляции проекта и его запуска в Visual Studio 2010 следует создать новые конфигурации проекта для /clr вместо изменения конфигурации по умолчанию.Параметр /clr несовместим с некоторыми параметрами компилятора, и создание отдельных конфигураций позволяет построить проект как собственный или управляемый.При выборе параметра /clr в диалоговом окне страниц свойств параметры проекта, несовместимые с /clr, станут недоступны (недоступные параметры не включаются автоматически при последующей отмене выбора параметра /clr).

ms173265.collapse_all(ru-ru,VS.110).gifСоздание новой конфигурации проекта

Для создания конфигурации проекта на основе текущих параметров проекта можно воспользоваться параметром Копировать параметры из в Диалоговое окно "Создание конфигурации проекта".Эту операцию необходимо повторить отдельно для конфигурации отладки и конфигурации выпуска.Последующие изменения могут применяться только к конфигурациям, связанным с /clr, что позволяет сохранять исходные конфигурации проекта неизменными.

Проекты, для которых используются настраиваемые правила построения, могут потребовать особого внимания.

На данном этапе используются различные реализации для проектов, в которых применяются файлы makefile.В этом случае можно настроить отдельный путь построения или создать особую версию для компиляции с параметром /clr из копии оригинала.

ms173265.collapse_all(ru-ru,VS.110).gifИзменение параметров проекта

Чтобы установить параметр /clr в среде разработки, следуйте инструкциям, приведенным в разделе /clr (компиляция CLR).Как было сказано ранее, на данном этапе конфликтующие параметры проекта будут отключены.

ПримечаниеПримечание

При обновлении управляемой библиотеки или проекта веб-сервиса из Visual C++ 2003, параметр компилятора /Zl будет добавлен на страницу свойств Командная строка.Это приведет к ошибке LNK2001.Для решения проблемы удалите параметр /Zl на странице свойств Командная строка.Дополнительные сведения см. в разделах /Zl (Опущенное по умолчанию имя библиотеки) и Открытие свойств страниц проекта.Либо добавьте файлы msvcrt.lib и msvcmrt.lib в свойство Дополнительные зависимости компоновщика.

Для проектов, построенных с использованием файлов makefile, несовместимые параметры компилятора должны быть отключены вручную при добавлении параметра /clr.Сведения о параметрах компилятора, несовместимых с параметром /clr, см. в разделе Ограничения /clr.

ms173265.collapse_all(ru-ru,VS.110).gifПредкомпилированные заголовки

Параметр /clr поддерживает использование предкомпилированных заголовков.Однако если параметр /clr используется при компиляции только некоторых CPP-файлов (в то время как остальные компилируются как машинные), потребуются некоторые изменения, так как предкомпилированные заголовки, созданные с параметром /clr, несовместимы с заголовками, созданными без этого параметра.Эта несовместимость объясняется тем, что параметр /clr создает и требует использования метаданных.Поэтому модули, скомпилированные с параметром /clr, не могут использовать предкомпилированные заголовки, не содержащие метаданных, а модули, скомпилированные без параметра /clr, не могут использовать файлы предкомпилированных заголовков, содержащие метаданные.

Наиболее простым способом компиляции проекта, некоторые модули которого скомпилированы с использованием параметра /clr, является полное отключение предкомпилированных заголовков.(В диалоговом окне "Страницы свойств" проекта откройте узел "C/C++" и выберите категорию "Предварительно скомпилированные заголовки".Далее измените свойство "Создавать или использовать предварительно скомпилированный заголовочный файл" на "Не использовать предварительно скомпилированные заголовки".)

Однако использование предкомпилированных заголовков обеспечивает гораздо более высокую скорость компиляции, особенно в случае больших проектов, поэтому отключение этой функции является нежелательным.В этом случае лучше настроить файлы, использующие и не использующие параметр /clr, на использование разных предкомпилированных заголовков.Это можно сделать за один прием, выбрав в обозревателе решений несколько модулей, подлежащих компиляции с использованием параметра /clr, щелкнув правой кнопкой мыши группу и выбрав в контекстном меню пункт "Свойства".Затем измените свойства "Создаваемый или используемый PCH-файл" и "Предварительно скомпилированный заголовочный файл", указав разные имена заголовочных файлов и PCH-файлов соответственно.

Устранение ошибок

Компиляция с использованием параметра /clr может приводить к возникновению ошибок компилятора, компоновщика или ошибок времени выполнения.В этом подразделе рассматриваются наиболее распространенные проблемы.

ms173265.collapse_all(ru-ru,VS.110).gifСлияние метаданных

Различия в версиях типов данных могут приводить к сбоям компоновщика, поскольку метаданные, созданные для двух типов данных, не соответствуют друг другу.Обычно это происходит, если члены типа определены условно, однако условия определения неодинаковы для всех CPP-файлов, использующих данный тип. В этом случае происходит сбой компоновщика с сообщением только имени символа и имени второго OBJ-файла, в котором определен данный тип.Часто бывает полезным изменить порядок, в котором OBJ-файлы поступают в компоновщик, чтобы установить размещение других версий типа данных.

ms173265.collapse_all(ru-ru,VS.110).gifВзаимоблокировка при блокировке загрузчика

В Visual C++ .NET и Visual C++ 2003 при инициализации с использованием параметра /clr может происходить недетерминированная взаимоблокировка.Эта проблема известна как "взаимоблокировка при блокировке загрузчика".В среде Visual Studio 2010 этой взаимоблокировки проще избежать, она обнаруживается во время выполнения и уже не является недетерминированной.Возникновение блокировки загрузчика все еще возможно, однако теперь ее проще избежать и устранить.Подробные сведения о предпосылках возникновения этой проблемы, рекомендации и решения см. в разделе Инициализация смешанных сборок.

ms173265.collapse_all(ru-ru,VS.110).gifЭкспорт данных

Экспорт данных библиотек DLL может приводить к ошибкам и не рекомендуется.Причиной этого является то, что некоторые разделы данных библиотеки DLL могут оказаться неинициализированными, в то время как некоторые управляемые разделы уже будут выполнены.Для создания ссылок на метаданные следует использовать директиву Директивы #using (C++).

ms173265.collapse_all(ru-ru,VS.110).gifВидимость типов

Собственные типы теперь по умолчанию являются закрытыми.В Visual C++ .NET 2002 и Visual C++ 2003 собственные типы по умолчанию были открытыми.Это может привести к тому, что собственный тип может оказаться невидимым вне библиотеки DLL.Для устранения этой ошибки перед такими типами следует указывать ключевое слово public.Дополнительные сведения см. в разделе Видимость типов и членов.

ms173265.collapse_all(ru-ru,VS.110).gifПроблемы, связанные с использованием плавающей запятой и выравниванием

__controlfp не поддерживается средой CLR (дополнительные сведения см. в разделе _control87, _controlfp, __control87_2).Среда CLR также не поддерживает выравнивание (C++).

ms173265.collapse_all(ru-ru,VS.110).gifИнициализация COM

Среда CLR производит инициализацию COM автоматически при инициализации модуля (автоматическая инициализация COM производится в режиме многопотокового подразделения).Поэтому при попытке явной инициализации COM будут возвращены коды, указывающие на то, что инициализация COM уже произведена.Попытка явной инициализации COM с использованием однопотоковой модели может привести к сбою приложения, если среда CLR уже произвела инициализацию COM с использованием иной потоковой модели.

По умолчанию среда CLR производит инициализацию COM в режиме многопотокового подразделения. Чтобы изменить данную настройку, воспользуйтесь параметром /CLRTHREADATTRIBUTE (Установка атрибута потока среды CLR).

ms173265.collapse_all(ru-ru,VS.110).gifПроблемы производительности

При опосредованном вызове (вызовы виртуальных функций или использование указателей на функции) собственных методов C++, созданных в MSIL, может наблюдаться снижение производительности.Дополнительные сведения см. в разделе Двойное преобразование (С++).

При переходе от машинного кода к MSIL будет наблюдаться увеличение размера рабочего множества.Это объясняется тем, что среда CLR предоставляет множество функций, обеспечивающих правильную работу программ.Если приложение /clr работает неправильно, может потребоваться включить C4793 (по умолчанию выключено). Дополнительные сведения см. в разделе Предупреждение компилятора (уровни 1 и 3) C4793.

ms173265.collapse_all(ru-ru,VS.110).gifСбой программы при завершении работы

В некоторых случаях библиотека CLR может завершить работу до того, как будет выполнен управляемый код.Это может быть вызвано использованием std::set_terminate и SIGTERM.Дополнительные сведения см. в разделах константы ввода и set_terminate (<exception>).

Использование новых возможностей Visual C++

После компиляции, компоновки и запуска приложения можно использовать возможности .NET в любом модуле, скомпилированном с параметром /clr.Дополнительные сведения см. в разделе Расширения компонентов для платформ среды выполнения.

Если использовались управляемые расширения для C++, можно преобразовать код для использования нового синтаксиса.Краткий обзор синтаксических различий см. в разделе Managed Extensions for C++ Syntax Upgrade Checklist.Дополнительные сведения о преобразовании кода управляемых расширений для C++ см. в разделе Основы миграции C++/CLI.

Сведения о программировании .NET в Visual C++ см. в следующих разделах:

См. также

Основные понятия

Смешанные (собственные и управляемые) сборки