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

Обновлен: Ноябрь 2007

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

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

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

  • При компиляции в среде разработки проекта элемента управления ActiveX MFC с использованием параметра /clr система построения пытается зарегистрировать библиотеку DLL с помощью regasm, а не с помощью regsvr32. Следует зарегистрировать элемент управления вручную с помощью regsvr32.

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

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

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

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

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

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

Версии ранее Visual C++ 2003

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

Обновление с версии Visual C++ 2003

Проекты, построенные ранее с помощью Visual C++ 2003, также должны быть сперва скомпилированы без использования параметра /clr, так как в Visual C++ 2005 соблюдается более строгое соответствие стандартам ANSI/ISO и введен ряд изменений. Дополнительные сведения см. в разделе Критические изменения в компиляторе Visual C++. Изменение, которое может потребовать наибольшего внимания, это Security Enhancements in the CRT. Код, использующий библиотеку CRT, скорее всего, будет вызывать предупреждения об устаревании. Эти предупреждения можно отключить, однако более предпочтительным является переход на Security-Enhanced Versions of CRT Functions, поскольку они обеспечивают более высокий уровень безопасности и могут помочь выявить уязвимости в коде.

Обновление с управляемых расширений для C++

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

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

Хотя компиляция C-файлов в Visual C++ 2005 возможна, для компиляции с использованием параметра /clr необходимо преобразовать их в код C++. Реальное имя файла менять не обязательно, можно воспользоваться параметром /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 C++ 2005 следует создать новую конфигурацию проекта для /clr вместо изменения конфигурации по умолчанию. Параметр /clr несовместим с некоторыми параметрами компилятора, и создание отдельных конфигураций позволит выполнить построение проекта как управляемого или неуправляемого. При выборе параметра /clr в диалоговом окне страниц свойств параметры проекта, несовместимые с /clr, станут недоступны (недоступные параметры не включаются автоматически при последующей отмене выбора параметра /clr).

Создание новой конфигурации проекта

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

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

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

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

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

ms173265.alert_note(ru-ru,VS.90).gifПримечание.

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

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

Предкомпилированные заголовки

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

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

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

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

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

Слияние метаданных

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

Взаимоблокировка при блокировке загрузчика

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

Экспорт данных

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

Видимость типов

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

Проблемы, связанные с использованием плавающей запятой и выравниванием

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

Инициализация COM

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

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

Проблемы производительности

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

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

Сбой программы при завершении работы

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

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

После компиляции, компоновки и запуска приложения можно использовать возможности .NET в любом модуле, скомпилированном с параметром /clr. Дополнительные сведения см. в разделе Language Features for Targeting the CLR.

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

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

См. также

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

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