Вопрос

Сценарий

У меня есть две оболочки Microsoft Office: одна для 2003 года, другая для 2007 года.Поскольку одновременное использование двух версий Microsoft Office «официально невозможно» и не рекомендуется Microsoft, у нас есть две версии: одна с Office 2003, другая с Office 2007.Обертки компилируем отдельно.DLL включены в наше решение, каждая коробка имеет такой же checkout, но с «выгруженным» Office 2003 или 2007, поэтому он не пытается скомпилировать эту конкретную DLL.Если этого не сделать, при компиляции возникнут ошибки, поскольку библиотеки Office COM DLL недоступны.

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

Факты

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

Проблема

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

Вопрос

Можно ли скомпилировать DLL, которая будет работать с любой версия приложения, несмотря на то, что она «более старая»?Я читал кое-что о манифестах, но мне никогда не приходилось с ними взаимодействовать.Любые указатели будут оценены.

Секретная причина этого в том, что мы не меняли наши оболочки уже много лет, как и Microsoft с их древними API, однако мы перекомпилируем DLL, чтобы она соответствовала версии приложения на каждый релиз, который мы делаем.Я хотел бы автоматизировать этот процесс вместо того, чтобы полагаться на два машины.

Я не могу удалить DLL из проекта (ни одну из них), потому что есть зависимости.

Я мог бы создать третью «главную обертку», но пока об этом не думал.

Есть идеи?Кто-нибудь еще с таким же требованием?

ОБНОВЛЯТЬ

Уточнение:

У меня есть 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 «выгружена», и наоборот.

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

Это было полезно?

Решение

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

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

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-оболочке, гарантируя, что перехват будет установлен заранее.

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

Другие советы

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

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

Кроме того, я знаю, что это не помогает, но С# 4.0 с dynamic и/или «Нет PIA» может здесь помочь (в будущем;может быть).

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

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

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

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

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

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

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

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

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

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

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

Конечно, есть куда расти, но для меня это важно!

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top