2008-12-12 17 views
8

Mam okrągłą zależność między dwiema funkcjami. Chciałbym, aby każda z tych funkcji rezydowała w swojej własnej bibliotece dll. Czy można to zbudować za pomocą wizualnego studia?Okrągłe zależności między bibliotekami dll i wizualnym studio

foo(int i) 
{ 
    if (i > 0) 
     bar(i -i); 
} 

-> należy skompilować do foo.dll

bar(int i) 
{ 
    if (i > 0) 
     foo(i - i); 
} 

-> należy skompilować do bar.dll

I zostały utworzone dwa projekty w visual studio, jeden dla foo i jeden dla barów . Grając z "Referencjami" i kompilując kilka razy, udało mi się zdobyć bibliotekę DLL, którą chcę. Chciałbym jednak wiedzieć, czy visual studio oferuje sposób na zrobienie tego w czysty sposób.

W przypadku zmiany foo, pasek nie musi być rekompilowany, ponieważ zależę tylko od podpisu paska, a nie od implementacji paska. Jeśli obie biblioteki dll mają bibliotekę obecną, mogę ponownie skompilować nową funkcjonalność do jednego z dwóch i cały system nadal działa.

Powodem, dla którego próbuję tego, jest to, że mam starszą wersję systemu z kołowymi zależnościami, które są obecnie statycznie połączone. Chcemy przejść do bibliotek dll z różnych powodów. Nie chcemy czekać, aż oczyścimy wszystkie zależności cykliczne. Myślałem o rozwiązaniach i wypróbowałem pewne rzeczy z gcc na Linuksie i tam można robić to, co sugeruję. Więc możesz mieć dwie biblioteki współdzielone, które zależą od siebie i mogą być budowane niezależnie od siebie.

Wiem, że zależności okrężne nie są dobre, ale to nie jest dyskusja, którą chcę mieć.

+0

Bardzo interesujące pytanie. Wiem, że coś takiego może się podobać, ale tak naprawdę nie wiem jak - myślę, że wiąże się to ze specjalnymi paramami wiersza poleceń do linkera JEDNEJ z bibliotek dll, a inna biblioteka dll powinna mieć prostą zależność. – Paulius

+0

@PM: Jeśli tak jest, to jest to coś, co będzie stałym problemem zapomnianym, źle skonfigurowanym, itd. – annakata

Odpowiedz

0

Nie można wykonać czyszczenia. Ponieważ oba zależą od siebie nawzajem, jeśli A się zmieni, to B musi zostać ponownie skompilowany. Ponieważ B zostało zrekompilowane, zmieniło się i A musi zostać zrekompilowany i tak dalej.

Jest to jeden z powodów, dla których zależność okrągła jest zła i czy chcesz, czy nie, nie możesz tego pominąć w dyskusji.

+0

Jeśli A, B nie musi być rekompilowane, ponieważ zależy mi tylko na podpisie A, nie na implementacji A. Jeśli obie biblioteki dll mają prezentację biblioteki, mogę ponownie skompilować nową funkcjonalność do jednego z dwóch i cały system nadal działa. –

+0

Wspomniałeś o pracy z References, więc założyłem, że pracujesz z Managed code. Kod zarządzany nie używa nagłówków z innych projektów i działa w ten sposób. –

7

głęboko współczuć sytuacji (jak wyjaśniono przez Edit), ale jako zwolennikiem robi właściwą rzecz, nie jest rzeczą, która działa Dotychczas, czy istnieje jakakolwiek możliwość w ogóle myślę, że trzeba refactor tych projektów.

Napraw problem, a nie objaw.

1

Jak o tym:

Projekt A

Klasa A publiczne Wbudowy C.IA

Public Function foo(ByVal value As C.IB) As Integer Implements C.IA.foo 
    Return value.bar(Me) 
End Function 

End Class

Projekt B

Publiczna klasa B Narzędzia C.IB

Public Function bar(ByVal value As C.IA) As Integer Implements C.IB.bar 
    Return value.foo(Me) 
End Function 

End Class

Projekt C

Public Interface IA 
    Function foo(ByVal value As IB) As Integer 
End Interface 

Public Interface IB 
    Function bar(ByVal value As IA) As Integer 
End Interface 

Projekt D

Sub Main() 

    Dim a As New A.A 
    Dim b As New B.B 

    a.foo(b) 

End Sub 
+0

Dzięki za szybkie odpowiedzi. To by działało, ale nie jest tym, co chcę robić. Szukam kompilatora, który pozwala mi to zrobić w czysty sposób, co zrobiłem, kompilując kilka razy i grając z zależnościami. –

0

Visual Studio wymusi zależności, ogólnie rzecz biorąc, od adresów funkcyjnych może się zmienić w nowo skompilowanej bibliotece DLL. Mimo że podpis może być taki sam, narażony adres może ulec zmianie.

Jednakże, jeśli zauważysz, że Visual Studio zazwyczaj zarządza zachowaniem tych samych adresów funkcji między kompilacjami, możesz użyć jednego z ustawień kompilacji "Tylko projekt" (ignoruje zależności). Jeśli to zrobisz i otrzymasz komunikat o błędzie, że nie można załadować biblioteki DLL zależności, a następnie po prostu odbuduj oba.

11

Powodem, dla którego działa na systemach uniksopodobnych, jest fakt, że wykonują rzeczywistą rozdzielczość łączenia w czasie ładowania. Wspólna biblioteka nie wie, skąd pochodzi jej definicja funkcji, dopóki nie zostanie załadowana do procesu. Minusem tego jest to, że ty też nie wiesz. Biblioteka może znajdować i wywoływać funkcje w dowolnej innej bibliotece (lub nawet w głównym pliku binarnym, który uruchomił proces w pierwszej kolejności). Domyślnie eksportowane jest również wszystko w bibliotece współdzielonej.

Windows tak nie działa. Wyeksportowane są tylko jawnie wyeksportowane rzeczy, a wszystkie importowanie musi zostać rozwiązane w czasie łączenia biblioteki, w którym to momencie określono tożsamość biblioteki DLL, która będzie dostarczać każdą importowaną funkcję. To wymaga biblioteki importu do połączenia.

Możesz jednak (przy dodatkowej pracy) obejść to. Użyj LoadLibrary, aby otworzyć dowolną bibliotekę DLL, którą chcesz, a następnie użyj GetProcAddress, aby zlokalizować funkcje, które chcesz wywołać. W ten sposób nie ma żadnych ograniczeń. Ale ograniczenia w normalnej metodzie są z jakiegoś powodu.

Jeśli chcesz przejść z bibliotek statycznych do bibliotek DLL, brzmi to tak, jak zakładasz, że powinieneś uczynić każdą bibliotekę statyczną biblioteką DLL. To nie jest twoja jedyna opcja. Dlaczego nie rozpocząć przenoszenia kodu do bibliotek DLL tylko wtedy, gdy identyfikujesz go jako samodzielny moduł, który pasuje do warstwowego projektu bez kolistości? W ten sposób możesz rozpocząć proces teraz, ale nadal atakować go kawałek po kawałku.

+0

Myślę, że opcja "Delay-Loaded" w Visual Studio rozwiązuje również pytanie OP, w taki sam sposób, jak napisał tutaj Daniel. – TCS

0

Musisz rozłączyć dwie biblioteki DLL, umieszczając interfejsy i implementację w dwóch różnych bibliotekach DLL, a następnie używając późnego wiązania, aby utworzyć instancję klasy.


// IFoo.cs: (build IFoo.dll) 
    interface IFoo { 
     void foo(int i); 
    } 

    public class FooFactory { 
     public static IFoo CreateInstance() 
     { 
     return (IFoo)Activator.CreateInstance("Foo", "foo").Unwrap(); 
     } 
    } 

// IBar.cs: (build IBar.dll) 
    interface IBar { 
     void bar(int i); 
    } 

    public class BarFactory { 
     public static IBar CreateInstance() 
     { 
     return (IBar)Activator.CreateInstance("Bar", "bar").Unwrap(); 
     } 
    } 

// foo.cs: (build Foo.dll, references IFoo.dll and IBar.dll) 
    public class Foo : IFoo { 
     void foo(int i) { 
     IBar objBar = BarFactory.CreateInstance(); 
     if (i > 0) objBar.bar(i -i); 
     } 
    } 

// bar.cs: (build Bar.dll, references IBar.dll and IFoo.dll) 
    public class Bar : IBar { 
     void bar(int i) { 
     IFoo objFoo = FooFactory.CreateInstance(); 
     if (i > 0) objFoo.foo(i -i); 
     } 
    } 

W "fabryce" klas nie są konieczne ze względów technicznych, ale jest to o wiele ładniejszy powiedzieć:

IFoo objFoo = FooFactory.CreateInstance(); 

w kodzie aplikacji niż:

IFoo objFoo = (IFoo)Activator.CreateInstance("Foo", "foo").Unwrap(); 

z powodu następujących powodów:

  1. można uniknąć "obsady" w kodzie aplikacji, która jest dobrą rzeczą
  2. Jeśli DLL, który obsługuje zmiany klasy, nie trzeba zmieniać wszystkich klientów, tylko fabryki.
  3. Nadpisywanie kodu nadal jest tworzone.
  4. W zależności od potrzeb należy wywoływać biblioteki DLL znane z kultury lub podpisane kluczem. W takim przypadku można dodać więcej parametrów do wywołania CreateInstance w fabryce w jednym miejscu.

- Kenneth Kasajian

0

Jedynym sposobem będziesz to obejść „czysto” (i używam terminu luźno) będzie wyeliminowanie jednego z statycznych zależnościach/link-czasowych i zmian do zależności w czasie wykonywania.

Może coś takiego:

// foo.h 
#if defined(COMPILING_BAR_DLL) 
inline void foo(int x) 
{ 
    HMODULE hm = LoadLibrary(_T("foo.dll"); 
    typedef void (*PFOO)(int); 
    PFOO pfoo = (PFOO)GetProcAddress(hm, "foo"); 
    pfoo(x); // call the function! 
    FreeLibrary(hm); 
} 
#else 
extern "C" { 
__declspec(dllexport) void foo(int); 
} 
#endif 

Foo.dll wyeksportuje funkcję. Bar.dll nie próbuje już importować funkcji; zamiast tego rozwiązuje adres funkcji w czasie wykonywania.

Rzuć własne ulepszenia obsługi błędów i wydajności.

+0

Ugh. Teraz sprawiasz, że koło staje się niewidoczne dla narzędzi analitycznych i innych programistów, ukrywa problem zamiast go rozwiązywać. – reinierpost

3

Możliwe jest użycie narzędzia LIB z plikami .EXP do "bootstrap" (kompilacja bez wcześniejszych plików .LIB) zestawu bibliotek DLL z odwołaniem cyklicznym, takim jak ten. Szczegóły: MSDN article.

Zgadzam się z innymi osobami, że tego rodzaju sytuacji należy unikać poprzez rewizję projektu.

Powiązane problemy