19 декабря 2017 г.

Runtime ошибка при работе с файлом ресурсов "Не удалось найти ресурсы..."

Список ошибок манифеста MissingManifestResourceException:
  • [Russian] Не удалось найти ресурсы, соответствующие указанной культуре или нейтральной культуре. Проверьте правильность внедрения или связывания *.resources со сборкой AssemblyName во время компиляции, или убедитесь, что все необходимые сопутствующие сборки полностью подписаны и могут быть загружены.
  • [English] Could not find any resources appropriate for the specified culture or the neutral culture.  Make sure *.resources was correctly embedded or linked into assembly AssemblyName at compile time, or that all the satellite assemblies required are loadable and fully signed.

18 декабря 2017 г.

Удаленный рабочий стол (RDP). WPF (MVVM).

Скачать исходный код можно по ссылке:

https://bitbucket.org/sergey_vaulin/remotedesktopterminalservice/src


Если у вас когда либо вставал вопрос о том, как реализовать в вашем приложении демонстрацию удаленного рабочего стола другого клиента, то скорее всего вам известны описанные ниже варианта:
  1. Делать скриншот на одной стороне, затем пересылать на сторону получателя и отображать на форме.
  2. Найти C# обёртки и реализации работы с VNC протоколом, который используется в opensource проекте UltraVNC
  3. Использование Windows Desktop Sharing в вашем приложении.
Сразу оговорюсь, что тут рассматривается схема, при которой запуск и остановку серверной части и демонстрация на другой стороне, осуществляется в прикладном коде нашего приложения.

(Пункт 1)
Использование скриншотов является самой простой в реализации, но достаточно трудоемкой операцией. Найденные примеры захвата снимка экрана и его обработка будет намного "тяжелее", чем используя обычный терминал удаленного рабочего стола. Попробуйте в Realtime поделать хотя бы 10 скриншотов и заметите, что ваше приложение будет активно отбирать ресурсы CPU. Да, сейчас компьютеры достаточно мощные и ваш компьютер может это не чувствовать, но клиенты могу сидеть и на Athlon II, имея загрузку в 30% процессорного времени, что неприемлемо. Выходом может стать нарезка скриншотов используя видео захват с дисплея через DirectX, но решения для copy+paste у меня нет.

(Пункт 2)
UltraVNC достаточно известное кросс платформенное приложение для демонстрации и управлением экраном. Находил обертки для .NET, поэтому, если все плохо, то можно посмотреть в эту сторону. Как мне известно, принцип работы VNC основан на захвате скриншотов клиента, и для того, что бы производить видео захват с дисплея, они используют свой драйвер, аккуратно подгружаемый при демонстрации. При этом сам драйвер идёт как отдельный устанавливаемый пакет и может быть скачан отдельно (искать UltraVNC Driver).

(Пункт 3)
Ну и наконец самый, но мой взгляд, простой способ реализовать демонстрацию удаленного экрана это использование нативного API под названием Windows Desktop Sharing (RDP). Используется стандартный механизм терминала удаленного рабочего стола, но в отличии от обычного подключения, удаленный компьютер не блокируется, как при терминале, и вы можете как в Teamviewer пользоваться одной мышкой и видеть экран одновременно. Далее я перечислю возможности, доступные при работе с Windows Desktop Sharing в формате плюсы и минусы:

Плюсы:
  • Демонстрацию удаленного экрана пользователя.
  • Подключение больше одного человека (код примера надо немного доработать).
  • Можно настраивать уровень взаимодействия для каждого подключающегося человека. Допустим одному дать полный доступ к управление, другому только для просмотра. За это отвечает CTRL_LEVEL выбор уровня. 
  • Мы можем в любой момент отключить пользователя, или приостановить показ.
  • Самой интересной особенностью считаю возможность "Фильтрации списка окон". Таким образом мы можем дать возможность видеть только то, что мы хотим показать, скрывая остальные окна.
  • Можно обеспечивать как прямое подключение клиента к серверу, так и сервера к клиенту. Тем самым можно решить проблему с NAT, инициируя подключение с другой стороны подсети.
  • Для авторизации можно использовать схему с логином и паролем.
  • Отображение идёт через ActiveX оснастку, которую можно разместить как на WPF, так и на WinForm.
  • Проекты можно без потери функциональности конвертировать под работы с .NET Framework 3.5.
Минусы:
  • Поддержка идет только начиная с Windows Vista.

14 ноября 2017 г.

Проверки орфографии русского языка в Visual Studio 2013/2015/2017.

Проверка орфографии, как способ упростить жизнь программисту.


Программисты, чаще всего народ грамотный, но встречаются и уникумы, у которых математический склад ума и грамматика русского языка им давалась с трудом. Это я о себе :) При написании комментариев к коду, формировании строк - я могу легко допустить грамматическую ошибку. Бывает, что кто то мне кидает текст с опечаткой, которую с первого раза не заметишь, а если набор текста идёт "слепым" способом, то количество опечаток увеличивается в разы!
Ладно, если место опечатки комментарий к коду, классу, методу или свойству, но это может быть подпись к форме, которая может остаться незамеченной и оказаться на виду у заказчика. И, честно говоря, за такое бывает стыдно!
Напоследок CodeReview - это то, из-за чего я вообще начал искать, как проверять ошибки до заливания на проверку кода. А дело тут вот в чём... При проверке кода, к которому обязательно требовалось добавлять комментарий ко всему, чему только можно - у меня и коллег уходило много времени на прочтение и проверку каждого написанного предложения. Я сидел и по слогам читал слова, что бы не пропустить ошибку. Доходило до того, что меня это изматывало больше, чем анализ написанного кода.
Поэтому для меня остро встал вопрос: Как же осуществить проверку орфографии в Visual Studio? 

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

Его название и ссылка на https://marketplace.visualstudio.com:
Visual Studio Spell Checker (VS2013/VS2015)

10 ноября 2017 г.

Как создать XML и XSD схему и наоборот

Хотелось бы в одном месте рассказать о вполне типичной задаче при работе с XML, а именно, о создании XML на основе существующего. Данная операция будет состоять из нескольких этапов, при выполнении которых, будут решены дополнительные задачи, такие как:
  • Создание XML на основе XSD, и наоборот.
  • Сериализация и десериализация объектов.
  • Создание класса, для будущей сериализации из XML или XSD.
Перед началом планирую, что читатель знаком со словами XML и XSD. Что касается XML, думаю с этим проблем нет, но что касается XSD могу возникнут вопросы, что же это такое. Если коротко, то:
XSD - это язык, для описания XML. И если вы посмотрите содержимое файла содержащего это описание, то возникнет вопрос, зачем же описывать казалось бы очевидные теги вашего XML? Но представьте, что вы интегрируетесь с какой то внешней системой, и для обмена данных хотите использовать формат XML. Так вот, XSD позволяет описать для каждого элемента его именя, возможные атрибуты, обязательность полей или атрибутов, дать понять, что содержимое какого то тега может содержать лишь один внутренний элемент (<a></b></a>) и не более, либо узел может иметь последовательность элементов (<a></b></b>...</a>). Когда внешняя система является вашей, тогда проблем может не возникнуть, но когда это сторонняя организация, тогда данное описание через XSD, будет инструментом урегулирования споров с форматом, а так же, используя утилиты, можно проверить соответствие любой XML изначальному формату.
Итак, что же мы будем делать, для того, что бы научиться штамповать XML на основе вашего эталона? Опишу всё по порядку.

Имеется: 
Какой то XML (xmlfile.xml), сохраненный в текстовом формате в файл. Ниже приведу текст:

<?xml version="1.0"?>
<catalog>
   <book id="bk101">
      <author>Gambardella, Matthew</author>
      <title>XML Developer's Guide</title>
      <genre>Computer</genre>
      <price>44.95</price>
      <publish_date>2000-10-01</publish_date>
      <description>An in-depth look at creating applications 
      with XML.</description>
   </book>
   <book id="bk102">
      <author>Ralls, Kim</author>
      <title>Midnight Rain</title>
      <genre>Fantasy</genre>
      <price>5.95</price>
      <publish_date>2000-12-16</publish_date>
      <description>A former architect battles corporate zombies, 
      an evil sorceress, and her own childhood to become queen 
      of the world.</description>
   </book>
</catalog>

23 октября 2017 г.

Msbuild debugging. Отладка csproj скрипта проекта в Visual Studio.

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

Но выход есть! Скрипт MsBuild можно отлаживать, ставить breakpoints и смотреть значения переменных. Приведу ссылку на исходную статью Debugging MSBuild script with Visual Studio а тут опишу действия на примере Visual Studio 2015 и некоторые особенности.

Сборку и отладку придется запускать через командную строку msbuild.exe используя опцию /debug, но изначально данная функция не доступна, поэтому при попытке воспользоваться ключом будет ошибка:

D:\!Blog\DebugFeatures\DebugFeatures>C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe /debug DebugFeatures.csproj
Microsoft (R) Build Engine version 4.6.1055.0
[Microsoft .NET Framework, version 4.0.30319.42000]
Copyright (C) Microsoft Corporation. All rights reserved.

MSBUILD : error MSB1001: Unknown switch.
Switch: /debug

For switch syntax, type "MSBuild /help"

Поэтому потребуется:
  • Сначала нужно добавить в ветку реестра HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\4.0 значение DebuggerEnabled = true. На компьютере могут стаять две версии .NET Framework - под x64 и x86 поэтому для обоих свои ветки реестра. В статье выше указан путь только к одной ветке, поэтому я приведу текст написанного мной скрипта, который добавит это значение в оба места.

    SET FrameworkDir=%windir%\Microsoft.NET\Framework
    SET FrameworkDir64=%windir%\Microsoft.NET\Framework64
     
    SET Cmd32Exe=%windir%\System32\cmd.exe
    SET Cmd64Exe=%windir%\SysWOW64\cmd64.exe
     
    IF EXIST %FrameworkDir% (
     start %Cmd32Exe% /c "reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\4.0" /v DebuggerEnabled /d true"IF EXIST %FrameworkDir64% (
     start %Cmd64Exe% /c "reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\MSBuild\4.0" /v DebuggerEnabled /d true"
    )
    
    Для запуска скопируйте в блокнот, сохраните как AnyName.bat, затем запустите.
    
  • Затем идём в студию, выбираем проект и в контекстном меню выбираем пункт "Unload Project", проект должен стать "Недоступным":

  • Далее для запуска отладчика пишем в командной строке "Путь\msbuild.exe" /debug "Путь\MyProject.csproj", запускаем и должны увидеть:

14 октября 2017 г.

Симулятор игры "Жизнь" ("Game of life") на WPF

Все игры:

Исходный код:

Скачать




Game of life.zip
Скомпилированная версия

.NET Framework 4.0
Требуемая версия установленного Framework.

Однажды, просматривая видео на youtube, я наткнулся на крайне занимательный рассказ про клеточные автоматы. Они мне настолько понравилось, что захотелось самому увидеть их работу, а так же реализовать алгоритм на языке C#. Ссылка на то видео:


В нём достаточно хорошо все рассказано, поэтому в данной статье будет только краткое описание алгоритма и пример реализации.

Алгоритм (правила) работы клеточных автоматов.

Действие разворачивается на бесконечном поле в решётку. На каждом кадре анимации (называемом поколением), мы обходим все ячейки и вычисляем количество стоящих по периметру закрашенных клеток. Посчитав, выполняем два правила:
  1. Клетка закрашена. Если вокруг клетки ровно 2 или 3 закрашенные клетки, тогда с ней ничего не случается и она живёт, иначе становится пустой.
  2. Клетка пустая. Если вокруг ровно 3 закрашенные клетки, тогда в клетке зарождается жизнь и она становится закрашенной.

Пример на C# + WPF.


Исходный код своего проект я разместил на bitbucket.org, ссылка на проект размещена в начале статьи. Так же можно скачать (выше) собранную версию, но для её работы необходимо наличие предустановленного .NET Framework 4.0.

Интерфейс программы:



7 сентября 2017 г.

WCF System.Diagnostics логирование в реальном времени

https://bitbucket.org/sergey_vaulin/wcftracker/src
Скачать проект.


Оглавление:

  1. WCF service. С чего начать. Основные моменты создание службы. 
  2. WCF. Callback через BasicHttpBinding без дуплексной связи.
  3. Realtime WCF Tracker вызовов клиентского приложения.
  4. Как отключить публикацию метаданных mex WCF сервисом. 

Введение


При разработке не раз была потребность понять, кто вызывает в конкретный момент мой сервис с клиента. Для этого (лично я) у сервиса в каждом методе добавлял Breakpoint и далее производил TestCase, выясняя из каких мест на клиенте производятся вызовы по Call Stack. Или представьте, когда происходит какая то проблема, не на вашем компьютере, влияющая на отзывчивость приложения и вам надо найти подозреваемого, а значит понять дело в сервере или клиенте путём анализа вызовов.
Для этого можно в приложении включить полную троссировку вызовов, воспользовавшись Trace Viewer (Using Service Trace Viewer for Viewing Correlated Traces and Troubleshooting) но он пишет все в лог файл, при этом требует каких то действий для настройки его на клиенте. Не спорю, его может быть достаточно, но там будет лог всех сообщений за время работы  клиента и в этой куче не сразу найдёшь что было вызвано в нужный интервал времени. Но хочется, что то на уровне - нажал - посмотрел вызовы, нашёл что надо - закрыл, отключил.

WCF Tracker в реальном времени.


Хочу представить альтернативный вариант, который, помимо возможности видеть все вызовы, имеет возможность ставить breakpoint.

Демонстрация работы:



25 августа 2017 г.

MSSQL DATETIME2, DATETIME truncate минуты, секунд.

В какой то момент может понадобится  возможность в SQL запросе по полю типа DATETIME или DATETIME2 производить сравнение дат с какой то конкретной датой, отбросив при этом минуты, секунды, миллисекунды. Для дат, которые лежат в настоящие годы, это можно сделать без каких либо проблем через DATEADD и DATEDIFF. Пример использования с обрезанием до минут:

SELECT DATEADD(MINUTE, DATEDIFF(MINUTE, 0, '2017-08-25 00:00'), 0)

В случае, если требуется обрезание до секунд, миллисекунд или любой другой составляющей даты достаточно поменять первый параметр метода DATEADD и DATEDIFF на нужный тип. Список типов одинаков и описан на msdn каждого метода.

Все было бы хорошо, но есть ложка дёгтя в бочке с медом. DATEDIFF возвращает количество прошедших минут, секунд, миллисекунд с 1900-01-01 00:00:00 при этом возвращаемое значение INT. Это значит:
  • Даты до 1900 года будут иметь отрицательное значение
  • Мы имеем ограничение на возвращаемое значение, равное диапазону значений INT, а это ровняется от -2,147,483,648 до +2,147,483,647.
  • Чем меньшую единицу измерения времени мы хотим обрезать, тем ближе к 1900 году должна быть дата в таблице.
При этом, максимальный год для DATETIME и DATETIME2 это 9999. И если в таблице будет присутствовать значение, которая будет приводить к переполнению INT при вычисление DATEDIFF - вы будете получать ошибку на ваш SQL запрос:
The datediff function resulted in an overflow. The number of dateparts separating two date/time instances is too large. Try to use datediff with a less precise datepart.

15 августа 2017 г.

Решение проблемы при установке ccollab клиента.

Столкнулся с проблемой при обновлении клиента Code Review - Collaborator от SmartBear с 6.x на 11.x. Для установки я удалил старую версию и решил поставить новую, но в ответ получил ошибку:


Формат текста ошибки следующий:
The installation/removal of a previous program was not completed. You will need to restart your computer to complete that installation.\n\nAfter restarting your computer, run Setup again to complete the installation of {0}.
Для её решения я используя Process Explorer открыл текущую директорию у для установщика, она была во временной папке по пути C:\Users\SAP\AppData\Local\Temp\[Random], в папке содержался файла MessagesDefault в котором было само сообщение с ключем PreviousInstallNotCompleted. Декомпилировав было выяснено, что в файле i4jruntime.jar, лежащем в каталоге, содержится часть логики установщика. После непродолжительного анализа была найдена проверка и выдача ошибки, а логика такая:
  • Читаем из HKEY_CURRENT_USER и HKEY_LOCAL_MACHINE значение RebootCheckFile по адресу SOFTWARE\ej-technologies\install4j\.
  • Если файл по пути RebootCheckFile существует то падаем с ошибкой.
Почему у меня не удалился файл я не знаю, но действительно, по пути C:\Users\SAP\AppData\Local\Temp\ существовал пустой файл, который стопарил все.



Удаляем RebootCheckFile и все! Установка должна пройти успешно.

14 августа 2017 г.

Учим Ecm7.Migrator работать с MSSQL типом DATETIME2

Сегодня хотелось бы рассказать зачем мы начали использовать ECM7.Migrator и как его научить понимать DbType.DateTime2 на версии ECM7 2.8.0.0.

Зачем использовать ECM7?


Всю информацию можно узнать на сайте проекта ECM7.Migrator ссылка на NuGet. Тут я опишу почему мы его стали использовать на работе. В решениях используем EntityFramework с Code First подход при работе с базой данных. Для этого EntityFramework умеет создавать, на основе объектной модели, сущности в базе данных. Данные об этих сущностях хранятся в миграциях, а их применением и версионностью занимается DbMigrator. Все было бы хорошо, но есть одно ограничение - все миграции должны находиться в проекте с DbContext. И в ситуации, если решение состоит из плагинов, каждому из которых требуется своя структура в базе, это ограничение требует, что бы все миграции до подключения плагина уже были применены к базе. Но в этом случае теряется смысл в выделении модулей и подключении только нужных.

Для решения мы использовали ECM7.Migrator, на который пришлось переписать создание базы данных решения, а модульные миграции без проблем перенесли в другие сборки.

Работа с DateTime2.


Решения из коробки у версии ECM7 2.8.0.0, для работы с типом DateTime2, найдено не было, но это лечится простым добавлением типа в карту типов SqlServerTransformationProvider.


using System.Data;
using System.Data.SqlClient;
 
namespace ECM7.Migrator.Providers.SqlServer
{
    /// <summary>
    /// Extended <see cref="SqlServerTransformationProvider"/> MSSQL provider.
    /// </summary>
    internal class ExtendedSqlServerTransformationProvider : SqlServerTransformationProvider
    {
        /// <summary>
        /// <see cref="ExtendedSqlServerTransformationProvider"/> constructor.
        /// </summary>
        public ExtendedSqlServerTransformationProvider(SqlConnection connection, int commandTimeout)
            : base(connection, commandTimeout)
        {
            typeMap.Put(DbType.DateTime2, "DATETIME2");
        }
    }
}
 

13 августа 2017 г.

WPF/WinForms Хостинг (встраивание) окна чужого win32 приложения.

Ссылка на пример из статьи:
https://bitbucket.org/sergey_vaulin/wpf-application-host


Хочу поделиться с вами способом, как можно окно внешнего приложение "встроить" в свою программу. Ситуация из жизни: разрабатывая CRM систему для заказчика может появиться потребность, в одном из сценариев обслуживания, дать пользователю возможность произвести какие то манипуляции во внешней системе. Если эта система является Web сайтом, то задача решается добавлением WebBrowser на форму. Но представьте, что это какое то приложение, и что тогда делать?
Для этого можно провернуть трюк используя WinAPI, в результате которого окно внешнего приложения окажется "внутри" нашего (как будто экран виртуальной машиной), но у заказчика будет складываться иллюзия того, что мы его запустили внутри нашего приложения. И это похоже на магию.

В примере, ссылка на который имеется в начале статьи, я добавил TabControl, у которого на вкладках размещены три стандартных Windows приложения:
  • Notepad - блокнот.
  • Write - простой редактор документов.
  • Regedit - редактор реестра.
Если запустить пример и переключать закладки, то можно увидеть приведённый на скриншотах ниже, результат работы:

Блокнот:


25 июля 2017 г.

WCF. Callback через BasicHttpBinding без дуплексной связи.

Оглавление:

  1. WCF service. С чего начать. Основные моменты создание службы. 
  2. WCF. Callback через BasicHttpBinding без дуплексной связи.
  3. Realtime WCF Tracker вызовов клиентского приложения.
  4. Как отключить публикацию метаданных mex WCF сервисом. 

В данной статье я хочу поделиться с вами примером того, как можно в WCF инициировать передачу данных с сервера на клиент используемые BasicHttpBinding в качестве привязки для взаимодействия.
Как вам должно быть известно, для того, что бы сервер выступа в качестве инициатора передачи данных на клиент используя WCF, ему надо использовать одну из схем ниже:
  • Производить подключение к клиенту самостоятельно в ответ на вызов клиента (что выглядит глупо).
  • Использовать Duplex связь, которая была создана специально для этого.
    [ServiceContract(CallbackContract = typeof(ICallback))]
    internal interface IMyContract
    {
    }
Duplex связь покрывает наши запросы, но есть одно "но" - привязка (Binding) должена поддерживать работу с использованием сессии. А BasicHttpBinding (на основе протокола HTTP) в силу своей природы не может поддерживать сессию (постоянное TCP соединение), так как работает по схеме "Запрос-ответ". Как же тогда поступить?

WSDualHttpBinding привязка.


Для работы через HTTP транспорт, с возможностью использования Duplex связи, специально был создан WSDualHttpBinding, который, для имитации сессии, используя HTTP заголовок, добавляя туда техническую информацию. А для обратного соединения сам создаёт подключение к клиенту. Замечу, что обратное соединение создаётся через WCF engine, и используется как при работе с любой другой привязкой, а не программистом, при первом подключении клиента. В качестве обратного соединения используются подключения на 80 (по умолчанию) порт клиента со стороны сервера, когда надо передать данные.
Все было бы хорошо, но в какой то момент, может возникнуть ситуация, когда клиент будет находиться за NAT, что приводит к невозможности подключиться к нему сервером. Почему? Читайте как работает NAT, он разрешает соединения из внутренней сети, но запрещает подключения из внешней (за маршрутизатором).  В это случае выходом может быть лишь привязки на основе протокола TCP (NetTcpBinding), в которые, для обратного и прямого соединения, используется одно TCP соединение. Но что, если нам нужно как то через HTTP производить обратный вызов клиента.

2 июля 2017 г.

WCF service. С чего начать. Основные моменты создание службы.

Примеры, которые используются тут, можно скачать по ссылке:

https://bitbucket.org/sergey_vaulin/wcf-examples/src

Еще ссылки:

  1. WCF service. С чего начать. Основные моменты создание службы. 
  2. WCF. Callback через BasicHttpBinding без дуплексной связи.
  3. Realtime WCF Tracker вызовов клиентского приложения.
  4. Как отключить публикацию метаданных mex WCF сервисом.  

Оглавление статьи:

  1. Что такое endpoint?
  2.  Способы общения клиента с сервисом. 
  3. Канал связи клиента и сервера.  
  4. Надежные каналы связи (reliableSession).
  5. Управление состоянием службы.
  6. Управление созданием экземпляров службы.
  7. Управление временем жизни экземпляра службы атрибутами контракта.
  8. Режимы параллельной работы с экземплярами службы
  9. Регулирование нагрузки.
  10. Обработка исключений.  
  11. Обработка исключений. Продолжение работы с посредником если используется HTTP транспорт.
  12. Обработка исключений. Обертывание всех исключений в FaultException, используя IErrorHandler.
  13. Закрытие канала посредника.
  14. Использование общей сборки с контрактами.
  15. Как убрать Web Reference. Использование общей сборки с контрактами.

Введение


В данной статье хочу описать настройки, которые разработчику следует знать  перед началом создания сервиса и клиента к нему. Я исхожу из того, что читатель уже имеет представление о WCF, пользовался какими то наработками, но не углублялся в настройки Binding и сервиса.
  • Если вы являетесь новичком и хотели бы увидеть примеры служб и работу с ними, перед прочтением советую скачать и посмотреть примеры Windows Communication Foundation (WCF) and Windows Workflow Foundation (WF) Samples for .NET Framework 4/ созданные Microsoft.
  • Из книг советую читать "Создание служб WCF (Джувел Леве)", на основе которой, а так же собственном опыте будет писаться данная статья.
  • Для демонстрации можно использовать мои примеры из проекта, ссылка на который в начале статьи. 
Я не буду расписывать все процессы создания с нуля. Тут будет представлены только наиболее интересные, на мой взгляд, понятия и настройки, о которых узнал после прочтения книги, просмотра описаний на msdn и написании примеров для демонстрации.
По каждой теме я старался не просто написать определение, а сделать реальный пример с демонстрацией, что бы все стало ясно.

Основные понятия и настройки.


Что такое endpoint?


Конечной точкой называется совокупность трех элементов:
  1. Адрес сервиса. Подразумевается, что это URL, по которому располагается сам сервис.
  2. Контракт сервиса.
  3. Binding - содержит настройки работы с сервисом, протокол взаимодействия, Timeouts операций и т.д.


Способы общения клиента с сервисом.


В WCF существует три способа общения, которые обязательно надо знать:
  1. Request-reply (запрос-ответ) - это когда с клиента делает вызов метода контракта Method(), текущий поток останавливается и ждет, пока сервер обработает ваш вызов и вернет ответ и управление. При этом все происходит в синхронном режиме, вы не сможете продолжить выполнение пока не получите ответа. Если происходит исключение на стороне сервера, то оно прилетит на клиента.
  2. OneWay (или datagram) - данный способ отличается тем, что вызываемая метод контракта сторона (это может быть клиент, или сервер при дуплексной связи), не останавливается при вызове, а продолжает выполнение. Как если бы вызов практически сразу вернул результат. Иными словами мы в любом случае ожидаем доставки SOAP сообщения на противоположную сторону, но не ожидаем получения результата в ответ.
    Возможны ситуации, когда и при таком взаимодействии тоже произойдет блокировка потока на вызываемой стороне:
    • Когда при вызове, на сервере мы упираемся в какой то нагрузочный maxConcurrent* придел сервиса и наше сообщение с вызовом помещается в очередь на обслуживание. В этом случае мы будем ожидать пока вызов не встанет в обработку либо отвалимся по Timeout.
  3. Duplex - это взаимодействие в обе стороны, когда клиент может вызывать методы сервера, а сервер, используя callback контракт, может вызывать методы клиента. Что бы это понять представьте чат, вы можете напечатать сообщение и отослать его на сервер, но что делать, если сообщение напишут вам? В этом случае сервер должен инициировать передачу на ваш компьютер. Как раз таки для этого он будет вызывать callback контракта, который задается отдельно, что бы уведомить вас.


Как реализуются данные схемы общения.

  1. Request-reply. Данное поведение является "по умолчанию".

    [ServiceContract]
    internal interface IMyContract 
    {
        [OperationContract]
        void Method();
    }
    
  2. OneWay. Для того, что бы метод контракта или callback контракта стал односторонним, достаточно указать IsOneWay = true.
    [ServiceContract]
    internal interface IMyContract
    {
        [OperationContract(IsOneWay = true)]
        void Method();
    }
    
  3. Duplex. Для реализации используется свойство атрибута под названием CallbackContract.
    
    
    [ServiceContract(CallbackContract = typeof(ICallback))]
    internal interface IMyContract
    {
        [OperationContract]
        void Method1();
     
        [OperationContract(IsOneWay = true)]
        void MethodOneWay1();
    }
     
    [ServiceContract]
    internal interface ICallback
    {
        [OperationContract]
        void Method2();
     
        [OperationContract(IsOneWay = true)]
        void MethodOneWay2();
    }

6 июня 2017 г.

Как отключить публикацию метаданных mex WCF сервисом.

Оглавление:

  1. WCF service. С чего начать. Основные моменты создание службы. 
  2. WCF. Callback через BasicHttpBinding без дуплексной связи.
  3. Realtime WCF Tracker вызовов клиентского приложения.
  4. Как отключить публикацию метаданных mex WCF сервисом. 

Публикация метаданных вашего сервиса может снизить безопасность системы. Ведь вы предоставляете возможность узнать всю необходимую для работы с ним информацию. При этом, отсутствие возможности получить информация никак не влияет на его работу. Поэтому хотелось бы рассказать как убрать публикацию метаданных сервиса, если не вы их включали. Для начала стоит знать, что метаданные могут быть доступны двумя способами:
  • Через HTTP-GET .
  • И через специально подготовленную Endpoint для MEX, используя IMetadataExchange.
На MSDN есть две статьи, описывающие наоборот, оба процесса настройки. Поэтому можно опираться и на них:

1. Проверяем файл конфигурации сервера.
1.1 Ищем HTTP-GET

Открываем файл конфигурации, в котором хранятся настройки для ваших сервисов. И ищем раздел  <serviceBehaviors

<system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="SERVICEMETADATANAME">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

Если таковой имеется, тогда ищем tag <behavior/>, у которого внутри есть <serviceMetadata />. Значения атрибутов http(s)GetEnabled="True" говорят о том, что все <services />, у которых задано <service behaviorConfiguration="SERVICEMETADATANAME"  />, будут публиковать свои метаданные. Поэтому для того, что бы отключить публикацию достаточно задать:<serviceMetadata httpGetEnabled="false" httpsGetEnabled="false" />
ВНИМАНИЕ! Если используется пустое значение name <behavior name="">, это говорит о том, что данное поведение будет применено ко ВСЕМ Endpoint вне зависимости от того, где происходит инициализация ServiceHost. Ещё одно замечание, Endpoint не может работать одновременно в режиме httpEnabled="false" и httpsEnabled="true" поэтому надо менять оба параметра.
 Запускаем Web сервер. Если при обращении к сервисам получаете сообщение
"Metadata publishing for this service is currently disabled", значит все сделано правильно.

4 июня 2017 г.

WSDL import в Visual Studio из файла или текста.

Когда пишется Web сервис, разработчику стоит задуматься о безопасности. Один из простых способов это скрытие из публичного доступа WSDL информации. Поэтому представьте ситуацию: вы интегрируетесь с какой то внешней системой. Разработчики этой системы присылают .docx файл с документацией к ней. Внутри содержится текст XML <wsdl:definitions /> и сразу встает вопрос, как же это использовать.
  1. Первым делом выделяем всю XML из документа, копируем и сохраняем в отдельном файле с расширением .asmx.
  2. Далее отрываем Visual Studio жмем по References правую кнопку и выбираем "Add Service Reference..."
  3. В отрывшемся окне добавления ссылки в поле Address вводим строку формата "file://PATH.asmx" жмем GO и видим, что студия успешно считала содержащуюся в файле информацию о сервисе.
  4. После нажатия на можно начать работу со службой, передав в класс посредник её адрес. Пример создания клиента указан ниже:

Snoop WPF. Как использовать? Полное описание на русском.

При разработке интерфейса на WPF, не раз может возникать ситуация, когда требуется найти тот или иной элемент управления в Visual Tree приложения, узнать его свойства или путь до него. Если работодатель еще не приобрел Visual Studio 2015, в котором была добавлена функция UI Debugging, то можно использовать бесплатную утилиту Snoop. О всех её особенностях я постараюсь рассказать далее.

Главное окно Snoop.


После установки и запуска у вас на экране появится небольшое окошко:

  1. Кнопка цели под номером один позволяет выбрать конкретное окно приложения. Если зажать левую кнопку мыши на прицеле, то мышь превратится в такой же прицел. Далее не отпуская левую кнопку надо навести на окно (заголовок или любое место внутри) вашего приложения. Если все сделать правильно, то покажется окно с Visual Tree. При этом, если у приложения два и более окон, то можно с помощью этого прицела выбирать нужное вам окно.
  2. В ситуации, когда приложения работает, а окно скрыто, можно воспользоваться кнопкой поиска всех совместимых окон работающих процессов. После нажатия кнопку обновить (2), все WPF приложения появятся в списке пункта три.
  3. Если вы воспользовались кнопкой в пункте два, то в раскрывающемся списке появятся все работающие WPF приложения.
  4. При нажатии на бинокль, если в списке пункта три присутствует приложение, тогда откроется Visual Tree его главного окна. 
  5. С помощью этого прицела, можно выбрать окно для визуального просмотра всех его элементов управления. При просмотре есть возможностью увеличить какой то участок окна, рассмотрев все с точностью до 1 пикселя. Так же слева в углу можно увидеть 3D режим, тем самым понять, как получается видимый Layout.
  6. Запускает аналогичное действие с пунктом пять, но с выбранным в списке окном приложения.

Описание основных возможностей Visual Tree окна.


На изображении ниже представлены два окна. Слева окно вашего приложения, справа окно, которое появится если зажать прицел у начала стрелки и перетащить на ваше приложение. В появившемся окне можно увидеть Visual Tree вашего приложения:



25 мая 2017 г.

Port Forwarding. Доступ к сайту через VPN.

Что такое Port Forwarding (Port mapping)?


Если у вас дома для раздачи интернета используется Router и вы сами делали его настройку, значит наверняка в настройках видели пункт меню Port Forwarding (Port Mapping). Port Forwarding - это технология позволяет минуя NAT настроить доступ из интернета к конкретному компьютеру, находящемуся у вас дома и подключенному к Router. Чаще всего в настройках указываются две вещи:
  1. Имя или IP домашнего компьютера и порт на который будет идти переадресация из вне.
  2. Порт, который будет отрыт на Router со стороны интернета.
В результате получается поведение как на рисунке:


20.1.1.1 - IP адрес вас в интернете выданный интернет провайдером. Если из интернета подключаются к 80 порту на Router, то происходит перенаправление трафика на стационарный компьютер, а при обращении к 8080 на ноутбук.

Как это поможет разработчику?


Представьте, что у вас есть задача интеграции с какой то системой в сети заказчика. Вам дают VPN и тестовый компьютер с которого эта система доступна, при этом у пользователей VPN напрямую доступа к системе нет. Через RDP вы подключаетесь к компьютеру и понимаете, что для того, что бы начать разработку вам требуется устанавливать Visual Studio.

Что же можно в этом случае сделать?

Мы можем в качестве Router использовать компьютер до которого есть RDP доступ. На нём запустить программу, которая будет перенаправлять входящий трафик в систему заказчика, что даст нам возможность локально работать над интеграцией.

Сложно? Нет - проще паренной репы! А как это сделать объясню дальше.

Настройка Port Forwarding.


Первый шаг. Узнаем IP или Hostname системы (что заказчик конечно же скажет) с которой нам надо интегрироваться. Далее нам необходимо узнать порт, через который планируется взаимодействовать с системой. Если у вас возникает проблема с определением порта,  попробую привести типовые ситуации и объяснить как я узнал в них порт.
  • Если у вас взаимодействие с каким то вэб сервисом и заказчик дал URL формата http://host/service это значит, что вэб сервис работает по HTTP протоколу и порт по умолчанию 80.
  • Если адрес https://host/service значит с системой придётся взаимодействовать по HTTPS порту и его значение по умолчанию 443.
  • Если в адресе URL после host стоит двоеточие число: http(s)://host:8001/service значит используется нестандартный порт для HTTP(S) протокола и нам потребуется просто в явном виде указать его, в моём случае это 8001.
  • Такое же перенаправление можно настроить для чего угодно, даже для SQL сервера, только в этом случае придётся поискать нужный порт в интернете.
Второй шаг. Скачиваем самую маленькую и удобную утилиту для создания переадресаций с возможность их сохранять. Ссылка блога автора http://www.quantumg.net/portforward.php если ссылка на исполняемый файл не доступна, то можно скачать отсюда: [Download-Port-Forward] использую уже лет 5 и не жалуюсь. Есть масса аналогов, платных, бесплатных работающих как сервисы или же есть варианты штатными средствами без стороннего софта поднять перенаправление. Мне же по душе самому выбирать когда и что я хочу начать перенаправлять, при этом, должна быть возможность легко выключить это. Данная утилита лёгкая (44 кб) и без лишних кнопок делает то, что сказано. Рекомендую.

Для демонстрации я опишу пример:
  • Имя моего локального компьютера sapcomputer и это будет тем компьютером с RDP.
  • В качестве внешней системы я хочу взять доступ к сайту https://worldofwarcraft.com, к которому нет доступа с других компьютеров.
  • Рабочим компьютером к сожалению будет выступать мой же компьютер, я просто подключусь к локальному порту и должен оказаться на сайте.
Приступим! Запускаем утилиту, жмём Redirection >  Add в поле Port указываем локальный порт который будет отрыт для подключения клиентов. Можно указать такой же как и для Destination, но у меня локально 443 занят, вы в праве использовать любой свободный. Далее в поле с адресом указываем worldofwarcraft.com а порт 443 потому, что HTTPS://.



Жмём OK и можно проверять! Открываем браузер вводим https://sapcomputer:9999/ и вуаля, мы видим сайт:


Важно! Если вы перенаправляете на https порт 443 то и при обращении к локальному сайту надо обязательно использовать префикс https, аналогично с http. Иначе вы не сможете увидеть содержимое страницы.
Теперь с рабочего компьютера можно нацеливать разрабатываемый модуль на sapcomputer:9999 и работать, как если бы вы работали на sapcomputer. Просто не так ли?

23 мая 2017 г.

Инструменты отладки: System.Diagnostics, Output window, DebuggerDisplay, Edit and continue, Immediate Window и т.д

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

Пространство имен System.Diagnostics 


Если говорить о инструментарии, который уже имеется в составе .NET Framework, тогда речь пойдут о классах лежащих в пространстве имен System.Diagnostics.* . Из всего многообразия классов я выделю только те, которые помогут нам в разработке.

Класс взаимодействия с отладчиком (Debugger).


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

Метод Debugger.Launch() 


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

Простенький пример консольного приложения:

using System.Diagnostics;
 
namespace DebugFeatures
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Debugger.Launch();
        }
    }
}
 
Запустив по F5 в студии вы ничего не заметите, но запусти в проводнике увидите диалог:

В котором можете выбрать либо уже запущенную студию (у меня это 1-я строка), либо использовать новый экземпляр студии по вашему выбору.
Внимание! Если "New instance Microsoft ....", тогда весь код проекта будет не доступен. Поэтому желательно выбирать студию с отрытым вашим проектом.
Теперь по поводу жизненных ситуаций, когда этот метод пригодится:
  • При отладке двух процессов, когда ваше приложение запускает второе приложение, которое быстро "что то делает" и закрывается. При этом вы не успеваете сделать Debug -> Attach to process.
  • Допустим вы являетесь разработчиком какого то расширения или add-on для какой то системы. При запуске система подгружаете ваш модуль в память и передает ему управление. Встает вопрос, как же теперь отлаживать ситуацию в момент запуска модуля? В этом случае добавляем в код метода Debugger.Launch() и в момент запуска получаем возможность отладить процесс запуска.
    P. S. Если вы пользуетесь Thread.Sleep(15000) + Attach to process, то мои вам соболезнования :)
Важно! Стоит помнить что даже в релизной сборке будет отображаться окно с отладчиками, поэтому не забывайте удалять Debugger.Launch(), когда надобность в нем отпадает.
 

Метод Debugger.Break()


Break работает по правилам:
  • Если отладчик подключен, то происходит остановка при вызове методе, сравнимая остановке на breakpoint.
  • Если же отладчик не подключен и вызывается метод Break(), тогда будет показано окно с выбором отладчика, но при этом закрыв его приложение продолжит работу.

Пример кода:

using System.Diagnostics;
 
namespace DebugFeatures
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            int a = 1;
            while (a < 10 * 1000 * 1000)
            {
                a ++;
                if (a == 9*999* 999)
                {
                    Debugger.Break();
                }
            }
        }
    }
} 
 
На практике использовал этот метод довольна редко, но было дело, что студия "глючила" и breakpoint слетали после перезапуска. Выходом был этот метод.

 Output window и класс Debug.


Мне кажется, что люди не дооценивают окно Output внутри Visual Studio убирая его с рабочего пространства, а ведь это просто идеальная консоль для вывода событий происходящих в приложении и просто спасительный круг, если вы отлаживаете сложное многопоточное приложение. Либо пытаясь распутать какие то ситуации, где требуется анализ параметров и локальных переменных, что бы понять причину неисправности. Сейчас я постараюсь продемонстрировать как использовать Output, но при этом не загромождать им место.
Наблюдение показывает, что у всех программистов, которых я видел одна из ситуаций ниже:
  • Окно Output не присутствует на видимом рабочем пространстве. А находится где то снизу на вкладках, при этом программист может иногда поглядывать туда.
  • Окно Output всегда видно, но имеет настолько малый размер, что в нем ничего не понятно. При сборке что то мелькает, а на вопрос "Зачем тебе оно если ничего не понятно?" получал ответ "Что бы знать, что студия не подвисла и было на что поглядеть". 
  • Последний вариант, программист активно пользуется им и читает, что пишется в окне. Но при этом в окне два scroll умещается 4-6 строчек, а при работе приложение внутри мелькает текст и бедняга программист пытается найти нужное место.
Если вы из пункта 3 тогда хотел бы предложить мой способ размещения окна, которым я пользуюсь и перенастраиваю везде уже так в течении 5 лет. На всё про всё уходит 5-10 минут, но бывают и такие которые годами будут пользоваться неудобным интерфейсом или способом, при этом зная, что правится проблема в считанные минуты, но человеку "Лениво разбираться" :)

17 мая 2017 г.

MiniDumpWriteDump. Создаём minidump на C#.

Что такое minidump?

Minidump - это, в зависимости от запроса, полный или частичный "слепок" оперативной памяти работающего процесса с сохранением полного или частичного состояния на момент получения снимка.
Как это можно поможет в работе? Дело в том, что minidump можно создавать самому, а начиная с Framework 4.0 - Visual Studio при открытии minidump файла может показать точку останова, стек вызова и даже локальные процессы. Если конечно их не убрал /Release оптимизатор. Иными словами мы увидим такую же картину, как если бы мы работающее приложение поставили на паузу и решили посмотреть стек.
Заинтересовало не так ли? Тогда приступим!

Способы создания.

Известные мне способы создания дампа:
  1. Создание дампа внутри вашего приложения вызывая метод MiniDumpWriteDump из dbghelp.dll.
  2. Используя какие то внешние (out of process) утилиты:
    Стандартный диспетчер задач, Process Explorer или Process Hacker и т.д

Создание Minidump внутри приложения


Для создания дампа внутри нашего приложения нам необходимо всего лишь вызвать функцию MiniDumpWriteDump из библиотеки dbghelp.dll, которая есть у всех, или почти всех, версий Windows. Из отличий версий могут быть только флаги параметра, которые добавлялись в ходе развития библиотеки.
Зачем создавать дамп из тела приложения? Создание таким образом дает возможность контролировать место останова, для дальнейшего анализа дампа. Дампы созданные из диспетчера задач не могу гарантировать, что при открытии в студию вы попадете в "полезное" место и в котором будет все, что требуется для решении вашей задачи или проблемы.

Я создал простенькое консольное приложение для демонстрации работы:
  1. Код  метода Main():
    namespace Dumper
    {
        using System;
        using System.IO;
        using System.Linq;
        using System.Runtime.CompilerServices;
        using System.Threading;
        using Dumps;
        
        internal static class Program
        {
            private static bool _stopThread;
     
            /// <summary>
            /// Entry point.
            /// </summary>
            static void Main(string[] args)
            {
                Directory.GetFiles(DumpUtils.DumpDirectory).ToList().ForEach(File.Delete);
                CreateThread();
                TestMethod();
                _stopThread = true;
            }
     
            private static void CreateThread()
            {
                new Thread(() =>
                {
                    while (!_stopThread)
                    {
                        Console.WriteLine("while(true)");
                    }
                })
                {
                    Name = "While(true) thread"
                }.Start();
            }
     
            [MethodImpl(MethodImplOptions.NoOptimization)]
            private static void TestMethod()
            {
                var dateTime = DateTime.Now;
                try
                {
                    Console.WriteLine(dateTime);
     
                    int someDouble = 0;
                    someDouble = 10 / someDouble;
                }
                catch (Exception)
                {
                    DumpUtils.WriteDump();
                }
            }
        }
    }
    
  2. Код класса создающий Minidump файл:
    namespace Dumper.Dumps
    {
        using System;
        using System.Diagnostics;
        using System.IO;
        using System.Reflection;
        using System.Runtime.CompilerServices;
        using System.Runtime.InteropServices;
     
        /// <summary>
        /// Minidump support tools.
        /// </summary>
        public static class DumpUtils
        {
            /// <summary>
            /// Folder for saved minidumps.
            /// </summary>
            public const string DumpDirectory = "Minidump";
     
            [DllImport("dbghelp.dll")]
            private static extern bool MiniDumpWriteDump(IntPtr hProcess, int processId, IntPtr hFile, int dumpType,
                IntPtr exceptionParam, IntPtr userStreamParam, IntPtr callStackParam);
     
            /// <summary>
            /// Write minidump to file.
            /// </summary>
            /// <param name="minidumpType">Minidump flag(s).</param>
            [MethodImpl(MethodImplOptions.NoOptimization)]
            public static bool WriteDump(
                MinidumpType minidumpType = MinidumpType.MiniDumpWithFullMemory |
                MinidumpType.MiniDumpWithHandleData |
                MinidumpType.MiniDumpWithUnloadedModules |
                MinidumpType.MiniDumpWithFullMemoryInfo |
                MinidumpType.MiniDumpWithThreadInfo)
            {
                try
                {
                    if (!Directory.Exists(DumpDirectory))
                    {
                        Directory.CreateDirectory(DumpDirectory);
                    }
     
                    var currentProcess = Process.GetCurrentProcess();
                    var fileName = GetNewDumpFileName(currentProcess.ProcessName);
                    var currentDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                    var filePath = Path.Combine(currentDir, DumpDirectory, fileName);
                    var handler = currentProcess.Handle;
                    var processId = currentProcess.Id;
     
                    using (var fileStream = new FileStream(filePath, FileMode.CreateNew))
                    {
                        return MiniDumpWriteDump(
                            handler,
                            processId,
                            fileStream.SafeFileHandle.DangerousGetHandle(),
                            (int) minidumpType,
                            IntPtr.Zero,
                            IntPtr.Zero,
                            IntPtr.Zero);
                    }
                }
                catch (Exception)
                {
                    return false;
                }
            }
     
            private static string GetNewDumpFileName(string processName)
            {
                return string.Format("{0}_{1}_{2}.dmp", processName,
                    DateTime.Now.ToString("yyyy-dd-mm_HH-mm-ss"),
                    Path.GetRandomFileName().Replace("."""));
            }
        }
    }