<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>distributedlife &#187; development</title>
	<atom:link href="http://distributedlife.com/blog/category/development/feed" rel="self" type="application/rss+xml" />
	<link>http://distributedlife.com/blog</link>
	<description>passionate about everything</description>
	<lastBuildDate>Sun, 31 Jul 2011 03:32:56 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Trainlines and discovery</title>
		<link>http://distributedlife.com/blog/2011/07/trainlines-and-discovery.html</link>
		<comments>http://distributedlife.com/blog/2011/07/trainlines-and-discovery.html#comments</comments>
		<pubDate>Sun, 31 Jul 2011 03:32:56 +0000</pubDate>
		<dc:creator>Ryan Boucher</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[australia]]></category>
		<category><![CDATA[borneo]]></category>
		<category><![CDATA[distributedlife]]></category>
		<category><![CDATA[hong kong]]></category>
		<category><![CDATA[malaysia]]></category>
		<category><![CDATA[new zealand]]></category>
		<category><![CDATA[rail]]></category>
		<category><![CDATA[route]]></category>
		<category><![CDATA[ryan boucher]]></category>
		<category><![CDATA[rybo]]></category>
		<category><![CDATA[sabah state railway]]></category>
		<category><![CDATA[singapore]]></category>
		<category><![CDATA[train]]></category>
		<category><![CDATA[trainlines]]></category>
		<category><![CDATA[tranzapline]]></category>

		<guid isPermaLink="false">http://distributedlife.com/blog/?p=1032</guid>
		<description><![CDATA[I&#8217;ve finished placing all the trainlines in Australia, New Zealand, Malaysia, Hong Kong and I&#8217;ve almost finished Singapore. Data entry can be a tedius task at times and there are some usability flaws that trainlines has, that make it harder than it should be.
One of the things I&#8217;ve really enjoyed is learning little things about places I want to [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve finished placing all the trainlines in <a href="trainlines.heroku.com">Australia, New Zealand, Malaysia, Hong Kong and I&#8217;ve almost finished Singapore</a>. Data entry can be a tedius task at times and there are some usability flaws that trainlines has, that make it harder than it should be.</p>
<p>One of the things I&#8217;ve really enjoyed is learning little things about places I want to go and see. In Malaysia on the island of Borneo there is a train route and a couple of the stations can&#8217;t be found in Google maps. They have a name and that is all. The <a href="http://en.wikipedia.org/wiki/File:SabahStateRailwayRouteMap.png">rail site claims they stop there</a> but from the terrain and satellite view it appears like a <a href="http://trainlines.heroku.com/trainlines/55">mountainous tree filled valley</a>. I want to go there.</p>
<p>Another is the <a href="http://trainlines.heroku.com/trainlines/29">TranzAlpine train route in New Zealand</a> that winds between mountains and by riverbeds. I&#8217;m sure it will be breathtaking journey.</p>
<p>To help with exploring, I have an upcoming feature that will show flickr photos that are within 10kms of a train station. <a href="http://www.panoramio.com/map/#lt%3D-42.714480%26ln%3D171.568680%26z%3D5%26k%3D2%26a%3D1%26tab%3D1">Here is an example of how google does it now with panaramio</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://distributedlife.com/blog/2011/07/trainlines-and-discovery.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Introducing Trainlines</title>
		<link>http://distributedlife.com/blog/2011/07/introducing-trainlines.html</link>
		<comments>http://distributedlife.com/blog/2011/07/introducing-trainlines.html#comments</comments>
		<pubDate>Mon, 18 Jul 2011 04:08:13 +0000</pubDate>
		<dc:creator>Ryan Boucher</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[distributedlife]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[maps]]></category>
		<category><![CDATA[public transport]]></category>
		<category><![CDATA[rail]]></category>
		<category><![CDATA[ryan boucher]]></category>
		<category><![CDATA[rybo]]></category>
		<category><![CDATA[train]]></category>
		<category><![CDATA[trainlines]]></category>

		<guid isPermaLink="false">http://distributedlife.com/blog/?p=1018</guid>
		<description><![CDATA[I had an idea where I wanted to visualise and emphasise train routes over road routes on google maps. The best I could find online was a lot of static images with train lines drawn over the top. The value of this arrangement is for travelling and travel planning. Trains are an excellent mode of [...]]]></description>
			<content:encoded><![CDATA[<p>I had an idea where I wanted to visualise and emphasise train routes over road routes on google maps. The best I could find online was a lot of static images with train lines drawn over the top. The value of this arrangement is for travelling and travel planning. Trains are an excellent mode of transport being more efficient than aeroplanes, provide access to amazing scenery and get you in contact with the people who live in the country you are travelling through.</p>
<p>So I built <a href="http://trainlines.heroku.com/" target="_blank">trainlines.heroku.com</a></p>
<p>It&#8217;s an open repository of train routes. I&#8217;ve got regional australia, new zealand and hong kong, malaysia, singapore and the philippines on there at the moment. Google clustering takes care of visualisations at zoom levels so any level of route (metro, regional, international) can be supported but I do have an emphasis on regional and international routes. If you want to add a train route go right ahead.</p>
<p>If you can think of a suggestion to make it easier to use, let me know and I&#8217;ll consider it. For the meantime it&#8217;s feature complete (minus a few known issues).</p>
<p>For the testers out there that I know, if you feel the urge to break it, please go to <a href="http://trainlines-preprod.heroku.com/" target="_blank">trainlines-preprod.heroku.com</a> and do your worst.</p>
]]></content:encoded>
			<wfw:commentRss>http://distributedlife.com/blog/2011/07/introducing-trainlines.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Business Engagement Models</title>
		<link>http://distributedlife.com/blog/2010/05/business-engagement-models.html</link>
		<comments>http://distributedlife.com/blog/2010/05/business-engagement-models.html#comments</comments>
		<pubDate>Mon, 17 May 2010 13:55:16 +0000</pubDate>
		<dc:creator>Ryan Boucher</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[business engagement]]></category>
		<category><![CDATA[distributedlife]]></category>
		<category><![CDATA[ryan boucher]]></category>
		<category><![CDATA[rybo]]></category>
		<category><![CDATA[sdlc]]></category>
		<category><![CDATA[software development]]></category>
		<category><![CDATA[software testing]]></category>
		<category><![CDATA[vmodel]]></category>

		<guid isPermaLink="false">http://distributedlife.com/blog/?p=900</guid>
		<description><![CDATA[Over my career I&#8217;ve been drawn into numerous discussions regarding which software development lifecycle model is right for us? Which is right for testing?
One of the questions missed is what is the right model for the business? An in-house software development shop providing for an organisation then may not have a choice about what is [...]]]></description>
			<content:encoded><![CDATA[<p>Over my career I&#8217;ve been drawn into numerous discussions regarding which <a href="http://en.wikipedia.org/wiki/Software_development_process">software development lifecycle model</a> is right for us? Which is right for testing?</p>
<p>One of the questions missed is what is the right model for the business? An in-house software development shop providing for an organisation then may not have a choice about what is right. Some organisations don&#8217;t care what model is used or if any process is used at all. They control the money, which projects are initiated, stopped, started and when, they control the deadlines and the requirements. Through all of this they also control the quality.</p>
<p>In such cases picking any model is pointless because it&#8217;s going to fail. These models are not about software development; they are about <strong>business engagement and software development</strong>. Trying to control how the business engages with you via a model when they have their own model, <em>and adhoc is a model</em>, is pointless. They always win. It&#8217;s their money.</p>
<p>This leaves you with two options: get the business to change or come up with your own model that uses the current business engagement model.</p>
<p>The first option is the hardest; you may not be empowered to make such changes and the business may not want to change. This is where strong IT leadership from the CIO is needed. The second option is the short term solution. The goal is to achieve as much as you can knowing what you don&#8217;t control. With this in mind you can at least solve the pain points between your team and the other stakeholders involved in creating the software.</p>
]]></content:encoded>
			<wfw:commentRss>http://distributedlife.com/blog/2010/05/business-engagement-models.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>It&#8217;s the little things&#8230;</title>
		<link>http://distributedlife.com/blog/2010/05/its-the-little-things.html</link>
		<comments>http://distributedlife.com/blog/2010/05/its-the-little-things.html#comments</comments>
		<pubDate>Thu, 06 May 2010 13:55:42 +0000</pubDate>
		<dc:creator>Ryan Boucher</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[cakephp]]></category>
		<category><![CDATA[defect dependencies]]></category>
		<category><![CDATA[distributedlife]]></category>
		<category><![CDATA[integration]]></category>
		<category><![CDATA[ryan boucher]]></category>
		<category><![CDATA[rybo]]></category>

		<guid isPermaLink="false">http://distributedlife.com/blog/?p=877</guid>
		<description><![CDATA[That is what they say; it&#8217;s the little things in life that make life worth living or something.
Obviously they have never had to debug code before.
I just raised a bug with cakephp that has had me confused for about a month now but it wasn&#8217;t a blocker. It became one and after almost two days [...]]]></description>
			<content:encoded><![CDATA[<p>That is what they say; it&#8217;s the little things in life that make life worth living or something.</p>
<p>Obviously they have never had to debug code before.</p>
<p>I just raised a <a href="http://cakephp.lighthouseapp.com/projects/42648-cakephp-1x/tickets/673-configuration-sessioncookie-with-fullstop-causes-auth-component-to-forget-successful-authentication-attempt">bug with cakephp</a> that has had me confused for about a month now but it wasn&#8217;t a blocker. It became one and after almost two days of debugging and fishing for red herrings I finally uncovered the cause of my grief.</p>
<p>It turns out that it was a full-stop.</p>
<p>Let me explain the problem. The user would be successfully validated and then redirected to their home page. But would be logged out by the time they got there.</p>
<p>It turns out that when the session.cookie variable has a full-stop in it doesn&#8217;t retain the session information. The authentication part still works so as I was debugging it I was seeing code that works great. On the next request it was pretending like there was no session information. Tracing it back to the session.cookie configuration option took a while.</p>
<p>This isn&#8217;t a post about cakephp having bugs; most software has bugs and cakephp is generally good to work with.</p>
<p>This is about implicit dependencies in integration problems. The cakephp people may come back and say that this isn&#8217;t an issue and I should stop putting full-stops in my session cookie names&#8230; <a href="http://cakephp.lighthouseapp.com/projects/42648-cakephp-1x/tickets/673-configuration-sessioncookie-with-fullstop-causes-auth-component-to-forget-successful-authentication-attempt#ticket-673-3">oh wait I haven&#8217;t even finished this article and they already have.</a></p>
<p>Let me tell another story; I was asked to test a service that was responsible for auditing. Other services would call this service to audit. I said there was not enough time to test it before the release <strong>and</strong> I was going on six weeks leave. They had business approval to go without auditing so that was the safe option.</p>
<p>I come back 6 weeks later and find auditing had gone to production without being tested.</p>
<p>Apparently without issue.</p>
<p>The first test I run finds that the consuming services are putting null in the source column so all audit records are being written and you can&#8217;t tell where they came from. Not a big issue as we only have 40 services. End sarcasm.</p>
<p>I raised the issue with the auditing service developer and he responded. &#8220;This isn&#8217;t a bug, it is the responsibility of a service to populate the source property&#8221;.</p>
<p>He is correct in one regard. But he is also wrong in another.</p>
<p>His service shouldn&#8217;t let the consuming services fail. They depend on the auditing service to let them know when they get it wrong.</p>
<p>A null source value is an invalid record as per the business and the auditing service should not allow invalid audit records to exist. Therefore bug.</p>
<p>A session cookie name with a fullstop is invalid as per cakephp code components and cakephp should not allow invalid session cookie names. Therefore to maintain the integrity of the system cakephp should ensure that it&#8217;s dependencies are correct.</p>
]]></content:encoded>
			<wfw:commentRss>http://distributedlife.com/blog/2010/05/its-the-little-things.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>ISupport Contracts with Behaviours in PHP</title>
		<link>http://distributedlife.com/blog/2010/04/isupport-contracts-with-behaviours-in-php.html</link>
		<comments>http://distributedlife.com/blog/2010/04/isupport-contracts-with-behaviours-in-php.html#comments</comments>
		<pubDate>Sat, 10 Apr 2010 13:55:16 +0000</pubDate>
		<dc:creator>Ryan Boucher</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[behaviour]]></category>
		<category><![CDATA[cakephp]]></category>
		<category><![CDATA[distributedlife]]></category>
		<category><![CDATA[integration test]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[ryan boucher]]></category>
		<category><![CDATA[rybo]]></category>
		<category><![CDATA[unit testing]]></category>

		<guid isPermaLink="false">http://distributedlife.com/blog/?p=692</guid>
		<description><![CDATA[One of the problems I have with working with the Model-View-Controller setup is that I’m continually reinventing the same functionality in each model and then the same set of tests. This was slowing me down and felt too much like a code smell.
Some frameworks like CakePHP provide behaviours which is a discrete behaviour that can [...]]]></description>
			<content:encoded><![CDATA[<p>One of the problems I have with working with the Model-View-Controller setup is that I’m continually reinventing the same functionality in each model and then the same set of tests. This was slowing me down and felt too much like a code smell.</p>
<p>Some frameworks like <a href="http://cakephp.org/">CakePHP </a>provide behaviours which is a discrete behaviour that can be applied to any Model. This option was close but didn’t feel right. I wanted to be more explicit about my behaviours.</p>
<p>So what I did was abstract out common methods into interfaces that use the convention ISupport&#8230;</p>
<p>An example is ISupportDeletion which has the following contract:</p>
<pre><code class="csharp">
interface ISupportDeletion
{
    public function Delete ($Id) ;
}
</code></pre>
<p>Another example is the ISupportCollections contract:</p>
<pre><code class="csharp">
interface ISupportCollections
{
    public function GetAll ($Page) ;
    public function GetAllCount () ;
    public function GetList ($List, $Page) ;
    public function GetListCount ($List) ;
}
</code></pre>
<p>The model then inherits these contracts piecing together promised behaviour from the setting of contracts. I had original thought that the model should only implement interfaces and never add new methods; but this would be pointless as each model would need to implement an interface designed for itself, which is extra coding for no benefit.</p>
<pre><code class="csharp">
class User extends Model implements ISupportDeletion, ISupportObjectAccess
{
}
</code></pre>
<p>So there are a bunch of contracts that a model can support:</p>
<ul>
<li>ISupportActiveInactive</li>
<li>ISupportCollections</li>
<li>ISupportDefaultState</li>
<li>ISupportDeletion</li>
<li>ISupportFilteredCollections</li>
<li>ISupportObjectAccess</li>
<li>ISupportSuspendedState</li>
<li>ISupportHistory</li>
<li>ISupportComments</li>
<li>ISupportTags</li>
<li>ISupportVersioning</li>
</ul>
<p>And as I come up with behaviour that belongs to more than one model then it’ll become a supported behaviour.</p>
<p>The idea is that the model is really just content but in my MVC setup I require the model manage its own behaviour. This is to stop data oriented code being scattered across the controllers. The controller defines user interaction while the model defines data interaction. Interactions have behaviour and common behaviours are defined in ISupport contracts.</p>
<p>Having an interface isn’t enough and does need to be implemented. This is where the behaviour implementations come in. For each ISupport contract there are one or more implementations of behaviour.</p>
<p>For example ISupportDeletion is implemented in two ways</p>
<ul>
<li>LogicalDeleteBehaviour</li>
<li>PhysicalDeleteBehaviour</li>
</ul>
<p>From the perspective of the controller it doesn’t care how the model implements the ISupportDeletion contract as long as it does. My user model requires logical deletion whilst most other models require physical deletion.</p>
<p>Let’s take a look at the User model cut down to only support deletion</p>
<pre><code class="csharp">
class User extends Model implements ISupportDeletion
{
    protected $LogicalDeleteBehaviour = null ;

    public function __construct ()
    {
        parent::__construct () ;

        $this-&gt;LogicalDeleteBehaviour = new LogicalDeleteBehaviour ($this) ;
    }

    public function Delete ($id)
    {
        return $this-&gt;LogicalDeleteBehaviour-&gt;Delete ($id) ;
    }
}
</code></pre>
<p>As shown the constructer instantiates our logical delete behaviour and accepts the model itself as input. This is needed because the behaviour provides an implementation but is dependent on information in the model. So we dependency inject the model in.</p>
<p>If we were worried about the foot print of these behaviours we could delay their instantiation until needed. The delete operation is nothing more complicated then delegation to the behaviour.</p>
<p>Our behaviours are similar to the command pattern and when the need arises to provide undo support it could be implemented in the behaviour rather than each model.</p>
<p>Let’s take a look at a behaviour implementation:</p>
<pre><code class="csharp">
class LogicalDeleteBehaviour extends Behaviour implements ISupportDeletion
{
    protected $model = null ;

    public function __construct ($model)
    {
        parent::__construct () ;

        $this-&gt;model = $model ;
    }

    /**
    * Logically deletes the record for the model
    */
    public function Delete ($Id)
    {
        if (!$Id)
        {
            return false ;
        }

        $qs = (
            "UPDATE " . $this-&gt;model-&gt;GetTable() . " " .
            "SET date_logically_deleted = NOW()" . " " .
            "WHERE " . $this-&gt;model-&gt;GetPK() . " = " . $Id . " " .
            "AND date_logically_deleted IS NULL"
        ) ;

        $this-&gt;model-&gt;query ($qs) ;

        return true ;
    }
}
</code></pre>
<p>The injection of the model is purely required to get the table name and the pk for the model although it could be assumed as id.</p>
<p>I’ve used inline SQL here but we could be calling a stored proc which would be preferred.</p>
<p>The physical deletion behaviour is not too different:</p>
<pre><code class="csharp">
class PhysicalDeleteBehaviour extends Behaviour implements ISupportDeletion
{
    protected $model = null ;

    public function __construct ($model)
    {
        parent::__construct () ;

        $this-&gt;model = $model ;
    }

    /**
    * physically deletes the record for the model
    */
    public function Delete ($Id)
    {
        if (!$Id)
        {
            return false ;
        }

        $qs = (
            "DELETE FROM " . $this-&gt;model-&gt;GetTable() . " " .
            "WHERE " . $this-&gt;model-&gt;GetPK() . " = " . $Id
        ) ;

        $this-&gt;model-&gt;query ($qs) ;

        return true ;
    }
}
</code></pre>
<h3>Testing</h3>
<p>So this just helps us implement behaviour more easily but the big benefit for me was to also simplify the number of tests I had to write. Tests needed to be written in three places:</p>
<ul>
<li>Against the behaviour</li>
<li>To prove the model implements the behaviour</li>
<li>Integration tests against the model involving multiple behaviours</li>
</ul>
<p><strong>Against the behaviour</strong></p>
<p>These were unit tests against the ISupport contract to prove the implementation of the behaviour. So for the ISupportDeletion contract the following tests were written:</p>
<ul>
<li>Is Already Deleted</li>
<li>Is Not Currently Deleted</li>
<li>ID Not Supplied</li>
<li>ID Does Not Exist</li>
</ul>
<p>These were encapsulated in a test set object. These tests were written also using dependency injection to provide the model, the implementation and the test runner which contained methods for creating records, etc.</p>
<p>It’s all very abstract but a test reads like this:</p>
<pre><code class="csharp">
protected function IsNotDeleted ()
{
    $this-&gt;runner-&gt;fixtureEmptyTable ($this-&gt;model, $this-&gt;table) ;
    $Id = $this-&gt;runner-&gt;{$this-&gt;CreateOperation}($this-&gt;model) ;

    $result = $this-&gt;model-&gt;Delete($Id) ;

    $this-&gt;runner-&gt;assertTrue ($result) ;
    $this-&gt;runner-&gt;assertFalse ($this-&gt;model-&gt;DoesExist ($Id)) ;
}
</code></pre>
<p><strong>To test the Model implements Behaviour</strong></p>
<p>Having tested the behaviour is a good start but we haven’t proven the model is even using that behaviour. So we need some tests against the model the proves that it supports the contract. The test looks like this:</p>
<pre><code class="csharp">
function test_ISupportDeletion_Delete ()
{
    $this-&gt;UserTest =&amp; new UserTest () ;

    $behaviourTestSet =&amp; new ISupportDeletion_Delete_TestSet ($this, $this-&gt;UserTest, 'fixtureCreateUniqueUser');

    $behaviourTestSet-&gt;ExecuteTestSet () ;
}
</code></pre>
<p>We instantiate our User object for testing. We then create an instance of the behaviour test set passing in the test runner and the model. We then execute the behaviour test set using the model. This proves the model supports the contract assuming all tests pass.</p>
<p><strong>Behaviour Integration Tests</strong></p>
<p>Finally we have scenarios were you want to test logical delete behaviour with the object access behaviour. Object access provides a “Get” operation but given our understanding of what logical deletion is; the get should not return anything.</p>
<p>The behaviour test set for Get can’t handle this because it doesn’t know if the model uses physical or logical delete if it even supports delete.</p>
<p>So we write integration tests for this:</p>
<pre><code class="csharp">
function test_ISupportObjectAccess_Get_Deleted ()
{
    $this-&gt;UserTest =&amp; new UserTest () ;
    $this-&gt;fixtureEmptyTable ($this-&gt;UserTest, $this-&gt;UserTest-&gt;GetTable ()) ;

    $Id = $this-&gt;fixtureCreateUniqueUser ($this-&gt;UserTest) ;
    $this-&gt;UserTest-&gt;Delete ($Id) ;

    $result = $this-&gt;UserTest-&gt;Get ($Id) ;
    $this-&gt;assertFalse ($result) ;
}
</code></pre>
<p>These are just our normal tests that are written from the perspective of the model.</p>
<p>Finally before I go here are some diagrams of how the various objects inherit/interact with each other.</p>
<div id="attachment_718" class="wp-caption aligncenter" style="width: 564px"><a href="http://distributedlife.com/blog/wp-content/uploads/DSC_4012.jpg"><img class="size-full wp-image-718 " title="Interaction Contract Usage" src="http://distributedlife.com/blog/wp-content/uploads/DSC_4012.jpg" alt="Interaction Contract Usage" width="554" height="96" /></a><p class="wp-caption-text">Interaction Contract Usage</p></div>
<div id="attachment_717" class="wp-caption aligncenter" style="width: 554px"><a href="http://distributedlife.com/blog/wp-content/uploads/DSC_4011.jpg"><img class="size-full wp-image-717 " title="Model Inheritance &amp; Interaction Contracts" src="http://distributedlife.com/blog/wp-content/uploads/DSC_4011.jpg" alt="Model Inheritance &amp; Interaction Contracts" width="544" height="281" /></a><p class="wp-caption-text">Model Inheritance &amp; Interaction Contracts</p></div>
<div id="attachment_716" class="wp-caption aligncenter" style="width: 519px"><a href="http://distributedlife.com/blog/wp-content/uploads/DSC_4010.jpg"><img class="size-full wp-image-716 " title="Behaviour Interaction Contracts" src="http://distributedlife.com/blog/wp-content/uploads/DSC_4010.jpg" alt="Behaviour Interaction Contracts" width="509" height="315" /></a><p class="wp-caption-text">Behaviour Interaction Contracts</p></div>
]]></content:encoded>
			<wfw:commentRss>http://distributedlife.com/blog/2010/04/isupport-contracts-with-behaviours-in-php.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Vanilla C with Sprinkles &#8211; Find and Replace</title>
		<link>http://distributedlife.com/blog/2010/04/vanilla-c-with-sprinkles-find-and-replace.html</link>
		<comments>http://distributedlife.com/blog/2010/04/vanilla-c-with-sprinkles-find-and-replace.html#comments</comments>
		<pubDate>Sun, 04 Apr 2010 13:55:40 +0000</pubDate>
		<dc:creator>Ryan Boucher</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[distributedlife]]></category>
		<category><![CDATA[find]]></category>
		<category><![CDATA[HP Service Test]]></category>
		<category><![CDATA[replace]]></category>
		<category><![CDATA[ryan boucher]]></category>
		<category><![CDATA[rybo]]></category>
		<category><![CDATA[service testing]]></category>
		<category><![CDATA[string]]></category>
		<category><![CDATA[vanilla c]]></category>

		<guid isPermaLink="false">http://distributedlife.com/blog/?p=577</guid>
		<description><![CDATA[This isn&#8217;t the greatest implementation but it works for me. I use this function to change configuration files before I run a service test.
The first parameter is a char* pointer that is your source text. The next two are your find and replace char* pointers.
The result is returned to the caller as a pointer but [...]]]></description>
			<content:encoded><![CDATA[<p>This isn&#8217;t the greatest implementation but it works for me. I use this function to change configuration files before I run a service test.</p>
<p>The first parameter is a char* pointer that is your source text. The next two are your find and replace char* pointers.</p>
<p>The result is returned to the caller as a pointer but you won&#8217;t have to worry about the memory as it is owned by HP Service Test.</p>
<pre><code class="cplusplus">
const char* FindAndReplace (const char* Source, const char* const Find,const char* const Replace)
{
    //start by pointing to the start of the string
    long LengthOfSource = strlen (Source) ;
    long LengthOfStringToReplace = strlen (Find) ;

    char* LastFoundStart = (char*) Source ;
    char* NextFoundStart = 0 ;
    char* Buffer = 0 ;

    lr_set (Replace, "__FindAndReplace_Replace") ;
    lr_set ("", "__FindAndReplace_New") ;
</code></pre>
<p>The code looks for a substring and and replaces when found. It then jumps past that string before trying again. This is to avoid a scenario where our replaced string contains out find string and we get into a loop until memory expires.</p>
<pre><code class="cplusplus">
    while (NextFoundStart = (char*) strstr (LastFoundStart, Find))
    {
        //Allocate a buffer to copy our string into. We want from LastFoundStart to NextFoundStart
        Buffer = (char*) malloc ( (NextFoundStart - LastFoundStart + 1) * sizeof (char)) ;

        //copy from LastFoundStart to NextFoundStart in to our new paramtere
        memcpy (Buffer, Source, (NextFoundStart - LastFoundStart) * sizeof (char)) ;

        //null terminate the string
        Buffer[(NextFoundStart - LastFoundStart) * sizeof (char)] = '\0' ;

        //Move our LastFoundStart to the end of the replaced string
        LastFoundStart = NextFoundStart + LengthOfStringToReplace ;

        //Copy our buffer into our new parameter
        lr_set (Buffer, "__FindAndReplace_Buffer") ;

        lr_set (lr_get ("{__FindAndReplace_New}{__FindAndReplace_Buffer}{__FindAndReplace_Replace}"), "__FindAndReplace_New") ;

        //tidy up for next round
        free (Buffer) ;
        Buffer = 0 ;
        lr_set ("", "__FindAndReplace_Buffer") ;
    }
</code></pre>
<p>Somewhat repeated code&#8230; because I&#8217;m somewhat lazy.</p>
<pre><code class="cplusplus">

    NextFoundStart = ( (char*) Source) + LengthOfSource ;
    if (LastFoundStart != NextFoundStart)
    {
        //we still need to copy data
        //once we have found all our matches we need to copy the end of the string from the end of the last
        // find to the end of the string... assuming the last match was not at the end of the string
        //Allocate the final buffer
        Buffer = (char*) malloc ( (NextFoundStart - LastFoundStart + 1) * sizeof (char)) ;

        //copy from LastFoundStart to NextFoundStart in to our new paramtere
        memcpy (Buffer, LastFoundStart, (NextFoundStart - LastFoundStart) * sizeof (char)) ;

        //null terminate the string
        Buffer[(NextFoundStart - LastFoundStart) * sizeof (char)] = '\0' ;

        //Copy our buffer into our new parameter
        lr_set (Buffer, "__FindAndReplace_Buffer") ;

        lr_set (lr_get ("{__FindAndReplace_New}{__FindAndReplace_Buffer}"), "__FindAndReplace_New") ;

        //tidy up for next round
        free (Buffer) ;
        Buffer = 0 ;
        lr_set ("", "__FindAndReplace_Buffer") ;
    }

    return lr_get ("{__FindAndReplace_New}") ;
}
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://distributedlife.com/blog/2010/04/vanilla-c-with-sprinkles-find-and-replace.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Vanilla C with Sprinkles &#8211; Windows Event Log</title>
		<link>http://distributedlife.com/blog/2010/04/vanilla-c-with-sprinkles-windows-event-log.html</link>
		<comments>http://distributedlife.com/blog/2010/04/vanilla-c-with-sprinkles-windows-event-log.html#comments</comments>
		<pubDate>Sat, 03 Apr 2010 13:55:57 +0000</pubDate>
		<dc:creator>Ryan Boucher</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[distributedlife]]></category>
		<category><![CDATA[HP Service Test]]></category>
		<category><![CDATA[ryan boucher]]></category>
		<category><![CDATA[rybo]]></category>
		<category><![CDATA[service testing]]></category>
		<category><![CDATA[vanilla c]]></category>

		<guid isPermaLink="false">http://distributedlife.com/blog/?p=588</guid>
		<description><![CDATA[The Event Log C functions make use of the Log Parser application. You&#8217;ll need this in a central location so that any script can call it. You will also need some file reading and string manipulating functions.
Get Event

void GetEvent (const char* EventId, const char* Event)
{
    if (!ExceptionReportingEnabled ())
    {
 [...]]]></description>
			<content:encoded><![CDATA[<p>The Event Log C functions make use of the <a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=890cd06b-abf8-4c25-91b2-f8d975cf8c07&amp;displaylang=en">Log Parser</a> application. You&#8217;ll need this in a central location so that any script can call it. You will also need some file reading and string manipulating functions.</p>
<h3>Get Event</h3>
<pre><code class="cplusplus">
void GetEvent (const char* EventId, const char* Event)
{
    if (!ExceptionReportingEnabled ())
    {
        //only check for exceptions if we are supposed to

        return ;
    }

    lr_save_string (EventId, "__EventId") ;

    Create_GetEvent_BatchFile (EventId) ;

    system ("ServiceTest.bat") ;

    ReadFile (lr_eval_string("{TempFile}"), "FileData", "ISO-10646-UCS-2") ;

    lr_xml_extract
    (
        "XML={FileData}",
        "FastQuery=ROOT/ROW",
        "XMLFragmentParam=__Event",
        "NotFound=Continue",
        LAST
    );

    lr_save_string (lr_eval_string ("{__Event}"), Event) ;
}
</code></pre>
<p>The follow function is used to create a batch file. This is the best option that trying to call the LogParser directly from the command line.</p>
<pre><code class="cplusplus">
void Create_GetEvent_BatchFile (const char* EventId)
{
    long FileHandle = 0 ;

    lr_set (EventId, "__EventId") ;

    lr_set
    (
        lr_get
        (
            "\"\\\\vcclfs02-data1\\Wessupp\\Applications Test Team\\ServiceTest\\Tools\\Log Parser 2.2\\LogParser.exe\" -i:EVT -o:xml -schemaType:0 -compact -oCodepage:0
            \"SELECT
                EventLog,
                RecordNumber,
                TimeGenerated,
                TimeWritten,
                EventId,
                EventType,
                EventTypeName,
                EventCategory,
                EventCategoryName,
                SourceName,
                ComputerName,
                SID,
                Message
            INTO
                {TempFile}
            FROM
                \\\\{Server}\\{EventLogSource}
            WHERE
                RecordNumber = '{__EventId}'\""
        ),
        "__Create_GetEvent_BatchFile"
    ) ;

    lr_set (lr_get ("{__Create_GetEvent_BatchFile}"), "__ServiceTest_LastBatchBody") ;

    FileHandle = fopen("ServiceTest.bat", "w") ;
    if (!FileHandle)
    {
        lr_error_message ("Unable to create the \"ServiceTest.bat\" file.") ;

        return ;
    }

    fprintf (FileHandle, lr_get ("{__Create_GetEvent_BatchFile}")) ;
    fclose (FileHandle) ;
}
</code></pre>
<h3>Get Most Recent Event Record Number</h3>
<p>If we wanted to get the highest record number in the log at the current point in time; use this log parser call in the batch file.</p>
<pre><code class="cplusplus">
lr_set
(
    lr_get
    (
        "\"\\\\vcclfs02-data1\\Wessupp\\Applications Test Team\\ServiceTest\\Tools\\Log Parser 2.2\\LogParser.exe\" -i:EVT -o:xml -schemaType:0 -compact -oCodepage:0
        \"SELECT
            MAX(RecordNumber)
        INTO
            {TempFile}
        FROM
            \\\\{Server}\\{EventLogSource}
        WHERE
            SourceName = '{FullServiceName}'\""
    ),
    "__Create_GetMostRecentEventRecordNumber_BatchFile"
) ;
</code></pre>
<p>Call like GetEvent above but you should trim the response so it can be cast as a number</p>
<pre><code class="cplusplus">
void GetMostRecentEventRecordNumber (HpParameter StartEventCount)
{
    if (!ExceptionReportingEnabled ())
    {
        //only check for exceptions if we are supposed to
        return ;
    }

    Create_GetMostRecentEventRecordNumber_BatchFile () ;

    system ("ServiceTest.bat") ;

    ReadFile (lr_eval_string("{TempFile}"), "FileData", "UTF-8") ;

    lr_xml_get_values
    (
        "XML={FileData}",
        "FastQuery=ROOT/ROW/MAX_ALL_RecordNumber_",
        "ValueParam=__GetMostRecentEventRecordNumber_StartEventCount",
        "NotFound=Continue",
        LAST
    );

    Trim (lr_get("{__GetMostRecentEventRecordNumber_StartEventCount}"), StartEventCount)  ;
}
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://distributedlife.com/blog/2010/04/vanilla-c-with-sprinkles-windows-event-log.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Vanilla C with Sprinkles &#8211; Trimming Strings</title>
		<link>http://distributedlife.com/blog/2010/03/vanilla-c-with-sprinkles-trimming-strings.html</link>
		<comments>http://distributedlife.com/blog/2010/03/vanilla-c-with-sprinkles-trimming-strings.html#comments</comments>
		<pubDate>Tue, 30 Mar 2010 13:55:25 +0000</pubDate>
		<dc:creator>Ryan Boucher</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[distributedlife]]></category>
		<category><![CDATA[HP Service Test]]></category>
		<category><![CDATA[ryan boucher]]></category>
		<category><![CDATA[rybo]]></category>
		<category><![CDATA[service testing]]></category>
		<category><![CDATA[string]]></category>
		<category><![CDATA[trim]]></category>

		<guid isPermaLink="false">http://distributedlife.com/blog/?p=573</guid>
		<description><![CDATA[I was going to talk about disciplines in software testing but my netbook isn&#8217;t connecting to the network and consequently I can&#8217;t get the post off. Still I had this one scheduled for the weekend. It&#8217;s about strings in old languages like Latin. I&#8217;ll retype the other one or fix the network. Till tomorrow!
&#8212;
Implementing the [...]]]></description>
			<content:encoded><![CDATA[<p>I was going to talk about disciplines in software testing but my netbook isn&#8217;t connecting to the network and consequently I can&#8217;t get the post off. Still I had this one scheduled for the weekend. It&#8217;s about strings in old languages like Latin. I&#8217;ll retype the other one or fix the network. Till tomorrow!</p>
<p>&#8212;</p>
<p>Implementing the trim function barely gets a mention in languages these days because having to implemented it in a new language is stoopid. However C is old and grizzled.</p>
<p>This version stores the result in a HP Paramater for use outside the function. You could return it if you wanted. If you did you would need to remove the free statement.</p>
<pre><code class="cplusplus">
void Trim (const char* Bloated, const char* Parameter)
{
    long BloatedLength = strlen (Bloated) ;

    char* Trimmed = 0 ;
    long TrimmedLength = 0 ;

    long TrimFromStart = strspn (Bloated, " \t") ;
    long TrimFromEnd = 0 ;

    long index = 0 ;
</code></pre>
<p>We need to calculate the number of characters that are white space at the end of the string. This ignores new line characters.</p>
<pre><code class="cplusplus">
    //Count Trailing whitespace
    index = BloatedLength - 1 ;
    while ( (Bloated[index] == ' ') || (Bloated[index] == '\t'))
    {
        TrimFromEnd++ ;

        if (index == 0)
        {
            break ;
        }

        index-- ;
    }

    TrimmedLength = BloatedLength - TrimFromEnd - TrimFromStart ;
    TrimmedLength++ ; //space for null terminating

    Trimmed = (char*) malloc (TrimmedLength * sizeof(char)) ;
    if (!Trimmed)
    {
        lr_error_message("Failed to allocate memory for the trimmed string") ;

        return ;
    }
</code></pre>
<p>Our actual trim is nothing more than memcpy so you could write a substring function and do it that way.</p>
<pre><code class="cplusplus">
    //"trim"
    memcpy (Trimmed, Bloated + TrimFromStart, TrimmedLength) ;

    //set null terminating char
    Trimmed[TrimmedLength - 1] = '\0' ;

    lr_set (Trimmed, Parameter);

    free (Trimmed) ;
}
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://distributedlife.com/blog/2010/03/vanilla-c-with-sprinkles-trimming-strings.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Vanilla C with Sprinkles &#8211; Useful String Functions</title>
		<link>http://distributedlife.com/blog/2010/03/vanilla-c-with-sprinkles-useful-string-functions.html</link>
		<comments>http://distributedlife.com/blog/2010/03/vanilla-c-with-sprinkles-useful-string-functions.html#comments</comments>
		<pubDate>Sun, 28 Mar 2010 13:55:31 +0000</pubDate>
		<dc:creator>Ryan Boucher</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[distributedlife]]></category>
		<category><![CDATA[HP Service Test]]></category>
		<category><![CDATA[ryan boucher]]></category>
		<category><![CDATA[rybo]]></category>
		<category><![CDATA[service testing]]></category>
		<category><![CDATA[string]]></category>
		<category><![CDATA[vanilla c]]></category>

		<guid isPermaLink="false">http://distributedlife.com/blog/?p=564</guid>
		<description><![CDATA[Service testing is made so much easier if you have some basic string functionality to assist you. C isn&#8217;t spectacular with strings so we need to write most of the basic stuff ourself.
Here are some of my favourite string functions for use within service tests.
Get Random String of Length

enum GetStringOfLengthTypes
{
    AlphaOnly,
  [...]]]></description>
			<content:encoded><![CDATA[<p>Service testing is made so much easier if you have some basic string functionality to assist you. C isn&#8217;t spectacular with strings so we need to write most of the basic stuff ourself.</p>
<p>Here are some of my favourite string functions for use within service tests.</p>
<h3>Get Random String of Length</h3>
<pre><code class="cplusplus">
enum GetStringOfLengthTypes
{
    AlphaOnly,
    NumericOnly,
    AlphaNumeric
} ;

const char* GetStringOfLength (const unsigned int length, const unsigned int Type)
{
    unsigned int i = 0 ;
    static const char Alpha[] = "abcdefghijklmnopqrstuvwxyz"
                                         "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    static const char Numeric[] = "0123456789" ;
    static const char AlphaNumeric[] = "abcdefghijklmnopqrstuvwxyz"
                                                    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                                                    "0123456789" ;

    char* Value = (char*) malloc ( (sizeof(char) * length) + 1) ;
</code></pre>
<p>SrandSingleton is a global integer that is defaulted to zero. It represents a &#8217;singleton&#8217; object.</p>
<pre><code class="cplusplus">

    if (SrandSingleton == 0)
    {
        srand (time (NULL)) ;
        SrandSingleton = 1 ;
    }

    for (i = 0; i &lt; length; i++)
    {
        switch (Type)
        {
        case 0:
            Value[i] = Alpha[rand() % (sizeof (Alpha) - 1)] ;
            break ;
        case 1:
            Value[i] = Numeric[rand() % (sizeof (Numeric) - 1)] ;
            break ;
        case 2:
            Value[i] = AlphaNumeric[rand() % (sizeof (AlphaNumeric) - 1)] ;
            break ;
        }
    }

    Value[length] = '\0' ;

    lr_get (Value, "__GetStringOfLength_Total") ;

    free (Value) ;

    return lr_get ("{__GetStringOfLength_Total}") ;
}
</code></pre>
<h3>String is Empty?</h3>
<pre><code class="cplusplus">
enum EnabledOptions StringIsEmpty (const char* const a)
{
    if (strcmp (a, "") == '\0')
    {
        return Yes ;
    }
    else
    {
        return No ;
    }
}
</code></pre>
<h3>Strings are Equal?</h3>
<pre><code class="cplusplus">
enum EnabledOptions StringsAreEqual (const char* const a, const char* const b)
{
    if (strcmp (a, b) == '\0')
    {
        return Yes ;
    }
    else
    {
        return No ;
    }
}
</code></pre>
<h3>Is In String?</h3>
<pre><code class="cplusplus">
enum EnabledOptions IsInString (const char* const Source, const char* const Find)
{
    if (strstr (Source, Find) == 0)
    {
        return No ;
    }
    else
    {
        return Yes ;
    }
}
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://distributedlife.com/blog/2010/03/vanilla-c-with-sprinkles-useful-string-functions.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Vanilla C with Sprinkles &#8211; Windows Registry Fun!</title>
		<link>http://distributedlife.com/blog/2010/03/vanilla-c-with-sprinkles-windows-registry-fun.html</link>
		<comments>http://distributedlife.com/blog/2010/03/vanilla-c-with-sprinkles-windows-registry-fun.html#comments</comments>
		<pubDate>Sat, 27 Mar 2010 13:55:13 +0000</pubDate>
		<dc:creator>Ryan Boucher</dc:creator>
				<category><![CDATA[development]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[distributedlife]]></category>
		<category><![CDATA[HP Service Test]]></category>
		<category><![CDATA[ryan boucher]]></category>
		<category><![CDATA[rybo]]></category>
		<category><![CDATA[vanilla c]]></category>

		<guid isPermaLink="false">http://distributedlife.com/blog/?p=556</guid>
		<description><![CDATA[The registry functions do a lot of duplication but are primarily used for deployment validation so building objects of keys and enumerating these arrays is not necessary. Some of these leverage the file functions.
Does Registry Path Exist?

void CheckRegistryPathExists (const char* const Server, const char* const SubKey)
{
    const char LineSeperators[] = "\n" ;
 [...]]]></description>
			<content:encoded><![CDATA[<p>The registry functions do a lot of duplication but are primarily used for deployment validation so building objects of keys and enumerating these arrays is not necessary. Some of these leverage the <a href="http://distributedlife.com/blog/2010/03/vanilla-c-with-sprinkles-file-reading.html">file functions</a>.</p>
<h3>Does Registry Path Exist?</h3>
<pre><code class="cplusplus">
void CheckRegistryPathExists (const char* const Server, const char* const SubKey)
{
    const char LineSeperators[] = "\n" ;
    const char LinePartSeperators[] = "\t" ;
    char* LineToken;
    char* LinePartToken ;
    char* NextPartToken ;

    lr_set (Server, "__CheckRegistryValue_Server") ;
    lr_set (SubKey, "__CheckRegistryValue_SubKey") ;
</code></pre>
<p>Run the REG Query command and pipe the results into a temporary file.</p>
<pre><code class="cplusplus">
    system
    (
        lr_get
        (
            "REG QUERY \"\\\\{__CheckRegistryValue_Server}\\{__CheckRegistryValue_SubKey}\" &gt; {TempFile}"
        )
    ) ;
</code></pre>
<p>Read temp file in and look for the exact string specified below. If there is no registry value found then it writes a specific error message out. We can use this to determine whether our value exists in the registry.</p>
<pre><code class="cplusplus">
    ReadFile (lr_get ("{TempFile}"), "RegExtract", "UTF-8") ;

    if (strstr (lr_get ("{RegExtract}"), "Error:  The system was unable to find the specified registry key or value") != '\0')
    {
        lr_error_message (lr_get ("The supplied registry path ({__CheckRegistryValue_Server}\\{__CheckRegistryValue_SubKey}) does not exist")) ;
    }
}
</code></pre>
<h3>Check Registry Value</h3>
<pre><code class="cplusplus">
void CheckRegistryValue (const char* Server, const char* SubKey, const char* Property, const char* ExpectedValue)
{
    const char LineSeperators[] = "\n" ;
    const char LinePartSeperators[] = "\t" ;
    char* LineToken;
    char* LinePartToken ;
    char* NextPartToken ;

    lr_set (Server, "__CheckRegistryValue_Server") ;
    lr_set (SubKey, "__CheckRegistryValue_SubKey") ;
    lr_set (Property, "__CheckRegistryValue_Property") ;

    system
    (
        lr_get
        (
            "REG QUERY \"\\\\{__CheckRegistryValue_Server}\\{__CheckRegistryValue_SubKey}\" /v {__CheckRegistryValue_Property} &gt; {TempFile}"
        )
    ) ;

    ReadFile (lr_get ("{TempFile}"), "RegExtract", "UTF-8") ;
</code></pre>
<p>There is a &#8220;standard&#8221; format for the REG query output. </p>
<pre><code class="cplusplus">
    //Format for Reg output
    //    blank line
    //    declaration
    //    blank line
    //    path
    //    property type value
</code></pre>
<p>Tokenise the string and then parse it to get the value.</p>
<pre><code class="cplusplus">
    LineToken = (char *)strtok(lr_get ("{RegExtract}"), LineSeperators); // Get the first token
    if (!LineToken)
    {
        lr_error_message ("Registry Value Not Found (No tokens found in string!)") ;
        return ;
    }

    while (LineToken != NULL)
    {
        // Find line that contains the property
        if (strstr (LineToken, lr_get ("{__CheckRegistryValue_Property}")) &gt; 0)
        {
            //The format of the line is "property[\t]type[\t]value"
            LinePartToken = (char *)strtok(LineToken, LinePartSeperators); // Get the first token
            if (!LinePartToken)
            {
                lr_error_message ("Registry Value Not Found (No tokens found in string!)") ;
                return ;
            }

            while (LinePartToken != NULL)
            {
                NextPartToken = LinePartToken ;
                LinePartToken = (char *) strtok (NULL, LinePartSeperators) ;
            }

            //we have the last token in 'Next' so use that
            Trim (NextPartToken, "ActualValue") ;
            TrimNewLine (lr_get ("{ActualValue}"), "ActualValue") ;

            CompareStrings (lr_get ("{ActualValue}"), ExpectedValue) ;

            return ;
        }

        LineToken = (char *) strtok (NULL, LineSeperators) ;
    }

    lr_error_message ("Registry Value Not Found") ;
}
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://distributedlife.com/blog/2010/03/vanilla-c-with-sprinkles-windows-registry-fun.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

