<?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>Ardamis &#187; application</title>
	<atom:link href="http://www.ardamis.com/tag/application/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.ardamis.com</link>
	<description>Ardamis is a blog about web development and technology in general.</description>
	<lastBuildDate>Sat, 04 Feb 2012 15:26:27 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>A common sense workaround for installing SetPoint in a VMware virtual machine</title>
		<link>http://www.ardamis.com/2011/11/01/setpoint-functionality-in-vmware/</link>
		<comments>http://www.ardamis.com/2011/11/01/setpoint-functionality-in-vmware/#comments</comments>
		<pubDate>Tue, 01 Nov 2011 20:47:56 +0000</pubDate>
		<dc:creator>ardamis</dc:creator>
				<category><![CDATA[Hardware]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[Nonsense]]></category>
		<category><![CDATA[application]]></category>
		<category><![CDATA[troubleshooting]]></category>
		<category><![CDATA[vmware]]></category>

		<guid isPermaLink="false">http://www.ardamis.com/?p=1749</guid>
		<description><![CDATA[A workaround to installing Logitech's SetPoint utility in order to map mouse buttons on a Windows XP virtual machine running in VMWare Workstation 7.]]></description>
			<content:encoded><![CDATA[<p>I have a Windows XP guest running in VMWare Workstation 7 on a Windows 7 Ultimate host machine.  This is working pretty well.  The XP guest is nice and responsive.  I have only one gripe.  I&#8217;d like all of the buttons on my Logitech MX510 (the best mouse ever) to be mappable in the guest.</p>
<p>Starting from square one, I decided to try installing the current version of SetPoint in the guest OS.  The installation went fine, but the usual functionality of the SetPoint settings utility was absent.</p>
<div id="attachment_1795" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.ardamis.com/wp-content/uploads/2011/10/setpoint-vmware.png"><img src="http://www.ardamis.com/wp-content/uploads/2011/10/setpoint-vmware-300x227.png" alt="SetPoint Settings in an XP virtual machine" title="SetPoint Settings in an XP virtual machine" width="300" height="227" class="size-medium wp-image-1795" /></a><p class="wp-caption-text">SetPoint Settings in an XP virtual machine</p></div>
<p>As shown in the screenshot, the SetPoint Settings utility displays only the Tools tab.  It is missing the My Mouse tab (and if a keyboard were installed, I presume it would be missing the Keyboard tab, too).</p>
<p>After some Googling around, it appears to be a due to the way VMware approximates the physical mouse.  VMware seems to treat USB mice connected to the host as PS/2 devices in the guest.  SetPoint, then, doesn&#8217;t detect any Logitech hardware that it can configure.</p>
<p>The question of how to obtain SetPoint functionality in virtual machines is one that has been asked many, many times before, without a satisfactory answer.  More on that in a little bit.</p>
<h2>The best work around</h2>
<p>Thankfully, it seems that, at least in the case of a Windows host and a Windows guest, installing SetPoint inside the virtual machine is not necessary.  Installing it on the host seems to make all of the functionality available in the guest.  This is the solution that I&#8217;m implementing now, and it is what I would recommend, provided you have rights to install software on the host. </p>
<h2>Paths to follow if you want to pursue installing SetPoint inside a VMware virtual machine</h2>
<p>I applaud your courage.  There are a few settings that can be tweaked that may get you closer to a working installation.</p>
<p><strong>Possible setting number one</strong></p>
<p>From the post at http://coreygilmore.com/blog/2008/04/30/better-multi-button-mouse-support-with-vmware-fusion-and-workstation/</p>
<p>Add the following line to the virtual machine&#8217;s .vmx file:</p>
<pre class="brush: plain; title: ; notranslate">
mouse.vusb.enable = &quot;TRUE&quot;
</pre>
<p>From what I can tell, this setting allows me to use the Forward and Back buttons on the mouse, but does not make the mouse detectable by SetPoint.  The remaining mouse buttons do nothing.</p>
<p><strong>Possible setting number two</strong></p>
<p>From the post at <a href="http://superuser.com/questions/35830/back-forward-mouse-buttons-do-not-work-in-vmware-workstation-6-5-guest-os/304583#304583">http://superuser.com/questions/35830/back-forward-mouse-buttons-do-not-work-in-vmware-workstation-6-5-guest-os/304583#304583</a></p>
<p>The solution given (which did not work for me) is to:</p>
<p>First add the following line to the virtual machine&#8217;s .vmx file:</p>
<pre class="brush: plain; title: ; notranslate">
usb.generic.allowHID = &quot;TRUE&quot;
</pre>
<p>An explanation of what this does, by a VMware associate, can be found in the thread at <a href="http://communities.vmware.com/thread/110919?start=15&#038;tstart=0">http://communities.vmware.com/thread/110919?start=15&#038;tstart=0</a></p>
<blockquote><p>
If you&#8217;re feeling really adventurous and/or desperate, you can take out the mouse.vusb.enable line and add this option instead:</p>
<p>usb.generic.allowHID = &#8220;TRUE&#8221;</p>
<p>Then, you&#8217;ll notice that your main mouse and keyboard (if they are USB) are available to pass through into the guest via the USB devices menu.</p>
<p>The dangerous part here is that once you pass through the mouse, it is actually disconnected from the host, so you won&#8217;t be able to ungrab from the guest just by mousing out of the Fusion window. You can still ungrab with the keyboard (ctrl-cmd I believe is the shortcut?). If you actually pass through your keyboard and your mouse, you&#8217;ll be stuck in the guest and you&#8217;ll have to shut it down (or worse, reboot your physical machine).
</p></blockquote>
<p>This sounded like a great idea, and I was willing to set up a second, PS/2 mouse to control just the host, if necessary.  Without connecting a second mouse, I tried passing the Logitech mouse as a USB device to the VM, just as I would an external hard drive, but VMware prevented this, with a warning message:</p>
<blockquote><p>
[Machine Name] &#8211; VMware Workstation<br />
Cannot connect &#8220;Logitech USB-PS/2 Optical Mouse&#8221; to this virtual machine. The host requires this device for input.<br />
[OK]
</p></blockquote>
<p>The second step would have been to go into Device Manager, click Actions, and then choose &#8220;Scan for hardware changes&#8221;.</p>
<p>I didn&#8217;t get to the second step, as I was too lazy to track down a PS/2 mouse to keep attached to the host, and I still wanted to find a software solution.  I suspect, though, that this would be were to begin, were I to need to get SetPoint running in the guest OS.</p>
<h2>Summary</h2>
<p>While I wasn&#8217;t able to figure out how to install SetPoint on a guest OS, the workaround of installing SetPoint on the host OS seems to accomplish my goal.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ardamis.com/2011/11/01/setpoint-functionality-in-vmware/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Dreamweaver open files in code view</title>
		<link>http://www.ardamis.com/2011/10/01/dreamweaver-open-files-in-code-view/</link>
		<comments>http://www.ardamis.com/2011/10/01/dreamweaver-open-files-in-code-view/#comments</comments>
		<pubDate>Sun, 02 Oct 2011 04:47:00 +0000</pubDate>
		<dc:creator>ardamis</dc:creator>
				<category><![CDATA[Web Site Dev]]></category>
		<category><![CDATA[application]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.ardamis.com/?p=1745</guid>
		<description><![CDATA[A simple registry merge can fix Dreamweaver's default setting of opening .php and .asp files in Design view, instead of Code view.]]></description>
			<content:encoded><![CDATA[<p>If there is one thing that bothers me about Dreamweaver&#8217;s default settings (other than its annoying habit of <a href="http://www.ardamis.com/2009/09/30/dreamweaver-rewriting-valid-code-as-camelcase/">rewriting valid code as camelCase</a>), it is that double-clicking .php and .asp files launches Dreamweaver in Design view.  Who on earth thinks that people generally want to open these kinds of files in anything other than code view?</p>
<p>Well, thankfully, this ridiculous behavior can be changed with a quick registry tweak.  Just add an extension to the &#8220;Open As Text&#8221; value to cause Dreamweaver to always open that file type in code view.  The .reg file below assumes you&#8217;re running Dreamweaver CS5.</p>
<pre class="brush: plain; title: ; notranslate">
Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Adobe\Dreamweaver CS5\Helper Applications Preferences]
&quot;Open As Text&quot;=&quot;.js .asa .css .cs .config .inc .txt .as .asc .asr .vb .htaccess .htpasswd .php .asp .html&quot;
</pre>
<p>It would be nice if Dreamweaver were smart enough to realize that, if your preferred layout is Coder, any file should be opened in Code view.</p>
<p>The link below explains how to do the same thing via Edit | Preferences.</p>
<p>Source: <a href="http://help.adobe.com/en_US/dreamweaver/cs/using/WSc78c5058ca073340dcda9110b1f693f21-7be2a.html">Open files in Code view by default</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.ardamis.com/2011/10/01/dreamweaver-open-files-in-code-view/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>A cache-friendly method for reducing WordPress comment spam</title>
		<link>http://www.ardamis.com/2011/08/27/a-cache-proof-method-for-reducing-comment-spam/</link>
		<comments>http://www.ardamis.com/2011/08/27/a-cache-proof-method-for-reducing-comment-spam/#comments</comments>
		<pubDate>Sat, 27 Aug 2011 08:34:53 +0000</pubDate>
		<dc:creator>ardamis</dc:creator>
				<category><![CDATA[Nonsense]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Web Site Dev]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[application]]></category>
		<category><![CDATA[blogging]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[comment spam]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[spam]]></category>
		<category><![CDATA[templates]]></category>
		<category><![CDATA[themes]]></category>
		<category><![CDATA[web app]]></category>
		<category><![CDATA[xhtml]]></category>

		<guid isPermaLink="false">http://www.ardamis.com/?p=1673</guid>
		<description><![CDATA[A safe-for-cached-pages method of filtering out spam comments by requiring at least some time to have passed between the time the page is loaded and the form is submitted.]]></description>
			<content:encoded><![CDATA[<p>In the endless battle against WordPress comment spam, I&#8217;ve developed and then refined a few different methods for preventing spam from getting to the database to begin with.  My philosophy has always been that a human visitor and a spam bot behave differently (after all, we&#8217;re not dealing with <a href="http://en.wikipedia.org/wiki/Do_Androids_Dream_of_Electric_Sheep%3F#Androids">Nexus-6 model androids</a> here), and an effective spam-prevention method should be able to recognize the differences.  I also have a dislike for CAPTCHA methods that require a human visitor to <em>prove</em>, via an intentionally difficult test, that they aren&#8217;t a bot.  The ideal method, I feel, would be invisible to a human visitor, but still accurately identify comments submitted by bots.</p>
<h2>A history of spam fighting</h2>
<p>The most successful and simple method I found was a server-side system for <a href="http://www.ardamis.com/2007/12/15/using-timestamps-to-reduce-wordpress-comment-spam/">reducing comment spam by using a handshake method involving timestamps</a> on hidden form fields.  The general idea was that a bot would submit a comment more quickly than a human visitor, so if the comment was submitted too soon after the page was loaded, it was rejected.  A human caught in this trap would be able to click the Back button on the browser to resubmit.  This had proven to be very effective on ardamis.com, cutting the number of <a href="http://www.ardamis.com/2010/08/09/reducing-wordpress-spam-comments/">spam comments intercepted by Akismet per day to nearly zero</a>.  For a long time, the only problem was that it required modifying a core WordPress file, <strong>wp-comments-post.php</strong>.  Each time WordPress was updated, the core file was replaced.  If I didn&#8217;t then go back and make my modifications again, <a href="http://www.ardamis.com/2011/01/18/a-chart-illustrating-the-reduction-in-comment-spam-at-ardamis-com/">I would lose the spam protection</a> until I made the changes.  As it became easier to update WordPress (via the admin panel) and I updated it more frequently, editing the core file became more of a nuisance.</p>
<h2>A huge facepalm</h2>
<p>When Google began weighting page load times as part of its ranking algorithm, I implemented the <a href="http://wordpress.org/extend/plugins/wp-super-cache/" title="WP Super Cache">WP Super Cache</a> caching plugin on ardamis.com and configured it to use .htaccess and mod_rewrite to serve cache files.  Page load times certainly decreased, but the amount of spam detected by Akismet increased.  After a while, I realized that this was because the spam bots were submitting comments from static, cached pages, and the timestamps on those pages, which had been generated server-side with PHP, were already minutes old when the page was requested.  The form processing script, which normally rejects comments that are submitted too quickly to be written by a human visitor, happily accepted the timestamps.  Even worse, a second function of my anti-spam method also rejected comments that were submitted 10 minutes or more after the page was loaded.  Of course, most of the visitors were being served cached pages that were already more than 10 minutes old, so even legitimate comments were being rejected.  Using PHP to generate my timestamps obviously was not going to work if I wanted to keep serving cached pages.</p>
<h2>JavaScript to the rescue</h2>
<p>Generating real-time timestamps on cached pages requires JavaScript.  But instead of a reliable server clock setting the timestamp, the time is coming from the visitor&#8217;s system, which can&#8217;t be trusted to be accurate.  Merely changing the comment form to use JavaScript to generate the first timestamp wouldn&#8217;t work, because verifying a timestamp generated on the client-side against one generated with a server-side language would be disastrous.</p>
<p>Replacing the PHP-generated timestamps with JavaScript-generated timestamps would require substantial changes to the system.</p>
<p>Traditional client-side form validation using JavaScript happens when the form is submitted.  If the validation fails, the form is not submitted, and the visitor typically gets an alert with suggestions on how to make the form acceptable.  If the validation passes, the form submission continues without bothering the visitor.  To get our two timestamps, we can generate a first timestamp when the page loads and compare it to a second timestamp generated when the form is submitted.  If the visitor submits the form too quickly, we can display an alert showing the number of seconds remaining until the form can be successfully submitted.  This should hopefully be invisible to most visitors who choose to leave comments, but at the very least, far less irritating than a CAPTCHA system.</p>
<p>It took me two tries to get it right, but I&#8217;m going to discuss the less successful method first to point out its flaws.</p>
<h3>Method One (not good enough)</h3>
<p>Here&#8217;s how the original system flowed.</p>
<ol>
<li>Generate a first JS timestamp when the page is loaded.</li>
<li>Generate a second JS timestamp when the form is submitted.</li>
<li>Before the form is submitted, compare the two, and if enough time has passed, write a pre-determined passcode to a hidden INPUT element, then submit the form.</li>
<li>On the form processing page, use server-side logic to verify that the passcode is present and valid.</li>
</ol>
<p>The problem was that it seemed that certain bots could parse JavaScript enough to drop the pre-determined passcode into the hidden form field before submitting the form, circumventing the timestamps completely and defeating the system.  </p>
<p>It also failed to adhere to one of the basic tenants of form validation &#8211; that the input must be checked on both the client-side and the server-side.</p>
<h3>Method Two (better)</h3>
<p>Rather than having the server-side validation be merely a check to confirm that the passcode is present, method two goes back to comparing the timestamps a second time on the server side. Instead of a single hidden input, we now have two &#8211; one for each timestamp.  This is intended to prevent a bot from figuring out the ultimate validation mechanism by simply parsing the JavaScript.  Finally, the hidden fields are added to the form via jQuery, which makes it easier to implement and may act as another layer of obfuscation. </p>
<ol>
<li>Generate a first JS timestamp when the page is loaded and write it to a hidden form field.</li>
<li>Generate a second JS timestamp when the form is submitted and write it to a hidden form field.</li>
<li>Before the form is submitted, compare the two, and if enough time has passed, submit the form (client-side validation).</li>
<li>On the form processing page, use server-side logic to compare the timestamps a second time (server-side validation).</li>
</ol>
<p>The timestamp handshake works more like it did in the server-side-only method.  We still have to pass something from the comment form to the processing script, but it&#8217;s not too obvious from the HTML what is being done with it.</p>
<h2>The same downside plagues me</h2>
<p>Unfortunately, if we want to have any server-side validation at all, and we do, the core file <strong>wp-comments-post.php</strong> will still have to be modified.  In my experience, the system is not sufficiently effective using just client-side validation.  </p>
<h2>The code</h2>
<p>Two files must be modified to implement the validation.</p>
<p><strong>File 1: The theme&#8217;s comments.php file (older themes) or wp-includes\comment-template.php (newer themes)</strong></p>
<p>Your comment form lives somewhere.  My theme is based on Kubrick, the old default WordPress theme, and my comment form is in my theme folder, in a file named <strong>comments.php</strong>.  If your theme is newer and based on the current default theme, twentyeleven, the form is in <strong>wp-includes\comment-template.php</strong>.  If your theme isn&#8217;t based on either of these, all bets are off.  I know it&#8217;s confusing.  Sorry.</p>
<p>Add the JavaScript that creates and populates the timestamp fields.  Be sure to confirm that your comment form has an ID of <strong>commentform</strong>.  I&#8217;m using jQuery to help fire functions when the page loads.</p>
<pre class="brush: jscript; title: ; notranslate">
&lt;script type=&quot;text/javascript&quot; src=&quot;http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
$(document).ready(function(){
	ardGenTS1();
});

function ardGenTS1() {
	// prepare the form
	$('#commentform').append('&lt;input type=&quot;hidden&quot; name=&quot;ardTS1&quot; id=&quot;ardTS1&quot; value=&quot;1&quot; /&gt;');
	$('#commentform').append('&lt;input type=&quot;hidden&quot; name=&quot;ardTS2&quot; id=&quot;ardTS2&quot; value=&quot;1&quot; /&gt;');
	$('#commentform').attr('onsubmit', 'return validate()');
	// set a first timestamp when the page loads
	var ardTS1 = (new Date).getTime();
	document.getElementById(&quot;ardTS1&quot;).value = ardTS1;
}

function validate() {
	// read the first timestamp
	var ardTS1 = document.getElementById(&quot;ardTS1&quot;).value;
//	alert ('ardTS1: ' + ardTS1);
	// generate the second timestamp
	var ardTS2 = (new Date).getTime();
	document.getElementById(&quot;ardTS2&quot;).value = ardTS2;
//	alert ('ardTS2: ' + document.getElementById(&quot;ardTS2&quot;).value);
	// find the difference
	var diff = ardTS2 - ardTS1;
	var elapsed = Math.round(diff / 1000);
	var remaining = 10 - elapsed;
//	alert ('diff: ' + diff + '\n\nelapsed:' + elapsed);
	// check whether enough time has elapsed
	if (diff &gt; 10000) {
		// submit the form
		return true;
	}else{
		// display an alert if the form is submitted within 10 seconds
		alert(&quot;This site is protected by an anti-spam feature that requires 10 seconds to have elapsed between the page load and the form submission.\n\nPlease close this alert window.  The form may be resubmitted successfully in &quot; + remaining + &quot; seconds.&quot;);
		// prevent the form from being submitted
		return false;
	}
}
&lt;/script&gt;
</pre>
<p><strong>File 2: The wp-comments-post.php file</strong></p>
<p>The wp-comments-post.php file lives in the root of WordPress and handles the form processing.  We need to add a few lines that check the contents of our new validation input field.</p>
<p>Somewhere after line 53 or so (where <em>$comment_content</em> is defined), insert the following code.</p>
<pre class="brush: php; title: ; notranslate">
$ardTS1 = ( isset($_POST['ardTS1']) ) ? trim($_POST['ardTS1']) : 1;
$ardTS2 = ( isset($_POST['ardTS2']) ) ? trim($_POST['ardTS2']) : 2;
$ardTS = $ardTS2 - $ardTS1;

if ( $ardTS &lt; 10000 ) {
// If the difference of the timestamps is not more than 10 seconds, exit
    wp_die( __('&lt;strong&gt;ERROR&lt;/strong&gt;:  This site uses JavaScript validation to reduce comment spam.  Either your browser has JavaScript disabled, or the comment was not legitimately submitted.') );
}
</pre>
<p>That&#8217;s it.  Not so bad, right?</p>
<h2>Final thoughts</h2>
<p>One advantage to this method over the old PHP-only method is that even if the core file is replaced and the server-side validation is lost, the client-side validation continues to work, perhaps providing some measure of protection.</p>
<p>The method is safe for use with caching systems that create purely static, HTML pages.  I&#8217;ll follow up later and report on how effective it seems to be at stopping spam comments before they get to Akismet (and into the WordPress database).</p>
<p>Now, for a little extra protection, you can rename the <strong>wp-comments-post.php</strong> file and change the path in the comment form&#8217;s action attribute.  I&#8217;ve <a href="http://www.ardamis.com/2010/08/09/reducing-wordpress-spam-comments/">posted logs</a> showing that some bots just try to post spam directly to the <strong>wp-comments-post.php</strong> file, so renaming that file is an easy way to cut down on spam.  Just remember to come back and delete the <strong>wp-comments-post.php</strong> file each time you update WordPress.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ardamis.com/2011/08/27/a-cache-proof-method-for-reducing-comment-spam/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>OOP PHP Notes</title>
		<link>http://www.ardamis.com/2011/07/30/oop-php-notes/</link>
		<comments>http://www.ardamis.com/2011/07/30/oop-php-notes/#comments</comments>
		<pubDate>Sat, 30 Jul 2011 18:56:43 +0000</pubDate>
		<dc:creator>ardamis</dc:creator>
				<category><![CDATA[Nonsense]]></category>
		<category><![CDATA[Web Site Dev]]></category>
		<category><![CDATA[application]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[web app]]></category>

		<guid isPermaLink="false">http://www.ardamis.com/?p=1565</guid>
		<description><![CDATA[My notes on learning Object Oriented Programming in PHP.]]></description>
			<content:encoded><![CDATA[<p><strong>Overview of classes and objects</strong></p>
<p>Objects are the building blocks of the application (ie: the workers in a factory)<br />
Classes can be thought of as blueprints for the objects.  Classes describe the objects, which are created in memory.<br />
So, the programmer writes the classes and the PHP interpreter creates the objects from the classes.</p>
<p>A class may contain both variables and functions.<br />
A variable inside a class is called a <em>property</em>.<br />
A function inside a class is called a <em>method</em>.</p>
<p><strong>Instantiation</strong></p>
<p>To create an object, you instantiate a class (you create an instance of the class as an object).<br />
For example, if we have a class named &#8216;person&#8217; and want to instantiate it as the variable $oliver:</p>
<pre class="brush: php; title: ; notranslate">$oliver = new person();</pre>
<p>The variable $oliver is referred to as the &#8216;handle&#8217;.</p>
<p><strong>Accessing properties and methods</strong></p>
<p>To access the properties and methods of a class, we use the object&#8217;s handle, followed by the arrow operator &#8220;-&gt;&#8221;.<br />
For example, if our class has a method &#8216;get_name&#8217;, we can echo that to the page with:</p>
<pre class="brush: php; title: ; notranslate">echo $oliver-&gt;get_name();</pre>
<p>Note that there are no single or double quotes used in instantiating a class or accessing properties and methods of a class.</p>
<p><strong>Constructors</strong></p>
<p>A class may have a special method called a constructor.  The constructor method is called automatically when the object is instantiated.<br />
The constructor method begins with two underscores and the word &#8216;construct&#8217;:</p>
<pre class="brush: php; title: ; notranslate">function __construct($variable) { }</pre>
<p>One can pass values to the constructor method by providing arguments after the class name.<br />
For example, to pass the name &#8220;John Doe&#8221; to the constructor method in the &#8216;person&#8217; class:</p>
<pre class="brush: php; title: ; notranslate">$john = new person(&quot;John Doe&quot;);</pre>
<p><span style="color:#F00">!</span> If a constructor exists and expects arguments, you must instantiate the class with the arguments expected by the constructor.</p>
<p><strong>Access modifiers and visibility declarations</strong></p>
<p>Properties must, and methods may, have one of three access modifiers (visibility declarations): public, protected, and private.<br />
Public: can be accessed from outside the class, eg: $myclass->secret_variable;<br />
Protected: can be accessed within the class and by classes derived from the class<br />
Private: can be accessed only within the class</p>
<p>Declaring a property with var makes the property public.</p>
<p>Methods declared without an explicit access modifier are considered public.</p>
<p><span style="color:#F00">!</span> If you call a protected method from outside the class, any PHP output before the call is still processed, but you get an error message when the interpreter gets to that call:</p>
<pre class="brush: plain; title: ; notranslate">Fatal error: Call to protected method...</pre>
<p><strong>Inheritance</strong></p>
<p>Inheritance allows a child class to be created from a parent class, whereby the child has all of the public and protected properties and methods of the parent.</p>
<p>A child class <em>extends</em> a parent class:</p>
<pre class="brush: php; title: ; notranslate">class employee extends person {
}</pre>
<p>A child class can redefine/override/replace a method in the parent class by reusing the method name.</p>
<p><span style="color:#F00">!</span>  A child class&#8217;s method&#8217;s access modifier can not be more restrictive than that of the parent class.  For example, if the parent class has a public set_name() method and the child class&#8217;s set_name() method is protected, the class itself will generate a fatal error, and no prior PHP output will be rendered. (In the error below, <em>employee</em> is the child class to <em>person</em>):</p>
<pre class="brush: plain; title: ; notranslate">Fatal error: Access level to employee::set_name() must be public (as in class person) in E:\xampp\htdocs\tester\oop\class_lib.php on line 38</pre>
<p>To differentiate between a method in a parent class vs the method as redefined in a child class, one must specifically name the class that contains the method you want to call using the scope resolution operator (::):</p>
<pre class="brush: php; title: ; notranslate">person::set_name($new_name);</pre>
<p>The scope resolution operator allows access to static, constant, and overridden properties or methods of a class, generally, a parent class. This would be done inside the child class, after redefining a parent&#8217;s method of the same name.</p>
<p>It&#8217;s also possible to use &#8216;parent&#8217; to refer to the child&#8217;s parent class:</p>
<pre class="brush: php; title: ; notranslate">parent::set_name($new_name);</pre>
<p>(I&#8217;m still a bit vague on this and am looking for examples of situations in which this would be used.)</p>
<p><strong>Classes inside classes</strong></p>
<p>Just as it&#8217;s possible to instantiate a class and use the object in a view file, it&#8217;s possible to instantiate an object and call its methods from inside another class.</p>
<p><strong>Static properties and methods</strong></p>
<p>Declaring class properties or methods as static makes them accessible without needing an instantiation of the class. A property declared as static can not be accessed with an instantiated class object (though a static method can).</p>
<p><strong>Resources</strong><br />
<a href="http://us2.php.net/manual/en/language.oop5.php" title="http://us2.php.net/manual/en/language.oop5.php">http://us2.php.net/manual/en/language.oop5.php</a><br />
<a href="http://net.tutsplus.com/tutorials/php/oop-in-php/" title="http://net.tutsplus.com/tutorials/php/oop-in-php/">http://net.tutsplus.com/tutorials/php/oop-in-php/</a><br />
<a href="http://www.phpfreaks.com/tutorial/oo-php-part-1-oop-in-full-effect" title="http://www.phpfreaks.com/tutorial/oo-php-part-1-oop-in-full-effect">http://www.phpfreaks.com/tutorial/oo-php-part-1-oop-in-full-effect</a><br />
<a href="http://www.killerphp.com/tutorials/object-oriented-php/" title="http://www.killerphp.com/tutorials/object-oriented-php/">http://www.killerphp.com/tutorials/object-oriented-php/</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.ardamis.com/2011/07/30/oop-php-notes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Native Windows compressed folders utility fails, asks for 5.99 PB free space</title>
		<link>http://www.ardamis.com/2011/07/28/native-windows-compressed-folders-utility-5-99-pb/</link>
		<comments>http://www.ardamis.com/2011/07/28/native-windows-compressed-folders-utility-5-99-pb/#comments</comments>
		<pubDate>Thu, 28 Jul 2011 16:58:21 +0000</pubDate>
		<dc:creator>ardamis</dc:creator>
				<category><![CDATA[Nonsense]]></category>
		<category><![CDATA[application]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[troubleshooting]]></category>
		<category><![CDATA[Windows 7]]></category>

		<guid isPermaLink="false">http://www.ardamis.com/?p=1623</guid>
		<description><![CDATA[Extracting certain large zip files using Windows' native compression utility fails with a pretty neat error: You need an additional 5.99 PB to copy these files.]]></description>
			<content:encoded><![CDATA[<p>The other day, I needed to extract a 7 GB zip file containing a VMware virtual machine onto the hard drive of a nearly stock 64-bit Windows 7 Professional machine.  Because this machine did not have a third-party compression utility installed, I tried to extract it using Windows&#8217; native zip utility, called Compressed Folders.</p>
<p>This failed with a pretty neat error.</p>
<div id="attachment_1638" class="wp-caption aligncenter" style="width: 489px"><a href="http://www.ardamis.com/wp-content/uploads/2011/07/you-need-an-additional-5.99-PB-to-copy-these-files.jpg"><img src="http://www.ardamis.com/wp-content/uploads/2011/07/you-need-an-additional-5.99-PB-to-copy-these-files.jpg" alt="" title="You need an additional 5.99 PB to copy these files." width="479" height="299" class="size-full wp-image-1638" /></a><p class="wp-caption-text">You need an additional 5.99 PB to copy these files.</p></div>
<p>As you can see in the screenshot above, Windows reported that&#8230;</p>
<blockquote><p><strong>Copy Folder</strong></p>
<p>There is not enough space on Local Disk. You need an additional 5.99 PB to copy these files.</p>
<p>Local Disk<br />
Space free: 125 GB<br />
Total size: 232 GB</p>
<p>[Try Again] [Cancel]
</p></blockquote>
<p>I found the 5.99 petabyte requirement pretty amusing, but I was in a hurry, so I downloaded the excellent <a href="http://www.7-zip.org/">7-Zip</a>, unpacked the file, and set about building the vm.</p>
<p>I had meant to write a post about the error message, but some time passed and I forgot all about it.  Then, about three weeks later, someone else in the department tried to extract a copy of the file on a 32-bit Windows XP Professional machine and got the same error.  At that point, I had to investigate.</p>
<p>The Compressed Folders native Windows utility seemed to be unable to accurately calculate the free space needed to extract the file.  The file was admittedly pretty large, but was size the only reason?</p>
<p>According to the <a href="http://en.wikipedia.org/wiki/ZIP_(file_format)">Wikipedia page on ZIP files</a>, there are a number of known limitations of Compressed Folders.</p>
<blockquote><p>ZIP64, AES Encryption, split or spanned archives, and Unicode entry encoding are not known to be readable or writable by the Compressed Folders feature in Windows XP or Windows Vista.</p>
<p><a href="http://en.wikipedia.org/wiki/ZIP_(file_format)#Windows_compressed_folders">http://en.wikipedia.org/wiki/ZIP_(file_format)#Windows_compressed_folders</a></p></blockquote>
<p>None of these things applied to my file, but I found a rather telling and simultaneously ambiguous (go figure) KB article at Microsoft Support: <a href="http://support.microsoft.com/kb/301325">Compressed folder becomes corrupted when larger than 2 gigabytes</a>.</p>
<p>According to various threads, the popular theory is that the problem stems from size limitations on compressed files.  Windows Vista and later have a 4 GB limit (compressed and uncompressed size), while XP has a 2 GB limit.</p>
<p>Strangely, the same error appears before a copy process when the OS encounters a file that exceeds its maximum individual file size, which I can understand, but find a bit confusing in the context of a zip file.  Certainly, both Windows 7 and XP (NTFS) were able to handle the file to begin with, and only had a problem when decompressing it.  </p>
<p>My best guess is that the file was created using the Compressed Folders feature on XP. The file exceeded the maximum size limit for that version of Windows, but due to the bug described in MS KB article 301325, the file was created anyway using 32-bit headers.  When the file was later opened by Compressed Folders, the 64-bit headers were read (as a file of that size would naturally use 64-bit headers), but that information was garbage, preventing Windows from accurately calculating the space required to extract.</p>
<p>If anyone has a more complete understanding of the cause of this error, please leave me a comment.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ardamis.com/2011/07/28/native-windows-compressed-folders-utility-5-99-pb/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Jealous of The Social Network</title>
		<link>http://www.ardamis.com/2011/06/12/jealous-of-the-social-network/</link>
		<comments>http://www.ardamis.com/2011/06/12/jealous-of-the-social-network/#comments</comments>
		<pubDate>Mon, 13 Jun 2011 02:46:50 +0000</pubDate>
		<dc:creator>ardamis</dc:creator>
				<category><![CDATA[Nonsense]]></category>
		<category><![CDATA[Web Site Dev]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[application]]></category>
		<category><![CDATA[coding]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[downloads]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[web app]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[xhtml]]></category>

		<guid isPermaLink="false">http://www.ardamis.com/?p=1467</guid>
		<description><![CDATA[A short list of my programming accomplishments.]]></description>
			<content:encoded><![CDATA[<p>So I finally watched <a href="http://en.wikipedia.org/wiki/The_Social_Network"><em>The Social Network</em></a> over the weekend, and it&#8217;s made me feel jealous and a bit guilty.</p>
<p>In a meager effort to console myself for so far failing to be a billionaire, I&#8217;m assembling the short list of web-application type things I&#8217;ve built here.</p>
<ol>
<li>A dice roller: <a href="http://alephstudios.com/rollforit/">rollforit</a>. Enter a name, create a room, invite your friends, and start rolling dice.  For people who want to play pen and paper, table-top RPG dice games with their distant friends.</li>
<li>A URL shortener: <a href="http://minifi.de/">Minifi.de</a>.  Minifi.de comes with an API and a bookmarklet.  It really works, too!  The <a href="http://minifi.de/technical.php">technical explanation</a> has more details.</li>
<li>A social networking site: <a href="http://alephstudios.com/snapbase/">Snapbase</a>.  Snapbase is a social site that shows you what&#8217;s going on in your city or anywhere in the world as pictures are uploaded by your friends and neighbors.  The application extracts location information from the EXIF data embedded in images and displays recent images taken near your present location.</li>
<li>A <a href="http://alephstudios.com/helpdesk/">trouble-ticketing system</a> for an IT help desk or technical support center.  It&#8217;s really pretty extensive, with asset management, user accounts, salted encrypted passwords, and all sorts of nifty things.  I really must write a full description of it at some point, but until then, the <a href="http://alephstudios.com/helpdesk/documentation/">documentation</a> is the next best thing.</li>
<li>An <a href="http://alephstudios.com/ias/">account-based invoice tracking and access system</a> for grouping invoices according to clients, then sharing invoice history with those clients and allowing them to easily pay outstanding invoices via Paypal.</li>
<li>An <a href="http://alephstudios.com/ias/">account-based invoice access system</a> where clients can view paid and unpaid invoices, and even easily pay an outstanding invoice via Paypal. I actually use this almost every day.</li>
<li>A simple method for <a href="http://www.ardamis.com/2008/06/11/protecting-a-download-using-a-unique-url/">protecting a download using a unique URL</a> that can be emailed to authorized users. The URL can be set to expire after a certain amount of time or any number of downloads.</li>
<li>An update to the above download protection script to <a href="http://www.ardamis.com/2009/06/26/protecting-multiple-downloads-using-unique-urls/">protect multiple downloads</a>, generate batches of keys, leave notes about who received the key, the ability to specify per-key the allowable number of downloads and age, and some basic reporting.</li>
<li>An HTML auction template generator called <a href="http://simpleauctionwizard.com/">Simple Auction Wizard</a>.  It helps you create HTML auction templates for eBay, and uses SWFUpload and tinyMCE.</li>
</ol>
<p>I have another project in the works that promises to be more financially viable, but the most clever thing on that list is Snapbase.  It&#8217;s in something akin to alpha right now; barely usable.  I really wish I had the time to pursue it.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ardamis.com/2011/06/12/jealous-of-the-social-network/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Office 2010 Rearm</title>
		<link>http://www.ardamis.com/2011/05/31/office-2010-rearm/</link>
		<comments>http://www.ardamis.com/2011/05/31/office-2010-rearm/#comments</comments>
		<pubDate>Wed, 01 Jun 2011 03:46:47 +0000</pubDate>
		<dc:creator>ardamis</dc:creator>
				<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[Thriftiness]]></category>
		<category><![CDATA[application]]></category>
		<category><![CDATA[Office]]></category>
		<category><![CDATA[Windows 7]]></category>

		<guid isPermaLink="false">http://www.ardamis.com/?p=1426</guid>
		<description><![CDATA[Delay Microsoft Office 2010 activation for up to 180 days.]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s widely known that Microsoft allows anyone to install and use any version of Windows 7 for 30 days without having to enter a product activation key. By using the <strong>slmgr -rearm</strong> command, this 30-day trial period can be extended three times for a total of 120 days before the installation must be activated to continue functioning.</p>
<p>It is less known, perhaps, that Microsoft has implemented a similar method for delaying the activation of Office 2010 for up to 180 days.  The steps below are from the <a href="http://technet.microsoft.com/en-us/library/ee624357.aspx#section4">Deploy volume activation of Office 2010</a> instructions on <a href="http://technet.microsoft.com">TechNet</a>.</p>
<h2>To rearm your Office 2010 installation</h2>
<ol>
<li>Make sure all Office 2010 applications are closed.</li>
<li>Open an elevated command prompt.</li>
<li>Go to %installdir%\%Program Files%\Common Files\Microsoft Shared\OfficeSoftwareProtectionPlatform. <em>If you installed the 32-bit edition of Office 2010 on a 64-bit operating system, %Program Files% is the Program Files (x86) folder.</em></li>
<li>Run ospprearm.exe.</li>
</ol>
<p>(The italics are mine, as I was tripped up for awhile by looking for ospprearm.exe under C:\Program Files\, rather than the Program Files (x86) folder.)</p>
<p>The Office 2010 rearm delays the activation requirement for 30 days, and it can be used five times, for a total of 180 days of activation-free use.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ardamis.com/2011/05/31/office-2010-rearm/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Programmatically re-enabling Word COM add-ins</title>
		<link>http://www.ardamis.com/2011/05/05/programmatically-re-enabling-word-com-add-ins/</link>
		<comments>http://www.ardamis.com/2011/05/05/programmatically-re-enabling-word-com-add-ins/#comments</comments>
		<pubDate>Thu, 05 May 2011 21:14:09 +0000</pubDate>
		<dc:creator>ardamis</dc:creator>
				<category><![CDATA[Nonsense]]></category>
		<category><![CDATA[application]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[Office]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[templates]]></category>

		<guid isPermaLink="false">http://www.ardamis.com/?p=1354</guid>
		<description><![CDATA[A Word macro to re-enable all disabled COM add-ins, then force desired LoadBehaviors on specific add-ins, effectively putting Word back into a certain state.]]></description>
			<content:encoded><![CDATA[<p>Nearly a year ago, I wrote a post on how to <a href="http://www.ardamis.com/2010/06/20/detect-and-fix-word-add-in-problems-with-a-macro-and-batch-file/">detect and fix Word add-in problems with a macro and batch file</a>, in a Windows XP and Office 2007 environment.</p>
<p>This was sufficiently effective, but it was also overly complicated, requiring four separate components:</p>
<ol>
<li>an autoexec Word 2007 macro that runs each time Word is opened</li>
<li>a batch file that runs the registry merge file and writes an entry to a log file</li>
<li>the registry merge file that contains the correct LoadBehavior settings for the add-ins</li>
<li>a text file that acts as a log</li>
</ol>
<p>This month, I decided to rewrite the macro to handle the registry changes and write to the log file.  It was also a good opportunity to dig a bit deeper into VBA, and I also wanted to confirm that it would work in a more modern environment of Windows 7 and Office 2010 (that code is near the bottom of the post). The new system has only two components:</p>
<ol>
<li>an autoexec Word 2007 macro that runs each time Word is opened</li>
<li>a text file that acts as a log</li>
</ol>
<h2>Background</h2>
<p>First, a bit of background.</p>
<p>Many of the problems with Word 2007 are due to Word&#8217;s handling of add-ins. When something unexpected happens in Word, and Word attributes the problem to an add-in, Word will react by flagging it and prompting the user for a decision the next time Word opens. Depending on the severity of the problem and the user&#8217;s response, the add-in can be either &#8216;hard-disabled&#8217; or &#8216;soft-disabled&#8217;.</p>
<p>Microsoft explains the differences between Hard Disabled vs Soft Disabled in a MSDN article at: <a href="http://msdn.microsoft.com/en-us/library/ms268871(VS.80).aspx">http://msdn.microsoft.com/en-us/library/ms268871(VS.80).aspx</a>.</p>
<p>I&#8217;ve explained a bit about the process by which Word disables add-ins at the end of this post, and I&#8217;ve written a shorter post about <a href="http://www.ardamis.com/2010/02/26/fixing-word-2007-add-in-issues/">the basics behind the registry keys responsible for disabling add-ins</a>.</p>
<h2>Handling disabled add-ins programmatically</h2>
<p>A Word macro can access the condition of an add-in via an Application.COMAddIns object, and it can read and write to the registry.  This allows us to tell when an add-in has been disabled and re-enabled it.</p>
<p>My macro has some admittedly hackish parts that need to be cleaned up, there is the matter of unsetting variables to be addressed, and it could certainly be made more elegant, but it works.  Note that a file named <strong>addinslog.txt</strong> must exist in the %TEMP% directory in order for the macro to write the log file.  This is what the Word 2007 macro looks like, using the COM add-in installed with Adobe Acrobat 8 Standard as the required add-in&#8230;</p>
<pre class="brush: plain; title: ; notranslate">
Option Explicit

' Set up a function to search for a key and return true or false
Public Function KeyExists(key)
    Dim objShell
    On Error Resume Next
    Set objShell = CreateObject(&quot;WScript.Shell&quot;)
        objShell.RegRead (key)
    Set objShell = Nothing
    If Err = 0 Then KeyExists = True
End Function

Sub AutoExec()
'
' FixMissingAddins Macro
' Display a message box with any critical but not 'Connected' COM add-ins, then fix them programatically
'
' Oliver Baty
' June, 2010 - April, 2011
'
' Information on the Application.COMAddIns array
' http://msdn.microsoft.com/en-us/library/aa831759(v=office.10).aspx
'
' Running macros automatically
' http://support.microsoft.com/kb/286310
'
' Using Windows Scripting Shell (WshShell) to read from and write to the local registry
' http://technet.microsoft.com/en-us/library/ee156602.aspx

' Declare the WshShell variable (this is used to edit the registry)
    Dim WshShell

' Declare the fso and logFile variables (these are used to write to a txt file)
    Dim fso
    Dim logFile

' Create an instance of the WScript Shell object
    Set WshShell = CreateObject(&quot;WScript.Shell&quot;)

' Declare some other variables
   Dim MyAddin As COMAddIn
   Dim stringOfAddins As String
   Dim listOfDisconnectedAddins As String
   Dim requiredAddIn As Variant
   Dim msg As String

' Notes on deleting registry keys and values in VB
' http://www.vbforums.com/showthread.php?t=425483
' http://www.tek-tips.com/viewthread.cfm?qid=674375
' http://www.robvanderwoude.com/vbstech_registry_wshshell.php

' Create a string containing the names of all 'Connected' COM add-ins named &quot;stringOfAddins&quot;
   For Each MyAddin In Application.COMAddIns
      If MyAddin.Connect = True Then
          stringOfAddins = stringOfAddins &amp; MyAddin.ProgID &amp; &quot; - &quot;
      End If
   Next

' Create an array to hold the names of the critical (required) add-ins named &quot;requiredAddIns&quot;
' Example: change to &quot;Dim requiredAddIns(0 To 4)&quot; if the macro is checking 5 total add-ins)
   Dim requiredAddIns(0 To 0) As String

' Add each required AddIn to the array
   requiredAddIns(0) = &quot;PDFMaker.OfficeAddin&quot;
'   requiredAddIns(1) = &quot;&quot;
'   requiredAddIns(2) = &quot;&quot;
'   requiredAddIns(3) = &quot;&quot;
'   requiredAddIns(4) = &quot;&quot;

' Cycle through the array of required add-ins, and see if they exist in the connected add-ins list
   For Each requiredAddIn In requiredAddIns
      If InStr(stringOfAddins, requiredAddIn) Then
        ' The required add-in is in the string of connected add-ins
         msg = msg
      Else
        ' The required add-in is not in the string of connected add-ins, so add the add-in name to a string named &quot;listOfDisconnectedAddins&quot;
         msg = msg &amp; requiredAddIn &amp; vbCrLf
         listOfDisconnectedAddins = requiredAddIn &amp; &quot; &quot; &amp; listOfDisconnectedAddins
         listOfDisconnectedAddins = Trim(listOfDisconnectedAddins)
      End If
   Next

' If the msg variable is not blank (it contains at least one add-in's name) handle it, otherwise, do nothing
   If msg = &quot;&quot; Then
        ' There are no critical, unconnected add-ins (yay!)
        ' The script can now exit
   Else
        ' There are critical add-ins that are not connected, so handle this
        MsgBox &quot;The following critical Word Add-In(s) are disabled: &quot; &amp; vbCrLf &amp; vbCrLf &amp; msg &amp; vbCrLf &amp; vbCrLf &amp; &quot;To correct this problem, please save any documents you are working on, then close Word and reopen Word.&quot;

            ' I find it extremely hackish to check for each possible key and delete it if found... need to research how to delete the tree
            ' One potential obstacle to this method is that I've seen a DocumentRecovery subkey under Resiliency (only once, while editing this macro), that I haven't researched yet

            ' Note: Since the WSH Shell has no Enumeration functionality, you cannot
            '       use the WSH Shell object to delete an entire &quot;tree&quot; unless you
            '       know the exact name of every subkey.
            '       If you don't, use the WMI StdRegProv instead.
            ' http://www.robvanderwoude.com/vbstech_registry_wshshell.php

            ' More info on WMI StdRegProv at:
            ' http://msdn.microsoft.com/en-us/library/aa393664(v=vs.85).aspx

        ' This is hackish, but it effectively deletes a registry key, if it exists
        If KeyExists(&quot;HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Word\Resiliency\DisabledItems\&quot;) Then
            WshShell.RegDelete &quot;HKCU\Software\Microsoft\Office\12.0\Word\Resiliency\DisabledItems\&quot;
        ElseIf KeyExists(&quot;HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Word\Resiliency\StartupItems\&quot;) Then
            WshShell.RegDelete &quot;HKCU\Software\Microsoft\Office\12.0\Word\Resiliency\StartupItems\&quot;
        ElseIf KeyExists(&quot;HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Word\Resiliency\&quot;) Then
            WshShell.RegDelete &quot;HKCU\Software\Microsoft\Office\12.0\Word\Resiliency\&quot;
        End If

        ' To be completely thorough, we can also set the desired LoadBehavior for certain add-ins
        ' This can be done selectively, and only if the LoadBehavior was incorrect, but the quick and dirty way would be to just force the values

        WshShell.RegWrite &quot;HKLM\SOFTWARE\Microsoft\Office\Word\Addins\PDFMaker.OfficeAddin\LoadBehavior&quot;, 3, &quot;REG_DWORD&quot;

        ' Release the WshShell object
        Set WshShell = Nothing

        ' Declare a few variables for the log file
        Dim user, machine, datetime, output

        Set WshShell = CreateObject(&quot;WScript.Shell&quot;)
        user = WshShell.ExpandEnvironmentStrings(&quot;%USERNAME%&quot;)
        machine = WshShell.ExpandEnvironmentStrings(&quot;%COMPUTERNAME%&quot;)
        temp = WshShell.ExpandEnvironmentStrings(&quot;%TEMP%&quot;)
        ' Convert the slashes in Now to hyphens to prevent a fatal error
        datetime = Replace(Now, &quot;/&quot;, &quot;-&quot;)
        ' Create the string that will be written to the log file
        output = datetime + &quot;, &quot; + user + &quot;, &quot; + machine + &quot;, &quot; + listOfDisconnectedAddins

        ' Write the event to a log file
        logfile = temp + &quot;\addinslog.txt&quot;
        ' http://msdn.microsoft.com/en-us/library/2z9ffy99(v=vs.85).aspx
        ' http://www.devguru.com/technologies/vbscript/quickref/filesystemobject_opentextfile.html
        Set fso = CreateObject(&quot;Scripting.FileSystemObject&quot;)
        Set logFile = fso.OpenTextFile(logfile, 8, True)
        logFile.WriteLine (output)
        logFile.Close
        Set logFile = Nothing
        Set fso = Nothing

        ' Should we clear the variables?

        ' Release the WshShell object
        Set WshShell = Nothing
   End If

   ' Ardamis.com - We're in your macros, fixing your COM add-ins.
End Sub
</pre>
<p>While working on this, I found that there were some gaps in my understanding of the sequence of events that occur when Word 2007 disables a COM add-in.  Please comment if you find that any of this is inaccurate or incomplete.</p>
<h2>What happens when Word launches</h2>
<p>A critical key to the whole business of Word add-ins is HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Word\Resiliency</p>
<p>When Word launches, it looks for data under the Resiliency key and a subkey: HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Word\Resiliency\StartupItems</p>
<p>If the StartupItems subkey contains a REG_BINARY value that corresponds to an add-in, Word throws the familiar warning:</p>
<p><em>Microsoft Office Word</em><br />
<em>Word experienced a serious problem with the ‘[addin name]’ add-in. If you have seen this message multiple times, you should disable this add-in and check to see if an update is available. Do you want to disable this add-in?</em><br />
<em>[Yes] [No]</em></p>
<p>Choosing No at the prompt removes the Resiliency key and allows Word to continue to launch, leaving the LoadBehavior for that add-in unchanged.</p>
<p>Choosing No also writes an Error event to the Application Event Viewer log:</p>
<pre class="brush: plain; title: ; notranslate">
Event Type:	Error
Event Source:	Microsoft Office 12
Event Category:	None
Event ID:	2000
Date:		5/23/2011
Time:		3:15:29 PM
User:		N/A
Computer:	[WORKSTATION_NAME]
Description:
Accepted Safe Mode action : Microsoft Office Word.

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.
</pre>
<p>Choosing Yes at the prompt removes the StartupItems subkey and creates a new DisabledItems subkey. This DisabledItems subkey will contain a different REG_BINARY value, the data of which contains information about the disabled add-in.</p>
<p>Choosing Yes also writes an Error event to the Application Event Viewer log:</p>
<pre class="brush: plain; title: ; notranslate">
Event Type:	Error
Event Source:	Microsoft Office 12
Event Category:	None
Event ID:	2001
Date:		5/23/2011
Time:		3:12:36 PM
User:		N/A
Computer:	[WORKSTATION_NAME]
Description:
Rejected Safe Mode action : Microsoft Office Word.

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.
</pre>
<p>At this point, the add-in is &#8216;hard-disabled&#8217;, but not &#8216;soft-disabled&#8217;.</p>
<p>Word then continues to launch, but without loading the add-in.  </p>
<p>To see which add-ins have been hard-disabled, click on the Office Button | Word Options | Add-Ins, and scroll down to &#8220;Disabled Application Add-ins&#8221;.</p>
<p>To see which add-ins have been soft-disabled, click on the Office Button | Word Options | Add-Ins. Select &#8220;COM Add-Ins&#8221; in the Manage menu and click Go.</p>
<p>Word is somewhat tricky in this regard, as the add-in will not have a checkmark, but the LoadBehavior registry value will be unchanged.  At any other time, the presence of a checkmark is an indication of the LoadBehavior, but when an add-in has been hard-disabled, the box will always be unchecked.</p>
<h2>What users can do at this point</h2>
<p>Going through Word Options and enabling the hard-disabled COM add-in will remove the Resiliency key.  This may not make the add-in immediately available in Word, however.</p>
<p>To immediately load the add-in and gain its functionality, you can check the box.  Otherwise, close and reopen Word, which will cause Word to launch with the add-in&#8217;s specified LoadBehavior.</p>
<p>In case you were curious about the keyboard shortcuts used to enable the first disabled add-in in the list of disabled add-ins (maybe you wanted to do something with SendKeys, for example), they are:<br />
<strong>Alt+F, I, A, A, Tab, Tab, Tab, D, Enter, G, Space, Alt+E, C, Alt+F4</strong>.</p>
<p>In summary, deleting the Resiliency key after the &#8220;serious problem&#8221; prompt, then closing and reopening Word, returns Word to a normal operating state.</p>
<p>What I intend to accomplish with the macro is to re-enable the hard-disabled add-in, return any LoadBehavior values back to the desired settings, then prompt the user to save their work and close and reopen Word.</p>
<p>This should return Word to a working state.</p>
<h2>Word 2010 on 64-bit Windows 7</h2>
<p>As a bonus, here&#8217;s the same macro, with some minor adjustments to run in Word 2010 on Windows 7 64-bit, with Adobe Acrobat 9 Pro&#8217;s COM add-in acting as one of the required add-ins.  The OneNote add-in is not enabled in Word by default, and the macro below does not attempt to enable it, but does consider it a required add-in.  This is done to demonstrate the pop-up window.  Note that a file named <strong>addinslog.txt</strong> must exist in the %TEMP% directory in order for the macro to write the log file.</p>
<pre class="brush: plain; title: ; notranslate">
Option Explicit

' Set up a function to search for a key and return true or false
Public Function KeyExists(key)
    Dim objShell
    On Error Resume Next
    Set objShell = CreateObject(&quot;WScript.Shell&quot;)
        objShell.RegRead (key)
    Set objShell = Nothing
    If Err = 0 Then KeyExists = True
End Function

Sub AutoExec()
'
' FixMissingAddins Macro
' Display a message box with any critical but not 'Connected' COM add-ins, then fix them programatically
'
' Oliver Baty
' June, 2010 - April, 2011
'
' Information on the Application.COMAddIns array
' http://msdn.microsoft.com/en-us/library/aa831759(v=office.10).aspx
'
' Running macros automatically
' http://support.microsoft.com/kb/286310
'
' Using Windows Scripting Shell (WshShell) to read from and write to the local registry
' http://technet.microsoft.com/en-us/library/ee156602.aspx

' Declare the WshShell variable (this is used to edit the registry)
    Dim WshShell

' Declare the fso and logFile variables (these are used to write to a txt file)
    Dim fso
    Dim logfile

' Create an instance of the WScript Shell object
    Set WshShell = CreateObject(&quot;WScript.Shell&quot;)

' Declare some other variables
   Dim MyAddin As COMAddIn
   Dim stringOfAddins As String
   Dim listOfDisconnectedAddins As String
   Dim requiredAddIn As Variant
   Dim msg As String

' Notes on deleting registry keys and values in VB
' http://www.vbforums.com/showthread.php?t=425483
' http://www.tek-tips.com/viewthread.cfm?qid=674375
' http://www.robvanderwoude.com/vbstech_registry_wshshell.php

' Create a string containing the names of all 'Connected' COM add-ins named &quot;stringOfAddins&quot;
   For Each MyAddin In Application.COMAddIns
      If MyAddin.Connect = True Then
          stringOfAddins = stringOfAddins &amp; MyAddin.ProgID &amp; &quot; - &quot;
      End If
   Next

' Create an array to hold the names of the critical (required) add-ins named &quot;requiredAddIns&quot;
' Example: change to &quot;Dim requiredAddIns(0 To 4)&quot; if the macro is checking 5 total add-ins)
   Dim requiredAddIns(0 To 1) As String

' Add each required AddIn to the array
   requiredAddIns(0) = &quot;PDFMaker.OfficeAddin&quot;
   requiredAddIns(1) = &quot;OneNote.WordAddinTakeNotesService&quot;
'   requiredAddIns(2) = &quot;&quot;
'   requiredAddIns(3) = &quot;&quot;
'   requiredAddIns(4) = &quot;&quot;

' Cycle through the array of required add-ins, and see if they exist in the connected add-ins list
   For Each requiredAddIn In requiredAddIns
      If InStr(stringOfAddins, requiredAddIn) Then
        ' The required add-in is in the string of connected add-ins
         msg = msg
      Else
        ' The required add-in is not in the string of connected add-ins, so add the add-in name to a string named &quot;listOfDisconnectedAddins&quot;
         msg = msg &amp; requiredAddIn &amp; vbCrLf
         listOfDisconnectedAddins = requiredAddIn &amp; &quot; &quot; &amp; listOfDisconnectedAddins
         listOfDisconnectedAddins = Trim(listOfDisconnectedAddins)
      End If
   Next

' If the msg variable is not blank (it contains at least one add-in's name) handle it, otherwise, do nothing
   If msg = &quot;&quot; Then
        ' There are no critical, unconnected add-ins (yay!)
        ' The script can now exit
   Else
        ' There are critical add-ins that are not connected, so handle this
        MsgBox &quot;The following critical Word Add-In(s) are disabled: &quot; &amp; vbCrLf &amp; vbCrLf &amp; msg &amp; vbCrLf &amp; vbCrLf &amp; &quot;To correct this problem, please save any documents you are working on, then close Word and reopen Word.&quot;

            ' I find it extremely hackish to check for each possible key and delete it if found... need to research how to delete the tree
            ' One potential obstacle to this method is that I've seen a DocumentRecovery subkey under Resiliency (only once, while editing this macro), that I haven't researched yet

            ' Note: Since the WSH Shell has no Enumeration functionality, you cannot
            '       use the WSH Shell object to delete an entire &quot;tree&quot; unless you
            '       know the exact name of every subkey.
            '       If you don't, use the WMI StdRegProv instead.
            ' http://www.robvanderwoude.com/vbstech_registry_wshshell.php

            ' More info on WMI StdRegProv at:
            ' http://msdn.microsoft.com/en-us/library/aa393664(v=vs.85).aspx

        ' This is hackish, but it effectively deletes a registry key, if it exists
        If KeyExists(&quot;HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\Word\Resiliency\DisabledItems\&quot;) Then
            WshShell.RegDelete &quot;HKCU\Software\Microsoft\Office\14.0\Word\Resiliency\DisabledItems\&quot;
        ElseIf KeyExists(&quot;HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\Word\Resiliency\StartupItems\&quot;) Then
            WshShell.RegDelete &quot;HKCU\Software\Microsoft\Office\14.0\Word\Resiliency\StartupItems\&quot;
        ElseIf KeyExists(&quot;HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\Word\Resiliency\&quot;) Then
            WshShell.RegDelete &quot;HKCU\Software\Microsoft\Office\14.0\Word\Resiliency\&quot;
        End If

        ' To be completely thorough, we can also set the desired LoadBehavior for certain add-ins
        ' This can be done selectively, and only if the LoadBehavior was incorrect, but the quick and dirty way would be to just force the values

        WshShell.RegWrite &quot;HKCU\Software\Microsoft\Office\Word\Addins\PDFMaker.OfficeAddin\LoadBehavior&quot;, 3, &quot;REG_DWORD&quot;

        ' Release the WshShell object
        Set WshShell = Nothing

        ' Declare a few variables for the log file
        Dim user, machine, temp, datetime, output

        Set WshShell = CreateObject(&quot;WScript.Shell&quot;)
        user = WshShell.ExpandEnvironmentStrings(&quot;%USERNAME%&quot;)
        machine = WshShell.ExpandEnvironmentStrings(&quot;%COMPUTERNAME%&quot;)
        temp = WshShell.ExpandEnvironmentStrings(&quot;%TEMP%&quot;)
        ' Convert the slashes in Now to hyphens to prevent a fatal error
        datetime = Replace(Now, &quot;/&quot;, &quot;-&quot;)
        ' Create the string that will be written to the log file
        output = datetime + &quot;, &quot; + user + &quot;, &quot; + machine + &quot;, &quot; + listOfDisconnectedAddins

        ' Write the event to a log file
        logfile = temp + &quot;\addinslog.txt&quot;
        ' http://msdn.microsoft.com/en-us/library/2z9ffy99(v=vs.85).aspx
        ' http://www.devguru.com/technologies/vbscript/quickref/filesystemobject_opentextfile.html
        Set fso = CreateObject(&quot;Scripting.FileSystemObject&quot;)
        Set logfile = fso.OpenTextFile(logfile, 8, True)
        logfile.WriteLine (output)
        logfile.Close
        Set logfile = Nothing
        Set fso = Nothing

        ' Should we clear the variables?

        ' Release the WshShell object
        Set WshShell = Nothing
   End If

   ' Ardamis.com - We're in your macros, fixing your COM add-ins.
End Sub
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.ardamis.com/2011/05/05/programmatically-re-enabling-word-com-add-ins/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>A macro for toggling the read-only attribute of Word.qat</title>
		<link>http://www.ardamis.com/2010/11/02/a-macro-for-toggling-the-read-only-attribute-of-word-qat/</link>
		<comments>http://www.ardamis.com/2010/11/02/a-macro-for-toggling-the-read-only-attribute-of-word-qat/#comments</comments>
		<pubDate>Tue, 02 Nov 2010 15:01:55 +0000</pubDate>
		<dc:creator>ardamis</dc:creator>
				<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[application]]></category>
		<category><![CDATA[Office]]></category>
		<category><![CDATA[troubleshooting]]></category>

		<guid isPermaLink="false">http://www.ardamis.com/?p=1116</guid>
		<description><![CDATA[Protect the Word QAT from unintended changes by setting the read-only attribute on Word.qat from within Word itself.]]></description>
			<content:encoded><![CDATA[<p>Actually, my method of toggling the read-only attribute of Word.qat requires two macros.  One switches on the read-only attribute of Word.qat to prevent it from being changed.  The other clears the read-only attribute so buttons can be added or removed.</p>
<p>Just add these macros as buttons to the QAT to quickly protect and unprotect it.  I prefer two buttons to a single button because I don&#8217;t know of a way of telling, visually, the current state of the read-only attribute of a file.  I&#8217;d love to change the appearance of the button to indicate the state, but I haven&#8217;t found a way to do this.  So for now, two buttons allow the user to take exactly the action desired.</p>
<p>Why would you ever need to do this?</p>
<p>Buttons disappearing from the QAT is a pretty common occurrence.</p>
<p>If your Quick Access Toolbar contains buttons from templates or COM add-ins, these custom buttons can be lost when Word is closed and reopened.  To demonstrate this, add such a button to the QAT, then close Word and reopen it from the command line with <strong>winword.exe /a</strong> (the <strong>/n</strong> switch may also demonstrate this).  Word will open, but without any add-ins.  Instead of creating a temporary Word.qat with the default buttons, the working Word.qat file is edited to remove all the non-native Word buttons.  The appearance to the user is that Word loses the custom buttons.  Once the buttons disappear, they do not return when Word is opened normally.</p>
<p>From what I can tell, the QAT buttons disappearing isn&#8217;t a random event or a bug, but an intentional consequence of initiating a Word instance without any add-ins or a result of a badly written function in a template or add-in.</p>
<h3>The LockQAT Macro</h3>
<pre class="brush: plain; title: ; notranslate">
Sub LockQAT()
'
' LockQAT Macro
'
'

    Dim appdata, thepath, objFSO, objFile

    Set oShell = CreateObject(&quot;WScript.Shell&quot;)
    appdata = oShell.ExpandEnvironmentStrings(&quot;%APPDATA%&quot;)
    thepath = appdata &amp; &quot;\Microsoft\Office\Word.qat&quot;

    Set objFSO = CreateObject(&quot;Scripting.FileSystemObject&quot;)
    Set objFile = objFSO.GetFile(thepath)

    'Determine if the file is ALREADY Read-Only
    If objFile.Attributes And 1 Then
        MsgBox &quot;The Word.QAT file is already Read-only.&quot;
    Else
        MsgBox &quot;Locking the QAT from further editing.&quot;
        objFile.Attributes = objFile.Attributes + 1
    End If

' Resources
' http://www.4guysfromrolla.com/webtech/112600-1.shtml

End Sub
</pre>
<h3>The UnlockQAT Macro</h3>
<pre class="brush: plain; title: ; notranslate">
Sub UnlockQAT()
'
' UnlockQAT Macro
'
'

    Dim appdata, thepath, objFSO, objFile

    Set oShell = CreateObject(&quot;WScript.Shell&quot;)
    appdata = oShell.ExpandEnvironmentStrings(&quot;%APPDATA%&quot;)
    thepath = appdata &amp; &quot;\Microsoft\Office\Word.qat&quot;

    Set objFSO = CreateObject(&quot;Scripting.FileSystemObject&quot;)
    Set objFile = objFSO.GetFile(thepath)

    'Determine if the file is ALREADY Read-Only
    If objFile.Attributes And 1 Then
        MsgBox &quot;Unlocking the QAT for editing.&quot;
        objFile.Attributes = objFile.Attributes - 1
    Else
        MsgBox &quot;The Word.QAT file is already writeable.&quot;
    End If

' Resources
' http://www.4guysfromrolla.com/webtech/112600-1.shtml

End Sub
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.ardamis.com/2010/11/02/a-macro-for-toggling-the-read-only-attribute-of-word-qat/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>GroupWise 7 attachment filename truncation</title>
		<link>http://www.ardamis.com/2010/08/30/groupwise-7-attachment-filename-truncation/</link>
		<comments>http://www.ardamis.com/2010/08/30/groupwise-7-attachment-filename-truncation/#comments</comments>
		<pubDate>Mon, 30 Aug 2010 18:32:52 +0000</pubDate>
		<dc:creator>ardamis</dc:creator>
				<category><![CDATA[Nonsense]]></category>
		<category><![CDATA[application]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[troubleshooting]]></category>

		<guid isPermaLink="false">http://www.ardamis.com/?p=1004</guid>
		<description><![CDATA[An investigation of attachment filename truncation in GroupWise 7 and an interesting discovery.]]></description>
			<content:encoded><![CDATA[<p>A user pointed out that while an attachment to an email had the icon of an installed application (in this case, Word) and the correct extention (.DOC), it would not open in Word with a double-click.  The <strong>Open With</strong> dialog box opened instead.  When the attachment was dragged to the desktop, it opened in Word with a double-click, as expected.</p>
<p>Because the filename of this attachment was pretty long, I suspected that it had something to do with the length of the filename, and so I started investigating.</p>
<h2>Test files</h2>
<p>I created text files with the following filenames:</p>
<p>119-CAsEvpz1R29z7sdrgs4-apJFmpissj-xxdfgh-dfght66x1aseg-7tSs5lF7lLyd3T-HJf42YiMYwguijgrsywh-wgh3q45y4tys64ysy45-119.txt</p>
<p>120-CAsEvpz1R29z7sdrgs4-apJFmpiRssj-xxdfgh-dfght66x1aseg-7tSs5lF7lLyd3T-HJf42YiMYwguijgrsywh-wgh3q45y4tys64ysy45-120.txt</p>
<p>123-CAsEvpz1R29z7sdrgs437-apJFmpiRssj-xxdfgh-dfght66x1aseg-7tSs5lF7lLyd3T-HJ6f42YiMYwguijgrsywh-wgh3q45y4tys64ysy45-123.txt</p>
<p>129-vpz1R296z7apJFmpiRss6x1IA7eu9utSs5lF7lLyd3THJf42YiMYwgVZ4FJ-CAsEvpz1R296z7apJFmpiRss6x1IA7eu9utSs5lF7lLyd3THJf42YiMYa-129.txt</p>
<p>131-vpz1R296z7apJFmpiRss6x23451IA7e9utSs5lF7lLyd3THJf42YiMYwgVZ4FJ-CAsEvpz1R296z7apJFmpiRss6x451I7eu9utSlF7lLyd3THJf42YiMYa-131.txt</p>
<p>133-vpz1R296z7apJFmpiRss6x1IA7eu9utSs5lF7lLyd3THJf42YiMYwgVZ4FJ-CAsEvpz1R296z7apJFmpiRss6x1IA7eu9utSs5lF7lLyd3THJf42YiMYwgVZa-133.txt</p>
<p>Including the extensions, these filenames are 119, 120, 123, 129, 131, and 133 characters, respectively.</p>
<h2>Outgoing emails</h2>
<p>I added the files as attachments to a new mail message.</p>
<p>For attachments with less than 130 characters in the filename, the filename is displayed in full.</p>
<p>The &#8220;129-&#8221; attachment&#8217;s name is 129 characters and the full name was displayed.  The icon was the correct Text Document file icon.<br />
The &#8220;131-&#8221; attachment&#8217;s name is 131 characters and the name was truncated at the 129th character (&#8230;-131.t).  This caused the icon to be the Windows unrecognized file type icon (presumably because my computer has no application associated with a &#8220;.t&#8221; extension).<br />
The &#8220;133-&#8221; attachment&#8217;s name is 133 characters and was truncated at the 129th character, before the . character (&#8230;-133).  The icon was a Notepad icon, but not the typical Text Document file icon.</p>
<p>It would appear that attachment filenames are truncated at the 129th character when displayed in the attachment pane in the <strong>Mail To:</strong> window.</p>
<h2>Incoming emails</h2>
<p>Send the email.</p>
<p>Open the received email or view it with the Quickviewer and note that the filenames in the attachment window are again truncated at the 129th character.  </p>
<p>Try to open each attachment with a double-click.  The &#8220;119-&#8221; attachment should open in Notepad, but all attachments with longer filenames will not open, and instead cause an Open With prompt to open.  </p>
<p>Why was this happening?</p>
<h2>The locally stored attachment filename is differently truncated</h2>
<p>Even when the full filename <em>with the extension</em> is visible, the filename of the local copy of the attachment is truncated at X characters, where <strong>X = (130 &#8211; PATH/TO/XPgrpwise)</strong>.</p>
<p>In my case, the path to the XPgrpwise folder that contains the attachments is 63 characters long:<br />
C:\Documents and Settings\F_LAST\Local Settings\Temp\XPgrpwise\<br />
This means the maximum filename length for emails opened under my account is 67 characters (130 &#8211; 63 = 67).</p>
<p>A longer or shorter path to this folder will cause the maximum filename length to change accordingly, so if you repeat these tests, your experience may vary.</p>
<p>This matters less than one would think, though, because of the way GroupWise 7 shortens the filenames.</p>
<h3>An unexpected discovery</h3>
<p>Instead of simply chopping off any characters after the character limit has been reached, however, GroupWise will truncate the name portion of the filename and leave the extention portion intact, up to 119 characters.  Filenames 120 characters or longer are truncated to the 119-character limit but the extention begins to be cut off.  Once the extention begins to be cut off, double-clicking the attachment will cause the Open With dialog box to open if the shortened extention doesn&#8217;t have an application associated with it or is missing entirely (as one would expect).  Filenames of 123 characters or longer are truncated such that the dot+extension is completely removed.  This 119-character limit seems to be independent of the number of characters in the path to XPgrpwise.</p>
<h3>In summary</h3>
<p>To sum up, just because GroupWise 7 displays the full filename of an attachment and the correct icon doesn&#8217;t mean that the attachment can be opened in its associated application from the message window.  When GroupWise saves the file to the hard drive, it truncates long filenames, potentially removing the extension. Opening attachments with filenames greater than 119 characters in length requires a little more effort because of the way the local copy of the file is saved without an extension.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ardamis.com/2010/08/30/groupwise-7-attachment-filename-truncation/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

