Скомпилируйте независимую от версии DLL в .NET - PullRequest
9 голосов
/ 10 ноября 2008

Сценарий

У меня есть две оболочки вокруг Microsoft Office, одна для 2003 года и одна для 2007 года. Поскольку две версии Microsoft Office работают бок о бок "официально не возможно" и не рекомендуются Microsoft, у нас есть две коробки, одна с Office 2003 а другой с Office 2007. Мы компилируем обертки отдельно. DLL включены в наше решение, каждый блок имеет одинаковую проверку, но с Office 2003 или 2007 "выгружен", поэтому он не пытается компилировать эту конкретную DLL. Невыполнение этого условия приведет к возникновению ошибок при компиляции из-за недоступности библиотек Office COM.

Мы используем .NET 2.0 и Visual Studio 2008.

Факты

Так как Microsoft загадочным образом изменила API Office 2003 в 2007 году, переименовав и изменив некоторые методы ( sigh ), тем самым сделав их несовместимыми с предыдущими версиями, нам потребуется две оболочки. У нас есть каждая сборочная машина с активированным решением и одна Office DLL. Например: на компьютере с Office 2003 выгружена DLL-библиотека "Office 2007", поэтому она не компилируется. Другая коробка - та же самая идея, но наоборот. Все это потому, что у нас не может быть двух разных Office в одной коробке для целей программирования. (технически вы могли бы иметь два Office вместе, согласно Microsoft), но не для программирования и не без некоторых проблем.

Задача

Когда мы меняем версию приложения (например, с 1.5.0.1 на 1.5.0.2), нам нужно перекомпилировать DLL, чтобы она соответствовала новой версии приложения, это автоматически выполняется, поскольку в решение включена оболочка Office. , Поскольку в решении содержатся обертки, они наследуют версию APP, но мы должны сделать это дважды, а затем «скопировать» другую DLL на компьютер, который создает установщик. (Боль ...)

Вопрос * * 1023 Можно ли скомпилировать DLL, которая будет работать с любой версией приложения, несмотря на то, что она "старше"? Я читал кое-что о манифестах, но мне никогда не приходилось взаимодействовать с ними. Любые указатели будут оценены. Секретная причина этого заключается в том, что мы не меняли наши обертки «в возрасте», равно как и Microsoft с их древними API, но мы перекомпилируем DLL, чтобы соответствовать версии приложения в каждом выпуске. мы делаем. Я хотел бы автоматизировать этот процесс вместо того, чтобы полагаться на две машины. Я не могу удалить DLL из проекта (ни одну из них), потому что есть зависимости. Я мог бы создать третью "мастер-оболочку", но пока не думал об этом. Есть идеи? Кто-нибудь еще с таким же требованием? UPDATE

Разъяснение:

У меня есть 1 решение с N проектами.

«Приложение» + Office11Wrapper.dll + Office12Wrapper.dll.

Обе "оболочки" используют зависимости для приложения + другие библиотеки в решении (слой данных, бизнес-уровень, фреймворк и т. Д.)

У каждой оболочки есть ссылки на соответствующий пакет Office (2003 и 2007).

Если я скомпилировал и не установил Office 12, я получаю ошибки от Office12Wrapper.dll, не находящего библиотеки Office 2007. Итак, у меня есть две сборочные машины, одна с Office 2003, другая с Office 2007. После полного обновления SVN + компиляции на каждой машине мы просто используем office12.dll в «установщике», чтобы скомпилировать оболочку с «той же код, та же версия ".

Примечание. Машина сборки Office 2007 с «выгруженным» оберткой для Office 2003 и наоборот.

Заранее спасибо.

Ответы [ 5 ]

19 голосов
/ 12 ноября 2008

Когда распознаватель сборок .NET не может найти ссылочную сборку во время выполнения (в этом случае он не может найти конкретную версию DLL-оболочки, с которой связано приложение), его поведение по умолчанию - сбой и, по существу, сбой приложения. Однако это поведение можно переопределить, подключив событие AppDomain.AssemblyResolve. Это событие вызывается всякий раз, когда указанная сборка не может быть найдена, и дает вам возможность заменить другую сборку вместо отсутствующей (при условии, что они совместимы). Так, например, вы можете заменить старую версию DLL-оболочки, которую вы загружаете самостоятельно.

Лучший способ сделать это - добавить статический конструктор в основной класс приложения, которое перехватывает событие, например ::100100

using System.Reflection;

static Program()
{
    AppDomain.CurrentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs e)
    {
        AssemblyName requestedName = new AssemblyName(e.Name);

        if (requestedName.Name == "Office11Wrapper")
        {
            // Put code here to load whatever version of the assembly you actually have

            return Assembly.LoadFile("Office11Wrapper.DLL");
        }
        else
        {
            return null;
        }
    }
}

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

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

2 голосов
/ 10 ноября 2008

Я не уверен, что полностью следую всему, что вы заявили, но позвольте мне попробовать:

Похоже, у вас есть одно решение с 2 (?) Проектами. Одним из них является собственно приложение, а другим - оболочка для Office API. Тогда ваше приложение имеет ссылку на проект на вашу оболочку Office API. Я никогда раньше не программировал для офиса, но похоже, что API-интерфейсы программирования - это общий компонент, который может иметь только одну версию на компьютере (т. Е. 2003 или 2007, но не обе). И, возможно, именно в этом проблема, но поскольку у вас есть ссылка на проект, оболочка будет сначала скомпилирована, скопирована в каталог bin вашего приложения, где ваше приложение будет связано с этой сборкой оболочки. Это заставит манифест приложения специально запрашивать эту версию оболочки во время выполнения.

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

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

Или, аналогично идее Марка, вы можете извлечь интерфейс и вставить некоторые общие объекты в библиотеку Framework, а также закодировать свое приложение в соответствии с интерфейсом и общими объектами. Затем во время выполнения используйте отражение, чтобы загрузить сборку и создать нужную оболочку.

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

2 голосов
/ 10 ноября 2008

Просто мысль - не могли бы вы использовать TlbExp для создания двух сборок взаимодействия (с разными именами и сборками) и использовать интерфейс / фабрику для кодирования этих двух через ваш собственный интерфейс? Если у вас есть dll взаимодействия, вам не нужна зависимость от COM (за исключением, конечно, тестирования и т. Д.).

TlbImp имеет / asmversion для версии, так что это можно сделать как часть сценария сборки; но я уверен, что вам даже нужно это: просто убедитесь, что «конкретная версия» является ложной в ссылке (обозреватель решений)?

Кроме того - я знаю, что это не помогает, но C # 4.0 с dynamic и / или "No PIA" может помочь здесь (в будущем; возможно).

1 голос
/ 10 ноября 2008

Установка Office 2003 и 2007 параллельно на одном компьютере определенно возможна - мы делаем это в нашей организации даже на рабочих станциях конечных пользователей.

В этой связанной статье Microsoft рекомендует не делать этого для фактического использования. Но в вашем случае это, кажется, только для одной машины сборки, то есть вы не собираетесь фактически использовать любую версию Office на этой машине. В этом контексте я бы попытался выяснить, можно ли выполнить параллельную установку.

Мое предположение может быть неверным, и вы пытаетесь сделать это для машины каждого разработчика. В этом случае вы должны игнорировать этот ответ: -)

0 голосов
/ 16 июня 2009

Хорошая работа! Я просто собрал реализацию, основанную на представленной выше концепции, и она прекрасно работает:

static Assembly domain_AssemblyResolve(object sender, ResolveEventArgs args)
{
     string partialName = args.Name.Substring(0, args.Name.IndexOf(','));
     return Assembly.Load(new AssemblyName(partialName));
}

Конечно, есть место для совершенствования, но это помогает мне!

...