Tuesday, October 27, 2009

Lab #13: Creating a Windows Service Host

Members:

Rafael Zuniga, Stephen Cave

Summary:

This lab will focus on creating and installing a Windows Service Host. A client will also be made which will be able to use the host.

Procedure:

We being by opening the existing solution which can be found in Chapter4/WindowsServiceHost in the labs directory. Next we add a new Windows Service project and rename the default service to MessagingServiceHost. The default constructor is modified as follows:

public MessagingServiceHost()
{
InitializeComponent();
this.ServiceName = "MessagingServiceHost";
}


Next we add a new application configuration and add a service model section which will expose an endpoint for our client to use. A reference to the Messaging project and the ServiceModel assembly is added. The OnStart and OnStop events are modified so the service begins and closes when the service is started and stopped:

protected override void OnStart(string[] args)
{
m_serviceHost = new ServiceHost(typeof(Messaging.MessagingService)); m_serviceHost.Open();
}

protected override void OnStop()
{
if (m_serviceHost != null) m_serviceHost.Close(); m_serviceHost = null;
}


A reference to Configuration.Install is added in order to create an installer for the service. The project is then compiled and the service is installed successfully:


And we can see the that the service can be found in the service management console:

A proxy for the client is then generated using svcutil.exe and the generated App.config and serviceproxy.cs are added to the project. A new click event is added to the client's call service button:

private void button1_Click(object sender, EventArgs e)
{
MessagingServiceClient proxy = new MessagingServiceClient();
MessageBox.Show(proxy.SendMessage(string.Format("Hello from {0}",
this.Text)));
}


The project is then compiled. And that's about it. There's problems running the service and when it does run it hates the client with a fiery passion. See below.

Observations:

  1. Windows Vista/7 are really harsh on what can and can't run
  2. The service installs fine but is unable to run due to the Network Service account not having enough privileges
  3. Changing the account to run the service under to system account allows it to run fine
  4. The problem is then on the client's end because it will not connect to the host giving the error "Credentials Rejected"
  5. To get around this the client must impersonate the credentials of a user with administrative access
  6. Even then it doesn't work due to the alignment of the moon and stars

Lab #12: Working with a Windows Forms Host

Members:

Rafael Zuniga, Stephen Cave

Summary:

The purpose of this lab is to add an existing service to a Windows Forms application and to experiment with starting the service from different locations.

Procedure:

We start by opening the solution found in Chapter4/WindowsApplicationHost which can be found under the labs directory. Right from the start the solution only builds two of the projects with the actual Windows Forms project failing since it lacks a reference to System.ServiceModel. We add the missing reference and also add a reference to the Message service project which is contained within the same solution. This time the project builds succesfully:

We must now modify the App.config for the host since it will require endpoint definitions for the service to be used:



behaviorConfiguration="serviceBehavior">



Next we modify the Windows Forms host code itself which is found in Program.cs:

static class Program
{
public static ServiceHost MessageServiceHost;

[STAThread]
static void Main()
{
Application.ApplicationExit += new EventHandler(Application_ApplicationExit); Program.MessageServiceHost = new ServiceHost(typeof(Messaging.MessagingService)); Program.MessageServiceHost.Open();

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}

static void Application_ApplicationExit(object sender, EventArgs e) { Program.MessageServiceHost.Close(); Program.MessageServiceHost = null; }
}


This code will allow the service to begin at application start up and shut down when the application exits. Next we must modify the client to use the service. We start by adding a service reference to our client which references the host project. Next we add a new button to Form1 and add an event handler to send a message to our host:

private void button1_Click(object sender, EventArgs e)
{
localhost.MessagingServiceClient proxy = new
Client.localhost.MessagingServiceClient();
proxy.SendMessage(
string.Format("Hello from {0}", this.Text));
}


Compiling and running the solution give us two windows. One is running the service while the other is the client which can call the service. Calling the service on the client results in the following:

In the image above the host is running in its own thread. Next we will modify the service to run within the UI thread. We begin by removing all of the previous start up code from the host and adding two buttons to Form1. Then we modify the start up code:

ServiceHost m_serviceHost;

public Form1()
{
InitializeComponent();
this.button2.Enabled = false; m_serviceHost = new ServiceHost( typeof(Messaging.MessagingService));
}


Two event handlers are added for each button; one to start the service and one to stop it:

private void button1_Click(object sender, EventArgs e)
{
this.button1.Enabled = false;
this.button2.Enabled = true;
m_serviceHost.Open();
}

private void button2_Click(object sender, EventArgs e)
{
this.button1.Enabled = true;
this.button2.Enabled = false;
m_serviceHost.Close();
}


The close event is also modified to close the service when the form is closed:

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
DialogResult result = MessageBox.Show(
"Are you sure you want to close the service?",
"Service Controller", MessageBoxButtons.YesNo,
MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);

if (result == DialogResult.Yes) { if (m_serviceHost != null) { m_serviceHost.Close(); m_serviceHost = null; } } else
e.Cancel = true;
}


We recompile and run the solution resulting in:


Here the service is running within the UI's message loop. Modifying the implementation of IMessagingService by adding the ServiceBehavior attribute with UseSynchronizationContext set to false forces the service to operate outside of the message loop and within its own thread. Next we will modify the service to allow for callbacks to the client. We modify MessagingService.cs by adding a callback attribute to the previous IMessagingService interface and creating a new interface called IMessagingServiceCallback:

public interface IMessagingServiceCallback
{
[OperationContract(IsOneWay = true)]
void MessageNotification(string message);
}


We next modify the client by updating the service reference, creating a new class named MessagingServiceCallback which implements the IMessagingServiceCallback interface, and adding code to provide a context to the client proxy. The end result is show below:


Observations:

  1. You can host a service through a GUI application and have it start in it's own thread by either starting it before the GUI is created or adding an attribute to force it into it's own thread.
  2. Running a service in the GUI thread is probably a bad idea since the message pump has a whole lot of things to handle already.
  3. Gotta freakin' run with administrator privileges
  4. Blogger butchered some of the XML configuration file stuff up there
  5. Is there a Blogger equivalent to [code][/code] tags most forums have?

Tuesday, October 20, 2009

Lab #11: Throwing Faults and Declaring Fault Contracts

Members:

Stephen Cave, Rafael Zuniga

Summary
:

The purpose of this lab is to demonstrate how you can declare fault contracts, throw faults, and trap faults.

Procedure

We start by opening up the provided Visual Studio solution found in the Chapter8/FaultContract/ of our labs directory. It opens up and compiles fine as you can see below:

Attempting to run the server we find the following error:


Following the link gives a solution involving registering our URL as a privileged since we're using Windows 7 which, like Vista, does not run everything with administrator privileges. Instead of registering the host we instead run it with administrator privileges. This time we have the host running successfully and can attempt to upload an image which results in:


An exception message that doesn't really tell us much. Next we modify the code in PhotoManagerService.cs to throw a fault exception instead by adding the following to the UploadPhoto method:

try {
PhotoUploadUtil photoUploadUtil = new PhotoUploadUtil();
photoUploadUtil.SavePhoto(fileInfo, fileData);
} catch (ConfigurationErrorsException exConfig) { throw new FaultException(exConfig.Message); }

We compile again, run it, and this time we see the following e:


Which is much more descriptive and helps us in figuring out what's gone wrong. Next we add an attribute to the UploadPhoto method declaration within the IPhotoUpload interface. This will allow us to throw a strongly typed exception when a fault occurs which is useful if you'd like to use custom exception classes. To continue we need to modify our previous exception catching code:

catch (ConfigurationErrorsException exConfig)
{
throw new FaultException(new ConfigurationErrorsException(exConfig.Message), new FaultReason(exConfig.Message), FaultCode.CreateReceiverFaultCode(new FaultCode("ConfigurationErrorsException")));
}


We also modify the client side proxy to support the new exception contract by adding an identical fault contract attribute to the existing code which can be found in localhost.cs and adding an assembly reference to the client project. Now the client must be modified to actually catch the exception:

catch(FaultException exConfig)
{
string s = String.Format("{0}\r\nERROR:(1)",
exConfig.GetType(), exConfig.Detail.Message);
MessageBox.Show(s);
}


We recompile and now see our strongly typed fault exception:


Observations:

  1. Windows 7 is pretty neat but the UAC options can really throw you for a loop if you're used to everything running as an administrator
  2. The default settings in for the TCP transport cause it to choke on larger images
  3. Being able to give a user a better error than the cryptic "ERROR: -14" is always a good thing
  4. I'm surprised that the error in the first screen shot also gave me a link on how to resolve it

Lab #10: Working with Uncaught Exceptions

Members:

Rafael Zuniga, Stephen Cave

Summary:

The purpose of this lab is to show how exceptions are reported in WCF.

Procedure:

We begin by opening the solution found in Chapter8/Exceptions in the lab directory. The project is compiled successfully and the host and client are run:



Next we try to upload theband.jpg which results in a fault:



The current exception message does not help in solving what the problem is so we modify the service application configuration to include debugging by adding a new element to the behavior section. We add a serviceDebug element with the includeExceptionDetailsInFailts attribute set to true. Now when we run the project again we get error information which helps us identify the problem:



Observations:

  1. If you're debugging you should probably turn this on
  2. This lab was really short

Tuesday, October 13, 2009

Lab #9: Using MTOM to Handle Large Messages with Binary Data

Members:

Rafael Zuniga, Stephen Cave

Summary:

The purpose of this lab is to how MTOM can be used to upload large files over HTTP.

Procedure:

We begin by opening the solution in Chapter3/LargeMessages/ which is in the labs directory. Next we attempt to run it and:



It seems the SQL database is not set up so we need to install SQL Server Manager Express edition to install the SQL database included with the lab. Running the setup gives us:



Ok, let's find out if there's a solution:



Well that would help except we just want SQL Server Manager Express but apparently Microsoft hates us and has us download the whole SQL server instead of offering it stand alone.

Observations:
  1. Early adopters beware

Tuesday, October 6, 2009

Lab 8: Distributing Calls with NetNamedPipeBinding and NetTcpBinding

Members:
Rafael Zuniga, Stephen Cave

Summary:
Unable to initiate database for query access and submittal. Trying to run without the DB produces the following.

Attempting to add database to the solution as a project yeilds failure.


Notes on Usefulness:
There is nothing useful about failure save motivation to try again later.

Problems/Mistakes/Differences -> troubleshooting encountered:
-Unable to run querry string on database, or attach database to solution. Cannot continue. Potentially missing installation features.