Pull to refresh

Обеспечение обратной совместимости .NET-приложений при использовании WinRT

Reading time3 min
Views6.9K
Создание Windows Runtime (WinRT) в качестве API, с одной стороны, можно только приветствовать, так как предыдущий — WinAPI — особой простотой и человеколюбием не отличался. С другой стороны, в полный рост при этом всплывает проблема обратной совместимости. Что делать, если нужно заиспользовать какую-то приятную мелочь, появившуюся в Win8, но при этом не терять совместимости с Win7, все еще бодро шагающей в строю?

Официальный MSDN к этому относится весьма однозначно: если нужен WinRT, то забываем про версии Windows, старше восьмерки; если нужно поддерживать всякое старье, то собираем приложение отдельно без упоминания об WinRT. Такие вот простые и незатейливые парни работают в Microsoft. Тем не менее, решение проблемы, причем достаточно простое, удалось отыскать.

Суть в том, что мы можем всю нужную для счастья имплементацию, требующую WinRT, отнести в отдельную сборку, и в случае необходимости пытаться подгрузить ее динамически: удается — замечательно, ну а не получается — смиряемся и подсовываем заглушку с тем же интерфейсом.

Допустим, у нас возникло желание, пользуясь WinRT, читать значения датчика освещенности ноутбука/планшета. За это отвечает класс LightSensor из пространства имен Windows.Devices.Sensors. В таком случае диаграмму классов можно представить следующим образом:
image

Interfaces — самый обыкновенный Class Library, содержащий интерфейс IWinRTAccessor, возвращающий своим единственным методом GetLightValue ровно то, что нам нужно, а именно уровень освещенности в случае наличия сенсора и null в случае его отсутствия. Там же объявлена заглушка FakeWinRTAccessor, реализующая этот интерфейс, всегда возвращающая null. Кроме того здесь же расположен класс WinRTAccessorFactory, чей фабричный метод инстанцирует нужную реализацию IWinRTAccessor. Самая примитивная реализация этого фабричного метода выглядит так:
        public static IWinRTAccessor GetAccessor()
        {
            if (_winRtAccessor == null)
            {
                try
                {
                    String path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), WinRTAccessorAssemblyName);
                    Assembly assembly = Assembly.LoadFile(path);
                    Type type = assembly.GetTypes().First(t => t.GetInterface(typeof (IWinRTAccessor).Name) != null);
                    _winRtAccessor = (IWinRTAccessor) Activator.CreateInstance(type);
                }
                catch
                {
                    _winRtAccessor = new FakeWinRTAccessor();
                }
            }
            return _winRtAccessor;
        }


WinRTLib — не менее обыкновенный Class Library (на имя которого и указывает константа WinRTAccessorAssemblyName), в котором сосредоточена логика работы с WinRT, однако для его поддержки нужно пойти на небольшую хитрость: прописать в файле проекта (.csproj) следующий Property Group:

<PropertyGroup>
<targetplatformversion>8.0</targetplatformversion>
</PropertyGroup>

И уже только после этого добавлять Windows Core References. Нас в данном случае интересует Windows.Devices:


Сам метод, извлекающий показания датчика освещенности, спасибо новому API, весьма прост:
        public Single? GetLightValue()
        {
            LightSensor light = LightSensor.GetDefault();

            if (light != null)
                return light.GetCurrentReading().IlluminanceInLux;
            
            return null;
        }


В принципе, этого бы и хватило, если не отложенная компиляция: в более ранних версиях Windows эта сборка имеет все шансы корректно загрузиться и инстанцировать экземпляр RealWinRTAccessor, а упасть уже на вызове его метода при обращении к WinRT. Чтобы этого не произошло, дернем WinRT прямо в конструкторе:
        public RealWinRTAccessor()
        {
            LightSensor.GetDefault();
        }


Теперь, когда нужно получить показание датчика освещенности, можно дергать:
        WinRTAccessorFactory.GetAccessor().GetLightValue()


Этот код отработает как при наличии WinRT, так и для более ранних версий Windows — в этом случае результатом вызова GetLightValue будет null.

Из преимуществ данного подхода: достижение поставленной цели и относительная простота.
Из недостатков: необходимость наличия прав на загрузку сборок через Assembly.LoadFile, ручная проверка (ее здесь нет, но должна быть) оригинальности подгружаемой сборки, ручное управление сборкой и местоположением WinRTLib, т.к. она не является Reference кого бы то ни было.
Tags:
Hubs:
+12
Comments3

Articles

Change theme settings