круговые зависимости между dll с визуальной студией

У меня есть круговая зависимость между двумя функциями. Я хотел бы, чтобы каждая из этих функций находилась в своей собственной dll. Можно ли построить это с Visual Studio?

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

-> должен скомпилироваться в foo.dll

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

-> должен скомпилировать в bar.dll

Я создал два проекта в visual studio, один для foo и один для бара. Поиграв с 'References' и скомпилировав несколько раз, мне удалось получить те dll, которые я хочу. Я хотел бы знать, однако, предлагает ли Visual Studio способ сделать это чистым способом.

Если foo изменяется, bar не нужно перекомпилировать, потому что я зависит только от сигнатуры bar, а не от реализации bar. Если на обоих dll есть библиотека lib, я могу перекомпилировать новую функциональность в любую из двух, и вся система по-прежнему работает.

Причина, по которой я пытаюсь это сделать, заключается в том, что у меня есть устаревшая система с круговыми зависимостями, которая в настоящее время статически связана. Мы хотим двигаться к DLL по разным причинам. Мы не хотим ждать, пока не очистим все циклические зависимости. Я думал о решениях и попробовал кое-что с gcc на linux, и там можно сделать то, что я предлагаю. Таким образом, вы можете иметь две общие библиотеки, которые зависят друг от друга и могут быть построены независимо друг от друга.

Я знаю, что круговые зависимости не очень хорошая вещь, но я не хочу обсуждать это.

12.12.2008 13:59:39
Очень интересный вопрос Я знаю, что что-то может быть, как это возможно, но я не знаю, как - я думаю, что это связано с некоторыми специальными параметрами командной строки для компоновщика одной из DLL, а другая DLL должна иметь простую зависимость.
Paulius 12.12.2008 15:04:42
@PM: Если это так, то это будет постоянная проблема, о которой забывают, неправильно конфигурируют и т. Д.
annakata 12.12.2008 15:19:56
9 ОТВЕТОВ

Это не возможно сделать чисто. Поскольку оба они зависят друг от друга, если A изменяется, то B необходимо перекомпилировать. Поскольку B был перекомпилирован, он изменился, и A необходимо перекомпилировать и так далее.

Это одна из причин, по которой круговые зависимости являются плохими, и, хотите вы этого или нет, вы не можете оставить это вне обсуждения.

0
12.12.2008 14:31:35
Если A изменится, B не нужно перекомпилировать, потому что я зависим только от сигнатуры A, а не от реализации A. Если оба dll имеют библиотеку lib, я могу перекомпилировать новые функциональные возможности в любой из двух и в целом Система все еще работает.
Kris Steegmans 12.12.2008 14:46:25
Вы упомянули работу с ссылками, поэтому я предположил, что вы работаете с управляемым кодом. Управляемый код не использует заголовки из других проектов и работает таким образом.
Rob Prouse 12.12.2008 16:28:32

Я глубоко сочувствую вашей ситуации (как разъяснено вашим редактированием), но как твердый сторонник делать правильные вещи, а не то, что работает на данный момент , если есть какая-либо возможность, я думаю, что вам нужно реорганизовать эти проекты.

Исправить проблему, а не симптом.

7
12.12.2008 15:18:28

Как насчет этого:

Проект А

Общественный класс А реализует C.IA

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

Конечный класс

Проект Б

Открытый класс B реализует C.IB

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

Конечный класс

Проект С

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

Проект D

Sub Main()

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

    a.foo(b)

End Sub
1
12.12.2008 15:23:55
Спасибо за быстрые ответы. Это будет работать, но это не то, что я хочу сделать. Я ищу трюк с компилятором, который позволяет мне делать чисто, что я делал, компилируя несколько раз и играя с зависимостями.
Kris Steegmans 12.12.2008 15:33:38

В общем случае Visual Studio будет применять зависимости, поскольку адреса функций могут изменяться во вновь скомпилированной DLL. Даже если подпись может совпадать, открытый адрес может измениться.

Однако, если вы заметили, что Visual Studio обычно сохраняет одинаковые адреса функций между сборками, вы можете использовать один из параметров сборки «Только проект» (игнорирует зависимости). Если вы сделаете это и получите ошибку о невозможности загрузить DLL зависимостей, просто пересоберите обе.

0
22.12.2008 20:55:24

Причина, по которой он работает в Unix-подобных системах, заключается в том, что они выполняют фактическое разрешение соединения во время загрузки. Совместно используемая библиотека не знает, откуда придет определение ее функции, пока не будет загружена в процесс. Недостатком этого является то, что вы тоже не знаете. Библиотека может находить и вызывать функции в любой другой библиотеке (или даже в основном двоичном файле, который первым запустил процесс). Также по умолчанию все в общей библиотеке экспортируется.

Windows не работает так вообще. Экспортируются только явно экспортированные вещи, и все операции импорта должны быть разрешены во время компоновки библиотеки, после чего была определена идентичность библиотеки DLL, которая будет предоставлять каждую импортированную функцию. Для этого требуется библиотека импорта для связи.

Тем не менее, вы можете (с некоторой дополнительной работой) обойти это. Используйте, LoadLibraryчтобы открыть любую понравившуюся DLL, а затем используйте, GetProcAddressчтобы найти функции, которые вы хотите вызвать. Таким образом, нет никаких ограничений. Но ограничения в обычном методе существуют по причине.

Поскольку вы хотите перейти от статических библиотек к DLL, похоже, вы предполагаете, что вы должны превратить каждую статическую библиотеку в DLL. Это не единственный вариант. Почему бы не начать перемещение кода в библиотеки DLL только тогда, когда вы идентифицируете его как автономный модуль, который вписывается в многоуровневый дизайн без округлости? Таким образом, вы можете начать процесс сейчас, но все равно атаковать его по частям.

13
22.12.2008 21:16:54
Я думаю, что опция «Delay-Loaded» в Visual studio также решает вопрос OP, так же, как Даниил писал здесь.
TCS 3.04.2013 19:52:40

Вам необходимо разделить две библиотеки DLL, поместив интерфейсы и реализацию в две разные библиотеки DLL, а затем использовать позднюю привязку для создания экземпляра класса.


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

Классы «Фабрика» технически не нужны, но гораздо приятнее сказать:

IFoo objFoo = FooFactory.CreateInstance();

в коде приложения, чем:

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

по следующим причинам:

  1. Вы можете избежать «приведения» в коде приложения, что является хорошей вещью
  2. Если DLL, в которой размещается класс, изменяется, вам не нужно менять всех клиентов, только фабрику.
  3. Завершение кода все еще работает.
  4. В зависимости от ваших потребностей вы должны вызывать DLL-библиотеки с учетом культуры или ключом, и в этом случае вы можете добавить дополнительные параметры к вызову CreateInstance на заводе в одном месте.

- Кеннет Касаджян

0
4.09.2009 00:49:47

Единственный способ обойти это «чисто» (а я использую термин «свободно») - это устранить одну из статических / зависимостей времени соединения и изменить ее на зависимость во время выполнения.

Может быть, что-то вроде этого:

// 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 будет экспортировать функцию. Bar.dll больше не пытается импортировать функцию; вместо этого он разрешает адрес функции во время выполнения.

Сверните свои собственные ошибки обработки и улучшения производительности.

0
4.09.2009 01:04:28
Тьфу. Теперь вы делаете круг невидимым для инструментов анализа и коллег-программистов, скрывая проблему, а не решая ее.
reinierpost 11.06.2010 13:54:26

Можно использовать утилиту LIB с файлами .EXP для «начальной загрузки» (сборки без предыдущих файлов .LIB) набора DLL с циклической ссылкой, такой как эта. См. Статью MSDN для деталей.

Я согласен с другими людьми выше, что такого рода ситуации следует избегать путем пересмотра дизайна.

5
28.05.2011 20:39:19

Этот вопрос был первым в моем поиске «циклической зависимости dll», и даже если ему 10 лет, это позор, что большинство ответов указывают на «рефакторинг», который является очень очень очень глупым советом для большого проекта и не был вопрос в любом случае.

Поэтому я должен указать, что циклическая зависимость не так опасна. Они совершенно нормально работают в Unix / Linux. Они упоминаются во многих статьях MSDN как возможные ситуации с путями обхода . Это происходит в JAVA (компилятор решает это путем muilti-pass компиляции). Сказать, что рефакторинг - это единственный способ, это все равно, что запретить «друзей» на занятиях.

Так что хитрость заключается в том, чтобы использовать двухпроходную компиляцию: первая, которая создаст просто 'import-libs', и вторая, которая сама сгенерирует dll.

Для визуальной студии и любой графической компиляции это, вероятно, все еще что-то странное. Но если вы создаете свои собственные файлы Makefile и имеете лучший контроль над процессом компоновки и флагами, то это не так сложно сделать.

Используя OP-файлы экспертизы и синтаксис mingw-gcc в качестве концепции для демонстрации (поскольку я проверил это и точно знаю, что он хорошо работает в Windows), необходимо: - скомпилировать / связать a.lib и b.lib без указания циклических библиотек:

g++ -shared -Wl,--out-implib=a.lib -o a.dll a.obj //without specifying b.lib  
g++ -shared -Wl,--out-implib=b.lib -o b.dll b.obj //without specifying a.lib

... покажет «неопределенные ошибки референции» и не предоставит dll-s, но он создаст a.libи b.lib, что мы хотим для связывания второго прохода:

g++ -shared -Wl,--out-implib=a.lib -o a.dll a.obj b.lib  
g++ -shared -Wl,--out-implib=b.lib -o b.dll b.obj a.lib

и результат a.dllи b.dllс довольно чистым методом. Использование компиляторов Microsoft должно быть простым, с их советом переключить link.exe на lib.exe (не проверял, кажется, даже чище, но, вероятно, сложнее сделать что-то продуктивное по сравнению с инструментами mingw + make).

0
26.02.2019 17:50:36