Wednesday, July 15, 2009

Unit Testing - First Attempts

I had some time to use while I was investigating the smart card issues, so I decided to do the right thing. Something I have never done before. To learn how to write and use unit tests. Since I had a very small application that was testing the capabilities of the printer, it looked like a perfect guinea pig for my experiment. It turned out that writing tests is not so hard as I expected it to be. Well, I can not promise that I did it right, of course, because no one writes them here anyway and there is no one to mentor me or point to my mistakes.

So first of all I downloaded and installed NUnit framework

NUnit framework

Then I added a project of type class library to my solution and a single class called UnitTest to this solution. Here is the full code of the UnitTest class:

using System;
using NUnit.Framework;
using SmartCardTest;
using DataCardCP40;

[TestFixture]
public class UnitTest
{
public DataCardPrinter printer;
ICE_API.DOCINFO di;

[Test]
public void CreateObjects()
{
printer = new DataCardPrinter();
di = DataCardPrinter.InitializeDI();
printer.CreateHDC();
Assert.AreNotEqual(printer.Hdc, 0);
Assert.Greater(di.cbSize, 0);
}

[Test]
public void SetInteractiveMode()
{
int res = ICE_API.SetInteractiveMode(printer.Hdc, true);
Assert.Greater(res, 0);
}

[Test]
public void StartDoc()
{
int res = ICE_API.StartDoc(printer.Hdc, ref di);
Assert.Greater(res, 0);
}

[Test]
public void StartPage()
{
int res = ICE_API.StartPage(printer.Hdc);
Assert.Greater(res, 0);
}

[Test]
public void RotateCardSide()
{
int res = ICE_API.RotateCardSide(printer.Hdc, 1);
Assert.Greater(res, 0);
}

[Test]
public void FeedCard()
{
int res = ICE_API.FeedCard(printer.Hdc, ICE_API.ICE_SMARTCARD_FRONT + ICE_API.ICE_GRAPHICS_FRONT);
Assert.Greater(res, 0);
}

[Test]
public void SmartCardContinue()
{
int res = ICE_API.SmartCardContinue(printer.Hdc, ICE_API.ICE_SMART_CARD_GOOD);
Assert.Greater(res, 0);
}

[Test]
public void EndPage()
{
int res = ICE_API.EndPage(printer.Hdc);
Assert.Greater(res, 0);
}

[Test]
public void EndDoc()
{
int res = ICE_API.EndDoc(printer.Hdc);
Assert.Greater(res, 0);
}
}

There's not much to explain. First I create the objects required and verify that the device context was created and the DOCINFO struct was initialized. All the other tests just check the return codes of the printer functions. The error code is 0, so the check is for return value being greater than zero.

After compiling and fixing errors I realized that I have no way to set the sequence of the execution. Supposedly, as the theory teaches us, each test should be able to run alone and independent of whether the rest were passed, failed or run at all. Well, does not work so well in my case - if I want to test that the card can be ejected from the printer, I need to somehow insert it first! I found out, however, that the tests are executed in the alphabetic order of their names. Okay, that'll do for now. So I just renamed my tests like this A_CreateObjects(), B_SetInteractiveMode() etc. Then I compiled the solution, creating the "DataCardTest.dll". Next step is to run NUnit and open the dll. Wow! The smart thing can see all my tests now. When ready, just select Test->Run all from the menu and enjoy ...

It does not alway end that well, however - it might be like this (see how it tells what was the line where the error happened and how the expected test result was different from the actual).

What happened here? Took me some time to figure out ... the default printer was not set to my card printer.

by . Also posted on my website

Friday, July 10, 2009

Smart Cards Hurt - 2

Now, the slightly harder part is communicating with the Smart Card reader. Most, if not all, of the functionality resides within the winscard.dll. For functions reference, this MSDN page could be a start.

Smart Card Functions

I also found a nice example using google code search which resides here

ACR120Driver.cs

and using this code as a template, I used the following code to test the functionality of my SCM reader.

long retCode;
int hContext = 0;
int ReaderCount = 0;
int Protocol = 0;
int hCard = 0;
string defaultReader = null;
int SendLen, RecvLen;

byte[] SendBuff = new byte[262];
byte[] RecvBuff = new byte[262];

ModWinsCard.SCARD_IO_REQUEST ioRequest;

retCode = ModWinsCard.SCardEstablishContext(ModWinsCard.SCARD_SCOPE_USER, 0, 0, ref hContext);
if (retCode != ModWinsCard.SCARD_S_SUCCESS)
{
System.Diagnostics.Debug.WriteLine(ModWinsCard.GetScardErrMsg(retCode));
}

retCode = ModWinsCard.SCardListReaders(hContext, null, null, ref ReaderCount);

if (retCode != ModWinsCard.SCARD_S_SUCCESS)
{
System.Diagnostics.Debug.WriteLine(ModWinsCard.GetScardErrMsg(retCode));
}

byte[] retData = new byte[ReaderCount];
byte[] sReaderGroup = new byte[0];

//Get the list of reader present again but this time add sReaderGroup, retData as 2rd & 3rd parameter respectively.
retCode = ModWinsCard.SCardListReaders(hContext, sReaderGroup, retData, ref ReaderCount);

if (retCode != ModWinsCard.SCARD_S_SUCCESS)
{
System.Diagnostics.Debug.WriteLine(ModWinsCard.GetScardErrMsg(retCode));
}

//Convert retData(Hexadecimal) value to String
string readerStr = System.Text.ASCIIEncoding.ASCII.GetString(retData);
string[] rList = readerStr.Split('\0');

foreach (string readerName in rList)
{
if (readerName != null && readerName.Length > 1)
{
defaultReader = readerName;
break;
}
}

if (defaultReader != null)
{
retCode = ModWinsCard.SCardConnect(hContext, defaultReader, ModWinsCard.SCARD_SHARE_DIRECT,
ModWinsCard.SCARD_PROTOCOL_UNDEFINED, ref hCard, ref Protocol);
//Check if it connects successfully
if (retCode != ModWinsCard.SCARD_S_SUCCESS)
{
string error = ModWinsCard.GetScardErrMsg(retCode);
}
else
{
int pcchReaderLen = 256;
int state = 0;
byte atr = 0;
int atrLen = 255;

//get card status
retCode = ModWinsCard.SCardStatus(hCard, defaultReader, ref pcchReaderLen, ref state, ref Protocol, ref atr, ref atrLen);

if (retCode != ModWinsCard.SCARD_S_SUCCESS)
{
return;
}

//read/write data etc.

.....
}
}

ModWinsCard.cs is, again, a wrapper for the winscard.dll functions, data structures, and declares all required constants.

Anyway, this code actually worked fine, except one little detail - the state variable that gets returned by the SCardStatus returned the value of 2. And the possible values are explained here:

SCardStatus

"2" is SCARD_PRESENT, which means "A card is present in the card reader, but it is not in position for use". A better result would be something like SCARD_NEGOTIABLE which is "The card has been reset and is waiting for protocol negotiation".

Also, using SCardConnect with preferred protocol set to T0 or T1 returned SCARD_W_UNRESPONSIVE_CARD error.

Now this is the point where I had to consult with the printer manufacturer because there's a number of possible reasons for the errors - hardware, firmware, drivers or incompatible card. Work still in progress.

by . Also posted on my website

Tuesday, July 7, 2009

Smart Cards Hurt - 1

So here's the new toy I've got to play with - the DataCard CP40 Plus card printer with the SCM SCR331-DI Smart Card reader.

Datacard CP40 Plus

Developing the application for the printer consists mostly of two parts - communicating with the printer and communicating with the smart card reader. You tell the printer to pick up the card, you tell the printer to position the card for smart card processing, you tell the smart card reader to write data to the smart card, you tell the printer to encode the magstripe and print something on the card, you tell the printer to finish with the print job.

It does not look so easy when you read the manual. This is how the programming flow looks like:

In reality, though, the whole printer communication is mostly done by the following code:

printer.Hdc = PrintDoc.PrinterSettings.CreateMeasurementGraphics().GetHdc().ToInt32();

/* Set Interactive mode */
if (ICE_API.SetInteractiveMode(printer.Hdc, true) > 0)
{
ICE_API.DOCINFO di = new ICE_API.DOCINFO();
/* Initialize DOCINFO */
di.cbSize = 16;
di.lpszDocName = "Card Printer SDK Test";
di.lpszDataType = string.Empty;
di.lpszOutput = string.Empty;

/* Start document and page */
if (ICE_API.StartDoc(printer.Hdc, ref di) > 0)
{
if (ICE_API.StartPage(printer.Hdc) > 0)
{
/* Set card rotation on */
ICE_API.RotateCardSide(printer.Hdc, 1);
/* Feed the card into the smart card reader */
if (ICE_API.FeedCard(printer.Hdc, ICE_API.ICE_SMARTCARD_FRONT + ICE_API.ICE_GRAPHICS_FRONT) > 0)
{
/* Put any SmartCard processing/communication here */
TalkToSmartCard();
}
/* Remove the card from the reader and continue printing */
ICE_API.SmartCardContinue(printer.Hdc, ICE_API.ICE_SMART_CARD_GOOD);
/* End the page */
ICE_API.EndPage(printer.Hdc);
}
/* End job */
ICE_API.EndDoc(printer.Hdc);
}
}

The ICE_API mostly contains wrappings for the functions from the ICE_API.dll which comes with the printer and defines some constants and data structures, like this

[StructLayout(LayoutKind.Sequential)]
public struct DOCINFO
{
public int cbSize;
public string lpszDocName;
public string lpszOutput;
public string lpszDataType;
}

........

[DllImport("ICE_API.dll")]
public static extern int FeedCard(int hdc, int dwCardData);

[DllImport("ICE_API.dll")]
public static extern int GetCardId(int hdc, CARDIDTYPE pCardId);

[DllImport("ICE_API.dll")]
public static extern int SmartCardContinue(int hdc, int dwCommand);

.........

public const int ICE_SMARTCARD_FRONT = 0x10;
public const int ICE_GRAPHICS_FRONT = 0x1;

public const int ICE_SMART_CARD_GOOD = 0;
public const int ICE_SMART_CARD_ABORT = 1;

Now that was the easy part.

by . Also posted on my website

Saturday, June 27, 2009

Embedded Technology Workshop

Some members of our team, including myself, have attended a small, half-day workshop on Microsoft Embedded Technologies. Here's how the agenda looked like:










TIMETOPIC
12:30Registration and light lunch
13:00 – 13:05Welcome speech
13:10 – 13:30Introduction: Why use Embedded? What are the benefits?
13:30 to 15:00Module 1: Windows Embedded Standard – Development Suite, Tools and Utilities.

Module 2: Embedded Enabling Features.
15:00Tea/Coffee Break
14:30 to 16:00Module 3: Demo:
- Building an image using File Based Write Filter
Module 4: Componentization of 3rd Party Drivers.

Module 5: Demo:
- Creating Custom Components in your image.
16:00 - 16:30Q & A
16:30Closing and thank you

It was held at the local Microsoft office (not Microsoft Office, but the actual place where like, people work). The office was pretty boring by the way - no huge Bill Gates portaits, no sacrifices etc ... maybe they clean up when they know strangers will be present.

Anyway, the topic was mostly about how to assemble your own embedded OS from parts of dismembered Windows XP or Windows Embedded Standard etc. Basically, if I know exactly what peripherial devices will my hardware use, I can only include drivers for these devices, hugely reducing the size of the OS. Also, I may choose to cut out other elements of the OS - I may get rid of the whole explorer shell altogether. They mentioned that the smallest OS they have actually seen used by one of the clients was about 8MB in size. Quite impressive compared to the standard XP footprint of about 1.9GB.

As they said, the goal of the workshop was to show the participants that the process of assembling your own OS is not as complicated as people usually think. Can't say they succeeded - looked fairly complex to me so far ...

P.S. I have no idea why blogger inserts so many empty lines before the table ... will try to fix it later.

by . Also posted on my website

Tuesday, June 23, 2009

VSS => TFS converter application update

After a bit of thought, I decided what would be the easiest and the most convinient way to run my small application that helps to convert projects from VSS to TFS.

I will start the command line tool passing them together with parameters to cmd.exe application, and run cmd.exe with the -k parameter to prevent the command window from closing after the tool exits. I will keep the ID of the process that runs cmd.exe. Next time I run the cmd.exe, I will check if there is ID present, and if yes, I will kill the process, and then start a new one. This way the user's computer will not be littered with command windows.

So, the small class that would take care of process management looks like this

public class ProcessFactory
{
private static int _currentProcessID = -1;

private static Process _cmdProcess;

private static ProcessStartInfo _startInfo;

public static ProcessStartInfo StartInfo
{
get
{
if (_startInfo == null)
{
_startInfo = new ProcessStartInfo();
}
return _startInfo;
}
}

public static void RunProcess(string filename, string args, string workingdir)
{
try
{
if (_currentProcessID > 0)
{
Process processToClose = Process.GetProcessById(_currentProcessID);
if (processToClose != null)
{
processToClose.Kill();
}
_currentProcessID = -1;
}

StartInfo.FileName = filename;
StartInfo.Arguments = args;
StartInfo.WorkingDirectory = workingdir;

_cmdProcess = Process.Start(StartInfo);

if (_cmdProcess != null)
{
_currentProcessID = _cmdProcess.Id;
}
}
catch (Exception ex)
{
Logger.LogInfo(ex);
}
}
}

And then I just call the RunProcess as many times as I want, but the user will not be bothered with "leftover" command windows

string args = "/k ssarc.exe -d- -i -y" + SettingsManager.GetSetting(Constants.VSSLOGIN)
+ "," +
SettingsManager.GetSetting(Constants.VSSPASSWORD) + " -s" +
SettingsManager.GetSetting(Constants.VSSDBFOLDER) + " " +
SettingsManager.GetSetting(Constants.VSSARCHIVEFILENAME)
+ ".ssa" + " \"" + SettingsManager.GetSetting(Constants.VSSPROJECTNAME) + "\"";

ProcessFactory.RunProcess("cmd.exe", args, SettingsManager.GetSetting
(Constants.VSSINSTFOLDER));

.....

args = "/k ssrestor.exe \"-p" + SettingsManager.GetSetting(Constants.VSSPROJECTNAME)
+ "\"" + " -s" + SettingsManager.GetSetting(Constants.VSSARCHIVEFOLDER) +
" -y" + SettingsManager.GetSetting(Constants.VSSLOGIN) + "," +
SettingsManager.GetSetting(Constants.VSSPASSWORD) + " " +
SettingsManager.GetSetting(Constants.VSSARCHIVEFILENAME) + ".ssa" +
" \"" + SettingsManager.GetSetting(Constants.VSSPROJECTNAME) + "\"";

ProcessFactory.RunProcess("cmd.exe", args, SettingsManager.GetSetting
(Constants.VSSINSTFOLDER));

etc., until finished.

by . Also posted on my website

Tuesday, June 16, 2009

Using the Process class.

I was not too loaded with work recently so I decided to write a small application that would help to automate the process of converting existing Visual SourceSafe projects to Team Foundation Server. The idea is to get some information from the user first, and then spare him from some manual tasks - running tools like ssarc, ssrestor or VSSConverter, manually creating and editing XML files etc.

When the application starts, the user needs to provide (or just check) the following information:

  • A folder where Visual SourceSafe is installed
  • A folder where Visual SourceSafe database is located
  • Visual SourceSafe database administrator login credentials
  • The name of the Visual SourceSafe project to be converted
  • A folder that will be used during conversion to restore VSS database, keep XML files etc.
  • SQL Server that will be used by the converter
  • A name of the TFS and the port number
  • A name of the project on the TFS where the converted files will go

A significant chunk of the application functionality is just wrapping the calls to command line tools so that the user does not have to bother with manually locating them, typing the correct parameters etc.

For that purpose, the .NET class Process is quite handy.
Here is the example:
To archive the VSS project MyProject which is in the VSS database located on MyServer into the archive file called MyArchive.ssa I need to run the following from the command line:

>"C:\Program Files\Microsoft Visual SourceSafe\ssarc.exe" "-d- -i -yadmin,password -s\\MyServer\ MyArchive.ssa \$/MyProject\"

To run this command from the C# code I can use the following code:

ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "ssarc.exe";
startInfo.Arguments = @"-d- -i -yadmin,password -s\\MyServer\ MyArchive.ssa \$/MyProject\";
startInfo.WorkingDirectory = @"C:\Program Files\Microsoft Visual SourceSafe";
Process process = Process.Start(startInfo);

This is quite self-explanatory.

There are a couple of things that I had trouble with however. First thing is logging. It would be nice to log the errors and messages that the process generates. This is possible, according to the MSDN article.

ProcessStartInfo Class

Standard input is usually the keyboard, and standard output and standard error are usually the monitor screen. However, you can use the RedirectStandardInput, RedirectStandardOutput, and RedirectStandardError properties to cause the process to get input from or return output to a file or other device. If you use the StandardInput, StandardOutput, or StandardError properties on the Process component, you must first set the corresponding value on the ProcessStartInfo property. Otherwise, the system throws an exception when you read or write to the stream.

However, if I redirect standard output to the text file, for example, the user is unable to see it. And some of the tools used required interaction with the user. So it looks like I either interact with the user, or log the messages somewhere.

Also, when the process completes, it closes the window where it was running. So, if there is a message shown by the process when it exits, the user does not have time to read it. It might be frustrating when the process exits with an error message and the user does not know what exactly the error was. And it can not be logged because the output can not be redirected somewhere - the user needs to see it on the screen.

I will still be looking for the 'elegant' solution for this, but so far I found a workaround: rather than starting the process itself, I can start the command line using the "cmd.exe" and pass the whole tool together with the parameters as a parameter to cmd.exe.

CMD

The trick is that specifying the /k parameter prevents the command window from closing after the process exits. Here is how the previous code will look like when changed according to my workaround:

ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "cmd.exe";
startInfo.Arguments = @"/k "C:\Program Files\Microsoft Visual SourceSafe\ssarc.exe" "-d- -i -yadmin,password -s\\MyServer\ MyArchive.ssa \$/MyProject\"";
Process process = Process.Start(startInfo);

I will be looking for a better solution when I have time to improve this application.

by . Also posted on my website

Tuesday, June 9, 2009

Small Things Refreshed Today

I had to write a small Windows Forms application today. It just gets some user input, creates an XML file, sends it to the webservice, gets the response, parces it and shows the results to the user. Good thing is that I had to remind myself how to use two simple things.

1. Saving and retrieving values using the app.config file.

If I want to get some values from the app.config file, I can keep them in the appSetting section and the whole app.config file for the small application can be as simple as that








To read the values I need to do just the following (after I add a reference to System.configuration to the project):

string myFirstValue = ConfigurationManager.AppSettings.Get("MYKEY1");

To update the values I need to put a little bit more effort

Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
AppSettingsSection appSettings = config.AppSettings;

appSettings.Settings["MYKEY1"].Value = myNewValue1;
appSettings.Settings["MYKEY2"].Value = myNewValue2;

config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("appSettings");

It is useful to know that this would not work at debug time, though - it will not throw an exception, but the values would not be updated too. I spent a few minutes trying to find out why it does not work before I understood that this behaviour is expected.

2. Creating the XML document.

Of course, for the purposes of my application, where the whole XML is maybe 10 to 15 elements, I could go with the following:

string myXML = "
";
myXML += "" + someID + "";
...
myXML += "";
return myXML;

The code would actually be shorter than the "proper" XML handling, take less time to write and maybe even will work faster (especially if I use a StringBuilder to concatenate strings). I did it the "proper" way, however - for practice.

To create a document

XmlDocument xmlDoc = new XmlDocument();

To create a declaration

XmlDeclaration xDec = xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", null);

To create an element in a format of

myValue1
I created a small helper function

private XmlElement CreateElementFromNameValue(string name, string value)
{
XmlElement element = xmlDoc.CreateElement(name);
element.InnerText = value;
return element;
}

To create an attribute to the element

XmlElement xmlHeader = xmlDoc.CreateElement("header");
XmlAttribute schema = xmlDoc.CreateAttribute("SchemaVersion");
schema.Value = "2.0";
xmlHeader.SetAttributeNode(schema);

To bring it all together

XmlDocument xmlDoc = new XmlDocument();
XmlDeclaration xDec = xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", null);

XmlElement request = xmlDoc.CreateElement("request");
XmlAttribute schema = xmlDoc.CreateAttribute("SchemaVersion");
schema.Value = "2.0";
request.SetAttributeNode(schema);

request.AppendChild(CreateElementFromNameValue("MYKEY1", "myValue1"));
request.AppendChild(CreateElementFromNameValue("MYKEY2", "myValue2"));

xmlDoc.AppendChild(xDec);
xmlDoc.AppendChild(request);

Expected InnerXml of the xmlDoc



myValue1
myValue2
by . Also posted on my website