Udostępnij za pośrednictwem


Importy wzajemne

Eksportowanie lub importowanie do innego pliku wykonywalnego powoduje komplikacje, gdy importy są wzajemne (lub cykliczne). Na przykład dwa biblioteki DLL importuje ze siebie symbole, podobnie jak wzajemnie rekursywne funkcje.

Problem z wzajemnie importem plików wykonywalnych (zwykle DLL) polega na tym, że nie można ich skompilować bez uprzedniego utworzenia drugiego. Każdy proces kompilacji wymaga, jako danych wejściowych, biblioteki importu utworzonej przez inny proces kompilacji.

Rozwiązaniem jest użycie narzędzia LIB z opcją /DEF, która tworzy bibliotekę importu bez kompilowania pliku wykonywalnego. Za pomocą tego narzędzia można utworzyć wszystkie potrzebne biblioteki importu, niezależnie od liczby bibliotek DLL lub jak skomplikowane są zależności.

Ogólne rozwiązanie do obsługi wzajemnych importów to:

  1. Weź każdą bibliotekę DLL z kolei. (Każda kolejność jest wykonalna, chociaż niektóre zamówienia są bardziej optymalne). Jeśli wszystkie potrzebne biblioteki importu istnieją i są aktualne, uruchom polecenie LINK, aby skompilować plik wykonywalny (DLL). Spowoduje to utworzenie biblioteki importu. W przeciwnym razie uruchom lib, aby utworzyć bibliotekę importu.

    Uruchomienie biblioteki LIB z opcją /DEF powoduje utworzenie dodatkowego pliku z . Rozszerzenie EXP. Tthe. Plik EXP należy użyć później do skompilowania pliku wykonywalnego.

  2. Po utworzeniu wszystkich bibliotek importu przy użyciu linku lub LIB wróć i uruchom polecenie LINK, aby skompilować wszystkie pliki wykonywalne, które nie zostały utworzone w poprzednim kroku. Należy pamiętać, że odpowiedni plik exp musi być określony w wierszu LINK.

    Jeśli wcześniej uruchomiono narzędzie LIB, aby utworzyć bibliotekę importu dla biblioteki DLL1, biblioteka LIB również utworzyłaby plik DLL1.exp. Podczas kompilowania biblioteki DLL1.dlll należy użyć biblioteki DLL1.exp jako danych wejściowych.

Poniższa ilustracja przedstawia rozwiązanie dwóch wzajemnie importowanych bibliotek DLL, DLL1 i DLL2. Krok 1 polega na uruchomieniu biblioteki LIB z zestawem opcji /DEF w bibliotece DLL1. Krok 1 tworzy bibliotekę DLL1.lib, bibliotekę importu i bibliotekę DLL1.exp. W kroku 2 biblioteka importu jest używana do kompilowania biblioteki DLL2, która z kolei tworzy bibliotekę importu dla symboli DLL2. Krok 3 kompiluje bibliotekę DLL1 przy użyciu bibliotek DLL1.exp i DLL2.lib jako danych wejściowych. Należy pamiętać, że plik exp dla biblioteki DLL2 nie jest konieczny, ponieważ biblioteka LIB nie została użyta do skompilowania biblioteki importu dll2.

Diagram that shows the inputs and outputs when you use mutual imports to link two DLLs.
Łączenie dwóch bibliotek DLL z importami wzajemnymi

Ograniczenia _AFXEXT

Możesz użyć symbolu _AFXEXT preprocesora dla bibliotek DLL rozszerzeń MFC, o ile nie masz wielu warstw bibliotek DLL rozszerzeń MFC. Jeśli masz biblioteki DLL rozszerzeń MFC, które wywołuje lub pochodzą z klas we własnych bibliotekach DLL rozszerzeń MFC, które następnie pochodzą z klas MFC, musisz użyć własnego symbolu preprocesora, aby uniknąć niejednoznaczności.

Problem polega na tym, że w systemie Win32 należy jawnie zadeklarować dowolne dane tak, jakby __declspec(dllexport) zostały wyeksportowane z biblioteki DLL i __declspec(dllimport) czy mają zostać zaimportowane z biblioteki DLL. Podczas definiowania _AFXEXTnagłówków MFC upewnij się, że AFX_EXT_CLASS jest poprawnie zdefiniowana.

Jeśli masz wiele warstw, jeden symbol, taki jak AFX_EXT_CLASS , nie jest wystarczający, ponieważ biblioteka DLL rozszerzenia MFC może eksportować nowe klasy, a także importować inne klasy z innej biblioteki DLL rozszerzenia MFC. Aby rozwiązać ten problem, użyj specjalnego symbolu preprocesora, który wskazuje, że tworzysz samą bibliotekę DLL w porównaniu z użyciem biblioteki DLL. Załóżmy na przykład, że dwie biblioteki DLL rozszerzeń MFC, A.dll i B.dll. Każda z nich eksportuje odpowiednio niektóre klasy w klasach A.h i B.h. Biblioteka B.dll używa klas z biblioteki A.dll. Pliki nagłówków będą wyglądać mniej więcej tak:

/* A.H */
#ifdef A_IMPL
   #define CLASS_DECL_A   __declspec(dllexport)
#else
   #define CLASS_DECL_A   __declspec(dllimport)
#endif

class CLASS_DECL_A CExampleA : public CObject
{ ... class definition ... };

// B.H
#ifdef B_IMPL
   #define CLASS_DECL_B   __declspec(dllexport)
#else
   #define CLASS_DECL_B   __declspec(dllimport)
#endif

class CLASS_DECL_B CExampleB : public CExampleA
{ ... class definition ... };
...

Skompilowana biblioteka A.dll jest kompilowana za pomocą /D A_IMPL biblioteki , a po skompilowanych bibliotekach B.dll jest kompilowana za pomocą /D B_IMPLpolecenia . Używając oddzielnych symboli dla każdej biblioteki DLL, CExampleB jest eksportowany i CExampleA importowany podczas tworzenia biblioteki B.dll. CExampleA jest eksportowany podczas kompilowania biblioteki A.dll i importowany w przypadku użycia przez bibliotekę B.dll (lub innego klienta).

Nie można wykonać tego typu warstw podczas korzystania z wbudowanych symboli AFX_EXT_CLASS i _AFXEXT preprocesora. Opisana powyżej technika rozwiązuje ten problem w sposób, który nie różni się od samego mechanizmu MFC używanego podczas tworzenia bibliotek DLL rozszerzeń Active, Database i Network MFC.

Nie eksportuj całej klasy

Jeśli nie eksportujesz całej klasy, musisz upewnić się, że niezbędne elementy danych utworzone przez makra MFC są prawidłowo eksportowane. Można to zrobić, ponownie definiując AFX_DATA makro określonej klasy. Należy to zrobić za każdym razem, gdy nie eksportujesz całej klasy.

Przykład:

/* A.H */
#ifdef A_IMPL
   #define CLASS_DECL_A  _declspec(dllexport)
#else
   #define CLASS_DECL_A  _declspec(dllimport)
#endif

#undef  AFX_DATA
#define AFX_DATA CLASS_DECL_A

class CExampleA : public CObject
{
   DECLARE_DYNAMIC()
   CLASS_DECL_A int SomeFunction();
   //... class definition ...
};

#undef AFX_DATA
#define AFX_DATA

Co chcesz zrobić?

Co chcesz dowiedzieć się więcej?

Zobacz też

Importowanie i eksportowanie