Ошибка «The switch could not bind to ‘interface name’ because it is already bound to another switch» при попытке сменить сетевую карту в настройках виртуальных сетей

Поймал забавную ошибку, когда эксперементировал с виртуальными сетями в Hyper-V RC1. На хостовом сервере было 2 сетевых интерфейса, Extrenal Network была подключена ко второму.

В результате некоторых действий, данный сетевой интерфейс был отключен. Как и ожидалось, External Network во всех виртуальных машинах перестала работать.

После этого мне захотелось сменить в настройках Virtual Network сетевой интерфейс на первый. После переключения и нажатия "применить" выскакивает вот такая ошибка: "Setup switch failed. The switch could not bind to ‘имя сетевого интерфейса’ because it is already bound to another switch":

image

Проблема решается достаточно просто — необходимо в настройках интерфейса отключить привязку протокола "Microsoft Virtual Network Switch Protocol" и повторить настройку виртуальных сетей в Hyper-V:

image

Реклама

Доступ к локальным ресурсам клиента Sharepoint

Иногда возникает потребность получить доступ из кода к ресурсам, расположенным на компьютере клиента WSS/Sharepoint. У меня такая необходимость возникла в связи с тем, что необходимо было подписать вложение в списке Sharepoint, а как известно, сертификат для подписи должен содежать в себе закрытый ключ, который по соображениям безопасности нельзя передавать по сети.
Также известно, что все браузеры блокируют доступ к локальным ресурсам, по тем же соображениям безопасности.
Т.к. я всё-же больше админ, нежели программист, такие понятия как ActiveX в браузере и иже с ними для меня просто пугающие. Но задачу выполнять надо.
 
Решения я подсмотрел (как не странно ;) ) у Microsoft. По работе мне часто приходить иметь дело с Microsoft SQL Server 2005 (название полное, большой привет dg ;) ). Одним их сервисов этого пакета является Repoting Services. Он позволяет публиковать отчеты и получать к ним доступ по средствам HTTP/HTTPS. Так вот в этом сервисе встроен редактор отчетов, который запускается прямо из браузра и написан, судя по внешнему виду и скорости его работы, явно на .NET.
 
Как такое реализовать пришлось искать не долго — технология ClickOnce от Microsoft нам в помошь. Данная технология позволяет публиковтаь приложения, обновлять их и отслеживать версионность. Причем на ряду с обычной публикацией на сетевой или локальный диск, присутствует возможность публиковать приложение на web-сайты (локалььный IIS или удаленный сервер через FrontPage Server Extensions) и FTP-разделы. Опыт использования ClickOnce для "обычного" распространения у меня был, поэтому долго учиться не пришлось.
 
Я создал приложение для подписывания PDF-файлов в Microsoft Visual Studio 2008, и тогда встал ряд вопросов:
 — куда и как его опубликовать.
 — как передать параметры этому приложению.
 
После решения этих проблем я был очень порадован легкостью, с которой они решились, что случается не часто при работе с Sharepoint.
Опубликование прошло просто "на ура". Shrepoint полностью поддерживает функции по передачи файлов FrontPage Server Extensions, поэтому Visual Studio 2008 легко может использовать ClockOnce совместно с WSS/Sharepoint. Достаточно в свойствах проекта на вкладке "Publish" указать адрес узла WSS/Sharepoint и директорию для приложения.
Единственный минус — в "Центре Администрирования" WSS/Sharepoint необходимо изменить параметры заблокированных файлов (Операции — Заблокированные типы файлов, _admin/BlockedFileType.aspx) — убрать из списка EXE-файлы. Это конечно серьезный удар по безопасности, но за удобство всегда приходится чем-то платить. Также необходимо удостовериться, что у вас есть права на операции через FrontPage и имя папки уже не занято.
Второй вопрос — как передавать параметры нашему приложению. Оказалось, что Microsoft позаботился и об этом. При вызове приложения, опубликованного через ClickOnce, ему можно передававать параметры обычным веб-запросом. Единственное, что нужно сделать, в Visual Studio 2008 в свойствах проекта на вкладке Publish зайти в опции (кнопка Options) и на появившейся форме отметить "галочку" "Allow URL parametrs to be passed to application".
После опубликования доступ к приложению производиться с помощью браузера и обычного HTTP-запроса, н-р так:
Чтобы получить доступ к этим параметрам из кода, в примерах MSDN (How to: Retrieve Query String Information in a ClickOnce Application) приводится вот такая функция:
 
        private NameValueCollection GetQueryStringParameters()
        {
            NameValueCollection nameValueTable = new NameValueCollection();
            /*
             * Проверяем, что приложение распространено через ClickOnce
             * Не смотря на название свойства, оно означается, что приложение запущено 
             * через любой из методов распространения ClickOnce
             */
            if (ApplicationDeployment.IsNetworkDeployed)
            {
                //Собственно самая интересная часть всей функции. Подробность в MSDN.
                string queryString = ApplicationDeployment.CurrentDeployment.ActivationUri.Query;
                //Используется стандартная функция по работе с HTTP, 
                //которая за одним позволяет избежать атак подстановкой запросов
                nameValueTable = System.Web.HttpUtility.ParseQueryString(queryString);
            }
            else
            {
                //Тут можно н-р анализировать параметры, которые были переданы приложению обычным путем
            }
 
            return (nameValueTable);
        }
 
 
Если выполнить приложение по ссылке выше, то данная функция вернем переменную с парами значений:
КЛЮЧ          ЗНАЧЕНИЕ
param1        4
param2        null
param3        1,2
 
Такое приложение легко встроить в WSS\Sharepoint по средствам CustomActions (см. WSS SDK). Вот пример CustomAction-а, который в меню редактирования элемента добавляет новый пункт и при его нажатии вызывает приложение, опубликованное через ClickOnce:
<CustomAction
  Id="AccointItem.DigitalSign"
  ShowInLists="TRUE"
  RegistrationType="ContentType"
  RegistrationId="0x0100e22559554ae6489abe6c615bc9a0e3cb"
  Description="Подписать счет цифровой подписью"
  Title="Подписать"
  Sequence="20"
  ImageUrl="/_layouts/images/CRT16.GIF"
  Location="EditControlBlock"
  Rights="EditListItems"
    >
  <UrlAction Url="http://wssserver/AccountPDFSign/AccountPDFSign.application?ServerName={SiteUrl}&amp;listItemIDs={ItemId}&amp;listGUID={ListId}"/>
</CustomAction>
 
Хотелось бы отметить, что опубликовывая приложение через WSS/Sharepoint мы получаем много преимуществ:
 — его резервная копия содается вместе с резервными копиями самого WSS/Sharepoint
 — приложение подчиняется тем же правилам и ведет себя также, как и любое другое содержимое WSS/Sharpeoint 

Изменение элемента списка без вызова событий OnItemChanged

Иногда (я бы даже сказал часто) необходима реализовать следующую конструкцию: в wokflow имеется активность (activity) OnWorkflowItemChanges или OnTaskChanged. В этой активности производится какая-то проверка, и по результатам проверки присваивается значение какому-либо полю текущего элемента.
Чтобы было понятнее, приведу пример.
Имеется список со следующими полями:

  • Наименование
  • Статус (Новая, утверждена первым, уверждена вторым, отклонена)
  • Примечание
В списке включена опция "требовать утверждения". Необходимо, чтобы каждый элемент утверждали два человека (для примера пусть это будет один и тот же человек, просто он будет утверждать элемент два раза)
Создан Workflow:
image
whileApproved проверяет состояние переменной this.itemNotApproved.
logToHistoryListActivity записывает в журнал рабочего процесса значение поля "Статус".
Нам необходимо:
— проверить, что элемент утвержден или отклонен
— присвоить соотвественный статус
Вот код, который должен это делать:
            switch (workflowProperties.Item.ModerationInformation.Status)
            {
                case SPModerationStatusType.Approved:
                    if ((string)workflowProperties.Item["Статус"] == "Новая")
                    {
                        workflowProperties.Item["Статус"] = "Утверждена первым";
                        this.itemNotApproved = true;

                        workflowProperties.Item.ModerationInformation.Status = SPModerationStatusType.Pending;

                        workflowProperties.Item.Update();
                    }
                    else
                        if ((string)workflowProperties.Item["Статус"] == "Утверждена вторым")
                        {
                            workflowProperties.Item["Статус"] = "Утверждена";
                            this.itemNotApproved = false;
                            workflowProperties.Item.Update();
                        }
 
                    break;
                case SPModerationStatusType.Denied:
                    workflowProperties.Item["Статус"] = "Отклонена";
                    this.itemNotApproved = false;
                    workflowProperties.Item.Update();
                    break;
            }

Если вы его выполните, то работать будет всё так, как и ожидалось. За одиним маленьким исключением.
Допустим, вы создали элемент, и следом его утвердили. Затем утвердили его еще раз.
Если Вы загляните в журнал рабочего процесса, то увидите, что в нем есть 4 записи:
Статус = Утверждена первым
Статус = Утверждена первым
Статус = Утверждена вторым
Статус = Утверждена вторым
 
Возникает вопрос — почему именно 4? Ответ просто — мы ведь изменили элемент, вызвав метод Update(), и как следствие, каждый раз вызываем новое событие OnItemChanged. У класса SPListItem есть метод UpdateOverwriteVersion(), но и он вызывает событие OnItemChanged.
Возникает вопрос — что же делать? Ответ также прост, как и не очевиден. Если бы мы делали подобную конструкцию в отработчики события списка с помощью features (н-р события того же события OntItemChanged, но уже списка), то всё было бы просто — там все отработчики наледованы от класса SPItemEventReceiver, который имеет методы DisableEventFiring() и EnableEventFiring(), смысл работы которых ясен из названия. Проблема в том, что workflow понятия не имеет об этом классе.
Но есть решение, как исправить сею несправедливость. Нам необходимо создать новый класс, наследумый от SPItemEventReceiver, и создать в нем всего одну функцию, которая на вход будет принимать экземпляр класса SPListItem, выключать генерацию событий с помощью DisableEventFiring(), обновлять элемент, а затем включать генерацию событий с помощью EnableEventFiring().
Вот примерный код такого класса:
class EmptyItemReceiver : SPItemEventReceiver
    {
        public void UpdateDisableEventFiring(SPListItem item)
        {
            this.DisableEventFiring();
            item.SystemUpdate(false);
            this.EnableEventFiring();
        }
 
    }

 
Всё, что нам осталось, это создать экземпляр созданного нами класса, и использовать его функцию UpdateDisableEventFiring.
После всех изменений, код должен быть таким:
EmptyItemReceiver _eventReceiver = new EmptyItemReceiver();
switch (workflowProperties.Item.ModerationInformation.Status)
{
   case SPModerationStatusType.Approved:
           if ((string)workflowProperties.Item["Статус"] == "Новая")
           {
               workflowProperties.Item["Статус"] = "Утверждена первым";
               this.itemNotApproved = true;

               workflowProperties.Item.ModerationInformation.Status = SPModerationStatusType.Pending;

               _eventReceiver.UpdateDisableEventFiring(workflowProperties.Item);
           }
           else
           if ((string)workflowProperties.Item["Статус"] == "Утверждена вторым")
           {
               workflowProperties.Item["Статус"] = "Утверждена";
               this.itemNotApproved = false;
               _eventReceiver.UpdateDisableEventFiring(workflowProperties.Item);
           }
 
       break;
   case SPModerationStatusType.Denied:
        workflowProperties.Item["Статус"] = "Отклонена";
        this.itemNotApproved = false;
        workflowProperties.Item.Update();
        break;
}

 
И после этих изменений всё будет работать так, как ожидается.
 
На последок покажу для тех, кто подумал, что это ерундовая проблема, вот такой пример.
Сделаем так, что бы в поле примечание вносились данные о том, кто и когда изменил элемент. Добавим такие строки в onWorkflowItemChanged_Invoked:

workflowProperties.Item["Примечание"] = String.Format("Изменен {0} {1}",workflowProperties.OriginatorUser.Name,DateTime.Now().ToString());

workflowProperties.Item.Update();

и запустим рабочий процесс. Эффект будет убийственный для системы: workflow просто зациклиться, ведь каждый раз будет вызываться событие OnWorkflowItemChanged, и в нем каждый раз будет происходить изменение элемента. Хорошо, что разработчики позаботились и об этом, и таких итераций будет всего 10.

Одна из реализаций разрешений на уровне столбцов в WSS

В поисках вечного и доброго нашел очень не плохое решение по реализации разрешений на уровне столбцов в WSS:
 
Данное решение устанавливается как feature и добаляет ко всем основным спискам новую опцию "Display Settings", которая позволяет настроить отображение столбцов на каждой из форм создания, редактирования и просмотра элементов списка в зависимости от принадлжености к определенной группе:
Плюсы:
 — легкая установка
 — работает с русскими версиями
Минусы:
 — не работает с библиотеками документов
 — работает с ограниченным кол-вом списков (жесткий набор ID списков)
 

Бесплатный Microsoft Search Server 2008

Microsoft сделала шаг, который лично меня очень радует: выпустила бесплатную редакцию Search Server-а, основанного на WSS3. Search Server представляет собой единую точку входа для поиска и индексирования информации из различных источников — файловые ресурсы, сайты Sharepoint (как WSS, так и полной Sharepoint Server), общие папки Exchange, репозитарии Lotus. Имеется возможность добавлять собственные инструменты для поиска.
Имеется русский интерфейс.
Посмотреть подробнее можно здесь (ENG).