Porady: migracja do /clr
W tym temacie omówiono zagadnienia, które występują podczas kompilowania kodu natywnego z użyciem /clr (patrz /clr (Kompilacja środowiska uruchomieniowego języka wspólnego), aby uzyskać więcej informacji)./clr pozwala modułom Visual C++ na wywoływanie i bycie wywoływanym z zestawów .NET, przy zachowaniu zgodności z modułami niezarządzanymi.Zobacz Zestawy mieszane (natywne i zarządzane) i Współdziałanie natywne i .NET, aby uzyskać więcej informacji o zaletach kompilacji z użyciem /clr.
Znane problemy dotyczące kompilacji projektów biblioteki z użyciem /clr
Program Visual Studio posiada niektóre znane problemy występujące podczas kompilowania projektów biblioteki przy użyciu /clr:
Kod może wykonywać zapytania dotyczące typów w czasie wykonywania z użyciem CRuntimeClass::FromName.Jednakże, jeśli typ jest w MSIL .dll (skompilowane z /clr), wywołanie CRuntimeClass::FromName może się nie powieść, jeśli występuje przed uruchomieniem konstruktorów statycznych w zarządzanym pliku .dll (nie zobaczysz tego problemu, jeśli wywołanie FromName występuje po wykonaniu kodu w zarządzanym pliku .dll).Aby obejść ten problem, można wymusić konstrukcję zarządzanych konstruktorów statycznych, definiując funkcję w zarządzanym pliku .dll, eksportując i wywołując ją z natywnej aplikacji MFC.Na przykład:
// Extension DLL Header file: __declspec( dllexport ) void EnsureManagedInitialization () { // managed code that won't be optimized away System::GC::KeepAlive(System::Int32::MaxValue); }
Kompilacja z Visual C++
Przed użyciem /clr na dowolnym module w projekcie, najpierw należy skompilować i połączyć projekt natywny z Visual Studio 2010.
Następujące kroki wykonywane w kolejności zapewniają najłatwiejszą ścieżkę kompilacji /clr.Jest ważne, aby skompilować i uruchomić projekt po każdej z tych czynności.
Wersje wcześniejsze niż Visual C++ 2003
Uaktualniając Visual Studio 2010 z wersji wcześniejszej niż Visual C++ 2003 mogą pojawić się błędy kompilatora związane z rozszerzoną zgodnością standardu C++ w Visual C++ 2003
Uaktualnianie z Visual C++ 2003
Projekty skompilowane wcześniej przy użyciu Visual C++ 2003 powinny także najpierw zostać skompilowane bez /clr, ponieważ w Visual Studio zwiększono obecnie zgodność ANSI/ISO i wprowadzono zmiany kluczowe.Zmiana, która może wymagać największej uwagi, to Funkcje zabezpieczeń w CRT.Kod, który używa CRT ma duże prawdopodobieństwo generowania ostrzeżeń dotyczących zaniechania.Ostrzeżenia te mogą być pomijane, ale preferowane jest migrowanie do nowych Wersje funkcji CRT z rozszerzonymi zabezpieczeniami, ponieważ zapewniają one lepsze bezpieczeństwo i mogą ujawnić problemy z zabezpieczeniami w kodzie.
Uaktualnianie wersji rozszerzeń Managed Extensions for C++
Projekty utworzone za pomocą Visual C++ .NET lub Visual C++ 2003, które używały Managed Extensions for C++ będą wymagały co najmniej jednej zmiany w ustawieniach projektu, ponieważ rozszerzenia te są obecnie przestarzałe.W rezultacie kod napisany z użyciem Managed Extensions for C++ nie skompiluje się w /clr.Zamiast tego użyj elementu /clr:oldSyntax.
Konwertowanie kodu C do C++
Chociaż Visual Studio będzie kompilować pliki C, należy przekonwertować je na C++ dla kompilacji /clr.Rzeczywista nazwa pliku nie musi być zmieniona; można użyć /Tp (zobacz /Tc, /Tp, /TC, /TP (Określ typ pliku źródłowego)). Należy zauważyć, że chociaż pliki kodu źródłowego języka C++ są wymagane dla /clr, nie ma konieczności refaktoryzacji kodu w celu użycia paradygmatu zorientowanego obiektowo.
Istnieje duże prawdopodobieństwo, że kod C będzie wymagał zmian przy kompilacji jako plik C++.Zasady bezpieczeństwa typów w C++ są ścisłe, więc konwersje typów muszą być wykonywane jawnie z użyciem rzutowań.Na przykład funkcja malloc zwraca pusty wskaźnik, ale może być przypisana do wskaźnika dowolnego typu w C z rzutowaniem:
int* a = malloc(sizeof(int)); // C code
int* b = (int*)malloc(sizeof(int)); // C++ equivalent
Wskaźniki funkcji są również ściśle bezpieczne dla typów w C++, więc następujący kod C wymaga modyfikacji.W języku C++ najlepiej jest tworzyć typedef, który definiuje typ wskaźnika funkcji, a następnie użyć tego typu do rzutowania wskaźników funkcji:
NewFunc1 = GetProcAddress( hLib, "Func1" ); // C code
typedef int(*MYPROC)(int); // C++ equivalent
NewFunc2 = (MYPROC)GetProcAddress( hLib, "Func2" );
C++ również wymaga, aby funkcje były prototypowane lub w pełni definiowane zanim będą mogły być odwoływane lub wywoływane.
Należy zmienić nazwy identyfikatorów używanych w kodzie C, które są słowami kluczowymi w C++ (takich jak virtual, new, delete, bool, true, false, itd.).Ogólnie można to zrobić z użyciem prostych operacji wyszukiwania i zamiany.
Wreszcie wywołania COM w stylu C wymagają jawnego użycia v-table i wskaźnika this, natomiast w C++ nie:
COMObj1->lpVtbl->Method(COMObj, args); // C code
COMObj2->Method(args); // C++ equivalent
Ponownie skonfiguruj ustawienia projektu
Po kompilacji i uruchomieniu projektu w Visual Studio 2010 należy utworzyć nowe konfiguracje projektu dla /clr, zamiast modyfikowania konfiguracji domyślnych./clr jest niezgodne z niektórymi opcjami kompilatora i tworzenie rozdzielnych konfiguracji pozwala na kompilowanie projektu jako natywny lub zarządzany.Gdy /clr jest zaznaczone w oknie dialogowym stron właściwości, ustawienia projektu niezgodne z /clr są wyłączone (i wyłączone opcje nie są automatycznie przywracane, jeśli /clr zostanie następnie odznaczone).
Utwórz nowe konfiguracje projektu
Można użyć opcji Kopiowanie ustawień w Okno dialogowe konfiguracji dla nowego projektu do utworzenia konfiguracji projektu na podstawie istniejących ustawień projektu.Zrób to raz dla konfiguracji Debug i raz dla konfiguracji Release.Kolejne zmiany mogą następnie być zastosowane tylko dla konfiguracji specyficznych dla /clr, pozostawiając oryginalne konfiguracje projektu bez zmian.
Projekty korzystające z niestandardowych zasad kompilacji mogą wymagać szczególnej uwagi.
Ten krok ma różne skutki dla projektów, które używają makefile.W tym przypadku można skonfigurować oddzielny cel kompilacji lub utworzyć wersję specyficzną dla /clr z kopii oryginału.
Zmień ustawienia projektu
/clr można zaznaczyć w środowisku programistycznym postępując zgodnie z instrukcjami w /clr (Kompilacja środowiska uruchomieniowego języka wspólnego).Jak już wspomniano wcześniej, ten krok spowoduje automatyczne wyłączenie ustawień projektu powodujących konflikt.
[!UWAGA]
Uaktualniając bibliotekę zarządzaną lub projekt usługi sieci Web z Visual C++ 2003, opcja kompilatora /Zl zostanie dodana do strony właściwości Wiersz polecenia.Spowoduje to LNK2001.Usuń /Zl ze strony właściwości Wiersz polecenia, aby rozwiązać.Zobacz /Zl (Pomiń domyślną nazwę biblioteki) i Porady: otwieranie stron właściwości projektów, aby uzyskać więcej informacji.Można też dodać msvcrt.lib i msvcmrt.lib do właściwości Dodatkowe zależności konsolidatora.
W przypadku projektów skompilowanych z makefile, niezgodne opcje kompilatora muszą być wyłączone ręcznie po dodaniu /clr.Zobacz //clr Ograniczenia, aby uzyskać informacje dotyczące opcji kompilatora niezgodnych z /clr.
Prekompilowane nagłówki
Wstępnie skompilowane nagłówki są obsługiwane w /clr.Jednakże, w przypadku kompilowania niektórych plików CPP z /clr (kompilowanie reszty natywnie) będą wymagane pewne zmiany, ponieważ wstępnie skompilowane nagłówki wygenerowane z /clr nie są zgodne z tymi wygenerowanymi bez /clr.Ta niezgodność jest ze względu na fakt, że /clr generuje i wymaga metadanych.Moduły skompilowane z /clr nie mogą zatem używać wstępnie skompilowanych nagłówków, które nie zawierają metadanych, natomiast moduły nie /clr nie mogą używać wstępnie skompilowanych plików nagłówka, które zawierają metadane.
Najprostszym sposobem kompilowania projektu, w którym niektóre moduły są kompilowane /clr jest całkowite wyłączenie wstępnie skompilowanych nagłówków. (W oknie dialogowym Strony właściwości projektu, otwórz węzeł C/C++ i wybierz Prekompilowane nagłówki.Następnie zmień właściwość Twórz/Używaj nagłówków prekompilowanych na "Prekompilowane nagłówki nie są używane").
Jednakże, szczególnie w przypadku dużych projektów, wstępnie skompilowane nagłówki zapewniają dużo lepszą szybkość kompilacji, więc wyłączenie tej funkcji nie jest pożądane.W tym przypadku najlepiej jest skonfigurować pliki /clr i nie /clr, aby używać rozdzielnych wstępnie skompilowanych nagłówków.Można to zrobić w jednym kroku przez wybranie wielu modułów do skompilowania z /clr używając Eksploratora rozwiązań, kliknięcie prawym przyciskiem myszy na grupę i wybranie Właściwości.Następnie należy zmienić właściwości Twórz/Używaj nagłówka prekompilowanego poprzez plik i Prekompilowany plik nagłówka, aby używać odpowiednio innej nazwy pliku nagłówka i prekompilowanego pliku nagłówka.
Naprawianie błędów
Kompilowanie z /clr może powodować błędy kompilatora, konsolidatora lub czasu wykonywania.W tej sekcji omówiono najczęstsze problemy.
Scalanie metadanych
Różne wersje typów danych mogą powodować błąd konsolidatora, ponieważ metadane wygenerowane dla dwóch typów nie są zgodne. (Jest to zazwyczaj spowodowane, gdy elementy członkowskie typu są warunkowo zdefiniowane, ale warunki nie są takie same dla wszystkich plików CPP, które używają typu). W takim przypadku konsolidator zwróci błąd, raportując tylko nazwę symbolu i nazwę drugiego pliku OBJ, w którym został zdefiniowany typ.Często jest przydatne, aby obrócić kolejność tak, aby pliki OBJ były wysyłane do konsolidatora w celu odnalezienia lokalizacji innej wersji typu danych.
Zakleszczenie blokady modułu ładującego
W Visual C++ .NET i Visual C++ 2003 inicjalizacja w /clr była podatna na niedeterministyczne zakleszczenie.Ten problem jest znany jako "zakleszczenie blokady modułu ładującego".W Visual Studio 2010 to zakleszczenie łatwiej jest uniknąć, jest ono wykrywane i raportowane w czasie wykonywania, a także nie jest już niedeterministyczne.Napotkanie problemu blokady modułu ładujące jest nadal możliwe, ale teraz jest dużo łatwiej go uniknąć i naprawić.Zobacz Inicjalizacja zestawów mieszanych, aby uzyskać więcej szczegółowych informacji kontekstowych, wskazówek i rozwiązań.
Eksport danych
Eksportowanie danych DLL jest podatne na błędy i nie jest zalecane.Jest to spowodowane tym, że nie ma gwarancji inicjalizacji sekcji danych w bibliotece DLL, zanim pewna zarządzana część biblioteki DLL zostanie wykonana.Należy odwołać się do metadanych z użyciem #using — dyrektywa (C++).
Widoczność typów
Typy natywne są obecnie domyślnie prywatne.W Visual C++ .NET 2002 i Visual C++ 2003 typy natywne były domyślnie publiczne.Może to spowodować, że typ natywny nie będzie widoczny spoza biblioteki DLL.Należy rozwiązać ten błąd, dodając public do tych typów.Aby uzyskać więcej informacji, zobacz Widoczność typów i członków.
Problemy związane z liczbami zmiennoprzecinkowymi i wyrównaniem
__controlfp nie jest obsługiwane w środowisku uruchomieniowym języka wspólnego (zobacz _control87, _controlfp, __control87_2, aby uzyskać więcej informacji).Środowisko CLR nie będzie również przestrzegać align (C++).
Inicjalizacja COM
Środowisko uruchomieniowe języka wspólnego inicjalizuje COM automatycznie podczas inicjalizacji modułu (gdy COM jest inicjalizowane automatycznie, wykonywane jest to jako MTA).W rezultacie jawna inicjalizacja COM zwraca kody powrotne, wskazując że COM jest już zainicjowany.Próby jawnego zainicjowania COM z modelem wątkowości, gdy CLR dokonało już inicjalizacji COM do innego modelu wątkowości, mogą spowodować błąd aplikacji.
Środowisko uruchomieniowe języka wspólnego domyślnie uruchamia COM jako MTA; użyj /CLRTHREADATTRIBUTE (Ustaw atrybut wątku CTR), aby to zmodyfikować.
Problemy z wydajnością
Może być widoczne obniżenie wydajności, gdy natywne metody C++ wygenerowane do MSIL są wywoływane bezpośrednio (wirtualne wywołania funkcji lub używanie wskaźników funkcji).Aby dowiedzieć się więcej na ten temat, zobacz Podwójna konwersja bitowa adresów (C++).
Przy przejściu z natywnego do MSIL można zauważyć zwiększenie rozmiaru zestawu roboczego.Jest to spowodowane tym, że środowisko uruchomieniowe języka wspólnego dostarcza wielu funkcji, aby upewnić się, że programy działają prawidłowo.Jeśli aplikacja /clr nie działa prawidłowo, może istnieć potrzeba włączenia C4793 (domyślnie wyłączone), zobacz Ostrzeżenie kompilatora (poziom 1 i 3) C4793, aby uzyskać więcej informacji.
Program zawiesza się podczas zamykania
W niektórych przypadkach CLR może dokonać zamknięcia, zanim kod zarządzany skończy działać.Korzystanie z std::set_terminate i SIGTERM może to powodować.Zobacz sygnał — Stałe i set_terminate (<exception>), aby uzyskać więcej informacji.
Korzystanie z nowych funkcji Visual C++
Po kompilacji, konsolidacji i uruchomieniu aplikacji, można rozpocząć używanie funkcji .NET w dowolnym module skompilowanym z użyciem /clr.Aby uzyskać dodatkowe informacje, zobacz Funkcje języka dla określania wartości docelowej do środowiska CLR.
Jeśli użyto Managed Extensions for C++, można skonwertować kod tak, aby używać nowej składni.Aby uzyskać podsumowanie różnic składniowych, zobacz Managed Extensions for C++ Syntax Upgrade Checklist.Aby uzyskać szczegółowe informacje dotyczące Managed Extensions for C++, zobacz Podręcznik migracji C++/CLI.
Aby uzyskać informacje dotyczące programowania w Visual C++ .NET, zobacz: