Udostępnij za pośrednictwem


Inicjowanie mieszanych zestawów

W Visual C++ .NET i Visual C++ 2003 bibliotek DLL skompilowane z /clr opcję kompilatora nie deterministically może zakleszczenie po załadowaniu; Ten problem był nazywany mieszanych DLL ładowania lub problem blokady modułu ładującego.Prawie wszystkie nie determinizmu został usunięty z mieszanego procesu ładowania biblioteki DLL.Jednak istnieje kilka pozostałe scenariusze, dla których program ładujący lock (deterministically) mogą wystąpić.Aby dowiedzieć się więcej na temat tego problemu, zobacz "Mieszane DLL ładowania Problem" w MSDN Library.

Kod w ramach DllMain nie muszą uzyskać dostępu do środowiska CLR.Oznacza to, że DllMain należy dzwonić nie do funkcji zarządzanych, bezpośrednio lub pośrednio: nie kodu zarządzanego powinny zadeklarowane lub stosowane w DllMain; i nie wyrzucania elementów bezużytecznych lub ładowania biblioteki automatyczne powinna być dokonywana w ramach DllMain.

[!UWAGA]

Program Visual C++ 2003 pod warunkiem _vcclrit.h w celu ułatwienia Inicjowanie DLL, minimalizując możliwość zakleszczenia.Za pomocą _vcclrit.h nie jest już potrzebna i powoduje, że oczekiwany ostrzeżenia, które należy opracować podczas kompilacji.Zalecana strategia jest w celu usunięcia zależności dotyczących ten plik przy użyciu kroki opisane w Removing Deprecated Header File _vcclrit.h.Mniej idealne rozwiązania obejmują, pomijanie ostrzeżenia, definiując _CRT_VCCLRIT_NO_DEPRECATE przed tym _vcclrit.h lub jedynie ignorowanie ostrzeżeń oczekiwany.

Przyczyny blokady modułu ładującego

Wraz z wprowadzeniem platformy .NET są dwa różne mechanizmy ładowania modułu wykonanie (plik EXE lub DLL): jeden dla systemu Windows, który jest używany dla modułów niezarządzane, a drugi dla platformy .NET Common Language Runtime (CLR) które ładuje zestawów .NET.Ładowanie biblioteki DLL w mieszanych problem skupia się wokół moduł ładujący systemu operacyjnego Microsoft Windows.

Po złożeniu zawierające tylko konstrukcje .NET jest załadowany do procesu, moduł ładujący CLR można wykonywać wszystkie niezbędne zadania ładowania i inicjowania sam.Jednakże dla zespołów mieszanych, ponieważ mogą one zawierać kodu macierzystego i danych, moduł ładujący systemu Windows musi być także używany.

Moduł ładujący systemu Windows gwarantuje żadnego kodu można kod dostępu lub dane w tej bibliotece DLL przed został zainicjowany i że żaden kod nadmiarowo można załadować biblioteki DLL, a częściowo jego inicjowania.Aby to zrobić, moduł ładujący systemu Windows używa procesu globalnego sekcji krytycznej (często nazywane "blokady modułu ładującego") uniemożliwiający niebezpieczne dostępu podczas inicjowania modułu.W efekcie proces ładowania jest zagrożony wiele scenariuszy klasyczne zakleszczenia.Dla zespołów mieszanego poniższe dwa scenariusze zwiększać ryzyko zakleszczenia:

  • Po pierwsze, jeśli użytkownicy próbują wykonać funkcji po blokadę modułu ładującego kompilowana do języka Microsoft intermediate language (MSIL) (z DllMain lub w inicjatory statycznych, na przykład), może to spowodować zakleszczenie.Należy rozważyć przypadek, w którym funkcja MSIL odwołuje się do typu w zestawie, do którego nie został załadowany.Środowisko CLR spróbuje automatycznie załadować tego zestawu, co może wymagać moduł ładujący systemu Windows do blokowania na blokady modułu ładującego.Ponieważ blokady modułu ładującego jest już używana przez kod wcześniej w sekwencję wywołań, wyniki zakleszczenie.Jednak wykonywanie MSIL pod blokady modułu ładującego nie gwarantuje zakleszczenie wystąpienia, co w tym scenariuszu trudne do zdiagnozowania i usunięcia.W pewnych okolicznościach takich jak których biblioteki DLL typu odwołanie zawiera nie macierzystego konstrukcje i wszystkimi jego zależnościami zawierać nie konstrukcje macierzystego systemu Windows programu ładującego nie ma obowiązku załadować zestawu .NET, do którego istnieje odwołanie typu.Dodatkowo wymaganego zestawu lub jego zależności mieszanych w trybie macierzystym/.NET może już załadowane przez inny kod.W związku z tym zakleszczeniu się może być trudne do przewidzenia i może się różnić w zależności od konfiguracji komputera docelowego.

  • Po drugie podczas ładowania biblioteki DLL w wersjach 1.0 i 1.1.NET Framework, środowiska CLR założyć, że blokady modułu ładującego nie został zablokowany i wykonać wiele czynności, które są nieprawidłowe w obszarze blokady modułu ładującego.Przy założeniu, że blokada modułu ładującego nie jest używana jest prawidłowy założeń czysto .NET DLL, ale ponieważ mieszanych bibliotek DLL macierzystego inicjowania procedur, wymagają one macierzysty moduł ładujący systemu Windows, a zatem blokady modułu ładującego.W związku z tym nawet jeśli autora nie próbował wykonać żadnych funkcji MSIL podczas inicjowania biblioteki DLL, było jeszcze małą możliwość zakleszczenia niedeterministyczne w wersji 1.0 i 1.1.NET Framework.

Wszystkich innych niż determinizmu został usunięty z mieszanego procesu ładowania biblioteki DLL.Z tego powodu istnieje z tych zmian:

  • Środowisko CLR nie są już założenia false podczas ładowania biblioteki DLL mieszanych.

  • Inicjowanie niezarządzane i zarządzane jest wykonywane w dwóch etapach odrębne i niezależne.Niezarządzane inicjowania odbywa się po raz pierwszy (przez DllMain) i inicjowania zarządzanej odbywa się później, za pośrednictwem.Obsługiwane NET konstrukcja o nazwie .cctor.Jest całkowicie przezroczyste dla użytkownika o ile nie /Zl lub /NODEFAULTLIB są używane.Zobacz/ NODEFAULTLIB (Ignoruj biblioteki) i /ZL (Brak domyślnej nazwy biblioteki) Aby uzyskać więcej informacji.

Blokady modułu ładującego może nadal występują, ale teraz występuje odtwarzalnie i zostanie wykryta.Jeśli funkcja DllMain zawiera instrukcje MSIL, kompilator generuje ostrzeżenie C4747 (poziom 1) ostrzeżenia kompilatora.Ponadto CRT lub CLR spróbuje wykrywania i zgłaszania próbuje wykonać MSIL pod blokady modułu ładującego.Wykrywanie CRT wyniki w czasie wykonywania R6033 błąd wykonania C diagnostycznych.

Pozostała część tego dokumentu są opisane scenariusze pozostałych, dla których MSIL można wykonać w obszarze blokady modułu ładującego rozwiązań problemu w ramach każdej z tych scenariuszy i technik debugowania.

Scenariusze i rozwiązania problemu

Jest wiele różnych sytuacji, w jakich użytkownika mogła zostać wykonana MSIL pod blokady modułu ładującego.Programista musi zapewniać, że implementację kodu użytkownika nie próbuje wykonać instrukcje MSIL w ramach każdej z tych okoliczności.Poniżej opisano wszystkie możliwości z dyskusji na temat sposobu rozwiązywania problemów występujących w większości przypadków.

  • DllMain

  • Inicjatory statycznych

  • Funkcje dostarczane przez użytkownika, mające wpływ na starcie

  • Niestandardowych ustawień regionalnych

ms173266.collapse_all(pl-pl,VS.110).gifFunkcja DllMain

DllMain Funkcja jest to punkt wejścia zdefiniowane przez użytkownika dla biblioteki DLL.Chyba że użytkownik określi inaczej, DllMain jest wywoływana za każdym razem, gdy proces lub wątek dołącza go lub odłączenie od zawierające biblioteki DLL.Ponieważ to wywołanie może wystąpić podczas, gdy moduł ładujący blokadę, nr dostarczone przez użytkownika DllMain funkcji, które należy zebrać Aby MSIL.Ponadto żadnej funkcji w drzewie wywołanie osadzone na DllMain mógł być kompilowany MSIL.Aby rozwiązać problemy w tym miejscu bloku kodu, który definiuje DllMain powinny być modyfikowane z #pragma unmanaged.To samo powinno odbywać się dla każdej funkcji, DllMain wywołań.

W przypadkach gdzie tych funkcji należy wywołać funkcję, która wymaga implementacja MSIL dla innych kontekstach wywołującego strategię powielania można gdzie tworzone są .NET oraz macierzystą wersję tej samej funkcji.

Alternatywnie Jeśli DllMain nie jest wymagana lub jeśli go nie musi być wykonywana w świetle loader zablokować, wprowadzoną przez użytkownika DllMain wykonania mogą być usunięte, która wyeliminuje problem.

Jeśli funkcja DllMain próbował wykonać MSIL bezpośrednio, C4747 (poziom 1) ostrzeżenia kompilatora spowoduje.Jednak kompilator nie może wykryć przypadki, gdzie wywołuje funkcję DllMain inny moduł, które z kolei może próbować wykonać MSIL.

Więcej informacji na temat tego scenariusza, zapoznaj się z "Przeszkód do diagnozy".

ms173266.collapse_all(pl-pl,VS.110).gifInicjowanie statyczne obiekty

Inicjowanie statyczne obiektów może spowodować zakleszczenie Jeśli inicjatorze dynamicznego jest wymagany.W przypadku prostych, gdy zmienna statyczna jest po prostu przypisany do wartość znana w czasie kompilacji, takie jak nie dynamicznego inicjowania jest wymagany, dlatego nie istnieje ryzyko zakleszczenie.Jednak zmienne statyczne zainicjowany przez wywołania funkcji, wywołania konstruktora lub wyrażeń, które nie mogą być poddawane ocenie w kompilacji czas wszystkie wymagają kodu do wykonania podczas inicjowania modułu.

Poniższy kod zawiera przykłady inicjatory statycznych, wymagające dynamicznego inicjowania: wywołanie funkcji, obiektów budowlanych i inicjowania wskaźnika.(Te przykłady nie są statyczne, ale są przyjmowane jako określane są w zasięgu globalnym, która działa tak samo).

// dynamic initializer function generated
int a = init();
CObject o(arg1, arg2);  
CObject* op = new CObject(arg1, arg2);

To ryzyko zakleszczenie zależy od tego, czy moduł zawierający została skompilowana z /clr i czy wykonywane MSIL.W szczególności jeśli zmienna statyczna jest kompilowany bez /clr (lub znajduje się w #pragma unmanaged bloku), i dynamiczne inicjator wymaganej do zainicjowania go powoduje wykonanie instrukcje MSIL, może wystąpić zakleszczenie.To dlatego dla modułów skompilowany bez /clr, inicjowanie zmiennych statycznych jest wykonywana przez DllMain.Z drugiej strony, zmienne statyczne, skompilowane z /clr są inicjowane przez .cctor, po zakończeniu etapu inicjalizacji niezarządzanych i wydała blokady modułu ładującego.

Istnieje kilka rozwiązań zakleszczenie spowodowane przez dynamiczne inicjalizacji zmiennych statycznych (mniej więcej w sposób uporządkowane czas wymagany, aby rozwiązać problem):

  • Plik źródłowy zawierający zmienna statyczna może zostać skompilowany z /clr.

  • Wszystkie funkcje wywoływane przez zmienna statyczna mógł być kompilowany do kodu macierzystego za pomocą #pragma unmanaged dyrektywy.

  • Ręcznie klon kod, który zmiennej statycznej zależy, zapewniając .NET i macierzystej wersji przy użyciu różnych nazw.Deweloperzy można wywoływać macierzystej wersji z macierzystego inicjatory statycznych i wywołać .NET w wersji w innym miejscu.

ms173266.collapse_all(pl-pl,VS.110).gifFunkcje dostarczane przez użytkownika, mające wpływ na starcie

Istnieje kilka funkcji dostarczone przez użytkownika, od których bibliotek zależy na zainicjowanie podczas uruchamiania systemu.Na przykład, kiedy globalnie przeciążanie operatorów w języku C++ w takich jak new i delete podmioty gospodarcze, wersje wprowadzoną przez użytkownika są stosowane wszędzie, w tym w inicjalizacji STL i zniszczenia.W efekcie STL i inicjatory statycznych wprowadzoną przez użytkownika będzie wywoływał wszelkich dostarczonych przez użytkownika wersje tych operatorów.

Jeśli wersje wprowadzoną przez użytkownika są kompilowane do MSIL, te inicjatory będzie próbować wykonać instrukcje MSIL, podczas gdy blokadę modułu ładującego.Funkcja malloc dostarczone przez użytkownika ma takie same skutki.Aby rozwiązać ten problem, żadnego z tych przeciążenia lub przez inne definicje musi być zaimplementowana jako kodu macierzystego za pomocą #pragma unmanaged dyrektywy.

Więcej informacji na temat tego scenariusza, zapoznaj się z "Przeszkód do diagnozy".

ms173266.collapse_all(pl-pl,VS.110).gifNiestandardowych ustawień regionalnych

Jeśli użytkownik udostępnia niestandardowe globalne ustawienia regionalne, tej lokalizacji będzie używany do inicjowania wszystkich przyszłych strumieni We/Wy, łącznie z tymi, które są statycznie inicjowane.Jeśli ten obiekt globalnych ustawień regionalnych jest skompilowany do MSIL, funkcje składowe obiektu ustawień regionalnych, opracowanych w celu MSIL może powoływać natomiast odbywa się blokady modułu ładującego.

Możliwe są trzy opcje dotyczące rozwiązywania tego problemu:

Pliki źródłowe zawierające wszystkie definicje globalnego strumienia wejścia/wyjścia można kompilować za pomocą /clr opcji.Pozwoli to uniknąć ich inicjatory statycznych z aktualnie wykonywane pod blokady modułu ładującego.

Definicja funkcji niestandardowych ustawień regionalnych mógł być kompilowany do kodu macierzystego za pomocą #pragma unmanaged dyrektywy.

Powstrzymują się od ustawienia regionalne niestandardowych jako globalne ustawienia regionalne do czasu, po zwolnieniu blokady modułu ładującego.Następnie skonfiguruj jawnie strumieni We/Wy utworzona podczas inicjacji niestandardowych ustawień regionalnych.

Przeszkody do diagnostyki

W niektórych przypadkach trudno wykryć przyczynę zakleszczenia.Następujące podsekcje omówienia tych scenariuszy i sposobów, aby obejść ten problem.

ms173266.collapse_all(pl-pl,VS.110).gifImplementacja w nagłówkach

W wybranych przypadkach implementacji funkcji wewnątrz pliki nagłówkowe może skomplikować diagnozy.Wbudowane funkcje i kod szablonu wymaga się, że funkcje określone w pliku nagłówka.Język C++ Określa jedną regułę definicji, co zmusza wszystkie implementacje funkcji o tej samej nazwie semantycznie równoważne.W związku z tym program łączący C++ nie potrzebują sporządzać jakieś specjalne zagadnienia, scalając pliki obiektów, których zduplikowane implementacji danej funkcji.

W Visual C++ .NET i Visual C++ .NET 2003 program łączący po prostu wybiera największą z tych definicji semantycznie równoważne, aby pomieścić scenariuszy i deklaracje do przodu, gdy opcje optymalizacji różnych są używane dla plików z innego źródła.Powoduje to problem dla mieszanych w trybie macierzystym/.NET bibliotek DLL.

Ponieważ ten sam nagłówek może być włączone zarówno przez pliki CPP z /clr włączone i wyłączone, lub # obejmują może być wlana za #pragma unmanaged bloku, jest możliwe, że MSIL i macierzystych wersje funkcji, które zapewniają implementacje w nagłówkach.MSIL i implementacji macierzystym mają różne składnie właściwości w odniesieniu do inicjowania w obszarze blokady modułu ładującego, które skutecznie narusza reguły jedną definicję.W związku z tym: gdy program łączący wybiera największą implementacji, może on wybrać wersję MSIL funkcji, nawet jeśli jawnie został skompilowany do kodu macierzystego, gdzie indziej za pomocą dyrektywy niezarządzanego #pragma.W celu zapewnienia, że wersja MSIL szablonu lub wewnętrznej funkcji nigdy nie nazywa się w obszarze blokady modułu ładującego, każdy definicji każda funkcja o nazwie w blokady modułu ładującego musi być zmienione z #pragma unmanaged dyrektywy.Jeśli plik nagłówkowy jest od strony trzeciej, najłatwiejszym sposobem osiągnięcia tego jest naciskać i pop dyrektywy niezarządzanego #pragma wokół # dołączona dyrektywa do niepoprawnego pliku nagłówka.(Zobacz managed, unmanaged na przykład.) Strategia ta nie będzie działać dla nagłówków, które zawierają inny kod, który należy bezpośrednio wywoływać interfejsy API .NET.

Dla wygody użytkowników do czynienia z blokady modułu ładującego program łączący wybierze macierzystej implementacji nad zarządzanych przedstawionej zarówno.Pozwala to uniknąć powyższych kwestii.Istnieją jednak dwa wyjątki od tej reguły w tym wydaniu, ze względu na dwie sprawy związane z kompilatora:

  • Wywołanie jest zakotwiczony w wierszu funkcja jest za pomocą wskaźnika globalnego funkcja statyczna.W tym scenariuszu jest szczególnie interesujące, ponieważ wirtualnego funkcje są wywoływane za pośrednictwem wskaźników funkcji globalnych.Na przykład:
#include "definesmyObject.h"
#include "definesclassC.h"

typedef void (*function_pointer_t)();

function_pointer_t myObject_p = &myObject;

#pragma unmanaged
void DuringLoaderlock(C & c)
{
    // Either of these calls could resolve to a managed implementation, 
    // at link-time, even if a native implementation also exists.
    c.VirtualMember();
    myObject_p();
}
  • Z kompilacji kierowanych na procesorach Itanium jest to błąd we wprowadzaniu w życie wszystkich wskaźników funkcji.W powyższym przykładzie Jeśli myObject_p zostały zdefiniowane lokalnie wewnątrz during_loaderlock(), połączenie może również rozwiązać zarządzaną implementację.

ms173266.collapse_all(pl-pl,VS.110).gifDiagnozowanie w trybie debugowania

Opiera się wszystkich rozpoznań blokady modułu ładującego, które problemy powinny odbywać się przy użyciu programu Debug.Trybie budowania wersji mogą nie dawać diagnostyki i optymalizacje wykonywane w trybie Release może maskować niektóre MSIL w scenariuszach blokady modułu ładującego.

Sposób debugowania problemów blokady modułu ładującego

Diagnostyki, która generuje CLR, gdy jest wywoływana jest funkcja MSIL powoduje, że CLR w celu wstrzymania wykonywania.Z kolei wywołuje debuger trybu mieszanego Visual C++, jak również zostaje zawieszony, gdy proces debugowany z wykorzystaniem procesów.Jednak podczas dołączania do procesu, to nie można uzyskać zarządzanego stosu wywołań dla proces debugowany za pomocą debugera mieszanych.

Aby zidentyfikować określoną funkcję MSIL, która została wywołana w obszarze blokady modułu ładującego, deweloperzy należy wykonać następujące kroki:

  1. Upewnij się, że symbole mscoree.dll i mscorwks.dll są dostępne.

    Można to zrobić na dwa sposoby.Po pierwsze PDB mscoree.dll i mscorwks.dll mogą być dodawane do ścieżki wyszukiwania symboli.Aby to zrobić, Otwórz okno Opcje ścieżka wyszukiwania symbolu.(W menu Narzędzia kliknij polecenie Opcje.W lewym okienku okna dialogowego Opcje Otwórz węzeł Debugging i kliknij symbole.) Dodaj ścieżkę do plików PDB mscoree.dll i mscorwks.dll do listy wyszukiwania.Te PDB są instalowane do % VSINSTALLDIR%\SDK\v2.0\symbols.Kliknij przycisk OK.

    Po drugie PDB mscoree.dll i mscorwks.dll można pobrać z serwera symboli firmy Microsoft.Aby skonfigurować serwer symbolu, Otwórz okno Opcje ścieżka wyszukiwania symbolu.(W menu Narzędzia kliknij polecenie Opcje.W lewym okienku okna dialogowego Opcje Otwórz węzeł Debugging i kliknij symbole.) Dodaj następującą ścieżkę wyszukiwania do listy wyszukiwania: http://msdl.microsoft.com/download/symbols.Dodać katalog pamięci podręcznej symbol do pola tekstowego pamięci podręcznej serwera symboli.Kliknij przycisk OK.

  2. Ustaw tryb debugera na tryb tylko do macierzystego.

    Aby to zrobić, otwórz właściwości siatki dla projekt uruchamiania w roztworze.W obszarze poddrzewo właściwości konfiguracji wybierz węzeł Debugging.Pola typu debugera ustawić tylko w trybie macierzystym.

  3. Uruchom debuger (F5).

  4. Gdy /clr diagnostic jest generowany, kliknij przycisk Ponów próbę, a następnie kliknij polecenie Break.

  5. Otwórz okno wywołanie stosu.(W menu debugowania kliknij Windows, a następnie stos wywołań). Jeśli przestępstwa DllMain lub statycznego inicjatora jest identyfikowana z zieloną strzałką.Jeśli funkcja powodująca problemy nie został zidentyfikowany, poniższe kroki należy go znaleźć.

  6. Otwórz okienko bezpośrednie (w menu debugowania kliknij Windows, a następnie natychmiastowe).

  7. Typ .load sos.dll w okienku bezpośrednim załadować SOS debugowania usługi.

  8. Typ! dumpstack w okienku bezpośrednim, aby uzyskać pełną listę wewnętrznego /clr stosu.

  9. Poszukaj pierwszego wystąpienia (najbliżej dole stosu) albo _CorDllMain (Jeśli DllMain powoduje, że problem) lub _VTableBootstrapThunkInitHelperStub lub GetTargetForVTableEntry (Jeśli inicjator statycznych powoduje, że problem).Wpis stosu tuż poniżej tego wywołania jest wywoływanie MSIL zaimplementowana funkcja, którą próbował wykonać w blokady modułu ładującego.

  10. Przejdź do pliku źródłowego i numer zidentyfikowanego w kroku 9 i prawidłowe linii problem przy użyciu scenariuszy i rozwiązania opisane w sekcji scenariuszy.

Przykład

ms173266.collapse_all(pl-pl,VS.110).gifOpis

Poniższy przykład pokazuje jak uniknąć blokady modułu ładującego przenosząc kod z funkcji DllMain do konstruktora obiektu globalnego.

W tym przykładzie jest zarządzany obiekt globalny, którego konstruktor zawiera zarządzanym obiektem, który był pierwotnie w funkcji DllMain.Druga część tej próbki odwołuje się do zestawu, utworzenie wystąpienia obiektu zarządzanego do wywołania konstruktora moduł, który nie wymaga inicjowania.

ms173266.collapse_all(pl-pl,VS.110).gifKod

// initializing_mixed_assemblies.cpp
// compile with: /clr /LD 
#pragma once
#include <stdio.h>
#include <windows.h>
struct __declspec(dllexport) A {
   A() {
      System::Console::WriteLine("Module ctor initializing based on global instance of class.\n");
   }

   void Test() {
      printf_s("Test called so linker does not throw away unused object.\n");
   }
};
 
#pragma unmanaged
// Global instance of object
A obj;
 
extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) {
   // Remove all managed code from here and put it in constructor of A.
   return true;
}

Przykład

ms173266.collapse_all(pl-pl,VS.110).gifKod

// initializing_mixed_assemblies_2.cpp
// compile with: /clr initializing_mixed_assemblies.lib
#include <windows.h>
using namespace System;
#include <stdio.h>
#using "initializing_mixed_assemblies.dll"
struct __declspec(dllimport) A {
   void Test();
};

int main() {
   A obj;
   obj.Test();
}

ms173266.collapse_all(pl-pl,VS.110).gifDane wyjściowe

Module ctor initializing based on global instance of class.

Test called so linker does not throw away unused object.

Zobacz też

Koncepcje

Mieszane (macierzystych i zarządzanych)