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:
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. Ten. Plik EXP należy użyć później do skompilowania pliku wykonywalnego.
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.
Łą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 _AFXEXT
nagłó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. B.dll używa klas z 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 ... };
...
Po skompiluj A.dll jest kompilowany za pomocą /D A_IMPL
polecenia , a po utworzeniu B.dll jest on kompilowany za pomocą polecenia /D B_IMPL
. Używając oddzielnych symboli dla każdej biblioteki DLL, CExampleB
jest eksportowany i CExampleA
importowany podczas kompilowania B.dll. CExampleA
jest eksportowany podczas kompilowania A.dll i importowany w przypadku użycia przez 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.
Na 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ć?
Wyeksportuj z biblioteki DLL przy użyciu polecenia . Pliki DEF
Eksportowanie z biblioteki DLL przy użyciu biblioteki __declspec(dllexport)
Eksportowanie funkcji języka C++ do użycia w plikach wykonywalnych języka C
Importowanie do aplikacji przy użyciu atrybutu __declspec(dllimport)