As you all know windows services run in background and user can start or configure them through Services panel in Windows Operating System. In order to do the logging to check what the service is doing you can log messages in database, message queues, event logs etc. But in order to send back and forth messages to any windows application to show real time task execution is only possible via TCP socket connection, remoting etc.
In this post I will show you the way of logging messages from Windows Services to Windows form based Monitor application using WCF Duplex Messaging.
Step 1: Creating WCF Service Library
- Create a WCF Service Library project from Visual Studio
- Add a Service Contract interface named as “IMonitorService”
[ServiceContract(
Name = “IMonitorService”,
SessionMode = SessionMode.Required,
CallbackContract = typeof(IMonitorServiceCallback))]
public
interface
IMonitorService
{
[OperationContract]
void Login();
[OperationContract]
void Logout();
[OperationContract]
void LogMessages(String message);
}
In the above code, I have created an interface and provided three methods signature Login, Logout and LogMessages. Login and Logout will be used to connect or disconnect to the Service and LogMessages is used to log messages to client. You will notice that in the ServiceContract attribute I have specified CallbackContract which actually holds the type information of the Call back contract interface through which when the client invoke any of the login, logout or logmessages server can get the callbackcontract object and sends the response back to client itself.
- Here is the code for IMonitorServiceCallback contract interface.
public
interface
IMonitorServiceCallback
{
[OperationContract]
void NotifyClient(String message);
}
In the above code there is a callback contract interface which holds one method signature “NotifyClient” which takes string as a parameter. Server can call NotifyClient method and send messages to the connected clients.
-
Now create another class MonitorService and implement IMonitorService interface. Following code shows the complete
code shows the complete implementation.
[ServiceBehavior(
ConcurrencyMode = ConcurrencyMode.Reentrant,
InstanceContextMode = InstanceContextMode.PerCall)]
public
class
MonitorService : IMonitorService
{
public
static
List<IMonitorServiceCallback> callBackList = new
List<IMonitorServiceCallback>();
public MonitorService()
{
}
public
void Login()
{
IMonitorServiceCallback callback = OperationContext.Current.GetCallbackChannel<IMonitorServiceCallback>();
if (!callBackList.Contains(callback))
{
callBackList.Add(callback);
}
}
public
void Logout()
{
IMonitorServiceCallback callback = OperationContext.Current.GetCallbackChannel<IMonitorServiceCallback>();
if (callBackList.Contains(callback))
{
callBackList.Remove(callback);
}
callback.NotifyClient(“You are Logged out”);
}
public
void LogMessages(string message)
{
foreach (IMonitorServiceCallback callback in callBackList)
{
callback.NotifyClient(message);
}
}
The above code shows the implementation of the IMonitorService interface.
Step 2: Create Windows Service project and use WCF Service Library
- Create a new “Windows Service” project using Visual Studio.
- In the Start method write some code to let service app do some work.
- Add project reference of the WCF Service Application
-
Initialize the ServiceHost object of WCF framework
ServiceHost host = new
ServiceHost(typeof(MonitorService));
host.Open();
- Implement the LogMessage method and notify callback contracts.
foreach (IMonitorServiceCallback callback in
MonitorService.callBackList)
{
callback.NotifyClient(message);
}
-
App.config for Windows Service
<?xml
version=“1.0“
encoding=“utf-8“ ?>
<configuration>
<system.web>
<compilation
debug=“true“ />
</system.web>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding
name=“DefaultNetTCPBinding“
receiveTimeout=“Infinite“>
<reliableSession
inactivityTimeout=“Infinite“ />
</binding>
</netTcpBinding>
</bindings>
<services>
<service
name=“MonitorService“>
<host>
<baseAddresses>
<add
baseAddress=“net.tcp://localhost:9909/MonitorService/“ />
</baseAddresses>
</host>
<!– Service Endpoints –>
<!– Unless fully qualified, address is relative to base address supplied above –>
<endpoint
address=“service“
binding=“netTcpBinding“
bindingConfiguration=“DefaultNetTCPBinding“
contract=“IMonitorService“
name=“TcpBinding“ />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<!– To avoid disclosing metadata information,
set the value below to false and remove the metadata endpoint above before deployment –>
<serviceMetadata
httpGetEnabled=“False“/>
<!– To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information –>
<serviceDebug
includeExceptionDetailInFaults=“False“ />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Step 3: Developing Monitor Application
- Create a new Windows Application project using Visual Studio.
- Add RichTextBox control to log messages
- Add two buttons connect and disconnect.
- Now Add the WCF Service reference
- Implement Login, Logout and NotifyClient messages
- Add following code in the Login method
- Implement IMonitorServiceCallback interface and write below code.
try
{
client = new
MonitorServiceClient(new
InstanceContext(this), “TcpBinding”);
client.Open();
client.Login();
WriteTextMessage(“Monitor successfully connected to the Windows Service for logging messages”);
}
catch (Exception ex)
{
WriteTextMessage(“Couldn’t connect to the Service, cause “ + ex.Message);
}
- Add following code in the Logout nethod
client.Close();
- Add following code in the NotifyClient method.
public
void LogMessage(string message)
{
if (this.InvokeRequired == false)
{
this.BeginInvoke(new
WriteMessage(WriteTextMessage),message);
}
else
{
this.Invoke(new
WriteMessage(WriteTextMessage), message);
}
}
- As the application thread is different so we need to invoke the WriteTextMessage using BeginInvoke. In that case I have declared a delegate with the same method signature as of WriteTextMessage and set messages in the RichTextBox control.
public
delegate
void
WriteMessage(String str);
public
void WriteTextMessage(String str)
{
rchTextBox.Text += str + “\n”;
rchTextBox.ScrollToCaret();
}
- App.config for Monitor App
<?xml
version=“1.0“
encoding=“utf-8“ ?>
<configuration>
<appSettings>
<add
key=“DatabaseServer“
value=“.\sqlexpress“/>
</appSettings>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding
name=“TcpBinding“
closeTimeout=“00:01:00“
openTimeout=“00:01:00“
receiveTimeout=“00:10:00“
sendTimeout=“00:01:00“
transactionFlow=“false“
transferMode=“Buffered“
transactionProtocol=“OleTransactions“
hostNameComparisonMode=“StrongWildcard“
listenBacklog=“10“
maxBufferPoolSize=“524288“
maxBufferSize=“65536“
maxConnections=“10“
maxReceivedMessageSize=“65536“>
<readerQuotas
maxDepth=“32“
maxStringContentLength=“8192“
maxArrayLength=“16384“
maxBytesPerRead=“4096“
maxNameTableCharCount=“16384“ />
<reliableSession
ordered=“true“
inactivityTimeout=“00:10:00“
enabled=“false“ />
<security
mode=“Transport“>
<transport
clientCredentialType=“Windows“
protectionLevel=“EncryptAndSign“ />
<message
clientCredentialType=“Windows“ />
</security>
</binding>
</netTcpBinding>
<wsDualHttpBinding>
<binding
name=“WSDualHttpBinding_IMonitorService“
closeTimeout=“00:01:00“
openTimeout=“00:01:00“
receiveTimeout=“00:10:00“
sendTimeout=“00:01:00“
bypassProxyOnLocal=“false“
transactionFlow=“false“
hostNameComparisonMode=“StrongWildcard“
maxBufferPoolSize=“524288“
maxReceivedMessageSize=“65536“
messageEncoding=“Text“
textEncoding=“utf-8“
useDefaultWebProxy=“true“>
<readerQuotas
maxDepth=“32“
maxStringContentLength=“8192“
maxArrayLength=“16384“
maxBytesPerRead=“4096“
maxNameTableCharCount=“16384“ />
<reliableSession
ordered=“true“
inactivityTimeout=“00:10:00“ />
<security
mode=“Message“>
<message
clientCredentialType=“Windows“
negotiateServiceCredential=“true“
algorithmSuite=“Default“ />
</security>
</binding>
</wsDualHttpBinding>
</bindings>
<client>
<endpoint
address=“net.tcp://localhost:9909/MonitorService/service“
binding=“netTcpBinding“
bindingConfiguration=“TcpBinding“
contract=“IMonitorService“
name=“TcpBinding“>
<identity>
<userPrincipalName
value=“ovais“ />
</identity>
</endpoint>
<endpoint
address=“http://localhost:8732/Design_Time_Addresses/MonitorService/“
binding=“wsDualHttpBinding“
bindingConfiguration=“WSDualHttpBinding_IMonitorService“
contract=“IMonitorService“
name=“WSDualHttpBinding_IMonitorService“>
<identity>
<dns
value=“localhost“ />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
Step 4: Running solution
- Install the service and start it
- Start the Monitor app and click on connect
- Once the service start it will send messages to client and real time logging is achieved.