Thursday, November 19, 2009

Compact Framework and forms management.

This was my first experience with the Microsoft Compact Framework. The application is developed in Visual Studio 2005 and is targeted to run on Pocket PC 2003 devices. Basically, I had to extend a simple application that had only one form to add some functionality and a few more forms. I understand that the forms in Compact Framework are treated a bit differently compared to a desktop application. What to use for navigation between forms, Show() or ShowDialog()? I decided to use Show() because I have only about 5 forms, most of those are very simple and also, my application will be the only one running on the device. So I thought, if I create each form once and keep them all in memory, just showing and hiding them, it may use more memory, which I do not care that much about, but be easier on the device battery. Okay, I may be saying total nonsense here - I have about 7 days Compact Framework development experience at this very moment.

So I have a dictionary where all existing forms are kept.

private static Dictionary _applicationForms = new Dictionary();

And the function that gets the form from the dictionary by name.

internal static Form GetFormByName(string formName)
{
if (_applicationForms.ContainsKey(formName))
{
return _applicationForms[formName];
}
else
{
Form newForm = CreateFormByName(formName);
AddFormIfNotExists(newForm);
return newForm;
}
}

And the function to create a form if it has not been yet created.

private static Form CreateFormByName(string name)
{
Form form = new Form();

switch (name)
{
case Constants.frmFirst:
form = new frmFirst();
break;

...

case Constants.frmLast:
form = new frmLast();
break;
default:
form = new frmLast();
break;
}
return form;
}

And the function to add the form to the dictionary if it is not there.

internal static void AddFormIfNotExists(Form frm)
{
if (!_applicationForms.ContainsKey(frm.Name))
{
_applicationForms.Add(frm.Name, frm);
}
}

And when I need to show another form, I get it from the dictionary and show, and hide the current form.

internal static void ShowFromForm(Form source, string targetName)
{
Form frm = GetFormByName(targetName);
frm.Show();
source.Hide();
}

There's a bit more to it, sometimes I need to find which form is currently visible etc, but these are the core things. Stupid? Good enough? I don't know ...

by . Also posted on my website

Saturday, October 3, 2009

Doing Some Stuff on Another Computer

It is fairly easy to restart a service running on a remote computer. You only need to know two things - the name of a remote computer and the name of the service itself. No surprises.

private void RestartService(string MachineName, string ServiceName)
{
using (ServiceController controller = new ServiceController())
{
controller.MachineName = MachineName;
controller.ServiceName = ServiceName;
controller.Stop();
controller.Start();
}
}

Almost as easy is to monitor the printers on the remote computer using WMI. This time, only the remote computer name is required. Here's a small function that returns the list of CustomPrinterObjects. CustomPrinterObject can be defined like this, for example:

public class CustomPrinterObject
{
private string _name;

public string Name
{
get { return _name; }
set { _name = value; }
}

//many other properties
....

private string _status;

public string Status
{
get { return _status; }
set { _status = value; }
}
}

Here's how I get the information about the printers on the remote computer:

public List GetLocalPrinters(string serverName)
{
string[] pStatus = {"Other","Unknown","Idle","Printing","WarmUp","Stopped Printing",
"Offline"};

string[] pState = {"Paused","Error","Pending Deletion","Paper Jam","Paper Out",
"Manual Feed","Paper Problem", "Offline","IO Active","Busy",
"Printing","Output Bin Full","Not Available","Waiting",
"Processing","Initialization","Warming Up","Toner Low",
"No Toner","Page Punt", "User Intervention Required",
"Out of Memory","Door Open","Server_Unknown","Power Save"};

List printers = new List();

string query = string.Format("SELECT * from Win32_Printer");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
searcher.Scope = new ManagementScope("\\\\" + serverName + "\\root\\CIMV2");
ManagementObjectCollection coll = searcher.Get();

System.Windows.Forms.MessageBox.Show(coll.Count.ToString());

foreach (ManagementObject printer in coll)
{
CustomPrinterObject prn = new CustomPrinterObject();

foreach (PropertyData property in printer.Properties)
{
if (property.Value != null)
{
switch (property.Name)
{
case "Name":
prn.Name = property.Value.ToString();
break;
case "Comment":
prn.Comment = property.Value.ToString();
break;
case "PrinterState":
prn.PrinterState = pState[Convert.ToInt32(property.Value)];
break;
case "PrinterStatus":
prn.PrinterStatus = pStatus[Convert.ToInt32(property.Value)];
break;
case "Location":
prn.Location = property.Value.ToString();
break;
case "Type":
prn.Type = property.Value.ToString();
break;
case "DriverName":
prn.Model = property.Value.ToString();
break;
case "WorkOffline":
prn.Status = property.Value.ToString().Equals("True") ? "Offline" : "Online";
break;
default:
break;
}
}
}
printers.Add(prn);
}
return printers;
}

Reading the registry contents on the remote machine is very easy again.

On the local computer I would open the key like this

RegistryKey rk = Registry.LocalMachine.OpenSubKey(subKey);

And on the remote I would do it like this

RegistryKey hklm = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, "MyRemoteServer");
RegistryKey rk = hklm.OpenSubKey(subKey);

Obviously, all of the samples will work subject to permissions of the account that runs them. Errors will happen if the account does not have enough privileges to access the printers or services on the remote computer.

by . Also posted on my website

Thursday, October 1, 2009

Simple WCF client/server

So communicating between two windows services on the same computer is easy. But then I was asked, what if we decide in the future that we want these services to run on the separate machines? Well, I guess we'll need to make changes to both of them ... and that's exactly what we want to avoid. Okay, the WCF offers a few ways to host a service - in a managed application, in a managed windows service, in IIS, in WAS ... (Hosting Options) Since I already have windows services implemented, the choice is obvious. I looked up a couple of tutorials on the subject fairly quickly:
How to: Host a WCF Service in a Managed Windows Service, WCF Tutorial - Basic Interprocess Communication

However, that was not quite enough for me because the first tutorial's problem was that it explained the configuration file a bit, but did not implement the "client", and the second tutorial implemented both server and client, but had no info on configuration at all. So I quickly got to the point where I could have a server and client running inside separate windows services on the same machine, but as soon as I tried taking one of the services away - to another computer on the network - different errors started to happen. Not enough time and space on explaining each error and what was the reason for it, just a few words on what I ended up with (which eventually worked).

Service implementation

To define and implement the function on the server:

[ServiceContract(Namespace="MyNamespace.IMyInterface")]
public interface IMyInterface
{
[OperationContract]
string ReturnMyString();
}

public class MyInterfaceImplementation : IMyInterface
{
public string ReturnMyString()
{
return "My String";
}
}

To create the instance of the host, first define the host

private ServiceHost host;

In the service OnStart method

if (host != null)
{
host.Close();
}

host = new ServiceHost(typeof(MyInterfaceImplementation), new Uri[] { new Uri(http://MyServer:8080) });
host.AddServiceEndpoint(typeof(IMyInterface), new BasicHttpBinding(), "MyMethod");

In the service OnStop method

if (host != null)
{
host.Close();
host = null;
}

This part was fairly easy.

Service configuration

This bit went into the app.config file inside the "configuration".

<system.serviceModel>
<services>
<service name="MyNamespace.MyService" behaviorConfiguration="MyServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="http://MyServer:8000/MyMethod"/>
</baseAddresses>
</host>
<!-- this endpoint is exposed at the base address provided by host-->
<endpoint address="" binding="basicHttpBinding" contract="MyNamespace.IMyInterface" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="False"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>

Note the service name attribute "MyNamespace.MyService" which is the windows service names, and the endpoint contract attribute, which is the ServiceContract Namespace attribute. Some small things are easy to get wrong, and the error messages will not be very informative.

Client implementation

[ServiceContract(Namespace="MyNamespace.IMyInterface")]
public interface IMyInterface
{
[OperationContract]
string ReturnMyString();
}

public string MyClientString()
{
string result = string.Empty;

string endpoint = "http://MyServer:8000/MyMethod";

ChannelFactory httpFactory = new ChannelFactory(
new BasicHttpBinding(), new EndpointAddress(endpoint));

IMyInterface httpProxy = httpFactory.CreateChannel();

while (true)
{
result = httpProxy.ReturnMyString();
if (result != string.Empty)
{
return result;
}
}
}

I missed the [ServiceContract(Namespace="MyNamespace.IMyInterface")] bit initially on the interface definition and the error message was really not helping. It went like that: "Exception: The message with Action 'http://tempuri.org/IMyInterface/ReturnMyString' cannot be processed at the receiver" and so on. What tempuri.org? I never pun any tempuri.org in my project! OK, turns out it is some default name that was used because I have not provided my own.

Client configuration

Just a small bit of configuration was required here (and I'm not even 100% sure that all of it is required)

<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="basicHttp"/>
</basicHttpBinding>
</bindings>
<client>
<!-- this endpoint is exposed at the base address provided by host-->
<endpoint address="" binding="basicHttpBinding" contract="MyNamespace.IMyInterface" />
</client>
</system.serviceModel>

Overall, it's just a few dozen lines of code, but it took me almost a whole day to get it working properly through the network.

by . Also posted on my website

Wednesday, September 16, 2009

A Small Unit Testing Gem

Since I started writing unit tests for my code, I had this question in mind. Let's say I have a project that is a class library. I have a class in that library and this class has some internal methods. Like this:

public class MyClass
{
public void MyPublicMethod
{
int k
// do something ...
int z = MyInternalMethod(k);
// do something else ...
}

internal int MyInternalMethod(int i)
{
// do something ...
}
}

Now I want to write unit tests for these methods. I would create a "UnitTests" project, reference the nunit.framework from it and write something like this:

[TestFixture]
public class UnitTests
{
private MyClass myClass;

[SetUp]
public void SetupTest
{
myClass = new MyClass();
}

[Test]
public void TestMyInternalMethod
{
int z = 100;
int k = myClass.MyInternalMethod(z); //CAN NOT DO THIS!
Assert.AreEqual(k, 100000);
}

[TearDown]
public void TearDown
{
myClass = null;
}
}

Of course, I can not do this, because of the MyInternalMethod scope. Today the StackOverflow guys pointed me to this little gem which is very helpful.

.Net Gem - How to Unit Test Internal Methods

Here's the short summary:

Go to the project that contains MyClass. Locate the AssemblyInfo.cs file. Add the following line to it:

[assembly: InternalsVisibleTo("UnitTests")]

Done!

by . Also posted on my website

Thursday, September 10, 2009

Thread Pooling

I have to take care of multiple printers in my application. The "Print Manager" receives a list of jobs which is basically an XML file of a simple structure - a number of PrintJob nodes. Each print job has a printer assigned to it.

The Print Manager has to send each job to the appropriate printer, and also notify the sender of the XML of the completion or failure of each job. I'm sure tasks like these are common but somehow could not find good suggestions on implementing this one. I found a Miscellaneous Utility Library though (written by Jon Skeet himself by the way) which implemented a class called "CustomThreadPool", which allows creating multiple thread pools in a .NET application.

So, my approach so far is as follows: Get a print job. If a pool exists for this printer, place the job in a thread in the pool. Otherwise, create a pool and place the job in a thread in this pool. Get next job.

Here is how it looks like so far:

private List _printerThreads = new List();

delegate Errors ThreadMethod(PrintJob job);

private Errors InsertThread(PrintJob job)
{
ProcessSinglePrintJob(job);
}

// stuff ...

public void ProcessPrintJobs()
{
if (_printJobs != null)
{
foreach (PrintJob printJob in _printJobs)
{
if(String.IsNullOrEmpty(printJob.PrinterName))
{
printJob.JobResult = Errors.PrinterNameNotSpecified;
}
else if (String.IsNullOrEmpty(printJob.ReaderName) && printJob.IsEncodeSmartCard)
{
printJob.JobResult = Errors.SmartCardReaderNameNotSpecified;
}
else
{
CustomThreadPool pool = _printerThreads.Find(delegate(CustomThreadPool test)
{
return test.Name == printJob.PrinterName;
});

if (pool == null)
{
pool = new CustomThreadPool(printJob.PrinterName);
}

ThreadMethod method = new ThreadMethod(InsertThread);

pool.AddWorkItem(method, printJob);
}
}
}
}

I don't have extensive experience with multithreading so this solution might not even work or it may be too complex for the task. I'll run some tests soon anyway with the actual printers.

by . Also posted on my website

Tuesday, September 1, 2009

Studying Interprocess Communication

Today I had to solve a simple problem. Let's say there are two processes running on one computer. The first service polls a database for print jobs. As soon as a job is found, a second service has to send the job to the printer. So, effectively, I have to pass some data from one local service to another.

The first, "amateurish" solution that came to my mind was to write data to a text file by the "polling" service and read from that file by "printing" service. But I thought that the task like this should be a standard one and looked around. Here's one of the examples I found:

.NET 3.5 Adds Named Pipes Support

Here's the probably the simplest working example: First, I need to create two windows services. I add a timer to each service. I also add an event log to each of the services to be able to check if they work. One of the services will be a "server". Here's what goes into it's timer_Elapsed:

using (NamedPipeServerStream pipeServer = new NamedPipeServerStream("testPipe", PipeDirection.Out))
{
pipeServer.WaitForConnection();

try
{
using (StreamWriter sw = new StreamWriter(pipeServer))
{
sw.AutoFlush = true;
string dt = DateTime.Now.ToString();
sw.WriteLine(dt);
pollingEventLog.WriteEntry(dt + " written by the server");
}
}
catch (IOException ex)
{
pollingEventLog.WriteEntry(ex.Message);
}
}

The other service will be a "client". Here's what goes into it's timer_Elapsed:

using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", "testPipe", PipeDirection.In))
{
pipeClient.Connect();
using (StreamReader sr = new StreamReader(pipeClient))
{
string temp;
while ((temp = sr.ReadLine()) != null)
{
printManagerEventLog.WriteEntry(temp + " read by the client");
}
}
}

This is it - after both services are compiled, installed and started, their cooperation can be observed through the Event Log. Total time including googling, understanding the concept and implementing the working example - under 30 minutes.

by . Also posted on my website

Monday, August 24, 2009

Human Readable Entries For The Event Log

Looks like I've been a bit busy recently!
Anyway, just a little trick I used today to produce human readable messages for the event log, avoiding complex switches or if/else blocks.
First, I put all possible errors in the enum, including the "no error", like this:

public enum Errors
{
ProcessingCompletedSuccessfully = 0,
CouldNotEstablishConnectionToPrinter = 1,
...
GlobalSystemShutdownPreventedCompletingTheTaskInATimelyFashion = 999
}

The event log is created as usual

private System.Diagnostics.EventLog pollingEventLog;

if (!EventLog.SourceExists("MyHumbleSource"))
{
EventLog.CreateEventSource("MyHumbleSource", "MyHumbleService");
}
pollingEventLog.Source = "MyHumbleSource";
pollingEventLog.Log = "MyHumbleService";

The function should return the error code and the error code should be written to the event log

Errors error = PerformMyVeryComplexProcessing(XmlDocument data);
WriteErrorToLogFile(error);

Finally, a small function that does the important stuff:

private void WriteErrorToLogFile(Errors error)
{
string inputstr = error.ToString();
Regex reg = new Regex(@"([a-z])[A-Z]");
MatchCollection col = reg.Matches(inputstr);
int iStart = 0;
string Final = string.Empty;
foreach (Match m in col)
{
int i = m.Index;
Final += inputstr.Substring(iStart, i - iStart + 1) + " ";
iStart = i + 1;
}
string Last = inputstr.Substring(iStart, inputstr.Length - iStart);
Final += Last;

pollingEventLog.WriteEntry(Final);
}

I did not write the function myself - I got the solution from
Split words by capital letter using Regex

It takes the error, converts its name to string and splits the string by capital letters.
If the error returned was CouldNotEstablishConnectionToPrinter, then "Could Not Establish Connection To Printer" will be written to the event log.

by . Also posted on my website