2008-12-12 5 views
8

Ich habe eine zirkuläre Abhängigkeit zwischen zwei Funktionen. Ich möchte, dass jede dieser Funktionen in einer eigenen DLL liegt. Ist es möglich, dies mit Visual Studio zu bauen?zirkuläre Abhängigkeiten zwischen DLLs mit Visual Studio

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

-> sollte in foo.dll kompilieren

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

-> sollte in bar.dll kompilieren

ich zwei Projekte in Visual Studio erstellt haben, eine für foo und eine für Bar . Indem ich mit den 'Referenzen' gespielt und einige Male kompiliert habe, habe ich es geschafft, die DLLs zu bekommen, die ich möchte. Ich würde jedoch gerne wissen, ob Visual Studio eine Möglichkeit bietet, dies auf eine saubere Art und Weise zu tun.

Wenn foo ändert, muss bar nicht neu kompiliert werden, da ich nur auf die Signatur von bar angewiesen bin, nicht auf die Implementierung von bar. Wenn beide DLLs die lib vorhanden haben, kann ich eine neue Funktionalität in beide kompilieren und das ganze System funktioniert immer noch.

Der Grund, warum ich das versuche, ist, dass ich ein Altsystem mit zirkulären Abhängigkeiten habe, das derzeit statisch verknüpft ist. Wir wollen aus verschiedenen Gründen auf dlls zugehen. Wir wollen nicht warten, bis wir alle zirkulären Abhängigkeiten aufgeräumt haben. Ich habe über Lösungen nachgedacht und einige Sachen mit gcc auf Linux ausprobiert und dort ist es möglich zu tun, was ich vorschlage. Sie können also zwei gemeinsam genutzte Bibliotheken verwenden, die voneinander abhängen und unabhängig voneinander erstellt werden können.

Ich weiß, dass zirkuläre Abhängigkeiten keine gute Sache zu haben sind, aber das ist nicht die Diskussion, die ich haben möchte.

+0

Sehr interessante Frage. Ich weiß, dass etwas wie das möglich ist, aber ich weiß nicht wirklich wie - ich denke, es beinhaltet einige spezielle Kommandozeilenparameter für den Linker von einer der DLLs und die andere DLL sollte eine einfache Abhängigkeit haben. – Paulius

+0

@ PM: Wenn das der Fall ist, dann ist es die Art der Sache, die ein konstantes Problem vergessen wird, falsch konfiguriert, etc .. – annakata

Antwort

0

Es ist nicht möglich, sauber zu machen. Weil beide voneinander abhängen, wenn A sich ändert, muss B neu kompiliert werden. Weil B neu kompiliert wurde, hat es sich geändert und A muss neu kompiliert werden und so weiter.

Das ist ein Teil des Grundes, warum zirkuläre Abhängigkeiten schlecht sind und ob Sie wollen oder nicht, können Sie das nicht aus der Diskussion lassen.

+0

Wenn A ändert, muss B nicht neu kompiliert werden, weil ich nur auf die Signatur angewiesen bin von A, nicht von der Implementierung von A. Wenn beide DLLs die lib vorhanden haben, kann ich neue Funktionalität in beide kompilieren und das ganze System funktioniert immer noch. –

+0

Sie haben erwähnt, dass Sie mit Referenzen arbeiten, daher nahm ich an, dass Sie mit verwaltetem Code arbeiten. Verwalteter Code verwendet nicht die Header aus anderen Projekten und funktioniert auf diese Weise. –

7

ich mit Ihrer Situation tief sympathisieren (wie von Ihrem bearbeiten geklärt), sondern als fester Gläubiger in die richtige Sache zu tun, nicht das, was jetzt funktioniert, wenn es eine Möglichkeit, überhaupt ist ich glaube, Sie müssen refactor diese Projekte.

Fix das Problem nicht das Symptom.

1

Wie sei es damit:

Projekt A

öffentliche Klasse A Implementiert 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

Öffentliche Klasse B Implementiert 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

Danke für die schnellen Antworten. Das würde funktionieren, aber das ist nicht das, was ich tun möchte. Ich suche nach dem Compiler-Trick, der es mir erlaubt, auf eine saubere Art und Weise zu arbeiten, was ich getan habe, indem ich ein paar mal kompiliert habe und mit den Abhängigkeiten gespielt habe. –

0

Visual Studio die Abhängigkeiten erzwingen wird, allgemein gesprochen, da Funktionsadressen kann sich innerhalb der neu kompilierten DLL ändern. Obwohl die Signatur identisch sein kann, kann sich die exponierte Adresse ändern.

Wenn Sie jedoch feststellen, dass Visual Studio in der Regel die gleichen Funktionsadressen zwischen den Builds verwaltet, können Sie eine der Buildeinstellungen "Nur Projekt" verwenden (ignoriert Abhängigkeiten). Wenn Sie dies tun und eine Fehlermeldung erhalten, dass die Abhängigkeits-DLL nicht geladen werden kann, erstellen Sie beide einfach neu.

11

Der Grund, warum es auf Unix-ähnlichen Systemen funktioniert, ist, dass sie die tatsächliche Verbindungsauflösung zur Ladezeit durchführen. Eine gemeinsam genutzte Bibliothek weiß nicht, woher ihre Funktionsdefinition kommt, bis sie in einen Prozess geladen wird. Der Nachteil davon ist, dass Sie es auch nicht wissen. Eine Bibliothek kann Funktionen in jeder anderen Bibliothek finden und aufrufen (oder sogar in der Hauptbinärdatei, die den Prozess gestartet hat). Außerdem wird standardmäßig alles in einer gemeinsam genutzten Bibliothek exportiert.

Windows funktioniert überhaupt nicht so. Nur explizit exportierte Objekte werden exportiert, und alle Importe müssen zur Bibliotheksverbindungszeit aufgelöst werden. Zu diesem Zeitpunkt wurde die Identität der DLL ermittelt, die jede importierte Funktion bereitstellt. Dies erfordert eine Importbibliothek, mit der verlinkt werden kann.

Allerdings können Sie (mit etwas mehr Arbeit) umgehen. Verwenden Sie LoadLibrary, um eine beliebige DLL zu öffnen, und verwenden Sie dann GetProcAddress, um die Funktionen zu finden, die Sie aufrufen möchten. Auf diese Weise gibt es keine Einschränkungen. Aber die Beschränkungen in der normalen Methode gibt es aus einem Grund.

Da Sie von statischen Bibliotheken zu DLLs wechseln möchten, klingt es so, als ob Sie davon ausgehen, dass Sie jede statische Bibliothek zu einer DLL machen sollten. Das ist nicht deine einzige Option. Warum fangen Sie nicht an, Code nur dann in DLLs zu verschieben, wenn Sie ihn als eigenständiges Modul identifizieren, das in ein mehrschichtiges Design ohne Rundheit passt? Auf diese Weise können Sie den Prozess jetzt beginnen und ihn immer noch Stück für Stück angreifen.

+0

Ich denke, dass die Option "Delay-Loaded" in Visual Studio auch die Frage von OP löst, genau wie Daniel hier geschrieben hat. – TCS

0

Sie müssen die zwei DLLs entkoppeln, die Schnittstellen und die Implementierung in zwei verschiedenen DLLs platzieren und dann die späte Bindung verwenden, um die Klasse zu instanziieren.


// 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); 
     } 
    } 

Die "Factory" Klassen sind technisch nicht notwendig, aber es ist viel schöner zu sagen:

IFoo objFoo = FooFactory.CreateInstance(); 

in Anwendungscode als:

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

wegen der folgenden Gründe:

  1. Sie können eine "Besetzung" im Anwendungscode vermeiden, was eine gute Sache ist
  2. Wenn Die DLL, die die Klasse hostet, muss nicht alle Clients ändern, nur die Factory.
  3. Code-Vervollständigung noch wroks.
  4. Je nach Ihren Anforderungen müssen Sie Culture-Aware- oder Key-Signed-DLLs aufrufen. In diesem Fall können Sie den CreateInstance-Aufruf in der Factory an einem Ort mit weiteren Parametern versehen.

- Kenneth Kasajian

0

Die einzige Möglichkeit, um dieses „sauber“ bekommen (und ich verwende den Begriff lose) wird eine der statischen/link-Zeitabhängigkeiten zu beseitigen und Veränderung es zu einer Laufzeitabhängigkeit.

Vielleicht so etwas wie folgt aus:

// 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 wird die Funktion exportieren. Bar.dll versucht nicht länger, die Funktion zu importieren; Stattdessen löst es die Funktionsadresse zur Laufzeit auf.

Rollen Sie Ihre eigenen Fehlerbehandlung und Leistungsverbesserungen.

+0

Ugh. Jetzt machen Sie den Kreis für Analysetools und andere Programmierer unsichtbar und verstecken das Problem, anstatt es zu lösen. – reinierpost

3

Es ist möglich, das LIB-Dienstprogramm mit .EXP-Dateien zu verwenden, um eine Reihe von DLLs mit einem Zirkelverweis wie diesem zu "bootstrappen" (Build ohne vorherige LIB-Dateien). Einzelheiten finden Sie unter MSDN article.

Ich stimme mit anderen Leuten überein, dass diese Art von Situation vermieden werden sollte, indem das Design überarbeitet wird.