<?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"
	>

<channel>
	<title>Paint.NET</title>
	<atom:link href="http://blog.getpaint.net/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.getpaint.net</link>
	<description>The best free image and photo editor. By Rick Brewster.</description>
	<pubDate>Wed, 07 May 2008 00:35:25 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5.1</generator>
	<language>en</language>
			<item>
		<title>Paint.NET turns 4!</title>
		<link>http://blog.getpaint.net/2008/05/06/paintnet-turns-4/</link>
		<comments>http://blog.getpaint.net/2008/05/06/paintnet-turns-4/#comments</comments>
		<pubDate>Wed, 07 May 2008 00:35:25 +0000</pubDate>
		<dc:creator>Rick Brewster</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.getpaint.net/?p=293</guid>
		<description><![CDATA[According to the Roadmap page, version 1.0 was released four years ago on May 6th, 2004. Happy birthday Paint.NET!
As most people know, Paint.NET started as a senior design project at Washington State University&#8217;s EECS department. When we finished the first version, it was meant as a &#8220;hey look we actually made something - neat!&#8221; project [...]]]></description>
			<content:encoded><![CDATA[<p>According to <a href="http://www.getpaint.net/roadmap.html#v1_0">the Roadmap page</a>, version 1.0 was released four years ago on May 6th, 2004. Happy birthday Paint.NET!</p>
<p>As most people know, Paint.NET started as a senior design project at <a href="http://www.wsu.edu">Washington State University</a>&#8217;s <a href="http://eecs.wsu.edu/">EECS department</a>. When we finished the first version, it was meant as a &#8220;hey look we actually made something - neat!&#8221; project that we showed off to family and friends. A simple website was put up on the eecs.wsu.edu web server, and the project was neither hyped nor advertised once the semester was over.  Fast forward a few months later, after I started working full time at Microsoft, and I was forwarded e-mail from some interns who had managed to find it and were asking about why it wouldn&#8217;t install on XP SP2.</p>
<p>Interesting things to know about the first version of Paint.NET, in no particular order:</p>
<blockquote><p>· It took 15 weeks to complete, from start to finish.<br />
· It was about 36,000 lines of code.<br />
· It was distributed as a raw MSI, instead of an installer/wizard EXE.<br />
· It required Windows XP SP1 as the minimum OS. There was no support for 2000.<br />
· And, because of a typo, it did not work in Windows XP SP2! Changing an equals operator, =, to a greater-than-or-equals operator, >=, fixed this.<br />
· Plugins were not supported.<br />
· We used a CVS server that was running on an XP box that doubled as one of our development workstations. It was in the EECS computer lab that other people had access to, and we had to tape a piece of paper on to it that said, &#8220;SERVER - DO NOT TURN OFF!&#8221;<br />
· We did not have a bug tracking system. We either fixed bugs immediately as we saw them, or put them in to an Excel spreadsheet (mostly the former).<br />
· I made the floating windows translucent after observing that most people didn&#8217;t even try drawing in the occluded areas when I handed them a tablet PC with Paint.NET on it (&#8221;hey you, draw something!&#8221;). It was if that area of the canvas didn&#8217;t exist. Once the windows were made translucent, this went away! Usability studies really do work.<br />
· For the first month or so of development, the status bar had no information in it. Instead, it had the text: &#8220;I am the status bar!!!!!!!!!1111&#8243;</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://blog.getpaint.net/2008/05/06/paintnet-turns-4/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Paint.NET v3.31 is now available!</title>
		<link>http://blog.getpaint.net/2008/05/01/paintnet-v331-is-now-available/</link>
		<comments>http://blog.getpaint.net/2008/05/01/paintnet-v331-is-now-available/#comments</comments>
		<pubDate>Fri, 02 May 2008 01:19:24 +0000</pubDate>
		<dc:creator>Rick Brewster</dc:creator>
		
		<category><![CDATA[Update News]]></category>

		<guid isPermaLink="false">http://blog.getpaint.net/?p=292</guid>
		<description><![CDATA[This is mostly a servicing release to fix a few important bugs that have been causing some people a lot of headaches. You can get it in the usual ways: either via the built-in updater (Help -> Check for Updates), or directly from the website: http://www.getpaint.net/.
Changes since v3.30:
* New: Enabled integration with Window Clippings, which [...]]]></description>
			<content:encoded><![CDATA[<p>This is mostly a servicing release to fix a few important bugs that have been causing some people a lot of headaches. You can get it in the usual ways: either via the built-in updater (Help -> Check for Updates), or directly from the website: <a href="http://www.getpaint.net">http://www.getpaint.net/</a>.</p>
<p>Changes since v3.30:</p>
<blockquote><p>* <b>New:</b> Enabled integration with Window Clippings, which is a high quality screen capture utility by Kenny Kerr.<br />
<b>* Fixed:</b> Fixed a crash with the Open/Save dialogs if the Documents or Pictures directories were either inaccessible or if the user did not have certain file system permissions.<br />
<b>* Changed / Fixed:</b> The hotkey for Adjustments -> Levels is now Ctrl+Shift+L instead of Ctrl+Alt+L. This was preventing certain characters, such as the Polish &#8216;ł&#8217;, from being typed.</p></blockquote>
<p>For this update I&#8217;ve also worked with <a href="http://weblogs.asp.net/kennykerr/">Kenny Kerr</a> to enable integration between Paint.NET and his <a href="http://www.windowclippings.com/">Window Clippings</a> utility. It now has a built-in &#8220;Send to Paint.NET&#8221; action so that when you take a screenshot it will automatically send it to Paint.NET without a filename (&#8221;Untitled&#8221;). Before, this had to be done manually and it was a bit clumsy because it would show up in the File -> Open Recent menu with a long, computer generated file name. It was also tricky to ensure that the alpha channel, which Window Clippings captures correctly for Vista/Aero effects, made the trip correctly. </p>
<p>To kick this off, we also have a special deal &#8212; for the month of May we&#8217;re knocking $4 USD off the price of Window Clippings for Paint.NET users! Now it&#8217;s only $14 USD instead of $18 USD.<br />
<a href="http://www.windowclippings.com/pdnmay2008.aspx"><img border=0 src="http://www.windowclippings.com/pdnmay2008button.png"></a><br />
You will also see this offer when you install Paint.NET v3.31. I use this program myself, and it saves me a <i>ton</i> of time when I make screenshots, e.g. for the <a href="http://www.getpaint.net/doc/latest">Paint.NET help and documentation</a>. I really like how it automatically captures all of the Vista/Aero effects, such as the transparency and the drop shadow. I highly recommend it.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.getpaint.net/2008/05/01/paintnet-v331-is-now-available/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Paint.NET v3.31 is on the way</title>
		<link>http://blog.getpaint.net/2008/04/28/paintnet-v331-is-on-the-way/</link>
		<comments>http://blog.getpaint.net/2008/04/28/paintnet-v331-is-on-the-way/#comments</comments>
		<pubDate>Mon, 28 Apr 2008 22:52:33 +0000</pubDate>
		<dc:creator>Rick Brewster</dc:creator>
		
		<category><![CDATA[Update News]]></category>

		<guid isPermaLink="false">http://blog.getpaint.net/?p=291</guid>
		<description><![CDATA[I&#8217;m shooting to have a version 3.31 release by this Thursday, May 1st. There&#8217;s a nasty bug that&#8217;s come up in the v3.30 code base that I need to fix! The problem occurs with my use of the native Win32 API, SHGetFolderPathW(). If your Documents or Pictures folder is on a network share, and that [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m shooting to have a version 3.31 release by this Thursday, May 1st. There&#8217;s a nasty bug that&#8217;s come up in the v3.30 code base that I need to fix! The problem occurs with my use of the native Win32 API, SHGetFolderPathW(). If your Documents or Pictures folder is on a network share, and that path is unavailable (network down, haven&#8217;t typed in credentials, etc. etc.), then Paint.NET will almost certainly crash when you go to Open or Save. The fix is simple enough, of course, but this is causing a lot of headaches and an inbox full of crash logs for me.</p>
<p>In other news, the weather here in Kirkland is great* and I&#8217;ve got the week off. I can finally reliably finish <i>Green Grass and High Tides</i> in Rock Band on Expert (4 stars), and Grand Theft Auto IV comes out tomorrow.</p>
<p><i>* Well, for now &#8230; who knows what tomorrow will be like!</i></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.getpaint.net/2008/04/28/paintnet-v331-is-on-the-way/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Paint.NET merchandise store is now open!</title>
		<link>http://blog.getpaint.net/2008/04/21/paintnet-merchandise-store-is-now-open/</link>
		<comments>http://blog.getpaint.net/2008/04/21/paintnet-merchandise-store-is-now-open/#comments</comments>
		<pubDate>Mon, 21 Apr 2008 19:54:53 +0000</pubDate>
		<dc:creator>Rick Brewster</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.getpaint.net/?p=288</guid>
		<description><![CDATA[I signed up for a Cafepress account awhile ago but never got around to adding merchandise to it. This weekend, I finally uploaded all the images and set up the products:
The Official Paint.NET Merchandise store: http://www.cafepress.com/paintdotnet/

There are shirts, a mouse pad, mugs, stickers, buttons, hats, and a tote bag. If you have feedback on the [...]]]></description>
			<content:encoded><![CDATA[<p>I signed up for a <a href="http://www.cafepress.com">Cafepress</a> account awhile ago but never got around to adding merchandise to it. This weekend, I finally uploaded all the images and set up the products:</p>
<blockquote><p><b>The Official Paint.NET Merchandise store: <a href="http://www.cafepress.com/paintdotnet/">http://www.cafepress.com/paintdotnet/</a></b><br />
<a href="http://www.cafepress.com/paintdotnet/"><img src="http://www.getpaint.net/users/rick/blog/20080421_pdnstore.png" border=0 alt="Paint.NET Merchandise Store" /></a></p></blockquote>
<p>There are shirts, a mouse pad, mugs, stickers, buttons, hats, and a tote bag. If you have feedback on the selection, either leave a comment or join the discussion on the forum here: <a href="http://paintdotnet.forumer.com/viewtopic.php?f=12&#038;t=24051">http://paintdotnet.forumer.com/viewtopic.php?f=12&#038;t=24051</a></p>
<p>Over the next week I’ll add links to the store added in other places, such as the main website. For now I’ve got the link here on the blog and on the forum. <a href="http://www.cafepress.com">Cafepress</a> doesn’t leave much room for profit, so this is mostly a promotional tool. Which means that if your intent is to send $20 to Paint.NET then we’re both better off if you donate directly. Purchasing a $19.99 shirt via Cafepress only nets me $1 or $2. If you want a shirt, then by all means buy one – but if you want to donate, then just donate <img src='http://getpaint.setupmyblog.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>I’m not sure why I didn’t do this two years ago, it was rather easy to set up and the response so far has been very positive.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.getpaint.net/2008/04/21/paintnet-merchandise-store-is-now-open/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Paint.NET v3.30 &#8212; Final release now available!</title>
		<link>http://blog.getpaint.net/2008/04/10/paintnet-v330-final-release-now-available/</link>
		<comments>http://blog.getpaint.net/2008/04/10/paintnet-v330-final-release-now-available/#comments</comments>
		<pubDate>Fri, 11 Apr 2008 05:04:02 +0000</pubDate>
		<dc:creator>Rick Brewster</dc:creator>
		
		<category><![CDATA[Update News]]></category>

		<guid isPermaLink="false">http://blog.getpaint.net/2008/04/10/paintnet-v330-final-release-now-available/</guid>
		<description><![CDATA[It&#8217;s finally here! You can grab it via the built-in updater from the Help-&#62; Check for Updates menu item, or go get it straight from the website at http://www.getpaint.net/ 
Enjoy!
Changes since v3.22:

New: Italian translation.
New: Ability to save PNG&#8217;s at 8- and 24-bit color depths.
New: Ability to save BMP&#8217;s at 8-bit color depth.
New: &#34;Auto-detect&#34; bit-depth option [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s finally here! You can grab it via the built-in updater from the Help-&gt; Check for Updates menu item, or go get it straight from the website at <a href="http://www.getpaint.net/">http://www.getpaint.net/</a> </p>
<p>Enjoy!</p>
<p>Changes since v3.22:</p>
<ul>
<li><strong>New</strong>: Italian translation.</li>
<li><strong>New</strong>: Ability to save PNG&#8217;s at 8- and 24-bit color depths.</li>
<li><strong>New</strong>: Ability to save BMP&#8217;s at 8-bit color depth.</li>
<li><strong>New</strong>: &quot;Auto-detect&quot; bit-depth option for PNG, BMP, and TGA file types. It will analyze the image and determine the lowest bit-depth that can still save the image without quality loss.</li>
<li><strong>New</strong>: &quot;Fragment&quot; blur effect, by Ed Harvey</li>
<li><strong>New</strong>: The &quot;Polar Inversion&quot; distortion effect has been enhanced to allow changing the rendering offset, and the behavior for &quot;edge&quot; pixels (clamp, reflect, or wrap).</li>
<li><strong>New</strong>: For developers, added a Color Wheel control to IndirectUI for use in effect plugins.</li>
<li><strong>New</strong>: For developers, added ability to customize certain properties of the effect configuration dialog via IndirectUI.</li>
<li><strong>New</strong>: For developers, IndirectUI can now be used to write configuration UI for file types.</li>
<li><strong>New</strong>: For developers, IndirectUI has a new radio button control type for enumerations, and some new property constraint rules.</li>
<li><strong>Changed</strong>: Most effects and adjustments, including plugins that use IndirectUI, have been visually refreshed. The new look is cleaner and more compact. These changes are shown in more detail at <a href="http://blog.getpaint.net/2008/03/10/cleaning-up-the-ui-for-paintnet-v330/">http://blog.getpaint.net/2008/03/10/cleaning-up-the-ui-for-paintnet-v330/</a></li>
<li><strong>Fixed</strong>: Various UI issues with the Levels adjustment.</li>
<li><strong>Fixed</strong>: When pasting text into the Text tool with Ctrl+V, it would hide the nub for moving the text.</li>
<li><strong>Fixed</strong>: If the startup tool was set to the Zoom or Pan (Hand) tool, then the toolbar would render incorrectly.</li>
<li><strong>Fixed</strong>: Thumbnails for PDN images now include transparency, which improves their appearance in Windows Vista.</li>
<li><strong>Fixed</strong>: In some cases, an exponential property slider would get &quot;stuck&quot; at some values when using up/down keyboard keys. This mostly affects some effect plugins.</li>
<li><strong>Fixed</strong>: When using keyboard navigation, sometimes the File menu would scroll its items out of view.</li>
<li><strong>Fixed</strong>: Some crashes that were tracked down to out-of-bounds coordinate values in certain effects.</li>
<li><strong>Fixed</strong>: The installer would fail if Paint.NET had never been installed before, and was being installed to any non-default directory. This bug only affected version 3.22. </li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.getpaint.net/2008/04/10/paintnet-v330-final-release-now-available/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Paint.NET v3.30 Release Candidate 1 is now available!</title>
		<link>http://blog.getpaint.net/2008/04/07/paintnet-v330-release-candidate-1-is-now-available/</link>
		<comments>http://blog.getpaint.net/2008/04/07/paintnet-v330-release-candidate-1-is-now-available/#comments</comments>
		<pubDate>Tue, 08 Apr 2008 05:01:52 +0000</pubDate>
		<dc:creator>Rick Brewster</dc:creator>
		
		<category><![CDATA[Update News]]></category>

		<guid isPermaLink="false">http://blog.getpaint.net/2008/04/07/paintnet-v330-release-candidate-1-is-now-available/</guid>
		<description><![CDATA[Well this has been an interesting release to push out the door &#8230; all sorts of interesting hurdles have come up including hardware threatening to fail, myself being really busy with other cool stuff at my day job, translators with food poisoning (they are better now thankfully!), and pyrochild&#8217;s computer refusing to work with a [...]]]></description>
			<content:encoded><![CDATA[<p>Well this has been an interesting release to push out the door &#8230; all sorts of interesting hurdles have come up including hardware threatening to fail, myself being really busy with other cool stuff at my day job, translators with food poisoning (they are better now thankfully!), and pyrochild&#8217;s computer refusing to work with a test build (pyrochild is an active and popular member of <a href="http://paintdotnet.forumer.com" target="_blank">the Paint.NET forum</a>). Have no fear though, it&#8217;s almost ready to go! I had a few code changes since Beta 2 so I decided to push out a Release Candidate instead of a &quot;Final&quot; build tonight. If all goes as planned, the final release will be pushed out this weekend.</p>
<p>As usual, you can get this build from the built-in updater (Help -&gt; Check for Updates), or by downloading it straight off the website: <a href="http://www.getpaint.net/">http://www.getpaint.net/</a></p>
<p>Changes since Beta 2: </p>
<ul>
<li>Fixed: Various UI issues with the Levels adjustment.</li>
<li>Fixed: When pasting text into the Text tool with Ctrl+V, it would hide the nub for moving the text.</li>
<li>Fixed: If the startup tool was set to the Zoom or Pan (Hand) tool, then the toolbar would render incorrectly.</li>
<li>Changed: Enabled some compiler settings which improve security via support for DEP (/nxcompat), and Windows Vista&#8217;s Address Space Layout Randomization feature (ASLR) (/dynamicbase).</li>
</ul>
<p>As an aside, on April 1st <a href="http://www.gimp.org" target="_blank">the GIMP website</a> had an interesting version of the Wilber image, and I have adopted it for my forum avatar for the time being &#8230;</p>
<p><img src="http://www.getpaint.net/misc/wilber_ack.png" /></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.getpaint.net/2008/04/07/paintnet-v330-release-candidate-1-is-now-available/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Validating .NET/RESX translations easily with LINQ-to-XML</title>
		<link>http://blog.getpaint.net/2008/04/05/validating-netresx-translations-easily-with-linq-to-xml/</link>
		<comments>http://blog.getpaint.net/2008/04/05/validating-netresx-translations-easily-with-linq-to-xml/#comments</comments>
		<pubDate>Sun, 06 Apr 2008 00:21:32 +0000</pubDate>
		<dc:creator>Rick Brewster</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.getpaint.net/2008/04/05/validating-netresx-translations-easily-with-linq-to-xml/</guid>
		<description><![CDATA[One of the tools I finally decided to sit down and write was a &#34;ResXCheck&#34; utility. RESX files hold the string resources for many .NET programs, including Paint.NET. At build time, these are compiled into a binary format and then stored in a DLL, or a RESOURCES file (Paint.NET uses the latter). The binary format [...]]]></description>
			<content:encoded><![CDATA[<p>One of the tools I finally decided to sit down and write was a &quot;ResXCheck&quot; utility. RESX files hold the string resources for many .NET programs, including Paint.NET. At build time, these are compiled into a binary format and then stored in a DLL, or a RESOURCES file (Paint.NET uses the latter). The binary format is simply more efficient to access at run-time, and <a href="http://msdn2.microsoft.com/en-us/library/ccec7sz1(VS.80).aspx" target="_blank">resgen.exe</a> can even be used to convert between the two formats.</p>
<p>Essentially, a RESX is an XML files with a big list of name/value pairs. I will refer to these as <strong>string names</strong>, and <strong>string values</strong>. The XML looks like this:</p>
<blockquote><p><font face="courier new">&lt;data name=&quot;GradientTool.HelpText&quot;&gt;       <br />&#160; &lt;value&gt;<strong>Click and drag to start drawing. Holding shift constrains the angle. Right mouse button reverses colors.</strong>&lt;/value&gt;        <br />&lt;/data&gt;        <br />&lt;data name=&quot;GradientTool.HelpText.WhileAdjusting.Format&quot;&gt;        <br />&#160; &lt;value&gt;<strong>Offset: {0}{1} x {2}{3}, Length: {4} {5}, Angle: {6}&#176;. Holding other mouse button will move both nubs.</strong>&lt;/value&gt;        <br />&lt;/data&gt;</font></p>
</blockquote>
<p>The RESX files for translations are in the same format and should have all the same string names. The &lt;value&gt; elements should have different text in them however (it should be translated!). At runtime, both the translated and original RESOURCES files are needed &#8212; the latter is used as a &quot;fallback&quot; in case a string is not defined in the translation. Whether this is an error is up to your translation process and resource loader code. Sometimes you want that behavior, such as if you have the base &quot;EN&quot; (English) translation and then some strings must vary for &quot;EN-US&quot; (U.S. English) or &quot;EN-GB&quot; (British English). You can store common string definitions in &quot;EN&quot;, although usually it is better to completely duplicate the content, and use a tool to maintain the duplication.</p>
<p>Having worked with RESX files on Paint.NET and elsewhere, the following problems come up:</p>
<ol>
<li><strong>You could use the same string name twice.</strong> The RESX compiler will simply grab one of them, either the first or the last (I can&#8217;t remember). This is a problem if you go to update a string later and you change the wrong one. Then, your changes might not show up in the main program and you won&#8217;t know why. And you&#8217;ll have a heck of a time figure it out.      </li>
<li><strong>A translation could be missing some string names</strong>. If this happens, generally the fallback text (usually English) will show up. That is probably not the desired behavior, although in Paint.NET there are a few places where it&#8217;s okay. Translating a technical acronym for a pixel format, such as &quot;A8R8B8G8&quot;, isn&#8217;t really necessary.      </li>
<li><strong>A translation could have extra string names defined.</strong> This is likely to happen if strings are removed from the original RESX, but the translation hasn&#8217;t been updated yet. This will not cause any errors, it is just extra cruft that can accumulate if you don&#8217;t pay attention (most professional translation teams have tools which handle this case automatically).      </li>
<li><strong>A string value could have &quot;malformed&quot; formatting tags</strong>. In the XML listed above, the second text has formatting tags such as {0} through {6}. These represent values which must be supplied at runtime by the application. There are two hazards here. One is that you could have formatting that String.Format(&#8230;) doesn&#8217;t like, such as having a { without a closing }, or vice versa. The other hazard is if a translation defines <em>extra</em> formatting tags, such as if a {7} was added above. Then your application will crash when it goes to apply formatting to that string. This is mostly a problem when strings have not yet been updated for a newer version. The translation may define <em>fewer</em> formatting tags, and this may or may not be an error. You may have a formatting tag that represents a piece of text that is not necessary to display in a particular translation.</li>
</ol>
<p>Luckily, all of these can be checked for with some simple automation, which is what I have done with ResXCheck. I will be including its code in the next Paint.NET source code drop (for v3.30), and plan to tag it as &quot;public domain&quot; (just the utility, not Paint.NET itself). In the mean time, here&#8217;s a little utility function that can help you load a RESX and convert it to an IEnumerable of type KeyValuePair&lt;string, string&gt; (duplicate string names are not removed &#8212; this is important for being able to check #1 above).</p>
<blockquote><p><font face="courier new">using System;       <br />using System.Collections.Generic;        <br />using System.Linq;        <br />using System.Xml.Linq;        <br />using System.Xml.XPath;        </p>
<p>// Given a file name for a RESX, returns a non-consolidated list of string name/value pairs.        <br /></font><font face="courier new">IEnumerable&lt;KeyValuePair&lt;string, string&gt;&gt; FromResX(string resxFileName)       <br />{        <br />&#160;&#160;&#160; XDocument xDoc = XDocument.Load(resxFileName); </font></p>
<p><font face="courier new">&#160;&#160;&#160; var query = from xe in xDoc.XPathSelectElements(&quot;/root/data&quot;)       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; let attributes = xe.Attributes()        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; let name = (from attribute in attributes        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; where attribute.Name.LocalName == &quot;name&quot;        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; select attribute.Value)        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; let elements = xe.Elements()        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; let value = (from element in elements        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; where element.Name.LocalName == &quot;value&quot;        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; select element.Value)        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; select new KeyValuePair&lt;string, string&gt;(name.First(), value.First()); </font></p>
<p><font face="courier new">&#160;&#160;&#160; return query;       <br />}</font></p>
</blockquote>
<p>To check for duplicates, a simple query such as the following is all that&#8217;s needed:</p>
<blockquote><p><font face="courier new">var resx = FromResX(&quot;strings.resx&quot;);       </p>
<p>var dupeItems = resx.ToLookup(kv =&gt; kv.Key, kv =&gt; kv.Pair)&#160;&#160;&#160;&#160;&#160; // 1        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .Where(item =&gt; item.Take(2).Count() &gt; 1)&#160;&#160;&#160; // 2        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .SelectMany(item =&gt; item.Select(val =&gt;&#160;&#160;&#160;&#160;&#160; // 3        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; </font><font face="courier new">new KeyValuePair&lt;string, string&gt;(item.Key, val.Value));       </p>
<p></font><font face="courier new">// 1 &#8212; converts from KeyValuePair&lt;string, string&gt;[] to IEnumerable&lt;IGrouping&lt;string, string&gt;&gt; &#8212; essentially a list of keys, each of which has a nested list of values       <br />// 2 &#8212; finds any key which has 2 or more values in it. the &quot;Take(2)&quot; is an optimization        <br />// 3 &#8212; convert back to a list of key,value pairs (probably not necessary if you use the &quot;T-SQL&quot; style syntax)</font></p>
</blockquote>
<p>You can then do a foreach() over this virtual list and print out the key,value pairs. I could have written that query using the more succinct &quot;T-SQL&quot; style query syntax, but I hadn&#8217;t yet learned it when I wrote that part of the code. ResXCheck was a little project I took on to force myself to learn more about LINQ. Surprisingly, it only took about 2 minutes to learn the more compact query syntax.</p>
<p>I&#8217;m quite happy with LINQ. It&#8217;s letting me do some powerful data manipulation with very succinct, expressive code. And it&#8217;s very simple! I&#8217;ve already found a few mistakes in my RESX files, and they will be easy to fix. This tool will also help volunteer translators who <a href="http://paintdotnet.forumer.com/viewforum.php?f=14" target="_blank">publish their translations on the forum</a>. I know it is hard to validate these things for correctness sometimes, <em>especially</em> for problem #4 list above.</p>
<p>Oh and for fun I made the utility parallelize the processing so you can validate &quot;N&quot; number of translations at the same time. Sadly, on my quad-core box it only dropped the validation time from 560 milliseconds down to 300. If I only have 50 more translations, then I could <em>really</em> stress it! <img src='http://getpaint.setupmyblog.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://blog.getpaint.net/2008/04/05/validating-netresx-translations-easily-with-linq-to-xml/feed/</wfw:commentRss>
		</item>
		<item>
		<title>A simple C# / LINQ trick shown with Console.ReadLine()</title>
		<link>http://blog.getpaint.net/2008/03/31/a-simple-c-linq-trick-shown-with-consolereadline/</link>
		<comments>http://blog.getpaint.net/2008/03/31/a-simple-c-linq-trick-shown-with-consolereadline/#comments</comments>
		<pubDate>Tue, 01 Apr 2008 03:07:17 +0000</pubDate>
		<dc:creator>Rick Brewster</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.getpaint.net/2008/03/31/a-simple-c-linq-trick-shown-with-consolereadline/</guid>
		<description><![CDATA[I haven&#8217;t been working on Paint.NET v4.0 at all lately, but I have been reading, learning, and prototyping about a whole sleuth of things related to functional and asynchronous programming. One such topic is that of monads, which I have a blog entry almost completed on. However, I wanted to get a quick blog post [...]]]></description>
			<content:encoded><![CDATA[<p>I haven&#8217;t been working on Paint.NET v4.0 at all lately, but I have been reading, learning, and prototyping about a whole sleuth of things related to functional and asynchronous programming. One such topic is that of monads, which I have a blog entry almost completed on. However, I wanted to get a quick blog post to show something simple yet powerful. LINQ brings a lot of power to IEnumerable&lt;T&gt;, and in this case we&#8217;re going to map the console input into an IEnumerable&lt;T&gt;. Then we&#8217;ll use standard LINQ methods to only display those lines of text that have &quot;.dll&quot; in them. It&#8217;s a contrived example, but it&#8217;s easy enough to find more useful applications of this. I have to go in a minute, so I&#8217;ll throw this code up on the blog and let the conversation be driven from the comments.</p>
<blockquote><p><font face="courier new">public static class Extensions       <br />{        <br />&#160;&#160;&#160; public static IEnumerable&lt;T&gt; ToList&lt;T&gt;(this Func&lt;T&gt; sampleFn)        <br />&#160;&#160;&#160; {        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; while (true)        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; T val = sampleFn();        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; yield val;        <br /></font><font face="courier new">&#160;&#160;&#160;&#160;&#160;&#160;&#160; }       <br />&#160;&#160;&#160; }        </p>
<p>&#160;&#160;&#160; public static IEnumerable&lt;T&gt; While&lt;T&gt;(this IEnumerable&lt;T&gt; list, Func&lt;T, bool&gt; whileFn)        <br />&#160;&#160;&#160; {        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; foreach (T item in list)        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (whileFn(item))        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; yield return item;        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; else        <br /></font><font face="courier new">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; yield break;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }        <br />&#160;&#160;&#160; }        </p>
<p>&#160;&#160;&#160; public static void Execute&lt;T&gt;(this IEnumerable&lt;T&gt; list)        <br />&#160;&#160;&#160; {        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; foreach (T item in list) { }        <br />&#160;&#160;&#160; }        </p>
<p>&#160;&#160;&#160; public static IEnumerable&lt;T&gt; Do&lt;T&gt;(this IEnumerable&lt;T&gt; list, Action&lt;T&gt; action)        <br />&#160;&#160;&#160; {        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; foreach (T item in list)        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; {        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; action(item);        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; yield item;        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }        <br />&#160;&#160;&#160; }        <br />}        </p>
<p>class Program        <br />{        <br />&#160;&#160;&#160; public static void Main()        <br />&#160;&#160;&#160; {        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; Func&lt;string&gt; inputSampleFn = Console.ReadLine;        </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; var input = inputSampleFn.ToList()        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .While(s =&gt; s != null);        </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; var query = input.Select(s =&gt; s.ToLower())        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .Where(s =&gt; s.Contains(&quot;.dll&quot;));        </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; query.Do(s =&gt; Console.WriteLine(s))        <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .Execute();        <br />&#160;&#160;&#160; }        <br />}</font></p>
</blockquote>
<p>Now, compile this to something like &quot;dllFilter.exe&quot;. Then run, at the command line, &quot;dir c:\windows | filter.exe&quot;. Neat <img src='http://getpaint.setupmyblog.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://blog.getpaint.net/2008/03/31/a-simple-c-linq-trick-shown-with-consolereadline/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Paint.NET just can’t satisfy an 8-core Opteron</title>
		<link>http://blog.getpaint.net/2008/03/23/paintnet-just-can%e2%80%99t-satisfy-an-8-core-opteron/</link>
		<comments>http://blog.getpaint.net/2008/03/23/paintnet-just-can%e2%80%99t-satisfy-an-8-core-opteron/#comments</comments>
		<pubDate>Sun, 23 Mar 2008 23:50:11 +0000</pubDate>
		<dc:creator>Rick Brewster</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.getpaint.net/2008/03/23/paintnet-just-can%e2%80%99t-satisfy-an-8-core-opteron/</guid>
		<description><![CDATA[Just to make sure, I went and downloaded the trial version of Windows Server 2008 from the Microsoft website. I installed it on this 8-core Opteron workstation with 8 GB of RAM that AMD sent me awhile ago for doing multithreading and performance scaling stuff. I installed version 3.22, then updated it to 3.30 Beta [...]]]></description>
			<content:encoded><![CDATA[<p>Just to make sure, I went and downloaded the <a href="http://www.microsoft.com/windowsserver2008/en/us/try-it.aspx">trial version of Windows Server 2008</a> from the Microsoft website. I installed it on this 8-core Opteron workstation with 8 GB of RAM that AMD sent me awhile ago for doing multithreading and performance scaling stuff. I installed version 3.22, then updated it to 3.30 Beta 2 to make sure that would work, and sure enough it does!
</p>
<p style="margin-left: 36pt"><a href="http://www.getpaint.net/users/rick/blog/20080323_server2008and8cores.png"><img src="http://www.getpaint.net/users/rick/blog/20080323_server2008and8cores_thumb.png" /><br/><em>(click for full size, 1600&#215;1200)</a></em>
	</p>
<p>I&#8217;m not surprised, but I felt the need to verify this anyway <img src='http://getpaint.setupmyblog.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> I&#8217;ll make sure the website lists Windows Server 2008 as a supported OS on the next update.
</p>
<p>As an interesting side note, having access to systems with 4 and 8 cores has brought up an interesting performance problem in Paint.NET, and key insights for optimization and design going forward. In the past I have done a lot of work to ensure that certain key rendering code is multithreaded, and as such going from dual- to quad- to octa-core has yielded healthy performance gains. Even on these systems, quite often, I just can&#8217;t get CPU usage all the way up to 100%! Just look at the screenshot above: even on a very compute-intensive effect such as Julia Fractal, at highest quality, on a 16,000 x 9,000 pixel canvas (yes that&#8217;s 144 megapixels), the CPU usage is bouncing around and clearly not anywhere close to full performance. I guess the silver lining here is that power consumption is a little lower as a result, eh?
</p>
<p>Even on <a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16819115011">my personal quad core desktop</a>, I&#8217;m lucky if rendering a large gradient can hit 65% CPU usage. There&#8217;s a lot of wasted performance by having the rendering pipeline set up the way it us. Hopefully my talks with the <a href="http://blogs.msdn.com/pfxteam/">.NET Parallels Extensions team</a> will help me to improve this situation, while also putting some extra &#8220;real world&#8221; code in <em>their</em> laps to play with. With the current design, I wouldn&#8217;t be surprised if Paint.NET could barely keep a 64-core system more than 15% busy.
</p>
<p>Also, I&#8217;d like to point out another exciting project related to things like concurrency and parallelism: <a href="http://labs.live.com/volta/">Microsoft Live Labs Volta</a>. I had a meeting with them last Tuesday and their approach to asynchronous, stream/event-based programming just blew my mind. Their project as a whole is actually targeted for some kind of simplified web cloud tier-distributed development*, <em>but</em> they have a concurrency runtime which could prove extremely valuable for the two rich client/desktop applications that I currently work on**. I&#8217;m very excited – anytime somebody sends me a meeting request titled, &#8220;Discuss monads and composition in the context of asynchronous computations,&#8221; I am reassured that there are still smart people out there working on cool things. I&#8217;m not sure how much I can discuss publicly about their stuff, but once I get any kind of green light I&#8217;m sure I&#8217;ll start gushing. I&#8217;ll leave you with one quote from the discussion that I actually made sure to write down, as it embodies something I&#8217;ve been struggling and searching for with respect to this type of coding:
</p>
<p><em>&#8220;By construction, these things are correct.&#8221;<br />
</em></p>
<p>More on what that actually means later. I&#8217;ve got a lot of thoughts on <a href="http://en.wikipedia.org/wiki/Asynchronous_programming">parallel/asynchronous</a>, <a href="http://en.wikipedia.org/wiki/Concurrent_programming">concurrent</a>, and <a href="http://en.wikipedia.org/wiki/Functional_programming">functional</a> programming and how it relates to <a href="http://en.wikipedia.org/wiki/Multithreading">multithreading</a>, <a href="http://en.wikipedia.org/wiki/Correctness">correctness</a>, and performance. Hopefully I can put some of that into writing!
</p>
<p><span style="font-size:10pt"><em>* This is not an exact definition <img src='http://getpaint.setupmyblog.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> <br/>** That would be 1) Paint.NET, and 2) the other one that I work on at Microsoft (&#8221;my day job&#8221;).</em></span></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.getpaint.net/2008/03/23/paintnet-just-can%e2%80%99t-satisfy-an-8-core-opteron/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Thoughts on Component Isolation for Paint.NET v4.0</title>
		<link>http://blog.getpaint.net/2008/03/16/thoughts-on-component-isolation-for-paintnet-v40/</link>
		<comments>http://blog.getpaint.net/2008/03/16/thoughts-on-component-isolation-for-paintnet-v40/#comments</comments>
		<pubDate>Mon, 17 Mar 2008 01:29:35 +0000</pubDate>
		<dc:creator>Rick Brewster</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.getpaint.net/2008/03/16/thoughts-on-component-isolation-for-paintnet-v40/</guid>
		<description><![CDATA[I&#8217;ve been doing a lot of thinking about how Paint.NET needs to change or evolve before it hits version 4.0, and the big thing that keeps coming up is isolation. Right now, the control flow and error handling in Paint.NET lets one component crash or corrupt the whole program. This could be code sitting in [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been doing a lot of thinking about how Paint.NET needs to change or evolve before it hits version 4.0, and the big thing that keeps coming up is <em>isolation</em>. Right now, the control flow and error handling in Paint.NET lets one component crash or corrupt the whole program. This could be code sitting in a plugin that causes an unhandled exception in a way that my try/catch harnesses don&#8217;t handle, or it could be a COM component loaded by Windows common file Open/Save dialogs that crashes or locks up. Or maybe a piece of code doesn&#8217;t throw an exception but it does corrupt some other data, and then that corruption causes a crash <em>later</em>. And then that gets attributed to the wrong component. GDI+ seems to like doing that a lot.</p>
<p>Not only does Paint.NET need to be protected by crashing or &quot;bad&quot; plugins, but it needs to be protected from itself! Things like the Open/Save and Print dialogs are a good example, especially because they deal with external, &quot;native&quot; components. Some people have shell extensions installed that cause the Open/Save dialogs to fail, hang, or crash, and this breaks Paint.NET! This could be something as seemingly benign as a thumbnail provider for DivX videos. Yes, I get crash reports on the forum for that every so once in awhile. Why should Paint.NET have to crash if there&#8217;s a corrupted DivX video in a folder you&#8217;ve navigated to via File-&gt;Open? I&#8217;d rather crash just the Open dialog, detect the error without corruption in the main process, and then re-run the dialog in some kind of compatibility or reduced functionality mode. At least that way if it corrupts memory, then that corruption only affects the hosting process that it lives in.</p>
<p>So, there needs to be a really easy way for me to write a component in Paint.NET &#8212; whether it&#8217;s &quot;built-in&quot; or a &quot;plug-in&quot; &#8212; that can be isolated and not cause problems for the rest of the application. This means I want to host it in an external process and communicate with it, and then have a layer of abstraction in place for creating a &quot;remote&quot;, isolated object.</p>
<p>The new <a href="http://blogs.msdn.com/clraddins" target="_blank">System.AddIn</a> namespace in .NET 3.5 provides a whole system for this, but honestly I find it to be <em>way </em>too complex, confusing, and involved. They have host views, add-in views, host-side adapter, add-in side adapters, etc. It&#8217;s a bunch of terminology that seems invented &#8212; why not just say client and server, proxy, tear-off, etc.? Every time you add a new interface or type you have to add code that spans at least 3 assemblies. Plus, they do not provide the ability for me to be running in a 64-bit process and have it communicate with an add-in that requires 32-bit hosting. This is important for certain plug-ins which are doing native interop to DLL&#8217;s that are only available in 32-bit (the <a href="http://nemesis.thewavelength.net/index.php?p=50" target="_blank">VTF File Type Plugin</a> comes to mind). They also do not provide the ability to activate an add-in that needs to run at a <em>higher</em> security level &#8212; for instance, if I want to require my Settings dialog to run with administrator privilege. I don&#8217;t want to have to deal with UAC at the code level, and would prefer it to be abstracted away. They also enforce a specific directory structure, and so on &#8230; anyway, it&#8217;s just too much and also too little.</p>
<p>.NET Remoting isn&#8217;t really an end-to-end solution for what I need, as it does not provide a hosting process. COM provides component hosting in an external process, but it seems like <em>major </em>overkill to register my .NET components as COM-proxied objects. Windows Communication Foundation is more for &quot;enterprise&quot; remoting and communication scenarios.</p>
<p>So it looks like I may have to roll my own solution, using System.IO.Pipes (actually I may prefer an HWND and WM_COPYDATA for marshaling) and borrowing heavily from the lessons put forth by System.AddIn (they have a very good story for versioning), and learned from my inbox full of crash logs. In any case it must be something that is easy to use by both the &quot;host&quot; and the &quot;add-in&quot; (or &quot;built-in&quot; :)). I&#8217;m thinking code generation will be very handy here in order to be able to declare an isolatable interface and then auto-generate the local tear-off/proxy, client stub, server stub, and registration glue.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.getpaint.net/2008/03/16/thoughts-on-component-isolation-for-paintnet-v40/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
