Windows Communication Foundation – программная платформа от Microsoft для создания, настройки и развертывания распределенных сетевых сервисов. WCF-runtime и его пространство имен System.ServiceModel, представляющее его главный программный интерфейс, это преемник технологий создания распределенных систем, успешно применяемых разработчиками для создания распределенных приложений на платформе Windows в последнее десятилетие. Разберём тестовый пример создания WCF-сервиса.
Открываем Visual Studio 2015 и создаём новый проект типа Class Library. Проект назовём WCFMyServiceLibrary.

Файл Class1.cs переименуем в MyService.cs и добавим ещё один класс, файл для которого назовём IMyService.cs.
Добавим ссылку на сборку System.ServiceModel.
На этом разработка сервиса завершена. Переходим к созданию службы Windows, которая будет контейнером для данного сервиса.
В том же решении (Solution) создадим новый проект типа «Служба Windows». Называем проект WindowsServiceHostForMyService.

Затем файл Service1.cs (только что созданного проекта) переименуем в MyService.cs. В этот проект добавим ссылку на сборку System.ServiceModel, а также не забываем указывать в файле MyService.cs директивы:
В классе MyService добавляем новый член:
Также необходимо добавить ссылку на проект WCFMyServiceLibrary, который находится в этом же решении:

Затем в классе MyService изменим метод OnStart таким образом, чтобы в этом методе добавлялись конечные точки нашего сервиса (endpoint):
Затем реализуем остановку сервиса в методе OnStop:
Затем в Обозревателе решения — двойной клик на файле MyService.cs (проекта WindowsServiceHostForMyService) откроет этот файл в режиме конструктора (Design Mode).

На пустом пространстве вызываем контекстное меню (щелчок правой кнопкой мыши) и выбираем «Добавить установщик».

При этом будет создан новый класс ProjectInstaller.cs
Переименуем файл ProjectInstaller.cs в MyServiceInstaller.cs.
При этом выйдет окно с вопросом, следует ли переименовать зависимые объекты – отвечаем «Да».
Добавим в файл ссылку
Затем изменим код конструктора класса MyServiceInstaller:
Заметим, что вызов метода InitializeComponent() мы заблокировали с помощью комментария.
На этом разработка службы Windows завершена. Собираем всё решение (Build Solution) и переходим к следующему этапу – установка службы Windows.
Для установки нашей службы создадим bat-файл (с произвольным названием, например Install_Windows_Service.bat) следующего содержания:
Нужно скопировать этот bat-файл в ту же папку, где находится скомпилированный файл WindowsServiceHostForMyService.exe (вам нужно заранее продумать, в какой папке будет лежать этот файл, который будет всегда запущен в качестве службы Windows).
Запускаем bat-файл, после чего наша программа WindowsServiceHostForMyService.exe будет установлена в качестве службы Windows.
Запустим эту службу с помощью стандартной программы управления службами.
Следующий этап – разработка клиентского приложения для использования предоставляемых сервисом услуг.
Для этого прежде всего нужно организовать так называемый «переходник» — Service Proxy – набор настроек, описывающих сервис для клиентского приложения.
Воспользуемся для этого стандартной утилитой SvcUtil.exe. Создадим файл Generate_Proxy.bat следующего содержания
Запустим этот файл (стандартная утилита SvcUtil.exe находится в папке C:\Program Files\Microsoft SDKs\Windows\v7.0\Bin).
Этот файл нужно запустить во время работы нашего сервиса, т.е. в данном случае, после успешного запуска службы Windows WindowsServiceHostForMyService.
В случае успешного запуска, программа SvcUtil.exe сгенерирует 2 файла — MyServiceProxy.cs и App.config.
Эти файлы необходимо добавить для клиентского приложения, чтобы это приложение могло вызывать методы нашей службы (чуть ниже вы узнаете, что файл App.config я решил не добавлять — обойдёмся и без него).
Примечание. Аналогичного результата можно было добиться, запустив
Т.е. можно запускать эту утилиту, указав только одну конечную точку, либо http либо net.tcp.
В том же решении (Solution) создадим обычное приложение Windows Forms. Назовем его WindowsFormsApplication1

Добавим в этот проект ссылку на System.ServiceModel и, конечно же,
Добавим в этот проект файл MyServiceProxy.cs (именно его мы сгенерировали утилитой SvcUtil.exe). При этом следует добавить в файл MyServiceProxy.cs следующие строки:
После этого, мы сможем ссылаться на класс MyServiceClient (этот класс создан программой SvcUtil.exe), указав в файле формы директиву.
Поступим неординарно – и не будем добавлять файл App.Config в проект клиента!
Затем набросаем на форму 3 кнопки, текстовое поле textBox1 (пользователь вводит сюда строку для отправки на сервер) и поле richTextbox1 (имитация подсистемы сообщений нашего приложения – именно в это поле будут поступать сообщения от программы, в том числе и те, что вернул нам сервис)
Кнопка btn_Start – создаёт клиента
Кнопка btn_Send – отправляет сервису текстовую строку из текстового поля
Кнопка btn_Close – удаляет клиента из памяти и закрывает приложение
Компилируем и запускаем с другой машины из той же сети – и тестируем сервис, нажав сначала btn_Start, а затем отправляя сервису сообщения нажатием btn_Send.

Заметим, что в данном примере на клиенте мы совсем не использовали конечную точку
а работали только с
(вы легко сможете это сделать самостоятельно – раз уж вам net.tcp по плечу, то уж http-то с закрытыми глазами сделаете).
Кроме того, мы не использовали файл App.config, создав на клиенте конечную точку не с помощью настроек, а с помощью кода C#. Причины тому – субъективные – автор не любит возиться с XML-настройками, а по возможности всё делает явно в коде. Спасибо за внимание!
Лирическое отступление. Автор статейки познакомился с языком C# в марте сего года, первое приложение на C# написал в мае (до этого много лет формошлёпил на Delphi и даже на MS Access).
Открываем Visual Studio 2015 и создаём новый проект типа Class Library. Проект назовём WCFMyServiceLibrary.

Файл Class1.cs переименуем в MyService.cs и добавим ещё один класс, файл для которого назовём IMyService.cs.
Добавим ссылку на сборку System.ServiceModel.
В файле IMyService.cs опишем интерфейс:
using System.ServiceModel; namespace WCFMyServiceLibrary { [ServiceContract] public interface IMyService { [OperationContract] string Method1(string x); [OperationContract] string Method2(string x); } }
В файле MyService.cs опишем реализацию интерфейса:
namespace WCFMyServiceLibrary { public class MyService : IMyService { public string Method1(string x) { string s = $"1 You entered: {x} = = = 1"; return s; } public string Method2(string x) { string s = $"2 you entered: {x} = = = 2"; return s; } } }
На этом разработка сервиса завершена. Переходим к созданию службы Windows, которая будет контейнером для данного сервиса.
В том же решении (Solution) создадим новый проект типа «Служба Windows». Называем проект WindowsServiceHostForMyService.

Затем файл Service1.cs (только что созданного проекта) переименуем в MyService.cs. В этот проект добавим ссылку на сборку System.ServiceModel, а также не забываем указывать в файле MyService.cs директивы:
using System.ServiceModel; using System.ServiceModel.Description;
В классе MyService добавляем новый член:
private ServiceHost service_host = null;
Также необходимо добавить ссылку на проект WCFMyServiceLibrary, который находится в этом же решении:

Затем в классе MyService изменим метод OnStart таким образом, чтобы в этом методе добавлялись конечные точки нашего сервиса (endpoint):
метод OnStart
protected override void OnStart(string[] args) { if (service_host != null) service_host.Close(); string address_HTTP = "http://localhost:9001/MyService"; string address_TCP = "net.tcp://localhost:9002/MyService"; Uri[] address_base = { new Uri(address_HTTP), new Uri(address_TCP) }; service_host = new ServiceHost(typeof(WCFMyServiceLibrary.MyService), address_base); ServiceMetadataBehavior behavior = new ServiceMetadataBehavior(); service_host.Description.Behaviors.Add(behavior); BasicHttpBinding binding_http = new BasicHttpBinding(); service_host.AddServiceEndpoint(typeof(WCFMyServiceLibrary.IMyService), binding_http, address_HTTP); service_host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex"); NetTcpBinding binding_tcp = new NetTcpBinding(); binding_tcp.Security.Mode = SecurityMode.Transport; binding_tcp.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows; binding_tcp.Security.Message.ClientCredentialType = MessageCredentialType.Windows; binding_tcp.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign; service_host.AddServiceEndpoint(typeof(WCFMyServiceLibrary.IMyService), binding_tcp, address_TCP); service_host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexTcpBinding(), "mex"); service_host.Open(); }
Затем реализуем остановку сервиса в методе OnStop:
protected override void OnStop() { if (service_host != null) { service_host.Close(); service_host = null; } }
Затем в Обозревателе решения — двойной клик на файле MyService.cs (проекта WindowsServiceHostForMyService) откроет этот файл в режиме конструктора (Design Mode).

На пустом пространстве вызываем контекстное меню (щелчок правой кнопкой мыши) и выбираем «Добавить установщик».

При этом будет создан новый класс ProjectInstaller.cs
Переименуем файл ProjectInstaller.cs в MyServiceInstaller.cs.
При этом выйдет окно с вопросом, следует ли переименовать зависимые объекты – отвечаем «Да».
Добавим в файл ссылку
using System.ServiceProcess;
Затем изменим код конструктора класса MyServiceInstaller:
public MyServiceInstaller() { // InitializeComponent(); serviceProcessInstaller1 = new ServiceProcessInstaller(); serviceProcessInstaller1.Account = ServiceAccount.LocalSystem; serviceInstaller1 = new ServiceInstaller(); serviceInstaller1.ServiceName = "WindowsServiceHostForMyService"; serviceInstaller1.DisplayName = "WindowsServiceHostForMyService"; serviceInstaller1.Description = "WCF Service Hosted by Windows NT Service"; serviceInstaller1.StartType = ServiceStartMode.Automatic; Installers.Add(serviceProcessInstaller1); Installers.Add(serviceInstaller1); }
Заметим, что вызов метода InitializeComponent() мы заблокировали с помощью комментария.
На этом разработка службы Windows завершена. Собираем всё решение (Build Solution) и переходим к следующему этапу – установка службы Windows.
Для установки нашей службы создадим bat-файл (с произвольным названием, например Install_Windows_Service.bat) следующего содержания:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe WindowsServiceHostForMyService.exe
Нужно скопировать этот bat-файл в ту же папку, где находится скомпилированный файл WindowsServiceHostForMyService.exe (вам нужно заранее продумать, в какой папке будет лежать этот файл, который будет всегда запущен в качестве службы Windows).
Запускаем bat-файл, после чего наша программа WindowsServiceHostForMyService.exe будет установлена в качестве службы Windows.
Запустим эту службу с помощью стандартной программы управления службами.
Следующий этап – разработка клиентского приложения для использования предоставляемых сервисом услуг.
Для этого прежде всего нужно организовать так называемый «переходник» — Service Proxy – набор настроек, описывающих сервис для клиентского приложения.
Воспользуемся для этого стандартной утилитой SvcUtil.exe. Создадим файл Generate_Proxy.bat следующего содержания
SvcUtil http://localhost:9001/MyService /out:MyServiceProxy.cs /config:App.config
Запустим этот файл (стандартная утилита SvcUtil.exe находится в папке C:\Program Files\Microsoft SDKs\Windows\v7.0\Bin).
Этот файл нужно запустить во время работы нашего сервиса, т.е. в данном случае, после успешного запуска службы Windows WindowsServiceHostForMyService.
В случае успешного запуска, программа SvcUtil.exe сгенерирует 2 файла — MyServiceProxy.cs и App.config.
Эти файлы необходимо добавить для клиентского приложения, чтобы это приложение могло вызывать методы нашей службы (чуть ниже вы узнаете, что файл App.config я решил не добавлять — обойдёмся и без него).
Примечание. Аналогичного результата можно было добиться, запустив
SvcUtil net.tcp://localhost:9002/MyService /out:MyServiceProxy.cs /config:App.config
Т.е. можно запускать эту утилиту, указав только одну конечную точку, либо http либо net.tcp.
В том же решении (Solution) создадим обычное приложение Windows Forms. Назовем его WindowsFormsApplication1

Добавим в этот проект ссылку на System.ServiceModel и, конечно же,
using System.ServiceModel в файле формы.
Добавим в этот проект файл MyServiceProxy.cs (именно его мы сгенерировали утилитой SvcUtil.exe). При этом следует добавить в файл MyServiceProxy.cs следующие строки:
namespace ServiceReference1 { using System.Runtime.Serialization; using System; … затем идёт содержимое файла MyServiceProxy.cs … и конечно, не забываем поставить завершающую скобку для namespace }
После этого, мы сможем ссылаться на класс MyServiceClient (этот класс создан программой SvcUtil.exe), указав в файле формы директиву.
using ServiceReference1;
Пример готового файла MyServiceProxy.cs (комментарии удалены):
namespace ServiceReference1 { using System.Runtime.Serialization; using System; [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")] [System.ServiceModel.ServiceContractAttribute(ConfigurationName="IMyService")] public interface IMyService { [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IMyService/Method1", ReplyAction="http://tempuri.org/IMyService/Method1Response")] string Method1(string x); [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IMyService/Method2", ReplyAction="http://tempuri.org/IMyService/Method2Response")] string Method2(string x); } [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")] public interface IMyServiceChannel : IMyService, System.ServiceModel.IClientChannel { } [System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")] public partial class MyServiceClient : System.ServiceModel.ClientBase<IMyService>, IMyService { public MyServiceClient() { } public MyServiceClient(string endpointConfigurationName) : base(endpointConfigurationName) { } public MyServiceClient(string endpointConfigurationName, string remoteAddress) : base(endpointConfigurationName, remoteAddress) { } public MyServiceClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : base(endpointConfigurationName, remoteAddress) { } public MyServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : base(binding, remoteAddress) { } public string Method1(string x) { return base.Channel.Method1(x); } public string Method2(string x) { return base.Channel.Method2(x); } } }
Поступим неординарно – и не будем добавлять файл App.Config в проект клиента!
Затем набросаем на форму 3 кнопки, текстовое поле textBox1 (пользователь вводит сюда строку для отправки на сервер) и поле richTextbox1 (имитация подсистемы сообщений нашего приложения – именно в это поле будут поступать сообщения от программы, в том числе и те, что вернул нам сервис)
Кнопка btn_Start – создаёт клиента
Кнопка btn_Send – отправляет сервису текстовую строку из текстового поля
Кнопка btn_Close – удаляет клиента из памяти и закрывает приложение
Код формы:
using System; using System.ServiceModel; using System.Windows.Forms; using ServiceReference1; namespace WindowsFormsApplication1 { public partial class Form1 : Form { MyServiceClient client = null; public Form1() { InitializeComponent(); } private void Print(string text) { richTextBox1.Text += text + "\n\n"; richTextBox1.SelectionStart = richTextBox1.Text.Length; richTextBox1.ScrollToCaret(); } private void Print(Exception ex) { if (ex == null) return; Print(ex.Message); Print(ex.Source); Print(ex.StackTrace); } private void Create_New_Client() { if (client == null) try { Try_To_Create_New_Client(); } catch (Exception ex) { Print(ex); Print(ex.InnerException); client = null; } else { Print("Cannot create a new client. The current Client is active."); } } private void Try_To_Create_New_Client() { try { NetTcpBinding binding = new NetTcpBinding(SecurityMode.Transport); binding.Security.Message.ClientCredentialType = MessageCredentialType.Windows; binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows; binding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign; string uri = "net.tcp://192.168.1.2:9002/MyService"; EndpointAddress endpoint = new EndpointAddress(new Uri(uri)); client = new MyServiceClient(binding, endpoint); client.ClientCredentials.Windows.ClientCredential.Domain = ""; client.ClientCredentials.Windows.ClientCredential.UserName = "Vasya"; client.ClientCredentials.Windows.ClientCredential.Password = "12345"; Print("Creating new client ...."); Print(endpoint.Uri.ToString()); Print(uri); string test = client.Method1("test"); if (test.Length < 1) { throw new Exception("Проверка соединения не удалась"); } else { Print("test is OK ! " + test); } } catch (Exception ex) { Print(ex); Print(ex.InnerException); client = null; } } private void btn_Start_Click(object sender, EventArgs e) { Create_New_Client(); } private void btn_Send_Click(object sender, EventArgs e) { Print("sending message . . ."); string s = textBox1.Text; string x = ""; if (client != null) { x = client.Method1(s); Print(x); x = client.Method2(s); Print(x); } else { Print("Error! Client does not exist!"); } } private void btn_Close_Click(object sender, EventArgs e) { if (client != null) { Print("Closing a client ..."); client.Close(); client = null; } else { Print("Error! Client does not exist!"); } this.Close(); } } }
Компилируем и запускаем с другой машины из той же сети – и тестируем сервис, нажав сначала btn_Start, а затем отправляя сервису сообщения нажатием btn_Send.

Заметим, что в данном примере на клиенте мы совсем не использовали конечную точку
http://localhost:9001/MyServiceа работали только с
net.tcp://localhost:9002/MyService(вы легко сможете это сделать самостоятельно – раз уж вам net.tcp по плечу, то уж http-то с закрытыми глазами сделаете).
Кроме того, мы не использовали файл App.config, создав на клиенте конечную точку не с помощью настроек, а с помощью кода C#. Причины тому – субъективные – автор не любит возиться с XML-настройками, а по возможности всё делает явно в коде. Спасибо за внимание!
Лирическое отступление. Автор статейки познакомился с языком C# в марте сего года, первое приложение на C# написал в мае (до этого много лет формошлёпил на Delphi и даже на MS Access).
