<?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>Parks Computing &#187; software</title>
	<atom:link href="http://www.parkscomputing.com/labels/software/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.parkscomputing.com</link>
	<description>Pedagogy for the autodidactic programmer</description>
	<lastBuildDate>Thu, 12 Jan 2012 22:48:30 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.4</generator>
		<item>
		<title>Olympic Note Passing</title>
		<link>http://www.parkscomputing.com/2011/06/olympic-note-passing/</link>
		<comments>http://www.parkscomputing.com/2011/06/olympic-note-passing/#comments</comments>
		<pubDate>Sat, 04 Jun 2011 16:21:29 +0000</pubDate>
		<dc:creator>paulmooreparks</dc:creator>
				<category><![CDATA[analogies]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[rants]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[writing]]></category>

		<guid isPermaLink="false">http://www.parkscomputing.com/?p=654</guid>
		<description><![CDATA[A good analogy can often be useful to explain intricate technical details. In an earlier article, &#8220;Wrong Fish Food&#8221;, I related an analogy that I used to describe a technical issue to a non-technical audience. This article shares an analogy I created for a technical audience, because sometimes even techies need an analogy to grasp [...]]]></description>
			<content:encoded><![CDATA[<p>A good analogy can often be useful to explain intricate technical details. In an earlier article, <a href="http://www.parkscomputing.com/2011/04/wrong-fish-food/">&#8220;Wrong Fish Food&#8221;</a>, I related an analogy that I used to describe a technical issue to a non-technical audience. This article shares an analogy I created for a technical audience, because sometimes even techies need an analogy to grasp an unfamiliar technology.</p>
<p><span id="more-654"></span></p>
<p>For the past few years I&#8217;ve been responsible for maintaining a subsystem that allows a point-of-sale system to communicate with external applications. These external applications may display information gathered from the POS and poke inputs back into it. I was involved in the initial development of the subsystem back in 2004 or so, and when the other developers left for other positions it fell to me to continue enhancing and supporting the subsystem.</p>
<p>I wasn&#8217;t happy with the first implementation because of some design problems, but I couldn&#8217;t change the design too much without impacting existing integrations with the subsystem. Instead, over the course of a year or so I made several improvements to the bits of code that integrated the subsystem with other applications and improved the API that the subsystem exposes.</p>
<p>Considering the number of transactions it supports every day across hundreds of installations, I think the subsystem is rather fast and stable. Occasionally, however, whenever there is a performance issue with an application that uses the subsystem, the subsystem itself gets blamed, and I am usually called upon to investigate the issue. While there <em>are</em> some scenarios that can trigger bottlenecks with the subsystem, the problems I investigate are pretty much never the fault of the subsystem.</p>
<p>Finally, after having to explain the intricate technical details yet again, I decided to come up with an analogy to explain how the subsystem works, how it <em>could</em> be a bottleneck is some scenarios, and why it usually wasn&#8217;t the bottleneck in spite of the possibility. I e-mailed the analogy to all interested parties, and it seems to have had the desired effect. I&#8217;ve edited the analogy to remove specific product and subsystem names and provided it here. In the wrap-up, I changed the name of the subsystem to NPS, for Note-Passing Subsystem.</p>
<h2>The Story of Olympic Note Passing</h2>
<p>We&#8217;re going to imagine a strange sport called Olympic Note Passing. Two runners run around adjacent circular tracks: Runner A, and runner F. Each time runner A reaches the outside portion of the track he picks up a message that he will deliver to runner F. Runner F will then deliver this message to the other side of track F. Never mind why this happens; some sports are just unusual (think golf or curling).</p>
<p>Now, the first time this sport was introduced into the Olympics, the messages were passed from runner A to runner F via a specially designed, and somewhat magical, box. This box accepts messages via a device like a bill acceptor, and on the other side of the box is a device like a bill dispenser that spits out formatted, translated copies of the messages that were put into the other side. This way, runners from different nations that speak different languages can still participate in the sport and understand the messages being passed from A to F.</p>
<p>Runners on the two tracks could run at independent speeds, so that runner A could stop for just a moment and place a message into the box before taking off around the track again, and runner F could stop for just a moment to pick up any messages that were waiting.</p>
<div id="attachment_657" class="wp-caption aligncenter" style="width: 668px"><a href="http://www.parkscomputing.com/wp-content/uploads/Note-Passing-1.jpg"><img src="http://www.parkscomputing.com/wp-content/uploads/Note-Passing-1.jpg" alt="Note Passing, Old Style" title="Note Passing, Old Style" width="658" height="270" class="size-full wp-image-657" /></a><p class="wp-caption-text">The original practice of Olympic Note Passing</p></div>
<p>There was a problem with this magic box, however. If runner A was placing messages into one side of the box, runner F could not take any messages out of the box until A was finished. Likewise, if runner F was taking messages out, runner A couldn&#8217;t place any messages into the box. This turned out to be a problem because the runners had to keep stopping and waiting for each other.</p>
<p>This state of affairs threatened to kill the fledgling sport before it ever got started, since spectators didn&#8217;t want to see the two runners stopping, starting, and waiting for each other all the time, so the Olympic committee had to come up with a solution. One intrepid committee member suggested designing new magic boxes so that messages could be inserted and extracted at the same time, but the committee had already spent a lot of money designing the existing boxes, and any new boxes would have to be the same size, shape, and color as the existing boxes and would have to work EXACTLY the same way in every other respect. Furthermore, the guy that designed the original boxes was now working for NASCAR and doing equally silly things to the <a href="http://en.wikipedia.org/wiki/Car_of_Tomorrow">Car of Tomorrow</a>.</p>
<p>The way the committee decided to fix this problem was to station a person on either side of the magic box: helper A, and helper F. Now, runner A just hands messages to helper A on the way around the track, and helper A inserts the message into the machine while runner A keeps moving. Likewise, helper F extracts any waiting messages from the machine and hands them off to runner F when he comes around the track. If helper A sees that the machine is busy and he can&#8217;t insert any messages, he will just put the new messages in his pocket until the machine is available. Helper F will pull messages out of the machine as soon as they are available and keep them in his pocket until runner F comes around the track. If helper F has no messages, he will wave runner F on around the track.</p>
<div id="attachment_658" class="wp-caption aligncenter" style="width: 785px"><a href="http://www.parkscomputing.com/wp-content/uploads/Note-Passing-2.jpg"><img src="http://www.parkscomputing.com/wp-content/uploads/Note-Passing-2.jpg" alt="" title="Note Passing 2" width="775" height="270" class="size-full wp-image-658" /></a><p class="wp-caption-text">New and improved Olympic Note Passing</p></div>
<p>Now, we&#8217;ll bring all of this around to the actual NPS design. Runner A is an AwesomePOS system, and Runner F is FooApp. The magic box is NPS. The messages being passed through the magic box are NPS messages. The redesigned sport, with helper A and helper B, represents the new NPS integration design introduced at SuperCustomer in late 2009 and early 2010. The people stationed on either side of the magic box are the independent threads that are now running in AwesomePOS and FooApp that service the NPS message queue.</p>
<p>With this picture in mind, it should be a little more apparent why NPS is pretty much never the source of any performance issues in current implementations, but why it can be a performance bottleneck if the integration is not done carefully. If AwesomePOS or FooApp have to keep stopping and starting to deal with NPS, it can impact the performance of the entire system. With the new design, however, NPS is much less of a bottleneck since runners A and F can now keep moving around the track a lot more smoothly, but messages can still get from one side of track A to the other side of track F no more quickly than the slower runner can move.</p>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Fwww.parkscomputing.com%2F2011%2F06%2Folympic-note-passing%2F&amp;t=Olympic%20Note%20Passing" id="facebook_share_button_654" style="font-size:11px; line-height:13px; font-family:'lucida grande',tahoma,verdana,arial,sans-serif; text-decoration:none; display: -moz-inline-block; display:inline-block; padding:1px 20px 0 5px; margin: 5px 0; height:15px; border:1px solid #d8dfea; color: #3B5998; background: #fff url(http://b.static.ak.fbcdn.net/images/share/facebook_share_icon.gif) no-repeat top right;">Share</a>
	<script type="text/javascript">
	<!--
	var button = document.getElementById('facebook_share_link_654') || document.getElementById('facebook_share_icon_654') || document.getElementById('facebook_share_both_654') || document.getElementById('facebook_share_button_654');
	if (button) {
		button.onclick = function(e) {
			var url = this.href.replace(/share\.php/, 'sharer.php');
			window.open(url,'sharer','toolbar=0,status=0,width=626,height=436');
			return false;
		}
	
		if (button.id === 'facebook_share_button_654') {
			button.onmouseover = function(){
				this.style.color='#fff';
				this.style.borderColor = '#295582';
				this.style.backgroundColor = '#3b5998';
			}
			button.onmouseout = function(){
				this.style.color = '#3b5998';
				this.style.borderColor = '#d8dfea';
				this.style.backgroundColor = '#fff';
			}
		}
	}
	-->
	</script>
	]]></content:encoded>
			<wfw:commentRss>http://www.parkscomputing.com/2011/06/olympic-note-passing/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>I have a wide screen. Let me use it.</title>
		<link>http://www.parkscomputing.com/2011/04/i-have-a-wide-screen-let-me-use-it/</link>
		<comments>http://www.parkscomputing.com/2011/04/i-have-a-wide-screen-let-me-use-it/#comments</comments>
		<pubDate>Mon, 11 Apr 2011 19:20:44 +0000</pubDate>
		<dc:creator>paulmooreparks</dc:creator>
				<category><![CDATA[UI]]></category>
		<category><![CDATA[rants]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://www.parkscomputing.com/?p=629</guid>
		<description><![CDATA[Dear web-design people, Please don&#8217;t design sites that look like this: Rather, please design sites that look like this: I&#8217;m not knocking Herb Sutter, here. I&#8217;m pretty sure he&#8217;s just using a stock template, but templates that don&#8217;t flow to fit the browser width really puzzle me. Is there a good reason for them that [...]]]></description>
			<content:encoded><![CDATA[<p>Dear web-design people,</p>
<p>Please don&#8217;t design sites that look like this:</p>
<div id="attachment_630" class="wp-caption alignnone" style="width: 310px"><a href="http://www.parkscomputing.com/wp-content/uploads/BadWebDesign.png"><img src="http://www.parkscomputing.com/wp-content/uploads/BadWebDesign-300x164.png" alt="Waste of space!" title="Bad Web Design" width="300" height="164" class="size-medium wp-image-630" /></a><p class="wp-caption-text">Waste of space!</p></div>
<p>Rather, please design sites that look like this:</p>
<div id="attachment_632" class="wp-caption alignnone" style="width: 310px"><a href="http://www.parkscomputing.com/wp-content/uploads/GoodWebDesign.png"><img src="http://www.parkscomputing.com/wp-content/uploads/GoodWebDesign-300x164.png" alt="This uses space more effectively." title="Good Web Design" width="300" height="164" class="size-medium wp-image-632" /></a><p class="wp-caption-text">This uses space more effectively.</p></div>
<p>I&#8217;m not knocking Herb Sutter, here. I&#8217;m pretty sure he&#8217;s just using a stock template, but templates that don&#8217;t flow to fit the browser width really puzzle me. Is there a good reason for them that I&#8217;m just not aware of?</p>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Fwww.parkscomputing.com%2F2011%2F04%2Fi-have-a-wide-screen-let-me-use-it%2F&amp;t=I%20have%20a%20wide%20screen.%20Let%20me%20use%20it." id="facebook_share_button_629" style="font-size:11px; line-height:13px; font-family:'lucida grande',tahoma,verdana,arial,sans-serif; text-decoration:none; display: -moz-inline-block; display:inline-block; padding:1px 20px 0 5px; margin: 5px 0; height:15px; border:1px solid #d8dfea; color: #3B5998; background: #fff url(http://b.static.ak.fbcdn.net/images/share/facebook_share_icon.gif) no-repeat top right;">Share</a>
	<script type="text/javascript">
	<!--
	var button = document.getElementById('facebook_share_link_629') || document.getElementById('facebook_share_icon_629') || document.getElementById('facebook_share_both_629') || document.getElementById('facebook_share_button_629');
	if (button) {
		button.onclick = function(e) {
			var url = this.href.replace(/share\.php/, 'sharer.php');
			window.open(url,'sharer','toolbar=0,status=0,width=626,height=436');
			return false;
		}
	
		if (button.id === 'facebook_share_button_629') {
			button.onmouseover = function(){
				this.style.color='#fff';
				this.style.borderColor = '#295582';
				this.style.backgroundColor = '#3b5998';
			}
			button.onmouseout = function(){
				this.style.color = '#3b5998';
				this.style.borderColor = '#d8dfea';
				this.style.backgroundColor = '#fff';
			}
		}
	}
	-->
	</script>
	]]></content:encoded>
			<wfw:commentRss>http://www.parkscomputing.com/2011/04/i-have-a-wide-screen-let-me-use-it/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>A Script to Find All Aliases for a Cmdlet</title>
		<link>http://www.parkscomputing.com/2011/03/a-script-to-find-all-aliases-for-a-cmdlet/</link>
		<comments>http://www.parkscomputing.com/2011/03/a-script-to-find-all-aliases-for-a-cmdlet/#comments</comments>
		<pubDate>Tue, 22 Mar 2011 01:04:24 +0000</pubDate>
		<dc:creator>Paul Parks</dc:creator>
				<category><![CDATA[.NET]]></category>
		<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[scripting]]></category>
		<category><![CDATA[software]]></category>

		<guid isPermaLink="false">http://www.parkscomputing.com/2011/03/a-script-to-find-all-aliases-for-a-cmdlet/</guid>
		<description><![CDATA[I&#8217;ve mentioned before that I love PowerShell, but I&#8217;m still trying to commit enough Cmdlets and aliases to memory that I can be immediately productive from a Powershell prompt without having to have a browser window open to the Powershell documentation on another monitor. Several of the Cmdlets may also be referenced through one or [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve mentioned before that I love <a href="http://technet.microsoft.com/en-us/library/bb978526.aspx">PowerShell</a>, but I&#8217;m still trying to commit enough <a href="http://www.bing.com/browse?g=powershell_cmdlets&amp;FORM=Z9GE22#toc=0">Cmdlets</a> and <a href="http://msdn.microsoft.com/en-us/library/ms714395(VS.85).aspx">aliases</a> to memory that I can be immediately productive from a Powershell prompt without having to have a browser window open to the Powershell documentation on another monitor. Several of the Cmdlets may also be referenced through one or more aliases, and it&#8217;s rather cumbersome to discover what aliases are defined for a given Cmdlet.</p>
<p>I stumbled onto a bit of code that will find all aliases for a given Cmdlet which I put it into a script named <code>Find-Alias</code>. That will let me type the following:</p>
<div class="example">
<pre>
<strong>PS C:\>Find-Alias Get-ChildItem</strong>

CommandType     Name                     Definition
-----------     ----                     ----------
Alias           dir                      Get-ChildItem
Alias           gci                      Get-ChildItem
Alias           ls                       Get-ChildItem
</pre>
</div>
<p>Here is the script code:</p>
<div class="example">
<pre>
param(
    [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true, ValueFromPipeLineByPropertyName=$true)]
    [string]$Command)

Process
{
  get-alias | where-object {$_.Definition -match $Command}
}
</pre>
</div>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Fwww.parkscomputing.com%2F2011%2F03%2Fa-script-to-find-all-aliases-for-a-cmdlet%2F&amp;t=A%20Script%20to%20Find%20All%20Aliases%20for%20a%20Cmdlet" id="facebook_share_button_610" style="font-size:11px; line-height:13px; font-family:'lucida grande',tahoma,verdana,arial,sans-serif; text-decoration:none; display: -moz-inline-block; display:inline-block; padding:1px 20px 0 5px; margin: 5px 0; height:15px; border:1px solid #d8dfea; color: #3B5998; background: #fff url(http://b.static.ak.fbcdn.net/images/share/facebook_share_icon.gif) no-repeat top right;">Share</a>
	<script type="text/javascript">
	<!--
	var button = document.getElementById('facebook_share_link_610') || document.getElementById('facebook_share_icon_610') || document.getElementById('facebook_share_both_610') || document.getElementById('facebook_share_button_610');
	if (button) {
		button.onclick = function(e) {
			var url = this.href.replace(/share\.php/, 'sharer.php');
			window.open(url,'sharer','toolbar=0,status=0,width=626,height=436');
			return false;
		}
	
		if (button.id === 'facebook_share_button_610') {
			button.onmouseover = function(){
				this.style.color='#fff';
				this.style.borderColor = '#295582';
				this.style.backgroundColor = '#3b5998';
			}
			button.onmouseout = function(){
				this.style.color = '#3b5998';
				this.style.borderColor = '#d8dfea';
				this.style.backgroundColor = '#fff';
			}
		}
	}
	-->
	</script>
	]]></content:encoded>
			<wfw:commentRss>http://www.parkscomputing.com/2011/03/a-script-to-find-all-aliases-for-a-cmdlet/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Please Stop Holding Project Status Meetings</title>
		<link>http://www.parkscomputing.com/2011/01/please-stop-holding-project-status-meetings/</link>
		<comments>http://www.parkscomputing.com/2011/01/please-stop-holding-project-status-meetings/#comments</comments>
		<pubDate>Sat, 29 Jan 2011 17:41:40 +0000</pubDate>
		<dc:creator>Paul Parks</dc:creator>
				<category><![CDATA[consulting]]></category>
		<category><![CDATA[rants]]></category>
		<category><![CDATA[software]]></category>

		<guid isPermaLink="false">http://www.parkscomputing.com/?p=363</guid>
		<description><![CDATA[(I&#8217;ve also posted this same article on Code Project.) Before I launch into the body of this essay, let me say that I think meetings are wonderful things. I&#8217;ll even go so far as to say that people in my field, software development, probably don&#8217;t have enough meetings. I have to qualify these opinions, however, [...]]]></description>
			<content:encoded><![CDATA[<p>(I&#8217;ve also posted this same article on <a href="http://www.codeproject.com/KB/work/Stop_Project_Meetings.aspx">Code Project</a>.)</p>
<p>Before I launch into the body of this essay, let me say that I think meetings are wonderful things. I&#8217;ll even go so far as to say that people in my field, software development, probably don&#8217;t have enough meetings. I have to qualify these opinions, however, by saying that they only apply to the right <em>kinds</em> of meetings. Regularly scheduled project status meetings that involve an entire team are definitely the wrong kind of meeting. They provide very little useful communication, and they kill productivity. There are better ways to accomplish the purpose of status meetings without actually having them.</p>
<p>  <span id="more-363"></span></p>
<h2>What&#8217;s Wrong with Project Status Meetings?</h2>
<p>A project status meeting usually involves a manager and a team. Each member of the team, in turn, provides an update on the tasks that member has been working on since the last meeting. The manager or other team members may ask for additional information or provide comments on these updates. Team members that have little or no involvement with the tasks that are being discussed just sit and listen (or sleep, or doodle, or do what I do occasionally &#8212; write code long-hand on a pad of paper).</p>
<p>In a particularly malignant version of the status meeting, several teams and their managers are in a meeting with a higher-level manager, but each individual team member still reports on his tasks. On one project, I had meetings like this on a weekly basis, with upwards of 40 different people in one room. It was dreadful.</p>
<p>What makes these meetings so dreadful for me is that I&#8217;m only an active participant for two or three minutes. For the rest of the time (an hour and a half, or more, once a week, on my last project) I don&#8217;t have any real involvement with what&#8217;s being discussed. I&#8217;m a captive. I can neither provide any useful commentary on the discussion nor gain any useful insight from it. Even worse, I&#8217;m a consultant that bills on an hourly basis. I <em>hate</em> wasting my client&#8217;s time on uselessness when I could be doing something more productive with it.</p>
<h2>Why I Think We Do It This Way</h2>
<p>For several reasons, holding a management position is thought of as being a few steps up the career ladder, above the lowly status of the worker bee. I happen to think this situation is unfortunate, but that will be the topic of a later essay. In any case, people currently in management are usually older than the people that they manage, and they usually learned what they know about office interaction when they were younger, or at the very least from people who are older than they are. This means that the habits that are ingrained in managers may reflect a technological state of the art that was current ten or twenty years ago, maybe more.</p>
<p>Before the advent of telephones, e-mail, instant messaging, and wikis, the only way to get a timely update from several members of a team was to gather the members of the team together at a pre-arranged time and place. Fortunately, the telephone pre-dates most people currently in management, so for widely dispersed teams the conference call is in common use.</p>
<p>I try to take advantage of status meetings held both in-person and via conference calls by arranging my schedule so that my virtual working hours fall during the times that the status meetings are held. I dial into the meeting on my cell, using a headset so that my hands can stay on the keyboard. I listen for my name, provide my update and any comments that are relevant, and for the rest of the time I&#8217;m writing code, debugging, documenting, or testing. I&#8217;m less productive than I would be if I didn&#8217;t have to split my brain between the meeting and other tasks, but at least my downtime isn&#8217;t a total loss.</p>
<h2>A Better Way</h2>
<p>Rather than holding status meetings in person or via conference calls, why not take advantage of that wonderful new invention called e-mail? Every team I&#8217;ve worked on since 1993 has had e-mail. It&#8217;s safe to say, in fact, that I would <em>refuse</em> to work on a team in which one or more members didn&#8217;t have an e-mail address. E-mail has reached a comfortable level of ubiquity.</p>
<p>Here is what I propose: On a regular basis (for example, before noon every Monday) each team member would send an e-mail to the team&#8217;s manager and copy every other member of the team. The subject of the e-mail would clearly state that it contains a weekly status update: &quot;Status Update for 2011/01/31.&quot; The body of the e-mail would contain a suitably detailed list of that team member&#8217;s assigned tasks and their status. It might also list the accomplishments of the past week and the problems that are retarding progress on current tasks.</p>
<p>Each team member could read these status updates at some point in the day and perhaps send a follow-up e-mail to other <em>relevant</em> team members to provide additional comments or ask questions. A meeting could be scheduled among interested parties if a topic needed to be discussed in more depth, or a meeting may just happen spontaneously in a phone call or around a whiteboard.</p>
<p>This kind of arrangement provides several advantages. First of all, it eliminates the need to synchronize everyone&#8217;s schedule around an event at a particular time and place every week (or every day, if a team is particularly dysfunctional). Second, it allows a great deal of flexibility in providing status updates to the team. Some people might compose their e-mails on Friday afternoon, some on Sunday evening, and some at 11:47 on Monday morning. Whatever fits into each person&#8217;s style and schedule will work.</p>
<p>Finally, and most importantly to me, if one is really in a flow and madly banging away at the keyboard in a productive state that I like to call &quot;code falling out of the fingers,&quot; there&#8217;s no need to stop that state of productivity to listen to someone else drone about something which has little or no connection to whatever problem that person is thinking about. Each team member can reach a comfortable stopping point and deal with writing or reading the status e-mails on his own time.</p>
<h3>An Aside: Flow Is King</h3>
<p>In software development, particularly, achieving and maintaining flow is crucial for productivity. I worked on a project a while back that had a project status meeting every day at 11:00 AM. This REALLY killed my flow, because I like to spend the first part of each morning taking care of busy work, and by 9:30 or 10:00 I want to be in a really deep flow that may last until 1:00 or 2:00 in the afternoon, or whenever my hunger pangs finally remind that it&#8217;s time for lunch. Once, when my wife and I were engaged, she surprised me at my office while I was in this flow state. It took several seconds for me to even recognize her, and a few more seconds to be able to shift gears into human-to-human conversation. The flow state can be <em>that</em> deep!</p>
<p>If you&#8217;re a manager, you want your team members to be able to achieve and maintain a state of flow. Please, try to avoid doing anything that might interrupt it.</p>
<h2>The Right Kinds of Meetings</h2>
<p>As I wrote earlier, not all meetings are bad. I am a big fan of meetings in which all of the participants are focused on a particular issue. The meeting need not have a published agenda; that can often be a bad sign. All that&#8217;s needed is a clear statement of what the meeting needs to accomplish. For example, &quot;We need to figure out what databases contain the data we need to gather for the migration from product ABC to product XYZ.&quot; That&#8217;s a meeting that has a chance of getting something done.</p>
<p>Another favorite of mine is the ad-hoc meeting, where two or three people might be discussing an idea or a problem, and before long they&#8217;re scribbling on a whiteboard or sitting around a computer trying out ideas.</p>
<p>What these kinds of meetings have in common is that all of the participants are <em>engaged</em>. They&#8217;re working toward a common goal so that they can get work done based on the output of the meeting. Project status meetings, in my experience anyway, never meet this description.</p>
<h2>Why Not Try It?</h2>
<p>I don&#8217;t know why, but I have yet to convince a manager that holds conventional status meetings to switch to e-mail updates instead. I&#8217;d like to hear from some managers about this idea. Why might it not work? Are there downsides that I&#8217;m just not seeing?</p>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Fwww.parkscomputing.com%2F2011%2F01%2Fplease-stop-holding-project-status-meetings%2F&amp;t=Please%20Stop%20Holding%20Project%20Status%20Meetings" id="facebook_share_button_363" style="font-size:11px; line-height:13px; font-family:'lucida grande',tahoma,verdana,arial,sans-serif; text-decoration:none; display: -moz-inline-block; display:inline-block; padding:1px 20px 0 5px; margin: 5px 0; height:15px; border:1px solid #d8dfea; color: #3B5998; background: #fff url(http://b.static.ak.fbcdn.net/images/share/facebook_share_icon.gif) no-repeat top right;">Share</a>
	<script type="text/javascript">
	<!--
	var button = document.getElementById('facebook_share_link_363') || document.getElementById('facebook_share_icon_363') || document.getElementById('facebook_share_both_363') || document.getElementById('facebook_share_button_363');
	if (button) {
		button.onclick = function(e) {
			var url = this.href.replace(/share\.php/, 'sharer.php');
			window.open(url,'sharer','toolbar=0,status=0,width=626,height=436');
			return false;
		}
	
		if (button.id === 'facebook_share_button_363') {
			button.onmouseover = function(){
				this.style.color='#fff';
				this.style.borderColor = '#295582';
				this.style.backgroundColor = '#3b5998';
			}
			button.onmouseout = function(){
				this.style.color = '#3b5998';
				this.style.borderColor = '#d8dfea';
				this.style.backgroundColor = '#fff';
			}
		}
	}
	-->
	</script>
	]]></content:encoded>
			<wfw:commentRss>http://www.parkscomputing.com/2011/01/please-stop-holding-project-status-meetings/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Supporting the Ribbon and Menus</title>
		<link>http://www.parkscomputing.com/2010/12/supporting-the-ribbon-and-menus/</link>
		<comments>http://www.parkscomputing.com/2010/12/supporting-the-ribbon-and-menus/#comments</comments>
		<pubDate>Tue, 28 Dec 2010 04:07:50 +0000</pubDate>
		<dc:creator>Paul Parks</dc:creator>
				<category><![CDATA[COM]]></category>
		<category><![CDATA[UI]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[ripsaw]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[IUIFramework]]></category>
		<category><![CDATA[IUIRibbon]]></category>
		<category><![CDATA[menu]]></category>
		<category><![CDATA[Ribbon]]></category>
		<category><![CDATA[Win32]]></category>

		<guid isPermaLink="false">http://www.parkscomputing.com/?p=262</guid>
		<description><![CDATA[How to support both the Ribbon and a traditional menu in a single executable and allow users to switch between them.]]></description>
			<content:encoded><![CDATA[<p>(I&#8217;ve posted a version of this article on <a href="http://www.codeproject.com/KB/menus/Ribbon_and_Menu_Support.aspx">Code Project</a>.)</p>
<p>I&#8217;ve finally come back to <a href="/labels/ripsaw/">Ripsaw</a>, in a round-about way. I&#8217;ve started working on the client application again as a way to investigate the <a href="http://msdn.microsoft.com/en-us/library/dd316910(VS.85).aspx">Windows Ribbon Framework</a>. Originally, I had planned to create a Ribbon implementation for Windows 7 and later, and a menu-based implementation for earlier Windows versions, or for users that preferred a menu over the Ribbon.</p>
<div id="attachment_265" class="wp-caption alignright" style="width: 279px"><a href="http://www.parkscomputing.com/wp-content/uploads/Scratch-Ribbon-Project.png"><img class="size-full wp-image-265  " title="Scratch Ribbon Project" src="http://www.parkscomputing.com/wp-content/uploads/Scratch-Ribbon-Project.png" alt="" width="269" height="197" /></a><p class="wp-caption-text">Scratch Ribbon Project</p></div>
<p>After I played around with the API a while, I realized it would be fairly simple to support both the Ribbon and the traditional menu in one executable. In this article I&#8217;ll describe a sample app that I put together that shows how to accomplish support for both command-selection methods.</p>
<p>(<a href="/code/Ribbon/ScratchRibbonProject.zip">Download the source code</a>)<br />
(<a href="/code/Ribbon/ScratchRibbonProject.exe">Download the executable</a>)<br />
(<a href="http://www.microsoft.com/downloads/en/details.aspx?familyid=A7B7A05E-6DE6-4D3A-A423-37BF0912DB84">Download the Visual C++ 2010 Redistributable</a>)</p>
<p><span id="more-262"></span></p>
<h2>A Few Words about the Ribbon</h2>
<p>The Ribbon was first introduced with Microsoft Office 2007 as a way to tame the huge menu and toolbar hierarchy that had evolved to present all of the suite&#8217;s features to its users. It then made its way into Windows 7 in the Paint and Wordpad applets, and can now be found in several third-party applications.</p>
<p>As with most things Microsoft does, it seems that users either love or hate the Ribbon. I happen to love it, since I&#8217;ve always disliked toolbars and I find menus clumsy, at best. Since everyone doesn&#8217;t share this opinion, and it&#8217;s relatively cheap to include a menu, and since I want to support XP, Vista, and Windows 7 with a single executable, I&#8217;ll try to satisfy as many people as I can by allowing users to select the command presentation method they prefer.</p>
<h2>The Project</h2>
<p>The application we&#8217;ll be working with began its life as an adaptation of <a href="http://blogs.msdn.com/b/oldnewthing/archive/2005/04/22/410773.aspx">a scratch program</a> that Raymond Chen uses as a framework for example programs in his articles. I adapted it to my own very thin C++ wrapper and added some things that I like to have in fully featured applications, then set about making it work with the Ribbon. You may <a href="/code/Ribbon/ScratchRibbonProject.zip">download the Visual C++ 2010 Express project</a> and follow along below.</p>
<h2>License</h2>
<p>There is no license. This code is public-domain code, and may be used in any manner without restrictions. I offer no guarantees or warranties at all.</p>
<h2>Prerequisites</h2>
<p>First of all, you&#8217;ll need <a href="http://www.microsoft.com/express/Windows/">Visual C++ 2010 Express</a> (which is free) or one of its more sophisticated versions (which are not free) in order to compile the project. If you prefer to use a different compiler you&#8217;ll have to adapt the code and project as necessary. You&#8217;ll also need the <a href="http://www.microsoft.com/downloads/en/details.aspx?FamilyID=6b6c21d2-2006-4afa-9702-529fa782d63b&amp;displaylang=en">Windows 7 SDK version 7.1</a>. Finally, in order to see the Ribbon interface you&#8217;ll need to run the application on Windows 7 or on Windows Vista SP2 or later with the <a href="http://msdn.microsoft.com/en-us/library/ee663867(VS.85).aspx">Platform Update</a> installed. When the application runs on Vista SP1 or earlier it will only display the traditional menu.</p>
<h2>Configuring Visual Studio Projects for Ribbon Support</h2>
<p>Supporting the Ribbon in Visual Studio takes a little setup. First of all, once the SDK is installed, you need to change a setting in any new project in Visual Studio 2010 to take advantage of the SDK:</p>
<div id="attachment_284" class="wp-caption aligncenter" style="width: 628px"><a href="http://www.parkscomputing.com/wp-content/uploads/ScratchRibbonProject-Property-Pages.png"><img class="size-full wp-image-284 " title="Visual Studio Project Property Pages" src="http://www.parkscomputing.com/wp-content/uploads/ScratchRibbonProject-Property-Pages.png" alt="" width="618" height="475" /></a><p class="wp-caption-text">Choosing the Windows 7.1 SDK Platform Toolset in Visual Studio 2010</p></div>
<p>Next, you&#8217;ll need to add a new XML file to your project to contain your Ribbon markup. The Ribbon is defined by an <a href="http://msdn.microsoft.com/en-us/library/dd316913(VS.85).aspx">XML markup schema</a> described in detail on MSDN. This XML is compiled by a tool provided in the Windows 7.1 SDK, <a href="http://msdn.microsoft.com/en-us/library/dd316930(VS.85).aspx">UICC</a>. After you&#8217;ve added the XML file to your project, open the Property Pages for the file and change the &#8220;Item Type&#8221; property in the General properties to &#8220;Custom Build Tool.&#8221; Apply the setting, then in the Custom Build Tool&gt;General settings change the command line to something like the following:</p>
<div class="example">
<pre>"$(WindowsSdkToolsDir)\bin\uicc" "%(Identity)" "%(Filename).bin" /header:"%(Filename).h"
   /res:"%(Filename).rc2"</pre>
</div>
<p>Next, define the outputs from the UICC compiler in the &#8220;Outputs&#8221; property:</p>
<div class="example">
<pre>%(Filename).bin;%(Filename).h;%(Filename).rc2;%(Outputs)</pre>
</div>
<p>Make sure that you apply these changes for both the Debug and Release builds.</p>
<div id="attachment_296" class="wp-caption aligncenter" style="width: 628px"><a href="http://www.parkscomputing.com/wp-content/uploads/Ribbon.xml-Property-Pages.png"><img class="size-full wp-image-296 " title="Ribbon.xml Property Pages" src="http://www.parkscomputing.com/wp-content/uploads/Ribbon.xml-Property-Pages.png" alt="" width="618" height="475" /></a><p class="wp-caption-text">Changing the compiler settings for Ribbon markup in Visual Studio</p></div>
<p>Finally, you&#8217;ll need to add the output of the UICC compiler to the project&#8217;s main resource script. These need to go in a particular location in the resource file. In Visual Studio 2010 Professional, switch to the resource view (Ctrl+Shift+E), right-click on the resource script, and select &#8220;Resource Includes&#8221; from the context menu. Add the header file produced by UICC (for example, Ribbon.h) at the top in the &#8220;Read-only symbol directives&#8221; window, and add the resource script produced by UICC (for example, Ribbon.rc2) to the Compile-time directives window, as shown below:</p>
<div id="attachment_299" class="wp-caption aligncenter" style="width: 396px"><a href="http://www.parkscomputing.com/wp-content/uploads/Resource-Includes.png"><img class="size-full wp-image-299 " title="Resource Includes" src="http://www.parkscomputing.com/wp-content/uploads/Resource-Includes.png" alt="" width="386" height="447" /></a><p class="wp-caption-text">Visual Studio 2010 Professional resource includes window</p></div>
<p>In Visual C++ 2010 Express, you&#8217;ll need to edit the resource script code directly since there is no resource editor. Right-click on the resource script in the Solution Explorer and select &#8220;View Code&#8221; from the context menu. Find the TEXTINCLUDE sections and edit them as highlighted below:</p>
<div class="example">
<pre>#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE
BEGIN
    <span style="background-color: yellow;">"#include ""Ribbon.h""\r\n"</span>
    "#ifndef APSTUDIO_INVOKED\r\n"
    "#include ""targetver.h""\r\n"
    "#endif\r\n"
    "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
    "#include ""windows.h""\r\n"
    "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
    "\0"
END

3 TEXTINCLUDE
BEGIN
    <span style="background-color: yellow;">"#include ""Ribbon.rc2"</span>"\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED</pre>
</div>
<p>With these changes in place, the resource script will include the control IDs created by the UICC compiler so that you may reuse them in your menu resource.</p>
<h2>Running the Application</h2>
<p>As I mentioned earlier, this project began as a scratch application. The idea was to have a starting point for new applications that would provide a lot of the boring, boilerplate stuff so that I could concentrate on the interesting bits of new applications. Figuring out how to integrate the Ribbon and make it work was not entirely straightforward, which I found disappointing. The answers to my questions were usually somewhere out on the Internet, but never in one place. That&#8217;s why I decided to write this article to help those of you who might be having the same struggles I had.</p>
<p>After you&#8217;ve <a href="/code/Ribbon/ScratchRibbonProject.zip">downloaded the Visual C++ 2010 Express project</a>, open it in Express, build it, and run it. Or, if you prefer, just <a href="/code/Ribbon/ScratchRibbonProject.exe">download the executable</a> and run it. On Windows 7, and on Windows Vista SP2 or later with the <a href="http://msdn.microsoft.com/en-us/library/ee663867(VS.85).aspx">Platform Update</a> installed, you&#8217;ll see a window with a Ribbon control across the top. In the View tab of the Ribbon is a button that will allow the user to switch to a traditional menu bar. The View menu has a corresponding option to allow the user to display the Ribbon again. Most of the other commands will simply output a line of text in the client area describing the option that was selected.</p>
<div id="attachment_307" class="wp-caption aligncenter" style="width: 424px"><a href="http://www.parkscomputing.com/wp-content/uploads/Scratch-Ribbon-Project-menu-7.png"><img class="size-full wp-image-307 " title="Scratch Ribbon Project Menu on Windows 7" src="http://www.parkscomputing.com/wp-content/uploads/Scratch-Ribbon-Project-menu-7.png" alt="" width="414" height="263" /></a><p class="wp-caption-text">Scratch Ribbon Project Menu on Windows 7</p></div>
<p>On Windows XP, and on Windows Vista without the Platform Update, you&#8217;ll instead see a window with a traditional menu. The &#8220;Show Ribbon&#8221; menu option is disabled because the application detects that the Ribbon API is not supported on those platforms.</p>
<div id="attachment_308" class="wp-caption aligncenter" style="width: 299px"><a href="http://www.parkscomputing.com/wp-content/uploads/Scratch-Ribbon-Project-XP.png"><img class="size-full wp-image-308 " title="Scratch Ribbon Project XP" src="http://www.parkscomputing.com/wp-content/uploads/Scratch-Ribbon-Project-XP.png" alt="" width="289" height="179" /></a><p class="wp-caption-text">Scratch Ribbon Project on Windows XP</p></div>
<h2>Examining the Code</h2>
<p>I won&#8217;t go into too much detail about the usual Windows API elements of the code, or about the minimalist C++ framework it uses. I also won&#8217;t get into the details of the Ribbon markup, which is covered in detail in the <a href="http://msdn.microsoft.com/en-us/library/dd316913(VS.85).aspx">MSDN Ribbon documentation</a>. Instead, I&#8217;ll cover the bits of the code related to supporting the Ribbon and the menu.</p>
<p>The application&#8217;s initialization is performed in the <code>App::Initialize</code> method. The first order of business is initializing COM:</p>
<div class="example">
<pre>if (FAILED(CoInitialize(NULL)))
{
  ReportError(IDS_COINITIALIZE_FAILED);
  retVal = false;
  /* Fear of goto is highly irrational. Get over it. */
  goto exitinit;
}</pre>
</div>
<p>The comment about <code>goto</code> is a subject of another article entirely, so we&#8217;ll ignore that for now. It&#8217;s necessary to initialize COM because the Ribbon API is a set of COM objects that implement the various Ribbon interfaces (<code>IUIFramework</code>, <code>IUIRibbon</code>, etc.). A little further down in the initialization method the code reads the saved user settings, if they exist, then checks to see if the application should be using the Ribbon. The setting will be <code>true</code> by default, no matter what OS the application is running on, so the application will call the <code>CreateRibbon</code> method.</p>
<div class="example">
<pre>LoadAppSettings();

if (settings.isRibbon)
{
  CreateRibbon();
}</pre>
</div>
<p>The <code>CreateRibbon</code> method uses the Ribbon API&#8217;s COM objects to initialize and show the Ribbon.</p>
<div class="example">
<pre>bool App::CreateRibbon()
{
  /* Attempt to create the ribbon framework interface. */
  HRESULT hr = CoCreateInstance(
    CLSID_UIRibbonFramework,
    NULL,
    CLSCTX_INPROC_SERVER,
    IID_PPV_ARGS(&amp;pFramework));

  if (SUCCEEDED(hr))
  {
    /* Framework creation succeeded, so initialize the framework and
    create the ribbon. */

    /* The killRibbon flag controls the activation of message handling to fix a repaint
    problem that occurs when the ribbon is removed. */
    killRibbon = false;

    hr = pFramework-&gt;Initialize(GetHWND(), this);

    if (SUCCEEDED(hr))
    {
      hr = pFramework-&gt;LoadUI(GetModuleHandle(NULL), L"APPLICATION_RIBBON");

      if (SUCCEEDED(hr))
      {
        hr = pFramework-&gt;GetView(0, IID_PPV_ARGS(&amp;pRibbon));

        if (SUCCEEDED(hr))
        {
          settings.isRibbon = true;
        }
      }
    }
  }

  if (FAILED(hr))
  {
    /* If ribbon creation or initialization failed, make sure that any interfaces are released
    and set to null so that the UI will fall back and use the menu instead. */
    CloseRibbon();
  }

  return SUCCEEDED(hr);
}</pre>
</div>
<p>If the COM objects are created and their methods are called successfully, the API will remove the main window&#8217;s menu and display a Ribbon in its place. This method will fail on platforms that do not implement the Ribbon API, and since the application displays a menu on its main window by default, that menu will remain in place.</p>
<p>Next, the initial states of the various command options are set.</p>
<div class="example">
<pre>SetDirty(false);
SetRedo(false);
EnableMenuItem(GetMenu(GetHWND()), ID_SHOW_RIBBON,
  IsRibbonSupported() ? MF_ENABLED : MF_GRAYED);</pre>
</div>
<p>The <code>ID_SHOW_RIBBON</code> constant represents the menu selection that enables the Ribbon when the UI is displaying the menu bar. This function uses the return value of the <code>IsRibbonSupported</code> method to enable or disable the menu item. That method is implemented as follows:</p>
<div class="example">
<pre>bool App::IsRibbonSupported()
{
  bool isRibbonSupported = false;
  IUIFramework* pTmp = 0;

  /* Attempt to create the ribbon framework interface. */
  HRESULT hr = CoCreateInstance(
    CLSID_UIRibbonFramework,
    NULL,
    CLSCTX_INPROC_SERVER,
    IID_PPV_ARGS(&amp;pTmp));

  if (SUCCEEDED(hr))
  {
    isRibbonSupported = true;
    pTmp-&gt;Release();
  }

  return isRibbonSupported;
}</pre>
</div>
<p>All the method does is attempt to create a COM object provided by the Ribbon API. If the attempt fails, the method returns <code>false</code>. This will always fail on XP and on Vista without the Platform Update, unless of course some enterprising developer implements all the necessary COM interfaces and objects on those platforms.</p>
<h4>Implementing the COM Interfaces</h4>
<p>The Ribbon API requires an application using the Ribbon to implement two COM interfaces: <code>IUIApplication</code> and <code>IUICommandHandler</code>. The <a href="http://msdn.microsoft.com/en-us/library/dd371528(VS.85).aspx"><code>IUIApplication</code></a> interface defines callbacks into the application that are called by the Ribbon API. The <a href="http://msdn.microsoft.com/en-us/library/dd371491(VS.85).aspx"><code>IUICommandHandler</code></a> interface is called for each command that is exposed on the Ribbon.</p>
<p>The Scratch Ribbon Project implements both interfaces in the <code>App</code> object. This need not be the case, but it makes the shared implementation with the menu bar a little easier to accomplish. An alternative implementation would be to create a unique <code>IUICommandHandler</code> implementation for each command.</p>
<p>First of all, in the <code>App</code> class declaration in ScratchRibbonProject.h, the relevant methods are declared. The definitions are in ScratchRibbonProject.cpp, and we will examine each of them in turn.</p>
<p>First, we need to implement <a href="http://msdn.microsoft.com/en-us/library/ms680509(VS.85).aspx"><code>IUnknown</code></a> since all COM interfaces derive from this interface. The <code>AddRef</code> and <code>Release</code> methods are fairly straightforward.</p>
<div class="example">
<pre>ULONG STDMETHODCALLTYPE App::AddRef()
{
  return InterlockedIncrement(&amp;refCount);
}

ULONG STDMETHODCALLTYPE App::Release()
{
  return InterlockedDecrement(&amp;refCount);
}</pre>
</div>
<p>Next is <code>QueryInterface</code>, which should return a pointer to the requested interface if the object implements it.</p>
<div class="example">
<pre>HRESULT STDMETHODCALLTYPE App::QueryInterface(
  REFIID riid,
  void **ppvObject)
{
  if (!ppvObject)
  {
    return E_INVALIDARG;
  }

  if (riid == IID_IUnknown)
  {
    *ppvObject = static_cast&lt;IUnknown*>(static_cast<IUIApplication*>(this));
  }
  else if (riid == __uuidof(IUICommandHandler))
  {
    *ppvObject = static_cast&lt;IUICommandHandler*>(this);
  }
  else if (riid == __uuidof(IUIApplication))
  {
    *ppvObject = static_cast&lt;IUIApplication*>(this);
  }
  else
  {
    *ppvObject = 0;
    return E_NOINTERFACE;
  }

  AddRef();
  return S_OK;
}</pre>
</div>
<p>With that chore out of the way, we can move on to the interesting interfaces. The <code>IUIApplication</code> interface specifies three methods: <code>OnViewChanged</code>, <code>OnCreateUICommand</code>, and <code>OnDestroyUICommand</code>. The <a href="http://msdn.microsoft.com/en-us/library/dd371537(VS.85).aspx"><code>OnViewChanged</code></a> method is called by the Ribbon framework when the state of a Ribbon <a href="http://msdn.microsoft.com/en-us/library/dd371600(VS.85).aspx">View</a> changes. The <code>verb</code> parameter specifies the action performed by the view.</p>
<div class="example">
<pre>HRESULT STDMETHODCALLTYPE App::OnViewChanged(
  UINT32 viewId,
  UI_VIEWTYPE typeID,
  IUnknown* pView,
  UI_VIEWVERB verb,
  INT32 uReasonCode)
{
  HRESULT hr = E_NOTIMPL;

  if (UI_VIEWVERB_CREATE == verb)
  {
    IUIRibbon* pRibbon = NULL;
    hr = pView-&gt;QueryInterface(IID_PPV_ARGS(&amp;pRibbon));

    if (SUCCEEDED(hr))
    {
      LoadRibbonSettings(pRibbon);
      pRibbon-&gt;Release();
    }
  }
  else if (UI_VIEWVERB_SIZE == verb)
  {
    RECT rect = {};
    GetClientRect(GetHWND(), &amp;rect);
    OnSize(GetHWND(), 0, rect.right - rect.left, rect.bottom - rect.top);
  }
  else if (UI_VIEWVERB_DESTROY == verb)
  {
    IUIRibbon* pRibbon = NULL;
    hr = pView-&gt;QueryInterface(IID_PPV_ARGS(&amp;pRibbon));

    if (SUCCEEDED(hr))
    {
      SaveRibbonSettings(pRibbon);
      pRibbon-&gt;Release();
    }
  }

  return hr;
}</pre>
</div>
<p>The method is called with the <a href="http://msdn.microsoft.com/en-us/library/dd371588(VS.85).aspx"><code>UI_VIEWVERB_CREATE</code></a> and <code>UI_VIEWVERB_DESTROY</code> verb constants at Ribbon initialization and tear-down, respectively. This implementation uses those calls to load the ribbon settings when the Ribbon view is created and save them when it is destroyed. We&#8217;ll look at the <code>LoadRibbonSettings</code> and <code>SaveRibbonSettings</code> methods a little later on.</p>
<p>The <code>UI_VIEWVERB_SIZE</code> indicates that the Ribbon&#8217;s size has changed (for example, the ribbon has been minimized). The application may need to respond to this notification to adjust any other windows that need to be moved or resized based on the new Ribbon size. In this example, the method adjusts the client area by calling the <code>OnSize</code> message handler.</p>
<p>The <a href="http://msdn.microsoft.com/en-us/library/dd371531(VS.85).aspx"><code>OnCreateUICommand</code></a> method is called by the Ribbon framework for each command specified in the Ribbon markup. The application must return a pointer to an <code>IUICommandHandler</code> interface that will handle each particular command. In this application, all of the commands are serviced by the <code>App</code> object instance, so we just return  the requested pointer and increment the reference count.</p>
<div class="example">
<pre>HRESULT STDMETHODCALLTYPE App::OnCreateUICommand(
  UINT32 commandId,
  UI_COMMANDTYPE typeID,
  IUICommandHandler** commandHandler)
{
  if (commandHandler)
  {
    *commandHandler = static_cast&lt;IUICommandHandler*>(this);
    AddRef();
    return S_OK;
  }

  return E_INVALIDARG;
}</pre>
</div>
<p>The <a href="http://msdn.microsoft.com/en-us/library/dd371534(VS.85).aspx"><code>OnDestroyUICommand</code></a> method is called each time a command is destroyed. This would give the application an opportunity to clean up its command handlers, if necessary, but in our case there&#8217;s nothing to do.</p>
<div class="example">
<pre>HRESULT STDMETHODCALLTYPE App::OnDestroyUICommand(
  UINT32 commandId,
  UI_COMMANDTYPE typeID,
  IUICommandHandler* commandHandler)
{
  return E_NOTIMPL;
}</pre>
</div>
<p>Finally, we need to implement the two methods of the <code>IUICommandHandler</code> interface, which will be shared by all of the Ribbon commands in our application. The <a href="http://msdn.microsoft.com/en-us/library/dd371494(VS.85).aspx"><code>UpdateProperty</code></a> method is called by the framework to request an update to a command&#8217;s state. As an example of how to modify the enabled/disabled state, this application changes the state of the Save and Redo commands based on flags maintained by the <code>App</code> object.</p>
<div class="example">
<pre>HRESULT STDMETHODCALLTYPE App::UpdateProperty(
  UINT32 commandId,
  REFPROPERTYKEY key,
  const PROPVARIANT *currentValue,
  PROPVARIANT *newValue)
{
  if (newValue)
  {
    if (key.fmtid == UI_PKEY_Enabled.fmtid)
    {
      if (commandId == ID_CMD_SAVE)
      {
        (*newValue).boolVal = IsDirty() ? VARIANT_TRUE : VARIANT_FALSE;
      }
      else if (commandId == ID_CMD_REDO)
      {
        (*newValue).boolVal = CanRedo() ? VARIANT_TRUE : VARIANT_FALSE;
      }
    }
  }

  return S_OK;
}</pre>
</div>
<p>At last, we come to the method that connects activation of the Ribbon commands to actual application code. The <a href="http://msdn.microsoft.com/en-us/library/dd371489(VS.85).aspx"><code>Execute</code></a> is called with an <a href="http://msdn.microsoft.com/en-us/library/dd371563(VS.85).aspx">execution verb constant</a> of <code>UI_EXECUTIONVERB_EXECUTE</code> when the application needs to respond to a command event. This application posts a <a href="http://msdn.microsoft.com/en-us/library/ms647591(VS.85).aspx"><code>WM_COMMAND</code> message</a> equivalent to what would have been sent if the command had been selected from a menu. This, in turn, triggers the <code>WM_COMMAND</code> handler in the <code>WndProc</code> method.</p>
<div class="example">
<pre>HRESULT STDMETHODCALLTYPE App::Execute(
  UINT32 commandId,
  UI_EXECUTIONVERB verb,
  const PROPERTYKEY *key,
  const PROPVARIANT *currentValue,
  IUISimplePropertySet *commandExecutionProperties)
{
  if (verb == UI_EXECUTIONVERB_EXECUTE)
  {
    PostMessage(GetHWND(), WM_COMMAND, commandId, 0);
  }

  return S_OK;
}</pre>
</div>
<h4>The Ribbon XML</h4>
<p>The <a href="/code/Ribbon/Ribbon.xml">Ribbon.xml</a> file contains the markup that defines the application&#8217;s Ribbon. This file is compiled by UICC and included in the resource script. Each command has an <code>Id</code> attribute that specifies a numeric value sent to the application when the command is activated. This value is associated with the <code>Symbol</code> attribute in the Ribbon.h file generated by UICC. Following are the definitions for the &#8220;New&#8221; and &#8220;Open&#8221; commands:</p>
<div class="example">
<pre>&lt;Command Name="cmdNew" Id="0x0100" Symbol="ID_CMD_NEW" Keytip="N"&gt;
   &lt;Command.LabelTitle&gt;New&lt;/Command.LabelTitle&gt;
   &lt;Command.TooltipTitle&gt;New (Ctrl+N)&lt;/Command.TooltipTitle&gt;
   &lt;Command.TooltipDescription&gt;Create a new document&lt;/Command.TooltipDescription&gt;
   &lt;Command.LargeImages&gt;
      &lt;Image Source="images/New-icon-32.bmp" Id="101" Symbol="ID_NEW_LARGEIMAGE1" MinDPI="96" /&gt;
   &lt;/Command.LargeImages&gt;
   &lt;Command.SmallImages&gt;
      &lt;Image Source="images/New-icon-16.bmp" Id="102" Symbol="ID_NEW_SMALLIMAGE1" MinDPI="96" /&gt;
   &lt;/Command.SmallImages&gt;
&lt;/Command&gt;
&lt;Command Name="cmdOpen" Id="0x0103" Symbol="ID_CMD_OPEN" Keytip="O"&gt;
   &lt;Command.LabelTitle&gt;Open&lt;/Command.LabelTitle&gt;
   &lt;Command.TooltipTitle&gt;Open (Ctrl+O)&lt;/Command.TooltipTitle&gt;
   &lt;Command.TooltipDescription&gt;Open a document&lt;/Command.TooltipDescription&gt;
   &lt;Command.LargeImages&gt;
      &lt;Image Source="images/Open-icon-32.bmp" Id="103" Symbol="ID_OPEN_LARGEIMAGE1" MinDPI="96" /&gt;
   &lt;/Command.LargeImages&gt;
   &lt;Command.SmallImages&gt;
      &lt;Image Source="images/Open-icon-16.bmp" Id="104" Symbol="ID_OPEN_SMALLIMAGE1" MinDPI="96" /&gt;
   &lt;/Command.SmallImages&gt;
&lt;/Command&gt;</pre>
</div>
<p>When this XML is compiled, the following constants will be defined in Ribbon.h:</p>
<div class="example">
<pre>
#define ID_CMD_NEW 0x0100
#define ID_CMD_NEW_LabelTitle_RESID 60010
#define ID_CMD_NEW_Keytip_RESID 60011
#define ID_CMD_NEW_TooltipTitle_RESID 60012
#define ID_CMD_NEW_TooltipDescription_RESID 60013
#define ID_NEW_SMALLIMAGE1 102
#define ID_NEW_LARGEIMAGE1 101
#define ID_CMD_OPEN 0x0103
#define ID_CMD_OPEN_LabelTitle_RESID 60014
#define ID_CMD_OPEN_Keytip_RESID 60015
#define ID_CMD_OPEN_TooltipTitle_RESID 60016
#define ID_CMD_OPEN_TooltipDescription_RESID 60017
#define ID_OPEN_SMALLIMAGE1 104
#define ID_OPEN_LARGEIMAGE1 103
</pre>
</div>
<p>Most of these constants are used by various string and image resources, but the constants that will be used in the menu are <code>ID_CMD_NEW</code> and <code>ID_CMD_OPEN</code>.</p>
<h4>The Menu Resource</h4>
<p>Earlier, we modified ScratchRibbonProject.rc to include Ribbon.h so that the constants defined in that header by the Ribbon compiler could also used by the menu resource.</p>
<div class="example">
<pre>
IDC_APP_MENU MENU
BEGIN
    POPUP "&amp;File"
    BEGIN
        MENUITEM "&amp;New\tCtrl+N",                ID_CMD_NEW
        MENUITEM "&amp;Open\tCtrl+O",               ID_CMD_OPEN
        MENUITEM "&amp;Save\tCtrl+S",               ID_CMD_SAVE
        MENUITEM "Save &amp;As...\tCtrl+Shift+S",   ID_CMD_SAVEAS
        MENUITEM SEPARATOR
        MENUITEM "E&amp;xit\tAlt+F4",               ID_CMD_EXIT
    END
    POPUP "&amp;Edit"
    BEGIN
        MENUITEM "&amp;Undo\tCtrl+Z",               ID_CMD_UNDO
        MENUITEM "&amp;Redo\tCtrl+Y",               ID_CMD_REDO
        MENUITEM SEPARATOR
        MENUITEM "Cu&amp;t\tCtrl+X",                ID_CMD_CUT
        MENUITEM "&amp;Copy\tCtrl+C",               ID_CMD_COPY
        MENUITEM "&amp;Paste\tCtrl+V",              ID_CMD_PASTE
        MENUITEM "&amp;Delete\tDel",                ID_CMD_DELETE
    END
    POPUP "&amp;View"
    BEGIN
        MENUITEM "Zoom &amp;In\tCtrl+Plus",         ID_CMD_ZOOMIN
        MENUITEM "Zoom &amp;Out\tCtrl+Minus",       ID_CMD_ZOOMOUT
        MENUITEM "&amp;Normal Zoom\t Ctrl+0",       ID_CMD_NORMALZOOM
        MENUITEM SEPARATOR
        MENUITEM "&amp;Show Ribbon",                ID_SHOW_RIBBON
    END
    POPUP "&amp;Help"
    BEGIN
        MENUITEM "&amp;View Help\tF1",              ID_CMD_VIEWHELP
        MENUITEM "&amp;About\tCtrl+?",              ID_CMD_ABOUT
    END
END
</pre>
</div>
<p>The menu items defined in the menu resource now share the same numeric identifiers defined in Ribbon.xml. Activating any of these items, either on the Ribbon or the menu, will trigger the same handlers in the <code>OnCommand</code> message handler.</p>
<div class="example">
<pre>

void App::OnCommand(
  HWND hwnd,
  int id,
  HWND hwndCtl,
  UINT codeNotify)
{
  switch (id)
  {
  case ID_CMD_NEW:
    AppendText(L"New document\r\n");
    SetDirty(true);
    break;

  case ID_CMD_OPEN:
    AppendText(L"Open document\r\n");
    break;

    /* ... */
  }
}
</pre>
</div>
<h4>Swapping the Ribbon and the Menu</h4>
<p>When the application is run for the first time on a platform that supports the Ribbon, the application displays the Ribbon by default. Activating the Show Ribbon command on the View tab will cause the Ribbon to be removed from the window, and the menu bar will be shown in its place. The identifier for that command is <code>ID_HIDE_RIBBON</code>, and when it is activated the following code is executed in the <code>OnCommand</code> method:</p>
<div class="example">
<pre>
case ID_HIDE_RIBBON:
  /* I use PostMessage here because when I tried to remove the
  ribbon immediately after pressing the corresponding ribbon button,
  the UIRibbon code would die trying to process the WM_LBUTTONUP
  message on a ribbon window that no longer existed. This gets
  around that. */
  PostMessage(hwnd, AM_SHOW_MENU, 0, 0);
  break;
</pre>
</div>
<p>This puts the <code>AM_SHOW_MENU</code> message (an application-defined message) into the message queue, and that is handled by the following code in <code>App::WndProc</code>:</p>
<div class="example">
<pre>
case AM_SHOW_MENU:
  {
    /* Set this flag to trigger the repaint hack for DWM environments
    when the ribbon is removed. */
    killRibbon = true;
    CloseRibbon();
    PostMessage(GetHWND(), AM_RESTORE_MENU, 0, 0);
  }
  return 0;
</pre>
</div>
<p>This code sets a flag (<code>killRibbon</code>) to activate special message handling in order to work around some painting problems (which we&#8217;ll examine later). It then calls the <code>CloseRibbon</code> method.</p>
<div class="example">
<pre>
void App::CloseRibbon()
{
  /* If we have a ribbon, release it so that it will uninitialize cleanly. */
  if (pRibbon)
  {
    pRibbon->Release();
    pRibbon = 0;
  }

  /* Likewise, destroy and release the ribbon framework. */
  if (pFramework)
  {
    pFramework->Destroy();
    pFramework->Release();
    pFramework = 0;
  }

  settings.isRibbon = false;
}
</pre>
</div>
<p>This method releases the <code>pRibbon</code> object, if set, calls the <code>Destroy</code> method of the <code>pFramework</code> object, releases the <code>pFramework</code> object, and sets the pointers to null. Finally, it updates the <code>settings</code> object to note that the ribbon is not being used.</p>
<p>The last thing that the <code>AM_SHOW_MENU</code> handler does is post an <code>AM_RESTORE_MENU</code> message, which is handled as follows:</p>
<div class="example">
<pre>
case AM_RESTORE_MENU:
  {
    SetMenu(GetHWND(), hMenu);

    /* This is hackish, but it sets the menu items to the proper state. In a real app
    I'd probably use some sort of signal/slots implementation to wire up this stuff. */
    SetDirty(IsDirty());
    SetRedo(CanRedo());
    EnableMenuItem(GetMenu(GetHWND()), ID_SHOW_RIBBON,
      IsRibbonSupported() ? MF_ENABLED : MF_GRAYED);
  }
  return 0;
</pre>
</div>
<p>This restores the menu bar to the window with <code>SetMenu</code> and enables or disables the menu items as necessary.</p>
<p>Going the other direction, if the user selects the Show Ribbon option from the View menu, the <code>ID_SHOW_RIBBON</code> case in <code>OnCommand</code> is triggered.</p>
<div class="example">
<pre>
case ID_SHOW_RIBBON:
  PostMessage(hwnd, AM_SHOW_RIBBON, 0, 0);
  break;
</pre>
</div>
<p>This, in turn, activates a case in <code>WndProc</code>.</p>
<div class="example">
<pre>
case AM_SHOW_RIBBON:
  {
    if (IsRibbonSupported())
    {
      /* Don't need the repaint hack anymore. */
      killRibbon = false;

      ShowWindow(GetHWND(), SW_HIDE);
      CreateRibbon();
      ShowWindow(GetHWND(), SW_SHOW);

      RECT rect = {};
      GetClientRect(GetHWND(), &amp;rect);

      /* You aren't supposed to post a WM_SIZE message, but it's the only hack
      I could make work consistently to get the window to repaint correctly in
      all situations. */
      PostMessage(GetHWND(), WM_SIZE, 0,
        MAKELPARAM(rect.right - rect.left, rect.bottom - rect.top));
    }
  }
  return 0;
</pre>
</div>
<p>I ran into a number of painting problems in this code, and I eventually decided to just hide the window, restore the Ribbon, then show the window again. This is a little bit jarring, but until I can find a workaround for the painting problems it&#8217;s effective enough. (If you find a better workaround, please let me know.) I felt particularly dirty about posting a <code>WM_SIZE</code> message, but that was the only reliable way I could find to adjust the client area properly. When I saw that the same technique was used in WTL 8.0, I didn&#8217;t feel so bad anymore.</p>
<h4>Saving and Restoring Ribbon Settings</h4>
<p>Above, we saw that the <code>OnViewChanged</code> method calls <code>LoadRibbonSettings</code> when the Ribbon is initialized and <code>SaveRibbonSettings</code> when the ribbon is destroyed. The <code>SaveRibbonSettings</code> method creates an <code>IStream</code> object on a file in the user&#8217;s application data directory and passes that stream to the <a href="http://msdn.microsoft.com/en-us/library/dd371362(VS.85).aspx"><code>pRibbon->SaveSettingsToStream</code></a> method. The Ribbon framework will write its settings into this stream, after which the <code>SaveRibbonSettings</code> method releases the <code>IStream</code> and <code>IStorage</code> objects.</p>
<div class="example">
<pre>
bool App::SaveRibbonSettings(
  IUIRibbon* pRibbon)
{
  /* Build a path to an app-specific directory in the user's application
  data storage directory. */
  HRESULT hr = E_FAIL;
  WCHAR pPath[MAX_PATH] = {};

  if (BuildSettingsPath(pPath, L"ScratchRibbonProjectSettings.bin"))
  {
    IStorage* pStorage = 0;
    hr = StgCreateStorageEx(pPath, STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE,
      STGFMT_STORAGE, 0, NULL, NULL, __uuidof(IStorage), (void**)&amp;pStorage);

    if (SUCCEEDED(hr))
    {
      IStream* pStream = 0;

      hr = pStorage->CreateStream(L"Ribbon",
        STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE,
        0, 0, &amp;pStream);

      if (SUCCEEDED(hr))
      {
        hr = pRibbon->SaveSettingsToStream(pStream);
        pStream->Release();
      }

      pStorage->Release();
    }
  }

  return SUCCEEDED(hr);
}
</pre>
</div>
<p>At Ribbon initialization the <code>LoadRibbonSettings</code> opens the Ribbon stream in the file created by <code>SaveRibbonSettings</code> and passes it to <a href="http://msdn.microsoft.com/en-us/library/dd371361(VS.85).aspx"><code>pRibbon->LoadSettingsFromStream</code></a>. This will restore the previously saved state of the Ribbon.</p>
<div class="example">
<pre>
bool App::LoadRibbonSettings(
  IUIRibbon* pRibbon)
{
  HRESULT hr = E_FAIL;
  WCHAR pPath[MAX_PATH] = {};

  if (BuildSettingsPath(pPath, L"ScratchRibbonProjectSettings.bin"))
  {
    IStorage* pStorage = 0;

    hr = StgOpenStorageEx(
      pPath,
      STGM_READ|STGM_SHARE_DENY_WRITE,
      STGFMT_STORAGE,
      0, NULL, NULL,
      __uuidof(IStorage),
      (void**)&amp;pStorage);

    if (SUCCEEDED(hr))
    {
      IStream* pStream = 0;
      hr = pStorage->OpenStream( L"Ribbon", NULL,
        STGM_READ|STGM_SHARE_EXCLUSIVE,0, &amp;pStream);

      if (SUCCEEDED(hr))
      {
        LARGE_INTEGER liStart = {0, 0};
        ULARGE_INTEGER ulActual;
        pStream->Seek(liStart, STREAM_SEEK_SET, &amp;ulActual);
        hr = pRibbon->LoadSettingsFromStream(pStream);
        pStream->Release();
      }

      pStorage->Release();
    }
  }

  return SUCCEEDED(hr);
}
</pre>
</div>
<h4>Enabling and Disabling Ribbon Commands</h4>
<p>To demonstrate how to disable Ribbon commands, the Save and Redo commands will change state based on the other commands chosen by the user. The Save command is disabled until the New or Undo commands are activated, and the Redo command is disabled until the Undo command is activated.</p>
<p>The state of the Save command is controlled by a dirty flag set in the <code>App</code> object. If the current &#8220;document&#8221; is flagged as dirty, and therefore a candidate for a save operation, the Save command should be enabled. To set or clear the dirty flag the application calls the <code>SetDirty</code> method of the <code>App</code> object.</p>
<div class="example">
<pre>
void App::SetDirty(bool isDirtyInit)
{
  isDirty = isDirtyInit;

  if (pFramework)
  {
    pFramework->InvalidateUICommand(ID_CMD_SAVE, UI_INVALIDATIONS_STATE, NULL);
  }

  HMENU hMenu = GetMenu(GetHWND());

  if (hMenu)
  {
    EnableMenuItem(hMenu, ID_CMD_SAVE, isDirty ? MF_ENABLED : MF_GRAYED);
  }
}
</pre>
</div>
<p>This method modifies both the menu and Ribbon commands. It first checks to see if the <code>pFramework</code> pointer is non-null. If it is, it points to an instance of the <code>IUIFramework</code> interface implemented by the Ribbon API. It uses the pointer to call the <a href="http://msdn.microsoft.com/en-us/library/dd371375(VS.85).aspx"><code>InvalidateUICommand</code></a> method, specifying that the <code>ID_CMD_SAVE</code> command should be invalidated. This instructs the framework to call the application&#8217;s implementation of <code>IUICommandHandler::UpdateProperty</code>, which will set the state of the command based on the dirty flag.</p>
<h4>Various Clean-up Tasks</h4>
<p>All that we have left to cover are the various clean-up actions to restore the client area and handle some painting problems. First of all, there is a read-only edit control in the main window&#8217;s client area to display the descriptions of the selected Ribbon or menu commands. This control needs to be resized to fit around the Ribbon control when the main window is resized or the Ribbon size changes. This is accomplished by handling the <a href="http://msdn.microsoft.com/en-us/library/ms632646(VS.85).aspx"><code>WM_SIZE</code></a> message.</p>
<div class="example">
<pre>
void App::OnSize(
  HWND hwnd,
  UINT state,
  int cx,
  int cy)
{
  /* Adjust any child windows in the client area to match the new size. */
  AdjustClientArea(cx, cy);

  /* Hack to correctly repaint in DWM environments when the ribbon is removed. */
  if (killRibbon &amp;&amp; state != SIZE_MINIMIZED)
  {
    SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
      SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
  }
}
</pre>
</div>
<p>The <code>AdjustClientArea</code> function accepts the client width and height and adjusts the edit window to find around the Ribbon.</p>
<div class="example">
<pre>
void App::AdjustClientArea(
  int cx,
  int cy)
{
  /* Adjust the size of the edit window to fit into the client area.
  Take the size of the ribbon into consideration, if there is one. */
  UINT32 ribbonHeight = 0;

  if (pRibbon)
  {
    pRibbon->GetHeight(&amp;ribbonHeight);
  }

  SendMessage(hEdit, WM_SETREDRAW, 0, 0);
  MoveWindow(
    hEdit,
    0, ribbonHeight,
    cx,
    cy - ribbonHeight,
    TRUE);
  int textLen = GetWindowTextLength(hEdit);
  SendMessage(hEdit, EM_SETSEL, static_cast&lt;WPARAM>(textLen), static_cast&lt;LPARAM>(textLen));
  SendMessage(hEdit, WM_SETREDRAW, 1, 0);
  RedrawWindow(hEdit, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
  SendMessage(hEdit, EM_SCROLLCARET, 0, 0);
}
</pre>
</div>
<p><code>AdjustClientArea</code> calls <a href="http://msdn.microsoft.com/en-us/library/dd742708(VS.85).aspx"><code>pRibbon->GetHeight</code></a> in order to get the height of the Ribbon, and then adjusts the size of the edit window accordingly.</p>
<p>The <code>OnSize</code> method also checks to see if the <code>killRibbon</code> flag is set. This flag is set when the user hides the ribbon and restores the menu. If the flag is set, <code>OnSize</code> calls <code>SetWindowPos</code> to trigger a <a href="http://msdn.microsoft.com/en-us/library/ms632634(VS.85).aspx"><code>WM_NCCALCSIZE</code></a> message.</p>
<p>There are also handlers for <a href="http://msdn.microsoft.com/en-us/library/ms632647(VS.85).aspx"><code>WM_SIZING</code></a> and <a href="http://msdn.microsoft.com/en-us/library/ms646274(VS.85).aspx"><code>WM_ACTIVATE</code></a> that check the <code>killRibbon</code> flag and call <code>SetWindowPos</code> if it is set. If it is not, they defer handling to <a href="http://msdn.microsoft.com/en-us/library/ms633572(VS.85).aspx"><code>DefWindowProc</code></a>.</p>
<div class="example">
<pre>
case WM_SIZING:
  {
    /* Hack to correctly repaint in DWM environments when the ribbon is removed. */
    if (killRibbon)
    {
      switch (wParam)
      {
      case WMSZ_TOP:
      case WMSZ_TOPLEFT:
      case WMSZ_TOPRIGHT:
        {
          PRECT pRect = reinterpret_cast&lt;PRECT>(lParam);
          SetWindowPos(GetHWND(), NULL,
            pRect->left, pRect->top,
            pRect->right - pRect->left, pRect->bottom - pRect->top,
            SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED);
        }
        break;

      default:
        DefWindowProc(GetHWND(), msg, wParam, lParam);
      }

      return TRUE;
    }

    return DefWindowProc(GetHWND(), msg, wParam, lParam);
  }

case WM_ACTIVATE:
  {
    /* Hack to correctly repaint in DWM environments when the ribbon is removed. */
    if (killRibbon)
    {
      if (wParam != WA_INACTIVE)
      {
        SetWindowPos(GetHWND(), NULL,
          0, 0, 0, 0,
          SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
      }

      return FALSE;
    }

    return DefWindowProc(GetHWND(), msg, wParam, lParam);
  }
</pre>
</div>
<p>The <code>WM_NCCALCSIZE</code> handler does some additional work to draw the frame correctly.</p>
<div class="example">
<pre>
case WM_NCCALCSIZE:
  {
    LRESULT result = DefWindowProc(GetHWND(), msg, wParam, lParam);

    /* Hack to correctly repaint in DWM environments when the ribbon is removed. */
    if (killRibbon &#038;&#038; wParam)
    {
      MARGINS margins = {};
      DwmExtendFrameIntoClientArea(GetHWND(), &#038;margins);

      RECT adjustedRect = {};
      AdjustWindowRectEx(&#038;adjustedRect, GetWindowStyle(GetHWND()),
        TRUE, GetWindowExStyle(GetHWND()));

      LPNCCALCSIZE_PARAMS pParams = (LPNCCALCSIZE_PARAMS)lParam;
      pParams->rgrc[0].top = pParams->rgrc[1].top + (-adjustedRect.top);
    }

    return result;
  }
</pre>
</div>
<p>When the Ribbon is restored, the <code>killRibbon</code> flag is cleared and normal processing occurs for these messages.</p>
<h2>Final Thoughts, Future Changes</h2>
<p>I only have a couple of annoyances with this code. First of all, I have to check the validity of each command message in <code>OnCommand</code> since the keyboard accelerators are not synchronized with the corresponding Ribbon commands as they would be with the menu. This is a minor complaint, though. The more annoying issue is that the ribbon graphics increase the size of the executable. For the users that prefer to use the menu this is a waste of memory, but I could get around that problem by keeping the Ribbon resources in a separate DLL. I haven&#8217;t tried implementing that yet, but I&#8217;ll post an update where when I do.</p>
<p>I&#8217;ve covered a lot of ground here, so please leave a comment if anything is still unclear.</p>
<p>Note: This project uses the <a href="http://www.iconarchive.com/category/system/must-have-icons-by-visualpharm.html">Must Have Icons</a> by <a href="http://www.visualpharm.com/">VisualPharm</a>.</p>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Fwww.parkscomputing.com%2F2010%2F12%2Fsupporting-the-ribbon-and-menus%2F&amp;t=Supporting%20the%20Ribbon%20and%20Menus" id="facebook_share_button_262" style="font-size:11px; line-height:13px; font-family:'lucida grande',tahoma,verdana,arial,sans-serif; text-decoration:none; display: -moz-inline-block; display:inline-block; padding:1px 20px 0 5px; margin: 5px 0; height:15px; border:1px solid #d8dfea; color: #3B5998; background: #fff url(http://b.static.ak.fbcdn.net/images/share/facebook_share_icon.gif) no-repeat top right;">Share</a>
	<script type="text/javascript">
	<!--
	var button = document.getElementById('facebook_share_link_262') || document.getElementById('facebook_share_icon_262') || document.getElementById('facebook_share_both_262') || document.getElementById('facebook_share_button_262');
	if (button) {
		button.onclick = function(e) {
			var url = this.href.replace(/share\.php/, 'sharer.php');
			window.open(url,'sharer','toolbar=0,status=0,width=626,height=436');
			return false;
		}
	
		if (button.id === 'facebook_share_button_262') {
			button.onmouseover = function(){
				this.style.color='#fff';
				this.style.borderColor = '#295582';
				this.style.backgroundColor = '#3b5998';
			}
			button.onmouseout = function(){
				this.style.color = '#3b5998';
				this.style.borderColor = '#d8dfea';
				this.style.backgroundColor = '#fff';
			}
		}
	}
	-->
	</script>
	]]></content:encoded>
			<wfw:commentRss>http://www.parkscomputing.com/2010/12/supporting-the-ribbon-and-menus/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>New Contract</title>
		<link>http://www.parkscomputing.com/2009/12/new-contract/</link>
		<comments>http://www.parkscomputing.com/2009/12/new-contract/#comments</comments>
		<pubDate>Sat, 05 Dec 2009 03:26:00 +0000</pubDate>
		<dc:creator>Paul Parks</dc:creator>
				<category><![CDATA[consulting]]></category>
		<category><![CDATA[personal]]></category>
		<category><![CDATA[software]]></category>

		<guid isPermaLink="false">http://nightowl/?p=42</guid>
		<description><![CDATA[I&#8217;ve been signed to a new contract with my current client, on a new project working on self-service grocery again. This one should carry me well into next year, but I&#8217;m always keeping my eyes open just in case. Share]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been signed to a new contract with my current client, on a new project working on self-service grocery again. This one should carry me well into next year, but I&#8217;m always keeping my eyes open just in case.</p>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Fwww.parkscomputing.com%2F2009%2F12%2Fnew-contract%2F&amp;t=New%20Contract" id="facebook_share_button_42" style="font-size:11px; line-height:13px; font-family:'lucida grande',tahoma,verdana,arial,sans-serif; text-decoration:none; display: -moz-inline-block; display:inline-block; padding:1px 20px 0 5px; margin: 5px 0; height:15px; border:1px solid #d8dfea; color: #3B5998; background: #fff url(http://b.static.ak.fbcdn.net/images/share/facebook_share_icon.gif) no-repeat top right;">Share</a>
	<script type="text/javascript">
	<!--
	var button = document.getElementById('facebook_share_link_42') || document.getElementById('facebook_share_icon_42') || document.getElementById('facebook_share_both_42') || document.getElementById('facebook_share_button_42');
	if (button) {
		button.onclick = function(e) {
			var url = this.href.replace(/share\.php/, 'sharer.php');
			window.open(url,'sharer','toolbar=0,status=0,width=626,height=436');
			return false;
		}
	
		if (button.id === 'facebook_share_button_42') {
			button.onmouseover = function(){
				this.style.color='#fff';
				this.style.borderColor = '#295582';
				this.style.backgroundColor = '#3b5998';
			}
			button.onmouseout = function(){
				this.style.color = '#3b5998';
				this.style.borderColor = '#d8dfea';
				this.style.backgroundColor = '#fff';
			}
		}
	}
	-->
	</script>
	]]></content:encoded>
			<wfw:commentRss>http://www.parkscomputing.com/2009/12/new-contract/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ripsaw COM Interface, First Pass</title>
		<link>http://www.parkscomputing.com/2009/11/ripsaw-com-interface-first-pass/</link>
		<comments>http://www.parkscomputing.com/2009/11/ripsaw-com-interface-first-pass/#comments</comments>
		<pubDate>Tue, 10 Nov 2009 02:40:00 +0000</pubDate>
		<dc:creator>Paul Parks</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[COM]]></category>
		<category><![CDATA[ripsaw]]></category>
		<category><![CDATA[software]]></category>

		<guid isPermaLink="false">http://nightowl/?p=35</guid>
		<description><![CDATA[In this installment of the Ripsaw article series we&#8217;ll finally get to write some code. We&#8217;ve already gotten a pretty good idea about how we want to implement the core Ripsaw library, so now we&#8217;re going to define enough of the COM interface that we can create a simple test script that will eventually be [...]]]></description>
			<content:encoded><![CDATA[<p>In this installment of the <a href="/labels/ripsaw.html">Ripsaw article series</a> we&#8217;ll finally get to write some code. We&#8217;ve already gotten a pretty good idea about how we want to implement the core Ripsaw library, so now we&#8217;re going to define enough of the COM interface that we can create a simple test script that will eventually be used to exercise the library.</p>
<p><span id="more-35"></span></p>
<p>To be callable from script, the object&#8217;s interface should be OLE-automation compatible, so we&#8217;ll declare it as such in our <a href="http://msdn.microsoft.com/en-us/library/aa367091%28VS.85%29.aspx">IDL</a>:</p>
<div class="example">
<pre>[
   oleautomation,
   uuid(2D486A73-E912-4078-9F38-678226E4A0BD),
   dual,
   pointer_default(unique)
]
interface IRipsawFile : IDispatch
{
}</pre>
</div>
<p>The interface is also derived from <code>IDispatch</code> to support late-binding, which means that scripting languages can discover the methods and properties of the interface at runtime.</p>
<p>Before we can write the test script we&#8217;ll need decide what basic methods a Ripsaw object needs to implement. A single Ripsaw object will represent a connection to a file, and the object will fire events whenever the file changes so that listeners can take an action based on the update (change the display, parse the update, etc.). At a minimum, then, we need to be able to open a file and close a file.</p>
<div class="example">
<pre>interface IRipsawFile : IDispatch
{
   [id(1)] HRESULT Open(
      [in] BSTR fileName,
      [out,retval] VARIANT_BOOL* pSuccess);

   [id(2)] HRESULT Close();
}</pre>
</div>
<p>The <code>Open</code> method will accept a path to a file and return a Boolean indicating the success or failure of the method call. The <code>Close</code> method will close any existing connection, and we won&#8217;t bother to check for a return code from that method.</p>
<p>Now, it would probably be useful to be able to query a property of the object for the name of the file to which it is connected, and perhaps another property to check if the object is currently opened or closed. Let&#8217;s add those properties:</p>
<div class="example">
<pre>interface IRipsawFile : IDispatch
{
   [id(1), propget] HRESULT Name(
      [out,retval] BSTR* pName);

   [id(2), propget] HRESULT IsOpen(
      [out,retval] VARIANT_BOOL* pRetVal);

   [id(3)] HRESULT Open(
      [in] BSTR fileName,
      [out,retval] VARIANT_BOOL* pSuccess);

   [id(4)] HRESULT Close();
}</pre>
</div>
<p>Okay, not bad so far. We can open a file, query for the status, query for the name, and close the file. That&#8217;s no good if we can&#8217;t catch events fired by the object. We need to define a <code>dispinterface</code> that specifies the events that the object can fire at its clients:</p>
<div class="example">
<pre>[ uuid(6671C129-C761-42F3-AB2F-D12C33D95160) ]
dispinterface _IRipsawEvents
{
properties:
methods:
   [id(1)] HRESULT Open([in] BSTR fileName);
   [id(2)] HRESULT Close([in] BSTR fileName);
   [id(3)] HRESULT Update([in] BSTR updateData);
};</pre>
</div>
<p>The <code>Open</code> event fires when a file is opened successfully, of course. <code>Close</code> fires when a file is closed, and <code>Update</code> naturally fires when the file changes. The <code>Update</code> notification will also pass along the data that was gathered from the last file update.</p>
<p>Okay, based on those interfaces, our script might look like this:</p>
<div class="example">
<pre>var ripsaw = WScript.CreateObject("PMP.RipsawFile", "ripsaw_");

if (ripsaw)
{
   ripsaw.Open("testfile.log");

   if (ripsaw.IsOpen)
   {
      WScript.Echo("Ripsaw file is open: '" + ripsaw.Name + "'");

      /* Uh-oh... */

      ripsaw.Close();

      if (!ripsaw.IsOpen)
      {
         WScript.Echo("Ripsaw file is closed: '" + ripsaw.Name + "'");
      }
      else
      {
         WScript.Echo("ERROR: Ripsaw file reported open");
      }
   }
   else
   {
      WScript.Echo("ERROR: Ripsaw file reported closed");
   }
}
else
{
   WScript.Echo("ERROR: Failed to create Ripsaw file");
}

function ripsaw_Open(fileName)
{
   WScript.Echo("ripsaw_Open(" + fileName + ")")
}

function ripsaw_Close(fileName)
{
   WScript.Echo("ripsaw_Close(" + fileName + ")")
}

function ripsaw_Update(data)
{
   WScript.Echo("ripsaw_Update(" + data + ")")
}</pre>
</div>
<p>First of all, I&#8217;ll explain what all this means for those of you that are new to JavaScript — err, JScript — under <a href="http://msdn.microsoft.com/en-us/library/9bbdkx3k%28VS.85%29.aspx">Windows Script Host</a> (WSH). The first line creates a COM object based on its <a href="http://msdn.microsoft.com/en-us/library/dd542719%28VS.85%29.aspx">ProgID</a>, or programmatic identifier. This is a string that resolves, through the registry, to a <a href="http://en.wikipedia.org/wiki/Globally_Unique_Identifier">GUID</a> that uniquely identifies a COM library that provides the requested functionality.</p>
<p>The second parameter to <code>CreateObject</code> tells the scripting engine to wire events provided by the object to functions that start with the specified prefix. In our script, we want to handle the Open, Close, and Update notifications, so we created functions named <code>ripsaw_Open</code>, <code>ripsaw_Close</code>, and <code>ripsaw_Update</code> to catch these events. Since they all start with the &#8220;ripsaw_&#8221; prefix, the script engine will call them when any of the corresponding events are fired by our object.</p>
<p>If you looked carefully at the script, you noticed that I inserted a comment: &#8220;Uh-oh.&#8221; That&#8217;s because the script has no way of sitting and waiting for an update other than looping in a tight loop, and that is almost never a good idea. The script would waste CPU time doing nothing while we wait for the file to be updated. In other languages we have access to message loops, <code>WaitForSingleObject</code>, and other means of yielding CPU until an event occurs, but not in WSH. We need something better. What we&#8217;ll do is add a method to our object which, when called, will do absolutely nothing until an update occurs. We&#8217;ll call it <code>WaitForUpdate</code>.</p>
<div class="example">
<pre>interface IRipsawFile : IDispatch
{
   [id(1), propget] HRESULT Name(
      [out,retval] BSTR* pName);

   [id(2), propget] HRESULT IsOpen(
      [out,retval] VARIANT_BOOL* pRetVal);

   [id(3)] HRESULT Open(
      [in] BSTR fileName,
      [out,retval] VARIANT_BOOL* pSuccess);

   [id(4)] HRESULT Close();

   [id(5)] HRESULT WaitForUpdate(
      [in] ULONG timeout,
      [out,retval] BSTR* pNewData);
}</pre>
</div>
<p>The first parameter is a timeout, in milliseconds, so that the function will return if no updates arrive in the specified amount of time. We&#8217;ll take a page from Win32 and say that a value of 0xFFFFFFFF will tell the method to wait forever. If the timeout expires, the method will return a null <code>BSTR</code>; otherwise, the return value will be the new data from the last file update. We&#8217;ll modify the heart of our script to call the new method:</p>
<div class="example">
<pre>      WScript.Echo("Ripsaw file is open: '" + ripsaw.Name + "'");

      var timeout = 5000;
      WScript.Echo("Waiting for data...");
      var newData = ripsaw.WaitForUpdate(timeout);

      if (newData)
      {
         WScript.Echo("Waited for data:", newData);
      }
      else
      {
         WScript.Echo("Timed out after waiting for data for " + timeout + " milliseconds");
      }

      ripsaw.Close();</pre>
</div>
<p>In a real script we&#8217;ll probably call <code>WaitForUpdate</code> in a loop, but for now we&#8217;ll just wait for one update before exiting.</p>
<h3>Generalizing the Interface</h3>
<p>This looks good for files, but perhaps later on we&#8217;d like to extend Ripsaw to listen to other sources of events, like the <a href="http://msdn.microsoft.com/en-us/library/aa385780%28VS.85%29.aspx">Windows Event Log</a> (the first Ripsaw actually did that for a while, but I backed out the feature to focus on files). It would be a good idea to go ahead and factor out the common methods and properties into a base interface that could be extended by other event sources.</p>
<div class="example">
<pre>[
   oleautomation,
   uuid(0270FDFF-56AF-42ec-9971-AE8DCB0DAB36),
   dual,
   pointer_default(unique)
]
interface IRipsawObject : IDispatch
{
   [id(1), propget] HRESULT Name(
      [out,retval] BSTR* pName);

   [id(2), propget] HRESULT IsOpen(
      [out,retval] VARIANT_BOOL* pRetVal);

   [id(3)] HRESULT Close();

   [id(4)] HRESULT WaitForUpdate(
      [in] ULONG timeout,
      [out,retval] BSTR* pNewData);
};

[
   oleautomation,
   uuid(2D486A73-E912-4078-9F38-678226E4A0BD),
   dual,
   pointer_default(unique)
]
interface IRipsawFile : IRipsawObject
{
   [id(10)] HRESULT Open(
      [in] BSTR fileName,
      [out,retval] VARIANT_BOOL* pSuccess);
};</pre>
</div>
<p>Later on, if we define an interface for listening to event logs, we&#8217;ll derive that interface from <code>IRipsawObject</code> and define an <code>Open</code> method specific to event logs. We&#8217;ll also assign a special ProgID to event log objects so our client applications can create them:</p>
<div class="example">
<pre>var ripsaw = WScript.CreateObject("PMP.RipsawEventLog", "ripsaw_");</pre>
</div>
<h3>Next Steps</h3>
<p>Now we have an interface, and we have a script to exercise that interface. What we need next is some real code to implement the interface and a binary that the script can load and run. We&#8217;ll start on a preliminary implementation in the next article. Until then, you can download <a href="/ripsaw/articles/RipsawSource_20091109.zip">the IDL and the script file</a> and look them over. Please leave a comment with any suggestions you may have.</p>
<p><!-- fullpost --></p>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Fwww.parkscomputing.com%2F2009%2F11%2Fripsaw-com-interface-first-pass%2F&amp;t=Ripsaw%20COM%20Interface%2C%20First%20Pass" id="facebook_share_button_35" style="font-size:11px; line-height:13px; font-family:'lucida grande',tahoma,verdana,arial,sans-serif; text-decoration:none; display: -moz-inline-block; display:inline-block; padding:1px 20px 0 5px; margin: 5px 0; height:15px; border:1px solid #d8dfea; color: #3B5998; background: #fff url(http://b.static.ak.fbcdn.net/images/share/facebook_share_icon.gif) no-repeat top right;">Share</a>
	<script type="text/javascript">
	<!--
	var button = document.getElementById('facebook_share_link_35') || document.getElementById('facebook_share_icon_35') || document.getElementById('facebook_share_both_35') || document.getElementById('facebook_share_button_35');
	if (button) {
		button.onclick = function(e) {
			var url = this.href.replace(/share\.php/, 'sharer.php');
			window.open(url,'sharer','toolbar=0,status=0,width=626,height=436');
			return false;
		}
	
		if (button.id === 'facebook_share_button_35') {
			button.onmouseover = function(){
				this.style.color='#fff';
				this.style.borderColor = '#295582';
				this.style.backgroundColor = '#3b5998';
			}
			button.onmouseout = function(){
				this.style.color = '#3b5998';
				this.style.borderColor = '#d8dfea';
				this.style.backgroundColor = '#fff';
			}
		}
	}
	-->
	</script>
	]]></content:encoded>
			<wfw:commentRss>http://www.parkscomputing.com/2009/11/ripsaw-com-interface-first-pass/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Refining Ripsaw&#8217;s Design</title>
		<link>http://www.parkscomputing.com/2009/11/refining-ripsaws-design/</link>
		<comments>http://www.parkscomputing.com/2009/11/refining-ripsaws-design/#comments</comments>
		<pubDate>Fri, 06 Nov 2009 17:05:00 +0000</pubDate>
		<dc:creator>Paul Parks</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[ripsaw]]></category>
		<category><![CDATA[software]]></category>

		<guid isPermaLink="false">http://nightowl/?p=34</guid>
		<description><![CDATA[In my last entry in the Ripsaw article series, I discussed some of the design goals for Ripsaw. In this article I&#8217;ll flesh out the design a little more and discuss specific implementation possibilities. To bring you up to speed, Ripsaw is a log-viewing utility for Windows that I initially wrote about six years ago, [...]]]></description>
			<content:encoded><![CDATA[<p>In my last entry in the <a href="/labels/ripsaw.html">Ripsaw article series</a>, I discussed some of the <a href="/2009/10/design-goals-for-ripsaw.html">design goals for Ripsaw</a>. In this article I&#8217;ll flesh out the design a little more and discuss specific implementation possibilities.</p>
<p>To bring you up to speed, Ripsaw is a log-viewing utility for Windows that I initially wrote about six years ago, but never released widely. I&#8217;ve decided to rewrite it and discuss each step of the rewrite here.<br />
<span id="more-34"></span></p>
<h3>Implementing the API</h3>
<p>The initial Ripsaw implementation was a monolithic (albeit small) application, but this time I want to separate the log-reading mechanism from the log-viewing UI. There are times when I&#8217;d like to use the log-reading capabilities of Ripsaw from JavaScript or <a href="http://technet.microsoft.com/en-us/scriptcenter/dd742419.aspx">PowerShell</a>, and I&#8217;d also like to write more than one viewer application around the log reader. There are at least three possible technologies for this API:</p>
<ol>
<li>C DLL</li>
<li>COM</li>
<li>.NET</li>
</ol>
<p>Okay, there are a lot more that I could choose from, but for the machines I&#8217;m targeting these are the most likely candidates. A DLL exporting C functions would be the simplest to write and would be easy to integrate with most programming languages, but in order to use that sort of API from JavaScript (or similar languages) I&#8217;d have to write a COM wrapper. A .NET implementation would be COM-callable, but it would require having .NET installed on the target machine, and sometimes the systems I debug don&#8217;t have .NET installed (hard to believe, I know, but true). COM, however, is ubiquitous on Windows, fairly straightforward, can be used from JavaScript with ease, and is easily callable from .NET if I had a reason to do so later on. Therefore, I&#8217;ll implement Ripsaw&#8217;s non-UI behavior in a COM library.</p>
<h3>What About Registration?</h3>
<p>I&#8217;d still like to be able to run Ripsaw from a USB drive without having to install anything on the machine I&#8217;m debugging or examining, which means I&#8217;d like to avoid the hassle of having to run <a href="http://technet.microsoft.com/en-us/library/bb490985.aspx">regsvr32</a> on the target machine before I can use Ripsaw. Fortunately, there is <a href="http://msdn.microsoft.com/en-us/library/ms973913.aspx">registration-free COM</a>, which will let me run a Ripsaw viewer that loads the Ripsaw COM library with the aid of a manifest. This will let me keep a viewer and the Ripsaw library in a directory on my USB drive so I can just plug it in and run the viewer without having to register or install anything.</p>
<h3>Supporting Scripting in the Viewer</h3>
<p>Since Ripsaw&#8217;s log-reading capability will be implemented in COM, I&#8217;ll be able to write JavaScript apps that can watch for data from a log file, and these scripts can either parse the data or take actions based on the data. Not only would this be useful apart from a viewer, but it might also be useful <em>inside</em> a viewer. I could define script actions to operate on log lines, highlight them, parse them, etc., while I&#8217;m watching a log in the viewer. To take advantage of this I&#8217;ll add JavaScript support to the Windows Ripsaw viewer, along with the capability to load pre-defined scripts that act on log data.</p>
<h3>Viewer Implementation</h3>
<p>In keeping with my requirement for making Ripsaw light and portable, I don&#8217;t want to use .NET for the main viewer implementation. I might create a <a href="http://msdn.microsoft.com/en-us/library/dd30h2yb.aspx">Windows Forms</a> or <a href="http://msdn.microsoft.com/en-us/library/aa663364.aspx">WPF</a> viewer later on once .NET becomes more widespread on the systems I mainly work on (point-of-sale terminals and servers for grocery stores), or for when I&#8217;m debugging in a controlled environment, but for the main console and GUI viewers I&#8217;ll use C++ as the development language. COM is still a bit of a pain to use from C++, at least compared to JavaScript or .NET, but it won&#8217;t be too bad.</p>
<h3>Multiprocessor Support</h3>
<p>Fortunately, the newer POS terminals and servers that are being installed today have multi-core processors, and I definitely want to take advantage of that today. I want to architect the core Ripsaw library to take advantage of multiple threads of execution spread across multiple processors or processor cores. I already write multi-threaded, multi-process systems for these machines, and these systems tend to be heavily instrumented, so sometimes I&#8217;m watching several different log files at once during a testing or debugging session. I don&#8217;t want to slow a system down too much when I start up the viewer, and loading down one core of a multi-core processor with a log viewer would certainly be a bad thing.</p>
<h3>Okay, Can We Start Coding Now?</h3>
<p>Actually, I already have been doing some prototyping, which helped me decide on the details I&#8217;ve described above. Now that I&#8217;ve sorted out some of the lower-level requirements, in the next article I&#8217;ll start defining the Ripsaw log-reader COM interface.</p>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Fwww.parkscomputing.com%2F2009%2F11%2Frefining-ripsaws-design%2F&amp;t=Refining%20Ripsaw%27s%20Design" id="facebook_share_button_34" style="font-size:11px; line-height:13px; font-family:'lucida grande',tahoma,verdana,arial,sans-serif; text-decoration:none; display: -moz-inline-block; display:inline-block; padding:1px 20px 0 5px; margin: 5px 0; height:15px; border:1px solid #d8dfea; color: #3B5998; background: #fff url(http://b.static.ak.fbcdn.net/images/share/facebook_share_icon.gif) no-repeat top right;">Share</a>
	<script type="text/javascript">
	<!--
	var button = document.getElementById('facebook_share_link_34') || document.getElementById('facebook_share_icon_34') || document.getElementById('facebook_share_both_34') || document.getElementById('facebook_share_button_34');
	if (button) {
		button.onclick = function(e) {
			var url = this.href.replace(/share\.php/, 'sharer.php');
			window.open(url,'sharer','toolbar=0,status=0,width=626,height=436');
			return false;
		}
	
		if (button.id === 'facebook_share_button_34') {
			button.onmouseover = function(){
				this.style.color='#fff';
				this.style.borderColor = '#295582';
				this.style.backgroundColor = '#3b5998';
			}
			button.onmouseout = function(){
				this.style.color = '#3b5998';
				this.style.borderColor = '#d8dfea';
				this.style.backgroundColor = '#fff';
			}
		}
	}
	-->
	</script>
	]]></content:encoded>
			<wfw:commentRss>http://www.parkscomputing.com/2009/11/refining-ripsaws-design/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Busy Until January</title>
		<link>http://www.parkscomputing.com/2009/11/busy-until-january/</link>
		<comments>http://www.parkscomputing.com/2009/11/busy-until-january/#comments</comments>
		<pubDate>Tue, 03 Nov 2009 01:20:00 +0000</pubDate>
		<dc:creator>Paul Parks</dc:creator>
				<category><![CDATA[consulting]]></category>
		<category><![CDATA[personal]]></category>
		<category><![CDATA[software]]></category>

		<guid isPermaLink="false">http://nightowl/?p=33</guid>
		<description><![CDATA[I&#8217;ve been assigned to a new project through the end of 2009 so I&#8217;m unavailable for now, but if I&#8217;m not picked up for an extension I&#8217;ll be looking for new projects in 2010. Take a look at my resume and let me know if I might be a good fit for any contract or [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been assigned to a new project through the end of 2009 so I&#8217;m unavailable for now, but if I&#8217;m not picked up for an extension I&#8217;ll be looking for new projects in 2010. Take a look at <a href="/resume.html">my resume</a> and let me know if I might be a good fit for any contract or consulting opportunities in your organization. I&#8217;m currently W-2 with a small consulting company, but I&#8217;m open to 1099 or corp-to-corp starting next year.</p>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Fwww.parkscomputing.com%2F2009%2F11%2Fbusy-until-january%2F&amp;t=Busy%20Until%20January" id="facebook_share_button_33" style="font-size:11px; line-height:13px; font-family:'lucida grande',tahoma,verdana,arial,sans-serif; text-decoration:none; display: -moz-inline-block; display:inline-block; padding:1px 20px 0 5px; margin: 5px 0; height:15px; border:1px solid #d8dfea; color: #3B5998; background: #fff url(http://b.static.ak.fbcdn.net/images/share/facebook_share_icon.gif) no-repeat top right;">Share</a>
	<script type="text/javascript">
	<!--
	var button = document.getElementById('facebook_share_link_33') || document.getElementById('facebook_share_icon_33') || document.getElementById('facebook_share_both_33') || document.getElementById('facebook_share_button_33');
	if (button) {
		button.onclick = function(e) {
			var url = this.href.replace(/share\.php/, 'sharer.php');
			window.open(url,'sharer','toolbar=0,status=0,width=626,height=436');
			return false;
		}
	
		if (button.id === 'facebook_share_button_33') {
			button.onmouseover = function(){
				this.style.color='#fff';
				this.style.borderColor = '#295582';
				this.style.backgroundColor = '#3b5998';
			}
			button.onmouseout = function(){
				this.style.color = '#3b5998';
				this.style.borderColor = '#d8dfea';
				this.style.backgroundColor = '#fff';
			}
		}
	}
	-->
	</script>
	]]></content:encoded>
			<wfw:commentRss>http://www.parkscomputing.com/2009/11/busy-until-january/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Design Goals for Ripsaw</title>
		<link>http://www.parkscomputing.com/2009/10/design-goals-for-ripsaw/</link>
		<comments>http://www.parkscomputing.com/2009/10/design-goals-for-ripsaw/#comments</comments>
		<pubDate>Wed, 28 Oct 2009 00:47:00 +0000</pubDate>
		<dc:creator>Paul Parks</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[ripsaw]]></category>
		<category><![CDATA[software]]></category>

		<guid isPermaLink="false">http://nightowl/?p=31</guid>
		<description><![CDATA[In this installment of the Ripsaw project series I&#8217;ll sketch out some of my design goals for the new version of Ripsaw, and the rationale for those goals. Split the App Into DLLs The original Ripsaw was a 32-bit Windows application contained in a single executable. This made it easy to carry around on a [...]]]></description>
			<content:encoded><![CDATA[<p>In this installment of the <a href="/2009/10/a-new-article-series-ripsaw/">Ripsaw</a> project series I&#8217;ll sketch out some of my design goals for the new version of Ripsaw, and the rationale for those goals.<br />
<span id="more-31"></span></p>
<h3>Split the App Into DLLs</h3>
<p>The original Ripsaw was a 32-bit Windows application contained in a single executable. This made it easy to carry around on a USB drive and run on machines that I needed to debug. The downsides were that I had to link the C runtime library into the executable, and application was one monolithic entity that couldn&#8217;t be easily extended.</p>
<p>For the new Ripsaw I&#8217;m going to build it to use the C runtime in a DLL. For most of the machines I work with this will require carrying around the C runtime distributable, but I&#8217;m willing to do that as long as I don&#8217;t have to install anything on the terminal. I&#8217;m not completely up to speed with side-by-side assemblies; I prefer to just place the VS 2010 C runtime DLL in the same directory as the executable and run the app, but I&#8217;ll have to verify that&#8217;s still supported. (Edit: It is. <em>Duh!</em>) So far SxS has caused me a lot of grief, but that&#8217;s probably because I haven&#8217;t bothered to really understand it.</p>
<h3>Create a Ripsaw API</h3>
<p>Since I&#8217;m splitting out the C runtime, I&#8217;m also going to separate most of Ripsaw&#8217;s non-visual functionality into a separate API DLL. Not only will this make the core functionality more testable and enforce separation of UI, but I&#8217;ll be able to build more than one user interface around it. I&#8217;d like to build a command-line application as well as a graphical application. Sometimes you just can&#8217;t beat raw text.</p>
<h3>Embrace 64-bit</h3>
<p>Even though nearly all of my professional development is still 32-bit, I want to build Ripsaw to compile for both 32-bit and 64-bit architectures. I&#8217;ll need to do this eventually anyway, and this is a good time to start.</p>
<h3>Support Extensibility</h3>
<p>My original plans for Ripsaw included the ability to create filters for processing log output as it arrived in the application. Perhaps only lines containing certain values would be displayed, or maybe certain words would be shown in a particular color or font. Rather than trying to build every possible behavior into Ripsaw, I&#8217;ll publish an extensibility interface so that I can add features later on (or <em>you</em> can add them) without having to change the core API or UI.</p>
<h3>Create a Windows 7 UI</h3>
<p>The main UI that I&#8217;ll use will be a 32-bit graphical application targeting Windows XP, but I&#8217;d also like to take advantage of some of the new Windows 7 user interface controls such as the ribbon. I&#8217;m not inclined to try to serve both interface styles from one application, so after I finish the primary UI I&#8217;ll create a version that is specific to Windows 7. Since I&#8217;m putting most of the non-visual behavior into a separate DLL I&#8217;ll be free to experiment with different interfaces anyway.</p>
<h3>Coming Up</h3>
<p>In the next few articles I&#8217;ll delve into some more development specifics such as language and library choices.</p>
<a href="http://www.facebook.com/share.php?u=http%3A%2F%2Fwww.parkscomputing.com%2F2009%2F10%2Fdesign-goals-for-ripsaw%2F&amp;t=Design%20Goals%20for%20Ripsaw" id="facebook_share_button_31" style="font-size:11px; line-height:13px; font-family:'lucida grande',tahoma,verdana,arial,sans-serif; text-decoration:none; display: -moz-inline-block; display:inline-block; padding:1px 20px 0 5px; margin: 5px 0; height:15px; border:1px solid #d8dfea; color: #3B5998; background: #fff url(http://b.static.ak.fbcdn.net/images/share/facebook_share_icon.gif) no-repeat top right;">Share</a>
	<script type="text/javascript">
	<!--
	var button = document.getElementById('facebook_share_link_31') || document.getElementById('facebook_share_icon_31') || document.getElementById('facebook_share_both_31') || document.getElementById('facebook_share_button_31');
	if (button) {
		button.onclick = function(e) {
			var url = this.href.replace(/share\.php/, 'sharer.php');
			window.open(url,'sharer','toolbar=0,status=0,width=626,height=436');
			return false;
		}
	
		if (button.id === 'facebook_share_button_31') {
			button.onmouseover = function(){
				this.style.color='#fff';
				this.style.borderColor = '#295582';
				this.style.backgroundColor = '#3b5998';
			}
			button.onmouseout = function(){
				this.style.color = '#3b5998';
				this.style.borderColor = '#d8dfea';
				this.style.backgroundColor = '#fff';
			}
		}
	}
	-->
	</script>
	]]></content:encoded>
			<wfw:commentRss>http://www.parkscomputing.com/2009/10/design-goals-for-ripsaw/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

